diff --git a/.gitignore b/.gitignore index 776e2b659..9f2cba3bf 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,8 @@ data CMakeLists.txt.user *.swp *.swo +*.kate-swp +.cproject +.project +.settings/ +.directory diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..caf2e3389 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,39 @@ +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 libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev + - sudo apt-get install -qq libbullet-dev libogre-static-dev libmygui-static-dev libsdl2-static-dev libunshield-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 -DBOOST_STATIC=1 -DSDL2_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/CMakeLists.txt b/CMakeLists.txt index e1b8e32e5..625239aa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,10 @@ if (APPLE) set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") + + set(CMAKE_EXE_LINKER_FLAGS "-F /Library/Frameworks") + set(CMAKE_SHARED_LINKER_FLAGS "-F /Library/Frameworks") + set(CMAKE_MODULE_LINKER_FLAGS "-F /Library/Frameworks") endif (APPLE) # Macros @@ -15,7 +19,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 22) +set (OPENMW_VERSION_MINOR 26) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -27,6 +31,7 @@ configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_ option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE) option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE) option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE) +option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE) # Apps and tools option(BUILD_BSATOOL "build BSA extractor" OFF) @@ -48,18 +53,16 @@ option(OPENMW_OSX_DEPLOYMENT OFF) find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems") # Location of morrowind data files -if(DPKG_PROGRAM) +if (APPLE) + set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") + set(MORROWIND_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") +elseif(UNIX) set(MORROWIND_DATA_FILES "/usr/share/games/openmw/data/" CACHE PATH "location of Morrowind data files") set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files") else() - if (APPLE) - set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") - set(MORROWIND_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") - else() - set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files") - set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files") - endif(APPLE) -endif(DPKG_PROGRAM) + set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files") + set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files") +endif(APPLE) if (WIN32) option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON) @@ -75,8 +78,12 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp + ${LIBDIR}/openengine/ogre/lights.cpp + ${LIBDIR}/openengine/ogre/particles.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp + ${LIBDIR}/openengine/ogre/imagerotate.cpp ) + set(OENGINE_GUI ${LIBDIR}/openengine/gui/manager.cpp ) @@ -154,6 +161,20 @@ if (NOT FFMPEG_FOUND) message(WARNING "--------------------") endif (NOT FFMPEG_FOUND) +# TinyXML +option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) +if(USE_SYSTEM_TINYXML) + find_library(TINYXML_LIBRARIES tinyxml) + find_path(TINYXML_INCLUDE_DIR tinyxml.h) + message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}") + add_definitions (-DTIXML_USE_STL) + if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR) + include_directories(${TINYXML_INCLUDE_DIR}) + message(STATUS "Using system TinyXML library.") + else() + message(FATAL_ERROR "Detection of system TinyXML incomplete.") + endif() +endif() # Platform specific if (WIN32) @@ -180,15 +201,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 date_time) -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) @@ -197,7 +217,7 @@ endif() find_package(OGRE REQUIRED) find_package(MyGUI REQUIRED) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) -find_package(OIS REQUIRED) +find_package(SDL2 REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) IF(OGRE_STATIC) @@ -210,8 +230,8 @@ ENDIF(WIN32) ENDIF(OGRE_STATIC) include_directories("." ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS} - ${OGRE_Terrain_INCLUDE_DIR} - ${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR} + ${SDL2_INCLUDE_DIR} + ${Boost_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR} ${MYGUI_INCLUDE_DIRS} ${MYGUI_PLATFORM_INCLUDE_DIRS} @@ -220,7 +240,7 @@ include_directories("." ${LIBDIR} ) -link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MYGUI_LIB_DIR}) +link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MYGUI_LIB_DIR}) if (APPLE) # List used Ogre plugins @@ -293,9 +313,12 @@ configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local "${OpenMW_BINARY_DIR}/openmw.cfg") + configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg "${OpenMW_BINARY_DIR}/openmw.cfg.install") +configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg + "${OpenMW_BINARY_DIR}/opencs.cfg") if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop @@ -306,71 +329,122 @@ endif() # Compiler settings if (CMAKE_COMPILER_IS_GNUCC) - add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long) + SET(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long ${CMAKE_CXX_FLAGS}") # Silence warnings in OGRE headers. Remove once OGRE got fixed! - add_definitions (-Wno-ignored-qualifiers) + SET(CMAKE_CXX_FLAGS "-Wno-ignored-qualifiers ${CMAKE_CXX_FLAGS}") execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) - add_definitions (-Wno-unused-but-set-parameter) + SET(CMAKE_CXX_FLAGS "-Wno-unused-but-set-parameter ${CMAKE_CXX_FLAGS}") endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) endif (CMAKE_COMPILER_IS_GNUCC) -if(DPKG_PROGRAM) - SET(CMAKE_INSTALL_PREFIX "/usr") +IF(NOT WIN32 AND NOT APPLE) + ## Debian and non debian Linux building + # Paths + IF (DPKG_PROGRAM) + ## Debian specific + SET(CMAKE_INSTALL_PREFIX "/usr") + SET(DATAROOTDIR "share" CACHE PATH "Sets the root of data directories to a non-default location") + SET(DATADIR "share/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") + SET(ICONDIR "share/pixmaps" CACHE PATH "Set icon dir") + SET(SYSCONFDIR "../etc/openmw" CACHE PATH "Set config dir") + ELSE () + ## Non debian specific + 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}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") + SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") + SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.") + SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir") + + # Install binaries + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" 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 licenses + INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) + INSTALL(FILES "Daedric Font License.txt" DESTINATION "${LICDIR}" ) + INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" ) + INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) + ENDIF (DPKG_PROGRAM) + + # Install icon and desktop file + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + IF(BUILD_OPENCS) + INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs") + INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs") + ENDIF(BUILD_OPENCS) - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") - exec_program("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION ) - STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}") - exec_program("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME ) - exec_program("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL) - set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>") - else() - set(VERSION_STRING "${OPENMW_VERSION}") - set(PACKAGE_MAINTAINER "unknown") - endif() + # Install global configuration files + INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + IF(BUILD_OPENCS) + INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs") + ENDIF(BUILD_OPENCS) - #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}/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") - INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") - INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "../etc/openmw/" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") - - #Install resources - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "share/games/openmw/" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") - INSTALL(DIRECTORY DESTINATION "share/games/openmw/data/" COMPONENT "Resources") - - SET(CPACK_GENERATOR "DEB") - SET(CPACK_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org") - SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") - SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}") - SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind + # Install resources + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") + INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources") + + IF (DPKG_PROGRAM) + ## Debian Specific + IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") + EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION ) + STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}") + EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME ) + EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL) + SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>") + ELSE() + SET(VERSION_STRING "${OPENMW_VERSION}") + SET(PACKAGE_MAINTAINER "unknown") + ENDIF() + + SET(CPACK_GENERATOR "DEB") + SET(CPACK_PACKAGE_NAME "openmw") + SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org") + SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") + SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}") + SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind. Data files from the original game is required to run it.") - SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") + SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") + SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") 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_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") - SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") + SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") - string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE) - execute_process( - COMMAND ${DPKG_PROGRAM} --print-architecture - OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") + STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE) + EXECUTE_PROCESS( + COMMAND ${DPKG_PROGRAM} --print-architecture + OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") - include(CPack) -endif(DPKG_PROGRAM) + INCLUDE(CPack) + ENDIF(DPKG_PROGRAM) +ENDIF(NOT WIN32 AND NOT APPLE) if(WIN32) FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll") @@ -443,6 +517,7 @@ endif(WIN32) # Extern add_subdirectory (extern/shiny) add_subdirectory (extern/oics) +add_subdirectory (extern/sdl4ogre) # Components add_subdirectory (components) @@ -459,6 +534,13 @@ if (BUILD_ESMTOOL) endif() if (BUILD_LAUNCHER) + if(NOT WIN32) + find_package(LIBUNSHIELD REQUIRED) + if(NOT LIBUNSHIELD_FOUND) + message(SEND_ERROR "Failed to find libunshield") + endif(NOT LIBUNSHIELD_FOUND) + endif(NOT WIN32) + add_subdirectory( apps/launcher ) endif() @@ -578,6 +660,7 @@ if (APPLE) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + install(FILES "${OpenMW_BINARY_DIR}/opencs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) @@ -651,45 +734,3 @@ if (APPLE) include(CPack) 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(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(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(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") - - # Install binaries - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" 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}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}") - INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications") - - # Install global configuration files - INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" ) - #INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${SYSCONFDIR}" ) - INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" ) - INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" ) - - # Install resources - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) -endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index 5c588fb29..1d0026215 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -17,11 +17,6 @@ target_link_libraries(esmtool components ) -#if (APPLE) -# find_library(CARBON_FRAMEWORK Carbon) -# target_link_libraries(openmw ${CARBON_FRAMEWORK}) -#endif (APPLE) - if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(esmtool gcov) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 3c9476d7a..a60e9f0e2 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.") @@ -105,11 +108,26 @@ bool parseOptions (int argc, char** argv, Arguments &info) // there might be a better way to do this bpo::options_description all; all.add(desc).add(hidden); - bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) - .options(all).positional(p).run(); - bpo::variables_map variables; - bpo::store(valid_opts, variables); + + try + { + bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) + .options(all).positional(p).run(); + + bpo::store(valid_opts, variables); + } + catch(boost::program_options::unknown_option & x) + { + std::cerr << "ERROR: " << x.what() << std::endl; + return false; + } + catch(boost::program_options::invalid_command_line_syntax & x) + { + std::cerr << "ERROR: " << x.what() << std::endl; + return false; + } + bpo::notify(variables); if (variables.count ("help")) @@ -162,6 +180,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(); @@ -228,7 +247,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; } } @@ -284,16 +306,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; @@ -344,6 +363,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(); @@ -430,9 +450,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 f08c31003..7b1fc7fb2 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -609,8 +609,7 @@ std::string ruleFunction(int idx) "Alarm", "Flee", "Should Attack", - //Unkown but causes NPCs to growl and roar. - "UNKNOWN 72" + "Werewolf" }; if (idx >= 0 && idx <= 72) return ruleFunctions[idx]; @@ -627,10 +626,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/record.cpp b/apps/esmtool/record.cpp index 38fddd6b9..68e0dcc09 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -106,10 +106,10 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss) { case '0': oper_str = "=="; break; case '1': oper_str = "!="; break; - case '2': oper_str = "< "; break; - case '3': oper_str = "<="; break; - case '4': oper_str = "> "; break; - case '5': oper_str = ">="; break; + case '2': oper_str = "> "; break; + case '3': oper_str = ">="; break; + case '4': oper_str = "< "; break; + case '5': oper_str = "<="; break; } std::ostringstream stream; @@ -275,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: @@ -439,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; @@ -464,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<> @@ -679,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] != "") { @@ -753,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; @@ -771,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<> @@ -864,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; @@ -886,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; @@ -902,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; @@ -1103,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. @@ -1199,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 eb93d71e7..92cabffff 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -4,6 +4,7 @@ set(LAUNCHER main.cpp maindialog.cpp playpage.cpp + textslotmsgbox.cpp settings/gamesettings.cpp settings/graphicssettings.cpp @@ -14,12 +15,16 @@ set(LAUNCHER ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ) +if(NOT WIN32) + LIST(APPEND LAUNCHER unshieldthread.cpp) +endif(NOT WIN32) set(LAUNCHER_HEADER datafilespage.hpp graphicspage.hpp maindialog.hpp playpage.hpp + textslotmsgbox.hpp settings/gamesettings.hpp settings/graphicssettings.hpp @@ -30,6 +35,10 @@ set(LAUNCHER_HEADER utils/textinputdialog.hpp ) +if(NOT WIN32) + LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp) +endif(NOT WIN32) + # Headers that must be pre-processed set(LAUNCHER_HEADER_MOC @@ -37,11 +46,17 @@ set(LAUNCHER_HEADER_MOC graphicspage.hpp maindialog.hpp playpage.hpp + textslotmsgbox.hpp utils/checkablemessagebox.hpp utils/textinputdialog.hpp ) +if(NOT WIN32) + LIST(APPEND LAUNCHER_HEADER_MOC unshieldthread.hpp) +endif(NOT WIN32) + + set(LAUNCHER_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui @@ -64,8 +79,12 @@ 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}) +if(NOT WIN32) + include_directories(${LIBUNSHIELD_INCLUDE}) +endif(NOT WIN32) # Main executable IF(OGRE_STATIC) @@ -90,9 +109,17 @@ target_link_libraries(omwlauncher ${Boost_LIBRARIES} ${OGRE_LIBRARIES} ${OGRE_STATIC_PLUGINS} + ${SDL2_LIBRARY} ${QT_LIBRARIES} components ) +if(NOT WIN32) + target_link_libraries(omwlauncher + ${LIBUNSHIELD_LIBRARY} + ) +endif(NOT WIN32) + + if(DPKG_PROGRAM) INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher) @@ -102,3 +129,9 @@ 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/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 700ba3db9..9308c1d57 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -4,6 +4,12 @@ #include #include +#ifdef __APPLE__ +// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154 +#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +#endif +#include + #include #include @@ -35,13 +41,14 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g setupUi(this); // Set the maximum res we can set in windowed mode - QRect res = QApplication::desktop()->screenGeometry(); + QRect res = getMaximumResolution(); customWidthSpinBox->setMaximum(res.width()); customHeightSpinBox->setMaximum(res.height()); 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(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int))); } @@ -144,17 +151,41 @@ bool GraphicsPage::setupOgre() } antiAliasingComboBox->clear(); - resolutionComboBox->clear(); antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); - resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem)); - // Load the rest of the values - loadSettings(); return true; } -void GraphicsPage::loadSettings() +bool GraphicsPage::setupSDL() { + int displays = SDL_GetNumVideoDisplays(); + + if (displays < 0) + { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error receiving number of screens")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
SDL_GetNumDisplayModes failed:

") + QString::fromStdString(SDL_GetError()) + "
"); + msgBox.exec(); + return false; + } + + for (int i = 0; i < displays; i++) + { + screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); + } + + return true; +} + +bool GraphicsPage::loadSettings() +{ + if (!setupSDL()) + return false; + if (!setupOgre()) + return false; + if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true")) vSyncCheckBox->setCheckState(Qt::Checked); @@ -168,6 +199,9 @@ void GraphicsPage::loadSettings() QString width = mGraphicsSettings.value(QString("Video/resolution x")); QString height = mGraphicsSettings.value(QString("Video/resolution y")); QString resolution = width + QString(" x ") + height; + QString screen = mGraphicsSettings.value(QString("Video/screen")); + + screenComboBox->setCurrentIndex(screen.toInt()); int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith); @@ -180,6 +214,8 @@ void GraphicsPage::loadSettings() customHeightSpinBox->setValue(height.toInt()); } + + return true; } void GraphicsPage::saveSettings() @@ -205,6 +241,8 @@ void GraphicsPage::saveSettings() mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value())); mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->value())); } + + mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex())); } QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer) @@ -240,64 +278,83 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy return result; } -QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer) +QStringList GraphicsPage::getAvailableResolutions(int screen) { - QString key("Video Mode"); QStringList result; + SDL_DisplayMode mode; + int modeIndex, modes = SDL_GetNumDisplayModes(screen); - uint row = 0; - Ogre::ConfigOptionMap options = renderer->getConfigOptions(); - - for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++) + if (modes < 0) { - if (key.toStdString() != i->first) - continue; - - Ogre::StringVector::iterator opt_it; - uint idx = 0; + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error receiving resolutions")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
SDL_GetNumDisplayModes failed:

") + QString::fromStdString(SDL_GetError()) + "
"); + msgBox.exec(); + return result; + } - for (opt_it = i->second.possibleValues.begin (); - opt_it != i->second.possibleValues.end (); opt_it++, idx++) + for (modeIndex = 0; modeIndex < modes; modeIndex++) + { + if (SDL_GetDisplayMode(screen, modeIndex, &mode) < 0) { - QRegExp resolutionRe(QString("(\\d+) x (\\d+).*")); - QString resolution = QString::fromStdString(*opt_it).simplified(); - - if (resolutionRe.exactMatch(resolution)) { - - int width = resolutionRe.cap(1).toInt(); - int height = resolutionRe.cap(2).toInt(); + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error receiving resolutions")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
SDL_GetDisplayMode failed:

") + QString::fromStdString(SDL_GetError()) + "
"); + msgBox.exec(); + return result; + } - QString aspect = getAspect(width, height); - QString cleanRes = resolutionRe.cap(1) + QString(" x ") + resolutionRe.cap(2); + QString aspect = getAspect(mode.w, mode.h); + QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h); - if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { - cleanRes.append(tr("\t(Wide ") + aspect + ")"); + if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { + resolution.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); - } + } else if (aspect == QLatin1String("4:3")) { + resolution.append(tr("\t(Standard 4:3)")); } - } - // Sort the resolutions in descending order - qSort(result.begin(), result.end(), naturalSortGreaterThanCI); + result.append(resolution); + } + result.removeDuplicates(); return result; } +QRect GraphicsPage::getMaximumResolution() +{ + QRect max; + int screens = QApplication::desktop()->screenCount(); + for (int i = 0; i < screens; ++i) + { + QRect res = QApplication::desktop()->screenGeometry(i); + if (res.width() > max.width()) + max.setWidth(res.width()); + if (res.height() > max.height()) + max.setHeight(res.height()); + } + return max; +} + void GraphicsPage::rendererChanged(const QString &renderer) { mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString()); antiAliasingComboBox->clear(); - resolutionComboBox->clear(); antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); - resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem)); +} + +void GraphicsPage::screenChanged(int screen) +{ + if (screen >= 0) { + resolutionComboBox->clear(); + resolutionComboBox->addItems(getAvailableResolutions(screen)); + } } void GraphicsPage::slotFullScreenChanged(int state) diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 21039af43..d233ea12e 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -30,10 +30,11 @@ public: GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); void saveSettings(); - bool setupOgre(); + bool loadSettings(); public slots: void rendererChanged(const QString &renderer); + void screenChanged(int screen); private slots: void slotFullScreenChanged(int state); @@ -55,10 +56,11 @@ private: GraphicsSettings &mGraphicsSettings; QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); - QStringList getAvailableResolutions(Ogre::RenderSystem *renderer); - - void loadSettings(); + QStringList getAvailableResolutions(int screen); + QRect getMaximumResolution(); + bool setupOgre(); + bool setupSDL(); }; #endif diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 09da1d615..f67f5edcf 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -1,11 +1,27 @@ #include #include #include +#include + +#ifdef __APPLE__ +// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154 +#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +#endif +#include #include "maindialog.hpp" +// SDL workaround +#include "graphicspage.hpp" int main(int argc, char *argv[]) { + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError()); + return 0; + } + QApplication app(argc, argv); // Now we make sure the current dir is set to application path @@ -41,6 +57,8 @@ int main(int argc, char *argv[]) return 0; } - return app.exec(); + int returnValue = app.exec(); + SDL_Quit(); + return returnValue; } diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index e5da3431a..032f70916 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -11,6 +11,12 @@ #include +#ifndef WIN32 + #include "unshieldthread.hpp" +#endif + +#include "textslotmsgbox.hpp" + #include "utils/checkablemessagebox.hpp" #include "playpage.hpp" @@ -128,11 +134,16 @@ bool MainDialog::showFirstRunDialog() 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"))); + else + { + 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 @@ -292,8 +303,8 @@ bool MainDialog::setup() // 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()) + // Call this so we can exit on Ogre/SDL errors before mainwindow is shown + if (!mGraphicsPage->loadSettings()) return false; loadSettings(); @@ -310,6 +321,8 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) bool MainDialog::setupLauncherSettings() { + mLauncherSettings.setMultiValueEnabled(true); + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QStringList paths; @@ -342,6 +355,78 @@ bool MainDialog::setupLauncherSettings() return true; } +#ifndef WIN32 +bool expansions(UnshieldThread& cd) +{ + if(cd.BloodmoonDone()) + { + cd.Done(); + return false; + } + + QMessageBox expansionsBox; + expansionsBox.setText(QObject::tr("
Would you like to install expansions now ? (make sure you have the disc)
\ + If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.
")); + + QAbstractButton* tribunalButton = NULL; + if(!cd.TribunalDone()) + tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole); + + QAbstractButton* bloodmoonButton = expansionsBox.addButton(QObject::tr("&Bloodmoon"), QMessageBox::ActionRole); + QAbstractButton* noneButton = expansionsBox.addButton(QObject::tr("&None"), QMessageBox::ActionRole); + + expansionsBox.exec(); + + if(expansionsBox.clickedButton() == noneButton) + { + cd.Done(); + return false; + } + else if(expansionsBox.clickedButton() == tribunalButton) + { + + TextSlotMsgBox cdbox; + cdbox.setStandardButtons(QMessageBox::Cancel); + + QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); + QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); + + cd.SetTribunalPath( + QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select data1.hdr from Tribunal Installation CD (Tribunal/data1.hdr on GOTY CDs)"), + QDir::currentPath(), + QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData()); + + cd.start(); + cdbox.exec(); + } + else if(expansionsBox.clickedButton() == bloodmoonButton) + { + + TextSlotMsgBox cdbox; + cdbox.setStandardButtons(QMessageBox::Cancel); + + QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); + QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); + + cd.SetBloodmoonPath( + QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select data1.hdr from Bloodmoon Installation CD (Bloodmoon/data1.hdr on GOTY CDs)"), + QDir::currentPath(), + QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData()); + + cd.start(); + cdbox.exec(); + } + + + + return true; +} +#endif // WIN32 + bool MainDialog::setupGameSettings() { QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); @@ -399,9 +484,15 @@ bool MainDialog::setupGameSettings() Press \"Browse...\" to specify the location manually.
")); QAbstractButton *dirSelectButton = - msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole); + msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole); + + #ifndef WIN32 + QAbstractButton *cdSelectButton = + msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole); + #endif + - msgBox.exec(); + msgBox.exec(); QString selectedFile; if (msgBox.clickedButton() == dirSelectButton) { @@ -411,6 +502,40 @@ bool MainDialog::setupGameSettings() QDir::currentPath(), QString(tr("Morrowind master file (*.esm)"))); } + #ifndef WIN32 + else if(msgBox.clickedButton() == cdSelectButton) { + UnshieldThread cd; + + { + TextSlotMsgBox cdbox; + cdbox.setStandardButtons(QMessageBox::Cancel); + + QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); + QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); + + cd.SetMorrowindPath( + QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select data1.hdr from Morrowind Installation CD"), + QDir::currentPath(), + QString(tr("Installshield hdr file (*.hdr)"))).toUtf8().constData()); + + cd.SetOutputPath( + QFileDialog::getExistingDirectory( + NULL, + QObject::tr("Select where to extract files to"), + QDir::currentPath(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData()); + + cd.start(); + cdbox.exec(); + } + + while(expansions(cd)); + + selectedFile = QString::fromStdString(cd.GetMWEsmPath()); + } + #endif // WIN32 if (selectedFile.isEmpty()) return false; // Cancel was clicked; @@ -427,6 +552,8 @@ bool MainDialog::setupGameSettings() bool MainDialog::setupGraphicsSettings() { + mGraphicsSettings.setMultiValueEnabled(false); + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); @@ -608,8 +735,21 @@ void MainDialog::closeEvent(QCloseEvent *event) void MainDialog::play() { - if (!writeSettings()) + if (!writeSettings()) { qApp->quit(); + return; + } + + if(!mGameSettings.hasMaster()) { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("No master file selected")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
You do not have any master files selected.

\ + OpenMW will not start without a master file selected.
")); + msgBox.exec(); + return; + } // Launch the game detached startProgram(QString("openmw"), true); diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 7e818a74a..824dff6e8 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -2,9 +2,9 @@ #define MAINDIALOG_H #include - +#ifndef Q_MOC_RUN #include - +#endif #include "settings/gamesettings.hpp" #include "settings/graphicssettings.hpp" #include "settings/launchersettings.hpp" diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 56c08582f..205879bc3 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -6,8 +6,6 @@ #include #include -#include - #include #include @@ -96,15 +94,15 @@ bool GameSettings::readFile(QTextStream &stream) QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); // Don't remove existing data entries if (key != QLatin1String("data")) diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp index 7a17ef9af..55b2107e2 100644 --- a/apps/launcher/settings/gamesettings.hpp +++ b/apps/launcher/settings/gamesettings.hpp @@ -43,6 +43,7 @@ public: inline QStringList getDataDirs() { return mDataDirs; } inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } inline QString getDataLocal() {return mDataLocal; } + inline bool hasMaster() { return mSettings.count(QString("master")) > 0; } QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); bool readFile(QTextStream &stream); diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index bbfad1fbb..ed8ada56c 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -7,14 +7,12 @@ #include #include -#include - template class SettingsBase { public: - SettingsBase() {} + SettingsBase() { mMultiValue = false; } ~SettingsBase() {} inline QString value(const QString &key, const QString &defaultValue = QString()) @@ -36,6 +34,11 @@ public: mSettings.insertMulti(key, value); } + inline void setMultiValueEnabled(bool enable) + { + mMultiValue = enable; + } + inline void remove(const QString &key) { mSettings.remove(key); @@ -48,11 +51,12 @@ public: mCache.clear(); QString sectionPrefix; + QRegExp sectionRe("^\\[([^]]+)\\]"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; @@ -65,8 +69,8 @@ public: if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); if (!sectionPrefix.isEmpty()) key.prepend(sectionPrefix); @@ -74,8 +78,13 @@ public: mSettings.remove(key); QStringList values = mCache.values(key); + if (!values.contains(value)) { - mCache.insertMulti(key, value); + if (mMultiValue) { + mCache.insertMulti(key, value); + } else { + mCache.insert(key, value); + } } } } @@ -93,6 +102,8 @@ public: private: Map mSettings; Map mCache; + + bool mMultiValue; }; #endif // SETTINGSBASE_HPP diff --git a/apps/launcher/textslotmsgbox.cpp b/apps/launcher/textslotmsgbox.cpp new file mode 100644 index 000000000..0607d1cc6 --- /dev/null +++ b/apps/launcher/textslotmsgbox.cpp @@ -0,0 +1,6 @@ +#include "textslotmsgbox.hpp" + +void TextSlotMsgBox::setTextSlot(const QString& string) +{ + setText(string); +} diff --git a/apps/launcher/textslotmsgbox.hpp b/apps/launcher/textslotmsgbox.hpp new file mode 100644 index 000000000..a29e2c354 --- /dev/null +++ b/apps/launcher/textslotmsgbox.hpp @@ -0,0 +1,13 @@ +#ifndef TEXT_SLOT_MSG_BOX +#define TEXT_SLOT_MSG_BOX + +#include + +class TextSlotMsgBox : public QMessageBox +{ +Q_OBJECT + public slots: + void setTextSlot(const QString& string); +}; + +#endif diff --git a/apps/launcher/unshieldthread.cpp b/apps/launcher/unshieldthread.cpp new file mode 100644 index 000000000..6ddad7a21 --- /dev/null +++ b/apps/launcher/unshieldthread.cpp @@ -0,0 +1,493 @@ +#include "unshieldthread.hpp" + +#include +#include + +namespace bfs = boost::filesystem; + +namespace +{ + static bool make_sure_directory_exists(bfs::path directory) + { + + if(!bfs::exists(directory)) + { + bfs::create_directories(directory); + } + + return bfs::exists(directory); + } + + void fill_path(bfs::path& path, const std::string& name) + { + size_t start = 0; + + size_t i; + for(i = 0; i < name.length(); i++) + { + switch(name[i]) + { + case '\\': + path /= name.substr(start, i-start); + start = i+1; + break; + } + } + + path /= name.substr(start, i-start); + } + + std::string get_setting(const std::string& category, const std::string& setting, const std::string& inx) + { + size_t start = inx.find(category); + start = inx.find(setting, start) + setting.length() + 3; + + size_t end = inx.find("!", start); + + return inx.substr(start, end-start); + } + + std::string read_to_string(const bfs::path& path) + { + std::ifstream strstream(path.c_str(), std::ios::in | std::ios::binary); + std::string str; + + strstream.seekg(0, std::ios::end); + str.resize(strstream.tellg()); + strstream.seekg(0, std::ios::beg); + strstream.read(&str[0], str.size()); + strstream.close(); + + return str; + } + + void add_setting(const std::string& category, const std::string& setting, const std::string& val, std::string& ini) + { + size_t loc; + loc = ini.find("[" + category + "]"); + + // If category is not found, create it + if(loc == std::string::npos) + { + loc = ini.size() + 2; + ini += ("\r\n[" + category + "]\r\n"); + } + + loc += category.length() +2 +2; + ini.insert(loc, setting + "=" + val + "\r\n"); + } + + void bloodmoon_fix_ini(std::string& ini, const bfs::path inxPath) + { + std::string inx = read_to_string(inxPath); + + // Remove this one setting (the only one actually changed by bloodmoon, as opposed to just adding new ones) + size_t start = ini.find("[Weather Blight]"); + start = ini.find("Ambient Loop Sound ID", start); + size_t end = ini.find("\r\n", start) +2; + ini.erase(start, end-start); + + std::string category; + std::string setting; + + category = "General"; + { + setting = "Werewolf FOV"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Moons"; + { + setting = "Script Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather"; + { + setting = "Snow Ripples"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripple Radius"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripples Per Flake"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripple Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripple Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Gravity Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow High Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Low Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather Blight"; + { + setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather Snow"; + { + setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Diameter"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Height Min"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Height Max"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Entrance Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Max Snowflakes"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather Blizzard"; + { + setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Storm Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + } + + + void fix_ini(const bfs::path& output_dir, bfs::path cdPath, bool tribunal, bool bloodmoon) + { + bfs::path ini_path = output_dir; + ini_path /= "Morrowind.ini"; + + std::string ini = read_to_string(ini_path.string()); + + if(tribunal) + { + add_setting("Game Files", "GameFile1", "Tribunal.esm", ini); + add_setting("Archives", "Archive 0", "Tribunal.bsa", ini); + } + if(bloodmoon) + { + bloodmoon_fix_ini(ini, cdPath / "setup.inx"); + add_setting("Game Files", "GameFile2", "Bloodmoon.esm", ini); + add_setting("Archives", "Archive 1", "Bloodmoon.bsa", ini); + } + + std::ofstream inistream(ini_path.c_str()); + inistream << ini; + inistream.close(); + } + + void installToPath(const bfs::path& from, const bfs::path& to, bool copy = false) + { + make_sure_directory_exists(to); + + for ( bfs::directory_iterator end, dir(from); dir != end; ++dir ) + { + if(bfs::is_directory(dir->path())) + installToPath(dir->path(), to / dir->path().filename(), copy); + else + { + if(copy) + { + bfs::path dest = to / dir->path().filename(); + if(bfs::exists(dest)) + bfs::remove_all(dest); + bfs::copy_file(dir->path(), dest); + } + else + bfs::rename(dir->path(), to / dir->path().filename()); + } + } + } + + bfs::path findFile(const bfs::path& in, std::string filename, bool recursive = true) + { + if(recursive) + { + for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir ) + { + if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + return dir->path(); + } + } + else + { + for ( bfs::directory_iterator end, dir(in); dir != end; ++dir ) + { + if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + return dir->path(); + } + } + + return ""; + } + + bool contains(const bfs::path& in, std::string filename) + { + for(bfs::directory_iterator end, dir(in); dir != end; ++dir) + { + if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + return true; + } + + return false; + } + + time_t getTime(const char* time) + { + struct tm tms; + memset(&tms, 0, sizeof(struct tm)); + strptime(time, "%d %B %Y", &tms); + return mktime(&tms); + } +} + +bool UnshieldThread::SetMorrowindPath(const std::string& path) +{ + mMorrowindPath = path; + return true; +} + +bool UnshieldThread::SetTribunalPath(const std::string& path) +{ + mTribunalPath = path; + return true; +} + +bool UnshieldThread::SetBloodmoonPath(const std::string& path) +{ + mBloodmoonPath = path; + return true; +} + +void UnshieldThread::SetOutputPath(const std::string& path) +{ + mOutputPath = path; +} + +bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) +{ + bool success; + bfs::path dirname; + bfs::path filename; + int directory = unshield_file_directory(unshield, index); + + dirname = output_dir; + + if (prefix && prefix[0]) + dirname /= prefix; + + if (directory >= 0) + { + const char* tmp = unshield_directory_name(unshield, directory); + if (tmp && tmp[0]) + fill_path(dirname, tmp); + } + + make_sure_directory_exists(dirname); + + filename = dirname; + filename /= unshield_file_name(unshield, index); + + emit signalGUI(QString("Extracting: ") + QString(filename.c_str())); + + success = unshield_file_save(unshield, index, filename.c_str()); + + if (!success) + bfs::remove(filename); + + return success; +} + +void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) +{ + Unshield * unshield; + unshield = unshield_open(cab.c_str()); + + int i; + for (i = 0; i < unshield_file_group_count(unshield); i++) + { + UnshieldFileGroup* file_group = unshield_file_group_get(unshield, i); + + for (size_t j = file_group->first_file; j <= file_group->last_file; j++) + { + if (unshield_file_is_valid(unshield, j)) + extract_file(unshield, output_dir, file_group->name, j); + } + } + unshield_close(unshield); +} + + +bool UnshieldThread::extract() +{ + bfs::path outputDataFilesDir = mOutputPath; + outputDataFilesDir /= "Data Files"; + bfs::path extractPath = mOutputPath; + extractPath /= "extract-temp"; + + if(!mMorrowindDone && mMorrowindPath.string().length() > 0) + { + mMorrowindDone = true; + + bfs::path mwExtractPath = extractPath / "morrowind"; + extract_cab(mMorrowindPath, mwExtractPath, true); + + bfs::path dFilesDir = findFile(mwExtractPath, "morrowind.esm").parent_path(); + + installToPath(dFilesDir, outputDataFilesDir); + + // Videos are often kept uncompressed on the cd + bfs::path videosPath = findFile(mMorrowindPath.parent_path(), "video", false); + if(videosPath.string() != "") + { + emit signalGUI(QString("Installing Videos...")); + installToPath(videosPath, outputDataFilesDir / "Video", true); + } + + bfs::path cdDFiles = findFile(mMorrowindPath.parent_path(), "data files", false); + if(cdDFiles.string() != "") + { + emit signalGUI(QString("Installing Uncompressed Data files from CD...")); + installToPath(cdDFiles, outputDataFilesDir, true); + } + + + bfs::rename(findFile(mwExtractPath, "morrowind.ini"), outputDataFilesDir / "Morrowind.ini"); + + mTribunalDone = contains(outputDataFilesDir, "tribunal.esm"); + mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm"); + + } + + else if(!mTribunalDone && mTribunalPath.string().length() > 0) + { + mTribunalDone = true; + + bfs::path tbExtractPath = extractPath / "tribunal"; + extract_cab(mTribunalPath, tbExtractPath, true); + + bfs::path dFilesDir = findFile(tbExtractPath, "tribunal.esm").parent_path(); + + installToPath(dFilesDir, outputDataFilesDir); + + // Mt GOTY CD has Sounds in a seperate folder from the rest of the data files + bfs::path soundsPath = findFile(tbExtractPath, "sounds", false); + if(soundsPath.string() != "") + installToPath(soundsPath, outputDataFilesDir / "Sounds"); + + bfs::path cdDFiles = findFile(mTribunalPath.parent_path(), "data files", false); + if(cdDFiles.string() != "") + { + emit signalGUI(QString("Installing Uncompressed Data files from CD...")); + installToPath(cdDFiles, outputDataFilesDir, true); + } + + mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm"); + + fix_ini(outputDataFilesDir, bfs::path(mTribunalPath).parent_path(), mTribunalDone, mBloodmoonDone); + } + + else if(!mBloodmoonDone && mBloodmoonPath.string().length() > 0) + { + mBloodmoonDone = true; + + bfs::path bmExtractPath = extractPath / "bloodmoon"; + extract_cab(mBloodmoonPath, bmExtractPath, true); + + bfs::path dFilesDir = findFile(bmExtractPath, "bloodmoon.esm").parent_path(); + + installToPath(dFilesDir, outputDataFilesDir); + + // My GOTY CD contains a folder within cab files called Tribunal patch, + // which contains Tribunal.esm + bfs::path tbPatchPath = findFile(bmExtractPath, "tribunal.esm"); + if(tbPatchPath.string() != "") + bfs::rename(tbPatchPath, outputDataFilesDir / "Tribunal.esm"); + + bfs::path cdDFiles = findFile(mBloodmoonPath.parent_path(), "data files", false); + if(cdDFiles.string() != "") + { + emit signalGUI(QString("Installing Uncompressed Data files from CD...")); + installToPath(cdDFiles, outputDataFilesDir, true); + } + + fix_ini(outputDataFilesDir, bfs::path(mBloodmoonPath).parent_path(), false, mBloodmoonDone); + } + + + return true; +} + +void UnshieldThread::Done() +{ + // Get rid of unnecessary files + bfs::remove_all(mOutputPath / "extract-temp"); + + // Set modified time to release dates, to preserve load order + if(mMorrowindDone) + bfs::last_write_time(findFile(mOutputPath, "morrowind.esm"), getTime("1 May 2002")); + + if(mTribunalDone) + bfs::last_write_time(findFile(mOutputPath, "tribunal.esm"), getTime("6 November 2002")); + + if(mBloodmoonDone) + bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003")); +} + +std::string UnshieldThread::GetMWEsmPath() +{ + return findFile(mOutputPath / "Data Files", "morrowind.esm").string(); +} + +bool UnshieldThread::TribunalDone() +{ + return mTribunalDone; +} + +bool UnshieldThread::BloodmoonDone() +{ + return mBloodmoonDone; +} + +void UnshieldThread::run() +{ + extract(); + emit close(); +} + +UnshieldThread::UnshieldThread() +{ + unshield_set_log_level(0); + mMorrowindDone = false; + mTribunalDone = false; + mBloodmoonDone = false; +} diff --git a/apps/launcher/unshieldthread.hpp b/apps/launcher/unshieldthread.hpp new file mode 100644 index 000000000..655cb5b53 --- /dev/null +++ b/apps/launcher/unshieldthread.hpp @@ -0,0 +1,56 @@ +#ifndef UNSHIELD_THREAD_H +#define UNSHIELD_THREAD_H + +#include + +#include + +#include + +class UnshieldThread : public QThread +{ + Q_OBJECT + + public: + bool SetMorrowindPath(const std::string& path); + bool SetTribunalPath(const std::string& path); + bool SetBloodmoonPath(const std::string& path); + + void SetOutputPath(const std::string& path); + + bool extract(); + + bool TribunalDone(); + bool BloodmoonDone(); + + void Done(); + + std::string GetMWEsmPath(); + + UnshieldThread(); + + private: + + void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); + bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); + + boost::filesystem::path mMorrowindPath; + boost::filesystem::path mTribunalPath; + boost::filesystem::path mBloodmoonPath; + + bool mMorrowindDone; + bool mTribunalDone; + bool mBloodmoonDone; + + boost::filesystem::path mOutputPath; + + + protected: + virtual void run(); + + signals: + void signalGUI(QString); + void close(); +}; + +#endif 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 fc9ce417c..8732b3eab 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -645,7 +645,7 @@ std::string MwIniImporter::numberToString(int n) { return str.str(); } -MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { +MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filename) const { std::cout << "load ini file: " << filename << std::endl; std::string section(""); @@ -690,6 +690,10 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { std::string key(section + ":" + line.substr(0,pos)); std::string value(line.substr(pos+1)); + if(value.empty()) { + std::cout << "Warning: ignored empty value for key '" << key << "'." << std::endl; + continue; + } multistrmap::iterator it; if((it = map.find(key)) == map.end()) { @@ -701,7 +705,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { return map; } -MwIniImporter::multistrmap MwIniImporter::loadCfgFile(std::string filename) { +MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::string& filename) { std::cout << "load cfg file: " << filename << std::endl; MwIniImporter::multistrmap map; @@ -738,12 +742,11 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(std::string filename) { return map; } -void MwIniImporter::merge(multistrmap &cfg, multistrmap &ini) { - multistrmap::iterator cfgIt; - multistrmap::iterator iniIt; - for(strmap::iterator it=mMergeMap.begin(); it!=mMergeMap.end(); ++it) { +void MwIniImporter::merge(multistrmap &cfg, const multistrmap &ini) const { + multistrmap::const_iterator iniIt; + for(strmap::const_iterator it=mMergeMap.begin(); it!=mMergeMap.end(); ++it) { if((iniIt = ini.find(it->second)) != ini.end()) { - for(std::vector::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { + for(std::vector::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { cfg.erase(it->first); insertMultistrmap(cfg, it->first, *vc); } @@ -751,14 +754,13 @@ void MwIniImporter::merge(multistrmap &cfg, multistrmap &ini) { } } -void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) { +void MwIniImporter::mergeFallback(multistrmap &cfg, const multistrmap &ini) const { cfg.erase("fallback"); - multistrmap::iterator cfgIt; - multistrmap::iterator iniIt; - for(std::vector::iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); ++it) { + multistrmap::const_iterator iniIt; + for(std::vector::const_iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); ++it) { if((iniIt = ini.find(*it)) != ini.end()) { - for(std::vector::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { + for(std::vector::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { std::string value(*it); std::replace( value.begin(), value.end(), ' ', '_' ); std::replace( value.begin(), value.end(), ':', '_' ); @@ -769,21 +771,21 @@ void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) { } } -void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::string value) { - multistrmap::iterator it = cfg.find(key); +void MwIniImporter::insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value) { + const multistrmap::const_iterator it = cfg.find(key); if(it == cfg.end()) { cfg.insert(std::make_pair (key, std::vector() )); } cfg[key].push_back(value); } -void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) { +void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) const { std::vector archives; std::string baseArchive("Archives:Archive "); std::string archive; // Search archives listed in ini file - multistrmap::iterator it = ini.begin(); + multistrmap::const_iterator it = ini.begin(); for(int i=0; it != ini.end(); i++) { archive = baseArchive; archive.append(this->numberToString(i)); @@ -793,7 +795,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) { break; } - for(std::vector::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { + for(std::vector::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { archives.push_back(*entry); } } @@ -805,18 +807,18 @@ void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) { // 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) { + for(std::vector::const_iterator it=archives.begin(); it!=archives.end(); ++it) { cfg["fallback-archive"].push_back(*it); } } -void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) { +void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { std::vector esmFiles; std::vector espFiles; std::string baseGameFile("Game Files:GameFile"); std::string gameFile(""); - multistrmap::iterator it = ini.begin(); + multistrmap::const_iterator it = ini.begin(); for(int i=0; it != ini.end(); i++) { gameFile = baseGameFile; gameFile.append(this->numberToString(i)); @@ -826,7 +828,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) { break; } - for(std::vector::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { + for(std::vector::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::toLower(filetype); @@ -844,22 +846,22 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) { cfg.erase("master"); cfg.insert( std::make_pair > ("master", std::vector() ) ); - for(std::vector::iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) { + for(std::vector::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) { cfg["master"].push_back(*it); } cfg.erase("plugin"); cfg.insert( std::make_pair > ("plugin", std::vector() ) ); - for(std::vector::iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { + for(std::vector::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { cfg["plugin"].push_back(*it); } } -void MwIniImporter::writeToFile(boost::iostreams::stream &out, multistrmap &cfg) { +void MwIniImporter::writeToFile(boost::iostreams::stream &out, const multistrmap &cfg) { - for(multistrmap::iterator it=cfg.begin(); it != cfg.end(); ++it) { - for(std::vector::iterator entry=it->second.begin(); entry != it->second.end(); ++entry) { + for(multistrmap::const_iterator it=cfg.begin(); it != cfg.end(); ++it) { + for(std::vector::const_iterator entry=it->second.begin(); entry != it->second.end(); ++entry) { out << (it->first) << "=" << (*entry) << std::endl; } } diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index 6b99810bc..784980e09 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -18,18 +18,17 @@ class MwIniImporter { MwIniImporter(); void setInputEncoding(const ToUTF8::FromType& encoding); void setVerbose(bool verbose); - multistrmap loadIniFile(std::string filename); - multistrmap loadCfgFile(std::string filename); - 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); - + multistrmap loadIniFile(const std::string& filename) const; + static multistrmap loadCfgFile(const std::string& filename); + void merge(multistrmap &cfg, const multistrmap &ini) const; + void mergeFallback(multistrmap &cfg, const multistrmap &ini) const; + void importGameFiles(multistrmap &cfg, const multistrmap &ini) const; + void importArchives(multistrmap &cfg, const multistrmap &ini) const; + static void writeToFile(boost::iostreams::stream &out, const multistrmap &cfg); + private: - void insertMultistrmap(multistrmap &cfg, std::string key, std::string value); - std::string numberToString(int n); - std::string toUTF8(const std::string &str); + static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); + static std::string numberToString(int n); bool mVerbose; strmap mMergeMap; std::vector mMergeFallback; diff --git a/apps/mwiniimporter/main.cpp b/apps/mwiniimporter/main.cpp index c9d88c0bb..364a6b1a4 100644 --- a/apps/mwiniimporter/main.cpp +++ b/apps/mwiniimporter/main.cpp @@ -28,12 +28,26 @@ int main(int argc, char *argv[]) { p_desc.add("ini", 1).add("cfg", 1); bpo::variables_map vm; - bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) - .options(desc) - .positional(p_desc) - .run(); - bpo::store(parsed, vm); + try + { + bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) + .options(desc) + .positional(p_desc) + .run(); + + bpo::store(parsed, vm); + } + catch(boost::program_options::unknown_option & x) + { + std::cerr << "ERROR: " << x.what() << std::endl; + return false; + } + catch(boost::program_options::invalid_command_line_syntax & x) + { + std::cerr << "ERROR: " << x.what() << std::endl; + return false; + } if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) { std::cout << desc; @@ -55,10 +69,8 @@ int main(int argc, char *argv[]) { std::cerr << "ini file does not exist" << std::endl; return -3; } - if(!boost::filesystem::exists(cfgFile)) { + if(!boost::filesystem::exists(cfgFile)) std::cerr << "cfg file does not exist" << std::endl; - return -4; - } MwIniImporter importer; importer.setVerbose(vm.count("verbose")); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 0071dd1fc..00547a2ba 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -1,8 +1,8 @@ - set (OPENCS_SRC main.cpp) opencs_units (. editor) +set (CMAKE_BUILD_TYPE DEBUG) opencs_units (model/doc document @@ -18,16 +18,17 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel + idtable idtableproxymodel regionmap data ) opencs_units_noqt (model/world - universalid data record idcollection commands columnbase + universalid record commands columnbase scriptcontext cell refidcollection + refidadapter refiddata refidadapterimp ref collectionbase refcollection columns ) opencs_hdrs_noqt (model/world - columns + columnimp idcollection collection ) @@ -36,12 +37,14 @@ opencs_units (model/tools ) opencs_units_noqt (model/tools - stage verifier mandatoryid + stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + birthsigncheck spellcheck ) opencs_units (view/doc - viewmanager view operations operation subview startup filedialog + viewmanager view operations operation subview startup filedialog newgame filewidget + adjusterwidget ) @@ -55,11 +58,14 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world - table tablesubview + table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator + cellcreator referenceablecreator referencecreator ) opencs_units_noqt (view/world - dialoguesubview util subviews enumdelegate vartypedelegate + dialoguesubview subviews + enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate + scripthighlighter idvalidator ) @@ -71,6 +77,48 @@ opencs_units_noqt (view/tools subviews ) +opencs_units (view/settings + abstractblock + proxyblock + abstractwidget + usersettingsdialog + datadisplayformatpage + windowpage + ) + +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 + ) + +opencs_units_noqt (model/filter + node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode + ) + +opencs_hdrs_noqt (model/filter + filter + ) + +opencs_units (view/filter + filtercreator filterbox recordfilterbox editwidget + ) set (OPENCS_US ) @@ -88,7 +136,7 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtXmlPatterns REQUIRED) +find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED) include(${QT_USE_FILE}) qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 5966d089e..9a6832ec0 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -1,23 +1,38 @@ #include "editor.hpp" -#include +#include +#include +#include +#include #include "model/doc/document.hpp" #include "model/world/data.hpp" + CS::Editor::Editor() : mViewManager (mDocumentManager) { - connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ())); + mIpcServerName = "org.openmw.OpenCS"; + + setupDataFiles(); + + mNewGame.setLocalData (mLocal); + + connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); + connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ())); + connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ())); - connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ())); + connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ())); + connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ())); connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); + connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); - setupDataFiles(); + connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), + this, SLOT (createNewGame (const boost::filesystem::path&))); } void CS::Editor::setupDataFiles() @@ -35,35 +50,63 @@ void CS::Editor::setupDataFiles() mCfgMgr.readConfiguration(variables, desc); - Files::PathContainer mDataDirs, mDataLocal; + Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { - mDataDirs = Files::PathContainer(variables["data"].as()); + dataDirs = Files::PathContainer(variables["data"].as()); } std::string local = variables["data-local"].as(); if (!local.empty()) { - mDataLocal.push_back(Files::PathContainer::value_type(local)); + dataLocal.push_back(Files::PathContainer::value_type(local)); } - mCfgMgr.processPaths(mDataDirs); - mCfgMgr.processPaths(mDataLocal); + mCfgMgr.processPaths (dataDirs); + mCfgMgr.processPaths (dataLocal, true); + + if (!dataLocal.empty()) + mLocal = dataLocal[0]; + else + { + QMessageBox messageBox; + messageBox.setWindowTitle (tr ("No local data path available")); + messageBox.setIcon (QMessageBox::Critical); + messageBox.setStandardButtons (QMessageBox::Ok); + messageBox.setText(tr("
OpenCS is unable to access the local data directory. This may indicate a faulty configuration or a broken install.")); + messageBox.exec(); + + QApplication::exit (1); + return; + } // 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()); + dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { QString path = QString::fromStdString(iter->string()); mFileDialog.addFiles(path); } + + //load the settings into the userSettings instance. + const QString settingFileName = "opencs.cfg"; + CSMSettings::UserSettings::instance().loadSettings(settingFileName); } -void CS::Editor::createDocument() +void CS::Editor::createGame() +{ + mStartup.hide(); + + if (mNewGame.isHidden()) + mNewGame.show(); + + mNewGame.raise(); + mNewGame.activateWindow(); +} + +void CS::Editor::createAddon() { mStartup.hide(); @@ -86,7 +129,9 @@ void CS::Editor::openFiles() files.push_back(path.toStdString()); } - CSMDoc::Document *document = mDocumentManager.addDocument(files, false); + /// \todo Get the save path from the file dialogue + + CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false); mViewManager.addView (document); mFileDialog.hide(); @@ -103,15 +148,73 @@ void CS::Editor::createNewFile() files.push_back(mFileDialog.fileName().toStdString()); - CSMDoc::Document *document = mDocumentManager.addDocument (files, true); + /// \todo Get the save path from the file dialogue. + + CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true); mViewManager.addView (document); mFileDialog.hide(); } +void CS::Editor::createNewGame (const boost::filesystem::path& file) +{ + std::vector files; + + files.push_back (file); + + CSMDoc::Document *document = mDocumentManager.addDocument (files, file, true); + + mViewManager.addView (document); + + mNewGame.hide(); +} + +void CS::Editor::showStartup() +{ + if(mStartup.isHidden()) + mStartup.show(); + mStartup.raise(); + mStartup.activateWindow(); +} + +void CS::Editor::showSettings() +{ + if (mSettings.isHidden()) + mSettings.show(); + + mSettings.raise(); + mSettings.activateWindow(); +} + +bool CS::Editor::makeIPCServer() +{ + mServer = new QLocalServer(this); + + if(mServer->listen(mIpcServerName)) + { + connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup())); + return true; + } + + mServer->close(); + return false; +} + +void CS::Editor::connectToIPCServer() +{ + mClientSocket = new QLocalSocket(this); + mClientSocket->connectToServer(mIpcServerName); + mClientSocket->close(); +} + int CS::Editor::run() { + if (mLocal.empty()) + return 1; + mStartup.show(); + QApplication::setQuitOnLastWindowClosed (true); + return QApplication::exec(); } diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index c242a17ea..c83b2a685 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -2,14 +2,23 @@ #define CS_EDITOR_H #include +#include +#include +#include +#ifndef Q_MOC_RUN #include +#endif +#include "model/settings/usersettings.hpp" #include "model/doc/documentmanager.hpp" #include "view/doc/viewmanager.hpp" #include "view/doc/startup.hpp" #include "view/doc/filedialog.hpp" +#include "view/doc/newgame.hpp" + +#include "view/settings/usersettingsdialog.hpp" namespace CS { @@ -17,12 +26,16 @@ namespace CS { Q_OBJECT + CSMSettings::UserSettings mUserSettings; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; + CSVDoc::NewGameDialogue mNewGame; + CSVSettings::UserSettingsDialog mSettings; FileDialog mFileDialog; Files::ConfigurationManager mCfgMgr; + boost::filesystem::path mLocal; void setupDataFiles(); @@ -34,16 +47,31 @@ namespace CS Editor(); + bool makeIPCServer(); + void connectToIPCServer(); + int run(); ///< \return error status private slots: - void createDocument(); + void createGame(); + void createAddon(); void loadDocument(); void openFiles(); void createNewFile(); + void createNewGame (const boost::filesystem::path& file); + + void showStartup(); + + void showSettings(); + + private: + + QString mIpcServerName; + QLocalServer *mServer; + QLocalSocket *mClientSocket; }; } diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index aa315804b..ef7123c20 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include class Application : public QApplication @@ -39,5 +39,11 @@ int main(int argc, char *argv[]) CS::Editor editor; + if(!editor.makeIPCServer()) + { + editor.connectToIPCServer(); + return 0; + } + return editor.run(); } diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 11d877d0b..d7138f671 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,6 +1,4 @@ - #include "document.hpp" - #include void CSMDoc::Document::load (const std::vector::const_iterator& begin, @@ -20,6 +18,1927 @@ void CSMDoc::Document::load (const std::vector::const_i getData().loadFile (*end2, false); } +void CSMDoc::Document::addGmsts() +{ + static const char *gmstFloats[] = + { + "fAIFleeFleeMult", + "fAIFleeHealthMult", + "fAIMagicSpellMult", + "fAIMeleeArmorMult", + "fAIMeleeSummWeaponMult", + "fAIMeleeWeaponMult", + "fAIRangeMagicSpellMult", + "fAIRangeMeleeWeaponMult", + "fAlarmRadius", + "fAthleticsRunBonus", + "fAudioDefaultMaxDistance", + "fAudioDefaultMinDistance", + "fAudioMaxDistanceMult", + "fAudioMinDistanceMult", + "fAudioVoiceDefaultMaxDistance", + "fAudioVoiceDefaultMinDistance", + "fAutoPCSpellChance", + "fAutoSpellChance", + "fBargainOfferBase", + "fBargainOfferMulti", + "fBarterGoldResetDelay", + "fBaseRunMultiplier", + "fBlockStillBonus", + "fBribe1000Mod", + "fBribe100Mod", + "fBribe10Mod", + "fCombatAngleXY", + "fCombatAngleZ", + "fCombatArmorMinMult", + "fCombatBlockLeftAngle", + "fCombatBlockRightAngle", + "fCombatCriticalStrikeMult", + "fCombatDelayCreature", + "fCombatDelayNPC", + "fCombatDistance", + "fCombatDistanceWerewolfMod", + "fCombatForceSideAngle", + "fCombatInvisoMult", + "fCombatKODamageMult", + "fCombatTorsoSideAngle", + "fCombatTorsoStartPercent", + "fCombatTorsoStopPercent", + "fConstantEffectMult", + "fCorpseClearDelay", + "fCorpseRespawnDelay", + "fCrimeGoldDiscountMult", + "fCrimeGoldTurnInMult", + "fCrimeStealing", + "fDamageStrengthBase", + "fDamageStrengthMult", + "fDifficultyMult", + "fDiseaseXferChance", + "fDispAttacking", + "fDispBargainFailMod", + "fDispBargainSuccessMod", + "fDispCrimeMod", + "fDispDiseaseMod", + "fDispFactionMod", + "fDispFactionRankBase", + "fDispFactionRankMult", + "fDispositionMod", + "fDispPersonalityBase", + "fDispPersonalityMult", + "fDispPickPocketMod", + "fDispRaceMod", + "fDispStealing", + "fDispWeaponDrawn", + "fEffectCostMult", + "fElementalShieldMult", + "fEnchantmentChanceMult", + "fEnchantmentConstantChanceMult", + "fEnchantmentConstantDurationMult", + "fEnchantmentMult", + "fEnchantmentValueMult", + "fEncumberedMoveEffect", + "fEncumbranceStrMult", + "fEndFatigueMult", + "fFallAcroBase", + "fFallAcroMult", + "fFallDamageDistanceMin", + "fFallDistanceBase", + "fFallDistanceMult", + "fFatigueAttackBase", + "fFatigueAttackMult", + "fFatigueBase", + "fFatigueBlockBase", + "fFatigueBlockMult", + "fFatigueJumpBase", + "fFatigueJumpMult", + "fFatigueMult", + "fFatigueReturnBase", + "fFatigueReturnMult", + "fFatigueRunBase", + "fFatigueRunMult", + "fFatigueSneakBase", + "fFatigueSneakMult", + "fFatigueSpellBase", + "fFatigueSpellCostMult", + "fFatigueSpellMult", + "fFatigueSwimRunBase", + "fFatigueSwimRunMult", + "fFatigueSwimWalkBase", + "fFatigueSwimWalkMult", + "fFightDispMult", + "fFightDistanceMultiplier", + "fFightStealing", + "fFleeDistance", + "fGreetDistanceReset", + "fHandtoHandHealthPer", + "fHandToHandReach", + "fHoldBreathEndMult", + "fHoldBreathTime", + "fIdleChanceMultiplier", + "fIngredientMult", + "fInteriorHeadTrackMult", + "fJumpAcrobaticsBase", + "fJumpAcroMultiplier", + "fJumpEncumbranceBase", + "fJumpEncumbranceMultiplier", + "fJumpMoveBase", + "fJumpMoveMult", + "fJumpRunMultiplier", + "fKnockDownMult", + "fLevelMod", + "fLevelUpHealthEndMult", + "fLightMaxMod", + "fLuckMod", + "fMagesGuildTravel", + "fMagicCreatureCastDelay", + "fMagicDetectRefreshRate", + "fMagicItemConstantMult", + "fMagicItemCostMult", + "fMagicItemOnceMult", + "fMagicItemPriceMult", + "fMagicItemRechargePerSecond", + "fMagicItemStrikeMult", + "fMagicItemUsedMult", + "fMagicStartIconBlink", + "fMagicSunBlockedMult", + "fMajorSkillBonus", + "fMaxFlySpeed", + "fMaxHandToHandMult", + "fMaxHeadTrackDistance", + "fMaxWalkSpeed", + "fMaxWalkSpeedCreature", + "fMedMaxMod", + "fMessageTimePerChar", + "fMinFlySpeed", + "fMinHandToHandMult", + "fMinorSkillBonus", + "fMinWalkSpeed", + "fMinWalkSpeedCreature", + "fMiscSkillBonus", + "fNPCbaseMagickaMult", + "fNPCHealthBarFade", + "fNPCHealthBarTime", + "fPCbaseMagickaMult", + "fPerDieRollMult", + "fPersonalityMod", + "fPerTempMult", + "fPickLockMult", + "fPickPocketMod", + "fPotionMinUsefulDuration", + "fPotionStrengthMult", + "fPotionT1DurMult", + "fPotionT1MagMult", + "fPotionT4BaseStrengthMult", + "fPotionT4EquipStrengthMult", + "fProjectileMaxSpeed", + "fProjectileMinSpeed", + "fProjectileThrownStoreChance", + "fRepairAmountMult", + "fRepairMult", + "fReputationMod", + "fRestMagicMult", + "fSeriousWoundMult", + "fSleepRandMod", + "fSleepRestMod", + "fSneakBootMult", + "fSneakDistanceBase", + "fSneakDistanceMultiplier", + "fSneakNoViewMult", + "fSneakSkillMult", + "fSneakSpeedMultiplier", + "fSneakUseDelay", + "fSneakUseDist", + "fSneakViewMult", + "fSoulGemMult", + "fSpecialSkillBonus", + "fSpellMakingValueMult", + "fSpellPriceMult", + "fSpellValueMult", + "fStromWalkMult", + "fStromWindSpeed", + "fSuffocationDamage", + "fSwimHeightScale", + "fSwimRunAthleticsMult", + "fSwimRunBase", + "fSwimWalkAthleticsMult", + "fSwimWalkBase", + "fSwingBlockBase", + "fSwingBlockMult", + "fTargetSpellMaxSpeed", + "fThrownWeaponMaxSpeed", + "fThrownWeaponMinSpeed", + "fTrapCostMult", + "fTravelMult", + "fTravelTimeMult", + "fUnarmoredBase1", + "fUnarmoredBase2", + "fVanityDelay", + "fVoiceIdleOdds", + "fWaterReflectUpdateAlways", + "fWaterReflectUpdateSeldom", + "fWeaponDamageMult", + "fWeaponFatigueBlockMult", + "fWeaponFatigueMult", + "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", + "fWortChanceValue", + 0 + }; + + static const float gmstFloatsValues[] = + { + 0.3, // fAIFleeFleeMult + 7.0, // fAIFleeHealthMult + 3.0, // fAIMagicSpellMult + 1.0, // fAIMeleeArmorMult + 1.0, // fAIMeleeSummWeaponMult + 2.0, // fAIMeleeWeaponMult + 5.0, // fAIRangeMagicSpellMult + 5.0, // fAIRangeMeleeWeaponMult + 2000.0, // fAlarmRadius + 1.0, // fAthleticsRunBonus + 40.0, // fAudioDefaultMaxDistance + 5.0, // fAudioDefaultMinDistance + 50.0, // fAudioMaxDistanceMult + 20.0, // fAudioMinDistanceMult + 60.0, // fAudioVoiceDefaultMaxDistance + 10.0, // fAudioVoiceDefaultMinDistance + 50.0, // fAutoPCSpellChance + 80.0, // fAutoSpellChance + 50.0, // fBargainOfferBase + -4.0, // fBargainOfferMulti + 24.0, // fBarterGoldResetDelay + 1.75, // fBaseRunMultiplier + 1.25, // fBlockStillBonus + 150.0, // fBribe1000Mod + 75.0, // fBribe100Mod + 35.0, // fBribe10Mod + 60.0, // fCombatAngleXY + 60.0, // fCombatAngleZ + 0.25, // fCombatArmorMinMult + -90.0, // fCombatBlockLeftAngle + 30.0, // fCombatBlockRightAngle + 4.0, // fCombatCriticalStrikeMult + 0.1, // fCombatDelayCreature + 0.1, // fCombatDelayNPC + 128.0, // fCombatDistance + 0.3, // fCombatDistanceWerewolfMod + 30.0, // fCombatForceSideAngle + 0.2, // fCombatInvisoMult + 1.5, // fCombatKODamageMult + 45.0, // fCombatTorsoSideAngle + 0.3, // fCombatTorsoStartPercent + 0.8, // fCombatTorsoStopPercent + 15.0, // fConstantEffectMult + 72.0, // fCorpseClearDelay + 72.0, // fCorpseRespawnDelay + 0.5, // fCrimeGoldDiscountMult + 0.9, // fCrimeGoldTurnInMult + 1.0, // fCrimeStealing + 0.5, // fDamageStrengthBase + 0.1, // fDamageStrengthMult + 5.0, // fDifficultyMult + 2.5, // fDiseaseXferChance + -10.0, // fDispAttacking + -1.0, // fDispBargainFailMod + 1.0, // fDispBargainSuccessMod + 0.0, // fDispCrimeMod + -10.0, // fDispDiseaseMod + 3.0, // fDispFactionMod + 1.0, // fDispFactionRankBase + 0.5, // fDispFactionRankMult + 1.0, // fDispositionMod + 50.0, // fDispPersonalityBase + 0.5, // fDispPersonalityMult + -25.0, // fDispPickPocketMod + 5.0, // fDispRaceMod + -0.5, // fDispStealing + -5.0, // fDispWeaponDrawn + 0.5, // fEffectCostMult + 0.1, // fElementalShieldMult + 3.0, // fEnchantmentChanceMult + 0.5, // fEnchantmentConstantChanceMult + 100.0, // fEnchantmentConstantDurationMult + 0.1, // fEnchantmentMult + 1000.0, // fEnchantmentValueMult + 0.3, // fEncumberedMoveEffect + 5.0, // fEncumbranceStrMult + 0.04, // fEndFatigueMult + 0.25, // fFallAcroBase + 0.01, // fFallAcroMult + 400.0, // fFallDamageDistanceMin + 0.0, // fFallDistanceBase + 0.07, // fFallDistanceMult + 2.0, // fFatigueAttackBase + 0.0, // fFatigueAttackMult + 1.25, // fFatigueBase + 4.0, // fFatigueBlockBase + 0.0, // fFatigueBlockMult + 5.0, // fFatigueJumpBase + 0.0, // fFatigueJumpMult + 0.5, // fFatigueMult + 2.5, // fFatigueReturnBase + 0.02, // fFatigueReturnMult + 5.0, // fFatigueRunBase + 2.0, // fFatigueRunMult + 1.5, // fFatigueSneakBase + 1.5, // fFatigueSneakMult + 0.0, // fFatigueSpellBase + 0.0, // fFatigueSpellCostMult + 0.0, // fFatigueSpellMult + 7.0, // fFatigueSwimRunBase + 0.0, // fFatigueSwimRunMult + 2.5, // fFatigueSwimWalkBase + 0.0, // fFatigueSwimWalkMult + 0.2, // fFightDispMult + 0.005, // fFightDistanceMultiplier + 50.0, // fFightStealing + 3000.0, // fFleeDistance + 512.0, // fGreetDistanceReset + 0.1, // fHandtoHandHealthPer + 1.0, // fHandToHandReach + 0.5, // fHoldBreathEndMult + 20.0, // fHoldBreathTime + 0.75, // fIdleChanceMultiplier + 1.0, // fIngredientMult + 0.5, // fInteriorHeadTrackMult + 128.0, // fJumpAcrobaticsBase + 4.0, // fJumpAcroMultiplier + 0.5, // fJumpEncumbranceBase + 1.0, // fJumpEncumbranceMultiplier + 0.5, // fJumpMoveBase + 0.5, // fJumpMoveMult + 1.0, // fJumpRunMultiplier + 0.5, // fKnockDownMult + 5.0, // fLevelMod + 0.1, // fLevelUpHealthEndMult + 0.6, // fLightMaxMod + 10.0, // fLuckMod + 10.0, // fMagesGuildTravel + 1.5, // fMagicCreatureCastDelay + 0.0167, // fMagicDetectRefreshRate + 1.0, // fMagicItemConstantMult + 1.0, // fMagicItemCostMult + 1.0, // fMagicItemOnceMult + 1.0, // fMagicItemPriceMult + 0.05, // fMagicItemRechargePerSecond + 1.0, // fMagicItemStrikeMult + 1.0, // fMagicItemUsedMult + 3.0, // fMagicStartIconBlink + 0.5, // fMagicSunBlockedMult + 0.75, // fMajorSkillBonus + 300.0, // fMaxFlySpeed + 0.5, // fMaxHandToHandMult + 400.0, // fMaxHeadTrackDistance + 200.0, // fMaxWalkSpeed + 300.0, // fMaxWalkSpeedCreature + 0.9, // fMedMaxMod + 0.1, // fMessageTimePerChar + 5.0, // fMinFlySpeed + 0.1, // fMinHandToHandMult + 1.0, // fMinorSkillBonus + 100.0, // fMinWalkSpeed + 5.0, // fMinWalkSpeedCreature + 1.25, // fMiscSkillBonus + 2.0, // fNPCbaseMagickaMult + 0.5, // fNPCHealthBarFade + 3.0, // fNPCHealthBarTime + 1.0, // fPCbaseMagickaMult + 0.3, // fPerDieRollMult + 5.0, // fPersonalityMod + 1.0, // fPerTempMult + -1.0, // fPickLockMult + 0.3, // fPickPocketMod + 20.0, // fPotionMinUsefulDuration + 0.5, // fPotionStrengthMult + 0.5, // fPotionT1DurMult + 1.5, // fPotionT1MagMult + 20.0, // fPotionT4BaseStrengthMult + 12.0, // fPotionT4EquipStrengthMult + 3000.0, // fProjectileMaxSpeed + 400.0, // fProjectileMinSpeed + 25.0, // fProjectileThrownStoreChance + 3.0, // fRepairAmountMult + 1.0, // fRepairMult + 1.0, // fReputationMod + 0.15, // fRestMagicMult + 0.0, // fSeriousWoundMult + 0.25, // fSleepRandMod + 0.3, // fSleepRestMod + -1.0, // fSneakBootMult + 0.5, // fSneakDistanceBase + 0.002, // fSneakDistanceMultiplier + 0.5, // fSneakNoViewMult + 1.0, // fSneakSkillMult + 0.75, // fSneakSpeedMultiplier + 1.0, // fSneakUseDelay + 500.0, // fSneakUseDist + 1.5, // fSneakViewMult + 3.0, // fSoulGemMult + 0.8, // fSpecialSkillBonus + 7.0, // fSpellMakingValueMult + 2.0, // fSpellPriceMult + 10.0, // fSpellValueMult + 0.25, // fStromWalkMult + 0.7, // fStromWindSpeed + 3.0, // fSuffocationDamage + 0.9, // fSwimHeightScale + 0.1, // fSwimRunAthleticsMult + 0.5, // fSwimRunBase + 0.02, // fSwimWalkAthleticsMult + 0.5, // fSwimWalkBase + 1.0, // fSwingBlockBase + 1.0, // fSwingBlockMult + 1000.0, // fTargetSpellMaxSpeed + 1000.0, // fThrownWeaponMaxSpeed + 300.0, // fThrownWeaponMinSpeed + 0.0, // fTrapCostMult + 4000.0, // fTravelMult + 16000.0,// fTravelTimeMult + 0.1, // fUnarmoredBase1 + 0.065, // fUnarmoredBase2 + 30.0, // fVanityDelay + 10.0, // fVoiceIdleOdds + 0.0, // fWaterReflectUpdateAlways + 10.0, // fWaterReflectUpdateSeldom + 0.1, // fWeaponDamageMult + 1.0, // fWeaponFatigueBlockMult + 0.25, // fWeaponFatigueMult + 150.0, // fWereWolfAcrobatics + 150.0, // fWereWolfAgility + 1.0, // fWereWolfAlchemy + 1.0, // fWereWolfAlteration + 1.0, // fWereWolfArmorer + 150.0, // fWereWolfAthletics + 1.0, // fWereWolfAxe + 1.0, // fWereWolfBlock + 1.0, // fWereWolfBluntWeapon + 1.0, // fWereWolfConjuration + 1.0, // fWereWolfDestruction + 1.0, // fWereWolfEnchant + 150.0, // fWereWolfEndurance + 400.0, // fWereWolfFatigue + 100.0, // fWereWolfHandtoHand + 2.0, // fWereWolfHealth + 1.0, // fWereWolfHeavyArmor + 1.0, // fWereWolfIllusion + 1.0, // fWereWolfIntellegence + 1.0, // fWereWolfLightArmor + 1.0, // fWereWolfLongBlade + 1.0, // fWereWolfLuck + 100.0, // fWereWolfMagicka + 1.0, // fWereWolfMarksman + 1.0, // fWereWolfMediumArmor + 1.0, // fWereWolfMerchantile + 1.0, // fWereWolfMysticism + 1.0, // fWereWolfPersonality + 1.0, // fWereWolfRestoration + 1.5, // fWereWolfRunMult + 1.0, // fWereWolfSecurity + 1.0, // fWereWolfShortBlade + 1.5, // fWereWolfSilverWeaponDamageMult + 1.0, // fWereWolfSneak + 1.0, // fWereWolfSpear + 1.0, // fWereWolfSpeechcraft + 150.0, // fWereWolfSpeed + 150.0, // fWereWolfStrength + 100.0, // fWereWolfUnarmored + 1.0, // fWereWolfWillPower + 15.0, // fWortChanceValue + }; + + static const char *gmstIntegers[] = + { + "i1stPersonSneakDelta", + "iAlarmAttack", + "iAlarmKilling", + "iAlarmPickPocket", + "iAlarmStealing", + "iAlarmTresspass", + "iAlchemyMod", + "iAutoPCSpellMax", + "iAutoRepFacMod", + "iAutoRepLevMod", + "iAutoSpellAlterationMax", + "iAutoSpellAttSkillMin", + "iAutoSpellConjurationMax", + "iAutoSpellDestructionMax", + "iAutoSpellIllusionMax", + "iAutoSpellMysticismMax", + "iAutoSpellRestorationMax", + "iAutoSpellTimesCanCast", + "iBarterFailDisposition", + "iBarterSuccessDisposition", + "iBaseArmorSkill", + "iBlockMaxChance", + "iBlockMinChance", + "iBootsWeight", + "iCrimeAttack", + "iCrimeKilling", + "iCrimePickPocket", + "iCrimeThreshold", + "iCrimeThresholdMultiplier", + "iCrimeTresspass", + "iCuirassWeight", + "iDaysinPrisonMod", + "iDispAttackMod", + "iDispKilling", + "iDispTresspass", + "iFightAlarmMult", + "iFightAttack", + "iFightAttacking", + "iFightDistanceBase", + "iFightKilling", + "iFightPickpocket", + "iFightTrespass", + "iFlee", + "iGauntletWeight", + "iGreavesWeight", + "iGreetDistanceMultiplier", + "iGreetDuration", + "iHelmWeight", + "iKnockDownOddsBase", + "iKnockDownOddsMult", + "iLevelUp01Mult", + "iLevelUp02Mult", + "iLevelUp03Mult", + "iLevelUp04Mult", + "iLevelUp05Mult", + "iLevelUp06Mult", + "iLevelUp07Mult", + "iLevelUp08Mult", + "iLevelUp09Mult", + "iLevelUp10Mult", + "iLevelupMajorMult", + "iLevelupMajorMultAttribute", + "iLevelupMinorMult", + "iLevelupMinorMultAttribute", + "iLevelupMiscMultAttriubte", + "iLevelupSpecialization", + "iLevelupTotal", + "iMagicItemChargeConst", + "iMagicItemChargeOnce", + "iMagicItemChargeStrike", + "iMagicItemChargeUse", + "iMaxActivateDist", + "iMaxInfoDist", + "iMonthsToRespawn", + "iNumberCreatures", + "iPauldronWeight", + "iPerMinChance", + "iPerMinChange", + "iPickMaxChance", + "iPickMinChance", + "iShieldWeight", + "iSoulAmountForConstantEffect", + "iTrainingMod", + "iVoiceAttackOdds", + "iVoiceHitOdds", + "iWereWolfBounty", + "iWereWolfFightMod", + "iWereWolfFleeMod", + "iWereWolfLevelToAttack", + 0 + }; + + static const int gmstIntegersValues[] = + { + 10, // i1stPersonSneakDelta + 50, // iAlarmAttack + 90, // iAlarmKilling + 20, // iAlarmPickPocket + 1, // iAlarmStealing + 5, // iAlarmTresspass + 2, // iAlchemyMod + 100, // iAutoPCSpellMax + 2, // iAutoRepFacMod + 0, // iAutoRepLevMod + 5, // iAutoSpellAlterationMax + 70, // iAutoSpellAttSkillMin + 2, // iAutoSpellConjurationMax + 5, // iAutoSpellDestructionMax + 5, // iAutoSpellIllusionMax + 5, // iAutoSpellMysticismMax + 5, // iAutoSpellRestorationMax + 3, // iAutoSpellTimesCanCast + -1, // iBarterFailDisposition + 1, // iBarterSuccessDisposition + 30, // iBaseArmorSkill + 50, // iBlockMaxChance + 10, // iBlockMinChance + 20, // iBootsWeight + 40, // iCrimeAttack + 1000, // iCrimeKilling + 25, // iCrimePickPocket + 1000, // iCrimeThreshold + 10, // iCrimeThresholdMultiplier + 5, // iCrimeTresspass + 30, // iCuirassWeight + 100, // iDaysinPrisonMod + -50, // iDispAttackMod + -50, // iDispKilling + -20, // iDispTresspass + 1, // iFightAlarmMult + 100, // iFightAttack + 50, // iFightAttacking + 20, // iFightDistanceBase + 50, // iFightKilling + 25, // iFightPickpocket + 25, // iFightTrespass + 0, // iFlee + 5, // iGauntletWeight + 15, // iGreavesWeight + 6, // iGreetDistanceMultiplier + 4, // iGreetDuration + 5, // iHelmWeight + 50, // iKnockDownOddsBase + 50, // iKnockDownOddsMult + 2, // iLevelUp01Mult + 2, // iLevelUp02Mult + 2, // iLevelUp03Mult + 2, // iLevelUp04Mult + 3, // iLevelUp05Mult + 3, // iLevelUp06Mult + 3, // iLevelUp07Mult + 4, // iLevelUp08Mult + 4, // iLevelUp09Mult + 5, // iLevelUp10Mult + 1, // iLevelupMajorMult + 1, // iLevelupMajorMultAttribute + 1, // iLevelupMinorMult + 1, // iLevelupMinorMultAttribute + 1, // iLevelupMiscMultAttriubte + 1, // iLevelupSpecialization + 10, // iLevelupTotal + 10, // iMagicItemChargeConst + 1, // iMagicItemChargeOnce + 10, // iMagicItemChargeStrike + 5, // iMagicItemChargeUse + 192, // iMaxActivateDist + 192, // iMaxInfoDist + 4, // iMonthsToRespawn + 1, // iNumberCreatures + 10, // iPauldronWeight + 5, // iPerMinChance + 10, // iPerMinChange + 75, // iPickMaxChance + 5, // iPickMinChance + 15, // iShieldWeight + 400, // iSoulAmountForConstantEffect + 10, // iTrainingMod + 10, // iVoiceAttackOdds + 30, // iVoiceHitOdds + 10000, // iWereWolfBounty + 100, // iWereWolfFightMod + 100, // iWereWolfFleeMod + 20, // iWereWolfLevelToAttack + }; + + static const char *gmstStrings[] = + { + "s3dAudio", + "s3dHardware", + "s3dSoftware", + "sAbsorb", + "sAcrobat", + "sActivate", + "sActivateXbox", + "sActorInCombat", + "sAdmire", + "sAdmireFail", + "sAdmireSuccess", + "sAgent", + "sAgiDesc", + "sAIDistance", + "sAlembic", + "sAllTab", + "sAlways", + "sAlways_Run", + "sand", + "sApparatus", + "sApparelTab", + "sArcher", + "sArea", + "sAreaDes", + "sArmor", + "sArmorRating", + "sAsk", + "sAssassin", + "sAt", + "sAttack", + "sAttributeAgility", + "sAttributeEndurance", + "sAttributeIntelligence", + "sAttributeListTitle", + "sAttributeLuck", + "sAttributePersonality", + "sAttributesMenu1", + "sAttributeSpeed", + "sAttributeStrength", + "sAttributeWillpower", + "sAudio", + "sAuto_Run", + "sBack", + "sBackspace", + "sBackXbox", + "sBarbarian", + "sBard", + "sBarter", + "sBarterDialog1", + "sBarterDialog10", + "sBarterDialog11", + "sBarterDialog12", + "sBarterDialog2", + "sBarterDialog3", + "sBarterDialog4", + "sBarterDialog5", + "sBarterDialog6", + "sBarterDialog7", + "sBarterDialog8", + "sBarterDialog9", + "sBattlemage", + "sBestAttack", + "sBirthSign", + "sBirthsignmenu1", + "sBirthsignmenu2", + "sBlocks", + "sBonusSkillTitle", + "sBookPageOne", + "sBookPageTwo", + "sBookSkillMessage", + "sBounty", + "sBreath", + "sBribe", + "sBribe", + "sBribe", + "sBribeFail", + "sBribeSuccess", + "sBuy", + "sBye", + "sCalcinator", + "sCancel", + "sCantEquipWeapWarning", + "sCastCost", + "sCaughtStealingMessage", + "sCenter", + "sChangedMastersMsg", + "sCharges", + "sChooseClassMenu1", + "sChooseClassMenu2", + "sChooseClassMenu3", + "sChooseClassMenu4", + "sChop", + "sClass", + "sClassChoiceMenu1", + "sClassChoiceMenu2", + "sClassChoiceMenu3", + "sClose", + "sCompanionShare", + "sCompanionWarningButtonOne", + "sCompanionWarningButtonTwo", + "sCompanionWarningMessage", + "sCondition", + "sConsoleTitle", + "sContainer", + "sContentsMessage1", + "sContentsMessage2", + "sContentsMessage3", + "sControlerVibration", + "sControls", + "sControlsMenu1", + "sControlsMenu2", + "sControlsMenu3", + "sControlsMenu4", + "sControlsMenu5", + "sControlsMenu6", + "sCostChance", + "sCostCharge", + "sCreate", + "sCreateClassMenu1", + "sCreateClassMenu2", + "sCreateClassMenu3", + "sCreateClassMenuHelp1", + "sCreateClassMenuHelp2", + "sCreateClassMenuWarning", + "sCreatedEffects", + "sCrimeHelp", + "sCrimeMessage", + "sCrouch_Sneak", + "sCrouchXbox", + "sCrusader", + "sCursorOff", + "sCustom", + "sCustomClassName", + "sDamage", + "sDark_Gamma", + "sDay", + "sDefaultCellname", + "sDelete", + "sDeleteGame", + "sDeleteNote", + "sDeleteSpell", + "sDeleteSpellError", + "sDetail_Level", + "sDialogMenu1", + "sDialogText1Xbox", + "sDialogText2Xbox", + "sDialogText3Xbox", + "sDifficulty", + "sDisposeCorpseFail", + "sDisposeofCorpse", + "sDone", + "sDoYouWantTo", + "sDrain", + "sDrop", + "sDuration", + "sDurationDes", + "sEasy", + "sEditNote", + "sEffectAbsorbAttribute", + "sEffectAbsorbFatigue", + "sEffectAbsorbHealth", + "sEffectAbsorbSkill", + "sEffectAbsorbSpellPoints", + "sEffectAlmsiviIntervention", + "sEffectBlind", + "sEffectBoundBattleAxe", + "sEffectBoundBoots", + "sEffectBoundCuirass", + "sEffectBoundDagger", + "sEffectBoundGloves", + "sEffectBoundHelm", + "sEffectBoundLongbow", + "sEffectBoundLongsword", + "sEffectBoundMace", + "sEffectBoundShield", + "sEffectBoundSpear", + "sEffectBurden", + "sEffectCalmCreature", + "sEffectCalmHumanoid", + "sEffectChameleon", + "sEffectCharm", + "sEffectCommandCreatures", + "sEffectCommandHumanoids", + "sEffectCorpus", + "sEffectCureBlightDisease", + "sEffectCureCommonDisease", + "sEffectCureCorprusDisease", + "sEffectCureParalyzation", + "sEffectCurePoison", + "sEffectDamageAttribute", + "sEffectDamageFatigue", + "sEffectDamageHealth", + "sEffectDamageMagicka", + "sEffectDamageSkill", + "sEffectDemoralizeCreature", + "sEffectDemoralizeHumanoid", + "sEffectDetectAnimal", + "sEffectDetectEnchantment", + "sEffectDetectKey", + "sEffectDisintegrateArmor", + "sEffectDisintegrateWeapon", + "sEffectDispel", + "sEffectDivineIntervention", + "sEffectDrainAttribute", + "sEffectDrainFatigue", + "sEffectDrainHealth", + "sEffectDrainSkill", + "sEffectDrainSpellpoints", + "sEffectExtraSpell", + "sEffectFeather", + "sEffectFireDamage", + "sEffectFireShield", + "sEffectFortifyAttackBonus", + "sEffectFortifyAttribute", + "sEffectFortifyFatigue", + "sEffectFortifyHealth", + "sEffectFortifyMagickaMultiplier", + "sEffectFortifySkill", + "sEffectFortifySpellpoints", + "sEffectFrenzyCreature", + "sEffectFrenzyHumanoid", + "sEffectFrostDamage", + "sEffectFrostShield", + "sEffectInvisibility", + "sEffectJump", + "sEffectLevitate", + "sEffectLight", + "sEffectLightningShield", + "sEffectLock", + "sEffectMark", + "sEffectNightEye", + "sEffectOpen", + "sEffectParalyze", + "sEffectPoison", + "sEffectRallyCreature", + "sEffectRallyHumanoid", + "sEffectRecall", + "sEffectReflect", + "sEffectRemoveCurse", + "sEffectResistBlightDisease", + "sEffectResistCommonDisease", + "sEffectResistCorprusDisease", + "sEffectResistFire", + "sEffectResistFrost", + "sEffectResistMagicka", + "sEffectResistNormalWeapons", + "sEffectResistParalysis", + "sEffectResistPoison", + "sEffectResistShock", + "sEffectRestoreAttribute", + "sEffectRestoreFatigue", + "sEffectRestoreHealth", + "sEffectRestoreSkill", + "sEffectRestoreSpellPoints", + "sEffects", + "sEffectSanctuary", + "sEffectShield", + "sEffectShockDamage", + "sEffectSilence", + "sEffectSlowFall", + "sEffectSoultrap", + "sEffectSound", + "sEffectSpellAbsorption", + "sEffectStuntedMagicka", + "sEffectSummonAncestralGhost", + "sEffectSummonBonelord", + "sEffectSummonCenturionSphere", + "sEffectSummonClannfear", + "sEffectSummonCreature01", + "sEffectSummonCreature02", + "sEffectSummonCreature03", + "sEffectSummonCreature04", + "sEffectSummonCreature05", + "sEffectSummonDaedroth", + "sEffectSummonDremora", + "sEffectSummonFabricant", + "sEffectSummonFlameAtronach", + "sEffectSummonFrostAtronach", + "sEffectSummonGoldensaint", + "sEffectSummonGreaterBonewalker", + "sEffectSummonHunger", + "sEffectSummonLeastBonewalker", + "sEffectSummonScamp", + "sEffectSummonSkeletalMinion", + "sEffectSummonStormAtronach", + "sEffectSummonWingedTwilight", + "sEffectSunDamage", + "sEffectSwiftSwim", + "sEffectTelekinesis", + "sEffectTurnUndead", + "sEffectVampirism", + "sEffectWaterBreathing", + "sEffectWaterWalking", + "sEffectWeaknessToBlightDisease", + "sEffectWeaknessToCommonDisease", + "sEffectWeaknessToCorprusDisease", + "sEffectWeaknessToFire", + "sEffectWeaknessToFrost", + "sEffectWeaknessToMagicka", + "sEffectWeaknessToNormalWeapons", + "sEffectWeaknessToPoison", + "sEffectWeaknessToShock", + "sEnableJoystick", + "sEnchanting", + "sEnchantItems", + "sEnchantmentHelp1", + "sEnchantmentHelp10", + "sEnchantmentHelp2", + "sEnchantmentHelp3", + "sEnchantmentHelp4", + "sEnchantmentHelp5", + "sEnchantmentHelp6", + "sEnchantmentHelp7", + "sEnchantmentHelp8", + "sEnchantmentHelp9", + "sEnchantmentMenu1", + "sEnchantmentMenu10", + "sEnchantmentMenu11", + "sEnchantmentMenu12", + "sEnchantmentMenu2", + "sEnchantmentMenu3", + "sEnchantmentMenu4", + "sEnchantmentMenu5", + "sEnchantmentMenu6", + "sEnchantmentMenu7", + "sEnchantmentMenu8", + "sEnchantmentMenu9", + "sEncumbrance", + "sEndDesc", + "sEquip", + "sExitGame", + "sExpelled", + "sExpelledMessage", + "sFace", + "sFaction", + "sFar", + "sFast", + "sFatDesc", + "sFatigue", + "sFavoriteSkills", + "sfeet", + "sFileSize", + "sfootarea", + "sFootsteps", + "sfor", + "sFortify", + "sForward", + "sForwardXbox", + "sFull", + "sGame", + "sGameWithoutLauncherXbox", + "sGamma_Correction", + "sGeneralMastPlugMismatchMsg", + "sGold", + "sGoodbye", + "sGoverningAttribute", + "sgp", + "sHair", + "sHard", + "sHeal", + "sHealer", + "sHealth", + "sHealthDesc", + "sHealthPerHourOfRest", + "sHealthPerLevel", + "sHeavy", + "sHigh", + "sin", + "sInfo", + "sInfoRefusal", + "sIngredients", + "sInPrisonTitle", + "sInputMenu1", + "sIntDesc", + "sIntimidate", + "sIntimidateFail", + "sIntimidateSuccess", + "sInvalidSaveGameMsg", + "sInvalidSaveGameMsgXBOX", + "sInventory", + "sInventoryMenu1", + "sInventoryMessage1", + "sInventoryMessage2", + "sInventoryMessage3", + "sInventoryMessage4", + "sInventoryMessage5", + "sInventorySelectNoIngredients", + "sInventorySelectNoItems", + "sInventorySelectNoSoul", + "sItem", + "sItemCastConstant", + "sItemCastOnce", + "sItemCastWhenStrikes", + "sItemCastWhenUsed", + "sItemName", + "sJournal", + "sJournalCmd", + "sJournalEntry", + "sJournalXbox", + "sJoystickHatShort", + "sJoystickNotFound", + "sJoystickShort", + "sJump", + "sJumpXbox", + "sKeyName_00", + "sKeyName_01", + "sKeyName_02", + "sKeyName_03", + "sKeyName_04", + "sKeyName_05", + "sKeyName_06", + "sKeyName_07", + "sKeyName_08", + "sKeyName_09", + "sKeyName_0A", + "sKeyName_0B", + "sKeyName_0C", + "sKeyName_0D", + "sKeyName_0E", + "sKeyName_0F", + "sKeyName_10", + "sKeyName_11", + "sKeyName_12", + "sKeyName_13", + "sKeyName_14", + "sKeyName_15", + "sKeyName_16", + "sKeyName_17", + "sKeyName_18", + "sKeyName_19", + "sKeyName_1A", + "sKeyName_1B", + "sKeyName_1C", + "sKeyName_1D", + "sKeyName_1E", + "sKeyName_1F", + "sKeyName_20", + "sKeyName_21", + "sKeyName_22", + "sKeyName_23", + "sKeyName_24", + "sKeyName_25", + "sKeyName_26", + "sKeyName_27", + "sKeyName_28", + "sKeyName_29", + "sKeyName_2A", + "sKeyName_2B", + "sKeyName_2C", + "sKeyName_2D", + "sKeyName_2E", + "sKeyName_2F", + "sKeyName_30", + "sKeyName_31", + "sKeyName_32", + "sKeyName_33", + "sKeyName_34", + "sKeyName_35", + "sKeyName_36", + "sKeyName_37", + "sKeyName_38", + "sKeyName_39", + "sKeyName_3A", + "sKeyName_3B", + "sKeyName_3C", + "sKeyName_3D", + "sKeyName_3E", + "sKeyName_3F", + "sKeyName_40", + "sKeyName_41", + "sKeyName_42", + "sKeyName_43", + "sKeyName_44", + "sKeyName_45", + "sKeyName_46", + "sKeyName_47", + "sKeyName_48", + "sKeyName_49", + "sKeyName_4A", + "sKeyName_4B", + "sKeyName_4C", + "sKeyName_4D", + "sKeyName_4E", + "sKeyName_4F", + "sKeyName_50", + "sKeyName_51", + "sKeyName_52", + "sKeyName_53", + "sKeyName_54", + "sKeyName_55", + "sKeyName_56", + "sKeyName_57", + "sKeyName_58", + "sKeyName_59", + "sKeyName_5A", + "sKeyName_5B", + "sKeyName_5C", + "sKeyName_5D", + "sKeyName_5E", + "sKeyName_5F", + "sKeyName_60", + "sKeyName_61", + "sKeyName_62", + "sKeyName_63", + "sKeyName_64", + "sKeyName_65", + "sKeyName_66", + "sKeyName_67", + "sKeyName_68", + "sKeyName_69", + "sKeyName_6A", + "sKeyName_6B", + "sKeyName_6C", + "sKeyName_6D", + "sKeyName_6E", + "sKeyName_6F", + "sKeyName_70", + "sKeyName_71", + "sKeyName_72", + "sKeyName_73", + "sKeyName_74", + "sKeyName_75", + "sKeyName_76", + "sKeyName_77", + "sKeyName_78", + "sKeyName_79", + "sKeyName_7A", + "sKeyName_7B", + "sKeyName_7C", + "sKeyName_7D", + "sKeyName_7E", + "sKeyName_7F", + "sKeyName_80", + "sKeyName_81", + "sKeyName_82", + "sKeyName_83", + "sKeyName_84", + "sKeyName_85", + "sKeyName_86", + "sKeyName_87", + "sKeyName_88", + "sKeyName_89", + "sKeyName_8A", + "sKeyName_8B", + "sKeyName_8C", + "sKeyName_8D", + "sKeyName_8E", + "sKeyName_8F", + "sKeyName_90", + "sKeyName_91", + "sKeyName_92", + "sKeyName_93", + "sKeyName_94", + "sKeyName_95", + "sKeyName_96", + "sKeyName_97", + "sKeyName_98", + "sKeyName_99", + "sKeyName_9A", + "sKeyName_9B", + "sKeyName_9C", + "sKeyName_9D", + "sKeyName_9E", + "sKeyName_9F", + "sKeyName_A0", + "sKeyName_A1", + "sKeyName_A2", + "sKeyName_A3", + "sKeyName_A4", + "sKeyName_A5", + "sKeyName_A6", + "sKeyName_A7", + "sKeyName_A8", + "sKeyName_A9", + "sKeyName_AA", + "sKeyName_AB", + "sKeyName_AC", + "sKeyName_AD", + "sKeyName_AE", + "sKeyName_AF", + "sKeyName_B0", + "sKeyName_B1", + "sKeyName_B2", + "sKeyName_B3", + "sKeyName_B4", + "sKeyName_B5", + "sKeyName_B6", + "sKeyName_B7", + "sKeyName_B8", + "sKeyName_B9", + "sKeyName_BA", + "sKeyName_BB", + "sKeyName_BC", + "sKeyName_BD", + "sKeyName_BE", + "sKeyName_BF", + "sKeyName_C0", + "sKeyName_C1", + "sKeyName_C2", + "sKeyName_C3", + "sKeyName_C4", + "sKeyName_C5", + "sKeyName_C6", + "sKeyName_C7", + "sKeyName_C8", + "sKeyName_C9", + "sKeyName_CA", + "sKeyName_CB", + "sKeyName_CC", + "sKeyName_CD", + "sKeyName_CE", + "sKeyName_CF", + "sKeyName_D0", + "sKeyName_D1", + "sKeyName_D2", + "sKeyName_D3", + "sKeyName_D4", + "sKeyName_D5", + "sKeyName_D6", + "sKeyName_D7", + "sKeyName_D8", + "sKeyName_D9", + "sKeyName_DA", + "sKeyName_DB", + "sKeyName_DC", + "sKeyName_DD", + "sKeyName_DE", + "sKeyName_DF", + "sKeyName_E0", + "sKeyName_E1", + "sKeyName_E2", + "sKeyName_E3", + "sKeyName_E4", + "sKeyName_E5", + "sKeyName_E6", + "sKeyName_E7", + "sKeyName_E8", + "sKeyName_E9", + "sKeyName_EA", + "sKeyName_EB", + "sKeyName_EC", + "sKeyName_ED", + "sKeyName_EE", + "sKeyName_EF", + "sKeyName_F0", + "sKeyName_F1", + "sKeyName_F2", + "sKeyName_F3", + "sKeyName_F4", + "sKeyName_F5", + "sKeyName_F6", + "sKeyName_F7", + "sKeyName_F8", + "sKeyName_F9", + "sKeyName_FA", + "sKeyName_FB", + "sKeyName_FC", + "sKeyName_FD", + "sKeyName_FE", + "sKeyName_FF", + "sKeyUsed", + "sKilledEssential", + "sKnight", + "sLeft", + "sLess", + "sLevel", + "sLevelProgress", + "sLevels", + "sLevelUp", + "sLevelUpMenu1", + "sLevelUpMenu2", + "sLevelUpMenu3", + "sLevelUpMenu4", + "sLevelUpMsg", + "sLevitateDisabled", + "sLight", + "sLight_Gamma", + "sLoadFailedMessage", + "sLoadGame", + "sLoadingErrorsMsg", + "sLoadingMessage1", + "sLoadingMessage14", + "sLoadingMessage15", + "sLoadingMessage2", + "sLoadingMessage3", + "sLoadingMessage4", + "sLoadingMessage5", + "sLoadingMessage9", + "sLoadLastSaveMsg", + "sLocal", + "sLockFail", + "sLockImpossible", + "sLockLevel", + "sLockSuccess", + "sLookDownXbox", + "sLookUpXbox", + "sLow", + "sLucDesc", + "sMagDesc", + "sMage", + "sMagic", + "sMagicAncestralGhostID", + "sMagicBonelordID", + "sMagicBoundBattleAxeID", + "sMagicBoundBootsID", + "sMagicBoundCuirassID", + "sMagicBoundDaggerID", + "sMagicBoundHelmID", + "sMagicBoundLeftGauntletID", + "sMagicBoundLongbowID", + "sMagicBoundLongswordID", + "sMagicBoundMaceID", + "sMagicBoundRightGauntletID", + "sMagicBoundShieldID", + "sMagicBoundSpearID", + "sMagicCannotRecast", + "sMagicCenturionSphereID", + "sMagicClannfearID", + "sMagicContractDisease", + "sMagicCorprusWorsens", + "sMagicCreature01ID", + "sMagicCreature02ID", + "sMagicCreature03ID", + "sMagicCreature04ID", + "sMagicCreature05ID", + "sMagicDaedrothID", + "sMagicDremoraID", + "sMagicEffects", + "sMagicFabricantID", + "sMagicFlameAtronachID", + "sMagicFrostAtronachID", + "sMagicGoldenSaintID", + "sMagicGreaterBonewalkerID", + "sMagicHungerID", + "sMagicInsufficientCharge", + "sMagicInsufficientSP", + "sMagicInvalidEffect", + "sMagicInvalidTarget", + "sMagicItem", + "sMagicLeastBonewalkerID", + "sMagicLockSuccess", + "sMagicMenu", + "sMagicOpenSuccess", + "sMagicPCResisted", + "sMagicScampID", + "sMagicSelectTitle", + "sMagicSkeletalMinionID", + "sMagicSkillFail", + "sMagicStormAtronachID", + "sMagicTab", + "sMagicTargetResisted", + "sMagicTargetResistsWeapons", + "sMagicWingedTwilightID", + "sMagnitude", + "sMagnitudeDes", + "sMake", + "sMap", + "sMaster", + "sMastPlugMismatchMsg", + "sMaximumSaveGameMessage", + "sMaxSale", + "sMedium", + "sMenu_Help_Delay", + "sMenu_Mode", + "sMenuModeXbox", + "sMenuNextXbox", + "sMenuPrevXbox", + "sMenus", + "sMessage1", + "sMessage2", + "sMessage3", + "sMessage4", + "sMessage5", + "sMessageQuestionAnswer1", + "sMessageQuestionAnswer2", + "sMessageQuestionAnswer3", + "sMiscTab", + "sMissingMastersMsg", + "sMonk", + "sMonthEveningstar", + "sMonthFirstseed", + "sMonthFrostfall", + "sMonthHeartfire", + "sMonthLastseed", + "sMonthMidyear", + "sMonthMorningstar", + "sMonthRainshand", + "sMonthSecondseed", + "sMonthSunsdawn", + "sMonthSunsdusk", + "sMonthSunsheight", + "sMore", + "sMortar", + "sMouse", + "sMouseFlip", + "sMouseWheelDownShort", + "sMouseWheelUpShort", + "sMove", + "sMoveDownXbox", + "sMoveUpXbox", + "sMusic", + "sName", + "sNameTitle", + "sNear", + "sNeedOneSkill", + "sNeedTwoSkills", + "sNewGame", + "sNext", + "sNextRank", + "sNextSpell", + "sNextSpellXbox", + "sNextWeapon", + "sNextWeaponXbox", + "sNightblade", + "sNo", + "sNoName", + "sNone", + "sNotifyMessage1", + "sNotifyMessage10", + "sNotifyMessage11", + "sNotifyMessage12", + "sNotifyMessage13", + "sNotifyMessage14", + "sNotifyMessage15", + "sNotifyMessage16", + "sNotifyMessage16_a", + "sNotifyMessage17", + "sNotifyMessage18", + "sNotifyMessage19", + "sNotifyMessage2", + "sNotifyMessage20", + "sNotifyMessage21", + "sNotifyMessage22", + "sNotifyMessage23", + "sNotifyMessage24", + "sNotifyMessage25", + "sNotifyMessage26", + "sNotifyMessage27", + "sNotifyMessage28", + "sNotifyMessage29", + "sNotifyMessage3", + "sNotifyMessage30", + "sNotifyMessage31", + "sNotifyMessage32", + "sNotifyMessage33", + "sNotifyMessage34", + "sNotifyMessage35", + "sNotifyMessage36", + "sNotifyMessage37", + "sNotifyMessage38", + "sNotifyMessage39", + "sNotifyMessage4", + "sNotifyMessage40", + "sNotifyMessage41", + "sNotifyMessage42", + "sNotifyMessage43", + "sNotifyMessage44", + "sNotifyMessage45", + "sNotifyMessage46", + "sNotifyMessage47", + "sNotifyMessage48", + "sNotifyMessage49", + "sNotifyMessage4XBOX", + "sNotifyMessage5", + "sNotifyMessage50", + "sNotifyMessage51", + "sNotifyMessage52", + "sNotifyMessage53", + "sNotifyMessage54", + "sNotifyMessage55", + "sNotifyMessage56", + "sNotifyMessage57", + "sNotifyMessage58", + "sNotifyMessage59", + "sNotifyMessage6", + "sNotifyMessage60", + "sNotifyMessage61", + "sNotifyMessage62", + "sNotifyMessage63", + "sNotifyMessage64", + "sNotifyMessage65", + "sNotifyMessage66", + "sNotifyMessage67", + "sNotifyMessage6a", + "sNotifyMessage7", + "sNotifyMessage8", + "sNotifyMessage9", + "sOff", + "sOffer", + "sOfferMenuTitle", + "sOK", + "sOn", + "sOnce", + "sOneHanded", + "sOnetypeEffectMessage", + "sonword", + "sOptions", + "sOptionsMenuXbox", + "spercent", + "sPerDesc", + "sPersuasion", + "sPersuasionMenuTitle", + "sPickUp", + "sPilgrim", + "spoint", + "spoints", + "sPotionSuccess", + "sPowerAlreadyUsed", + "sPowers", + "sPreferences", + "sPrefs", + "sPrev", + "sPrevSpell", + "sPrevSpellXbox", + "sPrevWeapon", + "sPrevWeaponXbox", + "sProfitValue", + "sQuality", + "sQuanityMenuMessage01", + "sQuanityMenuMessage02", + "sQuestionDeleteSpell", + "sQuestionMark", + "sQuick0Xbox", + "sQuick10Cmd", + "sQuick1Cmd", + "sQuick2Cmd", + "sQuick3Cmd", + "sQuick4Cmd", + "sQuick4Xbox", + "sQuick5Cmd", + "sQuick5Xbox", + "sQuick6Cmd", + "sQuick6Xbox", + "sQuick7Cmd", + "sQuick7Xbox", + "sQuick8Cmd", + "sQuick8Xbox", + "sQuick9Cmd", + "sQuick9Xbox", + "sQuick_Save", + "sQuickLoadCmd", + "sQuickLoadXbox", + "sQuickMenu", + "sQuickMenu1", + "sQuickMenu2", + "sQuickMenu3", + "sQuickMenu4", + "sQuickMenu5", + "sQuickMenu6", + "sQuickMenuInstruc", + "sQuickMenuTitle", + "sQuickSaveCmd", + "sQuickSaveXbox", + "sRace", + "sRaceMenu1", + "sRaceMenu2", + "sRaceMenu3", + "sRaceMenu4", + "sRaceMenu5", + "sRaceMenu6", + "sRaceMenu7", + "sRacialTraits", + "sRange", + "sRangeDes", + "sRangeSelf", + "sRangeTarget", + "sRangeTouch", + "sReady_Magic", + "sReady_Weapon", + "sReadyItemXbox", + "sReadyMagicXbox", + "sRechargeEnchantment", + "sRender_Distance", + "sRepair", + "sRepairFailed", + "sRepairServiceTitle", + "sRepairSuccess", + "sReputation", + "sResChangeWarning", + "sRest", + "sRestIllegal", + "sRestKey", + "sRestMenu1", + "sRestMenu2", + "sRestMenu3", + "sRestMenu4", + "sRestMenuXbox", + "sRestore", + "sRetort", + "sReturnToGame", + "sRight", + "sRogue", + "sRun", + "sRunXbox", + "sSave", + "sSaveGame", + "sSaveGameDenied", + "sSaveGameFailed", + "sSaveGameNoMemory", + "sSaveGameTooBig", + "sSaveMenu1", + "sSaveMenuHelp01", + "sSaveMenuHelp02", + "sSaveMenuHelp03", + "sSaveMenuHelp04", + "sSaveMenuHelp05", + "sSaveMenuHelp06", + "sSchool", + "sSchoolAlteration", + "sSchoolConjuration", + "sSchoolDestruction", + "sSchoolIllusion", + "sSchoolMysticism", + "sSchoolRestoration", + "sScout", + "sScrolldown", + "sScrollup", + "ssecond", + "sseconds", + "sSeldom", + "sSelect", + "sSell", + "sSellerGold", + "sService", + "sServiceRefusal", + "sServiceRepairTitle", + "sServiceSpellsTitle", + "sServiceTrainingTitle", + "sServiceTrainingWords", + "sServiceTravelTitle", + "sSetValueMessage01", + "sSex", + "sShadows", + "sShadowText", + "sShift", + "sSkill", + "sSkillAcrobatics", + "sSkillAlchemy", + "sSkillAlteration", + "sSkillArmorer", + "sSkillAthletics", + "sSkillAxe", + "sSkillBlock", + "sSkillBluntweapon", + "sSkillClassMajor", + "sSkillClassMinor", + "sSkillClassMisc", + "sSkillConjuration", + "sSkillDestruction", + "sSkillEnchant", + "sSkillHandtohand", + "sSkillHeavyarmor", + "sSkillIllusion", + "sSkillLightarmor", + "sSkillLongblade", + "sSkillMarksman", + "sSkillMaxReached", + "sSkillMediumarmor", + "sSkillMercantile", + "sSkillMysticism", + "sSkillProgress", + "sSkillRestoration", + "sSkillSecurity", + "sSkillShortblade", + "sSkillsMenu1", + "sSkillsMenuReputationHelp", + "sSkillSneak", + "sSkillSpear", + "sSkillSpeechcraft", + "sSkillUnarmored", + "sSlash", + "sSleepInterrupt", + "sSlideLeftXbox", + "sSlideRightXbox", + "sSlow", + "sSorceror", + "sSoulGem", + "sSoulGemsWithSouls", + "sSoultrapSuccess", + "sSpace", + "sSpdDesc", + "sSpecialization", + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationMenu1", + "sSpecializationStealth", + "sSpellmaking", + "sSpellmakingHelp1", + "sSpellmakingHelp2", + "sSpellmakingHelp3", + "sSpellmakingHelp4", + "sSpellmakingHelp5", + "sSpellmakingHelp6", + "sSpellmakingMenu1", + "sSpellmakingMenuTitle", + "sSpells", + "sSpellServiceTitle", + "sSpellsword", + "sStartCell", + "sStartCellError", + "sStartError", + "sStats", + "sStrafe", + "sStrDesc", + "sStrip", + "sSubtitles", + "sSystemMenuXbox", + "sTake", + "sTakeAll", + "sTargetCriticalStrike", + "sTaunt", + "sTauntFail", + "sTauntSuccess", + "sTeleportDisabled", + "sThief", + "sThrust", + "sTo", + "sTogglePOVCmd", + "sTogglePOVXbox", + "sToggleRunXbox", + "sTopics", + "sTotalCost", + "sTotalSold", + "sTraining", + "sTrainingServiceTitle", + "sTraits", + "sTransparency_Menu", + "sTrapFail", + "sTrapImpossible", + "sTrapped", + "sTrapSuccess", + "sTravel", + "sTravelServiceTitle", + "sTurn", + "sTurnLeftXbox", + "sTurnRightXbox", + "sTwoHanded", + "sType", + "sTypeAbility", + "sTypeBlightDisease", + "sTypeCurse", + "sTypeDisease", + "sTypePower", + "sTypeSpell", + "sUnequip", + "sUnlocked", + "sUntilHealed", + "sUse", + "sUserDefinedClass", + "sUses", + "sUseXbox", + "sValue", + "sVideo", + "sVideoWarning", + "sVoice", + "sWait", + "sWarrior", + "sWaterReflectUpdate", + "sWaterTerrainReflect", + "sWeaponTab", + "sWeight", + "sWerewolfAlarmMessage", + "sWerewolfPopup", + "sWerewolfRefusal", + "sWerewolfRestMessage", + "sWilDesc", + "sWitchhunter", + "sWorld", + "sWornTab", + "sXStrafe", + "sXTimes", + "sXTimesINT", + "sYes", + "sYourGold", + 0 + }; + + for (int i=0; gmstFloats[i]; i++) + { + ESM::GameSetting gmst; + gmst.mId = gmstFloats[i]; + gmst.mValue.setType (ESM::VT_Float); + gmst.mValue.setFloat (gmstFloatsValues[i]); + getData().getGmsts().add (gmst); + } + + for (int i=0; gmstIntegers[i]; i++) + { + ESM::GameSetting gmst; + gmst.mId = gmstIntegers[i]; + gmst.mValue.setType (ESM::VT_Int); + gmst.mValue.setInteger (gmstIntegersValues[i]); + getData().getGmsts().add (gmst); + } + + for (int i=0; gmstStrings[i]; i++) + { + ESM::GameSetting gmst; + gmst.mId = gmstStrings[i]; + gmst.mValue.setType (ESM::VT_String); + gmst.mValue.setString (""); + getData().getGmsts().add (gmst); + } +} + void CSMDoc::Document::addOptionalGmsts() { static const char *sFloats[] = @@ -150,6 +2069,10 @@ void CSMDoc::Document::addOptionalGlobals() 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); } } @@ -180,41 +2103,52 @@ void CSMDoc::Document::createBase() { static const char *sGlobals[] = { - "Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0 + "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); - record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Int); - - if (i==0) + if (i==0 || i==1) record.mValue.setInteger (1); getData().getGlobals().add (record); } - /// \todo add GMSTs + addGmsts(); + + 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) +CSMDoc::Document::Document (const std::vector& files, + const boost::filesystem::path& savePath, bool new_) +: mSavePath (savePath), mTools (mData) { 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 if (files.size()>1) + else { std::vector::const_iterator end = files.end(); @@ -232,11 +2166,15 @@ CSMDoc::Document::Document (const std::vector& files, b connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); - // dummy implementation -> remove when proper save is implemented. + // dummy implementation -> remove when proper save is implemented. mSaveCount = 0; connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); } +CSMDoc::Document::~Document() +{ +} + QUndoStack& CSMDoc::Document::getUndoStack() { return mUndoStack; @@ -258,9 +2196,9 @@ int CSMDoc::Document::getState() const return state; } -const std::string& CSMDoc::Document::getName() const +const boost::filesystem::path& CSMDoc::Document::getSavePath() const { - return mName; + return mSavePath; } void CSMDoc::Document::save() @@ -290,11 +2228,13 @@ void CSMDoc::Document::abortOperation (int type) } } + void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } + void CSMDoc::Document::operationDone (int type) { emit stateChanged (getState(), this); @@ -308,9 +2248,12 @@ void CSMDoc::Document::saving() if (mSaveCount>15) { + //clear the stack before resetting the save state + //to avoid emitting incorrect states + mUndoStack.setClean(); + mSaveCount = 0; mSaveTimer.stop(); - mUndoStack.setClean(); emit stateChanged (getState(), this); } } diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index a7b198689..3532721ea 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -31,7 +31,7 @@ namespace CSMDoc private: - std::string mName; ///< \todo replace name with ESX list + boost::filesystem::path mSavePath; CSMWorld::Data mData; CSMTools::Tools mTools; @@ -52,6 +52,8 @@ namespace CSMDoc void createBase(); + void addGmsts(); + void addOptionalGmsts(); void addOptionalGlobals(); @@ -62,14 +64,16 @@ namespace CSMDoc public: - Document (const std::vector& files, bool new_); + Document (const std::vector& files, + const boost::filesystem::path& savePath, bool new_); + + ~Document(); QUndoStack& getUndoStack(); int getState() const; - const std::string& getName() const; - ///< \todo replace with ESX list + const boost::filesystem::path& getSavePath() const; void save(); @@ -105,4 +109,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 740c0b582..b079109ea 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -14,10 +14,10 @@ CSMDoc::DocumentManager::~DocumentManager() delete *iter; } -CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, +CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { - Document *document = new Document (files, new_); + Document *document = new Document (files, savePath, new_); mDocuments.push_back (document); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index a307b76a5..dfded8d5c 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -23,7 +23,8 @@ namespace CSMDoc ~DocumentManager(); - Document *addDocument (const std::vector& files, bool new_); + Document *addDocument (const std::vector& files, + const boost::filesystem::path& savePath, bool new_); ///< The ownership of the returned document is not transferred to the caller. /// /// \param new_ Do not load the last content file in \a files and instead create in an diff --git a/apps/opencs/model/filter/andnode.cpp b/apps/opencs/model/filter/andnode.cpp new file mode 100644 index 000000000..dfaa56e78 --- /dev/null +++ b/apps/opencs/model/filter/andnode.cpp @@ -0,0 +1,20 @@ + +#include "andnode.hpp" + +#include + +CSMFilter::AndNode::AndNode (const std::vector >& nodes) +: NAryNode (nodes, "and") +{} + +bool CSMFilter::AndNode::test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const +{ + int size = getSize(); + + for (int i=0; i >& nodes); + + virtual bool test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const; + ///< \return Can the specified table row pass through to filter? + /// \param columns column ID to column index mapping + }; +} + +#endif diff --git a/apps/opencs/model/filter/booleannode.cpp b/apps/opencs/model/filter/booleannode.cpp new file mode 100644 index 000000000..267e06a64 --- /dev/null +++ b/apps/opencs/model/filter/booleannode.cpp @@ -0,0 +1,15 @@ + +#include "booleannode.hpp" + +CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {} + +bool CSMFilter::BooleanNode::test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const +{ + return mTrue; +} + +std::string CSMFilter::BooleanNode::toString (bool numericColumns) const +{ + return mTrue ? "true" : "false"; +} \ No newline at end of file diff --git a/apps/opencs/model/filter/booleannode.hpp b/apps/opencs/model/filter/booleannode.hpp new file mode 100644 index 000000000..d19219e35 --- /dev/null +++ b/apps/opencs/model/filter/booleannode.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_FILTER_BOOLEANNODE_H +#define CSM_FILTER_BOOLEANNODE_H + +#include "leafnode.hpp" + +namespace CSMFilter +{ + class BooleanNode : public LeafNode + { + bool mTrue; + + public: + + BooleanNode (bool true_); + + virtual bool test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const; + ///< \return Can the specified table row pass through to filter? + /// \param columns column ID to column index mapping + + virtual std::string toString (bool numericColumns) const; + ///< Return a string that represents this node. + /// + /// \param numericColumns Use numeric IDs instead of string to represent columns. + + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/filter/filter.hpp b/apps/opencs/model/filter/filter.hpp new file mode 100644 index 000000000..62170ca80 --- /dev/null +++ b/apps/opencs/model/filter/filter.hpp @@ -0,0 +1,25 @@ +#ifndef CSM_FILTER_FILTER_H +#define CSM_FILTER_FILTER_H + +#include +#include + +#include + +namespace CSMFilter +{ + /// \brief Wrapper for Filter record + struct Filter : public ESM::Filter + { + enum Scope + { + Scope_Project = 0, // per project + Scope_Session = 1, // exists only for one editing session; not saved + Scope_Content = 2 // embedded in the edited content file + }; + + Scope mScope; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/filter/leafnode.cpp b/apps/opencs/model/filter/leafnode.cpp new file mode 100644 index 000000000..055a1747c --- /dev/null +++ b/apps/opencs/model/filter/leafnode.cpp @@ -0,0 +1,8 @@ + +#include "leafnode.hpp" + +std::vector CSMFilter::LeafNode::getReferencedColumns() const +{ + return std::vector(); +} + diff --git a/apps/opencs/model/filter/leafnode.hpp b/apps/opencs/model/filter/leafnode.hpp new file mode 100644 index 000000000..2f3d91070 --- /dev/null +++ b/apps/opencs/model/filter/leafnode.hpp @@ -0,0 +1,20 @@ +#ifndef CSM_FILTER_LEAFNODE_H +#define CSM_FILTER_LEAFNODE_H + +#include + +#include "node.hpp" + +namespace CSMFilter +{ + class LeafNode : public Node + { + public: + + virtual std::vector getReferencedColumns() const; + ///< Return a list of the IDs of the columns referenced by this node. The column mapping + /// passed into test as columns must contain all columns listed here. + }; +} + +#endif diff --git a/apps/opencs/model/filter/narynode.cpp b/apps/opencs/model/filter/narynode.cpp new file mode 100644 index 000000000..98f706c87 --- /dev/null +++ b/apps/opencs/model/filter/narynode.cpp @@ -0,0 +1,60 @@ + +#include "narynode.hpp" + +#include + +CSMFilter::NAryNode::NAryNode (const std::vector >& nodes, + const std::string& name) +: mNodes (nodes), mName (name) +{} + +int CSMFilter::NAryNode::getSize() const +{ + return mNodes.size(); +} + +const CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const +{ + return *mNodes.at (index); +} + +std::vector CSMFilter::NAryNode::getReferencedColumns() const +{ + std::vector columns; + + for (std::vector >::const_iterator iter (mNodes.begin()); + iter!=mNodes.end(); ++iter) + { + std::vector columns2 = (*iter)->getReferencedColumns(); + + columns.insert (columns.end(), columns2.begin(), columns2.end()); + } + + return columns; +} + +std::string CSMFilter::NAryNode::toString (bool numericColumns) const +{ + std::ostringstream stream; + + stream << mName << " ("; + + bool first = true; + int size = getSize(); + + for (int i=0; i +#include + +#include + +#include "node.hpp" + +namespace CSMFilter +{ + class NAryNode : public Node + { + std::vector > mNodes; + std::string mName; + + public: + + NAryNode (const std::vector >& nodes, const std::string& name); + + int getSize() const; + + const Node& operator[] (int index) const; + + virtual std::vector getReferencedColumns() const; + ///< Return a list of the IDs of the columns referenced by this node. The column mapping + /// passed into test as columns must contain all columns listed here. + + virtual std::string toString (bool numericColumns) const; + ///< Return a string that represents this node. + /// + /// \param numericColumns Use numeric IDs instead of string to represent columns. + }; +} + +#endif diff --git a/apps/opencs/model/filter/node.cpp b/apps/opencs/model/filter/node.cpp new file mode 100644 index 000000000..276861cdc --- /dev/null +++ b/apps/opencs/model/filter/node.cpp @@ -0,0 +1,6 @@ + +#include "node.hpp" + +CSMFilter::Node::Node() {} + +CSMFilter::Node::~Node() {} \ No newline at end of file diff --git a/apps/opencs/model/filter/node.hpp b/apps/opencs/model/filter/node.hpp new file mode 100644 index 000000000..ef18353a4 --- /dev/null +++ b/apps/opencs/model/filter/node.hpp @@ -0,0 +1,53 @@ +#ifndef CSM_FILTER_NODE_H +#define CSM_FILTER_NODE_H + +#include +#include +#include + +#include + +#include + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSMFilter +{ + /// \brief Root class for the filter node hierarchy + /// + /// \note When the function documentation for this class mentions "this node", this should be + /// interpreted as "the node and all its children". + class Node + { + // not implemented + Node (const Node&); + Node& operator= (const Node&); + + public: + + Node(); + + virtual ~Node(); + + virtual bool test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const = 0; + ///< \return Can the specified table row pass through to filter? + /// \param columns column ID to column index mapping + + virtual std::vector getReferencedColumns() const = 0; + ///< Return a list of the IDs of the columns referenced by this node. The column mapping + /// passed into test as columns must contain all columns listed here. + + virtual std::string toString (bool numericColumns) const = 0; + ///< Return a string that represents this node. + /// + /// \param numericColumns Use numeric IDs instead of string to represent columns. + }; +} + +Q_DECLARE_METATYPE (boost::shared_ptr) + +#endif diff --git a/apps/opencs/model/filter/notnode.cpp b/apps/opencs/model/filter/notnode.cpp new file mode 100644 index 000000000..1b22ea7a6 --- /dev/null +++ b/apps/opencs/model/filter/notnode.cpp @@ -0,0 +1,10 @@ + +#include "notnode.hpp" + +CSMFilter::NotNode::NotNode (boost::shared_ptr child) : UnaryNode (child, "not") {} + +bool CSMFilter::NotNode::test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const +{ + return !getChild().test (table, row, columns); +} \ No newline at end of file diff --git a/apps/opencs/model/filter/notnode.hpp b/apps/opencs/model/filter/notnode.hpp new file mode 100644 index 000000000..b9e80b8c6 --- /dev/null +++ b/apps/opencs/model/filter/notnode.hpp @@ -0,0 +1,21 @@ +#ifndef CSM_FILTER_NOTNODE_H +#define CSM_FILTER_NOTNODE_H + +#include "unarynode.hpp" + +namespace CSMFilter +{ + class NotNode : public UnaryNode + { + public: + + NotNode (boost::shared_ptr child); + + virtual bool test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const; + ///< \return Can the specified table row pass through to filter? + /// \param columns column ID to column index mapping + }; +} + +#endif diff --git a/apps/opencs/model/filter/ornode.cpp b/apps/opencs/model/filter/ornode.cpp new file mode 100644 index 000000000..4fc34e1d5 --- /dev/null +++ b/apps/opencs/model/filter/ornode.cpp @@ -0,0 +1,20 @@ + +#include "ornode.hpp" + +#include + +CSMFilter::OrNode::OrNode (const std::vector >& nodes) +: NAryNode (nodes, "or") +{} + +bool CSMFilter::OrNode::test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const +{ + int size = getSize(); + + for (int i=0; i >& nodes); + + virtual bool test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const; + ///< \return Can the specified table row pass through to filter? + /// \param columns column ID to column index mapping + }; +} + +#endif diff --git a/apps/opencs/model/filter/parser.cpp b/apps/opencs/model/filter/parser.cpp new file mode 100644 index 000000000..8f4fcb70c --- /dev/null +++ b/apps/opencs/model/filter/parser.cpp @@ -0,0 +1,617 @@ + +#include "parser.hpp" + +#include +#include +#include + +#include + +#include "../world/columns.hpp" +#include "../world/data.hpp" +#include "../world/idcollection.hpp" + +#include "booleannode.hpp" +#include "ornode.hpp" +#include "andnode.hpp" +#include "notnode.hpp" +#include "textnode.hpp" +#include "valuenode.hpp" + +namespace CSMFilter +{ + struct Token + { + enum Type + { + Type_EOS, + Type_None, + Type_String, + Type_Number, + Type_Open, + Type_Close, + Type_OpenSquare, + Type_CloseSquare, + Type_Comma, + Type_OneShot, + Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously. + Type_Keyword_False, + Type_Keyword_And, + Type_Keyword_Or, + Type_Keyword_Not, + Type_Keyword_Text, + Type_Keyword_Value + }; + + Type mType; + std::string mString; + double mNumber; + + Token (Type type = Type_None); + + Token (Type type, const std::string& string); + ///< Non-string type that can also be interpreted as a string. + + Token (const std::string& string); + + Token (double number); + + operator bool() const; + + bool isString() const; + }; + + Token::Token (Type type) : mType (type) {} + + Token::Token (Type type, const std::string& string) : mType (type), mString (string) {} + + Token::Token (const std::string& string) : mType (Type_String), mString (string) {} + + Token::Token (double number) : mType (Type_Number), mNumber (number) {} + + bool Token::isString() const + { + return mType==Type_String || mType>=Type_Keyword_True; + } + + Token::operator bool() const + { + return mType!=Type_None; + } + + bool operator== (const Token& left, const Token& right) + { + if (left.mType!=right.mType) + return false; + + switch (left.mType) + { + case Token::Type_String: return left.mString==right.mString; + case Token::Type_Number: return left.mNumber==right.mNumber; + + default: return true; + } + } +} + +CSMFilter::Token CSMFilter::Parser::getStringToken() +{ + std::string string; + + int size = static_cast (mInput.size()); + + for (; mIndex1) + { + ++mIndex; + break; + } + }; + + if (!string.empty()) + { + if (string[0]=='"' && (string[string.size()-1]!='"' || string.size()<2) ) + { + error(); + return Token (Token::Type_None); + } + + if (string[0]!='"' && string[string.size()-1]=='"') + { + error(); + return Token (Token::Type_None); + } + + if (string[0]=='"') + return string.substr (1, string.size()-2); + } + + return checkKeywords (string); +} + +CSMFilter::Token CSMFilter::Parser::getNumberToken() +{ + std::string string; + + int size = static_cast (mInput.size()); + + bool hasDecimalPoint = false; + bool hasDigit = false; + + for (; mIndex> value; + + return value; +} + +CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token) +{ + static const char *sKeywords[] = + { + "true", "false", + "and", "or", "not", + "string", "value", + 0 + }; + + std::string string = Misc::StringUtils::lowerCase (token.mString); + + for (int i=0; sKeywords[i]; ++i) + if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0])) + return Token (static_cast (i+Token::Type_Keyword_True), token.mString); + + return token; +} + +CSMFilter::Token CSMFilter::Parser::getNextToken() +{ + int size = static_cast (mInput.size()); + + char c = 0; + + for (; mIndex=size) + return Token (Token::Type_EOS); + + switch (c) + { + case '(': ++mIndex; return Token (Token::Type_Open); + case ')': ++mIndex; return Token (Token::Type_Close); + case '[': ++mIndex; return Token (Token::Type_OpenSquare); + case ']': ++mIndex; return Token (Token::Type_CloseSquare); + case ',': ++mIndex; return Token (Token::Type_Comma); + case '!': ++mIndex; return Token (Token::Type_OneShot); + } + + if (c=='"' || c=='_' || std::isalpha (c) || c==':') + return getStringToken(); + + if (c=='-' || c=='.' || std::isdigit (c)) + return getNumberToken(); + + error(); + return Token (Token::Type_None); +} + +boost::shared_ptr CSMFilter::Parser::parseImp (bool allowEmpty, bool ignoreOneShot) +{ + if (Token token = getNextToken()) + { + if (token==Token (Token::Type_OneShot)) + token = getNextToken(); + + if (token) + switch (token.mType) + { + case Token::Type_Keyword_True: + + return boost::shared_ptr (new BooleanNode (true)); + + case Token::Type_Keyword_False: + + return boost::shared_ptr (new BooleanNode (false)); + + case Token::Type_Keyword_And: + case Token::Type_Keyword_Or: + + return parseNAry (token); + + case Token::Type_Keyword_Not: + { + boost::shared_ptr node = parseImp(); + + if (mError) + return boost::shared_ptr(); + + return boost::shared_ptr (new NotNode (node)); + } + + case Token::Type_Keyword_Text: + + return parseText(); + + case Token::Type_Keyword_Value: + + return parseValue(); + + case Token::Type_EOS: + + if (!allowEmpty) + error(); + + return boost::shared_ptr(); + + default: + + error(); + } + } + + return boost::shared_ptr(); +} + +boost::shared_ptr CSMFilter::Parser::parseNAry (const Token& keyword) +{ + std::vector > nodes; + + Token token = getNextToken(); + + if (token.mType!=Token::Type_Open) + { + error(); + return boost::shared_ptr(); + } + + for (;;) + { + boost::shared_ptr node = parseImp(); + + if (mError) + return boost::shared_ptr(); + + nodes.push_back (node); + + Token token = getNextToken(); + + if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma)) + { + error(); + return boost::shared_ptr(); + } + + if (token.mType==Token::Type_Close) + break; + } + + if (nodes.empty()) + { + error(); + return boost::shared_ptr(); + } + + switch (keyword.mType) + { + case Token::Type_Keyword_And: return boost::shared_ptr (new AndNode (nodes)); + case Token::Type_Keyword_Or: return boost::shared_ptr (new OrNode (nodes)); + default: error(); return boost::shared_ptr(); + } +} + +boost::shared_ptr CSMFilter::Parser::parseText() +{ + Token token = getNextToken(); + + if (token.mType!=Token::Type_Open) + { + error(); + return boost::shared_ptr(); + } + + token = getNextToken(); + + if (!token) + return boost::shared_ptr(); + + // parse column ID + int columnId = -1; + + if (token.mType==Token::Type_Number) + { + if (static_cast (token.mNumber)==token.mNumber) + columnId = static_cast (token.mNumber); + } + else if (token.isString()) + { + columnId = CSMWorld::Columns::getId (token.mString); + } + + if (columnId<0) + { + error(); + return boost::shared_ptr(); + } + + token = getNextToken(); + + if (token.mType!=Token::Type_Comma) + { + error(); + return boost::shared_ptr(); + } + + // parse text pattern + token = getNextToken(); + + if (!token.isString()) + { + error(); + return boost::shared_ptr(); + } + + std::string text = token.mString; + + token = getNextToken(); + + if (token.mType!=Token::Type_Close) + { + error(); + return boost::shared_ptr(); + } + + return boost::shared_ptr (new TextNode (columnId, text)); +} + +boost::shared_ptr CSMFilter::Parser::parseValue() +{ + Token token = getNextToken(); + + if (token.mType!=Token::Type_Open) + { + error(); + return boost::shared_ptr(); + } + + token = getNextToken(); + + if (!token) + return boost::shared_ptr(); + + // parse column ID + int columnId = -1; + + if (token.mType==Token::Type_Number) + { + if (static_cast (token.mNumber)==token.mNumber) + columnId = static_cast (token.mNumber); + } + else if (token.isString()) + { + columnId = CSMWorld::Columns::getId (token.mString); + } + + if (columnId<0) + { + error(); + return boost::shared_ptr(); + } + + token = getNextToken(); + + if (token.mType!=Token::Type_Comma) + { + error(); + return boost::shared_ptr(); + } + + // parse value + double lower = 0; + double upper = 0; + ValueNode::Type lowerType = ValueNode::Type_Open; + ValueNode::Type upperType = ValueNode::Type_Open; + + token = getNextToken(); + + if (token.mType==Token::Type_Number) + { + // single value + lower = upper = token.mNumber; + lowerType = upperType = ValueNode::Type_Closed; + } + else + { + // interval + if (token.mType==Token::Type_OpenSquare) + lowerType = ValueNode::Type_Closed; + else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open) + { + error(); + return boost::shared_ptr(); + } + + token = getNextToken(); + + if (token.mType==Token::Type_Number) + { + lower = token.mNumber; + + token = getNextToken(); + + if (token.mType!=Token::Type_Comma) + { + error(); + return boost::shared_ptr(); + } + } + else if (token.mType==Token::Type_Comma) + { + lowerType = ValueNode::Type_Infinite; + } + else + { + error(); + return boost::shared_ptr(); + } + + token = getNextToken(); + + if (token.mType==Token::Type_Number) + { + upper = token.mNumber; + + token = getNextToken(); + } + else + upperType = ValueNode::Type_Infinite; + + if (token.mType==Token::Type_CloseSquare) + { + if (upperType!=ValueNode::Type_Infinite) + upperType = ValueNode::Type_Closed; + } + else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close) + { + error(); + return boost::shared_ptr(); + } + } + + token = getNextToken(); + + if (token.mType!=Token::Type_Close) + { + error(); + return boost::shared_ptr(); + } + + return boost::shared_ptr (new ValueNode (columnId, lowerType, upperType, lower, upper)); +} + +void CSMFilter::Parser::error() +{ + mError = true; +} + +CSMFilter::Parser::Parser (const CSMWorld::Data& data) +: mIndex (0), mError (false), mData (data) {} + +bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined) +{ + // reset + mFilter.reset(); + mError = false; + mInput = filter; + mIndex = 0; + + Token token; + + if (allowPredefined) + token = getNextToken(); + + if (!allowPredefined || token==Token (Token::Type_OneShot)) + { + boost::shared_ptr node = parseImp (true, token!=Token (Token::Type_OneShot)); + + if (mError) + return false; + + if (getNextToken()!=Token (Token::Type_EOS)) + { + error(); + return false; + } + + if (node) + mFilter = node; + else + { + // Empty filter string equals to filter "true". + mFilter.reset (new BooleanNode (true)); + } + + return true; + } + // We do not use isString() here, because there could be a pre-defined filter with an ID that is + // equal a filter keyword. + else if (token.mType==Token::Type_String && allowPredefined) + { + if (getNextToken()!=Token (Token::Type_EOS)) + { + error(); + return false; + } + + int index = mData.getFilters().searchId (token.mString); + + if (index==-1) + { + error(); + return false; + } + + const CSMWorld::Record& record = mData.getFilters().getRecord (index); + + if (record.isDeleted()) + { + error(); + return false; + } + + return parse (record.get().mFilter, false); + } + else + { + error(); + return false; + } +} + +boost::shared_ptr CSMFilter::Parser::getFilter() const +{ + if (mError) + throw std::logic_error ("No filter available"); + + return mFilter; +} \ No newline at end of file diff --git a/apps/opencs/model/filter/parser.hpp b/apps/opencs/model/filter/parser.hpp new file mode 100644 index 000000000..5700102cf --- /dev/null +++ b/apps/opencs/model/filter/parser.hpp @@ -0,0 +1,59 @@ +#ifndef CSM_FILTER_PARSER_H +#define CSM_FILTER_PARSER_H + +#include + +#include "node.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSMFilter +{ + struct Token; + + class Parser + { + boost::shared_ptr mFilter; + std::string mInput; + int mIndex; + bool mError; + const CSMWorld::Data& mData; + + Token getStringToken(); + + Token getNumberToken(); + + Token getNextToken(); + + Token checkKeywords (const Token& token); + ///< Turn string token into keyword token, if possible. + + boost::shared_ptr parseImp (bool allowEmpty = false, bool ignoreOneShot = false); + ///< Will return a null-pointer, if there is nothing more to parse. + + boost::shared_ptr parseNAry (const Token& keyword); + + boost::shared_ptr parseText(); + + boost::shared_ptr parseValue(); + + void error(); + + public: + + Parser (const CSMWorld::Data& data); + + bool parse (const std::string& filter, bool allowPredefined = true); + ///< Discards any previous calls to parse + /// + /// \return Success? + + boost::shared_ptr getFilter() const; + ///< Throws an exception if the last call to parse did not return true. + }; +} + +#endif diff --git a/apps/opencs/model/filter/textnode.cpp b/apps/opencs/model/filter/textnode.cpp new file mode 100644 index 000000000..f3d98ce53 --- /dev/null +++ b/apps/opencs/model/filter/textnode.cpp @@ -0,0 +1,83 @@ + +#include "textnode.hpp" + +#include +#include + +#include + +#include "../world/columns.hpp" +#include "../world/idtable.hpp" + +CSMFilter::TextNode::TextNode (int columnId, const std::string& text) +: mColumnId (columnId), mText (text) +{} + +bool CSMFilter::TextNode::test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const +{ + const std::map::const_iterator iter = columns.find (mColumnId); + + if (iter==columns.end()) + throw std::logic_error ("invalid column in text node test"); + + if (iter->second==-1) + return true; + + QModelIndex index = table.index (row, iter->second); + + QVariant data = table.data (index); + + QString string; + + if (data.type()==QVariant::String) + { + string = data.toString(); + } + else if (data.type()==QVariant::Int || data.type()==QVariant::UInt || + CSMWorld::Columns::hasEnums (static_cast (mColumnId))) + { + int value = data.toInt(); + + std::vector enums = + CSMWorld::Columns::getEnums (static_cast (mColumnId)); + + if (value>=0 && value (enums.size())) + string = QString::fromUtf8 (enums[value].c_str()); + } + else if (data.type()==QVariant::Bool) + { + string = data.toBool() ? "true" : " false"; + } + else + return false; + + /// \todo make pattern syntax configurable + QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive); + + return regExp.exactMatch (string); +} + +std::vector CSMFilter::TextNode::getReferencedColumns() const +{ + return std::vector (1, mColumnId); +} + +std::string CSMFilter::TextNode::toString (bool numericColumns) const +{ + std::ostringstream stream; + + stream << "text ("; + + if (numericColumns) + stream << mColumnId; + else + stream + << "\"" + << CSMWorld::Columns::getName (static_cast (mColumnId)) + << "\""; + + stream << ", \"" << mText << "\")"; + + return stream.str(); +} \ No newline at end of file diff --git a/apps/opencs/model/filter/textnode.hpp b/apps/opencs/model/filter/textnode.hpp new file mode 100644 index 000000000..663fa7382 --- /dev/null +++ b/apps/opencs/model/filter/textnode.hpp @@ -0,0 +1,33 @@ +#ifndef CSM_FILTER_TEXTNODE_H +#define CSM_FILTER_TEXTNODE_H + +#include "leafnode.hpp" + +namespace CSMFilter +{ + class TextNode : public LeafNode + { + int mColumnId; + std::string mText; + + public: + + TextNode (int columnId, const std::string& text); + + virtual bool test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const; + ///< \return Can the specified table row pass through to filter? + /// \param columns column ID to column index mapping + + virtual std::vector getReferencedColumns() const; + ///< Return a list of the IDs of the columns referenced by this node. The column mapping + /// passed into test as columns must contain all columns listed here. + + virtual std::string toString (bool numericColumns) const; + ///< Return a string that represents this node. + /// + /// \param numericColumns Use numeric IDs instead of string to represent columns. + }; +} + +#endif diff --git a/apps/opencs/model/filter/unarynode.cpp b/apps/opencs/model/filter/unarynode.cpp new file mode 100644 index 000000000..43a24b76a --- /dev/null +++ b/apps/opencs/model/filter/unarynode.cpp @@ -0,0 +1,26 @@ + +#include "unarynode.hpp" + +CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr child, const std::string& name) +: mChild (child), mName (name) +{} + +const CSMFilter::Node& CSMFilter::UnaryNode::getChild() const +{ + return *mChild; +} + +CSMFilter::Node& CSMFilter::UnaryNode::getChild() +{ + return *mChild; +} + +std::vector CSMFilter::UnaryNode::getReferencedColumns() const +{ + return mChild->getReferencedColumns(); +} + +std::string CSMFilter::UnaryNode::toString (bool numericColumns) const +{ + return mName + " " + mChild->toString (numericColumns); +} \ No newline at end of file diff --git a/apps/opencs/model/filter/unarynode.hpp b/apps/opencs/model/filter/unarynode.hpp new file mode 100644 index 000000000..6bbc96092 --- /dev/null +++ b/apps/opencs/model/filter/unarynode.hpp @@ -0,0 +1,34 @@ +#ifndef CSM_FILTER_UNARYNODE_H +#define CSM_FILTER_UNARYNODE_H + +#include + +#include "node.hpp" + +namespace CSMFilter +{ + class UnaryNode : public Node + { + boost::shared_ptr mChild; + std::string mName; + + public: + + UnaryNode (boost::shared_ptr child, const std::string& name); + + const Node& getChild() const; + + Node& getChild(); + + virtual std::vector getReferencedColumns() const; + ///< Return a list of the IDs of the columns referenced by this node. The column mapping + /// passed into test as columns must contain all columns listed here. + + virtual std::string toString (bool numericColumns) const; + ///< Return a string that represents this node. + /// + /// \param numericColumns Use numeric IDs instead of string to represent columns. + }; +} + +#endif diff --git a/apps/opencs/model/filter/valuenode.cpp b/apps/opencs/model/filter/valuenode.cpp new file mode 100644 index 000000000..7eeb6beab --- /dev/null +++ b/apps/opencs/model/filter/valuenode.cpp @@ -0,0 +1,97 @@ + +#include "valuenode.hpp" + +#include +#include + +#include "../world/columns.hpp" +#include "../world/idtable.hpp" + +CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType, + double lower, double upper) +: mColumnId (columnId), mLowerType (lowerType), mUpperType (upperType), mLower (lower), mUpper (upper){} + +bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const +{ + const std::map::const_iterator iter = columns.find (mColumnId); + + if (iter==columns.end()) + throw std::logic_error ("invalid column in test value test"); + + if (iter->second==-1) + return true; + + QModelIndex index = table.index (row, iter->second); + + QVariant data = table.data (index); + + if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int && + data.type()!=QVariant::UInt) + return false; + + double value = data.toDouble(); + + switch (mLowerType) + { + case Type_Closed: if (valuemUpper) return false; break; + case Type_Open: if (value>=mUpper) return false; break; + case Type_Infinite: break; + } + + return true; +} + +std::vector CSMFilter::ValueNode::getReferencedColumns() const +{ + return std::vector (1, mColumnId); +} + +std::string CSMFilter::ValueNode::toString (bool numericColumns) const +{ + std::ostringstream stream; + + stream << "value ("; + + if (numericColumns) + stream << mColumnId; + else + stream + << "\"" + << CSMWorld::Columns::getName (static_cast (mColumnId)) + << "\""; + + stream << ", \""; + + if (mLower==mUpper && mLowerType!=Type_Infinite && mUpperType!=Type_Infinite) + stream << mLower; + else + { + switch (mLowerType) + { + case Type_Closed: stream << "[" << mLower; break; + case Type_Open: stream << "(" << mLower; break; + case Type_Infinite: stream << "("; break; + } + + stream << ", "; + + switch (mUpperType) + { + case Type_Closed: stream << mUpper << "]"; break; + case Type_Open: stream << mUpper << ")"; break; + case Type_Infinite: stream << ")"; break; + } + } + + stream << ")"; + + return stream.str(); +} \ No newline at end of file diff --git a/apps/opencs/model/filter/valuenode.hpp b/apps/opencs/model/filter/valuenode.hpp new file mode 100644 index 000000000..b1050709d --- /dev/null +++ b/apps/opencs/model/filter/valuenode.hpp @@ -0,0 +1,46 @@ +#ifndef CSM_FILTER_VALUENODE_H +#define CSM_FILTER_VALUENODE_H + +#include "leafnode.hpp" + +namespace CSMFilter +{ + class ValueNode : public LeafNode + { + public: + + enum Type + { + Type_Closed, Type_Open, Type_Infinite + }; + + private: + + int mColumnId; + std::string mText; + double mLower; + double mUpper; + Type mLowerType; + Type mUpperType; + + public: + + ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper); + + virtual bool test (const CSMWorld::IdTable& table, int row, + const std::map& columns) const; + ///< \return Can the specified table row pass through to filter? + /// \param columns column ID to column index mapping + + virtual std::vector getReferencedColumns() const; + ///< Return a list of the IDs of the columns referenced by this node. The column mapping + /// passed into test as columns must contain all columns listed here. + + virtual std::string toString (bool numericColumns) const; + ///< Return a string that represents this node. + /// + /// \param numericColumns Use numeric IDs instead of string to represent columns. + }; +} + +#endif 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..5af298a57 --- /dev/null +++ b/apps/opencs/model/settings/settingcontainer.hpp @@ -0,0 +1,47 @@ +#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); + + /// add a value to the container + /// multiple values supported + void insert (const QString &value); + + /// update an existing value + /// index specifies multiple values + void update (const QString &value, int index = 0); + + /// return value at specified index + QString getValue (int index = -1) const; + + /// retrieve list of all values + inline QStringList *getValues() const { return mValues; } + + /// return size of list + 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..5d897448a --- /dev/null +++ b/apps/opencs/model/settings/settingsitem.cpp @@ -0,0 +1,104 @@ +#include "settingsitem.hpp" + +#include + +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) +{ + //if there is no value list or value pair, there is no validation to do + bool isValid = !(!mValueList->isEmpty() || mValuePair); + + if (!isValid && !mValueList->isEmpty()) + { + for (QStringList::Iterator it = mValueList->begin(); it != mValueList->end(); ++it) + // foreach (QString listItem, *mValueList) + { + isValid = (value == *it); + + if (isValid) + break; + } + } + else if (!isValid && 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..a1daee4ac --- /dev/null +++ b/apps/opencs/model/settings/settingsitem.hpp @@ -0,0 +1,63 @@ +#ifndef SETTINGSITEM_HPP +#define SETTINGSITEM_HPP + +#include +#include "support.hpp" +#include "settingcontainer.hpp" + +namespace CSMSettings +{ + /// Represents a setting including metadata + /// (valid values, ranges, defaults, and multivalue status + class SettingsItem : public SettingContainer + { + QStringPair *mValuePair; + QStringList *mValueList; + bool mIsMultiValue; + QString mDefaultValue; + + public: + explicit SettingsItem(QString name, bool isMultiValue, + const QString& defaultValue, QObject *parent = 0) + : SettingContainer(defaultValue, parent), + mIsMultiValue (isMultiValue), mValueList (0), + mValuePair (0), mDefaultValue (defaultValue) + { + QObject::setObjectName(name); + } + + /// updateItem overloads for updating setting value + /// provided a list of values (multi-valued), + /// a specific value + /// or an index value corresponding to the mValueList + bool updateItem (const QStringList *values); + bool updateItem (const QString &value); + bool updateItem (int valueListIndex); + + /// retrieve list of valid values for setting + inline QStringList *getValueList() { return mValueList; } + + /// write list of valid values for setting + inline void setValueList (QStringList *valueList) { mValueList = valueList; } + + /// valuePair used for spin boxes (max / min) + inline QStringPair *getValuePair() { return mValuePair; } + + /// set value range (spinbox / integer use) + inline void setValuePair (QStringPair valuePair) { mValuePair = new QStringPair(valuePair); } + + inline bool isMultivalue () { return mIsMultiValue; } + + void setDefaultValue (const QString &value); + QString getDefaultValue () const; + + private: + + /// Verifies that the supplied value is one of the following: + /// 1. Within the limits of the value pair (min / max) + /// 2. One of the values indicated in the value list + 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..4ffd01b73 --- /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..1ce28ed75 --- /dev/null +++ b/apps/opencs/model/settings/usersettings.cpp @@ -0,0 +1,355 @@ +#include "usersettings.hpp" + +#include +#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 *CSMSettings::UserSettings::mUserSettingsInstance = 0; + +CSMSettings::UserSettings::UserSettings() +{ + assert(!mUserSettingsInstance); + mUserSettingsInstance = this; + + mReadWriteMessage = QObject::tr("
Could not open or create file for writing

\ + Please make sure you have the right permissions and try again.
"); + + mReadOnlyMessage = QObject::tr("
Could not open file for reading

\ + Please make sure you have the right permissions and try again.
"); + + buildEditorSettingDefaults(); +} + +void CSMSettings::UserSettings::buildEditorSettingDefaults() +{ + SettingContainer *windowHeight = new SettingContainer("768", this); + SettingContainer *windowWidth = new SettingContainer("1024", this); + SettingContainer *rsDelegate = new SettingContainer("Icon and Text", this); + SettingContainer *refIdTypeDelegate = new SettingContainer("Icon and Text", this); + + windowHeight->setObjectName ("Height"); + windowWidth->setObjectName ("Width"); + rsDelegate->setObjectName ("Record Status Display"); + refIdTypeDelegate->setObjectName ("Referenceable ID Type Display"); + + SettingMap *displayFormatMap = new SettingMap; + SettingMap *windowSizeMap = new SettingMap; + + displayFormatMap->insert (rsDelegate->objectName(), rsDelegate ); + displayFormatMap->insert (refIdTypeDelegate->objectName(), refIdTypeDelegate); + + windowSizeMap->insert (windowWidth->objectName(), windowWidth ); + windowSizeMap->insert (windowHeight->objectName(), windowHeight ); + + mEditorSettingDefaults.insert ("Display Format", displayFormatMap); + mEditorSettingDefaults.insert ("Window Size", windowSizeMap); +} + +CSMSettings::UserSettings::~UserSettings() +{ + mUserSettingsInstance = 0; +} + +QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) const +{ + QIODevice::OpenMode openFlags = QIODevice::Text; + + if (isReadOnly) + openFlags = QIODevice::ReadOnly | openFlags; + else + openFlags = QIODevice::ReadWrite | QIODevice::Truncate | openFlags; + + QFile *file = new QFile(filePath); + QTextStream *stream = 0; + + if (file->open(openFlags)) + { + stream = new QTextStream(file); + stream->setCodec(QTextCodec::codecForName("UTF-8")); + } + + return stream; + +} + +bool CSMSettings::UserSettings::writeSettings(QMap &settings) +{ + QTextStream *stream = openFileStream(mUserFilePath); + + bool success = (stream); + + if (success) + { + QList keyList = settings.keys(); + + foreach (QString key, keyList) + { + SettingList *sectionSettings = settings[key]; + + *stream << "[" << key << "]" << '\n'; + + foreach (SettingContainer *item, *sectionSettings) + *stream << item->objectName() << " = " << item->getValue() << '\n'; + } + + stream->device()->close(); + delete stream; + stream = 0; + } + else + { + displayFileErrorMessage(mReadWriteMessage, false); + } + + return (success); +} + + +const CSMSettings::SectionMap &CSMSettings::UserSettings::getSectionMap() const +{ + return mSectionSettings; +} + +const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString §ionName) const +{ + return getValidSettings(sectionName); +} + +bool CSMSettings::UserSettings::loadFromFile(const QString &filePath) +{ + if (filePath.isEmpty()) + return false; + + SectionMap loadedSettings; + + QTextStream *stream = openFileStream (filePath, true); + + bool success = (stream); + + if (success) + { + //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 whitespace, "\\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) + loadedSettings.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()); + sc->setObjectName(keyRe.cap(1).simplified()); + (*settings)[keyRe.cap(1).simplified()] = sc; + } + + } + + loadedSettings.insert(section, settings); + + stream->device()->close(); + delete stream; + stream = 0; + } + + mergeMap (loadedSettings); + + return success; +} + +void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap §ionSettings) +{ + foreach (QString key, sectionSettings.uniqueKeys()) + { + // insert entire section if it does not already exist in the loaded files + if (mSectionSettings.find(key) == mSectionSettings.end()) + mSectionSettings.insert(key, sectionSettings.value(key)); + else + { + SettingMap *passedSettings = sectionSettings.value(key); + SettingMap *settings = mSectionSettings.value(key); + + foreach (QString key2, passedSettings->uniqueKeys()) + { + //insert section settings individially if they do not already exist + if (settings->find(key2) == settings->end()) + settings->insert(key2, passedSettings->value(key2)); + else + { + settings->value(key2)->update(passedSettings->value(key2)->getValue()); + } + } + } + } +} + +void CSMSettings::UserSettings::loadSettings (const QString &fileName) +{ + mSectionSettings.clear(); + + //global + QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName; + bool globalOk = loadFromFile(globalFilePath); + + + //local + QString localFilePath = QString::fromStdString(mCfgMgr.getLocalPath().string()) + fileName; + bool localOk = loadFromFile(localFilePath); + + //user + mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName; + loadFromFile(mUserFilePath); + + if (!(localOk || globalOk)) + { + QString message = QObject::tr("
Could not open user settings files for reading

\ + Global and local settings files could not be read.\ + You may have incorrect file permissions or the OpenCS installation may be corrupted.
"); + + message += QObject::tr("
Global filepath: ") + globalFilePath; + message += QObject::tr("
Local filepath: ") + localFilePath; + + displayFileErrorMessage ( message, true); + } +} + +void CSMSettings::UserSettings::updateSettings (const QString §ionName, const QString &settingName) +{ + + SettingMap *settings = getValidSettings(sectionName); + + if (!settings) + return; + + if (settingName.isEmpty()) + { + foreach (const SettingContainer *setting, *settings) + emit signalUpdateEditorSetting (setting->objectName(), setting->getValue()); + } + else + { + if (settings->find(settingName) != settings->end()) + { + const SettingContainer *setting = settings->value(settingName); + emit signalUpdateEditorSetting (setting->objectName(), setting->getValue()); + } + } +} + +QString CSMSettings::UserSettings::getSetting (const QString §ion, const QString &setting) const +{ + SettingMap *settings = getValidSettings(section); + + QString retVal = ""; + + if (settings->find(setting) != settings->end()) + retVal = settings->value(setting)->getValue(); + + return retVal; +} + +CSMSettings::UserSettings& CSMSettings::UserSettings::instance() +{ + assert(mUserSettingsInstance); + return *mUserSettingsInstance; +} + +void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message, bool isReadOnly) +{ + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle(QObject::tr("OpenCS configuration file I/O error")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + + if (!isReadOnly) + msgBox.setText (mReadWriteMessage + message); + else + msgBox.setText (message); + + msgBox.exec(); +} + +CSMSettings::SettingMap * +CSMSettings::UserSettings::getValidSettings (const QString §ionName) const +{ + SettingMap *settings = 0; + + //copy the default values for the entire section if it's not found + if (mSectionSettings.find(sectionName) == mSectionSettings.end()) + { + if (mEditorSettingDefaults.find(sectionName) != mEditorSettingDefaults.end()) + settings = mEditorSettingDefaults.value (sectionName); + } + //otherwise, iterate the section's settings, looking for missing values and replacing them with defaults. + else + { + SettingMap *loadedSettings = mSectionSettings[sectionName]; + SettingMap *defaultSettings = mEditorSettingDefaults[sectionName]; + + foreach (QString key, defaultSettings->uniqueKeys()) + { + //write the default value to the loaded settings + if (loadedSettings->find((key))==loadedSettings->end()) + loadedSettings->insert(key, defaultSettings->value(key)); + } + + settings = mSectionSettings.value (sectionName); + } + + return settings; +} diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp new file mode 100644 index 000000000..63e78bd61 --- /dev/null +++ b/apps/opencs/model/settings/usersettings.hpp @@ -0,0 +1,94 @@ +#ifndef USERSETTINGS_HPP +#define USERSETTINGS_HPP + +#include +#include +#include +#include + +#include + +#include "support.hpp" + +#ifndef Q_MOC_RUN +#include +#endif + +namespace Files { typedef std::vector PathContainer; + struct ConfigurationManager;} + +class QFile; + +namespace CSMSettings { + + struct UserSettings: public QObject + { + + Q_OBJECT + + SectionMap mSectionSettings; + SectionMap mEditorSettingDefaults; + + static UserSettings *mUserSettingsInstance; + QString mUserFilePath; + Files::ConfigurationManager mCfgMgr; + QString mReadOnlyMessage; + QString mReadWriteMessage; + + public: + + /// Singleton implementation + static UserSettings& instance(); + + UserSettings(); + ~UserSettings(); + + UserSettings (UserSettings const &); //not implemented + void operator= (UserSettings const &); //not implemented + + /// Writes settings to the last loaded settings file + bool writeSettings(QMap §ions); + + /// Called from editor to trigger signal to update the specified setting. + /// If no setting name is specified, all settings found in the specified section are updated. + void updateSettings (const QString §ionName, const QString &settingName = ""); + + /// Retrieves the settings file at all three levels (global, local and user). + + /// \todo Multi-valued settings are not fully implemented. Setting values + /// \todo loaded in later files will always overwrite previously loaded values. + void loadSettings (const QString &fileName); + + /// Returns the entire map of settings across all sections + const SectionMap &getSectionMap () const; + + const SettingMap *getSettings (const QString §ionName) const; + + /// Retrieves the value as a QString of the specified setting in the specified section + QString getSetting(const QString §ion, const QString &setting) const; + + private: + + + /// Opens a QTextStream from the provided path as read-only or read-write. + QTextStream *openFileStream (const QString &filePath, bool isReadOnly = false) const; + + /// Parses a setting file specified in filePath from the provided text stream. + bool loadFromFile (const QString &filePath = ""); + + /// merge the passed map into mSectionSettings + void mergeMap (const SectionMap &); + + void displayFileErrorMessage(const QString &message, bool isReadOnly); + + void buildEditorSettingDefaults(); + + SettingMap *getValidSettings (const QString §ionName) const; + + 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/mandatoryid.cpp b/apps/opencs/model/tools/mandatoryid.cpp index f9f2ca378..b99abec6d 100644 --- a/apps/opencs/model/tools/mandatoryid.cpp +++ b/apps/opencs/model/tools/mandatoryid.cpp @@ -1,9 +1,11 @@ #include "mandatoryid.hpp" -#include "../world/idcollection.hpp" +#include "../world/collectionbase.hpp" -CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, +#include "../world/record.hpp" + +CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId, const std::vector& ids) : mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids) {} diff --git a/apps/opencs/model/tools/mandatoryid.hpp b/apps/opencs/model/tools/mandatoryid.hpp index 14fcec204..342e2d754 100644 --- a/apps/opencs/model/tools/mandatoryid.hpp +++ b/apps/opencs/model/tools/mandatoryid.hpp @@ -10,7 +10,7 @@ namespace CSMWorld { - class IdCollectionBase; + class CollectionBase; } namespace CSMTools @@ -18,13 +18,13 @@ namespace CSMTools /// \brief Verify stage: make sure that records with specific IDs exist. class MandatoryIdStage : public Stage { - const CSMWorld::IdCollectionBase& mIdCollection; + const CSMWorld::CollectionBase& mIdCollection; CSMWorld::UniversalId mCollectionId; std::vector mIds; public: - MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, const CSMWorld::UniversalId& collectionId, + MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId, const std::vector& ids); virtual int setup(); 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..cd58fca1e --- /dev/null +++ b/apps/opencs/model/world/cell.cpp @@ -0,0 +1,25 @@ + +#include "cell.hpp" + +#include + +void CSMWorld::Cell::load (ESM::ESMReader &esm) +{ + mName = mId; + + ESM::Cell::load (esm, false); + + if (!(mData.mFlags & Interior)) + { + std::ostringstream stream; + + stream << "#" << mData.mX << " " << mData.mY; + + mId = stream.str(); + } +} + +void CSMWorld::Cell::addRef (const std::string& id) +{ + mRefs.push_back (std::make_pair (id, false)); +} \ 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..89854312a --- /dev/null +++ b/apps/opencs/model/world/cell.hpp @@ -0,0 +1,24 @@ +#ifndef CSM_WOLRD_CELL_H +#define CSM_WOLRD_CELL_H + +#include +#include + +#include + +namespace CSMWorld +{ + /// \brief Wrapper for Cell record + struct Cell : public ESM::Cell + { + std::string mId; + std::vector > mRefs; // ID, modified + std::vector mDeletedRefs; + + void load (ESM::ESMReader &esm); + + void addRef (const std::string& id); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp new file mode 100644 index 000000000..84a00cef8 --- /dev/null +++ b/apps/opencs/model/world/collection.hpp @@ -0,0 +1,339 @@ +#ifndef CSM_WOLRD_COLLECTION_H +#define CSM_WOLRD_COLLECTION_H + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "columnbase.hpp" + +#include "collectionbase.hpp" + +namespace CSMWorld +{ + /// \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 Single-type record collection + template > + class Collection : public CollectionBase + { + std::vector > mRecords; + std::map mIndex; + std::vector *> mColumns; + + // not implemented + Collection (const Collection&); + Collection& operator= (const Collection&); + + public: + + Collection(); + + virtual ~Collection(); + + void add (const ESXRecordT& record); + ///< Add a new record (modified) + + virtual int getSize() const; + + virtual std::string getId (int index) const; + + virtual int getIndex (const std::string& id) const; + + virtual int getColumns() const; + + virtual QVariant getData (int index, int column) const; + + virtual void setData (int index, int column, const QVariant& data); + + virtual const ColumnBase& getColumn (int column) const; + + virtual void merge(); + ///< Merge modified into base. + + virtual void purge(); + ///< Remove records that are flagged as erased. + + virtual void removeRows (int index, int count) ; + + 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. + /// \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 = 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 const Record& getRecord (const std::string& id) const; + + virtual const Record& getRecord (int index) const; + + virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const; + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual std::vector getIds (bool listDeleted = true) const; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + + void addColumn (Column *column); + + void setRecord (int index, const Record& record); + ///< \attention This function must not change the ID. + }; + + template + Collection::Collection() + {} + + template + Collection::~Collection() + { + for (typename std::vector *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter) + delete *iter; + } + + template + void Collection::add (const ESXRecordT& record) + { + std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record)); + + std::map::iterator iter = mIndex.find (id); + + if (iter==mIndex.end()) + { + Record record2; + record2.mState = Record::State_ModifiedOnly; + record2.mModified = record; + + mRecords.push_back (record2); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1)); + } + else + { + mRecords[iter->second].setModified (record); + } + } + + template + int Collection::getSize() const + { + return mRecords.size(); + } + + template + std::string Collection::getId (int index) const + { + return IdAccessorT().getId (mRecords.at (index).get()); + } + + template + int Collection::getIndex (const std::string& id) const + { + int index = searchId (id); + + if (index==-1) + throw std::runtime_error ("invalid ID: " + id); + + return index; + } + + template + int Collection::getColumns() const + { + return mColumns.size(); + } + + template + QVariant Collection::getData (int index, int column) const + { + return mColumns.at (column)->get (mRecords.at (index)); + } + + template + void Collection::setData (int index, int column, const QVariant& data) + { + return mColumns.at (column)->set (mRecords.at (index), data); + } + + template + const ColumnBase& Collection::getColumn (int column) const + { + return *mColumns.at (column); + } + + template + void Collection::addColumn (Column *column) + { + mColumns.push_back (column); + } + + template + void Collection::merge() + { + for (typename std::vector >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) + iter->merge(); + + purge(); + } + + template + void Collection::purge() + { + int i = 0; + + while (i (mRecords.size())) + { + if (mRecords[i].isErased()) + removeRows (i, 1); + else + ++i; + } + } + + template + void Collection::removeRows (int index, int count) + { + mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count); + + typename std::map::iterator iter = mIndex.begin(); + + while (iter!=mIndex.end()) + { + if (iter->second>=index) + { + if (iter->second>=index+count) + { + iter->second -= count; + ++iter; + } + else + { + mIndex.erase (iter++); + } + } + else + ++iter; + } + } + + template + void Collection::appendBlankRecord (const std::string& id, + UniversalId::Type type) + { + ESXRecordT record; + IdAccessorT().getId (record) = id; + record.blank(); + add (record); + } + + template + int Collection::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 -1; + + return iter->second; + } + + template + void Collection::replace (int index, const RecordBase& record) + { + mRecords.at (index) = dynamic_cast&> (record); + } + + template + void Collection::appendRecord (const RecordBase& record, + UniversalId::Type type) + { + mRecords.push_back (dynamic_cast&> (record)); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( + dynamic_cast&> (record).get())), + mRecords.size()-1)); + } + + template + int Collection::getAppendIndex (UniversalId::Type type) const + { + return static_cast (mRecords.size()); + } + + template + std::vector Collection::getIds (bool listDeleted) const + { + std::vector ids; + + for (typename std::map::const_iterator iter = mIndex.begin(); + iter!=mIndex.end(); ++iter) + { + if (listDeleted || !mRecords[iter->second].isDeleted()) + ids.push_back (IdAccessorT().getId (mRecords[iter->second].get())); + } + + return ids; + } + + template + const Record& Collection::getRecord (const std::string& id) const + { + int index = getIndex (id); + return mRecords.at (index); + } + + template + const Record& Collection::getRecord (int index) const + { + return mRecords.at (index); + } + + template + void Collection::setRecord (int index, const Record& record) + { + if (IdAccessorT().getId (mRecords.at (index).get())!=IdAccessorT().getId (record.get())) + throw std::runtime_error ("attempt to change the ID of a record"); + + mRecords.at (index) = record; + } +} + +#endif diff --git a/apps/opencs/model/world/collectionbase.cpp b/apps/opencs/model/world/collectionbase.cpp new file mode 100644 index 000000000..932ea27b5 --- /dev/null +++ b/apps/opencs/model/world/collectionbase.cpp @@ -0,0 +1,6 @@ + +#include "collectionbase.hpp" + +CSMWorld::CollectionBase::CollectionBase() {} + +CSMWorld::CollectionBase::~CollectionBase() {} diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp new file mode 100644 index 000000000..ff6dab247 --- /dev/null +++ b/apps/opencs/model/world/collectionbase.hpp @@ -0,0 +1,89 @@ +#ifndef CSM_WOLRD_COLLECTIONBASE_H +#define CSM_WOLRD_COLLECTIONBASE_H + +#include + +#include "universalid.hpp" + +class QVariant; + +namespace CSMWorld +{ + struct ColumnBase; + struct RecordBase; + + /// \brief Base class for record collections + /// + /// \attention Modifying records through the interface does not update connected views. + /// Such modifications should be done through the table model interface instead unless no views + /// are connected to the model or special precautions have been taken to send update signals + /// manually. + class CollectionBase + { + // not implemented + CollectionBase (const CollectionBase&); + CollectionBase& operator= (const CollectionBase&); + + public: + + CollectionBase(); + + virtual ~CollectionBase(); + + virtual int getSize() const = 0; + + virtual std::string getId (int index) const = 0; + + virtual int getIndex (const std::string& id) const = 0; + + virtual int getColumns() const = 0; + + virtual const ColumnBase& getColumn (int column) const = 0; + + virtual QVariant getData (int index, int column) const = 0; + + virtual void setData (int index, int column, const QVariant& data) = 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; + ///< Remove records that are flagged as erased. + + virtual void removeRows (int index, int count) = 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. + /// \return index of record (if found) or -1 (not found) + + virtual void replace (int index, const RecordBase& record) = 0; + ///< 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, + UniversalId::Type type = UniversalId::Type_None) = 0; + ///< If the record type does not match, an exception is thrown. + + virtual const RecordBase& getRecord (const std::string& id) const = 0; + + virtual const RecordBase& getRecord (int index) const = 0; + + virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0; + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual std::vector getIds (bool listDeleted = true) const = 0; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 134c582c4..34bad20cc 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -1,8 +1,10 @@ #include "columnbase.hpp" -CSMWorld::ColumnBase::ColumnBase (const std::string& title, Display displayType, int flags) -: mTitle (title), mDisplayType (displayType), mFlags (flags) +#include "columns.hpp" + +CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags) +: mColumnId (columnId), mDisplayType (displayType), mFlags (flags) {} CSMWorld::ColumnBase::~ColumnBase() {} @@ -10,4 +12,9 @@ CSMWorld::ColumnBase::~ColumnBase() {} bool CSMWorld::ColumnBase::isUserEditable() const { return isEditable(); +} + +std::string CSMWorld::ColumnBase::getTitle() const +{ + return Columns::getName (static_cast (mColumnId)); } \ No newline at end of file diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index c44abda2b..c1b423c94 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -31,14 +31,26 @@ namespace CSMWorld Display_Float, Display_Var, Display_GmstVarType, - Display_GlobalVarType + Display_GlobalVarType, + Display_Specialisation, + Display_Attribute, + Display_Boolean, + Display_SpellType, + Display_Script, + Display_ApparatusType, + Display_ArmorType, + Display_ClothingType, + Display_CreatureType, + Display_WeaponType, + Display_RecordState, + Display_RefRecordType }; - std::string mTitle; + int mColumnId; int mFlags; Display mDisplayType; - ColumnBase (const std::string& title, Display displayType, int flag); + ColumnBase (int columnId, Display displayType, int flag); virtual ~ColumnBase(); @@ -47,22 +59,22 @@ namespace CSMWorld virtual bool isUserEditable() const; ///< Can this column be edited directly by the user? + virtual std::string getTitle() const; }; template struct Column : public ColumnBase { - std::string mTitle; int mFlags; - Column (const std::string& title, Display displayType, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (title, displayType, flags) {} + Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (columnId, displayType, flags) {} virtual QVariant get (const Record& record) const = 0; virtual void set (Record& record, const QVariant& data) { - throw std::logic_error ("Column " + mTitle + " is not editable"); + throw std::logic_error ("Column " + getTitle() + " is not editable"); } }; } diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp new file mode 100644 index 000000000..a13ac9a8a --- /dev/null +++ b/apps/opencs/model/world/columnimp.hpp @@ -0,0 +1,1289 @@ +#ifndef CSM_WOLRD_COLUMNIMP_H +#define CSM_WOLRD_COLUMNIMP_H + +#include + +#include + +#include + +#include "columnbase.hpp" +#include "columns.hpp" + +namespace CSMWorld +{ + /// \note Shares ID with VarValueColumn. A table can not have both. + template + struct FloatValueColumn : public Column + { + FloatValueColumn() : Column (Columns::ColumnId_Value, ColumnBase::Display_Float) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mValue.getFloat(); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mValue.setFloat (data.toFloat()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + /// \note Shares ID with IdColumn. A table can not have both. + template + struct StringIdColumn : public Column + { + StringIdColumn (bool hidden = false) + : Column (Columns::ColumnId_Id, ColumnBase::Display_String, + hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) + {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mId.c_str()); + } + + virtual bool isEditable() const + { + return false; + } + }; + + template + struct RecordStateColumn : public Column + { + RecordStateColumn() + : Column (Columns::ColumnId_Modification, ColumnBase::Display_RecordState) + {} + + virtual QVariant get (const Record& record) const + { + if (record.mState==Record::State_Erased) + return static_cast (Record::State_Deleted); + + return static_cast (record.mState); + } + + virtual void set (Record& record, const QVariant& data) + { + record.mState = static_cast (data.toInt()); + } + + virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const + { + return false; + } + }; + + template + struct FixedRecordTypeColumn : public Column + { + int mType; + + FixedRecordTypeColumn (int type) + : Column (Columns::ColumnId_RecordType, ColumnBase::Display_Integer, 0), + mType (type) + {} + + virtual QVariant get (const Record& record) const + { + return mType; + } + + virtual bool isEditable() const + { + 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 (Columns::ColumnId_ValueType, 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; + } + }; + + /// \note Shares ID with FloatValueColumn. A table can not have both. + template + struct VarValueColumn : public Column + { + VarValueColumn() : Column (Columns::ColumnId_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 (Columns::ColumnId_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 (Columns::ColumnId_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 (Columns::ColumnId_UseValue1 + 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 (Columns::ColumnId_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 (Columns::ColumnId_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 (Columns::ColumnId_Attribute1 + 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 ? Columns::ColumnId_MajorSkill1 : Columns::ColumnId_MinorSkill1) : + Columns::ColumnId_Skill1) + 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 (Columns::ColumnId_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 (Columns::ColumnId_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 (int columnId, int mask) + : Column (columnId, 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 ? Columns::ColumnId_MaleWeight : Columns::ColumnId_MaleHeight) : + (weight ? Columns::ColumnId_FemaleWeight : Columns::ColumnId_FemaleHeight), + 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 ? Columns::ColumnId_Volume : + (type==Type_MinRange ? Columns::ColumnId_MinRange : Columns::ColumnId_MaxRange), + 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 (Columns::ColumnId_SoundFile, 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 (Columns::ColumnId_MapColour, 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 (Columns::ColumnId_SleepEncounter, 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 (Columns::ColumnId_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 (Columns::ColumnId_SpellType, 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 (Columns::ColumnId_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 (Columns::ColumnId_ScriptText, 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 (Columns::ColumnId_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; + } + }; + + template + struct CellColumn : public Column + { + CellColumn() : Column (Columns::ColumnId_Cell, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mCellId.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mCellId = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const + { + return false; + } + }; + + /// \note Shares ID with StringIdColumn. A table can not have both. + template + struct IdColumn : public Column + { + IdColumn() : Column (Columns::ColumnId_Id, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mRefID.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mRefID = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ScaleColumn : public Column + { + ScaleColumn() : Column (Columns::ColumnId_Scale, ColumnBase::Display_Float) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mScale; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mScale = data.toFloat(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct OwnerColumn : public Column + { + OwnerColumn() : Column (Columns::ColumnId_Owner, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mOwner.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mOwner = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SoulColumn : public Column + { + SoulColumn() : Column (Columns::ColumnId_Soul, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mSoul.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mSoul = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct FactionColumn : public Column + { + FactionColumn() : Column (Columns::ColumnId_Faction, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mFaction.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mFaction = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct FactionIndexColumn : public Column + { + FactionIndexColumn() + : Column (Columns::ColumnId_FactionIndex, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mFactIndex; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mFactIndex = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ChargesColumn : public Column + { + ChargesColumn() : Column (Columns::ColumnId_Charges, ColumnBase::Display_Integer) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mCharge; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mCharge = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct EnchantmentChargesColumn : public Column + { + EnchantmentChargesColumn() + : Column (Columns::ColumnId_Enchantment, ColumnBase::Display_Float) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mEnchantmentCharge; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mEnchantmentCharge = data.toFloat(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct GoldValueColumn : public Column + { + GoldValueColumn() + : Column (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mGoldValue; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mGoldValue = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct TeleportColumn : public Column + { + TeleportColumn() + : Column (Columns::ColumnId_Teleport, ColumnBase::Display_Boolean) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mTeleport; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mTeleport = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct TeleportCellColumn : public Column + { + TeleportCellColumn() + : Column (Columns::ColumnId_TeleportCell, ColumnBase::Display_String) + {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mDestCell.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mDestCell = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const + { + return false; + } + }; + + template + struct LockLevelColumn : public Column + { + LockLevelColumn() + : Column (Columns::ColumnId_LockLevel, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mLockLevel; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mLockLevel = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct KeyColumn : public Column + { + KeyColumn() : Column (Columns::ColumnId_Key, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mKey.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mKey = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct TrapColumn : public Column + { + TrapColumn() : Column (Columns::ColumnId_Trap, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mTrap.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mTrap = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct FilterColumn : public Column + { + FilterColumn() : Column (Columns::ColumnId_Filter, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mFilter.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mFilter = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PosColumn : public Column + { + ESM::Position ESXRecordT::* mPosition; + int mIndex; + + PosColumn (ESM::Position ESXRecordT::* position, int index, bool door) + : Column ( + (door ? Columns::ColumnId_DoorPositionXPos : Columns::ColumnId_PositionXPos)+index, + ColumnBase::Display_Float), mPosition (position), mIndex (index) {} + + virtual QVariant get (const Record& record) const + { + const ESM::Position& position = record.get().*mPosition; + return position.pos[mIndex]; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + ESM::Position& position = record.get().*mPosition; + + position.pos[mIndex] = data.toFloat(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct RotColumn : public Column + { + ESM::Position ESXRecordT::* mPosition; + int mIndex; + + RotColumn (ESM::Position ESXRecordT::* position, int index, bool door) + : Column ( + (door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot)+index, + ColumnBase::Display_Float), mPosition (position), mIndex (index) {} + + virtual QVariant get (const Record& record) const + { + const ESM::Position& position = record.get().*mPosition; + return position.rot[mIndex]; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + ESM::Position& position = record.get().*mPosition; + + position.rot[mIndex] = data.toFloat(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; +} + +#endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp new file mode 100644 index 000000000..25047807a --- /dev/null +++ b/apps/opencs/model/world/columns.cpp @@ -0,0 +1,313 @@ + +#include "columns.hpp" + +#include + +#include "universalid.hpp" + +namespace CSMWorld +{ + namespace Columns + { + struct ColumnDesc + { + int mId; + const char *mName; + }; + + const ColumnDesc sNames[] = + { + { ColumnId_Value, "Value" }, + { ColumnId_Id, "ID" }, + { ColumnId_Modification, "Modified" }, + { ColumnId_RecordType, "Record Type" }, + { ColumnId_ValueType, "Value Type" }, + { ColumnId_Description, "Description" }, + { ColumnId_Specialisation, "Specialisation" }, + { ColumnId_Attribute, "Attribute" }, + { ColumnId_Name, "Name" }, + { ColumnId_Playable, "Playable" }, + { ColumnId_Hidden, "Hidden" }, + { ColumnId_MaleWeight, "Male Weight" }, + { ColumnId_FemaleWeight, "Female Weight" }, + { ColumnId_MaleHeight, "Male Height" }, + { ColumnId_FemaleHeight, "Female Height" }, + { ColumnId_Volume, "Volume" }, + { ColumnId_MinRange, "Min Range" }, + { ColumnId_MaxRange, "Max Range" }, + { ColumnId_SoundFile, "Sound File" }, + { ColumnId_MapColour, "Map Colour" }, + { ColumnId_SleepEncounter, "Sleep Encounter" }, + { ColumnId_Texture, "Texture" }, + { ColumnId_SpellType, "Spell Type" }, + { ColumnId_Cost, "Cost" }, + { ColumnId_ScriptText, "Script Text" }, + { ColumnId_Region, "Region" }, + { ColumnId_Cell, "Cell" }, + { ColumnId_Scale, "Scale" }, + { ColumnId_Owner, "Owner" }, + { ColumnId_Soul, "Soul" }, + { ColumnId_Faction, "Faction" }, + { ColumnId_FactionIndex, "Faction Index" }, + { ColumnId_Charges, "Charges" }, + { ColumnId_Enchantment, "Enchantment" }, + { ColumnId_Value, "Coin Value" }, + { ColumnId_Teleport, "Teleport" }, + { ColumnId_TeleportCell, "Teleport Cell" }, + { ColumnId_LockLevel, "Lock Level" }, + { ColumnId_Key, "Key" }, + { ColumnId_Trap, "Trap" }, + { ColumnId_BeastRace, "Beast Race" }, + { ColumnId_AutoCalc, "Auto Calc" }, + { ColumnId_StarterSpell, "Starter Spell" }, + { ColumnId_AlwaysSucceeds, "Always Succeeds" }, + { ColumnId_SleepForbidden, "Sleep Forbidden" }, + { ColumnId_InteriorWater, "Interior Water" }, + { ColumnId_InteriorSky, "Interior Sky" }, + { ColumnId_Model, "Model" }, + { ColumnId_Script, "Script" }, + { ColumnId_Icon, "Icon" }, + { ColumnId_Weight, "Weight" }, + { ColumnId_EnchantmentPoints, "Enchantment Points" }, + { ColumnId_Quality, "Quality" }, + { ColumnId_Ai, "AI" }, + { ColumnId_AiHello, "AI Hello" }, + { ColumnId_AiFlee, "AI Flee" }, + { ColumnId_AiFight, "AI Fight" }, + { ColumnId_AiAlarm, "AI Alarm" }, + { ColumnId_BuysWeapons, "Buys Weapons" }, + { ColumnId_BuysArmor, "Buys Armor" }, + { ColumnId_BuysClothing, "Buys Clothing" }, + { ColumnId_BuysBooks, "Buys Books" }, + { ColumnId_BuysIngredients, "Buys Ingredients" }, + { ColumnId_BuysLockpicks, "Buys Lockpicks" }, + { ColumnId_BuysProbes, "Buys Probes" }, + { ColumnId_BuysLights, "Buys Lights" }, + { ColumnId_BuysApparati, "Buys Apparati" }, + { ColumnId_BuysRepairItems, "Buys Repair Items" }, + { ColumnId_BuysMiscItems, "Buys Misc Items" }, + { ColumnId_BuysPotions, "Buys Potions" }, + { ColumnId_BuysMagicItems, "Buys Magic Items" }, + { ColumnId_SellsSpells, "Sells Spells" }, + { ColumnId_Trainer, "Trainer" }, + { ColumnId_Spellmaking, "Spellmaking" }, + { ColumnId_EnchantingService, "Enchanting Service" }, + { ColumnId_RepairService, "Repair Serivce" }, + { ColumnId_ApparatusType, "Apparatus Type" }, + { ColumnId_ArmorType, "Armor Type" }, + { ColumnId_Health, "Health" }, + { ColumnId_ArmorValue, "Armor Value" }, + { ColumnId_Scroll, "Scroll" }, + { ColumnId_ClothingType, "Clothing Type" }, + { ColumnId_WeightCapacity, "Weight Capacity" }, + { ColumnId_OrganicContainer, "Organic Container" }, + { ColumnId_Respawn, "Respawn" }, + { ColumnId_CreatureType, "Creature Type" }, + { ColumnId_SoulPoints, "Soul Points" }, + { ColumnId_OriginalCreature, "Original Creature" }, + { ColumnId_Biped, "Biped" }, + { ColumnId_HasWeapon, "Has Weapon" }, + { ColumnId_NoMovement, "No Movement" }, + { ColumnId_Swims, "Swims" }, + { ColumnId_Flies, "Flies" }, + { ColumnId_Walks, "Walks" }, + { ColumnId_Essential, "Essential" }, + { ColumnId_SkeletonBlood, "Skeleton Blood" }, + { ColumnId_MetalBlood, "Metal Blood" }, + { ColumnId_OpenSound, "Open Sound" }, + { ColumnId_CloseSound, "Close Sound" }, + { ColumnId_Duration, "Duration" }, + { ColumnId_Radius, "Radius" }, + { ColumnId_Colour, "Colour" }, + { ColumnId_Sound, "Sound" }, + { ColumnId_Dynamic, "Dynamic" }, + { ColumnId_Portable, "Portable" }, + { ColumnId_NegativeLight, "Negative Light" }, + { ColumnId_Flickering, "Flickering" }, + { ColumnId_SlowFlickering, "Slow Flickering" }, + { ColumnId_Pulsing, "Pulsing" }, + { ColumnId_SlowPulsing, "Slow Pulsing" }, + { ColumnId_Fire, "Fire" }, + { ColumnId_OffByDefault, "Off by default" }, + { ColumnId_IsKey, "Is Key" }, + { ColumnId_Race, "Race" }, + { ColumnId_Class, "Class" }, + { Columnid_Hair, "Hair" }, + { ColumnId_Head, "Head" }, + { ColumnId_Female, "Female" }, + { ColumnId_WeaponType, "Weapon Type" }, + { ColumnId_WeaponSpeed, "Weapon Speed" }, + { ColumnId_WeaponReach, "Weapon Reach" }, + { ColumnId_MinChop, "Min Chop" }, + { ColumnId_MaxChip, "Max Chop" }, + { Columnid_MinSlash, "Min Slash" }, + { ColumnId_MaxSlash, "Max Slash" }, + { ColumnId_MinThrust, "Min Thrust" }, + { ColumnId_MaxThrust, "Max Thrust" }, + { ColumnId_Magical, "Magical" }, + { ColumnId_Silver, "Silver" }, + { ColumnId_Filter, "Filter" }, + { ColumnId_PositionXPos, "Pos X" }, + { ColumnId_PositionYPos, "Pos Y" }, + { ColumnId_PositionZPos, "Pos Z" }, + { ColumnId_PositionXRot, "Rot X" }, + { ColumnId_PositionYRot, "Rot Y" }, + { ColumnId_PositionZRot, "Rot Z" }, + { ColumnId_DoorPositionXPos, "Teleport Pos X" }, + { ColumnId_DoorPositionYPos, "Teleport Pos Y" }, + { ColumnId_DoorPositionZPos, "Teleport Pos Z" }, + { ColumnId_DoorPositionXRot, "Teleport Rot X" }, + { ColumnId_DoorPositionYRot, "Teleport Rot Y" }, + { ColumnId_DoorPositionZRot, "Teleport Rot Z" }, + + { ColumnId_UseValue1, "Use value 1" }, + { ColumnId_UseValue2, "Use value 2" }, + { ColumnId_UseValue3, "Use value 3" }, + { ColumnId_UseValue4, "Use value 4" }, + + { ColumnId_Attribute1, "Attribute 1" }, + { ColumnId_Attribute2, "Attribute 2" }, + + { ColumnId_MajorSkill1, "Major Skill 1" }, + { ColumnId_MajorSkill2, "Major Skill 2" }, + { ColumnId_MajorSkill3, "Major Skill 3" }, + { ColumnId_MajorSkill4, "Major Skill 4" }, + { ColumnId_MajorSkill5, "Major Skill 5" }, + + { ColumnId_MinorSkill1, "Minor Skill 1" }, + { ColumnId_MinorSkill2, "Minor Skill 2" }, + { ColumnId_MinorSkill3, "Minor Skill 3" }, + { ColumnId_MinorSkill4, "Minor Skill 4" }, + { ColumnId_MinorSkill5, "Minor Skill 5" }, + + { ColumnId_Skill1, "Skill 1" }, + { ColumnId_Skill2, "Skill 2" }, + { ColumnId_Skill3, "Skill 3" }, + { ColumnId_Skill4, "Skill 4" }, + { ColumnId_Skill5, "Skill 5" }, + { ColumnId_Skill6, "Skill 6" }, + + { -1, 0 } // end marker + }; + } +} + +std::string CSMWorld::Columns::getName (ColumnId column) +{ + for (int i=0; sNames[i].mName; ++i) + if (column==sNames[i].mId) + return sNames[i].mName; + + return ""; +} + +int CSMWorld::Columns::getId (const std::string& name) +{ + std::string name2 = Misc::StringUtils::lowerCase (name); + + for (int i=0; sNames[i].mName; ++i) + if (name2==Misc::StringUtils::lowerCase (sNames[i].mName)) + return sNames[i].mId; + + return -1; +} + +namespace +{ + 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 + }; + + static const char *sModificationEnums[] = + { + "Base", "Modified", "Added", "Deleted", "Deleted", 0 + }; + + static const char *sVarTypeEnums[] = + { + "unknown", "none", "short", "integer", "long", "float", "string", 0 + }; + + const char **getEnumNames (CSMWorld::Columns::ColumnId column) + { + switch (column) + { + case CSMWorld::Columns::ColumnId_Specialisation: return sSpecialisations; + case CSMWorld::Columns::ColumnId_Attribute: return sAttributes; + case CSMWorld::Columns::ColumnId_SpellType: return sSpellTypes; + case CSMWorld::Columns::ColumnId_ApparatusType: return sApparatusTypes; + case CSMWorld::Columns::ColumnId_ArmorType: return sArmorTypes; + case CSMWorld::Columns::ColumnId_ClothingType: return sClothingTypes; + case CSMWorld::Columns::ColumnId_CreatureType: return sCreatureTypes; + case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes; + case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; + case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; + + default: return 0; + } + } +} + +bool CSMWorld::Columns::hasEnums (ColumnId column) +{ + return getEnumNames (column)!=0 || column==ColumnId_RecordType; +} + +std::vector CSMWorld::Columns::getEnums (ColumnId column) +{ + std::vector enums; + + if (const char **table = getEnumNames (column)) + for (int i=0; table[i]; ++i) + enums.push_back (table[i]); + else if (column==ColumnId_RecordType) + { + enums.push_back (""); // none + + for (int i=UniversalId::Type_None+1; i (i)).getTypeName()); + } + + return enums; +} \ No newline at end of file diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 018825831..582f5102b 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -1,182 +1,204 @@ #ifndef CSM_WOLRD_COLUMNS_H #define CSM_WOLRD_COLUMNS_H -#include "columnbase.hpp" +#include +#include namespace CSMWorld { - template - struct FloatValueColumn : public Column + namespace Columns { - FloatValueColumn() : Column ("Value", ColumnBase::Display_Float) {} - - virtual QVariant get (const Record& record) const - { - return record.get().mValue.getFloat(); - } - - virtual void set (Record& record, const QVariant& data) - { - ESXRecordT record2 = record.get(); - record2.mValue.setFloat (data.toFloat()); - record.setModified (record2); - } - - virtual bool isEditable() const - { - return true; - } - }; - - template - struct StringIdColumn : public Column - { - StringIdColumn() : Column ("ID", ColumnBase::Display_String) {} - - virtual QVariant get (const Record& record) const - { - return record.get().mId.c_str(); - } - - virtual bool isEditable() const - { - return false; - } - }; - - template - struct RecordStateColumn : public Column - { - RecordStateColumn() : Column ("*", ColumnBase::Display_Integer) {} - - virtual QVariant get (const Record& record) const - { - if (record.mState==Record::State_Erased) - return static_cast (Record::State_Deleted); - - return static_cast (record.mState); - } - - virtual void set (Record& record, const QVariant& data) - { - record.mState = static_cast (data.toInt()); - } - - virtual bool isEditable() const - { - return true; - } - - virtual bool isUserEditable() const - { - return false; - } - }; - - template - struct FixedRecordTypeColumn : public Column - { - int mType; - - FixedRecordTypeColumn (int type) - : Column ("Record Type", ColumnBase::Display_Integer, 0), mType (type) {} - - virtual QVariant get (const Record& record) const - { - return mType; - } - - virtual bool isEditable() const - { - 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 record.get().mValue.getString().c_str(); break; - - case ESM::VT_Int: - case ESM::VT_Short: - case ESM::VT_Long: - - return record.get().mValue.getInteger(); break; - - case ESM::VT_Float: - - return record.get().mValue.getFloat(); break; - - 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; - } - }; + enum ColumnId + { + ColumnId_Value = 0, + ColumnId_Id = 1, + ColumnId_Modification = 2, + ColumnId_RecordType = 3, + ColumnId_ValueType = 4, + ColumnId_Description = 5, + ColumnId_Specialisation = 6, + ColumnId_Attribute = 7, + ColumnId_Name = 8, + ColumnId_Playable = 9, + ColumnId_Hidden = 10, + ColumnId_MaleWeight = 11, + ColumnId_FemaleWeight = 12, + ColumnId_MaleHeight = 13, + ColumnId_FemaleHeight = 14, + ColumnId_Volume = 15, + ColumnId_MinRange = 16, + ColumnId_MaxRange = 17, + ColumnId_SoundFile = 18, + ColumnId_MapColour = 19, + ColumnId_SleepEncounter = 20, + ColumnId_Texture = 21, + ColumnId_SpellType = 22, + ColumnId_Cost = 23, + ColumnId_ScriptText = 24, + ColumnId_Region = 25, + ColumnId_Cell = 26, + ColumnId_Scale = 27, + ColumnId_Owner = 28, + ColumnId_Soul = 29, + ColumnId_Faction = 30, + ColumnId_FactionIndex = 31, + ColumnId_Charges = 32, + ColumnId_Enchantment = 33, + ColumnId_CoinValue = 34, + ColumnId_Teleport = 25, + ColumnId_TeleportCell = 26, + ColumnId_LockLevel = 27, + ColumnId_Key = 28, + ColumnId_Trap = 29, + ColumnId_BeastRace = 30, + ColumnId_AutoCalc = 31, + ColumnId_StarterSpell = 32, + ColumnId_AlwaysSucceeds = 33, + ColumnId_SleepForbidden = 34, + ColumnId_InteriorWater = 35, + ColumnId_InteriorSky = 36, + ColumnId_Model = 37, + ColumnId_Script = 38, + ColumnId_Icon = 39, + ColumnId_Weight = 40, + ColumnId_EnchantmentPoints = 31, + ColumnId_Quality = 32, + ColumnId_Ai = 33, + ColumnId_AiHello = 34, + ColumnId_AiFlee = 35, + ColumnId_AiFight = 36, + ColumnId_AiAlarm = 37, + ColumnId_BuysWeapons = 38, + ColumnId_BuysArmor = 39, + ColumnId_BuysClothing = 40, + ColumnId_BuysBooks = 41, + ColumnId_BuysIngredients = 42, + ColumnId_BuysLockpicks = 43, + ColumnId_BuysProbes = 44, + ColumnId_BuysLights = 45, + ColumnId_BuysApparati = 46, + ColumnId_BuysRepairItems = 47, + ColumnId_BuysMiscItems = 48, + ColumnId_BuysPotions = 49, + ColumnId_BuysMagicItems = 50, + ColumnId_SellsSpells = 51, + ColumnId_Trainer = 52, + ColumnId_Spellmaking = 53, + ColumnId_EnchantingService = 54, + ColumnId_RepairService = 55, + ColumnId_ApparatusType = 56, + ColumnId_ArmorType = 57, + ColumnId_Health = 58, + ColumnId_ArmorValue = 59, + ColumnId_Scroll = 60, + ColumnId_ClothingType = 61, + ColumnId_WeightCapacity = 62, + ColumnId_OrganicContainer = 63, + ColumnId_Respawn = 64, + ColumnId_CreatureType = 65, + ColumnId_SoulPoints = 66, + ColumnId_OriginalCreature = 67, + ColumnId_Biped = 68, + ColumnId_HasWeapon = 69, + ColumnId_NoMovement = 70, + ColumnId_Swims = 71, + ColumnId_Flies = 72, + ColumnId_Walks = 73, + ColumnId_Essential = 74, + ColumnId_SkeletonBlood = 75, + ColumnId_MetalBlood = 76, + ColumnId_OpenSound = 77, + ColumnId_CloseSound = 78, + ColumnId_Duration = 79, + ColumnId_Radius = 80, + ColumnId_Colour = 81, + ColumnId_Sound = 82, + ColumnId_Dynamic = 83, + ColumnId_Portable = 84, + ColumnId_NegativeLight = 85, + ColumnId_Flickering = 86, + ColumnId_SlowFlickering = 87, + ColumnId_Pulsing = 88, + ColumnId_SlowPulsing = 89, + ColumnId_Fire = 90, + ColumnId_OffByDefault = 91, + ColumnId_IsKey = 92, + ColumnId_Race = 93, + ColumnId_Class = 94, + Columnid_Hair = 95, + ColumnId_Head = 96, + ColumnId_Female = 97, + ColumnId_WeaponType = 98, + ColumnId_WeaponSpeed = 99, + ColumnId_WeaponReach = 100, + ColumnId_MinChop = 101, + ColumnId_MaxChip = 102, + Columnid_MinSlash = 103, + ColumnId_MaxSlash = 104, + ColumnId_MinThrust = 105, + ColumnId_MaxThrust = 106, + ColumnId_Magical = 107, + ColumnId_Silver = 108, + ColumnId_Filter = 109, + ColumnId_PositionXPos = 110, + ColumnId_PositionYPos = 111, + ColumnId_PositionZPos = 112, + ColumnId_PositionXRot = 113, + ColumnId_PositionYRot = 114, + ColumnId_PositionZRot = 115, + ColumnId_DoorPositionXPos = 116, + ColumnId_DoorPositionYPos = 117, + ColumnId_DoorPositionZPos = 118, + ColumnId_DoorPositionXRot = 119, + ColumnId_DoorPositionYRot = 120, + ColumnId_DoorPositionZRot = 121, + + // Allocated to a separate value range, so we don't get a collision should we ever need + // to extend the number of use values. + ColumnId_UseValue1 = 0x10000, + ColumnId_UseValue2 = 0x10001, + ColumnId_UseValue3 = 0x10002, + ColumnId_UseValue4 = 0x10003, + + // Allocated to a separate value range, so we don't get a collision should we ever need + // to extend the number of attributes. Note that this is not the number of different + // attributes, but the number of attributes that can be references from a record. + ColumnId_Attribute1 = 0x20000, + ColumnId_Attribute2 = 0x20001, + + // Allocated to a separate value range, so we don't get a collision should we ever need + // to extend the number of skills. Note that this is not the number of different + // skills, but the number of skills that can be references from a record. + ColumnId_MajorSkill1 = 0x30000, + ColumnId_MajorSkill2 = 0x30001, + ColumnId_MajorSkill3 = 0x30002, + ColumnId_MajorSkill4 = 0x30003, + ColumnId_MajorSkill5 = 0x30004, + + ColumnId_MinorSkill1 = 0x40000, + ColumnId_MinorSkill2 = 0x40001, + ColumnId_MinorSkill3 = 0x40002, + ColumnId_MinorSkill4 = 0x40003, + ColumnId_MinorSkill5 = 0x40004, + + ColumnId_Skill1 = 0x50000, + ColumnId_Skill2 = 0x50001, + ColumnId_Skill3 = 0x50002, + ColumnId_Skill4 = 0x50003, + ColumnId_Skill5 = 0x50004, + ColumnId_Skill6 = 0x50005 + }; + + std::string getName (ColumnId column); + + int getId (const std::string& name); + ///< Will return -1 for an invalid name. + + bool hasEnums (ColumnId column); + + std::vector getEnums (ColumnId column); + ///< Returns an empty vector, if \æ column isn't an enum type column. + } } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index e22ecf992..f6f421c6a 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,9 +1,9 @@ #include "commands.hpp" -#include +#include -#include "idtableproxymodel.hpp" +#include "idtable.hpp" #include "idtable.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, @@ -25,15 +25,28 @@ void CSMWorld::ModifyCommand::undo() mModel.setData (mIndex, mOld); } -CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id) +CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { setText (("Create record " + id).c_str()); } +void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) +{ + mValues[column] = value; +} + +void CSMWorld::CreateCommand::setType (UniversalId::Type type) +{ + mType = type; +} + void CSMWorld::CreateCommand::redo() { - mModel.addRecord (mId); + mModel.addRecord (mId, mType); + + for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) + mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); } void CSMWorld::CreateCommand::undo() @@ -56,7 +69,9 @@ CSMWorld::RevertCommand::~RevertCommand() void CSMWorld::RevertCommand::redo() { - QModelIndex index = mModel.getModelIndex (mId, 1); + int column = mModel.findColumnIndex (Columns::ColumnId_Modification); + + QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state==RecordBase::State_ModifiedOnly) @@ -71,7 +86,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) @@ -89,7 +104,9 @@ CSMWorld::DeleteCommand::~DeleteCommand() void CSMWorld::DeleteCommand::redo() { - QModelIndex index = mModel.getModelIndex (mId, 1); + int column = mModel.findColumnIndex (Columns::ColumnId_Modification); + + QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state==RecordBase::State_ModifiedOnly) @@ -104,5 +121,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/commands.hpp b/apps/opencs/model/world/commands.hpp index af419215d..3fc2522ea 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -4,17 +4,20 @@ #include "record.hpp" #include +#include #include #include #include +#include "universalid.hpp" + class QModelIndex; class QAbstractItemModel; namespace CSMWorld { - class IdTableProxyModel; + class IdTable; class IdTable; class RecordBase; @@ -37,12 +40,18 @@ namespace CSMWorld class CreateCommand : public QUndoCommand { - IdTableProxyModel& mModel; + IdTable& mModel; std::string mId; + UniversalId::Type mType; + std::map mValues; public: - CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0); + CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); + + void setType (UniversalId::Type type); + + void addValue (int column, const QVariant& value); virtual void redo(); diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index bbd8667b3..1e290d45f 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -2,27 +2,49 @@ #include "data.hpp" #include +#include -#include +#include #include #include #include +#include #include "idtable.hpp" +#include "columnimp.hpp" +#include "regionmap.hpp" #include "columns.hpp" -void CSMWorld::Data::addModel (QAbstractTableModel *model, UniversalId::Type type1, - UniversalId::Type type2) +void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, + UniversalId::Type type2, bool update) { mModels.push_back (model); mModelIndex.insert (std::make_pair (type1, model)); if (type2!=UniversalId::Type_None) mModelIndex.insert (std::make_pair (type2, model)); + + if (update) + { + connect (model, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&))); + connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (rowsChanged (const QModelIndex&, int, int))); + connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), + this, SLOT (rowsChanged (const QModelIndex&, int, int))); + } +} + +void CSMWorld::Data::appendIds (std::vector& ids, const CollectionBase& collection, + bool listDeleted) +{ + std::vector ids2 = collection.getIds (listDeleted); + + ids.insert (ids.end(), ids2.begin(), ids2.end()); } -CSMWorld::Data::Data() +CSMWorld::Data::Data() : mRefs (mCells) { mGlobals.addColumn (new StringIdColumn); mGlobals.addColumn (new RecordStateColumn); @@ -33,16 +55,157 @@ CSMWorld::Data::Data() 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 (Columns::ColumnId_Playable, 0x1)); + mRaces.addColumn (new FlagColumn (Columns::ColumnId_BeastRace, 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 (Columns::ColumnId_AutoCalc, 0x1)); + mSpells.addColumn (new FlagColumn (Columns::ColumnId_StarterSpell, 0x2)); + mSpells.addColumn (new FlagColumn (Columns::ColumnId_AlwaysSucceeds, 0x4)); + + mCells.addColumn (new StringIdColumn); + mCells.addColumn (new RecordStateColumn); + mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); + mCells.addColumn (new NameColumn); + mCells.addColumn (new FlagColumn (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx)); + mCells.addColumn (new RegionColumn); + + mRefs.addColumn (new StringIdColumn (true)); + mRefs.addColumn (new RecordStateColumn); + mRefs.addColumn (new CellColumn); + mRefs.addColumn (new IdColumn); + mRefs.addColumn (new PosColumn (&CellRef::mPos, 0, false)); + mRefs.addColumn (new PosColumn (&CellRef::mPos, 1, false)); + mRefs.addColumn (new PosColumn (&CellRef::mPos, 2, false)); + mRefs.addColumn (new RotColumn (&CellRef::mPos, 0, false)); + mRefs.addColumn (new RotColumn (&CellRef::mPos, 1, false)); + mRefs.addColumn (new RotColumn (&CellRef::mPos, 2, false)); + mRefs.addColumn (new ScaleColumn); + mRefs.addColumn (new OwnerColumn); + mRefs.addColumn (new SoulColumn); + mRefs.addColumn (new FactionColumn); + mRefs.addColumn (new FactionIndexColumn); + mRefs.addColumn (new ChargesColumn); + mRefs.addColumn (new EnchantmentChargesColumn); + mRefs.addColumn (new GoldValueColumn); + mRefs.addColumn (new TeleportColumn); + mRefs.addColumn (new TeleportCellColumn); + mRefs.addColumn (new PosColumn (&CellRef::mDoorDest, 0, true)); + mRefs.addColumn (new PosColumn (&CellRef::mDoorDest, 1, true)); + mRefs.addColumn (new PosColumn (&CellRef::mDoorDest, 2, true)); + mRefs.addColumn (new RotColumn (&CellRef::mDoorDest, 0, true)); + mRefs.addColumn (new RotColumn (&CellRef::mDoorDest, 1, true)); + mRefs.addColumn (new RotColumn (&CellRef::mDoorDest, 2, true)); + mRefs.addColumn (new LockLevelColumn); + mRefs.addColumn (new KeyColumn); + mRefs.addColumn (new TrapColumn); + + mFilters.addColumn (new StringIdColumn); + mFilters.addColumn (new RecordStateColumn); + mFilters.addColumn (new FilterColumn); + mFilters.addColumn (new DescriptionColumn); + 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, false); + 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); + addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference, false); + addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); } 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; } @@ -66,12 +229,155 @@ CSMWorld::IdCollection& CSMWorld::Data::getGmsts() return mGmsts; } -QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) +const CSMWorld::IdCollection& CSMWorld::Data::getSkills() const +{ + return mSkills; +} + +CSMWorld::IdCollection& CSMWorld::Data::getSkills() +{ + return mSkills; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getClasses() const +{ + 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() { - std::map::iterator iter = mModelIndex.find (id.getType()); + 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; +} + +const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const +{ + return mRefs; +} + +CSMWorld::RefCollection& CSMWorld::Data::getReferences() +{ + return mRefs; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getFilters() const +{ + return mFilters; +} + +CSMWorld::IdCollection& CSMWorld::Data::getFilters() +{ + return mFilters; +} + +QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) +{ + std::map::iterator iter = mModelIndex.find (id.getType()); if (iter==mModelIndex.end()) + { + // try creating missing (secondary) tables on the fly + // + // Note: We create these tables here so we don't have to deal with them during load/initial + // construction of the ESX data where no update signals are available. + if (id.getType()==UniversalId::Type_RegionMap) + { + RegionMap *table = 0; + addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, + UniversalId::Type_None, false); + return table; + } throw std::logic_error ("No table model available for " + id.toString()); + } return iter->second; } @@ -102,7 +408,44 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) { 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); + mRefs.load (reader, mCells.getSize()-1, 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: @@ -110,4 +453,55 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) reader.skipRecord(); } } +} + +bool CSMWorld::Data::hasId (const std::string& id) const +{ + return + getGlobals().searchId (id)!=-1 || + getGmsts().searchId (id)!=-1 || + getSkills().searchId (id)!=-1 || + getClasses().searchId (id)!=-1 || + getFactions().searchId (id)!=-1 || + getRaces().searchId (id)!=-1 || + getSounds().searchId (id)!=-1 || + getScripts().searchId (id)!=-1 || + getRegions().searchId (id)!=-1 || + getBirthsigns().searchId (id)!=-1 || + getSpells().searchId (id)!=-1 || + getCells().searchId (id)!=-1 || + getReferenceables().searchId (id)!=-1; +} + +std::vector CSMWorld::Data::getIds (bool listDeleted) const +{ + std::vector ids; + + appendIds (ids, mGlobals, listDeleted); + appendIds (ids, mGmsts, listDeleted); + appendIds (ids, mClasses, listDeleted); + appendIds (ids, mFactions, listDeleted); + appendIds (ids, mRaces, listDeleted); + appendIds (ids, mSounds, listDeleted); + appendIds (ids, mScripts, listDeleted); + appendIds (ids, mRegions, listDeleted); + appendIds (ids, mBirthsigns, listDeleted); + appendIds (ids, mSpells, listDeleted); + appendIds (ids, mCells, listDeleted); + appendIds (ids, mReferenceables, listDeleted); + + std::sort (ids.begin(), ids.end()); + + return ids; +} + +void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + if (topLeft.column()<=0) + emit idListChanged(); +} + +void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end) +{ + emit idListChanged(); } \ No newline at end of file diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 374534651..e900bb10f 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -6,35 +6,71 @@ #include +#include +#include + #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../filter/filter.hpp" #include "idcollection.hpp" #include "universalid.hpp" +#include "cell.hpp" +#include "refidcollection.hpp" +#include "refcollection.hpp" -class QAbstractTableModel; +class QAbstractItemModel; namespace CSMWorld { - class Data + class Data : public QObject { + Q_OBJECT + IdCollection mGlobals; IdCollection mGmsts; - std::vector mModels; - std::map mModelIndex; + IdCollection mSkills; + IdCollection mClasses; + IdCollection mFactions; + IdCollection mRaces; + IdCollection mSounds; + IdCollection mScripts; + IdCollection mRegions; + IdCollection mBirthsigns; + IdCollection mSpells; + IdCollection mCells; + RefIdCollection mReferenceables; + RefCollection mRefs; + IdCollection mFilters; + std::vector mModels; + std::map mModelIndex; // not implemented Data (const Data&); Data& operator= (const Data&); - void addModel (QAbstractTableModel *model, UniversalId::Type type1, - UniversalId::Type type2 = UniversalId::Type_None); + void addModel (QAbstractItemModel *model, UniversalId::Type type1, + UniversalId::Type type2 = UniversalId::Type_None, bool update = true); + + static void appendIds (std::vector& ids, const CollectionBase& collection, + bool listDeleted); + ///< Append all IDs from collection to \a ids. public: Data(); - ~Data(); + virtual ~Data(); const IdCollection& getGlobals() const; @@ -44,7 +80,59 @@ namespace CSMWorld IdCollection& getGmsts(); - QAbstractTableModel *getTableModel (const UniversalId& id); + 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(); + + const RefCollection& getReferences() const; + + RefCollection& getReferences(); + + const IdCollection& getFilters() const; + + IdCollection& getFilters(); + + 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 @@ -55,6 +143,23 @@ namespace CSMWorld void loadFile (const boost::filesystem::path& path, bool base); ///< Merging content of a file into base or modified. + + bool hasId (const std::string& id) const; + + std::vector getIds (bool listDeleted = true) const; + ///< Return a sorted collection of all IDs that are not internal to the editor. + /// + /// \param listDeleted include deleted record in the list + + signals: + + void idListChanged(); + + private slots: + + void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void rowsChanged (const QModelIndex& parent, int start, int end); }; } diff --git a/apps/opencs/model/world/idcollection.cpp b/apps/opencs/model/world/idcollection.cpp deleted file mode 100644 index 5ea953279..000000000 --- a/apps/opencs/model/world/idcollection.cpp +++ /dev/null @@ -1,6 +0,0 @@ - -#include "idcollection.hpp" - -CSMWorld::IdCollectionBase::IdCollectionBase() {} - -CSMWorld::IdCollectionBase::~IdCollectionBase() {} diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 9b69dfb88..04e65eea7 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -1,330 +1,34 @@ #ifndef CSM_WOLRD_IDCOLLECTION_H #define CSM_WOLRD_IDCOLLECTION_H -#include -#include -#include -#include -#include -#include -#include - -#include - #include -#include - -#include "columnbase.hpp" +#include "collection.hpp" namespace CSMWorld { - class IdCollectionBase - { - // not implemented - IdCollectionBase (const IdCollectionBase&); - IdCollectionBase& operator= (const IdCollectionBase&); - - public: - - IdCollectionBase(); - - virtual ~IdCollectionBase(); - - virtual int getSize() const = 0; - - virtual std::string getId (int index) const = 0; - - virtual int getIndex (const std::string& id) const = 0; - - virtual int getColumns() const = 0; - - virtual const ColumnBase& getColumn (int column) const = 0; - - virtual QVariant getData (int index, int column) const = 0; - - virtual void setData (int index, int column, const QVariant& data) = 0; - - virtual void merge() = 0; - ///< Merge modified into base. - - 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 int searchId (const std::string& id) const = 0; - ////< Search record with \a id. - /// \return index of record (if found) or -1 (not found) - - virtual void replace (int index, const RecordBase& record) = 0; - ///< 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) = 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 Throws an exception, if the type of \a record does not match. - - virtual const RecordBase& getRecord (const std::string& id) const = 0; - - virtual void load (ESM::ESMReader& reader, bool base) = 0; - }; - - ///< \brief Collection of ID-based records - template - class IdCollection : public IdCollectionBase + /// \brief Single type collection of top level records + template > + class IdCollection : public Collection { - std::vector > mRecords; - std::map mIndex; - std::vector *> mColumns; - - // not implemented - IdCollection (const IdCollection&); - IdCollection& operator= (const IdCollection&); - public: - IdCollection(); - - virtual ~IdCollection(); - - void add (const ESXRecordT& record); - ///< Add a new record (modified) - - virtual int getSize() const; - - virtual std::string getId (int index) const; - - virtual int getIndex (const std::string& id) const; - - virtual int getColumns() const; - - virtual QVariant getData (int index, int column) const; - - virtual void setData (int index, int column, const QVariant& data); - - virtual const ColumnBase& getColumn (int column) const; - - virtual void merge(); - ///< Merge modified into base. - - virtual void purge(); - ///< Remove records that are flagged as erased. - - virtual void removeRows (int index, int count) ; - - virtual void appendBlankRecord (const std::string& id); - - 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); - ///< If the record type does not match, an exception is thrown. - - 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 RecordBase& getRecord (const std::string& id) const; - - virtual void load (ESM::ESMReader& reader, bool base); - - void addColumn (Column *column); + 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 }; - 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) - { - std::string id = Misc::StringUtils::lowerCase(record.mId); - - std::map::iterator iter = mIndex.find (id); - - if (iter==mIndex.end()) - { - Record record2; - record2.mState = Record::State_ModifiedOnly; - record2.mModified = record; - - mRecords.push_back (record2); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1)); - } - else - { - mRecords[iter->second].setModified (record); - } - } - - template - int IdCollection::getSize() const - { - return mRecords.size(); - } - - template - std::string IdCollection::getId (int index) const - { - return mRecords.at (index).get().mId; - } - - template - int IdCollection::getIndex (const std::string& id) const - { - int index = searchId (id); - - if (index==-1) - throw std::runtime_error ("invalid ID: " + id); - - return index; - } - - template - int IdCollection::getColumns() const - { - return mColumns.size(); - } - - 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) - { - return mColumns.at (column)->set (mRecords.at (index), data); - } - - template - const ColumnBase& IdCollection::getColumn (int column) const - { - return *mColumns.at (column); - } - - template - void IdCollection::addColumn (Column *column) - { - mColumns.push_back (column); - } - - template - void IdCollection::merge() - { - for (typename std::vector >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) - iter->merge(); - - 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()); - } - - template - void IdCollection::removeRows (int index, int count) - { - mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count); - - typename std::map::iterator iter = mIndex.begin(); - - while (iter!=mIndex.end()) - { - if (iter->second>=index) - { - if (iter->second>=index+count) - { - iter->second -= count; - } - else - { - mIndex.erase (iter++); - } - } - - ++iter; - } - } - - template - void IdCollection::appendBlankRecord (const std::string& id) - { - ESXRecordT record; - record.mId = id; - record.blank(); - add (record); - } - - template - int IdCollection::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 -1; - - return iter->second; - } - - template - void IdCollection::replace (int index, const RecordBase& record) - { - mRecords.at (index) = dynamic_cast&> (record); - } - - template - void IdCollection::appendRecord (const RecordBase& record) - { - mRecords.push_back (dynamic_cast&> (record)); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (getId (record)), mRecords.size()-1)); - } - - template - std::string IdCollection::getId (const RecordBase& record) const - { - const Record& record2 = dynamic_cast&> (record); - return (record2.isModified() ? record2.mModified : record2.mBase).mId; - } - - template - void IdCollection::load (ESM::ESMReader& reader, bool base) + template + void IdCollection::load (ESM::ESMReader& reader, bool base, + UniversalId::Type type) { std::string id = reader.getHNOString ("NAME"); - int index = searchId (id); - if (reader.isNextSub ("DELE")) { + int index = Collection::searchId (id); + reader.skipRecord(); if (index==-1) @@ -337,19 +41,23 @@ namespace CSMWorld } else if (base) { - removeRows (index, 1); + Collection::removeRows (index, 1); } else { - mRecords[index].mState = RecordBase::State_Deleted; + Record record = Collection::getRecord (index); + record.mState = RecordBase::State_Deleted; + this->setRecord (index, record); } } else { ESXRecordT record; - record.mId = id; + IdAccessorT().getId (record) = id; record.load (reader); + int index = this->searchId (IdAccessorT().getId (record)); + if (index==-1) { // new record @@ -357,27 +65,22 @@ namespace CSMWorld record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; - appendRecord (record2); + this->appendRecord (record2); } else { // old record - Record& record2 = mRecords[index]; + Record record2 = Collection::getRecord (index); if (base) record2.mBase = record; else record2.setModified (record); + + this->setRecord (index, record2); } } } - - template - const RecordBase& IdCollection::getRecord (const std::string& id) const - { - int index = getIndex (id); - return mRecords.at (index); - } } #endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index afed6b6ed..baaf75289 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -1,9 +1,10 @@ #include "idtable.hpp" -#include "idcollection.hpp" +#include "collectionbase.hpp" +#include "columnbase.hpp" -CSMWorld::IdTable::IdTable (IdCollectionBase *idCollection) : mIdCollection (idCollection) +CSMWorld::IdTable::IdTable (CollectionBase *idCollection) : mIdCollection (idCollection) { } @@ -46,7 +47,7 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation return QVariant(); if (role==Qt::DisplayRole) - return tr (mIdCollection->getColumn (section).mTitle.c_str()); + return tr (mIdCollection->getColumn (section).getTitle().c_str()); if (role==ColumnBase::Role_Flags) return mIdCollection->getColumn (section).mFlags; @@ -96,13 +97,32 @@ bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& paren return true; } -void CSMWorld::IdTable::addRecord (const std::string& id) +QModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const { - int index = mIdCollection->getSize(); + 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, UniversalId::Type type) +{ + int index = mIdCollection->getAppendIndex(); beginInsertRows (QModelIndex(), index, index); - mIdCollection->appendBlankRecord (id); + mIdCollection->appendBlankRecord (id, type); endInsertRows(); } @@ -112,13 +132,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); @@ -137,4 +157,25 @@ void CSMWorld::IdTable::setRecord (const RecordBase& record) const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const { return mIdCollection->getRecord (id); +} + +int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const +{ + int columns = mIdCollection->getColumns(); + + for (int i=0; igetColumn (i).mColumnId==id) + return i; + + return -1; +} + +int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const +{ + int index = searchColumnIndex (id); + + if (index==-1) + throw std::logic_error ("invalid column index"); + + return index; } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index deaebaa38..00c577236 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -1,18 +1,21 @@ #ifndef CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H -#include +#include + +#include "universalid.hpp" +#include "columns.hpp" namespace CSMWorld { - class IdCollectionBase; + class CollectionBase; class RecordBase; - class IdTable : public QAbstractTableModel + class IdTable : public QAbstractItemModel { Q_OBJECT - IdCollectionBase *mIdCollection; + CollectionBase *mIdCollection; // not implemented IdTable (const IdTable&); @@ -20,7 +23,7 @@ namespace CSMWorld public: - IdTable (IdCollectionBase *idCollection); + IdTable (CollectionBase *idCollection); ///< The ownership of \a idCollection is not transferred. virtual ~IdTable(); @@ -39,14 +42,27 @@ namespace CSMWorld virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); - void addRecord (const std::string& id); + 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, UniversalId::Type type = UniversalId::Type_None); + ///< \param type Will be ignored, unless the collection supports multiple record types 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; + + int searchColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, -1 is returned. + + int findColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, an exception is + /// thrown. }; } diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 78995f60b..2b757adfe 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -1,18 +1,48 @@ #include "idtableproxymodel.hpp" +#include + #include "idtable.hpp" +void CSMWorld::IdTableProxyModel::updateColumnMap() +{ + mColumnMap.clear(); + + if (mFilter) + { + std::vector columns = mFilter->getReferencedColumns(); + + const IdTable& table = dynamic_cast (*sourceModel()); + + for (std::vector::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter) + mColumnMap.insert (std::make_pair (*iter, + table.searchColumnIndex (static_cast (*iter)))); + } +} + +bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) + const +{ + if (!mFilter) + return true; + + return mFilter->test ( + dynamic_cast (*sourceModel()), sourceRow, mColumnMap); +} + CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent) {} -void CSMWorld::IdTableProxyModel::addRecord (const std::string& id) +QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { - dynamic_cast (*sourceModel()).addRecord (id); + return mapFromSource (dynamic_cast (*sourceModel()).getModelIndex (id, column)); } -QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const +void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr& filter) { - return mapFromSource (dynamic_cast (*sourceModel()).getModelIndex (id, column)); + mFilter = filter; + updateColumnMap(); + invalidateFilter(); } \ No newline at end of file diff --git a/apps/opencs/model/world/idtableproxymodel.hpp b/apps/opencs/model/world/idtableproxymodel.hpp index 3f1537cce..b63dccd5e 100644 --- a/apps/opencs/model/world/idtableproxymodel.hpp +++ b/apps/opencs/model/world/idtableproxymodel.hpp @@ -1,9 +1,15 @@ #ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H #define CSM_WOLRD_IDTABLEPROXYMODEL_H +#include + +#include + +#include + #include -#include +#include "../filter/node.hpp" namespace CSMWorld { @@ -11,13 +17,22 @@ namespace CSMWorld { Q_OBJECT + boost::shared_ptr mFilter; + std::map mColumnMap; // column ID, column index in this model (or -1) + + private: + + void updateColumnMap(); + + bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const; + public: IdTableProxyModel (QObject *parent = 0); - virtual void addRecord (const std::string& id); - virtual QModelIndex getModelIndex (const std::string& id, int column) const; + + void setFilter (const boost::shared_ptr& filter); }; } 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/ref.cpp b/apps/opencs/model/world/ref.cpp new file mode 100644 index 000000000..af363bafb --- /dev/null +++ b/apps/opencs/model/world/ref.cpp @@ -0,0 +1,13 @@ + +#include "ref.hpp" + +#include "cell.hpp" + +void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id) +{ + mId = id; + mCellId = cell.mId; + + if (!mDeleted) + cell.addRef (mId); +} \ No newline at end of file diff --git a/apps/opencs/model/world/ref.hpp b/apps/opencs/model/world/ref.hpp new file mode 100644 index 000000000..3d107d675 --- /dev/null +++ b/apps/opencs/model/world/ref.hpp @@ -0,0 +1,26 @@ +#ifndef CSM_WOLRD_REF_H +#define CSM_WOLRD_REF_H + +#include + +namespace ESM +{ + class ESMReader; +} + +namespace CSMWorld +{ + class Cell; + + /// \brief Wrapper for CellRef sub record + struct CellRef : public ESM::CellRef + { + std::string mId; + std::string mCellId; + + void load (ESM::ESMReader &esm, Cell& cell, const std::string& id); + ///< Load cell ref and register it with \a cell. + }; +} + +#endif diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp new file mode 100644 index 000000000..696aeefaa --- /dev/null +++ b/apps/opencs/model/world/refcollection.cpp @@ -0,0 +1,37 @@ + +#include "refcollection.hpp" + +#include + +#include "ref.hpp" +#include "cell.hpp" + +void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base) +{ + Record cell = mCells.getRecord (cellIndex); + + Cell& cell2 = base ? cell.mBase : cell.mModified; + + CellRef ref; + + while (cell2.getNextRef (reader, ref)) + { + /// \todo handle deleted and moved references + ref.load (reader, cell2, getNewId()); + + Record record2; + record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2.mBase : record2.mModified) = ref; + + appendRecord (record2); + } + + mCells.setRecord (cellIndex, cell); +} + +std::string CSMWorld::RefCollection::getNewId() +{ + std::ostringstream stream; + stream << "ref#" << mNextId++; + return stream.str(); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp new file mode 100644 index 000000000..b5f8c8064 --- /dev/null +++ b/apps/opencs/model/world/refcollection.hpp @@ -0,0 +1,31 @@ +#ifndef CSM_WOLRD_REFCOLLECTION_H +#define CSM_WOLRD_REFCOLLECTION_H + +#include "collection.hpp" +#include "ref.hpp" +#include "record.hpp" + +namespace CSMWorld +{ + struct Cell; + + /// \brief References in cells + class RefCollection : public Collection + { + Collection& mCells; + int mNextId; + + public: + // MSVC needs the constructor for a class inheriting a template to be defined in header + RefCollection (Collection& cells) + : mCells (cells), mNextId (0) + {} + + void load (ESM::ESMReader& reader, int cellIndex, bool base); + ///< Load a sequence of references. + + std::string getNewId(); + }; +} + +#endif 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..cda2711cc --- /dev/null +++ b/apps/opencs/model/world/refidcollection.cpp @@ -0,0 +1,541 @@ + +#include "refidcollection.hpp" + +#include + +#include + +#include "refidadapter.hpp" +#include "refidadapterimp.hpp" +#include "columns.hpp" + +CSMWorld::RefIdColumn::RefIdColumn (int columnId, Display displayType, int flag, + bool editable, bool userEditable) +: ColumnBase (columnId, 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 (Columns::ColumnId_Id, ColumnBase::Display_String, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mId = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mModified = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mType = &mColumns.back(); + + ModelColumns modelColumns (baseColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Model, ColumnBase::Display_String)); + modelColumns.mModel = &mColumns.back(); + + NameColumns nameColumns (modelColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Name, ColumnBase::Display_String)); + nameColumns.mName = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_String)); + nameColumns.mScript = &mColumns.back(); + + InventoryColumns inventoryColumns (nameColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Icon, ColumnBase::Display_String)); + inventoryColumns.mIcon = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Weight, ColumnBase::Display_Float)); + inventoryColumns.mWeight = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer)); + inventoryColumns.mValue = &mColumns.back(); + + EnchantableColumns enchantableColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_String)); + enchantableColumns.mEnchantment = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_EnchantmentPoints, ColumnBase::Display_Integer)); + enchantableColumns.mEnchantmentPoints = &mColumns.back(); + + ToolColumns toolsColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Quality, ColumnBase::Display_Float)); + toolsColumns.mQuality = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Charges, ColumnBase::Display_Integer)); + toolsColumns.mUses = &mColumns.back(); + + ActorColumns actorsColumns (nameColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Ai, ColumnBase::Display_Boolean)); + actorsColumns.mHasAi = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_AiHello, ColumnBase::Display_Integer)); + actorsColumns.mHello = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFlee, ColumnBase::Display_Integer)); + actorsColumns.mFlee = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFight, ColumnBase::Display_Integer)); + actorsColumns.mFight = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_AiAlarm, ColumnBase::Display_Integer)); + actorsColumns.mAlarm = &mColumns.back(); + + static const struct + { + int mName; + unsigned int mFlag; + } sServiceTable[] = + { + { Columns::ColumnId_BuysWeapons, ESM::NPC::Weapon}, + { Columns::ColumnId_BuysArmor, ESM::NPC::Armor}, + { Columns::ColumnId_BuysClothing, ESM::NPC::Clothing}, + { Columns::ColumnId_BuysBooks, ESM::NPC::Books}, + { Columns::ColumnId_BuysIngredients, ESM::NPC::Ingredients}, + { Columns::ColumnId_BuysLockpicks, ESM::NPC::Picks}, + { Columns::ColumnId_BuysProbes, ESM::NPC::Probes}, + { Columns::ColumnId_BuysLights, ESM::NPC::Lights}, + { Columns::ColumnId_BuysApparati, ESM::NPC::Apparatus}, + { Columns::ColumnId_BuysRepairItems, ESM::NPC::RepairItem}, + { Columns::ColumnId_BuysMiscItems, ESM::NPC::Misc}, + { Columns::ColumnId_BuysPotions, ESM::NPC::Potions}, + { Columns::ColumnId_BuysMagicItems, ESM::NPC::MagicItems}, + { Columns::ColumnId_SellsSpells, ESM::NPC::Spells}, + { Columns::ColumnId_Trainer, ESM::NPC::Training}, + { Columns::ColumnId_Spellmaking, ESM::NPC::Spellmaking}, + { Columns::ColumnId_EnchantingService, ESM::NPC::Enchanting}, + { Columns::ColumnId_RepairService, ESM::NPC::Repair}, + { -1, 0 } + }; + + for (int i=0; sServiceTable[i].mName!=-1; ++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 (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean)); + const RefIdColumn *autoCalc = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType, + ColumnBase::Display_ApparatusType)); + const RefIdColumn *apparatusType = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorType, ColumnBase::Display_ArmorType)); + const RefIdColumn *armorType = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Health, ColumnBase::Display_Integer)); + const RefIdColumn *health = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorValue, ColumnBase::Display_Integer)); + const RefIdColumn *armor = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Scroll, ColumnBase::Display_Boolean)); + const RefIdColumn *scroll = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)); + const RefIdColumn *attribute = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_ClothingType, ColumnBase::Display_ClothingType)); + const RefIdColumn *clothingType = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_WeightCapacity, ColumnBase::Display_Float)); + const RefIdColumn *weightCapacity = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_OrganicContainer, ColumnBase::Display_Boolean)); + const RefIdColumn *organic = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Respawn, ColumnBase::Display_Boolean)); + const RefIdColumn *respawn = &mColumns.back(); + + CreatureColumns creatureColumns (actorsColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType)); + creatureColumns.mType = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_SoulPoints, ColumnBase::Display_Integer)); + creatureColumns.mSoul = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Scale, ColumnBase::Display_Float)); + creatureColumns.mScale = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_OriginalCreature, ColumnBase::Display_String)); + creatureColumns.mOriginal = &mColumns.back(); + + static const struct + { + int mName; + unsigned int mFlag; + } sCreatureFlagTable[] = + { + { Columns::ColumnId_Biped, ESM::Creature::Biped }, + { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon }, + { Columns::ColumnId_NoMovement, ESM::Creature::None }, + { Columns::ColumnId_Swims, ESM::Creature::Swims }, + { Columns::ColumnId_Flies, ESM::Creature::Flies }, + { Columns::ColumnId_Walks, ESM::Creature::Walks }, + { Columns::ColumnId_Essential, ESM::Creature::Essential }, + { Columns::ColumnId_SkeletonBlood, ESM::Creature::Skeleton }, + { Columns::ColumnId_MetalBlood, ESM::Creature::Metal }, + { -1, 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!=-1; ++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 (Columns::ColumnId_OpenSound, ColumnBase::Display_String)); + const RefIdColumn *openSound = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_String)); + const RefIdColumn *closeSound = &mColumns.back(); + + LightColumns lightColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); + lightColumns.mTime = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer)); + lightColumns.mRadius = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer)); + lightColumns.mColor = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_String)); + lightColumns.mSound = &mColumns.back(); + + static const struct + { + int mName; + unsigned int mFlag; + } sLightFlagTable[] = + { + { Columns::ColumnId_Dynamic, ESM::Light::Dynamic }, + { Columns::ColumnId_Portable, ESM::Light::Carry }, + { Columns::ColumnId_NegativeLight, ESM::Light::Negative }, + { Columns::ColumnId_Flickering, ESM::Light::Flicker }, + { Columns::ColumnId_SlowFlickering, ESM::Light::Flicker }, + { Columns::ColumnId_Pulsing, ESM::Light::Pulse }, + { Columns::ColumnId_SlowPulsing, ESM::Light::PulseSlow }, + { Columns::ColumnId_Fire, ESM::Light::Fire }, + { Columns::ColumnId_OffByDefault, ESM::Light::OffDefault }, + { -1, 0 } + }; + + for (int i=0; sLightFlagTable[i].mName!=-1; ++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 (Columns::ColumnId_IsKey, ColumnBase::Display_Boolean)); + const RefIdColumn *key = &mColumns.back(); + + NpcColumns npcColumns (actorsColumns); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_String)); + npcColumns.mRace = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_String)); + npcColumns.mClass = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_String)); + npcColumns.mFaction = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_String)); + npcColumns.mHair = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_String)); + npcColumns.mHead = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_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 (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType)); + weaponColumns.mType = &mColumns.back(); + + weaponColumns.mHealth = health; + + mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponSpeed, ColumnBase::Display_Float)); + weaponColumns.mSpeed = &mColumns.back(); + + mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponReach, ColumnBase::Display_Float)); + weaponColumns.mReach = &mColumns.back(); + + for (int i=0; i<6; ++i) + { + mColumns.push_back (RefIdColumn (Columns::ColumnId_MinChop + i, ColumnBase::Display_Integer)); + weaponColumns.mChop[i] = &mColumns.back(); + } + + static const struct + { + int mName; + unsigned int mFlag; + } sWeaponFlagTable[] = + { + { Columns::ColumnId_Magical, ESM::Weapon::Magical }, + { Columns::ColumnId_Silver, ESM::Weapon::Silver }, + { -1, 0 } + }; + + for (int i=0; sWeaponFlagTable[i].mName!=-1; ++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); +} + +std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) const +{ + return mData.getIds (listDeleted); +} diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp new file mode 100644 index 000000000..22f83150d --- /dev/null +++ b/apps/opencs/model/world/refidcollection.hpp @@ -0,0 +1,100 @@ +#ifndef CSM_WOLRD_REFIDCOLLECTION_H +#define CSM_WOLRD_REFIDCOLLECTION_H + +#include +#include +#include + +#include "columnbase.hpp" +#include "collectionbase.hpp" +#include "refiddata.hpp" + +namespace CSMWorld +{ + class RefIdAdapter; + + class RefIdColumn : public ColumnBase + { + bool mEditable; + bool mUserEditable; + + public: + + RefIdColumn (int columnId, 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 CollectionBase + { + 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; + + 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 + + virtual std::vector getIds (bool listDeleted) const; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + }; +} + +#endif diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp new file mode 100644 index 000000000..9457937f1 --- /dev/null +++ b/apps/opencs/model/world/refiddata.cpp @@ -0,0 +1,220 @@ + +#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(); +} + +std::vector CSMWorld::RefIdData::getIds (bool listDeleted) const +{ + std::vector ids; + + for (std::map::const_iterator iter (mIndex.begin()); iter!=mIndex.end(); + ++iter) + { + if (listDeleted || !getRecord (iter->second).isDeleted()) + { + std::map::const_iterator container = + mRecordContainers.find (iter->second.second); + + if (container==mRecordContainers.end()) + throw std::logic_error ("Invalid referenceable ID type"); + + ids.push_back (container->second->getId (iter->second.first)); + } + } + + return ids; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp new file mode 100644 index 000000000..e221fbc7c --- /dev/null +++ b/apps/opencs/model/world/refiddata.hpp @@ -0,0 +1,193 @@ +#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; + + std::vector getIds (bool listDeleted = true) const; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + }; +} + +#endif diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp new file mode 100644 index 000000000..697c5f450 --- /dev/null +++ b/apps/opencs/model/world/regionmap.cpp @@ -0,0 +1,513 @@ + +#include "regionmap.hpp" + +#include + +#include + +#include + +#include "data.hpp" +#include "universalid.hpp" + +CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} + +CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) +{ + const Cell& cell2 = cell.get(); + + if (!cell2.isExterior()) + throw std::logic_error ("Interior cell in region map"); + + mDeleted = cell.isDeleted(); + + mRegion = cell2.mRegion; + mName = cell2.mName; +} + +CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const +{ + return CellIndex (index.column()+mMin.first, + (mMax.second-mMin.second - index.row()-1)+mMin.second); +} + +QModelIndex CSMWorld::RegionMap::getIndex (const CellIndex& index) const +{ + // I hate you, Qt API naming scheme! + return QAbstractTableModel::index (mMax.second-mMin.second - (index.second-mMin.second)-1, + index.first-mMin.first); +} + +void CSMWorld::RegionMap::buildRegions() +{ + const IdCollection& regions = mData.getRegions(); + + int size = regions.getSize(); + + for (int i=0; i& region = regions.getRecord (i); + + if (!region.isDeleted()) + mColours.insert (std::make_pair (Misc::StringUtils::lowerCase (region.get().mId), + region.get().mMapColor)); + } +} + +void CSMWorld::RegionMap::buildMap() +{ + const IdCollection& cells = mData.getCells(); + + int size = cells.getSize(); + + for (int i=0; i& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellDescription description (cell); + + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + mMap.insert (std::make_pair (index, description)); + } + } + + std::pair mapSize = getSize(); + + mMin = mapSize.first; + mMax = mapSize.second; +} + +void CSMWorld::RegionMap::addCell (const CellIndex& index, const CellDescription& description) +{ + std::map::iterator cell = mMap.find (index); + + if (cell!=mMap.end()) + { + cell->second = description; + } + else + { + updateSize(); + + mMap.insert (std::make_pair (index, description)); + } + + QModelIndex index2 = getIndex (index); + + dataChanged (index2, index2); +} + +void CSMWorld::RegionMap::addCells (int start, int end) +{ + const IdCollection& cells = mData.getCells(); + + for (int i=start; i<=end; ++i) + { + const Record& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + CellDescription description (cell); + + addCell (index, description); + } + } +} + +void CSMWorld::RegionMap::removeCell (const CellIndex& index) +{ + std::map::iterator cell = mMap.find (index); + + if (cell!=mMap.end()) + { + mMap.erase (cell); + + QModelIndex index2 = getIndex (index); + + dataChanged (index2, index2); + + updateSize(); + } +} + +void CSMWorld::RegionMap::addRegion (const std::string& region, unsigned int colour) +{ + mColours[Misc::StringUtils::lowerCase (region)] = colour; +} + +void CSMWorld::RegionMap::removeRegion (const std::string& region) +{ + std::map::iterator iter ( + mColours.find (Misc::StringUtils::lowerCase (region))); + + if (iter!=mColours.end()) + mColours.erase (iter); +} + +void CSMWorld::RegionMap::updateRegions (const std::vector& regions) +{ + std::vector regions2 (regions); + + std::for_each (regions2.begin(), regions2.end(), &Misc::StringUtils::lowerCase); + std::sort (regions2.begin(), regions2.end()); + + for (std::map::const_iterator iter (mMap.begin()); + iter!=mMap.end(); ++iter) + { + if (!iter->second.mRegion.empty() && + std::find (regions2.begin(), regions2.end(), + Misc::StringUtils::lowerCase (iter->second.mRegion))!=regions2.end()) + { + QModelIndex index = getIndex (iter->first); + + dataChanged (index, index); + } + } +} + +void CSMWorld::RegionMap::updateSize() +{ + std::pair size = getSize(); + + { + int diff = size.first.first - mMin.first; + + if (diff<0) + { + beginInsertColumns (QModelIndex(), 0, -diff-1); + mMin.first = size.first.first; + endInsertColumns(); + } + else if (diff>0) + { + beginRemoveColumns (QModelIndex(), 0, diff-1); + mMin.first = size.first.first; + endRemoveColumns(); + } + } + + { + int diff = size.first.second - mMin.second; + + if (diff<0) + { + beginInsertRows (QModelIndex(), 0, -diff-1); + mMin.second = size.first.second; + endInsertRows(); + } + else if (diff>0) + { + beginRemoveRows (QModelIndex(), 0, diff-1); + mMin.second = size.first.second; + endRemoveRows(); + } + } + + { + int diff = size.second.first - mMax.first; + + if (diff>0) + { + int columns = columnCount(); + beginInsertColumns (QModelIndex(), columns, columns+diff-1); + mMax.first = size.second.first; + endInsertColumns(); + } + else if (diff<0) + { + int columns = columnCount(); + beginRemoveColumns (QModelIndex(), columns+diff, columns-1); + mMax.first = size.second.first; + endRemoveColumns(); + } + } + + { + int diff = size.second.second - mMax.second; + + if (diff>0) + { + int rows = rowCount(); + beginInsertRows (QModelIndex(), rows, rows+diff-1); + mMax.second = size.second.second; + endInsertRows(); + } + else if (diff<0) + { + int rows = rowCount(); + beginRemoveRows (QModelIndex(), rows+diff, rows-1); + mMax.second = size.second.second; + endRemoveRows(); + } + } +} + +std::pair CSMWorld::RegionMap::getSize() + const +{ + const IdCollection& cells = mData.getCells(); + + int size = cells.getSize(); + + CellIndex min (0, 0); + CellIndex max (0, 0); + + for (int i=0; i& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + if (min==max) + { + min = index; + max = std::make_pair (min.first+1, min.second+1); + } + else + { + if (index.first=max.first) + max.first = index.first + 1; + + if (index.second=max.second) + max.second = index.second + 1; + } + } + } + + return std::make_pair (min, max); +} + +CSMWorld::RegionMap::RegionMap (Data& data) : mData (data) +{ + buildRegions(); + buildMap(); + + QAbstractItemModel *regions = data.getTableModel (UniversalId (UniversalId::Type_Regions)); + + connect (regions, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); + connect (regions, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (regionsInserted (const QModelIndex&, int, int))); + connect (regions, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); + + QAbstractItemModel *cells = data.getTableModel (UniversalId (UniversalId::Type_Cells)); + + connect (cells, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (cellsAboutToBeRemoved (const QModelIndex&, int, int))); + connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (cellsInserted (const QModelIndex&, int, int))); + connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (cellsChanged (const QModelIndex&, const QModelIndex&))); +} + +int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return mMax.second-mMin.second; +} + +int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return mMax.first-mMin.first; +} + +QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const +{ + if (role==Qt::SizeHintRole) + return QSize (16, 16); + + if (role==Qt::BackgroundRole) + { + /// \todo GUI class in non-GUI code. Needs to be addressed eventually. + + std::map::const_iterator cell = + mMap.find (getIndex (index)); + + if (cell!=mMap.end()) + { + if (cell->second.mDeleted) + return QBrush (Qt::red, Qt::DiagCrossPattern); + + std::map::const_iterator iter = + mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); + + if (iter!=mColours.end()) + return QBrush ( + QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, + iter->second & 255)); + + if (cell->second.mRegion.empty()) + return QBrush (Qt::Dense6Pattern); // no region + + return QBrush (Qt::red, Qt::Dense6Pattern); // invalid region + } + + return QBrush (Qt::DiagCrossPattern); + } + + if (role==Qt::ToolTipRole) + { + CellIndex cellIndex = getIndex (index); + + std::ostringstream stream; + + stream << cellIndex.first << ", " << cellIndex.second; + + std::map::const_iterator cell = + mMap.find (cellIndex); + + if (cell!=mMap.end()) + { + if (!cell->second.mName.empty()) + stream << " " << cell->second.mName; + + if (cell->second.mDeleted) + stream << " (deleted)"; + + if (!cell->second.mRegion.empty()) + { + stream << "
"; + + std::map::const_iterator iter = + mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); + + if (iter!=mColours.end()) + stream << cell->second.mRegion; + else + stream << "" << cell->second.mRegion << ""; + } + } + else + stream << " (no cell)"; + + return QString::fromUtf8 (stream.str().c_str()); + } + + return QVariant(); +} + +Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const +{ + if (mMap.find (getIndex (index))!=mMap.end()) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + return 0; +} + +void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + std::vector update; + + const IdCollection& regions = mData.getRegions(); + + for (int i=start; i<=end; ++i) + { + const Record& region = regions.getRecord (i); + + update.push_back (region.get().mId); + + removeRegion (region.get().mId); + } + + updateRegions (update); +} + +void CSMWorld::RegionMap::regionsInserted (const QModelIndex& parent, int start, int end) +{ + std::vector update; + + const IdCollection& regions = mData.getRegions(); + + for (int i=start; i<=end; ++i) + { + const Record& region = regions.getRecord (i); + + if (!region.isDeleted()) + { + update.push_back (region.get().mId); + + addRegion (region.get().mId, region.get().mMapColor); + } + } + + updateRegions (update); +} + +void CSMWorld::RegionMap::regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + // Note: At this point an additional check could be inserted to see if there is any change to the + // columns we are interested in. If not we can exit the function here and avoid all updating. + + std::vector update; + + const IdCollection& regions = mData.getRegions(); + + for (int i=topLeft.row(); i<=bottomRight.column(); ++i) + { + const Record& region = regions.getRecord (i); + + update.push_back (region.get().mId); + + if (!region.isDeleted()) + addRegion (region.get().mId, region.get().mMapColor); + else + removeRegion (region.get().mId); + } + + updateRegions (update); +} + +void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + const IdCollection& cells = mData.getCells(); + + for (int i=start; i<=end; ++i) + { + const Record& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + removeCell (index); + } + } +} + +void CSMWorld::RegionMap::cellsInserted (const QModelIndex& parent, int start, int end) +{ + addCells (start, end); +} + +void CSMWorld::RegionMap::cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + // Note: At this point an additional check could be inserted to see if there is any change to the + // columns we are interested in. If not we can exit the function here and avoid all updating. + + addCells (topLeft.row(), bottomRight.row()); +} \ No newline at end of file diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp new file mode 100644 index 000000000..7fb89f20a --- /dev/null +++ b/apps/opencs/model/world/regionmap.hpp @@ -0,0 +1,111 @@ +#ifndef CSM_WOLRD_REGIONMAP_H +#define CSM_WOLRD_REGIONMAP_H + +#include +#include +#include + +#include + +#include "record.hpp" +#include "cell.hpp" + +namespace CSMWorld +{ + class Data; + + /// \brief Model for the region map + /// + /// This class does not holds any record data (other than for the purpose of buffering). + class RegionMap : public QAbstractTableModel + { + Q_OBJECT + + public: + + typedef std::pair CellIndex; + + private: + + struct CellDescription + { + bool mDeleted; + std::string mRegion; + std::string mName; + + CellDescription(); + + CellDescription (const Record& cell); + }; + + Data& mData; + std::map mMap; + CellIndex mMin; ///< inclusive + CellIndex mMax; ///< exclusive + std::map mColours; ///< region ID, colour (RGBA) + + CellIndex getIndex (const QModelIndex& index) const; + ///< Translates a Qt model index into a cell index (which can contain negative components) + + QModelIndex getIndex (const CellIndex& index) const; + + void buildRegions(); + + void buildMap(); + + void addCell (const CellIndex& index, const CellDescription& description); + ///< May be called on a cell that is already in the map (in which case an update is + // performed) + + void addCells (int start, int end); + + void removeCell (const CellIndex& index); + ///< May be called on a cell that is not in the map (in which case the call is ignored) + + void addRegion (const std::string& region, unsigned int colour); + ///< May be called on a region that is already listed (in which case an update is + /// performed) + /// + /// \note This function does not update the region map. + + void removeRegion (const std::string& region); + ///< May be called on a region that is not listed (in which case the call is ignored) + /// + /// \note This function does not update the region map. + + void updateRegions (const std::vector& regions); + ///< Update cells affected by the listed regions + + void updateSize(); + + std::pair getSize() const; + + public: + + RegionMap (Data& data); + + virtual int rowCount (const QModelIndex& parent = QModelIndex()) const; + + virtual int columnCount (const QModelIndex& parent = QModelIndex()) const; + + virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; + + virtual Qt::ItemFlags flags (const QModelIndex& index) const; + + private slots: + + void regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void regionsInserted (const QModelIndex& parent, int start, int end); + + void regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void cellsInserted (const QModelIndex& parent, int start, int end); + + void cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + }; +} + +#endif diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp new file mode 100644 index 000000000..86689d823 --- /dev/null +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -0,0 +1,44 @@ + +#include "scriptcontext.hpp" + +#include + +#include + +#include "data.hpp" + +CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {} + +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 +{ + if (!mIdsUpdated) + { + mIds = mData.getIds(); + + std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCase); + + mIdsUpdated = true; + } + + return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name)); +} + +void CSMWorld::ScriptContext::invalidateIds() +{ + mIdsUpdated = 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..b839b5a43 --- /dev/null +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -0,0 +1,39 @@ +#ifndef CSM_WORLD_SCRIPTCONTEXT_H +#define CSM_WORLD_SCRIPTCONTEXT_H + +#include +#include + +#include + +namespace CSMWorld +{ + class Data; + + class ScriptContext : public Compiler::Context + { + const Data& mData; + mutable std::vector mIds; + mutable bool mIdsUpdated; + + public: + + ScriptContext (const Data& data); + + 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? + + void invalidateIds(); + }; +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index c006852bc..d360fde8f 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -12,30 +12,83 @@ namespace CSMWorld::UniversalId::Class mClass; CSMWorld::UniversalId::Type mType; const char *mName; + const char *mIcon; }; static const TypeData sNoArg[] = { - { 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_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, + "Referenceables", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, + "References", 0 }, + { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, + "Region Map", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, + + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; 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_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":./armor.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":./book.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":./clothing.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container", ":./container.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":./creature.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, + "Creature Levelled List", ":./creature.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, + "Item Levelled List", ":./leveled-item.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, + "Miscellaneous", ":./miscellaneous.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", 0 }, + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; } @@ -43,48 +96,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; - 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); + 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; - } - 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); @@ -99,6 +149,22 @@ CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_Non return; } + for (int i=0; sIdArg[i].mName; ++i) + if (type==sIdArg[i].mType) + { + mArgumentType = ArgumentType_Id; + mClass = sIdArg[i].mClass; + return; + } + + for (int i=0; sIndexArg[i].mName; ++i) + if (type==sIndexArg[i].mType) + { + mArgumentType = ArgumentType_Index; + mClass = sIndexArg[i].mClass; + return; + } + throw std::logic_error ("invalid argument-less UniversalId type"); } @@ -218,6 +284,29 @@ std::string CSMWorld::UniversalId::toString() const return stream.str(); } +std::string CSMWorld::UniversalId::getIcon() const +{ + const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg : + (mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg); + + for (int i=0; typeData[i].mName; ++i) + if (typeData[i].mType==mType) + return typeData[i].mIcon ? typeData[i].mIcon : ""; + + throw std::logic_error ("failed to retrieve UniversalId type icon"); +} + +std::vector CSMWorld::UniversalId::listReferenceableTypes() +{ + std::vector list; + + for (int i=0; sIdArg[i].mName; ++i) + if (sIdArg[i].mClass==Class_RefRecord) + list.push_back (sIdArg[i].mType); + + return list; +} + bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) { return left.isEqual (right); @@ -236,4 +325,4 @@ bool CSMWorld::operator< (const UniversalId& left, const UniversalId& right) std::ostream& CSMWorld::operator< (std::ostream& stream, const CSMWorld::UniversalId& universalId) { return stream << universalId.toString(); -} \ No newline at end of file +} diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 9ff7d17b1..aa0cdacc0 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -14,13 +15,14 @@ namespace CSMWorld enum Class { - Class_None = 0, - Class_Record, - Class_SubRecord, - Class_RecordList, - Class_Collection, // multiple types of records combined - Class_Transient, // not part of the world data or the project data - Class_NonRecord // record like data that is not part of the world + Class_None = 0, + Class_Record, + Class_RefRecord, // referenceable record + Class_SubRecord, + Class_RecordList, + Class_Collection, // multiple types of records combined + Class_Transient, // not part of the world data or the project data + Class_NonRecord // record like data that is not part of the world }; enum ArgumentType @@ -32,15 +34,63 @@ namespace CSMWorld enum Type { - Type_None, + Type_None = 0, Type_Globals, Type_Global, Type_VerificationResults, Type_Gmsts, - Type_Gmst - + 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_References, + Type_Reference, + Type_RegionMap, + Type_Filter, + Type_Filters }; + enum { NumberOfTypes = Type_Filters+1 }; + private: Class mClass; @@ -54,7 +104,6 @@ namespace CSMWorld UniversalId (const std::string& universalId); UniversalId (Type type = Type_None); - ///< Using a type for a non-argument-less UniversalId will throw an exception. UniversalId (Type type, const std::string& id); ///< Using a type for a non-ID-argument UniversalId will throw an exception. @@ -81,6 +130,11 @@ namespace CSMWorld std::string getTypeName() const; std::string toString() const; + + std::string getIcon() const; + ///< Will return an empty string, if no icon is available. + + static std::vector listReferenceableTypes(); }; bool operator== (const UniversalId& left, const UniversalId& right); 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/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp new file mode 100644 index 000000000..910819700 --- /dev/null +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -0,0 +1,91 @@ + +#include "adjusterwidget.hpp" + +#include +#include + +#include + +#include +#include +#include + +CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) +: QWidget (parent), mValid (false) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + mIcon = new QLabel (this); + + layout->addWidget (mIcon, 0); + + mMessage = new QLabel (this); + mMessage->setWordWrap (true); + mMessage->setSizePolicy (QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum)); + + layout->addWidget (mMessage, 1); + + setName ("", false); + + setLayout (layout); +} + +void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData) +{ + mLocalData = localData; +} + +boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const +{ + if (!mValid) + throw std::logic_error ("invalid content file path"); + + return mResultPath; +} + +void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) +{ + QString message; + + if (name.isEmpty()) + { + mValid = false; + message = "No name."; + } + else + { + boost::filesystem::path path (name.toUtf8().data()); + + path.replace_extension (addon ? ".omwaddon" : ".omwgame"); + + if (path.parent_path().string()==mLocalData.string()) + { + // path already points to the local data directory + message = QString::fromUtf8 (("Will be saved as: " + path.native()).c_str()); + mResultPath = path; + mValid = true; + } + else + { + // path points somewhere else or is a leaf name. + path = mLocalData / path.filename(); + + message = QString::fromUtf8 (("Will be saved as: " + path.native()).c_str()); + mResultPath = path; + mValid = true; + + if (boost::filesystem::exists (path)) + { + /// \todo add an user setting to make this an error. + message += "

But a file with the same name already exists. If you continue, it will be overwritten."; + } + } + } + + mMessage->setText (message); + mIcon->setPixmap (style()->standardIcon ( + mValid ? QStyle::SP_MessageBoxInformation : QStyle::SP_MessageBoxWarning). + pixmap (QSize (16, 16))); + + emit stateChanged (mValid); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp new file mode 100644 index 000000000..f578dc4ae --- /dev/null +++ b/apps/opencs/view/doc/adjusterwidget.hpp @@ -0,0 +1,41 @@ +#ifndef CSV_DOC_ADJUSTERWIDGET_H +#define CSV_DOC_ADJUSTERWIDGET_H + +#include + +#include + +class QLabel; + +namespace CSVDoc +{ + class AdjusterWidget : public QWidget + { + Q_OBJECT + + boost::filesystem::path mLocalData; + QLabel *mMessage; + QLabel *mIcon; + bool mValid; + boost::filesystem::path mResultPath; + + public: + + AdjusterWidget (QWidget *parent = 0); + + void setLocalData (const boost::filesystem::path& localData); + + boost::filesystem::path getPath() const; + ///< This function must not be called if there is no valid path. + + public slots: + + void setName (const QString& name, bool addon); + + signals: + + void stateChanged (bool valid); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/doc/filewidget.cpp b/apps/opencs/view/doc/filewidget.cpp new file mode 100644 index 000000000..c8f33e92d --- /dev/null +++ b/apps/opencs/view/doc/filewidget.cpp @@ -0,0 +1,53 @@ + +#include "filewidget.hpp" + +#include +#include +#include +#include +#include + +QString CSVDoc::FileWidget::getExtension() const +{ + return mAddon ? ".omwaddon" : ".omwgame"; +} + +CSVDoc::FileWidget::FileWidget (QWidget *parent) : QWidget (parent), mAddon (false) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + mInput = new QLineEdit (this); + mInput->setValidator (new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$"))); + + layout->addWidget (mInput, 1); + + mType = new QLabel (this); + + layout ->addWidget (mType); + + connect (mInput, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); + + setLayout (layout); +} + +void CSVDoc::FileWidget::setType (bool addon) +{ + mAddon = addon; + + mType->setText (getExtension()); +} + +QString CSVDoc::FileWidget::getName() const +{ + QString text = mInput->text(); + + if (text.isEmpty()) + return ""; + + return text + getExtension(); +} + +void CSVDoc::FileWidget::textChanged (const QString& text) +{ + emit nameChanged (getName(), mAddon); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/filewidget.hpp b/apps/opencs/view/doc/filewidget.hpp new file mode 100644 index 000000000..c51c29632 --- /dev/null +++ b/apps/opencs/view/doc/filewidget.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_DOC_FILEWIDGET_H +#define CSV_DOC_FILEWIDGET_H + +#include + +class QLabel; +class QString; +class QLineEdit; + +namespace CSVDoc +{ + class FileWidget : public QWidget + { + Q_OBJECT + + bool mAddon; + QLineEdit *mInput; + QLabel *mType; + + QString getExtension() const; + + public: + + FileWidget (QWidget *parent = 0); + + void setType (bool addon); + + QString getName() const; + + private slots: + + void textChanged (const QString& text); + + signals: + + void nameChanged (const QString& file, bool addon); + }; +} + +#endif diff --git a/apps/opencs/view/doc/newgame.cpp b/apps/opencs/view/doc/newgame.cpp new file mode 100644 index 000000000..98681c499 --- /dev/null +++ b/apps/opencs/view/doc/newgame.cpp @@ -0,0 +1,68 @@ + +#include "newgame.hpp" + +#include +#include +#include +#include +#include + +#include "filewidget.hpp" +#include "adjusterwidget.hpp" + +CSVDoc::NewGameDialogue::NewGameDialogue() +{ + setWindowTitle ("Create New Game"); + + QVBoxLayout *layout = new QVBoxLayout (this); + + mFileWidget = new FileWidget (this); + mFileWidget->setType (false); + + layout->addWidget (mFileWidget, 1); + + mAdjusterWidget = new AdjusterWidget (this); + + layout->addWidget (mAdjusterWidget, 1); + + QDialogButtonBox *buttons = new QDialogButtonBox (this); + + mCreate = new QPushButton ("Create", this); + mCreate->setDefault (true); + mCreate->setEnabled (false); + + buttons->addButton (mCreate, QDialogButtonBox::AcceptRole); + + QPushButton *cancel = new QPushButton ("Cancel", this); + + buttons->addButton (cancel, QDialogButtonBox::RejectRole); + + layout->addWidget (buttons); + + setLayout (layout); + + connect (mAdjusterWidget, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool))); + connect (mCreate, SIGNAL (clicked()), this, SLOT (create())); + connect (cancel, SIGNAL (clicked()), this, SLOT (reject())); + connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), + mAdjusterWidget, SLOT (setName (const QString&, bool))); + + QRect scr = QApplication::desktop()->screenGeometry(); + QRect rect = geometry(); + move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); +} + +void CSVDoc::NewGameDialogue::setLocalData (const boost::filesystem::path& localData) +{ + mAdjusterWidget->setLocalData (localData); +} + +void CSVDoc::NewGameDialogue::stateChanged (bool valid) +{ + mCreate->setEnabled (valid); +} + +void CSVDoc::NewGameDialogue::create() +{ + emit createRequest (mAdjusterWidget->getPath()); +} diff --git a/apps/opencs/view/doc/newgame.hpp b/apps/opencs/view/doc/newgame.hpp new file mode 100644 index 000000000..aa97682ff --- /dev/null +++ b/apps/opencs/view/doc/newgame.hpp @@ -0,0 +1,44 @@ +#ifndef CSV_DOC_NEWGAME_H +#define CSV_DOC_NEWGAME_H + +#include + +#include +#include + +Q_DECLARE_METATYPE (boost::filesystem::path) + +class QPushButton; + +namespace CSVDoc +{ + class FileWidget; + class AdjusterWidget; + + class NewGameDialogue : public QDialog + { + Q_OBJECT + + QPushButton *mCreate; + FileWidget *mFileWidget; + AdjusterWidget *mAdjusterWidget; + + public: + + NewGameDialogue(); + + void setLocalData (const boost::filesystem::path& localData); + + signals: + + void createRequest (const boost::filesystem::path& file); + + private slots: + + void stateChanged (bool valid); + + void create(); + }; +} + +#endif diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index ce001afaa..7ee4b8726 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -56,7 +56,7 @@ void CSVDoc::Operations::quitOperation (int type) mLayout->removeItem ((*iter)->getLayout()); - delete *iter; + (*iter)->deleteLater(); mOperations.erase (iter); if (oldCount > 1) diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 7861a1c2e..4cc64f2df 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -1,20 +1,112 @@ #include "startup.hpp" -#include +#include +#include +#include #include +#include +#include +#include +#include +#include -CSVDoc::StartupDialogue::StartupDialogue() +QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon) { - QHBoxLayout *layout = new QHBoxLayout (this); + int column = mColumn--; + + QPushButton *button = new QPushButton (this); + + button->setIcon (QIcon (icon)); + + button->setSizePolicy (QSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred)); + + mLayout->addWidget (button, 0, column); + + mLayout->addWidget (new QLabel (label, this), 1, column, Qt::AlignCenter); + + int width = mLayout->itemAtPosition (1, column)->widget()->sizeHint().width(); + + if (width>mWidth) + mWidth = width; + + return button; +} - QPushButton *createDocument = new QPushButton ("new", this); - connect (createDocument, SIGNAL (clicked()), this, SIGNAL (createDocument())); - layout->addWidget (createDocument); - QPushButton *loadDocument = new QPushButton ("load", this); +QWidget *CSVDoc::StartupDialogue::createButtons() +{ + QWidget *widget = new QWidget (this); + + mLayout = new QGridLayout (widget); + + /// \todo add icons + QPushButton *loadDocument = addButton ("Edit A Content File", QIcon (":startup/edit-content")); connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument())); - layout->addWidget (loadDocument); + + QPushButton *createAddon = addButton ("Create A New Addon", QIcon (":startup/create-addon")); + connect (createAddon, SIGNAL (clicked()), this, SIGNAL (createAddon())); + + QPushButton *createGame = addButton ("Create A New Game", QIcon (":startup/create-game")); + connect (createGame, SIGNAL (clicked()), this, SIGNAL (createGame())); + + for (int i=0; i<3; ++i) + mLayout->setColumnMinimumWidth (i, mWidth); + + mLayout->setRowMinimumHeight (0, mWidth); + + mLayout->setSizeConstraint (QLayout::SetMinimumSize); + mLayout->setHorizontalSpacing (32); + + mLayout->setContentsMargins (16, 16, 16, 8); + + loadDocument->setIconSize (QSize (mWidth, mWidth)); + createGame->setIconSize (QSize (mWidth, mWidth)); + createAddon->setIconSize (QSize (mWidth, mWidth)); + + widget->setLayout (mLayout); + + return widget; +} + +QWidget *CSVDoc::StartupDialogue::createTools() +{ + QWidget *widget = new QWidget (this); + + QHBoxLayout *layout = new QHBoxLayout (widget); + layout->setDirection (QBoxLayout::RightToLeft); + layout->setContentsMargins (4, 4, 4, 4); + + QPushButton *config = new QPushButton (widget); + + config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + config->setIcon (QIcon (":startup/configure")); + + layout->addWidget (config); + + layout->addWidget (new QWidget, 1); // dummy widget; stops buttons from taking all the space + + widget->setLayout (layout); + + connect (config, SIGNAL (clicked()), this, SIGNAL (editConfig())); + + return widget; +} + +CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) +{ + setWindowTitle ("Open CS"); + + QVBoxLayout *layout = new QVBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + layout->addWidget (createButtons()); + layout->addWidget (createTools()); setLayout (layout); -} \ No newline at end of file + + 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 index f24d2a64b..f059a44e5 100644 --- a/apps/opencs/view/doc/startup.hpp +++ b/apps/opencs/view/doc/startup.hpp @@ -3,21 +3,43 @@ #include +class QGridLayout; +class QString; +class QPushButton; +class QWidget; +class QIcon; + namespace CSVDoc { class StartupDialogue : public QWidget { Q_OBJECT + private: + + int mWidth; + int mColumn; + QGridLayout *mLayout; + + QPushButton *addButton (const QString& label, const QIcon& icon); + + QWidget *createButtons(); + + QWidget *createTools(); + public: StartupDialogue(); signals: - void createDocument(); + void createGame(); + + void createAddon(); void loadDocument(); + + void editConfig(); }; } diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index affada012..09361a1c0 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -5,13 +5,15 @@ CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) /// \todo add a button to the title bar that clones this sub view setWindowTitle (mUniversalId.toString().c_str()); - - /// \todo remove (for testing only) - setMinimumWidth (100); - setMinimumHeight (60); } CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const { return mUniversalId; } + +void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QString &settingValue) +{ +} + +void CSVDoc::SubView::setStatusBar (bool show) {} \ No newline at end of file diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 985c5eb3c..aa073f81d 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -35,6 +35,10 @@ namespace CSVDoc CSMWorld::UniversalId getUniversalId() const; virtual void setEditLock (bool locked) = 0; + virtual void updateEditorSetting (const QString &, const QString &); + + virtual void setStatusBar (bool show); + ///< Default implementation: ignored signals: @@ -42,4 +46,4 @@ namespace CSVDoc }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/subviewfactoryimp.hpp b/apps/opencs/view/doc/subviewfactoryimp.hpp index d16e0b2b7..2c4158f85 100644 --- a/apps/opencs/view/doc/subviewfactoryimp.hpp +++ b/apps/opencs/view/doc/subviewfactoryimp.hpp @@ -22,28 +22,20 @@ namespace CSVDoc return new SubViewT (id, document); } - template - class SubViewFactoryWithCreateFlag : public SubViewFactoryBase - { - bool mCreateAndDelete; + template + class SubViewFactoryWithCreator : public SubViewFactoryBase + { public: - SubViewFactoryWithCreateFlag (bool createAndDelete); - virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; - template - SubViewFactoryWithCreateFlag::SubViewFactoryWithCreateFlag (bool createAndDelete) - : mCreateAndDelete (createAndDelete) - {} - - template - CSVDoc::SubView *SubViewFactoryWithCreateFlag::makeSubView (const CSMWorld::UniversalId& id, - CSMDoc::Document& document) + template + CSVDoc::SubView *SubViewFactoryWithCreator::makeSubView ( + const CSMWorld::UniversalId& id, CSMDoc::Document& document) { - return new SubViewT (id, document, mCreateAndDelete); + return new SubViewT (id, document, CreatorFactoryT()); } } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 286d7a6ed..7183753e1 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -7,13 +7,12 @@ #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" @@ -28,9 +27,13 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("&File")); - QAction *new_ = new QAction (tr ("New"), this); - connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest())); - file->addAction (new_); + QAction *newGame = new QAction (tr ("New Game"), this); + connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); + file->addAction (newGame); + + QAction *newAddon = new QAction (tr ("New Addon"), this); + connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); + file->addAction (newAddon); QAction *open = new QAction (tr ("&Open"), this); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); @@ -39,6 +42,20 @@ void CSVDoc::View::setupFileMenu() 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() @@ -52,6 +69,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, SIGNAL (editSettingsRequest())); + edit->addAction (userSettings); } void CSVDoc::View::setupViewMenu() @@ -61,23 +82,92 @@ void CSVDoc::View::setupViewMenu() QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); view->addAction (newWindow); + + mShowStatusBar = new QAction (tr ("Show Status Bar"), this); + mShowStatusBar->setCheckable (true); + connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); + view->addAction (mShowStatusBar); + + QAction *filters = new QAction (tr ("Filters"), this); + connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView())); + view->addAction (filters); } void CSVDoc::View::setupWorldMenu() { QMenu *world = menuBar()->addMenu (tr ("&World")); + QAction *regions = new QAction (tr ("Regions"), this); + connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); + world->addAction (regions); + + 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); + + QAction *references = new QAction (tr ("References"), this); + connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); + world->addAction (references); + + world->addSeparator(); // items that don't represent single record lists follow here + + QAction *regionMap = new QAction (tr ("Region Map"), this); + connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); + world->addAction (regionMap); +} + +void CSVDoc::View::setupMechanicsMenu() +{ + QMenu *mechanics = menuBar()->addMenu (tr ("&Mechanics")); + QAction *globals = new QAction (tr ("Globals"), this); connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); - world->addAction (globals); + mechanics->addAction (globals); QAction *gmsts = new QAction (tr ("Game settings"), this); connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); - world->addAction (gmsts); + mechanics->addAction (gmsts); - mVerify = new QAction (tr ("&Verify"), this); - connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); - world->addAction (mVerify); + QAction *skills = new QAction (tr ("Skills"), this); + connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); + mechanics->addAction (skills); + + QAction *classes = new QAction (tr ("Classes"), this); + connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); + mechanics->addAction (classes); + + QAction *factions = new QAction (tr ("Factions"), this); + connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); + mechanics->addAction (factions); + + QAction *races = new QAction (tr ("Races"), this); + connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); + mechanics->addAction (races); + + QAction *scripts = new QAction (tr ("Scripts"), this); + connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); + mechanics->addAction (scripts); + + QAction *birthsigns = new QAction (tr ("Birthsigns"), this); + connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); + mechanics->addAction (birthsigns); + + QAction *spells = new QAction (tr ("Spells"), this); + connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); + mechanics->addAction (spells); +} + +void CSVDoc::View::setupAssetsMenu() +{ + QMenu *assets = menuBar()->addMenu (tr ("&Assets")); + + QAction *sounds = new QAction (tr ("Sounds"), this); + connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); + assets->addAction (sounds); } void CSVDoc::View::setupUi() @@ -86,13 +176,15 @@ void CSVDoc::View::setupUi() setupEditMenu(); setupViewMenu(); setupWorldMenu(); + setupMechanicsMenu(); + setupAssetsMenu(); } void CSVDoc::View::updateTitle() { std::ostringstream stream; - stream << mDocument->getName(); + stream << mDocument->getSavePath().filename().string(); if (mDocument->getState() & CSMDoc::State_Modified) stream << " *"; @@ -117,15 +209,18 @@ void CSVDoc::View::updateActions() mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying)); } -CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent) - : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews), QMainWindow (viewParent) +CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) + : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), + mViewTotal (totalViews) { - setDockOptions (QMainWindow::AllowNestedDocks); + QString width = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Width")); + QString height = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Height")); - resize (300, 300); /// \todo get default size from settings and set reasonable minimal size + resize (width.toInt(), height.toInt()); - mSubViewWindow = new QMainWindow(); - setCentralWidget (mSubViewWindow); + mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); + + setCentralWidget (&mSubViewWindow); mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); @@ -200,11 +295,16 @@ 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); - mSubViewWindow->addDockWidget (Qt::TopDockWidgetArea, view); + + view->setStatusBar (mShowStatusBar->isChecked()); + + mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, SLOT (addSubView (const CSMWorld::UniversalId&))); + CSMSettings::UserSettings::instance().updateSettings("Display Format"); + view->show(); } @@ -233,13 +333,129 @@ 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::addReferencesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_References); +} + +void CSVDoc::View::addRegionMapSubView() +{ + addSubView (CSMWorld::UniversalId::Type_RegionMap); +} + +void CSVDoc::View::addFiltersSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Filters); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); updateActions(); } -QDockWidget *CSVDoc::View::getOperations() const +CSVDoc::Operations *CSVDoc::View::getOperations() const { return mOperations; } + +void CSVDoc::View::exit() +{ + emit exitApplicationRequest (this); +} + +void CSVDoc::View::resizeViewWidth (int width) +{ + if (width >= 0) + resize (width, geometry().height()); +} + +void CSVDoc::View::resizeViewHeight (int height) +{ + if (height >= 0) + resize (geometry().width(), height); +} + +void CSVDoc::View::updateEditorSetting (const QString &settingName, const QString &settingValue) +{ + if ( (settingName == "Record Status Display") || (settingName == "Referenceable ID Type Display") ) + { + foreach (QObject *view, mSubViewWindow.children()) + { + // not all mSubviewWindow children are CSVDoc::Subview objects + CSVDoc::SubView *subview = dynamic_cast(view); + + if (subview) + subview->updateEditorSetting (settingName, settingValue); + } + } + else if (settingName == "Width") + resizeViewWidth (settingValue.toInt()); + + else if (settingName == "Height") + resizeViewHeight (settingValue.toInt()); +} + +void CSVDoc::View::toggleShowStatusBar (bool show) +{ + foreach (QObject *view, mSubViewWindow.children()) + { + if (CSVDoc::SubView *subView = dynamic_cast (view)) + subView->setStatusBar (show); + } +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 28ab24b74..29a1d52f7 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -38,10 +38,12 @@ namespace CSVDoc QAction *mRedo; QAction *mSave; QAction *mVerify; + QAction *mShowStatusBar; std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; - QMainWindow* mSubViewWindow; + QMainWindow mSubViewWindow; + // not implemented View (const View&); @@ -59,15 +61,30 @@ namespace CSVDoc void setupWorldMenu(); + void setupMechanicsMenu(); + + void setupAssetsMenu(); + void setupUi(); void updateTitle(); void updateActions(); + void exitApplication(); + + void loadUserSettings(); + + /// User preference function + void resizeViewWidth (int width); + + /// User preference function + void resizeViewHeight (int height); + public: - View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent); + View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); + ///< The ownership of \a document is not transferred to *this. virtual ~View(); @@ -82,31 +99,72 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); - QDockWidget *getOperations() const; + Operations *getOperations() const; + + /// Function called by view manager when user preferences are updated + void updateEditorSetting (const QString &, const QString &); signals: - void newDocumentRequest(); + void newGameRequest(); + + void newAddonRequest(); void loadDocumentRequest(); + void exitApplicationRequest (CSVDoc::View *view); + + void editSettingsRequest(); + public slots: void addSubView (const CSMWorld::UniversalId& id); + void abortOperation (int type); + private slots: void newView(); void save(); + void exit(); + void verify(); void addGlobalsSubView(); void addGmstsSubView(); - void abortOperation (int type); + void addSkillsSubView(); + + void addClassesSubView(); + + void addFactionsSubView(); + + void addRacesSubView(); + + void addSoundsSubView(); + + void addScriptsSubView(); + + void addRegionsSubView(); + + void addBirthsignsSubView(); + + void addSpellsSubView(); + + void addCellsSubView(); + + void addReferenceablesSubView(); + + void addReferencesSubView(); + + void addRegionMapSubView(); + + void addFiltersSubView(); + + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 718b80728..83cd93e5d 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -3,15 +3,26 @@ #include +#include +#include + #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" +#include "../../model/world/columns.hpp" #include "../world/util.hpp" #include "../world/enumdelegate.hpp" #include "../world/vartypedelegate.hpp" +#include "../world/recordstatusdelegate.hpp" +#include "../world/idtypedelegate.hpp" +#include "../settings/usersettingsdialog.hpp" #include "view.hpp" +#include +#include +#include + void CSVDoc::ViewManager::updateIndices() { std::map > documents; @@ -31,7 +42,7 @@ void CSVDoc::ViewManager::updateIndices() } CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) -: mDocumentManager (documentManager) + : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false) { mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; @@ -41,6 +52,37 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType, new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float)); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_RecordState, + new CSVWorld::RecordStatusDelegateFactory()); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType, + new CSVWorld::IdTypeDelegateFactory()); + + struct Mapping + { + CSMWorld::ColumnBase::Display mDisplay; + CSMWorld::Columns::ColumnId mColumnId; + bool mAllowNone; + }; + + static const Mapping sMapping[] = + { + { CSMWorld::ColumnBase::Display_Specialisation, CSMWorld::Columns::ColumnId_Specialisation, false }, + { CSMWorld::ColumnBase::Display_Attribute, CSMWorld::Columns::ColumnId_Attribute, true }, + { CSMWorld::ColumnBase::Display_SpellType, CSMWorld::Columns::ColumnId_SpellType, false }, + { CSMWorld::ColumnBase::Display_ApparatusType, CSMWorld::Columns::ColumnId_ApparatusType, false }, + { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false }, + { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, + { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, + { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false } + }; + + for (std::size_t i=0; iadd (sMapping[i].mDisplay, new CSVWorld::EnumDelegateFactory ( + CSMWorld::Columns::getEnums (sMapping[i].mColumnId), sMapping[i].mAllowNone)); + + connect (&CSMSettings::UserSettings::instance(), SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)), + this, SLOT (slotUpdateEditorSetting (const QString &, const QString &))); } CSVDoc::ViewManager::~ViewManager() @@ -63,16 +105,16 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) this, SLOT (progress (int, int, int, int, CSMDoc::Document *))); } - QMainWindow *mainWindow = new QMainWindow; - - View *view = new View (*this, document, countViews (document)+1, mainWindow); + 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 (newGameRequest ()), this, SIGNAL (newGameRequest())); + connect (view, SIGNAL (newAddonRequest ()), this, SIGNAL (newAddonRequest())); connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest())); + connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest())); updateIndices(); @@ -94,23 +136,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 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; } - return 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) @@ -126,3 +288,31 @@ void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, if ((*iter)->getDocument()==document) (*iter)->updateProgress (current, max, type, threads); } + +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(); +} + +void CSVDoc::ViewManager::slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) +{ + foreach (CSVDoc::View *view, mViews) + view->updateEditorSetting (settingName, settingValue); +} diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 72e7a3e1a..01f495186 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -27,12 +27,17 @@ 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: @@ -50,17 +55,32 @@ namespace CSVDoc signals: - void newDocumentRequest(); + void newGameRequest(); + + void newAddonRequest(); void loadDocumentRequest(); + void closeMessageBox(); + + void editSettingsRequest(); + + 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); + + /// connected to update signal in UserSettings + void slotUpdateEditorSetting (const QString &, const QString &); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp new file mode 100644 index 000000000..708d45032 --- /dev/null +++ b/apps/opencs/view/filter/editwidget.cpp @@ -0,0 +1,58 @@ + +#include "editwidget.hpp" + +#include + +#include "../../model/world/data.hpp" + +CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) +: QLineEdit (parent), mParser (data) +{ + mPalette = palette(); + connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); + + QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); + + connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), + this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), + this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), + Qt::QueuedConnection); +} + +void CSVFilter::EditWidget::textChanged (const QString& text) +{ + if (mParser.parse (text.toUtf8().constData())) + { + setPalette (mPalette); + emit filterChanged (mParser.getFilter()); + } + else + { + QPalette palette (mPalette); + palette.setColor (QPalette::Text, Qt::red); + setPalette (palette); + + /// \todo improve error reporting; mark only the faulty part + } +} + +void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp new file mode 100644 index 000000000..31904e624 --- /dev/null +++ b/apps/opencs/view/filter/editwidget.hpp @@ -0,0 +1,48 @@ +#ifndef CSV_FILTER_EDITWIDGET_H +#define CSV_FILTER_EDITWIDGET_H + +#include + +#include +#include + +#include "../../model/filter/parser.hpp" +#include "../../model/filter/node.hpp" + +class QModelIndex; + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class EditWidget : public QLineEdit + { + Q_OBJECT + + CSMFilter::Parser mParser; + QPalette mPalette; + + public: + + EditWidget (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + + private slots: + + void textChanged (const QString& text); + + void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void filterRowsRemoved (const QModelIndex& parent, int start, int end); + + void filterRowsInserted (const QModelIndex& parent, int start, int end); + }; +} + +#endif diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp new file mode 100644 index 000000000..273170884 --- /dev/null +++ b/apps/opencs/view/filter/filterbox.cpp @@ -0,0 +1,24 @@ + +#include "filterbox.hpp" + +#include + +#include "recordfilterbox.hpp" + +CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); + + layout->addWidget (recordFilterBox); + + setLayout (layout); + + connect (recordFilterBox, + SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (recordFilterChanged (boost::shared_ptr))); +} \ No newline at end of file diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp new file mode 100644 index 000000000..2524fa0a3 --- /dev/null +++ b/apps/opencs/view/filter/filterbox.hpp @@ -0,0 +1,30 @@ +#ifndef CSV_FILTER_FILTERBOX_H +#define CSV_FILTER_FILTERBOX_H + +#include + +#include "../../model/filter/node.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class FilterBox : public QWidget + { + Q_OBJECT + + public: + + FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void recordFilterChanged (boost::shared_ptr filter); + }; + +} + +#endif diff --git a/apps/opencs/view/filter/filtercreator.cpp b/apps/opencs/view/filter/filtercreator.cpp new file mode 100644 index 000000000..47925ea57 --- /dev/null +++ b/apps/opencs/view/filter/filtercreator.cpp @@ -0,0 +1,63 @@ + +#include "filtercreator.hpp" + +#include +#include + +#include "../../model/filter/filter.hpp" + +std::string CSVFilter::FilterCreator::getNamespace() const +{ + switch (mScope->currentIndex()) + { + case CSMFilter::Filter::Scope_Project: return "project::"; + case CSMFilter::Filter::Scope_Session: return "session::"; + } + + return ""; +} + +void CSVFilter::FilterCreator::update() +{ + mNamespace->setText (QString::fromUtf8 (getNamespace().c_str())); + GenericCreator::update(); +} + +std::string CSVFilter::FilterCreator::getId() const +{ + return getNamespace() + GenericCreator::getId(); +} + +CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + mNamespace = new QLabel ("::", this); + insertAtBeginning (mNamespace, false); + + mScope = new QComboBox (this); + + mScope->addItem ("Project"); + mScope->addItem ("Session"); + /// \ŧodo re-enable for OpenMW 1.1 + // mScope->addItem ("Content"); + + connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int))); + + insertAtBeginning (mScope, false); + + QLabel *label = new QLabel ("Scope", this); + insertAtBeginning (label, false); + + mScope->setCurrentIndex (1); +} + +void CSVFilter::FilterCreator::reset() +{ + GenericCreator::reset(); +} + +void CSVFilter::FilterCreator::setScope (int index) +{ + update(); +} diff --git a/apps/opencs/view/filter/filtercreator.hpp b/apps/opencs/view/filter/filtercreator.hpp new file mode 100644 index 000000000..82d38d22c --- /dev/null +++ b/apps/opencs/view/filter/filtercreator.hpp @@ -0,0 +1,41 @@ +#ifndef CSV_FILTER_FILTERCREATOR_H +#define CSV_FILTER_FILTERCREATOR_H + +class QComboBox; +class QLabel; + +#include "../world/genericcreator.hpp" + +namespace CSVFilter +{ + class FilterCreator : public CSVWorld::GenericCreator + { + Q_OBJECT + + QComboBox *mScope; + QLabel *mNamespace; + + private: + + std::string getNamespace() const; + + protected: + + void update(); + + virtual std::string getId() const; + + public: + + FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + private slots: + + void setScope (int index); + }; +} + +#endif diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp new file mode 100644 index 000000000..c405177b0 --- /dev/null +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -0,0 +1,27 @@ + +#include "recordfilterbox.hpp" + +#include +#include + +#include "editwidget.hpp" + +CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + layout->addWidget (new QLabel ("Record Filter", this)); + + EditWidget *editWidget = new EditWidget (data, this); + + layout->addWidget (editWidget); + + setLayout (layout); + + connect ( + editWidget, SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (filterChanged (boost::shared_ptr))); +} diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp new file mode 100644 index 000000000..057d69518 --- /dev/null +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -0,0 +1,34 @@ +#ifndef CSV_FILTER_RECORDFILTERBOX_H +#define CSV_FILTER_RECORDFILTERBOX_H + +#include + +#include + +#include + +#include "../../model/filter/node.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class RecordFilterBox : public QWidget + { + Q_OBJECT + + public: + + RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + }; + +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/settings/abstractblock.cpp b/apps/opencs/view/settings/abstractblock.cpp new file mode 100644 index 000000000..65825ce8b --- /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 = new SettingWidget (def, layout, mBox); + break; + + case Widget_SpinBox: + widg = new SettingWidget (def, layout, mBox); + break; + + case Widget_CheckBox: + widg = new SettingWidget (def, layout, mBox); + break; + + case Widget_LineEdit: + widg = new SettingWidget (def, layout, mBox); + break; + + case Widget_ListBox: + widg = new SettingWidget (def, layout, mBox); + break; + + case Widget_ComboBox: + widg = new SettingWidget (def, layout, mBox); + 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..361339fe2 --- /dev/null +++ b/apps/opencs/view/settings/abstractblock.hpp @@ -0,0 +1,82 @@ +#ifndef ABSTRACTBLOCK_HPP +#define ABSTRACTBLOCK_HPP + +#include +#include + +#include "settingwidget.hpp" +#include "../../model/settings/settingsitem.hpp" +#include "groupbox.hpp" + +namespace CSVSettings +{ + + /// Abstract base class for all blocks + 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; + + /// update settings found in the passed map and are encapsulated by the block + virtual bool updateSettings (const CSMSettings::SettingMap &settings) = 0; + + /// update callback function called from update slot + /// used for updating application-level settings in the editor + virtual bool updateBySignal (const QString &name, const QString &value, bool &doEmit) + { return false; } + + protected: + + /// Creates the layout for the block's QGroupBox + QLayout *createLayout (Orientation direction, bool isZeroMargin, QWidget* parent = 0); + + /// Creates widgets that exist as direct children of the block + AbstractWidget *buildWidget (const QString &widgetName, WidgetDef &wDef, + QLayout *layout = 0, bool isConnected = true) const; + + QWidget *getParent() const; + + public slots: + + /// enables / disables block-level widgets based on signals from other widgets + /// used in ToggleBlock + void slotSetEnabled (bool value); + + /// receives updates to applicaion-level settings in the Editor + void slotUpdateSetting (const QString &settingName, const QString &settingValue); + + private slots: + + /// receives updates to a setting in the block pushed from the application level + void slotUpdate (const QString &value); + + signals: + + /// signal to UserSettings instance + void signalUpdateSetting (const QString &propertyName, const QString &propertyValue); + + /// signal to widget for updating widget value + void signalUpdateWidget (const QString & value); + + /// ProxyBlock use only. + /// Name and value correspond to settings for which the block is 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..e6c605275 --- /dev/null +++ b/apps/opencs/view/settings/abstractpage.cpp @@ -0,0 +1,44 @@ +#include "abstractpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CSVSettings::AbstractPage::AbstractPage(QWidget *parent): + QWidget(parent) +{ + QGridLayout *pageLayout = new QGridLayout(this); + setLayout (pageLayout); +} + +CSVSettings::AbstractPage::AbstractPage(const QString &pageName, QWidget *parent): + QWidget(parent) +{ + QWidget::setObjectName (pageName); + + QGridLayout *pageLayout = new QGridLayout(this); + setLayout (pageLayout); +} + +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..77ef4524f --- /dev/null +++ b/apps/opencs/view/settings/abstractpage.hpp @@ -0,0 +1,70 @@ +#ifndef ABSTRACTPAGE_HPP +#define ABSTRACTPAGE_HPP + +#include +#include +#include + +#include "abstractblock.hpp" + +class SettingMap; +class SettingList; + +namespace CSVSettings { + + typedef QList AbstractBlockList; + + /// Abstract base class for all setting pages in the dialog + + /// \todo Scripted implementation of settings should eliminate the need + /// \todo derive page classes. + /// \todo AbstractPage should be replaced with a general page construction class. + class AbstractPage: public QWidget + { + + protected: + + AbstractBlockList mAbstractBlocks; + + public: + + AbstractPage(QWidget *parent = 0); + AbstractPage (const QString &pageName, QWidget* parent = 0); + + ~AbstractPage(); + + virtual void setupUi() = 0; + + /// triggers widgiet initialization at the page level. All widgets updated to + /// current setting values + virtual void initializeWidgets (const CSMSettings::SettingMap &settings) = 0; + + /// retrieve the list of settings local to the page. + CSMSettings::SettingList *getSettings(); + + void setObjectName(); + + protected: + + /// Create a block for the page. + /// Block is constructed using passed definition struct + /// Page level-layout is created and assigned + template + AbstractBlock *buildBlock (T *def) + { + S *block = new S (this); + int ret = block->build (def); + + if (ret < 0) + return 0; + + QGroupBox *box = block->getGroupBox(); + QWidget::layout()->addWidget (box); + + 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..f268d3b27 --- /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..325de2bd2 --- /dev/null +++ b/apps/opencs/view/settings/abstractwidget.hpp @@ -0,0 +1,69 @@ +#ifndef ABSTRACTWIDGET_HPP +#define ABSTRACTWIDGET_HPP + +#include +#include "support.hpp" + +class QLayout; + +namespace CSVSettings +{ + /// Abstract base class for widgets which are used in user preferences dialog + class AbstractWidget : public QObject + { + Q_OBJECT + + QLayout *mLayout; + + public: + + /// Passed layout is assigned the constructed widget. + /// if no layout is passed, one is created. + 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: + + /// Callback called by receiving slot for widget udpates + virtual void updateWidget (const QString &value) = 0; + + /// Converts user-defined enum to Qt equivalents + QFlags getAlignment (Alignment flag); + + private: + + /// Creates layout and assigns label and widget as appropriate + void createLayout (Orientation direction, bool isZeroMargin); + + /// Creates label and widget according to passed definition + void buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel); + + + signals: + + /// outbound update signal + void signalUpdateItem (const QString &value); + + public slots: + + /// receives inbound updates + void slotUpdateWidget (const QString &value); + + /// Overloads for 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..837a31bee --- /dev/null +++ b/apps/opencs/view/settings/blankpage.cpp @@ -0,0 +1,50 @@ +#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) +{ + +} + +CSVSettings::BlankPage::BlankPage(const QString &title, QWidget *parent): + AbstractPage(title, parent) +{ + // 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); + layout()->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..07049fb71 --- /dev/null +++ b/apps/opencs/view/settings/blankpage.hpp @@ -0,0 +1,28 @@ +#ifndef BLANKPAGE_HPP +#define BLANKPAGE_HPP + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CSVSettings { + + class UserSettings; + class AbstractBlock; + + /// Derived page with no widgets + /// Reference use only. + class BlankPage : public AbstractPage + { + + public: + + BlankPage (QWidget *parent = 0); + BlankPage (const QString &title, QWidget *parent); + + void setupUi(); + void initializeWidgets (const CSMSettings::SettingMap &settings); + }; +} + +#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..bbceafabe --- /dev/null +++ b/apps/opencs/view/settings/customblock.cpp @@ -0,0 +1,121 @@ +#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); + 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->settingItems.size() != 1) + return -1; + + int retVal = block->build(def); + + if (retVal != 0) + return retVal; + + // The first settingItem is the proxy setting, containing the list of settings bound to it. + foreach (QStringList *list, *(def->settingItems.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..54c50f395 --- /dev/null +++ b/apps/opencs/view/settings/customblock.hpp @@ -0,0 +1,47 @@ +#ifndef CUSTOMBLOCK_HPP +#define CUSTOMBLOCK_HPP + +#include "abstractblock.hpp" + +namespace CSVSettings +{ + + class ProxyBlock; + + /// Base class for customized user preference setting blocks + /// Special block classes should be derived from CustomBlock + class CustomBlock : public AbstractBlock + { + + protected: + + GroupBlockList mGroupList; + + public: + + explicit CustomBlock (QWidget *parent = 0); + + /// Update settings local to the block + bool updateSettings (const CSMSettings::SettingMap &settings); + + /// Retrieve settings local to the block + CSMSettings::SettingList *getSettings(); + + /// construct the block using the passed definition + int build (GroupBlockDefList &defList, GroupBlockDefList::Iterator *it = 0); + + protected: + + /// construct the block groupbox + GroupBox *buildGroupBox (Orientation orientation); + + private: + + /// Construction function for creating a standard GroupBlock child + int buildGroupBlock(GroupBlockDef *def); + + /// Construction function for creating a standard ProxyBlock child + int buildProxyBlock(GroupBlockDef *def, ProxyBlock *block); + }; +} +#endif // CUSTOMBLOCK_HPP diff --git a/apps/opencs/view/settings/datadisplayformatpage.cpp b/apps/opencs/view/settings/datadisplayformatpage.cpp new file mode 100755 index 000000000..332b68f5c --- /dev/null +++ b/apps/opencs/view/settings/datadisplayformatpage.cpp @@ -0,0 +1,57 @@ +#include "datadisplayformatpage.hpp" +#include "groupblock.hpp" +#include "../../model/settings/usersettings.hpp" + +CSVSettings::DataDisplayFormatPage::DataDisplayFormatPage(QWidget* parent) : + AbstractPage("Display Format", parent) +{ + setupUi(); +} + +CSVSettings::GroupBlockDef *CSVSettings::DataDisplayFormatPage::setupDataDisplay( const QString &title) +{ + GroupBlockDef *statusBlock = new GroupBlockDef(QString(title)); + + SettingsItemDef *statusItem = new SettingsItemDef (statusBlock->title, "Icon Only"); + *(statusItem->valueList) << QString("Icon and Text") << QString("Icon Only") << QString("Text Only"); + + WidgetDef statusWidget (Widget_RadioButton); + statusWidget.valueList = statusItem->valueList; + + statusItem->widget = statusWidget; + + statusBlock->settingItems << statusItem; + + statusBlock->isZeroMargin = false; + + return statusBlock; +} + + +void CSVSettings::DataDisplayFormatPage::setupUi() +{ + + mAbstractBlocks << buildBlock (setupDataDisplay ("Record Status Display")); + mAbstractBlocks << buildBlock (setupDataDisplay ("Referenceable ID Type Display")); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); + } + + connect ( this, + SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), + &(CSMSettings::UserSettings::instance()), + SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); + +} + +void CSVSettings::DataDisplayFormatPage::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/datadisplayformatpage.hpp b/apps/opencs/view/settings/datadisplayformatpage.hpp new file mode 100755 index 000000000..b785bbd23 --- /dev/null +++ b/apps/opencs/view/settings/datadisplayformatpage.hpp @@ -0,0 +1,33 @@ +#ifndef EDITORPAGE_HPP +#define EDITORPAGE_HPP + +#include "support.hpp" +#include "abstractpage.hpp" + +namespace CSVSettings +{ + class DataDisplayFormatPage : public AbstractPage + { + Q_OBJECT + + public: + explicit DataDisplayFormatPage(QWidget *parent = 0); + + void initializeWidgets (const CSMSettings::SettingMap &settings); + void setupUi(); + + private: + + /// User preference view of the record status delegate's icon / text setting + GroupBlockDef *setupDataDisplay(const QString &); + + signals: + + /// Signals up for changes to editor application-level settings + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + + public slots: + }; +} + +#endif // EDITORPAGE_HPP diff --git a/apps/opencs/view/settings/editorpage.cpp b/apps/opencs/view/settings/editorpage.cpp new file mode 100644 index 000000000..153ac1551 --- /dev/null +++ b/apps/opencs/view/settings/editorpage.cpp @@ -0,0 +1,53 @@ +#include "editorpage.hpp" +#include "groupblock.hpp" +#include "../../model/settings/usersettings.hpp" + +CSVSettings::EditorPage::EditorPage(QWidget* parent) : + AbstractPage("Display Format", parent) +{ + setupUi(); +} + +CSVSettings::GroupBlockDef *CSVSettings::EditorPage::setupRecordStatusDisplay() +{ + GroupBlockDef *statusBlock = new GroupBlockDef(QString("Record Status Display")); + + SettingsItemDef *statusItem = new SettingsItemDef (statusBlock->title, "Icon and Text"); + *(statusItem->valueList) << QString("Icon and Text") << QString("Icon Only") << QString("Text Only"); + + WidgetDef statusWidget (Widget_RadioButton); + statusWidget.valueList = statusItem->valueList; + + statusItem->widget = statusWidget; + + statusBlock->settingItems << statusItem; + + return statusBlock; +} + +void CSVSettings::EditorPage::setupUi() +{ + + mAbstractBlocks << buildBlock(setupRecordStatusDisplay()); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); + } + + connect ( this, + SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), + &(CSMSettings::UserSettings::instance()), + 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..85215edab --- /dev/null +++ b/apps/opencs/view/settings/editorpage.hpp @@ -0,0 +1,33 @@ +#ifndef EDITORPAGE_HPP +#define EDITORPAGE_HPP + +#include "support.hpp" +#include "abstractpage.hpp" + +namespace CSVSettings +{ + class EditorPage : public AbstractPage + { + Q_OBJECT + + public: + explicit EditorPage(QWidget *parent = 0); + + void initializeWidgets (const CSMSettings::SettingMap &settings); + void setupUi(); + + private: + + /// User preference view of the record status delegate's icon / text setting + GroupBlockDef *setupRecordStatusDisplay(); + + signals: + + /// Signals up for changes to editor application-level settings + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + + public slots: + }; +} + +#endif // EDITORPAGE_HPP diff --git a/apps/opencs/view/settings/groupblock.cpp b/apps/opencs/view/settings/groupblock.cpp new file mode 100644 index 000000000..e31e526c0 --- /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->settingItems.size() == 0) + return -1; + + int retVal = 0; + + setVisible (def->isVisible); + + mBox->setLayout(createLayout (def->widgetOrientation, def->isZeroMargin)); + + setObjectName (def->title); + mBox->setTitle (def->title); + + foreach (SettingsItemDef *itemDef, def->settingItems) + { + 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..5c0754193 --- /dev/null +++ b/apps/opencs/view/settings/groupblock.hpp @@ -0,0 +1,43 @@ +#ifndef GROUPBLOCK_HPP +#define GROUPBLOCK_HPP + +#include +#include "abstractblock.hpp" + +namespace CSVSettings +{ + class ItemBlock; + + /// Base class for group blocks. + /// Derived block classes should use CustomBlock + class GroupBlock : public AbstractBlock + { + ItemBlockList mItemBlockList; + + public: + GroupBlock (QWidget* parent = 0); + GroupBlock (bool isVisible, QWidget *parent = 0); + + /// build the gorup block based on passed definition + int build (GroupBlockDef *def); + + /// update settings local to the group block + bool updateSettings (const CSMSettings::SettingMap &settings); + + /// retrieve setting list local to the group block + CSMSettings::SettingList *getSettings(); + + /// retrieve item block by name from the passed list or local list + ItemBlock *getItemBlock (const QString &name, ItemBlockList *blockList = 0); + + /// retrieve the item block by index from the local list + ItemBlock *getItemBlock (int index); + + protected: + + /// create block layout based on passed definition + 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..9d3a01936 --- /dev/null +++ b/apps/opencs/view/settings/groupbox.hpp @@ -0,0 +1,28 @@ +#ifndef GROUPBOX_HPP +#define GROUPBOX_HPP + +#include + +namespace CSVSettings +{ + /// Custom implementation of QGroupBox to be used with block classes + 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..2d1d45d41 --- /dev/null +++ b/apps/opencs/view/settings/itemblock.hpp @@ -0,0 +1,48 @@ +#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); + + /// pure virtual function not implemented + bool updateSettings (const CSMSettings::SettingMap &settings) { return false; } + + CSMSettings::SettingList *getSettings (); + + QString getValue () const; + + /// item blocks encapsulate only one setting + int getSettingCount(); + + /// update setting value and corresponding widget + bool update (const QString &value); + + /// virtual construction function + int build(SettingsItemDef &iDef); + + private: + + /// custom construction function + void buildItemBlock (SettingsItemDef& iDef); + void buildItemBlockWidgets (SettingsItemDef& iDef); + + /// update the setting value + bool updateItem (const QString &); + + /// callback function triggered when update to application level is signalled + 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..81cc54fca --- /dev/null +++ b/apps/opencs/view/settings/proxyblock.cpp @@ -0,0 +1,152 @@ +#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->settingItems.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; + + //find the value index of the selected value in the proxy setting + for (; i < mValueList->size(); ++i) + { + success = (value == mValueList->at(i)); + + if (success) + break; + } + + if (!success) + return false; + + // update the containing the proxied item's name + 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..90fb9bc97 --- /dev/null +++ b/apps/opencs/view/settings/proxyblock.hpp @@ -0,0 +1,52 @@ +#ifndef PROXYBLOCK_HPP +#define PROXYBLOCK_HPP + +#include "groupblock.hpp" + +namespace CSVSettings +{ + class ProxyBlock : public GroupBlock + { + Q_OBJECT + + /// TODO: Combine mProxyItemBlockList and mProxyList. + ItemBlockList mProxiedItemBlockList; + ProxyList mProxyList; + QStringList *mValueList; + + public: + + explicit ProxyBlock (QWidget *parent = 0); + explicit ProxyBlock (ItemBlock *proxyItemBlock, QWidget *parent = 0); + + /// Add a block that contains a proxied setting to the proxy block. + void addSetting (ItemBlock* settingBlock, QStringList *proxyList); + + int build (GroupBlockDef *def); + + CSMSettings::SettingList *getSettings() { return 0; } + + /// Update settings local to the proxy block pushed from application level + bool updateSettings (const CSMSettings::SettingMap &settings); + + /// callback function triggered when update to the application level is signaled. + bool updateBySignal (const QString &name, const QString &value, bool &doEmit); + + private: + + /// return the item block of a proxied setting + ItemBlock *getProxiedItemBlock (const QString &name); + + /// update the proxy setting with data from the proxied settings + bool updateByProxiedSettings(const CSMSettings::SettingMap *settings = 0); + + /// update proxied settings with data from the proxy setting + 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..9f4513671 --- /dev/null +++ b/apps/opencs/view/settings/settingwidget.hpp @@ -0,0 +1,214 @@ +#ifndef SETTINGWIDGET_HPP +#define SETTINGWIDGET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abstractwidget.hpp" + +namespace CSVSettings +{ + + /// Generic template for radiobuttons / checkboxes + 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); + } + }; + + /// spin box template + 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: + + }; + + /// combo box template + 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)); + } + + }; + + /// line edit template + 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); + } + }; + + /// list widget template + /// \todo Not fully implemented. Only widget supporting multi-valued settings + 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..5d954505c --- /dev/null +++ b/apps/opencs/view/settings/support.hpp @@ -0,0 +1,206 @@ +#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 + }; + + /// definition struct for widgets + struct WidgetDef + { + /// type of widget providing input + WidgetType type; + + /// width of caption label + int labelWidth; + + /// width of input widget + int widgetWidth; + + /// label / widget orientation (horizontal / vertical) + Orientation orientation; + + /// input mask (line edit only) + QString inputMask; + + /// label caption. Leave empty for multiple items. See BlockDef::captionList + QString caption; + + /// widget value. Leave empty for multiple items. See BlockDef::valueList + QString value; + + /// Min/Max QString value pair. If empty, assigned to property item value pair. + CSMSettings::QStringPair *minMax; + + /// value list for list widgets. If left empty, is assigned to property item value list during block build(). + QStringList *valueList; + + /// determined at runtime + bool isDefault; + + /// left / center / right-justify text in widget + Alignment valueAlignment; + + /// left / center / right-justify widget in group box + Alignment widgetAlignment; + + + 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 setting 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 setting. + struct SettingsItemDef + { + /// setting name + QString name; + + /// list of valid values for the setting + QStringList *valueList; + + /// Used to populate option widget captions or list widget item lists (see WidgetDef::caption / value) + QString defaultValue; + + /// flag indicating multi-valued setting + bool hasMultipleValues; + + /// minimum / maximum value pair + CSMSettings::QStringPair minMax; + + /// definition of the input widget for this setting + WidgetDef widget; + + /// general orientation of the widget / label for this setting + Orientation orientation; + + /// list of settings and corresponding default values for proxy widget + ProxyList *proxyList; + + 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) + {} + }; + + + /// Generic container block + struct GroupBlockDef + { + /// block title + QString title; + + /// list of captions for widgets at the block level (not associated with any particular setting) + QStringList captions; + + /// list of widgets at the block level (not associated with any particular setting) + WidgetList widgets; + + /// list of the settings which are subordinate to the setting block. + QList settingItems; + + /// general orientation of widgets in group block + Orientation widgetOrientation; + + /// determines whether or not box border/title are visible + bool isVisible; + + /// indicates whether or not this block defines a proxy block + bool isProxy; + + /// generic default value attribute + QString defaultValue; + + /// shows / hides margins + bool isZeroMargin; + + GroupBlockDef (): title(""), widgetOrientation (Orient_Vertical), isVisible (true), isProxy (false), defaultValue (""), isZeroMargin (true) + {} + + GroupBlockDef (QString blockTitle) + : title (blockTitle), widgetOrientation (Orient_Vertical), isProxy (false), isVisible (true), defaultValue (""), isZeroMargin (true) + {} + }; + + /// used to create unique, complex blocks + struct CustomBlockDef + { + /// block title + QString title; + + /// default value for widgets unique to the custom block + QString defaultValue; + + /// list of settings groups that comprise the settings within the custom block + GroupBlockDefList blockDefList; + + /// orientation of the widgets within the 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..3406a62c4 --- /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..4b6e8e344 --- /dev/null +++ b/apps/opencs/view/settings/toggleblock.hpp @@ -0,0 +1,29 @@ +#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: + /// Constructor for toggle widgets that are specific to toggle block + /// Widgets are not a part of the user preference settings + 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..e73e24dcb --- /dev/null +++ b/apps/opencs/view/settings/usersettingsdialog.cpp @@ -0,0 +1,119 @@ +#include "usersettingsdialog.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../model/settings/support.hpp" + +#include "datadisplayformatpage.hpp" +#include "windowpage.hpp" +#include "settingwidget.hpp" + +CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : + QMainWindow (parent), mStackedWidget (0) +{ + setWindowTitle(QString::fromUtf8 ("User Settings")); + buildPages(); + setWidgetStates (); + + connect (mListWidget, + SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, + SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); + + QRect scr = QApplication::desktop()->screenGeometry(); + QRect rect = geometry(); + move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); +} + +CSVSettings::UserSettingsDialog::~UserSettingsDialog() +{ +} + +void CSVSettings::UserSettingsDialog::closeEvent (QCloseEvent *event) +{ + writeSettings(); +} + +void CSVSettings::UserSettingsDialog::setWidgetStates () +{ + CSMSettings::UserSettings::instance().loadSettings("opencs.cfg"); + + //iterate the tabWidget's pages (sections) + for (int i = 0; i < mStackedWidget->count(); i++) + { + //get the settings defined for the entire section + //and update widget + QString pageName = mStackedWidget->widget(i)->objectName(); + + const CSMSettings::SettingMap *settings = CSMSettings::UserSettings::instance().getSettings(pageName); + 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); + + QGridLayout* dialogLayout = new QGridLayout(); + + mListWidget->setMinimumWidth(0); + mListWidget->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Expanding); + + mStackedWidget->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); + + dialogLayout->addWidget (mListWidget,0,0); + dialogLayout->addWidget (mStackedWidget,0,1, Qt::AlignTop); + + centralWidget->setLayout (dialogLayout); + + setCentralWidget (centralWidget); + setDockOptions (QMainWindow::AllowNestedDocks); + + createPage(); + createPage(); + +} + +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().writeSettings(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..3b3fa5b79 --- /dev/null +++ b/apps/opencs/view/settings/usersettingsdialog.hpp @@ -0,0 +1,71 @@ +#ifndef USERSETTINGSDIALOG_H +#define USERSETTINGSDIALOG_H + +#include +#include +#include +#include + +#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 + + QListWidget *mListWidget; + QStackedWidget *mStackedWidget; + + public: + UserSettingsDialog(QMainWindow *parent = 0); + ~UserSettingsDialog(); + + private: + + /// Settings are written on close + void closeEvent (QCloseEvent *event); + + /// return the setting page by name + /// performs dynamic cast to AbstractPage * + AbstractPage &getAbstractPage (int index); + void setWidgetStates (); + void buildPages(); + void writeSettings(); + + /// Templated function to create a custom user preference page + template + void createPage () + { + T *page = new T(mStackedWidget); + + mStackedWidget->addWidget (&dynamic_cast(*page)); + + new QListWidgetItem (page->objectName(), mListWidget); + + //finishing touches + QFontMetrics fm (QApplication::font()); + int textWidth = fm.width(page->objectName()); + + if ((textWidth + 50) > mListWidget->minimumWidth()) + mListWidget->setMinimumWidth(textWidth + 50); + + resize (mStackedWidget->sizeHint()); + } + + public slots: + + /// Called when a different page is selected in the left-hand list widget + void slotChangePage (QListWidgetItem*, QListWidgetItem*); + }; + +} +#endif // USERSETTINGSDIALOG_H diff --git a/apps/opencs/view/settings/windowpage.cpp b/apps/opencs/view/settings/windowpage.cpp new file mode 100644 index 000000000..ae42623b7 --- /dev/null +++ b/apps/opencs/view/settings/windowpage.cpp @@ -0,0 +1,144 @@ +#include "windowpage.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" +#include "../../view/settings/abstractblock.hpp" + +CSVSettings::WindowPage::WindowPage(QWidget *parent): + AbstractPage("Window Size", parent) +{ + // Hacks to get the stylesheet look properly +#ifdef Q_OS_MAC + QPlastiqueStyle *style = new QPlastiqueStyle; + //profilesComboBox->setStyle(style); +#endif + + setupUi(); +} + +CSVSettings::GroupBlockDef * CSVSettings::WindowPage::buildDefinedWindowSize() +{ + GroupBlockDef *block = new GroupBlockDef ( "Defined Size"); + + SettingsItemDef *widthByHeightItem = new SettingsItemDef ("Window Size", "640x480"); + WidgetDef widthByHeightWidget = WidgetDef (Widget_ComboBox); + widthByHeightWidget.widgetWidth = 90; + *(widthByHeightItem->valueList) << "640x480" << "800x600" << "1024x768" << "1440x900"; + + QStringList *widthProxy = new QStringList; + QStringList *heightProxy = new QStringList; + + (*widthProxy) << "Width" << "640" << "800" << "1024" << "1440"; + (*heightProxy) << "Height" << "480" << "600" << "768" << "900"; + + *(widthByHeightItem->proxyList) << widthProxy << heightProxy; + + widthByHeightItem->widget = widthByHeightWidget; + + block->settingItems << widthByHeightItem; + block->isProxy = true; + block->isVisible = false; + + return block; +} + +CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildCustomWindowSize() +{ + GroupBlockDef *block = new GroupBlockDef ("Custom Size"); + + //custom width + SettingsItemDef *widthItem = new SettingsItemDef ("Width", "640"); + widthItem->widget = WidgetDef (Widget_LineEdit); + widthItem->widget.widgetWidth = 45; + widthItem->widget.inputMask = "9999"; + + //custom height + SettingsItemDef *heightItem = new SettingsItemDef ("Height", "480"); + heightItem->widget = WidgetDef (Widget_LineEdit); + heightItem->widget.widgetWidth = 45; + heightItem->widget.caption = "x"; + heightItem->widget.inputMask = "9999"; + + block->settingItems << widthItem << heightItem; + block->widgetOrientation = Orient_Horizontal; + block->isVisible = false; + + return block; +} + +CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildWindowSizeToggle() +{ + GroupBlockDef *block = new GroupBlockDef (objectName()); + + // window size toggle + block->captions << "Pre-Defined" << "Custom"; + block->widgetOrientation = Orient_Vertical; + block->isVisible = false; + + //define a widget for each group in the toggle + for (int i = 0; i < 2; i++) + block->widgets << new WidgetDef (Widget_RadioButton); + + block->widgets.at(0)->isDefault = false; + + return block; +} + +CSVSettings::CustomBlockDef *CSVSettings::WindowPage::buildWindowSize(GroupBlockDef *toggle_def, + GroupBlockDef *defined_def, + GroupBlockDef *custom_def) +{ + CustomBlockDef *block = new CustomBlockDef(QString ("Window Size")); + + block->blockDefList << toggle_def << defined_def << custom_def; + block->defaultValue = "Custom"; + + return block; + +} + +void CSVSettings::WindowPage::setupUi() +{ + CustomBlockDef *windowSize = buildWindowSize(buildWindowSizeToggle(), + buildDefinedWindowSize(), + buildCustomWindowSize() + ); + + mAbstractBlocks << buildBlock (windowSize); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); + } + + connect ( this, + SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), + &(CSMSettings::UserSettings::instance()), + SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); + +} + + +void CSVSettings::WindowPage::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/windowpage.hpp b/apps/opencs/view/settings/windowpage.hpp new file mode 100644 index 000000000..2f2830625 --- /dev/null +++ b/apps/opencs/view/settings/windowpage.hpp @@ -0,0 +1,34 @@ +#ifndef WINDOWPAGE_H +#define WINDOWPAGE_H + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CSVSettings { + + class UserSettings; + class AbstractBlock; + + class WindowPage : public AbstractPage + { + Q_OBJECT + + public: + + WindowPage(QWidget *parent = 0); + + void setupUi(); + void initializeWidgets (const CSMSettings::SettingMap &settings); + + /// + GroupBlockDef *buildCustomWindowSize(); + GroupBlockDef *buildDefinedWindowSize(); + GroupBlockDef *buildWindowSizeToggle(); + CustomBlockDef *buildWindowSize (GroupBlockDef *, GroupBlockDef *, GroupBlockDef *); + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + }; +} +#endif //WINDOWPAGE_H diff --git a/apps/opencs/view/tools/reportsubview.cpp b/apps/opencs/view/tools/reportsubview.cpp index fe1be85d7..182d1cdd6 100644 --- a/apps/opencs/view/tools/reportsubview.cpp +++ b/apps/opencs/view/tools/reportsubview.cpp @@ -6,6 +6,8 @@ #include "../../model/tools/reportmodel.hpp" +#include "../../view/world/idtypedelegate.hpp" + CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : CSVDoc::SubView (id), mModel (document.getReport (id)) { @@ -18,6 +20,11 @@ CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc: mTable->setSelectionBehavior (QAbstractItemView::SelectRows); mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); + mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate ( + document.getUndoStack(), this); + + mTable->setItemDelegateForColumn (0, mIdTypeDelegate); + connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (show (const QModelIndex&))); } @@ -26,6 +33,11 @@ void CSVTools::ReportSubView::setEditLock (bool locked) // ignored. We don't change document state anyway. } +void CSVTools::ReportSubView::updateEditorSetting (const QString& key, const QString& value) +{ + mIdTypeDelegate->updateEditorSetting (key, value); +} + void CSVTools::ReportSubView::show (const QModelIndex& index) { focusId (mModel->getUniversalId (index.row())); diff --git a/apps/opencs/view/tools/reportsubview.hpp b/apps/opencs/view/tools/reportsubview.hpp index 626ceb663..6503ebd27 100644 --- a/apps/opencs/view/tools/reportsubview.hpp +++ b/apps/opencs/view/tools/reportsubview.hpp @@ -16,6 +16,11 @@ namespace CSMTools class ReportModel; } +namespace CSVWorld +{ + class CommandDelegate; +} + namespace CSVTools { class Table; @@ -26,6 +31,7 @@ namespace CSVTools CSMTools::ReportModel *mModel; QTableView *mTable; + CSVWorld::CommandDelegate *mIdTypeDelegate; public: @@ -33,6 +39,8 @@ namespace CSVTools virtual void setEditLock (bool locked); + virtual void updateEditorSetting (const QString&, const QString&); + private slots: void show (const QModelIndex& index); diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp new file mode 100644 index 000000000..74fb06870 --- /dev/null +++ b/apps/opencs/view/world/cellcreator.cpp @@ -0,0 +1,81 @@ + +#include "cellcreator.hpp" + +#include +#include + +#include +#include +#include + +std::string CSVWorld::CellCreator::getId() const +{ + if (mType->currentIndex()==0) + return GenericCreator::getId(); + + std::ostringstream stream; + + stream << "#" << mX->value() << " " << mY->value(); + + return stream.str(); +} + +CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + mY = new QSpinBox (this); + mY->setVisible (false); + mY->setMinimum (std::numeric_limits::min()); + mY->setMaximum (std::numeric_limits::max()); + connect (mY, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); + insertAtBeginning (mY, true); + + mYLabel = new QLabel ("Y", this); + mYLabel->setVisible (false); + insertAtBeginning (mYLabel, false); + + mX = new QSpinBox (this); + mX->setVisible (false); + mX->setMinimum (std::numeric_limits::min()); + mX->setMaximum (std::numeric_limits::max()); + connect (mX, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); + insertAtBeginning (mX, true); + + mXLabel = new QLabel ("X", this); + mXLabel->setVisible (false); + insertAtBeginning (mXLabel, false); + + mType = new QComboBox (this); + + mType->addItem ("Interior Cell"); + mType->addItem ("Exterior Cell"); + + connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int))); + + insertAtBeginning (mType, false); +} + +void CSVWorld::CellCreator::reset() +{ + mX->setValue (0); + mY->setValue (0); + mType->setCurrentIndex (0); + GenericCreator::reset(); +} + +void CSVWorld::CellCreator::setType (int index) +{ + setManualEditing (index==0); + mXLabel->setVisible (index==1); + mX->setVisible (index==1); + mYLabel->setVisible (index==1); + mY->setVisible (index==1); + + update(); +} + +void CSVWorld::CellCreator::valueChanged (int index) +{ + update(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp new file mode 100644 index 000000000..a5473e2c9 --- /dev/null +++ b/apps/opencs/view/world/cellcreator.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_CELLCREATOR_H +#define CSV_WORLD_CELLCREATOR_H + +class QLabel; +class QSpinBox; +class QComboBox; + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class CellCreator : public GenericCreator + { + Q_OBJECT + + QComboBox *mType; + QLabel *mXLabel; + QSpinBox *mX; + QLabel *mYLabel; + QSpinBox *mY; + + protected: + + virtual std::string getId() const; + + public: + + CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + + virtual void reset(); + + private slots: + + void setType (int index); + + void valueChanged (int index); + }; +} + +#endif diff --git a/apps/opencs/view/world/creator.cpp b/apps/opencs/view/world/creator.cpp new file mode 100644 index 000000000..d753a2b47 --- /dev/null +++ b/apps/opencs/view/world/creator.cpp @@ -0,0 +1,13 @@ + +#include "creator.hpp" + +CSVWorld::Creator:: ~Creator() {} + +CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {} + + +CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +{ + return 0; +} \ No newline at end of file diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp new file mode 100644 index 000000000..df9b116ee --- /dev/null +++ b/apps/opencs/view/world/creator.hpp @@ -0,0 +1,86 @@ +#ifndef CSV_WORLD_CREATOR_H +#define CSV_WORLD_CREATOR_H + +#include + +class QUndoStack; + +namespace CSMWorld +{ + class Data; + class UniversalId; +} + +namespace CSVWorld +{ + /// \brief Record creator UI base class + class Creator : public QWidget + { + Q_OBJECT + + public: + + virtual ~Creator(); + + virtual void reset() = 0; + + virtual void setEditLock (bool locked) = 0; + + signals: + + void done(); + + void requestFocus (const std::string& id); + ///< Request owner of this creator to focus the just created \a id. The owner may + /// ignore this request. + }; + + /// \brief Base class for Creator factory + class CreatorFactoryBase + { + public: + + virtual ~CreatorFactoryBase(); + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const = 0; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function can return a 0-pointer, which means no UI for creating/deleting + /// records should be provided. + }; + + /// \brief Creator factory that does not produces any creator + class NullCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function always returns 0. + }; + + template + class CreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function can return a 0-pointer, which means no UI for creating/deleting + /// records should be provided. + }; + + template + Creator *CreatorFactory::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const + { + return new CreatorT (data, undoStack, id); + } +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp new file mode 100755 index 000000000..d838395f6 --- /dev/null +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -0,0 +1,110 @@ +#include "datadisplaydelegate.hpp" +#include +#include + +CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, + const IconList &icons, + QUndoStack &undoStack, QObject *parent) + : EnumDelegate (values, undoStack, parent), mDisplayMode (Mode_TextOnly), mIcons (icons) + , mIconSize (QSize(16, 16)), mIconLeftOffset(3), mTextLeftOffset(8) +{ + mTextAlignment.setAlignment (Qt::AlignLeft | Qt::AlignVCenter ); + + buildPixmaps(); +} + +void CSVWorld::DataDisplayDelegate::buildPixmaps () +{ + if (mPixmaps.size() > 0) + mPixmaps.clear(); + + IconList::iterator it = mIcons.begin(); + + while (it != mIcons.end()) + { + mPixmaps.push_back (std::make_pair (it->first, it->second.pixmap (mIconSize) ) ); + it++; + } +} + +void CSVWorld::DataDisplayDelegate::setIconSize(const QSize size) +{ + mIconSize = size; + buildPixmaps(); +} + +void CSVWorld::DataDisplayDelegate::setIconLeftOffset(int offset) +{ + mIconLeftOffset = offset; +} + +void CSVWorld::DataDisplayDelegate::setTextLeftOffset(int offset) +{ + mTextLeftOffset = offset; +} + +void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + painter->save(); + + //default to enum delegate's paint method for text-only conditions + if (mDisplayMode == Mode_TextOnly) + EnumDelegate::paint(painter, option, index); + else + { + unsigned int i = 0; + + for (; i < mValues.size(); ++i) + { + if (mValues.at(i).first == index.data().toInt()) + break; + } + + if (i < mValues.size() ) + paintIcon (painter, option, i); + } + + painter->restore(); +} + +void CSVWorld::DataDisplayDelegate::paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int index) const +{ + //function-level statics + QRect iconRect = option.rect; + QRect textRect = iconRect; + + const QString &text = mValues.at(index).second; + + iconRect.setSize (mIconSize); + iconRect.translate(mIconLeftOffset, (option.rect.height() - iconRect.height())/2); + + if (mDisplayMode == Mode_IconAndText ) + { + textRect.translate (iconRect.width() + mTextLeftOffset, 0 ); + painter->drawText (textRect, text, mTextAlignment); + } + else + iconRect.translate( (option.rect.width() - iconRect.width()) / 2, 0); + + painter->drawPixmap (iconRect, mPixmaps.at(index).second); +} + +CSVWorld::DataDisplayDelegate::~DataDisplayDelegate() +{ + mIcons.clear(); + mPixmaps.clear(); +} + +void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName, QString iconFilename) +{ + mIcons.push_back (std::make_pair(enumValue, QIcon(iconFilename))); + EnumDelegateFactory::add(enumValue, enumName); + +} + +CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate (QUndoStack& undoStack, + QObject *parent) const +{ + + return new DataDisplayDelegate (mValues, mIcons, undoStack, parent); +} diff --git a/apps/opencs/view/world/datadisplaydelegate.hpp b/apps/opencs/view/world/datadisplaydelegate.hpp new file mode 100755 index 000000000..d23b86631 --- /dev/null +++ b/apps/opencs/view/world/datadisplaydelegate.hpp @@ -0,0 +1,85 @@ +#ifndef DATADISPLAYDELEGATE_HPP +#define DATADISPLAYDELEGATE_HPP + +#include +#include "enumdelegate.hpp" + +namespace CSVWorld +{ + + + class DataDisplayDelegate : public EnumDelegate + { + public: + + typedef std::vector < std::pair < int, QIcon > > IconList; + typedef std::vector > ValueList; + + protected: + + enum DisplayMode + { + Mode_TextOnly, + Mode_IconOnly, + Mode_IconAndText + }; + + DisplayMode mDisplayMode; + IconList mIcons; + + private: + + std::vector > mPixmaps; + QTextOption mTextAlignment; + QSize mIconSize; + int mIconLeftOffset; + int mTextLeftOffset; + + public: + explicit DataDisplayDelegate (const ValueList & values, + const IconList & icons, + QUndoStack& undoStack, QObject *parent); + + ~DataDisplayDelegate(); + + virtual void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + + /// pass a QSize defining height / width of icon. Default is QSize (16,16). + void setIconSize (const QSize icon); + + /// offset the horizontal position of the icon from the left edge of the cell. Default is 3 pixels. + void setIconLeftOffset (int offset); + + /// offset the horizontal position of the text from the right edge of the icon. Default is 8 pixels. + void setTextLeftOffset (int offset); + + private: + + /// custom paint function for painting the icon. Mode_IconAndText and Mode_Icon only. + void paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int i) const; + + /// rebuild the list of pixmaps from the provided icons (called when icon size is changed) + void buildPixmaps(); + + }; + + class DataDisplayDelegateFactory : public EnumDelegateFactory + { + protected: + + DataDisplayDelegate::IconList mIcons; + + public: + + virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + + protected: + + void add (int enumValue,const QString enumName, const QString iconFilename); + + }; + +} + +#endif // DATADISPLAYDELEGATE_HPP diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e16de99ef..cedb20de9 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -24,7 +24,7 @@ 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(); diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 7a8b45373..fc9b7ee3b 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -1,6 +1,7 @@ #include "enumdelegate.hpp" +#include #include #include @@ -43,6 +44,9 @@ CSVWorld::EnumDelegate::EnumDelegate (const std::vector 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()); @@ -72,22 +76,50 @@ void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - QStyleOptionViewItemV4 option2 (option); + if (index.data().isValid()) + { + QStyleOptionViewItemV4 option2 (option); - int value = index.data().toInt(); + 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; + 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); - QApplication::style()->drawControl (QStyle::CE_ItemViewItem, &option2, painter); + if (allowNone) + add (-1, ""); - break; - } + for (int i=0; names[i]; ++i) + add (i, names[i]); } +CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector& names, + bool allowNone) +{ + if (allowNone) + add (-1, ""); + + int size = static_cast (names.size()); + + for (int i=0; i > mValues; private: @@ -41,10 +43,20 @@ namespace CSVWorld class EnumDelegateFactory : public CommandDelegateFactory { + protected: 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) + + EnumDelegateFactory (const std::vector& names, bool allowNone = false); + /// \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. @@ -54,4 +66,4 @@ namespace CSVWorld } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp new file mode 100644 index 000000000..df43d6c5f --- /dev/null +++ b/apps/opencs/view/world/genericcreator.cpp @@ -0,0 +1,135 @@ + +#include "genericcreator.hpp" + +#include + +#include +#include +#include +#include + +#include "../../model/world/commands.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" + +#include "idvalidator.hpp" + +void CSVWorld::GenericCreator::update() +{ + mErrors = getErrors(); + + mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str())); + mId->setToolTip (QString::fromUtf8 (mErrors.c_str())); + + mCreate->setEnabled (mErrors.empty() && !mLocked); +} + +void CSVWorld::GenericCreator::setManualEditing (bool enabled) +{ + mId->setVisible (enabled); +} + +void CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretched) +{ + mLayout->insertWidget (0, widget, stretched ? 1 : 0); +} + +void CSVWorld::GenericCreator::insertBeforeButtons (QWidget *widget, bool stretched) +{ + mLayout->insertWidget (mLayout->count()-2, widget, stretched ? 1 : 0); +} + +std::string CSVWorld::GenericCreator::getId() const +{ + return mId->text().toUtf8().constData(); +} + +void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {} + +CSMWorld::Data& CSVWorld::GenericCreator::getData() const +{ + return mData; +} + +const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const +{ + return mListId; +} + +CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) +{ + mLayout = new QHBoxLayout; + mLayout->setContentsMargins (0, 0, 0, 0); + + mId = new QLineEdit; + mId->setValidator (new IdValidator (this)); + mLayout->addWidget (mId, 1); + + mCreate = new QPushButton ("Create"); + mLayout->addWidget (mCreate); + + QPushButton *cancelButton = new QPushButton ("Cancel"); + mLayout->addWidget (cancelButton); + + setLayout (mLayout); + + connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done())); + connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create())); + + connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); +} + +void CSVWorld::GenericCreator::setEditLock (bool locked) +{ + mLocked = locked; + update(); +} + +void CSVWorld::GenericCreator::reset() +{ + mId->setText (""); + update(); +} + +std::string CSVWorld::GenericCreator::getErrors() const +{ + std::string errors; + + std::string id = getId(); + + if (id.empty()) + { + errors = "Missing ID"; + } + else if (mData.hasId (id)) + { + errors = "ID is already in use"; + } + + return errors; +} + +void CSVWorld::GenericCreator::textChanged (const QString& text) +{ + update(); +} + +void CSVWorld::GenericCreator::create() +{ + if (!mLocked) + { + std::string id = getId(); + + std::auto_ptr command (new CSMWorld::CreateCommand ( + dynamic_cast (*mData.getTableModel (mListId)), id)); + + configureCreateCommand (*command); + + mUndoStack.push (command.release()); + + emit done(); + emit requestFocus (id); + } +} \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp new file mode 100644 index 000000000..6752d8591 --- /dev/null +++ b/apps/opencs/view/world/genericcreator.hpp @@ -0,0 +1,73 @@ +#ifndef CSV_WORLD_GENERICCREATOR_H +#define CSV_WORLD_GENERICCREATOR_H + +class QPushButton; +class QLineEdit; +class QHBoxLayout; + +#include "creator.hpp" + +#include "../../model/world/universalid.hpp" + +namespace CSMWorld +{ + class CreateCommand; +} + +namespace CSVWorld +{ + class GenericCreator : public Creator + { + Q_OBJECT + + CSMWorld::Data& mData; + QUndoStack& mUndoStack; + CSMWorld::UniversalId mListId; + QPushButton *mCreate; + QLineEdit *mId; + std::string mErrors; + QHBoxLayout *mLayout; + bool mLocked; + + protected: + + void update(); + + virtual void setManualEditing (bool enabled); + ///< Enable/disable manual ID editing (enabled by default). + + void insertAtBeginning (QWidget *widget, bool stretched); + + void insertBeforeButtons (QWidget *widget, bool stretched); + + virtual std::string getId() const; + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + CSMWorld::Data& getData() const; + + const CSMWorld::UniversalId& getCollectionId() const; + + public: + + GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void setEditLock (bool locked); + + virtual void reset(); + + virtual std::string getErrors() const; + ///< Return formatted error descriptions for the current state of the creator. if an empty + /// string is returned, there is no error. + + + private slots: + + void textChanged (const QString& text); + + void create(); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/idtypedelegate.cpp b/apps/opencs/view/world/idtypedelegate.cpp new file mode 100755 index 000000000..ce4e8f014 --- /dev/null +++ b/apps/opencs/view/world/idtypedelegate.cpp @@ -0,0 +1,46 @@ +#include "idtypedelegate.hpp" + +#include "../../model/world/universalid.hpp" + +CSVWorld::IdTypeDelegate::IdTypeDelegate + (const ValueList &values, const IconList &icons, QUndoStack& undoStack, QObject *parent) + : DataDisplayDelegate (values, icons, undoStack, parent) +{} + +bool CSVWorld::IdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue) +{ + /// \todo make the setting key a member variable, that is initialised from a constructor argument + if (settingName == "Referenceable ID Type Display") + { + if (settingValue == "Icon and Text") + mDisplayMode = Mode_IconAndText; + + else if (settingValue == "Icon Only") + mDisplayMode = Mode_IconOnly; + + else if (settingValue == "Text Only") + mDisplayMode = Mode_TextOnly; + + return true; + } + + return false; +} + + +CSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory() +{ + for (int i=0; i (i)); + + DataDisplayDelegateFactory::add (id.getType(), QString::fromUtf8 (id.getTypeName().c_str()), + QString::fromUtf8 (id.getIcon().c_str())); + } +} + +CSVWorld::CommandDelegate *CSVWorld::IdTypeDelegateFactory::makeDelegate (QUndoStack& undoStack, + QObject *parent) const +{ + return new IdTypeDelegate (mValues, mIcons, undoStack, parent); +} diff --git a/apps/opencs/view/world/idtypedelegate.hpp b/apps/opencs/view/world/idtypedelegate.hpp new file mode 100755 index 000000000..ea80fd0d9 --- /dev/null +++ b/apps/opencs/view/world/idtypedelegate.hpp @@ -0,0 +1,31 @@ +#ifndef IDTYPEDELEGATE_HPP +#define IDTYPEDELEGATE_HPP + +#include "enumdelegate.hpp" +#include "util.hpp" +#include "../../model/world/universalid.hpp" +#include "datadisplaydelegate.hpp" + +namespace CSVWorld +{ + class IdTypeDelegate : public DataDisplayDelegate + { + public: + IdTypeDelegate (const ValueList &mValues, const IconList &icons, QUndoStack& undoStack, QObject *parent); + + virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); + + }; + + class IdTypeDelegateFactory : public DataDisplayDelegateFactory + { + public: + + IdTypeDelegateFactory(); + + virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + }; +} + +#endif // REFIDTYPEDELEGATE_HPP diff --git a/apps/opencs/view/world/idvalidator.cpp b/apps/opencs/view/world/idvalidator.cpp new file mode 100644 index 000000000..cf6e5d77b --- /dev/null +++ b/apps/opencs/view/world/idvalidator.cpp @@ -0,0 +1,26 @@ + +#include "idvalidator.hpp" + +bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const +{ + if (c.isLetter() || c=='_') + return true; + + if (!first && (c.isDigit() || c.isSpace())) + return true; + + return false; +} + +CSVWorld::IdValidator::IdValidator (QObject *parent) : QValidator (parent) {} + +QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const +{ + bool first = true; + + for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false) + if (!isValid (*iter, first)) + return QValidator::Invalid; + + return QValidator::Acceptable; +} \ No newline at end of file diff --git a/apps/opencs/view/world/idvalidator.hpp b/apps/opencs/view/world/idvalidator.hpp new file mode 100644 index 000000000..db0ecb27a --- /dev/null +++ b/apps/opencs/view/world/idvalidator.hpp @@ -0,0 +1,23 @@ +#ifndef CSV_WORLD_IDVALIDATOR_H +#define CSV_WORLD_IDVALIDATOR_H + +#include + +namespace CSVWorld +{ + class IdValidator : public QValidator + { + private: + + bool isValid (const QChar& c, bool first) const; + + public: + + IdValidator (QObject *parent = 0); + + virtual State validate (QString& input, int& pos) const; + + }; +} + +#endif diff --git a/apps/opencs/view/world/recordstatusdelegate.cpp b/apps/opencs/view/world/recordstatusdelegate.cpp new file mode 100644 index 000000000..8085ec7be --- /dev/null +++ b/apps/opencs/view/world/recordstatusdelegate.cpp @@ -0,0 +1,53 @@ +#include "recordstatusdelegate.hpp" + +#include +#include +#include + +#include "../../model/settings/usersettings.hpp" +#include "../../model/world/columns.hpp" + +CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values, + const IconList & icons, + QUndoStack &undoStack, QObject *parent) + : DataDisplayDelegate (values, icons, undoStack, parent) +{} + +CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate (QUndoStack& undoStack, + QObject *parent) const +{ + return new RecordStatusDelegate (mValues, mIcons, undoStack, parent); +} + +bool CSVWorld::RecordStatusDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue) +{ + if (settingName == "Record Status Display") + { + if (settingValue == "Icon and Text") + mDisplayMode = Mode_IconAndText; + + else if (settingValue == "Icon Only") + mDisplayMode = Mode_IconOnly; + + else if (settingValue == "Text Only") + mDisplayMode = Mode_TextOnly; + + return true; + } + + return false; +} + +CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory() +{ + std::vector enums = + CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification); + + static const char *sIcons[] = + { + ":./base.png", ":./modified.png", ":./added.png", ":./removed.png", ":./removed.png", 0 + }; + + for (int i=0; sIcons[i]; ++i) + add (i, enums.at (i).c_str(), sIcons[i]); +} diff --git a/apps/opencs/view/world/recordstatusdelegate.hpp b/apps/opencs/view/world/recordstatusdelegate.hpp new file mode 100644 index 000000000..d9126fee0 --- /dev/null +++ b/apps/opencs/view/world/recordstatusdelegate.hpp @@ -0,0 +1,40 @@ +#ifndef RECORDSTATUSDELEGATE_H +#define RECORDSTATUSDELEGATE_H + +#include "util.hpp" +#include +#include + +#include "datadisplaydelegate.hpp" +#include "../../model/world/record.hpp" + +class QIcon; +class QFont; + +namespace CSVWorld +{ + class RecordStatusDelegate : public DataDisplayDelegate + { + public: + + explicit RecordStatusDelegate(const ValueList& values, + const IconList& icons, + QUndoStack& undoStack, QObject *parent = 0); + + virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); + + }; + + class RecordStatusDelegateFactory : public DataDisplayDelegateFactory + { + public: + + RecordStatusDelegateFactory(); + + virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + + }; +} +#endif // RECORDSTATUSDELEGATE_HPP + diff --git a/apps/opencs/view/world/referenceablecreator.cpp b/apps/opencs/view/world/referenceablecreator.cpp new file mode 100644 index 000000000..718fe9ca7 --- /dev/null +++ b/apps/opencs/view/world/referenceablecreator.cpp @@ -0,0 +1,43 @@ + +#include "referenceablecreator.hpp" + +#include +#include + +#include "../../model/world/universalid.hpp" +#include "../../model/world/commands.hpp" + +void CSVWorld::ReferenceableCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + command.setType ( + static_cast (mType->itemData (mType->currentIndex()).toInt())); +} + +CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + QLabel *label = new QLabel ("Type", this); + insertBeforeButtons (label, false); + + std::vector types = CSMWorld::UniversalId::listReferenceableTypes(); + + mType = new QComboBox (this); + + for (std::vector::const_iterator iter (types.begin()); + iter!=types.end(); ++iter) + { + CSMWorld::UniversalId id (*iter, ""); + + mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(), + static_cast (id.getType())); + } + + insertBeforeButtons (mType, false); +} + +void CSVWorld::ReferenceableCreator::reset() +{ + mType->setCurrentIndex (0); + GenericCreator::reset(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/referenceablecreator.hpp b/apps/opencs/view/world/referenceablecreator.hpp new file mode 100644 index 000000000..06e0e582b --- /dev/null +++ b/apps/opencs/view/world/referenceablecreator.hpp @@ -0,0 +1,30 @@ +#ifndef CSV_WORLD_REFERENCEABLECREATOR_H +#define CSV_WORLD_REFERENCEABLECREATOR_H + +class QComboBox; + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class ReferenceableCreator : public GenericCreator + { + Q_OBJECT + + QComboBox *mType; + + private: + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + }; +} + +#endif diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp new file mode 100644 index 000000000..aef25a81d --- /dev/null +++ b/apps/opencs/view/world/referencecreator.cpp @@ -0,0 +1,75 @@ + +#include "referencecreator.hpp" + +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/idtable.hpp" + +std::string CSVWorld::ReferenceCreator::getId() const +{ + return mId; +} + +void CSVWorld::ReferenceCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast (*getData().getTableModel (getCollectionId())). + findColumnIndex (CSMWorld::Columns::ColumnId_Cell); + + command.addValue (index, mCell->text()); +} + +CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + QLabel *label = new QLabel ("Cell", this); + insertBeforeButtons (label, false); + + mCell = new QLineEdit (this); + insertBeforeButtons (mCell, true); + + setManualEditing (false); + + connect (mCell, SIGNAL (textChanged (const QString&)), this, SLOT (cellChanged())); +} + +void CSVWorld::ReferenceCreator::reset() +{ + mCell->setText (""); + mId = getData().getReferences().getNewId(); + GenericCreator::reset(); +} + +std::string CSVWorld::ReferenceCreator::getErrors() const +{ + std::string errors = GenericCreator::getErrors(); + + std::string cell = mCell->text().toUtf8().constData(); + + if (cell.empty()) + { + if (!errors.empty()) + errors += "
"; + + errors += "Missing Cell ID"; + } + else if (getData().getCells().searchId (cell)==-1) + { + if (!errors.empty()) + errors += "
"; + + errors += "Invalid Cell ID"; + } + + return errors; +} + +void CSVWorld::ReferenceCreator::cellChanged() +{ + update(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp new file mode 100644 index 000000000..27f81564f --- /dev/null +++ b/apps/opencs/view/world/referencecreator.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_REFERENCECREATOR_H +#define CSV_WORLD_REFERENCECREATOR_H + +#include "genericcreator.hpp" + +class QLineEdit; + +namespace CSVWorld +{ + class ReferenceCreator : public GenericCreator + { + Q_OBJECT + + QLineEdit *mCell; + std::string mId; + + private: + + virtual std::string getId() const; + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + virtual std::string getErrors() const; + ///< Return formatted error descriptions for the current state of the creator. if an empty + /// string is returned, there is no error. + + private slots: + + void cellChanged(); + }; +} + +#endif diff --git a/apps/opencs/view/world/regionmapsubview.cpp b/apps/opencs/view/world/regionmapsubview.cpp new file mode 100644 index 000000000..b82c1afb5 --- /dev/null +++ b/apps/opencs/view/world/regionmapsubview.cpp @@ -0,0 +1,29 @@ + +#include "regionmapsubview.hpp" + +#include +#include + +CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, + CSMDoc::Document& document) +: CSVDoc::SubView (universalId) +{ + mTable = new QTableView (this); + + mTable->verticalHeader()->hide(); + mTable->horizontalHeader()->hide(); + + mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); + + mTable->setModel (document.getData().getTableModel (universalId)); + + mTable->resizeColumnsToContents(); + mTable->resizeRowsToContents(); + + setWidget (mTable); +} + +void CSVWorld::RegionMapSubView::setEditLock (bool locked) +{ + +} \ No newline at end of file diff --git a/apps/opencs/view/world/regionmapsubview.hpp b/apps/opencs/view/world/regionmapsubview.hpp new file mode 100644 index 000000000..1655107af --- /dev/null +++ b/apps/opencs/view/world/regionmapsubview.hpp @@ -0,0 +1,27 @@ +#ifndef CSV_WORLD_REGIONMAPSUBVIEW_H +#define CSV_WORLD_REGIONMAPSUBVIEW_H + +#include "../doc/subview.hpp" + +class QTableView; + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class RegionMapSubView : public CSVDoc::SubView + { + QTableView *mTable; + + public: + + RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + }; +} + +#endif diff --git a/apps/opencs/view/world/scripthighlighter.cpp b/apps/opencs/view/world/scripthighlighter.cpp new file mode 100644 index 000000000..e06dab372 --- /dev/null +++ b/apps/opencs/view/world/scripthighlighter.cpp @@ -0,0 +1,133 @@ + +#include "scripthighlighter.hpp" + +#include + +#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, mContext.isId (name) ? Type_Id : 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 (const CSMWorld::Data& data, QTextDocument *parent) +: QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data) +{ + /// \todo 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)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::blue); + mScheme.insert (std::make_pair (Type_Id, format)); + } + + // configure compiler + Compiler::registerExtensions (mExtensions); + mContext.setExtensions (&mExtensions); +} + +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 +} + +void CSVWorld::ScriptHighlighter::invalidateIds() +{ + mContext.invalidateIds(); +} \ 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..495c2e6a3 --- /dev/null +++ b/apps/opencs/view/world/scripthighlighter.hpp @@ -0,0 +1,85 @@ +#ifndef CSV_WORLD_SCRIPTHIGHLIGHTER_H +#define CSV_WORLD_SCRIPTHIGHLIGHTER_H + +#include + +#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, + Type_Id + }; + + private: + + Compiler::NullErrorHandler mErrorHandler; + Compiler::Extensions mExtensions; + 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 (const CSMWorld::Data& data, QTextDocument *parent); + + virtual void highlightBlock (const QString& text); + + void invalidateIds(); + }; +} + +#endif diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp new file mode 100644 index 000000000..446c34e5f --- /dev/null +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -0,0 +1,128 @@ + +#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))); + + connect (&document.getData(), SIGNAL (idListChanged()), this, SLOT (idListChanged())); + + mHighlighter = new ScriptHighlighter (document.getData(), mEditor->document()); + + connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); + + mUpdateTimer.setSingleShot (true); +} + +void CSVWorld::ScriptSubView::setEditLock (bool locked) +{ + mEditor->setReadOnly (locked); +} + +void CSVWorld::ScriptSubView::idListChanged() +{ + mHighlighter->invalidateIds(); + + if (!mUpdateTimer.isActive()) + mUpdateTimer.start (0); +} + +void CSVWorld::ScriptSubView::textChanged() +{ + if (mChangeLocked) + return; + + 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; + + ChangeLock lock (*this); + + 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(); +} + +void CSVWorld::ScriptSubView::updateHighlighting() +{ + if (mChangeLocked) + return; + + ChangeLock lock (*this); + + mHighlighter->rehighlight(); +} \ 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..7ceab70ba --- /dev/null +++ b/apps/opencs/view/world/scriptsubview.hpp @@ -0,0 +1,74 @@ +#ifndef CSV_WORLD_SCRIPTSUBVIEW_H +#define CSV_WORLD_SCRIPTSUBVIEW_H + +#include "../doc/subview.hpp" + +#include + +class QTextEdit; +class QModelIndex; + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSVWorld +{ + class ScriptHighlighter; + + class ScriptSubView : public CSVDoc::SubView + { + Q_OBJECT + + QTextEdit *mEditor; + CSMDoc::Document& mDocument; + CSMWorld::IdTable *mModel; + int mColumn; + int mChangeLocked; + ScriptHighlighter *mHighlighter; + QTimer mUpdateTimer; + + 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); + + public slots: + + void idListChanged(); + + void textChanged(); + + void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + private slots: + + void updateHighlighting(); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 351007ded..d22e07d89 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -3,17 +3,63 @@ #include "../doc/subviewfactoryimp.hpp" +#include "../filter/filtercreator.hpp" + #include "tablesubview.hpp" #include "dialoguesubview.hpp" +#include "scriptsubview.hpp" +#include "regionmapsubview.hpp" +#include "genericcreator.hpp" +#include "cellcreator.hpp" +#include "referenceablecreator.hpp" +#include "referencecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { - manager.add (CSMWorld::UniversalId::Type_Globals, - new CSVDoc::SubViewFactoryWithCreateFlag (true)); - + // Regular record tables (including references which are actually sub-records, but are promoted + // to top-level records within the editor) manager.add (CSMWorld::UniversalId::Type_Gmsts, - new CSVDoc::SubViewFactoryWithCreateFlag (false)); + new CSVDoc::SubViewFactoryWithCreator); + + manager.add (CSMWorld::UniversalId::Type_Skills, + new CSVDoc::SubViewFactoryWithCreator); + + 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_None // end marker + }; + + for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i) + manager.add (sTableTypes[i], + new CSVDoc::SubViewFactoryWithCreator >); + + manager.add (CSMWorld::UniversalId::Type_Cells, + new CSVDoc::SubViewFactoryWithCreator >); + + manager.add (CSMWorld::UniversalId::Type_Referenceables, + new CSVDoc::SubViewFactoryWithCreator >); + + manager.add (CSMWorld::UniversalId::Type_References, + new CSVDoc::SubViewFactoryWithCreator >); + + // Subviews for editing/viewing individual records + manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); + + // Other stuff (combined record tables) + manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); + + manager.add (CSMWorld::UniversalId::Type_Filters, + new CSVDoc::SubViewFactoryWithCreator >); - 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 f9167d259..6167c084a 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -13,6 +13,7 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" +#include "recordstatusdelegate.hpp" #include "util.hpp" void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) @@ -25,6 +26,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (!mEditLock) { + if (selectedRows.size()==1) + menu.addAction (mEditAction); + if (mCreateAction) menu.addAction (mCreateAction); @@ -40,19 +44,31 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) std::vector CSVWorld::Table::listRevertableSelectedIds() const { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - std::vector revertableIds; - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + if (mProxyModel->columnCount()>0) { - std::string id = mProxyModel->data (*iter).toString().toStdString(); + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - if (state!=CSMWorld::RecordBase::State_BaseOnly) - revertableIds.push_back (id); + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + revertableIds.push_back (id); + } + } } return revertableIds; @@ -60,19 +76,31 @@ std::vector CSVWorld::Table::listRevertableSelectedIds() const std::vector CSVWorld::Table::listDeletableSelectedIds() const { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - std::vector deletableIds; - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + if (mProxyModel->columnCount()>0) { - std::string id = mProxyModel->data (*iter).toString().toStdString(); + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); - if (state!=CSMWorld::RecordBase::State_Deleted) - deletableIds.push_back (id); + if (state!=CSMWorld::RecordBase::State_Deleted) + { + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); + } + } } return deletableIds; @@ -80,7 +108,7 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete) -: mUndoStack (undoStack), mCreateAction (0), mEditLock (false) + : mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0) { mModel = &dynamic_cast (*data.getTableModel (id)); @@ -115,12 +143,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q hideColumn (i); } - /// \todo make initial layout fill the whole width of the table + mEditAction = new QAction (tr ("Edit Record"), this); + connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); + addAction (mEditAction); if (createAndDelete) { mCreateAction = new QAction (tr ("Add Record"), this); - connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); + connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); } @@ -131,6 +161,17 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mDeleteAction = new QAction (tr ("Delete Record"), this); connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); addAction (mDeleteAction); + + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (tableSizeUpdate())); + + /// \note This signal could instead be connected to a slot that filters out changes not affecting + /// the records status column (for permanence reasons) + connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (tableSizeUpdate())); + + connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), + this, SLOT (selectionSizeUpdate ())); } void CSVWorld::Table::setEditLock (bool locked) @@ -148,21 +189,6 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); } -#include /// \todo remove -void CSVWorld::Table::createRecord() -{ - if (!mEditLock) - { - /// \todo ask the user for an ID instead. - static int index = 0; - - std::ostringstream stream; - stream << "id" << index++; - - mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str())); - } -} - void CSVWorld::Table::revertRecord() { if (!mEditLock) @@ -201,4 +227,74 @@ void CSVWorld::Table::deleteRecord() mUndoStack.endMacro(); } } +} + +void CSVWorld::Table::editRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + emit editRequest (selectedRows.begin()->row()); + } +} + +void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue) +{ + int columns = mModel->columnCount(); + + for (int i=0; i (*delegate). + updateEditorSetting (settingName, settingValue)) + emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); +} + +void CSVWorld::Table::tableSizeUpdate() +{ + int size = 0; + int deleted = 0; + int modified = 0; + + if (mProxyModel->columnCount()>0) + { + int rows = mProxyModel->rowCount(); + + for (int i=0; imapToSource (mProxyModel->index (i, 0)); + + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); + + switch (state) + { + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + } + } + } + + tableSizeChanged (size, deleted, modified); +} + +void CSVWorld::Table::selectionSizeUpdate() +{ + selectionSizeChanged (selectionModel()->selectedRows().size()); +} + +void CSVWorld::Table::requestFocus (const std::string& id) +{ + QModelIndex index = mProxyModel->getModelIndex (id, 0); + + if (index.isValid()) + scrollTo (index, QAbstractItemView::PositionAtTop); +} + +void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) +{ + mProxyModel->setFilter (filter); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index df0224583..d93109056 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -6,6 +6,8 @@ #include +#include "../../model/filter/node.hpp" + class QUndoStack; class QAction; @@ -28,12 +30,14 @@ namespace CSVWorld std::vector mDelegates; QUndoStack& mUndoStack; + QAction *mEditAction; QAction *mCreateAction; QAction *mRevertAction; QAction *mDeleteAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; bool mEditLock; + int mRecordStatusDisplay; private: @@ -52,13 +56,38 @@ namespace CSVWorld CSMWorld::UniversalId getUniversalId (int row) const; - private slots: + void updateEditorSetting (const QString &settingName, const QString &settingValue); + + signals: + + void editRequest (int row); + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); - void createRecord(); + private slots: void revertRecord(); void deleteRecord(); + + void editRecord(); + + public slots: + + void tableSizeUpdate(); + + void selectionSizeUpdate(); + + void requestFocus (const std::string& id); + + void recordFilterChanged (boost::shared_ptr filter); }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp new file mode 100644 index 000000000..3edf9af31 --- /dev/null +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -0,0 +1,159 @@ + +#include "tablebottombox.hpp" + +#include + +#include +#include +#include + +#include "creator.hpp" + +void CSVWorld::TableBottomBox::updateStatus() +{ + if (mShowStatusBar) + { + static const char *sLabels[4] = { "record", "deleted", "touched", "selected" }; + static const char *sLabelsPlural[4] = { "records", "deleted", "touched", "selected" }; + + std::ostringstream stream; + + bool first = true; + + for (int i=0; i<4; ++i) + { + if (mStatusCount[i]>0) + { + if (first) + first = false; + else + stream << ", "; + + stream + << mStatusCount[i] << ' ' + << (mStatusCount[i]==1 ? sLabels[i] : sLabelsPlural[i]); + } + } + + mStatus->setText (QString::fromUtf8 (stream.str().c_str())); + } +} + +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent) +: QWidget (parent), mShowStatusBar (false), mCreating (false) +{ + for (int i=0; i<4; ++i) + mStatusCount[i] = 0; + + setVisible (false); + + mLayout = new QStackedLayout; + mLayout->setContentsMargins (0, 0, 0, 0); + + mStatus = new QLabel; + + mStatusBar = new QStatusBar; + + mStatusBar->addWidget (mStatus); + + mLayout->addWidget (mStatusBar); + + setLayout (mLayout); + + mCreator = creatorFactory.makeCreator (data, undoStack, id); + + if (mCreator) + { + mLayout->addWidget (mCreator); + + connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone())); + + connect (mCreator, SIGNAL (requestFocus (const std::string&)), + this, SIGNAL (requestFocus (const std::string&))); + } +} + +void CSVWorld::TableBottomBox::setEditLock (bool locked) +{ + if (mCreator) + mCreator->setEditLock (locked); +} + +CSVWorld::TableBottomBox::~TableBottomBox() +{ + delete mCreator; +} + +void CSVWorld::TableBottomBox::setStatusBar (bool show) +{ + if (show!=mShowStatusBar) + { + setVisible (show || mCreating); + + mShowStatusBar = show; + + if (show) + updateStatus(); + } +} + +bool CSVWorld::TableBottomBox::canCreateAndDelete() const +{ + return mCreator; +} + +void CSVWorld::TableBottomBox::createRequestDone() +{ + if (!mShowStatusBar) + setVisible (false); + else + updateStatus(); + + mLayout->setCurrentWidget (mStatusBar); + + mCreating = false; +} + +void CSVWorld::TableBottomBox::selectionSizeChanged (int size) +{ + if (mStatusCount[3]!=size) + { + mStatusCount[3] = size; + updateStatus(); + } +} + +void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modified) +{ + bool changed = false; + + if (mStatusCount[0]!=size) + { + mStatusCount[0] = size; + changed = true; + } + + if (mStatusCount[1]!=deleted) + { + mStatusCount[1] = deleted; + changed = true; + } + + if (mStatusCount[2]!=modified) + { + mStatusCount[2] = modified; + changed = true; + } + + if (changed) + updateStatus(); +} + +void CSVWorld::TableBottomBox::createRequest() +{ + mCreator->reset(); + mLayout->setCurrentWidget (mCreator); + setVisible (true); + mCreating = true; +} \ No newline at end of file diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp new file mode 100644 index 000000000..a5ae5e0bd --- /dev/null +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -0,0 +1,82 @@ +#ifndef CSV_WORLD_BOTTOMBOX_H +#define CSV_WORLD_BOTTOMBOX_H + +#include + +class QLabel; +class QStackedLayout; +class QStatusBar; +class QUndoStack; + +namespace CSMWorld +{ + class Data; + class UniversalId; +} + +namespace CSVWorld +{ + class CreatorFactoryBase; + class Creator; + + class TableBottomBox : public QWidget + { + Q_OBJECT + + bool mShowStatusBar; + QLabel *mStatus; + QStatusBar *mStatusBar; + int mStatusCount[4]; + Creator *mCreator; + bool mCreating; + QStackedLayout *mLayout; + + private: + + // not implemented + TableBottomBox (const TableBottomBox&); + TableBottomBox& operator= (const TableBottomBox&); + + void updateStatus(); + + public: + + TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent = 0); + + virtual ~TableBottomBox(); + + void setEditLock (bool locked); + + void setStatusBar (bool show); + + bool canCreateAndDelete() const; + ///< Is record creation and deletion supported? + /// + /// \note The BotomBox does not partake in the deletion of records. + + signals: + + void requestFocus (const std::string& id); + ///< Request owner of this box to focus the just created \a id. The owner may + /// ignore this request. + + private slots: + + void createRequestDone(); + ///< \note This slot being called does not imply success. + + public slots: + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); + }; +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index f4deceb49..1e05fbf51 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,26 +1,78 @@ #include "tablesubview.hpp" +#include + #include "../../model/doc/document.hpp" +#include "../filter/filterbox.hpp" + #include "table.hpp" +#include "tablebottombox.hpp" +#include "creator.hpp" CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - bool createAndDelete) + const CreatorFactoryBase& creatorFactory) : SubView (id) { - setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete)); + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); + + layout->insertWidget (0, mTable = + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2); + + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + + layout->insertWidget (0, filterBox); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); + + connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); - connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (rowActivated (const QModelIndex&))); + connect (mTable, SIGNAL (selectionSizeChanged (int)), + mBottom, SLOT (selectionSizeChanged (int))); + connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), + mBottom, SLOT (tableSizeChanged (int, int, int))); + + mTable->tableSizeUpdate(); + mTable->selectionSizeUpdate(); + + if (mBottom->canCreateAndDelete()) + connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); + + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + mTable, SLOT (requestFocus (const std::string&))); + + connect (filterBox, + SIGNAL (recordFilterChanged (boost::shared_ptr)), + mTable, SLOT (recordFilterChanged (boost::shared_ptr))); } void CSVWorld::TableSubView::setEditLock (bool locked) { mTable->setEditLock (locked); + mBottom->setEditLock (locked); +} + +void CSVWorld::TableSubView::editRequest (int row) +{ + focusId (mTable->getUniversalId (row)); +} + +void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) +{ + mTable->updateEditorSetting(settingName, settingValue); } -void CSVWorld::TableSubView::rowActivated (const QModelIndex& index) +void CSVWorld::TableSubView::setStatusBar (bool show) { - /// \todo re-enable, after dialogue sub views have been fixed up -// focusId (mTable->getUniversalId (index.row())); + mBottom->setStatusBar (show); } \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 0e7b8aa30..d61c78935 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -13,23 +13,31 @@ namespace CSMDoc namespace CSVWorld { class Table; + class TableBottomBox; + class CreatorFactoryBase; class TableSubView : public CSVDoc::SubView { Q_OBJECT Table *mTable; + TableBottomBox *mBottom; public: - TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory); virtual void setEditLock (bool locked); + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); + private slots: - void rowActivated (const QModelIndex& index); + void editRequest (int row); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 5ada1d84f..97af3b99c 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -116,6 +116,16 @@ void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemMode ///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible. } +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; @@ -124,4 +134,10 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked) bool CSVWorld::CommandDelegate::isEditLocked() const { return mEditLock; +} + +bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName, + const QString &settingValue) +{ + return false; } \ No newline at end of file diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 5334abf9c..87f118cd7 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -82,6 +82,8 @@ namespace CSVWorld ///< \brief Use commands instead of manipulating the model directly class CommandDelegate : public QStyledItemDelegate { + Q_OBJECT + QUndoStack& mUndoStack; bool mEditLock; @@ -99,9 +101,19 @@ namespace CSVWorld 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; + + virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); + ///< \return Does column require update? + + private slots: + + virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {} }; } diff --git a/apps/opencs/view/world/vartypedelegate.cpp b/apps/opencs/view/world/vartypedelegate.cpp index 72cbaae42..15ce2dbaf 100644 --- a/apps/opencs/view/world/vartypedelegate.cpp +++ b/apps/opencs/view/world/vartypedelegate.cpp @@ -4,6 +4,7 @@ #include #include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) const @@ -75,29 +76,11 @@ CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate (QUndo void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type) { - struct Name - { - ESM::VarType mType; - const char *mName; - }; + std::vector enums = + CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType); - 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"); + if (type<0 && type>=enums.size()) + throw std::logic_error ("Unsupported variable type"); + + mValues.push_back (std::make_pair (type, QString::fromUtf8 (enums[type].c_str()))); } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8df2136e5..b367e2a1e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -14,9 +14,10 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation - actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows + renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation + actors objects renderinginterface localmap occlusionquery water shadows compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction + terrainstorage ) add_openmw_dir (mwinput @@ -24,13 +25,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 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 exposedwindow cursor spellicons + merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks + keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview + tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers ) add_openmw_dir (mwdialogue @@ -53,7 +57,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,9 +66,9 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanagerimp stat character creaturestats magiceffects movement actors activators + mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow - aiescort aiactivate + aiescort aiactivate repair enchanting pathfinding security ) add_openmw_dir (mwbase @@ -101,31 +105,43 @@ add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw ${OGRE_LIBRARIES} - ${OGRE_Terrain_LIBRARY} ${OGRE_STATIC_PLUGINS} - ${OIS_LIBRARIES} ${Boost_LIBRARIES} ${OPENAL_LIBRARY} ${SOUND_INPUT_LIBRARY} ${BULLET_LIBRARIES} ${MYGUI_LIBRARIES} + ${SDL2_LIBRARY} ${MYGUI_PLATFORM_LIBRARIES} - "shiny" - "shiny.OgrePlatform" + ${SHINY_LIBRARIES} "oics" + "sdl4ogre" components ) +if (USE_SYSTEM_TINYXML) + target_link_libraries(openmw ${TINYXML_LIBRARIES}) +endif() + +if (NOT UNIX) +target_link_libraries(openmw ${SDL2MAIN_LIBRARY}) +endif() + # Fix for not visible pthreads functions for linker with glibc 2.15 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}) + target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) if (FFMPEG_FOUND) find_library(COREVIDEO_FRAMEWORK CoreVideo) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index ce84b8dfe..a2eccbaf9 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1,4 +1,5 @@ #include "engine.hpp" + #include "components/esm/loadcell.hpp" #include @@ -6,6 +7,8 @@ #include +#include + #include #include #include @@ -37,6 +40,8 @@ #include "mwmechanics/mechanicsmanagerimp.hpp" +#include + void OMW::Engine::executeLocalScripts() { MWWorld::LocalScripts& localScripts = MWBase::Environment::get().getWorld()->getLocalScripts(); @@ -64,8 +69,9 @@ 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); + bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused); + MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame); return true; } @@ -74,7 +80,8 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) try { float frametime = std::min(evt.timeSinceLastFrame, 0.2f); - mEnvironment.setFrameDuration(frametime); + + mEnvironment.setFrameDuration (frametime); // update input MWBase::Environment::get().getInputManager()->update(frametime, false); @@ -116,6 +123,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); MWBase::Environment::get().getWindowManager()->onFrame(frametime); + MWBase::Environment::get().getWindowManager()->update(); } catch (const std::exception& e) { @@ -128,7 +136,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mOgre (0) , mFpsLevel(0) - , mDebug (false) , mVerboseScripts (false) , mNewGame (false) , mUseSound (true) @@ -137,9 +144,25 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mFSStrict (false) , mScriptConsoleMode (false) , mCfgMgr(configurationManager) + , mEncoding(ToUTF8::WINDOWS_1252) + , mEncoder(NULL) + , mActivationDistanceOverride(-1) + { std::srand ( std::time(NULL) ); MWClass::registerClasses(); + + Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE; + if(SDL_WasInit(flags) == 0) + { + //kindly ask SDL not to trash our OGL context + //might this be related to http://bugzilla.libsdl.org/show_bug.cgi?id=748 ? + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + if(SDL_Init(flags) != 0) + { + throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError())); + } + } } OMW::Engine::~Engine() @@ -147,34 +170,49 @@ OMW::Engine::~Engine() mEnvironment.cleanup(); delete mScriptContext; delete mOgre; + SDL_Quit(); } // Load BSA files void OMW::Engine::loadBSA() { + // We use separate resource groups to handle location priority. + const Files::PathContainer& dataDirs = mFileCollections.getPaths(); + + int i=0; + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) + { + // 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; + } + + i=0; for (std::vector::const_iterator archive = mArchives.begin(); archive != mArchives.end(); ++archive) { 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); + Bsa::addBSA(archivePath, groupName); + ++i; } else { std::cout << "Archive " << *archive << " not found" << std::endl; } } - - const Files::PathContainer& dataDirs = mFileCollections.getPaths(); - std::string dataDirectory; - for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) - { - dataDirectory = iter->string(); - std::cout << "Data dir " << dataDirectory << std::endl; - Bsa::addDir(dataDirectory, mFSStrict); - } } // add resources directory @@ -253,11 +291,6 @@ void OMW::Engine::addPlugin (const std::string& plugin) } } -void OMW::Engine::setDebugMode(bool debugMode) -{ - mDebug = debugMode; -} - void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) { mVerboseScripts = scriptsVerbosity; @@ -300,6 +333,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg")) nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg"); + settings.setBool("hardware cursors", "GUI", true); + return settingspath; } @@ -337,34 +372,52 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) addResourcesDirectory(mResDir / "shadows"); addZipResource(mResDir / "mygui" / "Obliviontt.zip"); - // Create the window OEngine::Render::WindowSettings windowSettings; windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); windowSettings.window_x = settings.getInt("resolution x", "Video"); windowSettings.window_y = settings.getInt("resolution y", "Video"); + windowSettings.screen = settings.getInt("screen", "Video"); windowSettings.vsync = settings.getBool("vsync", "Video"); + windowSettings.icon = "openmw.png"; std::string aa = settings.getString("antialiasing", "Video"); windowSettings.fsaa = (aa.substr(0, 4) == "MSAA") ? aa.substr(5, aa.size()-5) : "0"; + mOgre->createWindow("OpenMW", windowSettings); loadBSA(); + + // Create input and UI first to set up a bootstrapping environment for + // showing a loading screen and keeping the window responsive while doing so + + std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); + MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists); + mEnvironment.setInputManager (input); + + MWGui::WindowManager* window = new MWGui::WindowManager( + mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding); + mEnvironment.setWindowManager (window); + if (mNewGame) + mEnvironment.getWindowManager()->setNewGame(true); + // Create the world mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, - mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, + mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mActivationDistanceOverride)); + MWBase::Environment::get().getWorld()->setupPlayer(); + input->setPlayer(&mEnvironment.getWorld()->getPlayer()); + + window->initUI(); + window->renderWorldMap(); //Load translation data mTranslationDataStorage.setEncoder(mEncoder); 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)); + Compiler::registerExtensions (mExtensions); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -383,35 +436,29 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage)); - // Sets up the input system - - // Get the path for the keybinder xml file - std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - bool keybinderUserExists = boost::filesystem::exists(keybinderUser); - - mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, - MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *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; + MWBase::World *world = MWBase::Environment::get().getWorld(); + + if (world->findExteriorPosition(mCellName, pos)) { + world->changeToExteriorCell (pos); + } + else { + world->findInteriorPosition(mCellName, pos); + world->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 @@ -437,7 +484,7 @@ void OMW::Engine::go() assert (!mOgre); Settings::Manager settings; - std::string settingspath; + std::string settingspath; settingspath = loadSettings (settings); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index f80b67a35..665b0094c 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -71,7 +71,6 @@ namespace OMW std::vector mMaster; std::vector mPlugins; int mFpsLevel; - bool mDebug; bool mVerboseScripts; bool mNewGame; bool mUseSound; @@ -147,10 +146,6 @@ namespace OMW /// Enable fps counter void showFPS(int level); - /// Enable debug mode: - /// - non-exclusive input - void setDebugMode(bool debugMode); - /// Enable or disable verbose script output void setScriptsVerbosity(bool scriptsVerbosity); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 1fa461c2f..27afd734a 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -2,6 +2,7 @@ #include +#include #include "engine.hpp" #if defined(_WIN32) && !defined(_CONSOLE) @@ -53,7 +54,7 @@ void validate(boost::any &v, std::vector const &tokens, FallbackMap FallbackMap *map = boost::any_cast(&v); std::map::iterator mapIt; - for(std::vector::const_iterator it=tokens.begin(); it != tokens.end(); it++) + for(std::vector::const_iterator it=tokens.begin(); it != tokens.end(); ++it) { int sep = it->find(","); if(sep < 1 || sep == (int)it->length()-1) @@ -118,9 +119,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("anim-verbose", bpo::value()->implicit_value(true) ->default_value(false), "output animation indices files") - ("debug", bpo::value()->implicit_value(true) - ->default_value(false), "debug mode") - ("nosound", bpo::value()->implicit_value(true) ->default_value(false), "disable all sounds") @@ -206,7 +204,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // fallback archives StringsVector archives = variables["fallback-archive"].as(); - for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); it++) + for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); ++it) { engine.addArchive(*it); } @@ -217,8 +215,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat StringsVector master = variables["master"].as(); if (master.empty()) { - std::cout << "No master file given. Assuming Morrowind.esm" << std::endl; - master.push_back("Morrowind"); + std::cout << "No master file given. Aborting...\n"; + return false; } StringsVector plugin = variables["plugin"].as(); @@ -243,7 +241,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setNewGame(variables["new-game"].as()); // other settings - engine.setDebugMode(variables["debug"].as()); engine.setSoundUsage(!variables["nosound"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setCompileAll(variables["script-all"].as()); diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index a205e78db..58731d1c7 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; @@ -36,10 +40,14 @@ namespace MWBase virtual MWWorld::Ptr getActor() const = 0; ///< Return the actor the player is currently talking to. + virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const = 0; + //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 b8733259f..7e09f9b4d 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -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. @@ -99,6 +99,9 @@ namespace MWBase float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; ///< Perform a persuasion action on NPC + virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; + ///< Forces an object to refresh its animation state. + virtual void 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. @@ -109,6 +112,8 @@ namespace MWBase 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 5d396fac0..4d764597c 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -39,16 +39,19 @@ namespace MWBase Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position + Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ + + Play_LoopNoEnv = Play_Loop | Play_NoEnv }; enum PlayType { Play_TypeSfx = 1<<3, /* Normal SFX sound */ Play_TypeVoice = 1<<4, /* Voice sound */ - Play_TypeMusic = 1<<5, /* Music track */ - Play_TypeMovie = 1<<6, /* Movie audio track */ - Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie + Play_TypeFoot = 1<<5, /* Footstep sound */ + Play_TypeMusic = 1<<6, /* Music track */ + Play_TypeMovie = 1<<7, /* Movie audio track */ + Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeFoot|Play_TypeMusic|Play_TypeMovie }; private: @@ -102,12 +105,16 @@ namespace MWBase ///< Play a 2D audio track, using a custom decoder virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, - PlayMode mode=Play_Normal) = 0; + PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, + float offset=0) = 0; ///< Play a sound, independently of 3D-position + ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, - float volume, float pitch, PlayMode mode=Play_Normal) = 0; + float volume, float pitch, PlayType type=Play_TypeSfx, + PlayMode mode=Play_Normal, float offset=0) = 0; ///< Play a sound from an object + ///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts. virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, @@ -121,8 +128,15 @@ namespace MWBase virtual void stopSound(const std::string& soundId) = 0; ///< Stop a non-3d looping sound + virtual void fadeOutSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float duration) = 0; + ///< Fade out given sound (that is already playing) of given object + ///< @param reference Reference to object, whose sound is faded out + ///< @param soundId ID of the sound to fade out. + ///< @param duration Time until volume reaches 0. + virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? + /// If you want to check if sound played with playSound is playing, use empty Ptr virtual void pauseSounds(int types=Play_TypeMask) = 0; ///< Pauses all currently playing sounds, including music. diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 93cc8e44a..1cd867223 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -9,6 +9,8 @@ #include +#include + #include "../mwmechanics/stat.hpp" #include "../mwgui/mode.hpp" @@ -55,6 +57,11 @@ namespace MWGui class DialogueWindow; } +namespace SFO +{ + class CursorManager; +} + namespace MWBase { /// \brief Interface for widnow manager (implemented in MWGui) @@ -81,18 +88,28 @@ 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; + virtual void forceHide(MWGui::GuiWindow wnd) = 0; + virtual void unsetForceHide(MWGui::GuiWindow wnd) = 0; + /// Disallow all inventory mode windows virtual void disallowAll() = 0; @@ -126,6 +143,10 @@ namespace MWBase virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; + /// Set time left for the player to start drowning (update the drowning bar) + /// @param time value from [0,20] + virtual void setDrowningTimeLeft (float time) =0; + virtual void setPlayerClass (const ESM::Class &class_) = 0; ///< set current class of player @@ -153,7 +174,7 @@ namespace MWBase virtual void setFocusObject(const MWWorld::Ptr& focus) = 0; virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0; - virtual void setMouseVisible(bool visible) = 0; + virtual void setCursorVisible(bool visible) = 0; virtual void getMousePosition(int &x, int &y) = 0; virtual void getMousePosition(float &x, float &y) = 0; virtual void setDragDrop(bool dragDrop) = 0; @@ -169,6 +190,9 @@ namespace MWBase virtual void setInteriorMapTexture(const int x, const int y) = 0; ///< set the index of the map texture that should be used (for interiors) + /// sets the visibility of the drowning bar + virtual void setDrowningBarVisibility(bool visible) = 0; + /// sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible) = 0; @@ -176,12 +200,13 @@ namespace MWBase virtual void setMinimapVisibility(bool visible) = 0; virtual void setWeaponVisibility(bool visible) = 0; virtual void setSpellVisibility(bool visible) = 0; + virtual void setSneakVisibility(bool visible) = 0; 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; @@ -198,8 +223,12 @@ namespace MWBase virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector()) = 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 void activateKeyPressed () = 0; virtual int readPressedButton() = 0; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) @@ -222,24 +251,38 @@ namespace MWBase virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; - virtual void executeInConsole (const std::string& path) = 0; + virtual void windowResized(int x, int y) = 0; - virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0; - virtual void loadingDone() = 0; + virtual void executeInConsole (const std::string& path) = 0; 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 void setEnemy (const MWWorld::Ptr& enemy) = 0; + virtual const Translation::Storage& getTranslationDataStorage() const = 0; + + virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; + + virtual Loading::Listener* getLoadingScreen() = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6cd5b90b4..f8453afed 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -44,8 +44,14 @@ namespace MWRender class Animation; } +namespace MWMechanics +{ + class Movement; +} + namespace MWWorld { + class Fallback; class CellStore; class Player; class LocalScripts; @@ -53,7 +59,7 @@ namespace MWWorld class ESMStore; class RefData; - typedef std::vector > PtrMovementList; + typedef std::vector > PtrMovementList; } namespace MWBase @@ -88,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. @@ -103,11 +111,7 @@ 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; @@ -142,7 +146,7 @@ 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; @@ -197,6 +201,8 @@ namespace MWBase virtual void setMoonColour (bool red) = 0; + virtual void modRegion(const std::string ®ionid, const std::vector &chances) = 0; + virtual float getTimeScaleFactor() const = 0; virtual void changeToInteriorCell (const std::string& cellName, @@ -214,6 +220,14 @@ namespace MWBase virtual MWWorld::Ptr getFacedObject() = 0; ///< Return pointer to the object the player is looking at, if it is within activation range + /// Returns a pointer to the object the provided object would hit (if within the + /// specified distance), and the point where the hit occurs. This will attempt to + /// use the "Head" node as a basis. + virtual std::pair getHitContact(const MWWorld::Ptr &ptr, float distance) = 0; + + 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; @@ -225,6 +239,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). @@ -235,8 +251,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 MWWorld::PtrMovementList &actors, float duration) = 0; - ///< Run physics simulation and modify \a world accordingly. + virtual void queueMovement(const MWWorld::Ptr &ptr, const Ogre::Vector3 &velocity) = 0; + ///< Queues movement for \a ptr (in local space), to be applied in the next call to + /// doPhysics. + + 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 @@ -247,27 +267,44 @@ 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 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 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; @@ -288,18 +325,39 @@ namespace MWBase virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; + ///Is the head of the creature underwater? + virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; virtual bool 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 setCameraDistance(float dist, bool adjust = false, bool override = true)=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 enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; virtual int canRest() = 0; @@ -315,7 +373,32 @@ namespace MWBase /// \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; + virtual void frameStarted (float dt, bool paused) = 0; + + /// Find default position inside exterior cell specified by name + /// \return false if exterior with given name not exists, true otherwise + virtual bool findExteriorPosition(const std::string &name, ESM::Position &pos) = 0; + + /// Find default position inside interior cell specified by name + /// \return false if interior with given name not exists, true otherwise + virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos) = 0; + + /// Enables or disables use of teleport spell effects (recall, intervention, etc). + virtual void enableTeleporting(bool enable) = 0; + + /// Returns true if teleport spell effects are allowed. + virtual bool isTeleportingEnabled() const = 0; + + /// Turn actor into werewolf or normal form. + virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; + + /// Sets the NPC's Acrobatics skill to match the fWerewolfAcrobatics GMST. + /// It only applies to the current form the NPC is in. + virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0; + + virtual bool getGodModeState() = 0; + + virtual bool toggleGodMode() = 0; }; } diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 3a60d9c39..583cb08d3 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -6,19 +6,26 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/action.hpp" +#include "../mwworld/failedaction.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" +#include "../mwmechanics/npcstats.hpp" + + namespace MWClass { - void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); if (!model.empty()) { @@ -94,7 +101,23 @@ namespace MWClass return info; } - + + boost::shared_ptr Activator::activate(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor) const + { + if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfActivator"); + + boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); + if(sound) action->setSound(sound->mId); + + return action; + } + return boost::shared_ptr(new MWWorld::NullAction); + } + + MWWorld::Ptr Activator::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 4165fbc08..1e772ef4f 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -31,6 +31,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; + ///< Generate action for activation + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 2c561eb85..697b75579 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -25,9 +25,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -35,7 +33,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 @@ -62,15 +60,7 @@ 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)); - - action->setSound(getUpSoundId(ptr)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const @@ -159,4 +149,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 654cb87fd..a511207c4 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -16,6 +16,8 @@ #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" @@ -28,9 +30,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -38,7 +38,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 @@ -65,14 +65,7 @@ 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)); - - return action; + return defaultItemActivate(ptr, actor); } bool Armor::hasItemHealth (const MWWorld::Ptr& ptr) const @@ -247,8 +240,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}"); @@ -259,6 +253,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -273,6 +269,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)); @@ -290,4 +366,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..a692b30d8 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -9,6 +9,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" +#include "../mwworld/failedaction.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" @@ -17,15 +18,15 @@ #include "../mwgui/tooltips.hpp" +#include "../mwmechanics/npcstats.hpp" + namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -33,7 +34,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 @@ -60,8 +61,18 @@ namespace MWClass boost::shared_ptr Book::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - return boost::shared_ptr ( - new MWWorld::ActionRead (ptr)); + if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfItem"); + + boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); + if(sound) action->setSound(sound->mId); + + return action; + } + + return boost::shared_ptr(new MWWorld::ActionRead(ptr)); } std::string Book::getScript (const MWWorld::Ptr& ptr) const @@ -147,6 +158,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 +186,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 892ac091c..2dbb7ee6f 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -14,6 +14,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/player.hpp" #include "../mwgui/tooltips.hpp" @@ -26,9 +27,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -36,7 +35,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 @@ -61,16 +60,9 @@ namespace MWClass } boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, - const MWWorld::Ptr& actor) const + 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)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Clothing::getScript (const MWWorld::Ptr& ptr) const @@ -207,6 +199,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -221,6 +215,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)); @@ -238,4 +285,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 a2d75131e..783eabff6 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -13,6 +13,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/actionapply.hpp" #include "../mwworld/actionopen.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/player.hpp" @@ -23,6 +24,8 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" +#include "../mwmechanics/npcstats.hpp" + namespace { struct CustomData : public MWWorld::CustomData @@ -46,7 +49,11 @@ namespace MWClass { std::auto_ptr data (new CustomData); - // \todo add initial container content + MWWorld::LiveCellRef *ref = + ptr.get(); + + data->mContainerStore.fill( + ref->mBase->mInventory, ptr.getCellRef().mOwner, MWBase::Environment::get().getWorld()->getStore()); // store ptr.getRefData().setCustomData (data.release()); @@ -57,9 +64,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -89,6 +94,17 @@ namespace MWClass if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return boost::shared_ptr (new MWWorld::NullAction ()); + if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfContainer"); + + boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); + if(sound) action->setSound(sound->mId); + + return action; + } + const std::string lockedSound = "LockedChest"; const std::string trapActivationSound = "Disarm Trap Fail"; @@ -115,7 +131,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 = ""; @@ -133,7 +149,7 @@ namespace MWClass { // Trap activation goes here std::cout << "Activated trap: " << ptr.getCellRef().mTrap << std::endl; - boost::shared_ptr action(new MWWorld::FailedAction); + boost::shared_ptr action(new MWWorld::ActionApply(actor, ptr.getCellRef().mTrap)); action->setSound(trapActivationSound); ptr.getCellRef().mTrap = ""; return action; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 19f327dcb..20f95ab0e 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -5,14 +5,18 @@ #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 "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/actionopen.hpp" +#include "../mwworld/failedaction.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/physicssystem.hpp" @@ -22,12 +26,15 @@ #include "../mwgui/tooltips.hpp" +#include "../mwmechanics/npcstats.hpp" + namespace { struct CustomData : public MWWorld::CustomData { MWMechanics::CreatureStats mCreatureStats; MWWorld::ContainerStore mContainerStore; + MWMechanics::Movement mMovement; virtual MWWorld::CustomData *clone() const; }; @@ -46,6 +53,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 @@ -63,6 +82,8 @@ namespace MWClass data->mCreatureStats.setLevel(ref->mBase->mData.mLevel); + data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage); + data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight); data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee); @@ -73,6 +94,10 @@ namespace MWClass iter!=ref->mBase->mSpells.mList.end(); ++iter) data->mCreatureStats.getSpells().add (*iter); + // inventory + data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), + MWBase::Environment::get().getWorld()->getStore()); + // store ptr.getRefData().setCustomData (data.release()); } @@ -86,6 +111,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(); @@ -128,13 +158,87 @@ namespace MWClass return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; } + + void Creature::hit(const MWWorld::Ptr& ptr, int type) const + { + } + + void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const + { + // NOTE: 'object' and/or 'attacker' may be empty. + + if(!successful) + { + // TODO: Handle HitAttemptOnMe script function + + // Missed + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f); + return; + } + + if(!object.isEmpty()) + getCreatureStats(ptr).setLastHitObject(MWWorld::Class::get(object).getId(object)); + + if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") + { + const std::string &script = ptr.get()->mBase->mScript; + /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ + if(!script.empty()) + ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); + } + + if(ishealth) + { + if(damage > 0.0f) + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + else + { + MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); + fatigue.setCurrent(fatigue.getCurrent() - damage); + getCreatureStats(ptr).setFatigue(fatigue); + } + } + + void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + { + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); + bool wasDead = crstats.isDead(); + + MWMechanics::DynamicStat stat(crstats.getHealth()); + stat.setCurrent(health); + crstats.setHealth(stat); + + if(!wasDead && crstats.isDead()) + { + // actor was just killed + } + else if(wasDead && !crstats.isDead()) + { + // actor was just resurrected + } + } + + boost::shared_ptr Creature::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) - return boost::shared_ptr (new MWWorld::ActionOpen(ptr, true)); - else - return boost::shared_ptr (new MWWorld::ActionTalk (ptr)); + if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfCreature"); + + boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); + if(sound) action->setSound(sound->mId); + + return action; + } + + if(getCreatureStats(ptr).isDead()) + return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); + return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) @@ -175,6 +279,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 = @@ -191,6 +331,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); @@ -203,9 +349,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; @@ -213,6 +359,49 @@ 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; + } + + std::string Creature::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const + { + const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); + + int type = getSndGenTypeFromName(ptr, name); + if(type >= 0) + { + std::vector sounds; + sounds.reserve(8); + + std::string ptrid = Creature::getId(ptr); + MWWorld::Store::iterator sound = store.begin(); + while(sound != store.end()) + { + if(type == sound->mType && !sound->mCreature.empty() && + Misc::StringUtils::ciEqual(ptrid.substr(0, sound->mCreature.size()), + sound->mCreature)) + sounds.push_back(&*sound); + ++sound; + } + if(!sounds.empty()) + return sounds[(int)(rand()/(RAND_MAX+1.0)*sounds.size())]->mSound; + } + + return ""; + } + MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { @@ -221,4 +410,45 @@ namespace MWClass return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); } + + int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) + { + if(name == "left") + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + if(world->isUnderwater(ptr.getCell(), pos)) + return 2; + if(world->isOnGround(ptr)) + return 0; + return -1; + } + if(name == "right") + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + if(world->isUnderwater(ptr.getCell(), pos)) + return 3; + if(world->isOnGround(ptr)) + return 1; + return -1; + } + if(name == "swimleft") + return 2; + if(name == "swimright") + return 3; + if(name == "moan") + return 4; + if(name == "roar") + return 5; + if(name == "scream") + return 6; + if(name == "land") + return 7; + + throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); + } + + 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 a96c18a8c..0d8694ff8 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -12,6 +12,11 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name); + + static const ESM::GameSetting *fMinWalkSpeedCreature; + static const ESM::GameSetting *fMaxWalkSpeedCreature; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -22,6 +27,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. @@ -35,6 +42,12 @@ namespace MWClass virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; ///< Return creature stats + virtual void hit(const MWWorld::Ptr& ptr, int type) const; + + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation @@ -54,9 +67,30 @@ 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 std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) 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..3a0e4d0ba 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -6,12 +6,15 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" +#include "../mwworld/actionapply.hpp" #include "../mwworld/actionteleport.hpp" +#include "../mwworld/actiondoor.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" @@ -27,9 +30,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -67,16 +68,14 @@ namespace MWClass boost::shared_ptr Door::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = 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"; - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); + MWWorld::ContainerStore &invStore = get(actor).getContainerStore(actor); bool needKey = ptr.getCellRef().mLockLevel>0; bool hasKey = false; @@ -92,13 +91,14 @@ namespace MWClass if (refId == keyId) { hasKey = true; - keyName = MWWorld::Class::get(*it).getName(*it); + keyName = get(*it).getName(*it); } } if (needKey && hasKey) { - MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}", std::vector()); + if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); ptr.getCellRef().mLockLevel = 0; // using a key disarms the trap ptr.getCellRef().mTrap = ""; @@ -111,7 +111,7 @@ namespace MWClass // Trap activation std::cout << "Activated trap: " << ptr.getCellRef().mTrap << std::endl; - boost::shared_ptr action(new MWWorld::FailedAction); + boost::shared_ptr action(new MWWorld::ActionApply(actor, ptr.getCellRef().mTrap)); action->setSound(trapActivationSound); ptr.getCellRef().mTrap = ""; @@ -139,12 +139,25 @@ 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)) + { + MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, + closeSound, 0.5); + float offset = ptr.getRefData().getLocalRotation().rot[2]/ 3.14159265 * 2.0; + action->setSoundOffset(offset); + action->setSound(openSound); + } + else + { + MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, + openSound, 0.5); + float offset = 1.0 - ptr.getRefData().getLocalRotation().rot[2]/ 3.14159265 * 2.0; + //most if not all door have closing bang somewhere in the middle of the sound, + //so we divide offset by two + action->setSoundOffset(offset * 0.5); + action->setSound(closeSound); + } return action; } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bbba45df5..f629cc15d 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -36,9 +36,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -46,7 +44,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 @@ -73,14 +71,7 @@ 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)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const @@ -197,4 +188,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 200f6e2d4..7eefc6167 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -26,19 +26,10 @@ namespace MWClass { void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert (ref->mBase != NULL); - - const std::string &model = ref->mBase->mModel; - - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - - if (!model.empty()) - objects.insertMesh(ptr, "meshes\\" + model, true); - else - objects.insertLight(ptr); + const std::string model = getModel(ptr); + if(!model.empty()) { + renderingInterface.getObjects().insertModel(ptr, model); + } } void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const @@ -50,10 +41,12 @@ 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); + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, + MWBase::SoundManager::Play_TypeSfx, + MWBase::SoundManager::Play_Loop); } std::string Light::getModel(const MWWorld::Ptr &ptr) const @@ -83,20 +76,14 @@ 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 ()); + if(!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr(new MWWorld::NullAction()); - MWWorld::LiveCellRef *ref = - ptr.get(); - - if (!(ref->mBase->mData.mFlags & ESM::Light::Carry)) - return boost::shared_ptr (new MWWorld::FailedAction); - - boost::shared_ptr action(new MWWorld::ActionTake (ptr)); + MWWorld::LiveCellRef *ref = ptr.get(); + if(!(ref->mBase->mData.mFlags&ESM::Light::Carry)) + return boost::shared_ptr(new MWWorld::FailedAction()); - action->setSound(getUpSoundId(ptr)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Light::getScript (const MWWorld::Ptr& ptr) const @@ -203,4 +190,39 @@ 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; + } + + std::pair Light::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if(weapon == invStore.end()) + return std::make_pair(1,""); + + /// \todo the 2h check is repeated many times; put it in a function + if(weapon->getTypeName() == typeid(ESM::Weapon).name() && + (weapon->get()->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,""); + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 640e1705b..79d662763 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -57,6 +57,12 @@ 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; + + std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 7e909437c..5931a0102 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" @@ -26,9 +26,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -36,13 +34,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; @@ -54,8 +52,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; } @@ -63,20 +61,13 @@ 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)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mScript; } @@ -92,8 +83,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; } @@ -102,7 +93,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 @@ -117,24 +108,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()); @@ -142,9 +133,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,9 +162,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 d43a44359..6247191a9 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -15,6 +15,7 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/actionsoulgem.hpp" #include "../mwgui/tooltips.hpp" @@ -23,15 +24,25 @@ #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 { 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -39,7 +50,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 @@ -66,14 +77,7 @@ 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)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const @@ -89,7 +93,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() @@ -101,25 +113,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"); } @@ -150,12 +152,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) + ")"; @@ -171,10 +176,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()) { @@ -195,7 +200,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"; @@ -214,7 +219,8 @@ namespace MWClass MWWorld::LiveCellRef *ref = newRef.getPtr().get(); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); - newPtr.getRefData ().setCount(goldAmount); + newPtr.getRefData ().setCount(1); + newPtr.getCellRef().mGoldValue = goldAmount; } else { MWWorld::LiveCellRef *ref = ptr.get(); @@ -222,4 +228,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 d4e5e5cd6..073d1b1b9 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -13,6 +13,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -21,6 +23,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/actionopen.hpp" +#include "../mwworld/failedaction.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/physicssystem.hpp" @@ -38,7 +41,6 @@ namespace struct CustomData : public MWWorld::CustomData { MWMechanics::NpcStats mNpcStats; - MWMechanics::CreatureStats mCreatureStats; MWMechanics::Movement mMovement; MWWorld::InventoryStore mInventoryStore; @@ -49,6 +51,84 @@ 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) ); + } + + // initial health + int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); + int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); + + int multiplier = 3; + + if (class_->mData.mSpecialization == ESM::Class::Combat) + multiplier += 2; + else if (class_->mData.mSpecialization == ESM::Class::Stealth) + multiplier += 1; + + if (class_->mData.mAttribute[0] == ESM::Attribute::Endurance + || class_->mData.mAttribute[1] == ESM::Attribute::Endurance) + multiplier += 1; + + creatureStats.setHealth(static_cast (0.5 * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1)); + } } namespace MWClass @@ -76,8 +156,7 @@ namespace MWClass fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase"); fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); - // Added in Tribunal/Bloodmoon, may not exist - fWereWolfRunMult = gmst.search("fWereWolfRunMult"); + fWereWolfRunMult = gmst.find("fWereWolfRunMult"); inited = true; } @@ -108,47 +187,54 @@ namespace MWClass for (int i=0; i<27; ++i) data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]); - data->mCreatureStats.getAttribute(0).set (ref->mBase->mNpdt52.mStrength); - data->mCreatureStats.getAttribute(1).set (ref->mBase->mNpdt52.mIntelligence); - data->mCreatureStats.getAttribute(2).set (ref->mBase->mNpdt52.mWillpower); - data->mCreatureStats.getAttribute(3).set (ref->mBase->mNpdt52.mAgility); - data->mCreatureStats.getAttribute(4).set (ref->mBase->mNpdt52.mSpeed); - data->mCreatureStats.getAttribute(5).set (ref->mBase->mNpdt52.mEndurance); - data->mCreatureStats.getAttribute(6).set (ref->mBase->mNpdt52.mPersonality); - data->mCreatureStats.getAttribute(7).set (ref->mBase->mNpdt52.mLuck); - data->mCreatureStats.setHealth (ref->mBase->mNpdt52.mHealth); - data->mCreatureStats.setMagicka (ref->mBase->mNpdt52.mMana); - data->mCreatureStats.setFatigue (ref->mBase->mNpdt52.mFatigue); - - data->mCreatureStats.setLevel(ref->mBase->mNpdt52.mLevel); + data->mNpcStats.getAttribute(0).set (ref->mBase->mNpdt52.mStrength); + data->mNpcStats.getAttribute(1).set (ref->mBase->mNpdt52.mIntelligence); + data->mNpcStats.getAttribute(2).set (ref->mBase->mNpdt52.mWillpower); + data->mNpcStats.getAttribute(3).set (ref->mBase->mNpdt52.mAgility); + data->mNpcStats.getAttribute(4).set (ref->mBase->mNpdt52.mSpeed); + data->mNpcStats.getAttribute(5).set (ref->mBase->mNpdt52.mEndurance); + data->mNpcStats.getAttribute(6).set (ref->mBase->mNpdt52.mPersonality); + data->mNpcStats.getAttribute(7).set (ref->mBase->mNpdt52.mLuck); + data->mNpcStats.setHealth (ref->mBase->mNpdt52.mHealth); + data->mNpcStats.setMagicka (ref->mBase->mNpdt52.mMana); + data->mNpcStats.setFatigue (ref->mBase->mNpdt52.mFatigue); + + data->mNpcStats.setLevel(ref->mBase->mNpdt52.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt52.mDisposition); data->mNpcStats.setReputation(ref->mBase->mNpdt52.mReputation); } else { - 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->mNpcStats.setDynamic (i, 10); - data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel); + data->mNpcStats.setLevel(ref->mBase->mNpdt12.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); + + autoCalculateAttributes(ref->mBase, data->mNpcStats); } - data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); - data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight); - data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee); - data->mCreatureStats.setAiSetting (3, ref->mBase->mAiData.mAlarm); + data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage); + + data->mNpcStats.setAiSetting (0, ref->mBase->mAiData.mHello); + data->mNpcStats.setAiSetting (1, ref->mBase->mAiData.mFight); + data->mNpcStats.setAiSetting (2, ref->mBase->mAiData.mFlee); + data->mNpcStats.setAiSetting (3, ref->mBase->mAiData.mAlarm); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); iter!=ref->mBase->mSpells.mList.end(); ++iter) - data->mCreatureStats.getSpells().add (*iter); + data->mNpcStats.getSpells().add (*iter); + + // inventory + data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), + MWBase::Environment::get().getWorld()->getStore()); // store ptr.getRefData().setCustomData (data.release()); + + getInventoryStore(ptr).autoEquip(ptr); } } @@ -160,6 +246,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)); @@ -171,16 +262,22 @@ namespace MWClass 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 { MWWorld::LiveCellRef *ref = ptr.get(); assert(ref->mBase != NULL); - std::string headID = ref->mBase->mHead; + //std::string headID = ref->mBase->mHead; - int end = headID.find_last_of("head_") - 4; - std::string bodyRaceID = headID.substr(0, end); + //int end = headID.find_last_of("head_") - 4; + //std::string bodyRaceID = headID.substr(0, end); std::string model = "meshes\\base_anim.nif"; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); @@ -193,9 +290,15 @@ namespace MWClass std::string Npc::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + if(getNpcStats(ptr).isWerewolf()) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + return gmst.find("sWerewolfPopup")->getString(); + } + + MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } @@ -203,7 +306,7 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; + return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; } MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const @@ -213,15 +316,276 @@ namespace MWClass return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; } + + void Npc::hit(const MWWorld::Ptr& ptr, int type) const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + + // Get the weapon used (if hand-to-hand, weapon = inv.end()) + MWWorld::InventoryStore &inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr()); + if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) + weapon = MWWorld::Ptr(); + + float dist = 100.0f * (!weapon.isEmpty() ? + weapon.get()->mBase->mData.mReach : + gmst.find("fHandToHandReach")->getFloat()); + // TODO: Use second to work out the hit angle and where to spawn the blood effect + MWWorld::Ptr victim = world->getHitContact(ptr, dist).first; + if(victim.isEmpty()) // Didn't hit anything + return; + + const MWWorld::Class &othercls = MWWorld::Class::get(victim); + if(!othercls.isActor()) // Can't hit non-actors + return; + MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); + if(otherstats.isDead()) // Can't hit dead actors + return; + + if(ptr.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->setEnemy(victim); + + int weapskill = ESM::Skill::HandToHand; + if(!weapon.isEmpty()) + weapskill = get(weapon).getEquipmentSkill(weapon); + + MWMechanics::NpcStats &stats = getNpcStats(ptr); + const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); + float hitchance = stats.getSkill(weapskill).getModified() + + (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); + hitchance *= stats.getFatigueTerm(); + hitchance += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyAttack)).mMagnitude - + mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + hitchance -= otherstats.getEvasion(); + + if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) + { + othercls.onHit(victim, 0.0f, false, weapon, ptr, false); + return; + } + + bool healthdmg; + float damage = 0.0f; + if(!weapon.isEmpty()) + { + const bool weaphashealth = get(weapon).hasItemHealth(weapon); + const unsigned char *attack = NULL; + if(type == MWMechanics::CreatureStats::AT_Chop) + attack = weapon.get()->mBase->mData.mChop; + else if(type == MWMechanics::CreatureStats::AT_Slash) + attack = weapon.get()->mBase->mData.mSlash; + else if(type == MWMechanics::CreatureStats::AT_Thrust) + attack = weapon.get()->mBase->mData.mThrust; + if(attack) + { + damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); + damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + if(weaphashealth) + { + int weapmaxhealth = weapon.get()->mBase->mData.mHealth; + if(weapon.getCellRef().mCharge == -1) + weapon.getCellRef().mCharge = weapmaxhealth; + damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth; + } + if(!othercls.hasDetected(victim, ptr)) + { + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } + weapon.getCellRef().mCharge -= std::min(std::max(1, + (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), + weapon.getCellRef().mCharge); + } + healthdmg = true; + } + else + { + // Note: MCP contains an option to include Strength in hand-to-hand damage + // calculations. Some mods recommend using it, so we may want to include am + // option for it. + float minstrike = gmst.find("fMinHandToHandMult")->getFloat(); + float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat(); + damage = stats.getSkill(weapskill).getModified(); + damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); + if(!othercls.hasDetected(victim, ptr)) + { + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } + + healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f); + if(stats.isWerewolf()) + { + healthdmg = true; + // GLOB instead of GMST because it gets updated during a quest + const MWWorld::Store &glob = world->getStore().get(); + damage *= glob.find("WerewolfClawMult")->mValue.getFloat(); + } + if(healthdmg) + damage *= gmst.find("fHandtoHandHealthPer")->getFloat(); + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(stats.isWerewolf()) + { + const ESM::Sound *sound = world->getStore().get().searchRandom("WolfHit"); + if(sound) + sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f); + } + else + sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); + } + if(ptr.getRefData().getHandle() == "player") + skillUsageSucceeded(ptr, weapskill, 0); + + othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); + } + + void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + + // NOTE: 'object' and/or 'attacker' may be empty. + + if(!successful) + { + // TODO: Handle HitAttemptOnMe script function + + // Missed + sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f); + return; + } + + if(!object.isEmpty()) + getCreatureStats(ptr).setLastHitObject(get(object).getId(object)); + + if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") + { + const std::string &script = ptr.get()->mBase->mScript; + /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ + if(!script.empty()) + ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); + } + + if(damage > 0.0f) + { + // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying + // something, alert the character controller, scripts, etc. + + MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + + if(object.isEmpty()) + { + if(ishealth) + damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f); + } + else if(ishealth) + { + // Hit percentages: + // cuirass = 30% + // shield, helmet, greaves, boots, pauldrons = 10% each + // guantlets = 5% each + static const int hitslots[20] = { + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots, + MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet + }; + int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; + + float damagediff = damage; + damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f); + damagediff -= damage; + + MWWorld::InventoryStore &inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); + MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); + if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) + { + ESM::CellRef &armorref = armor.getCellRef(); + if(armorref.mCharge == -1) + armorref.mCharge = armor.get()->mBase->mData.mHealth; + armorref.mCharge -= std::min(std::max(1, (int)damagediff), + armorref.mCharge); + switch(get(armor).getEquipmentSkill(armor)) + { + case ESM::Skill::LightArmor: + sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::MediumArmor: + sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::HeavyArmor: + sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); + break; + } + } + } + } + + if(ishealth) + { + if(damage > 0.0f) + sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + else + { + MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); + fatigue.setCurrent(fatigue.getCurrent() - damage); + getCreatureStats(ptr).setFatigue(fatigue); + } + } + + void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + { + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); + bool wasDead = crstats.isDead(); + + MWMechanics::DynamicStat stat(crstats.getHealth()); + stat.setCurrent(health); + crstats.setHealth(stat); + + if(!wasDead && crstats.isDead()) + { + // actor was just killed + } + else if(wasDead && !crstats.isDead()) + { + // actor was just resurrected + } + } + + boost::shared_ptr Npc::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) - 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)); + if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfNPC"); + + boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); + if(sound) action->setSound(sound->mId); + + return action; + } + if(getCreatureStats(ptr).isDead()) + return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); + if(get(actor).getStance(actor, MWWorld::Class::Sneak)) + return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing + return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) @@ -326,11 +690,11 @@ namespace MWClass { const MWBase::World *world = MWBase::Environment::get().getWorld(); const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); - const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects(); + const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); - float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified()* + float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* (fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat()); walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed = std::max(0.0f, walkSpeed); @@ -347,7 +711,7 @@ namespace MWClass moveSpeed = 0.0f; else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0) { - float flySpeed = 0.01f*(npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified() + + float flySpeed = 0.01f*(npcdata->mNpcStats.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; @@ -368,7 +732,7 @@ namespace MWClass moveSpeed = runSpeed; else moveSpeed = walkSpeed; - if(getMovementSettings(ptr).mLeftRight != 0 && getMovementSettings(ptr).mForwardBackward == 0) + if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; return moveSpeed; @@ -377,7 +741,7 @@ namespace MWClass float Npc::getJump(const MWWorld::Ptr &ptr) const { const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); - const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects(); + const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + fJumpEncumbranceMultiplier->getFloat() * (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr)); @@ -392,15 +756,15 @@ namespace MWClass 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 += 3.0f * b * fJumpAcroMultiplier->getFloat(); + x += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::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; + x *= npcdata->mNpcStats.getFatigueTerm(); + x -= -627.2f;/*gravity constant*/ + x /= 3.0f; return x; } @@ -414,14 +778,24 @@ namespace MWClass Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const { - Ogre::Vector3 vector; - vector.x = getMovementSettings(ptr).mLeftRight; - vector.y = getMovementSettings(ptr).mForwardBackward; - vector.z = getMovementSettings(ptr).mUpDown; + 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 = @@ -445,16 +819,21 @@ namespace MWClass MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); + bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName; - std::string text; - if (MWBase::Environment::get().getWindowManager()->getFullHelp()) - text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); - info.text = text; + info.caption = getName(ptr); + if(fullHelp && getNpcStats(ptr).isWerewolf()) + { + info.caption += " ("; + info.caption += ref->mBase->mName; + info.caption += ")"; + } + + if(fullHelp) + info.text = MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); return info; } @@ -467,16 +846,19 @@ namespace MWClass float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const { - float weight = getContainerStore (ptr).getWeight(); - - const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - - weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather - - weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden + const MWMechanics::NpcStats &stats = getNpcStats(ptr); - if (weight<0) - weight = 0; + // According to UESP, inventory weight is ignored in werewolf form. Does that include + // feather and burden effects? + float weight = 0.0f; + if(!stats.isWerewolf()) + { + weight = getContainerStore(ptr).getWeight(); + weight -= stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Feather)).mMagnitude; + weight += stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).mMagnitude; + if(weight < 0.0f) + weight = 0.0f; + } return weight; } @@ -505,12 +887,168 @@ namespace MWClass stats.useSkill (skill, *class_, usageType); } + float Npc::getArmorRating (const MWWorld::Ptr& ptr) const + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + + MWMechanics::NpcStats &stats = getNpcStats(ptr); + MWWorld::InventoryStore &invStore = getInventoryStore(ptr); + + int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); + float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); + float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); + int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); + + int ratings[MWWorld::InventoryStore::Slots]; + 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 = stats.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 = stats.getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; + + return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3f + + (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.1f + + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet]) + * 0.05f + + 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; + } + + + std::string Npc::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const + { + if(name == "left") + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + if(world->isSwimming(ptr)) + return "Swim Left"; + if(world->isUnderwater(ptr.getCell(), pos)) + return "FootWaterLeft"; + if(world->isOnGround(ptr)) + { + MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr); + MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); + if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name()) + return "FootBareLeft"; + + switch(Class::get(*boots).getEquipmentSkill(*boots)) + { + case ESM::Skill::LightArmor: + return "FootLightLeft"; + case ESM::Skill::MediumArmor: + return "FootMedLeft"; + case ESM::Skill::HeavyArmor: + return "FootHeavyLeft"; + } + } + return ""; + } + if(name == "right") + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + if(world->isSwimming(ptr)) + return "Swim Right"; + if(world->isUnderwater(ptr.getCell(), pos)) + return "FootWaterRight"; + if(world->isOnGround(ptr)) + { + MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr); + MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); + if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name()) + return "FootBareRight"; + + switch(Class::get(*boots).getEquipmentSkill(*boots)) + { + case ESM::Skill::LightArmor: + return "FootLightRight"; + case ESM::Skill::MediumArmor: + return "FootMedRight"; + case ESM::Skill::HeavyArmor: + return "FootHeavyRight"; + } + } + return ""; + } + if(name == "land") + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + if(world->isUnderwater(ptr.getCell(), pos)) + return "DefaultLandWater"; + if(world->isOnGround(ptr)) + return "Body Fall Medium"; + return ""; + } + if(name == "swimleft") + return "Swim Left"; + if(name == "swimright") + return "Swim Right"; + // TODO: I have no idea what these are supposed to do for NPCs since they use + // voiced dialog for various conditions like health loss and combat taunts. Maybe + // only for biped creatures? + if(name == "moan") + return ""; + if(name == "roar") + return ""; + if(name == "scream") + return ""; + + throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); + } + MWWorld::Ptr Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index f41edb0df..3591d7c68 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -44,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. @@ -66,6 +68,12 @@ namespace MWClass virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; ///< Return inventory store + virtual void hit(const MWWorld::Ptr& ptr, int type) const; + + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation @@ -81,7 +89,7 @@ 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. @@ -96,6 +104,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. @@ -104,12 +115,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. @@ -117,13 +133,22 @@ 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; + + virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual bool - isActor() const { + virtual bool isActor() const { + return true; + } + + virtual bool isNpc() const { return true; } }; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 0ac78a2e4..08683a668 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -28,9 +28,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -38,7 +36,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 @@ -65,15 +63,7 @@ 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)); - - action->setSound (getUpSoundId(ptr)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Potion::getScript (const MWWorld::Ptr& ptr) const @@ -194,4 +184,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 a28be17e7..951265f40 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" @@ -26,9 +26,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -36,7 +34,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 @@ -62,14 +60,7 @@ 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)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Probe::getScript (const MWWorld::Ptr& ptr) const @@ -141,9 +132,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}"); @@ -175,4 +166,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 39a7f65e0..38c15ac92 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" @@ -12,6 +12,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/actionrepair.hpp" #include "../mwgui/tooltips.hpp" @@ -24,9 +25,7 @@ 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -34,7 +33,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 @@ -61,14 +60,7 @@ 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)); - - return action; + return defaultItemActivate(ptr, actor); } std::string Repair::getScript (const MWWorld::Ptr& ptr) const @@ -120,6 +112,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 = @@ -131,9 +136,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}"); @@ -156,4 +161,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/static.cpp b/apps/openmw/mwclass/static.cpp index 026eada7b..bd7deca88 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -15,9 +15,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), true); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index d8c11558c..4cb090328 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -22,13 +22,18 @@ namespace MWClass { + std::string Weapon::getId (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + return ref->mBase->mId; + } + void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { 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); + renderingInterface.getObjects().insertModel(ptr, model); } } @@ -36,7 +41,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 @@ -63,19 +68,15 @@ 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)); - - return action; + return defaultItemActivate(ptr, actor); } 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 @@ -334,15 +335,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"); @@ -361,6 +368,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)); @@ -378,4 +425,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..181c637db 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + virtual std::string getId (const MWWorld::Ptr& ptr) const; + ///< Return ID of \a ptr + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering @@ -65,11 +68,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 5258bb8a0..52493bf76 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -25,6 +25,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -50,12 +51,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(); @@ -67,6 +67,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; @@ -117,6 +125,8 @@ namespace MWDialogue void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { + mLastTopic = ""; + mChoice = -1; mIsInChoice = false; @@ -127,6 +137,9 @@ namespace MWDialogue mActorKnownTopics.clear(); + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + win->startDialogue(actor, MWWorld::Class::get (actor).getName (actor)); + //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI updateTopics(); @@ -145,8 +158,6 @@ namespace MWDialogue { //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)); creatureStats.talkedToPlayer(); @@ -158,10 +169,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; - mLastDialogue = *info; + mLastTopic = Misc::StringUtils::lowerCase(it->mId); break; } } @@ -260,6 +270,7 @@ namespace MWDialogue parseText (info->mResponse); + std::string title; if (dialogue.mType==ESM::Dialogue::Persuasion) { std::string modifiedTopic = "s" + topic; @@ -269,26 +280,23 @@ 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->addTitle (topic); - win->addText ("…"); - + win->addResponse ("…", topic); } } @@ -367,6 +375,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); @@ -395,6 +406,11 @@ 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 @@ -413,51 +429,41 @@ 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], true)) - { - 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)); - MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); - 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); } } - - 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; } @@ -518,12 +524,65 @@ 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; + } + + void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic) const + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!sndMgr->sayDone(actor)) + { + // Actor is already saying something. + return; + } + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Dialogue *dial = store.get().find(topic); + + Filter filter(actor, 0, false); + const ESM::DialInfo *info = filter.search(*dial, false); + if(info != NULL) + { + MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); + if(winMgr->getSubtitlesEnabled()) + winMgr->messageBox(info->mResponse); + sndMgr->say(actor, info->mSound); + } + } + + 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 0c0299619..1b7abed45 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -30,9 +30,7 @@ namespace MWDialogue bool mTalkedTo; int mChoice; - std::map mChoiceMap; std::string mLastTopic; - ESM::DialInfo mLastDialogue; bool mIsInChoice; float mTemporaryDispositionChange; @@ -46,14 +44,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, 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 +65,14 @@ namespace MWDialogue virtual MWWorld::Ptr getActor() const; ///< Return the actor the player is currently talking to. + virtual bool checkServiceRefused (); + + virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const; + //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 10740794e..a7b0f1924 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -73,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) @@ -121,7 +126,7 @@ bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const return true; } -bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const +bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const { bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); @@ -129,14 +134,19 @@ bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const return true; int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); - - return actorDisposition >= info.mData.mDisposition; + // 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()) { @@ -144,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; @@ -190,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)); } @@ -412,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)); + + case SelectWrapper::Function_NotFaction: + + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)!=select.getName(); + + case SelectWrapper::Function_NotClass: + + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)!=select.getName(); + + case SelectWrapper::Function_NotRace: - return select.getName()==Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)!=select.getName(); - case SelectWrapper::Function_Faction: + case SelectWrapper::Function_NotCell: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)!=select.getName(); - case SelectWrapper::Function_Class: + 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; - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)==select.getName(); + const ESM::Script *script = + MWBase::Environment::get().getWorld()->getStore().get().find (scriptName); - case SelectWrapper::Function_Race: + std::string name = select.getName(); - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)==select.getName(); + int i = 0; + for (; i < static_cast (script->mVarNames.size()); ++i) + if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) + break; - case SelectWrapper::Function_Cell: + if (i >= static_cast (script->mVarNames.size())) + return true; // script does not have a variable of this name - return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)==select.getName(); + return false; + } case SelectWrapper::Function_SameGender: @@ -458,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: { @@ -501,9 +538,9 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co return MWWorld::Class::get (mActor).getCreatureStats (mActor).getCreatureTargetted(); - case SelectWrapper::Function_PCWerewolf: + case SelectWrapper::Function_Werewolf: - return MWWorld::Class::get (player).getNpcStats (player).isWerewolf(); + return MWWorld::Class::get (mActor).getNpcStats (mActor).isWerewolf(); default: @@ -537,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, @@ -570,7 +607,7 @@ const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, } std::vector MWDialogue::Filter::list (const ESM::Dialogue& dialogue, - bool fallbackToInfoRefusal, bool searchAll) const + bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const { std::vector infos; @@ -582,7 +619,7 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue { if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) { - if (testDisposition (*iter)) { + if (testDisposition (*iter, invertDisposition)) { infos.push_back(&*iter); if (!searchAll) break; @@ -604,7 +641,7 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue for (std::vector::const_iterator iter = infoRefusalDialogue.mInfo.begin(); iter!=infoRefusalDialogue.mInfo.end(); ++iter) - if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter)) { + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter, invertDisposition)) { infos.push_back(&*iter); if (!searchAll) break; diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 069bf6353..5c6d092ad 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -30,8 +30,8 @@ namespace MWDialogue bool testSelectStructs (const ESM::DialInfo& info) const; ///< Are all select structs matching? - bool testDisposition (const ESM::DialInfo& info) const; - ///< Is the actor disposition toward the player high enough? + 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; @@ -54,7 +54,7 @@ namespace MWDialogue Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); std::vector list (const ESM::Dialogue& dialogue, - bool fallbackToInfoRefusal, bool searchAll) const; + 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. 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 5e2bc6bc0..23cfb5fdd 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -29,6 +29,13 @@ 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... 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 9d705f6be..3f22998f0 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -91,7 +91,7 @@ MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() case 66: return Function_FriendlyHit; case 67: case 68: case 69: case 70: return Function_AiSetting; case 71: return Function_ShouldAttack; - case 72: return Function_PCWerewolf; + case 72: return Function_Werewolf; case 73: return Function_WerewolfKills; } @@ -112,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; @@ -219,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, @@ -227,7 +226,14 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const Function_Alarmed, Function_Detected, Function_Attacked, Function_ShouldAttack, Function_CreatureTargetted, - Function_PCWerewolf, + Function_Werewolf, + Function_None // end marker + }; + + static const Function invertedBooleanFunctions[] = + { + Function_NotId, Function_NotFaction, Function_NotClass, + Function_NotRace, Function_NotCell, Function_NotLocal, Function_None // end marker }; @@ -245,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, @@ -267,7 +270,7 @@ bool MWDialogue::SelectWrapper::isNpcOnly() const Function_PcCrimeLevel, Function_RankRequirement, Function_Reputation, Function_FactionRankDiff, - Function_PCWerewolf, Function_WerewolfKills, + Function_Werewolf, Function_WerewolfKills, Function_RankLow, Function_RankHigh, Function_None // end marker }; @@ -283,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..ef787d8ee 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, @@ -41,7 +42,7 @@ namespace MWDialogue Function_Reputation, Function_Alarmed, Function_FactionRankDiff, Function_Detected, Function_Attacked, Function_ShouldAttack, Function_CreatureTargetted, - Function_PCWerewolf, Function_WerewolfKills, + Function_Werewolf, Function_WerewolfKills, Function_RankLow, Function_RankHigh }; @@ -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/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index fce612600..1e203dcd0 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,14 +26,29 @@ 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) { + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + getWidget(mCreateButton, "CreateButton"); getWidget(mCancelButton, "CancelButton"); getWidget(mIngredients[0], "Ingredient1"); @@ -42,6 +61,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 +74,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 +81,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 +94,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,15 +145,17 @@ 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); - int index = 0; + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy.beginTools()); iter!=mAlchemy.endTools() && index (mApparatus.size()); ++iter, ++index) { @@ -157,8 +176,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 +190,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 +206,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 +230,7 @@ namespace MWGui text->setCaption(getCountString(ingredient->getUserData()->getRefData().getCount())); } - drawItems(); + mItemView->update(); std::vector effects; ESM::EffectList list; @@ -232,7 +246,6 @@ 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); 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 4b07dd698..965606709 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -3,246 +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) - : WindowModal("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::Button* 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::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); + 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::Button* 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() -{ - WindowModal::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::Button* okButton; - getWidget(okButton, "OKButton"); - break; + if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) + { + mBirthList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } } - } - - updateSpells(); -} -// widget controls + updateSpells(); + } -void BirthDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} + // widget controls -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::Button* okButton; - getWidget(okButton, "OKButton"); + 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(); - // sort by name - std::vector < std::pair > birthSigns; + const MWWorld::Store &signs = + MWBase::Environment::get().getWorld()->getStore().get(); - 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); + // sort by name + std::vector < std::pair > birthSigns; - 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()) + MWWorld::Store::iterator it = signs.begin(); + for (; it != signs.end(); ++it) { - mBirthList->setIndexSelected(index); - mCurrentBirthId = it2->first; + birthSigns.push_back(std::make_pair(it->mId, &(*it))); } - else if (boost::iequals(it2->first, mCurrentBirthId)) + std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); + + int index = 0; + for (std::vector >::const_iterator it2 = birthSigns.begin(); + it2 != birthSigns.end(); ++it2, ++index) { - mBirthList->setIndexSelected(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 d3f82dace..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. @@ -13,7 +13,7 @@ namespace MWGui class BirthDialog : public WindowModal { public: - BirthDialog(MWBase::WindowManager& parWindowManager); + BirthDialog(); enum Gender { diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp new file mode 100644 index 000000000..694970e23 --- /dev/null +++ b/apps/openmw/mwgui/bookpage.cpp @@ -0,0 +1,1240 @@ +#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->empty()) + 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; + mViewTop = 0; + mViewBottom = 0; + mFocusItem = NULL; + mItemActive = false; + mNode = NULL; + } + + 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 777751069..efe089689 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -12,148 +12,197 @@ #include "formatting.hpp" -using namespace MWGui; - -BookWindow::BookWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_book.layout", parWindowManager) - , mTakeButtonShow(true) - , mTakeButtonAllowed(true) +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) + , mCurrentPage(0) + { + 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) -{ - mTakeButtonShow = show; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} + void BookWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } -void BookWindow::setInventoryAllowed(bool allowed) -{ - mTakeButtonAllowed = allowed; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} + void BookWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + 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::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); - mWindowManager.removeGuiMode(GM_Book); -} + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } -void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) + { + MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0); - MWWorld::ActionTake take(mBook); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWWorld::ActionTake take(mBook); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - mWindowManager.removeGuiMode(GM_Book); -} + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } -void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) -{ - if ((mCurrentPage+1)*2 < mPages.size()) + void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) { - MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); - - ++mCurrentPage; + nextPage(); + } - updatePages(); + void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) + { + prevPage(); } -} -void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) -{ - if (mCurrentPage > 0) + void BookWindow::updatePages() { - MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); + mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); + mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); - --mCurrentPage; + 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) + { + 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); + } + } - updatePages(); + 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)); } -} + + void BookWindow::nextPage() + { + if ((mCurrentPage+1)*2 < mPages.size()) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); -void BookWindow::updatePages() -{ - mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); - mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); + ++mCurrentPage; - unsigned int i=0; - for (std::vector::iterator it = mPages.begin(); - it != mPages.end(); ++it) + updatePages(); + } + } + void BookWindow::prevPage() { - if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) - (*it)->setVisible(true); - else + if (mCurrentPage > 0) { - (*it)->setVisible(false); + MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); + + --mCurrentPage; + + updatePages(); } - ++i; } + } diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index a509f131f..ef87dd9c4 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,12 @@ namespace MWGui class BookWindow : public WindowBase { public: - BookWindow(MWBase::WindowManager& parWindowManager); + BookWindow(); void open(MWWorld::Ptr book); void setTakeButtonShow(bool show); - + void nextPage(); + void prevPage(); void setInventoryAllowed(bool allowed); protected: @@ -27,6 +28,7 @@ namespace MWGui void updatePages(); void clearPages(); + void adjustButton(MWGui::ImageButton* button); private: MWGui::ImageButton* mCloseButton; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 08d024f5c..816f42e3d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,17 +1,19 @@ #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 "../mwmechanics/creaturestats.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/fallback.hpp" +#include "../mwworld/player.hpp" namespace { @@ -23,14 +25,13 @@ namespace }; const ESM::Class::Specialization mSpecializations[3]={ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}; // The specialization for each answer - Step sGenerateClassSteps(int number) { - MWBase::World *world = MWBase::Environment::get().getWorld(); number++; - Step step = {world->getFallback("Question_"+boost::lexical_cast(number)+"_Question"), - {world->getFallback("Question_"+boost::lexical_cast(number)+"_AnswerOne"), - world->getFallback("Question_"+boost::lexical_cast(number)+"_AnswerTwo"), - world->getFallback("Question_"+boost::lexical_cast(number)+"_AnswerThree")}, + 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; @@ -43,685 +44,714 @@ namespace // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz unsigned int points[3]; }; -} - -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) -{ - mCreationStage = CSE_NotStarted; -} -void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - if (mReviewDialog) + void updatePlayerHealth() { - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player); - for (int i=0; ids[i]; ++i) - { - if (ids[i]==id) - mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); - } + creatureStats.updateHealth(); } } -void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) +namespace MWGui { - if (mReviewDialog) + + CharacterCreation::CharacterCreation() + : mNameDialog(0) + , mRaceDialog(0) + , mClassChoiceDialog(0) + , mGenerateClassQuestionDialog(0) + , mGenerateClassResultDialog(0) + , mPickClassDialog(0) + , mCreateClassDialog(0) + , mBirthSignDialog(0) + , mReviewDialog(0) + , mGenerateClassStep(0) { - if (id == "HBar") - { - mReviewDialog->setHealth (value); - } - else if (id == "MBar") - { - mReviewDialog->setMagicka (value); - } - else if (id == "FBar") - { - mReviewDialog->setFatigue (value); - } + mCreationStage = CSE_NotStarted; + mGenerateClassSpecializations[0] = 0; + mGenerateClassSpecializations[1] = 0; + mGenerateClassSpecializations[2] = 0; } -} - -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::Stat& 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); - if (mCreationStage < CSE_NameChosen) - mCreationStage = CSE_NameChosen; - break; - - case GM_Class: - mWM->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; - mClassChoiceDialog = new ClassChoiceDialog(*mWM); - mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); - mClassChoiceDialog->setVisible(true); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - 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); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - 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); - if (mCreationStage < CSE_ClassChosen) - mCreationStage = CSE_ClassChosen; - 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); - 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: - 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) + { + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + for (int i=0; ids[i]; ++i) { - std::map > attributes = mWM->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); - it != attributes.end(); ++it) - { - mReviewDialog->setAttribute(static_cast (it->first), it->second); - } + if (ids[i]==id) + mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); } + } + } + void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + if (mReviewDialog) + { + if (id == "HBar") { - 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->setHealth (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); - if (mCreationStage < CSE_BirthSignChosen) - mCreationStage = CSE_BirthSignChosen; - break; + else if (id == "MBar") + { + mReviewDialog->setMagicka (value); + } + 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::doRenderUpdate() { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + if (mRaceDialog) + mRaceDialog->doRenderUpdate(); } - else if (mCreationStage >= CSE_ClassChosen) + + void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); + mPlayerHealth = value; } - else + + void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); + mPlayerMagicka = value; } -} -void CharacterCreation::onPickClassDialogBack() -{ - if (mPickClassDialog) + void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) { - const std::string classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - mWM->removeDialog(mPickClassDialog); - mPickClassDialog = 0; + mPlayerFatigue = value; } - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} - -void CharacterCreation::onClassChoice(int _index) -{ - mWM->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; - - mWM->popGuiMode(); - - switch(_index) + void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { - 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; -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; + MWBase::Environment::get().getWindowManager()->popGuiMode(); } - if (mCreationStage == CSE_ReviewNext) + void CharacterCreation::onReviewDialogBack() { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_NameChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Race); + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } - else + + void CharacterCreation::onReviewActivateDialog(int parDialog) { - mCreationStage = CSE_NameChosen; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + mCreationStage = CSE_ReviewNext; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + + 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); + }; } -} -void CharacterCreation::onRaceDialogBack() -{ - if (mRaceDialog) + void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) { - 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); + + 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) + updatePlayerHealth(); + } + + 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(); + } + + updatePlayerHealth(); } - 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(); + } + + updatePlayerHealth(); } - 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); + 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(); + } - mWM->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; + updatePlayerHealth(); } - if (mCreationStage == CSE_ReviewNext) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_ClassChosen) + void CharacterCreation::onCreateClassDialogBack() { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); - } - else - { - 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 = 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 == 10) + 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; + MWBase::Environment::get().getWindowManager()->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; - } + mGenerateClassResultDialog = new GenerateClassResultDialog(); + mGenerateClassResultDialog->setClassId(mGenerateClass); + mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); + mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); + mGenerateClassResultDialog->setVisible(true); + return; + } - if (mGenerateClassStep > 10) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); - return; - } + if (mGenerateClassStep > 10) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + return; + } - mWM->removeDialog(mGenerateClassQuestionDialog); - mGenerateClassQuestionDialog = 0; + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; - mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); + mGenerateClassQuestionDialog = new InfoBoxDialog(); - 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); + 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); - MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); -} + MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); + } -void CharacterCreation::onGenerateClassBack() -{ - 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(); + } + + updatePlayerHealth(); } - else + + CharacterCreation::~CharacterCreation() { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); + 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..bd8826677 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 @@ -45,6 +41,7 @@ namespace MWGui void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); void configureSkills (const SkillList& major, const SkillList& minor); + void doRenderUpdate(); private: //Dialogs @@ -58,8 +55,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 a2f09096a..c33e54d6b 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,879 +11,876 @@ #undef min #undef max -using namespace MWGui; - -/* GenerateClassResultDialog */ - -GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_generate_class_result.layout", parWindowManager) +namespace MWGui { - // Centre dialog - center(); - - setText("ReflectT", mWindowManager.getGameSettingString("sMessageQuestionAnswer1", "")); - getWidget(mClassImage, "ClassImage"); - getWidget(mClassName, "ClassName"); + /* GenerateClassResultDialog */ - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); + GenerateClassResultDialog::GenerateClassResultDialog() + : WindowModal("openmw_chargen_generate_class_result.layout") + { + // Centre dialog + center(); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); -} + setText("ReflectT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sMessageQuestionAnswer1", "")); -std::string GenerateClassResultDialog::getClassId() const -{ - return mClassName->getCaption(); -} + getWidget(mClassImage, "ClassImage"); + getWidget(mClassName, "ClassName"); -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); -} + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); -// widget controls + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); + } -void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); -} + std::string GenerateClassResultDialog::getClassId() const + { + return mClassName->getCaption(); + } -void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + 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); + } -/* PickClassDialog */ + // widget controls -PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_class.layout", parWindowManager) -{ - // Centre dialog - center(); + void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } - getWidget(mSpecializationName, "SpecializationName"); + void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } - getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); - getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); - mFavoriteAttribute[0]->setWindowManager(&mWindowManager); - mFavoriteAttribute[1]->setWindowManager(&mWindowManager); + /* PickClassDialog */ - for(int i = 0; i < 5; i++) + PickClassDialog::PickClassDialog() + : WindowModal("openmw_chargen_class.layout") { - 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); - } + // Centre dialog + center(); - 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(mSpecializationName, "SpecializationName"); - getWidget(mClassImage, "ClassImage"); + getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); + getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); + 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)); + } - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); + 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); - updateClasses(); - updateStats(); -} + getWidget(mClassImage, "ClassImage"); -void PickClassDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); -void PickClassDialog::open() -{ - WindowModal::open (); - updateClasses(); - updateStats(); -} + updateClasses(); + updateStats(); + } + void PickClassDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); -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) + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void PickClassDialog::open() { - if (boost::iequals(*mClassList->getItemDataAt(i), classId)) - { - mClassList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - break; - } + WindowModal::open (); + updateClasses(); + updateStats(); } - updateStats(); -} -// widget controls + 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) + { + if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + { + mClassList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } + } -void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} + updateStats(); + } -void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + // widget controls -void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; + void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); + void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } - const std::string *classId = mClassList->getItemDataAt(_index); - if (boost::iequals(mCurrentClassId, *classId)) - return; + void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; - mCurrentClassId = *classId; - updateStats(); -} + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); -// update widget content + const std::string *classId = mClassList->getItemDataAt(_index); + if (boost::iequals(mCurrentClassId, *classId)) + return; -void PickClassDialog::updateClasses() -{ - mClassList->removeAllItems(); + mCurrentClassId = *classId; + updateStats(); + } - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + // update widget content - int index = 0; - MWWorld::Store::iterator it = store.get().begin(); - for (; it != store.get().end(); ++it) + void PickClassDialog::updateClasses() { - bool playable = (it->mData.mIsPlayable != 0); - if (!playable) // Only display playable classes - continue; + mClassList->removeAllItems(); - 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)) + 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) { - mClassList->setIndexSelected(index); + 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; } - ++index; } -} - -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"); -} -/* InfoBoxDialog */ + 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; -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); -} + ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); -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; - - child->setPosition(child->getLeft(), pos); - width = std::max(width, child->getWidth()); - pos += child->getHeight() + margin; - } - width += margin*2; - widget->setSize(width, pos); -} + 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::InfoBoxDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_infobox.layout", parWindowManager) - , mCurrentButton(-1) -{ - getWidget(mTextBox, "TextBox"); - getWidget(mText, "Text"); - mText->getSubWidgetText()->setWordWrap(true); - getWidget(mButtonBar, "ButtonBar"); + 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()); - center(); -} + 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::setText(const std::string &str) -{ - mText->setCaption(str); - mTextBox->setVisible(!str.empty()); - fitToText(mText); -} + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); + } -std::string InfoBoxDialog::getText() const -{ - return mText->getCaption(); -} + /* InfoBoxDialog */ -void InfoBoxDialog::setButtons(ButtonList &buttons) -{ - for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) + void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) { - MyGUI::Gui::getInstance().destroyWidget(*it); + 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); } - 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) + void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) { - 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); + 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; + + child->setPosition(child->getLeft(), pos); + width = std::max(width, child->getWidth()); + pos += child->getHeight() + margin; + } + width += margin*2; + widget->setSize(width, pos); } -} -void InfoBoxDialog::open() -{ - WindowModal::open(); - // Fix layout - layoutVertically(mTextBox, 4); - layoutVertically(mButtonBar, 6); - layoutVertically(mMainWidget, 4 + 6); + InfoBoxDialog::InfoBoxDialog() + : WindowModal("openmw_infobox.layout") + , mCurrentButton(-1) + { + getWidget(mTextBox, "TextBox"); + getWidget(mText, "Text"); + mText->getSubWidgetText()->setWordWrap(true); + getWidget(mButtonBar, "ButtonBar"); - center(); -} + center(); + } -int InfoBoxDialog::getChosenButton() const -{ - return mCurrentButton; -} + void InfoBoxDialog::setText(const std::string &str) + { + mText->setCaption(str); + mTextBox->setVisible(!str.empty()); + fitToText(mText); + } -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) + std::string InfoBoxDialog::getText() const + { + return mText->getCaption(); + } + + 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) - : WindowModal("openmw_chargen_create_class.layout", parWindowManager) - , mSpecDialog(NULL) - , mAttribDialog(NULL) - , mSkillDialog(NULL) - , mDescDialog(NULL) -{ - // 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) + , mAffectedAttribute(NULL) + , mAffectedSkill(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::Button* descriptionButton; - getWidget(descriptionButton, "DescriptionButton"); - descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); + // Make sure the edit box has focus + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mEditName); - MyGUI::Button* 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::Button* 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::Button* 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::Widget* _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) + { + if (mFavoriteAttribute1->getAttributeId() == id) + mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); + } + else if (mAffectedAttribute == mFavoriteAttribute1) { - (*it)->setSkillId(mAffectedSkill->getSkillId()); - break; + 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::Button* 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::Widget* _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::Button* 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") + , mAttributeId(ESM::Attribute::Strength) + { + // 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::Button* 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::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); - okButton->setCaption(mWindowManager.getGameSettingString("sInputMenu1", "")); + // Make sure the edit box has focus + MWBase::Environment::get().getWindowManager()->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 8c60331d8..15fc89658 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -3,7 +3,7 @@ #include "widgets.hpp" -#include "window_base.hpp" +#include "windowbase.hpp" /* This file contains the dialogs for choosing a class. @@ -15,7 +15,7 @@ namespace MWGui class InfoBoxDialog : public WindowModal { public: - InfoBoxDialog(MWBase::WindowManager& parWindowManager); + InfoBoxDialog(); typedef std::vector ButtonList; @@ -60,13 +60,13 @@ namespace MWGui Class_Create = 2, Class_Back = 3 }; - ClassChoiceDialog(MWBase::WindowManager& parWindowManager); + ClassChoiceDialog(); }; class GenerateClassResultDialog : public WindowModal { public: - GenerateClassResultDialog(MWBase::WindowManager& parWindowManager); + GenerateClassResultDialog(); std::string getClassId() const; void setClassId(const std::string &classId); @@ -93,7 +93,7 @@ namespace MWGui 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() : ""; } @@ -241,7 +241,7 @@ namespace MWGui class CreateClassDialog : public WindowModal { public: - CreateClassDialog(MWBase::WindowManager& parWindowManager); + CreateClassDialog(); virtual ~CreateClassDialog(); std::string getName() const; 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 1aebe57da..a1e3fb738 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -1,16 +1,12 @@ - #include "console.hpp" -#include -#include - #include - -#include "../mwworld/esmstore.hpp" +#include #include "../mwscript/extensions.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWGui { @@ -106,63 +102,58 @@ namespace MWGui } Console::Console(int w, int h, bool consoleOnlyScripts) - : Layout("openmw_console.layout"), + : WindowBase("openmw_console.layout"), mCompilerContext (MWScript::CompilerContext::Type_Console), mConsoleOnlyScripts (consoleOnlyScripts) { setCoord(10,10, w-10, h/2); - getWidget(command, "edit_Command"); - getWidget(history, "list_History"); + getWidget(mCommandLine, "edit_Command"); + getWidget(mHistory, "list_History"); // Set up the command line box - command->eventEditSelectAccept += + mCommandLine->eventEditSelectAccept += newDelegate(this, &Console::acceptCommand); - command->eventKeyButtonPressed += + mCommandLine->eventKeyButtonPressed += newDelegate(this, &Console::keyPress); // Set up the log window - history->setOverflowToTheLeft(true); - history->setEditStatic(true); - history->setVisibleVScroll(true); + mHistory->setOverflowToTheLeft(true); + mHistory->setEditStatic(true); + mHistory->setVisibleVScroll(true); // compiler - MWScript::registerExtensions (mExtensions, mConsoleOnlyScripts); + Compiler::registerExtensions (mExtensions, mConsoleOnlyScripts); mCompilerContext.setExtensions (&mExtensions); } - void Console::enable() + void Console::open() { - setVisible(true); - // Give keyboard focus to the combo box whenever the console is // turned on - MyGUI::InputManager::getInstance().setKeyFocusWidget(command); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } - void Console::disable() + void Console::close() { - setVisible(false); - setSelectedObject(MWWorld::Ptr()); - // Remove keyboard focus from the console input whenever the - // console is turned off - MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); + // Apparently, hidden widgets can retain key focus + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); } void Console::setFont(const std::string &fntName) { - history->setFontName(fntName); - command->setFontName(fntName); + mHistory->setFontName(fntName); + mCommandLine->setFontName(fntName); } void Console::clearHistory() { - history->setCaption(""); + mHistory->setCaption(""); } void Console::print(const std::string &msg) { - history->addText(msg); + mHistory->addText(msg); } void Console::printOK(const std::string &msg) @@ -224,10 +215,10 @@ namespace MWGui { std::vector matches; listNames(); - command->setCaption(complete( command->getCaption(), matches )); + mCommandLine->setCaption(complete( mCommandLine->getCaption(), matches )); #if 0 int i = 0; - for(std::vector::iterator it=matches.begin(); it < matches.end(); it++,i++ ) + for(std::vector::iterator it=matches.begin(); it < matches.end(); ++it,++i ) { printOK( *it ); if( i == 50 ) @@ -236,64 +227,63 @@ namespace MWGui #endif } - if(command_history.empty()) return; + if(mCommandHistory.empty()) return; // Traverse history with up and down arrows if(key == MyGUI::KeyCode::ArrowUp) { // If the user was editing a string, store it for later - if(current == command_history.end()) - editString = command->getCaption(); + if(mCurrent == mCommandHistory.end()) + mEditString = mCommandLine->getCaption(); - if(current != command_history.begin()) + if(mCurrent != mCommandHistory.begin()) { - current--; - command->setCaption(*current); + --mCurrent; + mCommandLine->setCaption(*mCurrent); } } else if(key == MyGUI::KeyCode::ArrowDown) { - if(current != command_history.end()) + if(mCurrent != mCommandHistory.end()) { - current++; + --mCurrent; - if(current != command_history.end()) - command->setCaption(*current); + if(mCurrent != mCommandHistory.end()) + mCommandLine->setCaption(*mCurrent); else // Restore the edit string - command->setCaption(editString); + mCommandLine->setCaption(mEditString); } } } void Console::acceptCommand(MyGUI::EditBox* _sender) { - const std::string &cm = command->getCaption(); + const std::string &cm = mCommandLine->getCaption(); if(cm.empty()) return; // Add the command to the history, and set the current pointer to // the end of the list - command_history.push_back(cm); - current = command_history.end(); - editString.clear(); + mCommandHistory.push_back(cm); + mCurrent = mCommandHistory.end(); + mEditString.clear(); execute (cm); - command->setCaption(""); + mCommandLine->setCaption(""); } 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 +295,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 +307,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 +326,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 +334,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 +357,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,12 +394,25 @@ 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}"); - MyGUI::InputManager::getInstance().setKeyFocusWidget(command); + mPtr = MWWorld::Ptr(); + } + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } void Console::onReferenceUnavailable() diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index b1d961ed2..890176363 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -1,7 +1,6 @@ #ifndef MWGUI_CONSOLE_H #define MWGUI_CONSOLE_H -#include #include #include #include @@ -18,10 +17,11 @@ #include "../mwscript/interpretercontext.hpp" #include "referenceinterface.hpp" +#include "windowbase.hpp" namespace MWGui { - class Console : private OEngine::GUI::Layout, private Compiler::ErrorHandler, public ReferenceInterface + class Console : public WindowBase, private Compiler::ErrorHandler, public ReferenceInterface { private: @@ -55,21 +55,20 @@ namespace MWGui public: - MyGUI::EditBox* command; - MyGUI::EditBox* history; + MyGUI::EditBox* mCommandLine; + MyGUI::EditBox* mHistory; typedef std::list StringList; // History of previous entered commands - StringList command_history; - StringList::iterator current; - std::string editString; + StringList mCommandHistory; + StringList::iterator mCurrent; + std::string mEditString; Console(int w, int h, bool consoleOnlyScripts); - void enable(); - - void disable(); + virtual void open(); + virtual void close(); void setFont(const std::string &fntName); @@ -91,7 +90,7 @@ namespace MWGui void execute (const std::string& command); - void executeFile (const std::string& command); + void executeFile (const std::string& path); private: diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 2b8000312..bc869e5fe 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,717 +7,274 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/inventorystore.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) - { - // 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) + std::string getCountString(const int count) { - 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) - , mDisplayEquippedItems(true) - , mHighlightEquippedItems(true) -{ -} - -void ContainerBase::setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView) -{ - 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) +namespace MWGui { - 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(); -} + // If item is dropped where it was taken from, we don't need to do anything - + // otherwise, do the transfer + if (targetModel != mSourceModel) + { + 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(); - } - else - { - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, false); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); + finish(); + targetView->update(); } - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - drawItems(); -} - -void ContainerBase::startDragItem(MyGUI::Widget* _sender, int count) -{ - mDragAndDrop->mIsOnDragAndDrop = true; - mSelectedItem->detachFromWidget(); - mSelectedItem->attachToWidget(mDragAndDrop->mDragAndDropWidget); - - MWWorld::Ptr object = *mSelectedItem->getUserData(); - _unequipItem(object); - - mDragAndDrop->mDraggedCount = count; - - mDragAndDrop->mDraggedFrom = this; - - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - 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)); - - drawItems(); - - MWBase::Environment::get().getWindowManager()->setDragDrop(true); -} - -void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) -{ - if (mDragAndDrop == NULL) return; - - if(mDragAndDrop->mIsOnDragAndDrop) //drop item here + void DragAndDrop::finish() { - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - if (mDragAndDrop->mDraggedFrom != this) - { - 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()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mFlags & ESM::Container::Organic) - { - // user notification - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage2}", std::vector()); - - return; - } - } - - int origCount = object.getRefData().getCount(); - - // check that we don't exceed the allowed weight (only for containers, not for inventory) - if (!isInventory()) - { - 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); - } - } - else - { - object.getRefData().setCount (mDragAndDrop->mDraggedCount); - containerStore.add(object); - object.getRefData().setCount (origCount - mDragAndDrop->mDraggedCount); - } - } - - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - drawItems(); - mDragAndDrop->mDraggedFrom->drawItems(); + mIsOnDragAndDrop = false; + mSourceSortModel->clearDragItems(); + MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); + mDraggedWidget = 0; 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); } -} -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()) + ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) + : WindowBase("openmw_container_window.layout") + , mDragAndDrop(dragAndDrop) + , mSelectedItem(-1) + , mModel(NULL) + , mSortModel(NULL) { - 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) - { - categories = MWWorld::ContainerStore::Type_Clothing + MWWorld::ContainerStore::Type_Armor - + MWWorld::ContainerStore::Type_Weapon + MWWorld::ContainerStore::Type_Book - + MWWorld::ContainerStore::Type_Potion; - onlyMagic = true; - } - else if (mFilter == Filter_Misc) - { - 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; + getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); + getWidget(mTakeButton, "TakeButton"); + getWidget(mCloseButton, "CloseButton"); - /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them + getWidget(mItemView, "ItemView"); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &ContainerWindow::onBackgroundSelected); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &ContainerWindow::onItemSelected); - std::vector< std::pair > items; - - std::vector equippedItems = getEquippedItems(); - - // 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); + mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); - for (std::vector::iterator it=boughtItems.begin(); - it != boughtItems.end(); ++it) - { - items.push_back( std::make_pair(*it, ItemState_Barter) ); + setCoord(200,0,600,300); } - // 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 (mDisplayEquippedItems && mHighlightEquippedItems) + void ContainerWindow::onItemSelected(int index) { - for (std::vector::const_iterator it=equippedItems.begin(); - it != equippedItems.end(); ++it) + if (mDragAndDrop->mIsOnDragAndDrop) { - items.push_back( std::make_pair(*it, ItemState_Equipped) ); + if (!dynamic_cast(mModel)) + dropItem(); + return; } - } - std::vector ignoreItems = itemsToIgnore(); + const ItemStack& item = mSortModel->getItem(index); - // 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() - || (!mHighlightEquippedItems && mDisplayEquippedItems)) - && std::find(ignoreItems.begin(), ignoreItems.end(), *iter) == ignoreItems.end() - && std::find(mBoughtItems.begin(), mBoughtItems.end(), *iter) == mBoughtItems.end()) - regularItems.push_back(*iter); + 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, &ContainerWindow::dragItem); + } + else + dragItem (NULL, count); } - // sort them and add - std::sort(regularItems.begin(), regularItems.end(), sortItems); - for (std::vector::const_iterator it=regularItems.begin(); it!=regularItems.end(); ++it) + void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { - items.push_back( std::make_pair(*it, ItemState_Normal) ); + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } - for (std::vector< std::pair >::const_iterator it=items.begin(); - it != items.end(); ++it) + void ContainerWindow::dropItem() { - 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 (mPtr.getTypeName() == typeid(ESM::Container).name()) { - 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) + // 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) { - backgroundTex += "_barter"; + MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); + return; } - if (backgroundTex != "") - backgroundTex += ".dds"; - 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) + // check container organic flag + MWWorld::LiveCellRef* ref = mPtr.get(); + if (ref->mBase->mFlags & ESM::Container::Organic) { - x += 42; - y = 0; + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sContentsMessage2}"); + return; } - } - } - - 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); + mDragAndDrop->drop(mModel, mItemView); } -} -void ContainerBase::returnBoughtItems(MWWorld::ContainerStore& store) -{ - for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) + void ContainerWindow::onBackgroundSelected() { - store.add(*it); + if (mDragAndDrop->mIsOnDragAndDrop && !dynamic_cast(mModel)) + dropItem(); } -} - -std::vector ContainerBase::getEquippedItems() -{ - if (mPtr.getTypeName() != typeid(ESM::NPC).name()) - return std::vector(); - - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - std::vector items; - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) + void ContainerWindow::open(const MWWorld::Ptr& container, bool loot) { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); - if (it != invStore.end()) + mPtr = container; + + if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) { - items.push_back(*it); + // we are stealing stuff + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + mModel = new PickpocketItemModel(player, new InventoryItemModel(container)); } - } - - return items; -} - -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(mDisposeCorpseButton, "DisposeCorpseButton"); - getWidget(mTakeButton, "TakeButton"); - getWidget(mCloseButton, "CloseButton"); - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); + else + mModel = new InventoryItemModel(container); - mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); + mDisposeCorpseButton->setVisible(loot); - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ContainerWindow::onWindowResize); + setTitle(MWWorld::Class::get(container).getName(container)); - setCoord(200,0,600,300); -} + mSortModel = new SortFilterItemModel(mModel); -ContainerWindow::~ContainerWindow() -{ -} - -void ContainerWindow::onWindowResize(MyGUI::Window* window) -{ - drawItems(); -} - -void ContainerWindow::open(MWWorld::Ptr container, bool loot) -{ - mDisplayEquippedItems = true; - mHighlightEquippedItems = false; - if (container.getTypeName() == typeid(ESM::NPC).name() && !loot) - { - // we are stealing stuff - mDisplayEquippedItems = false; + mItemView->setModel (mSortModel); } - mDisposeCorpseButton->setVisible(loot); - - openContainer(container); - setTitle(MWWorld::Class::get(container).getName(container)); - drawItems(); -} - -void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); + } } -} -void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) { - // 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); - - if (i==0) + // transfer everything into the player's inventory + ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); + mModel->update(); + for (size_t i=0; igetItemCount(); ++i) { - // 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 (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); + } + + playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount); + mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount); } - ++i; + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } - - containerStore.clear(); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } -} -void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) { - onTakeAllButtonClicked(mTakeButton); + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + { + onTakeAllButtonClicked(mTakeButton); - /// \todo I don't think this is the correct flag to check - if (MWWorld::Class::get(mPtr).isEssential(mPtr)) - mWindowManager.messageBox("#{sDisposeCorpseFail}"); - else - MWBase::Environment::get().getWorld()->deleteObject(mPtr); + if (MWWorld::Class::get(mPtr).isPersistent(mPtr)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); + else + MWBase::Environment::get().getWorld()->deleteObject(mPtr); - mPtr = MWWorld::Ptr(); + mPtr = MWWorld::Ptr(); + } + } + + 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 3c8127b26..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,109 +34,41 @@ 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: - bool mDisplayEquippedItems; - bool mHighlightEquippedItems; - - 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(); - virtual void _unequipItem(MWWorld::Ptr item) { ; } - - virtual bool isTrading() { return false; } - - virtual void onSelectedItemImpl(MWWorld::Ptr item) { ; } - - 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, bool loot=false); + 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); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp new file mode 100644 index 000000000..eff8fbcc1 --- /dev/null +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -0,0 +1,173 @@ +#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]; + if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) + throw std::runtime_error("Item to copy needs to be from a different container!"); + int origCount = item.mBase.getRefData().getCount(); + item.mBase.getRefData().setCount(count); + source.getClass().getContainerStore(source).add(item.mBase, source); + item.mBase.getRefData().setCount(origCount); +} + +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/controllers.cpp b/apps/openmw/mwgui/controllers.cpp new file mode 100644 index 000000000..e62fb3fce --- /dev/null +++ b/apps/openmw/mwgui/controllers.cpp @@ -0,0 +1,54 @@ +#include "controllers.hpp" + +namespace MWGui +{ + namespace Controllers + { + + ControllerRepeatClick::ControllerRepeatClick() : + mInit(0.5), + mStep(0.1), + mEnabled(true), + mTimeLeft(0) + { + } + + ControllerRepeatClick::~ControllerRepeatClick() + { + } + + bool ControllerRepeatClick::addTime(MyGUI::Widget* _widget, float _time) + { + if(mTimeLeft == 0) + mTimeLeft = mInit; + + mTimeLeft -= _time; + while (mTimeLeft <= 0) + { + mTimeLeft += mStep; + eventRepeatClick(_widget, this); + } + return true; + } + + void ControllerRepeatClick::setRepeat(float init, float step) + { + mInit = init; + mStep = step; + } + + void ControllerRepeatClick::setEnabled(bool enable) + { + mEnabled = enable; + } + + void ControllerRepeatClick::setProperty(const std::string& _key, const std::string& _value) + { + } + + void ControllerRepeatClick::prepareItem(MyGUI::Widget* _widget) + { + } + + } +} diff --git a/apps/openmw/mwgui/controllers.hpp b/apps/openmw/mwgui/controllers.hpp new file mode 100644 index 000000000..798acde62 --- /dev/null +++ b/apps/openmw/mwgui/controllers.hpp @@ -0,0 +1,46 @@ +#ifndef MWGUI_CONTROLLERS_H +#define MWGUI_CONTROLLERS_H + +#include +#include + + +namespace MWGui +{ + namespace Controllers + { + class ControllerRepeatClick : + public MyGUI::ControllerItem + { + MYGUI_RTTI_DERIVED( ControllerRepeatClick ) + + public: + ControllerRepeatClick(); + virtual ~ControllerRepeatClick(); + + void setRepeat(float init, float step); + void setEnabled(bool enable); + virtual void setProperty(const std::string& _key, const std::string& _value); + + // Events + typedef MyGUI::delegates::CMultiDelegate2 EventHandle_RepeatClickVoid; + + /** Event : Repeat Click.\n + signature : void method(MyGUI::Widget* _sender, MyGUI::ControllerItem *_controller)\n + */ + EventHandle_RepeatClickVoid eventRepeatClick; + + private: + bool addTime(MyGUI::Widget* _widget, float _time); + void prepareItem(MyGUI::Widget* _widget); + + private: + float mInit; + float mStep; + bool mEnabled; + float mTimeLeft; + }; + } +} + +#endif diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 61c3c358a..02ccbbc05 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -3,12 +3,12 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.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 +21,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,24 +42,39 @@ namespace MWGui width, mMainWidget->getHeight()); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mItemEdit); + // by default, the text edit field has the focus of the keyboard + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); mItemEdit->setCaption(boost::lexical_cast(maxCount)); } - void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) + void CountDialog::cancel() { setVisible(false); } + void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) + { + cancel(); + } + void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); 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..06de3eb88 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -1,15 +1,16 @@ #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); + void cancel(); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; @@ -30,6 +31,7 @@ namespace MWGui 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 index b0d164bed..c069eca15 100644 --- a/apps/openmw/mwgui/cursor.cpp +++ b/apps/openmw/mwgui/cursor.cpp @@ -2,13 +2,11 @@ #include #include -#include #include #include #include - namespace MWGui { diff --git a/apps/openmw/mwgui/cursor.hpp b/apps/openmw/mwgui/cursor.hpp index 3a4a05f4c..badf82262 100644 --- a/apps/openmw/mwgui/cursor.hpp +++ b/apps/openmw/mwgui/cursor.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace MWGui { diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index cdcbfc4d1..c9a780691 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -1,513 +1,615 @@ #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 "../mwbase/soundmanager.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) +namespace { - 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) -{ - 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) + PersuasionDialog::PersuasionDialog() + : WindowModal("openmw_persuasion_dialog.layout") { - mWindowManager.getTradeWindow()->addOrRemoveGold(-10); - type = MWBase::MechanicsManager::PT_Bribe10; + 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 == mBribe100Button) - { - mWindowManager.getTradeWindow()->addOrRemoveGold(-100); - type = MWBase::MechanicsManager::PT_Bribe100; - } - 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::Button* 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 == NULL) - 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); - } + 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; } - else + 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)) { - // 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)); + if (i != match.mBeg) + addTopicLink (typesetter, 0, i - text.begin (), match.mBeg - text.begin ()); + + addTopicLink (typesetter, match.mValue, match.mBeg - text.begin (), match.mEnd - text.begin ()); + + i = match.mEnd; } - } - if(color == "#572D21") - MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); + if (i != text.end ()) + addTopicLink (typesetter, 0, i - text.begin (), text.size ()); + } } -} -void DialogueWindow::onWindowResize(MyGUI::Window* _sender) -{ - mTopicsList->adjustSize(); -} + 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)); -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); -} + 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); -void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); -} + if (topicId) + style = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId); + typesetter->write (style, begin, end); + } -void DialogueWindow::onSelectTopic(const std::string& topic, int id) -{ - if (!mEnabled) return; + Message::Message(const std::string& text) + { + mText = text; + } - int separatorPos = mTopicsList->getItemCount(); - for (unsigned int i=0; igetItemCount(); ++i) + void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { - if (mTopicsList->getItemNameAt(i) == "") - separatorPos = i; + 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())); } - if (id > separatorPos) - MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); - else + // -------------------------------------------------------------------------------------------------- + + void Choice::activated() { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - 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); - } - else if (topic == gmst.find("sSpells")->getString()) - { - mWindowManager.pushGuiMode(GM_SpellBuying); - mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); - } - else if (topic == gmst.find("sTravel")->getString()) - { - mWindowManager.pushGuiMode(GM_Travel); - mWindowManager.getTravelWindow()->startTravel(mPtr); - } - else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) - { - mWindowManager.pushGuiMode(GM_SpellCreation); - mWindowManager.startSpellMaking (mPtr); - } - else if (topic == gmst.find("sEnchanting")->getString()) - { - mWindowManager.pushGuiMode(GM_Enchanting); - mWindowManager.startEnchanting (mPtr); - } - else if (topic == gmst.find("sServiceTrainingTitle")->getString()) - { - mWindowManager.pushGuiMode(GM_Training); - mWindowManager.startTraining (mPtr); - } + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.0, 1.0); + MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); } -} -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 Topic::activated() + { -void DialogueWindow::setKeywords(std::list keyWords) -{ - mTopicsList->clear(); + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); + MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId)); + } + + void Goodbye::activated() + { - bool anyService = mServices > 0; + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + } - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + // -------------------------------------------------------------------------------------------------- - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addItem(gmst.find("sPersuasion")->getString()); + DialogueWindow::DialogueWindow() + : WindowBase("openmw_dialogue_window.layout") + , mPersuasionDialog() + , mEnabled(false) + , mServices(0) + , mGoodbye(false) + { + // Centre dialog + center(); - if (mServices & Service_Trade) - mTopicsList->addItem(gmst.find("sBarter")->getString()); + mPersuasionDialog.setVisible(false); - if (mServices & Service_BuySpells) - mTopicsList->addItem(gmst.find("sSpells")->getString()); + //History view + getWidget(mHistory, "History"); - if (mServices & Service_Travel) - mTopicsList->addItem(gmst.find("sTravel")->getString()); + //Topics list + getWidget(mTopicsList, "TopicsList"); + mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); - if (mServices & Service_CreateSpells) - mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); + MyGUI::Button* byeButton; + getWidget(byeButton, "ByeButton"); + byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); -// if (mServices & Service_Enchant) -// mTopicsList->addItem(gmst.find("sEnchanting")->getString()); + getWidget(mDispositionBar, "Disposition"); + getWidget(mDispositionText,"DispositionText"); + getWidget(mScrollBar, "VScroll"); - if (mServices & Service_Training) - mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); + mScrollBar->eventScrollChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onScrollbarMoved); + mHistory->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); - if (anyService || mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addSeparator(); + BookPage::ClickCallback callback = boost::bind (&DialogueWindow::notifyLinkClicked, this, _1); + mHistory->adviseLinkClicked(callback); - for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) + static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); + } + + void DialogueWindow::onWindowResize(MyGUI::Window* _sender) { - mTopicsList->addItem(*it); + mTopicsList->adjustSize(); + updateHistory(); } - mTopicsList->adjustSize(); -} -void DialogueWindow::removeKeyword(std::string keyWord) -{ - if(mTopicsList->hasItem(keyWord)) + void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - mTopicsList->removeItem(keyWord); + 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 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::onByeClicked(MyGUI::Widget* _sender) { - // do not add color if this portion of text is already colored. + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + } + + void DialogueWindow::onSelectTopic(const std::string& topic, int id) + { + 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() != NULL) + 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()) + { + 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); + } + + BookTypesetter::Ptr typesetter = BookTypesetter::create (mHistory->getWidth(), std::numeric_limits().max()); + + for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) + (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); + + + BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White); + + typesetter->sectionBreak(9); + // 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::reverse_iterator it = mChoices.rbegin(); it != mChoices.rend(); ++it) { - size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText); - std::string standardForm = hypertext[i].mText; - for(; asterisk_count > 0; --asterisk_count) - standardForm.append("*"); + Choice* link = new Choice(it->second); + mLinks.push_back(link); - standardForm = - MWBase::Environment::get().getWindowManager()-> - getTranslationDataStorage().topicStandardForm(standardForm); + 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( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() ) - { - result.append("#686EBA").append(hypertext[i].mText).append("#B29154"); + 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())); + } - mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length(); - mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm); - } - else - result += hypertext[i].mText; + 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(mMainWidget->getVisible() && 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 a8e0a6d17..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(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::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/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp new file mode 100644 index 000000000..7a6317c16 --- /dev/null +++ b/apps/openmw/mwgui/fontloader.cpp @@ -0,0 +1,291 @@ +#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; + } + + std::string getUtf8 (unsigned char c, ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding) + { + if (encoding == ToUTF8::WINDOWS_1250) + { + unsigned char win1250; + std::map conv; + conv[0x80] = 0xc6; + conv[0x81] = 0x9c; + conv[0x82] = 0xe6; + conv[0x83] = 0xb3; + conv[0x84] = 0xf1; + conv[0x85] = 0xb9; + conv[0x86] = 0xbf; + conv[0x87] = 0x9f; + conv[0x88] = 0xea; + conv[0x89] = 0xea; + conv[0x8a] = 0x0; // not contained in win1250 + conv[0x8b] = 0x0; // not contained in win1250 + conv[0x8c] = 0x8f; + conv[0x8d] = 0xaf; + conv[0x8e] = 0xa5; + conv[0x8f] = 0x8c; + conv[0x90] = 0xca; + conv[0x93] = 0xa3; + conv[0x94] = 0xf6; + conv[0x95] = 0xf3; + conv[0x96] = 0xaf; + conv[0x97] = 0x8f; + conv[0x99] = 0xd3; + conv[0x9a] = 0xd1; + conv[0x9c] = 0x0; // not contained in win1250 + conv[0xa0] = 0xb9; + conv[0xa1] = 0xaf; + conv[0xa2] = 0xf3; + conv[0xa3] = 0xbf; + conv[0xa4] = 0x0; // not contained in win1250 + conv[0xe1] = 0x8c; + conv[0xe1] = 0x8c; + conv[0xe3] = 0x0; // not contained in win1250 + conv[0xf5] = 0x0; // not contained in win1250 + + if (conv.find(c) != conv.end()) + win1250 = conv[c]; + else + win1250 = c; + return encoder.getUtf8(std::string(1, win1250)); + } + else + return encoder.getUtf8(std::string(1, c)); + } + +} + +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(getUtf8(i, encoder, mEncoding)); + + 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 7f28e9e17..bd75c078c 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -3,15 +3,15 @@ #include #include "../mwscript/interpretercontext.hpp" -#include "../mwworld/ptr.hpp" #include #include +#include #include +#include +#include #include -using namespace MWGui; - namespace { int convertFromHex(std::string hex) @@ -77,289 +77,331 @@ namespace 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 utf8Text, const int width, const int height) +namespace MWGui { - 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); + std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) + { + using Ogre::UTFString; + std::vector result; - boost::algorithm::replace_all(utf8Text, "\n", ""); - boost::algorithm::replace_all(utf8Text, "
", "\n"); - boost::algorithm::replace_all(utf8Text, "

", "\n\n"); + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); - UTFString text(utf8Text); - const int spacing = 48; + 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 UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); - const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); - const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); + UTFString text(utf8Text); + const int spacing = 48; - while (!text.empty()) - { - // read in characters until we have exceeded the size, or run out of text - int currentWidth = 0; - int currentHeight = 0; + const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); + const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); + const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); - size_t currentWordStart = 0; - size_t index = 0; - while (currentHeight <= height - spacing && index < text.size()) + while (!text.empty()) { - const UTFString::unicode_char ch = text.getChar(index); - if (ch == LEFT_ANGLE) + // read in characters until we have exceeded the size, or run out of text + int currentWidth = 0; + int currentHeight = 0; + + size_t currentWordStart = 0; + size_t index = 0; + + { + std::string texToTrim = text.asUTF8(); + boost::algorithm::trim( texToTrim ); + text = UTFString(texToTrim); + } + + + while (currentHeight <= height - spacing && index < text.size()) { - 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(); + const UTFString::unicode_char ch = text.getChar(index); + if (ch == LEFT_ANGLE) + { + 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")) + 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 (ch == NEWLINE) { - const int h = mHeight; - parseImage(tag, false); - currentHeight += (mHeight - h); + currentHeight += currentFontHeight(); currentWidth = 0; + currentWordStart = index; } - else if (boost::algorithm::starts_with(tag, "FONT")) + else if (ch == SPACE) { - parseFont(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } - currentWidth = 0; + currentWidth += 3; // keep this in sync with the font's SpaceWidth property + currentWordStart = index; } - else if (boost::algorithm::starts_with(tag, "DIV")) + else { - parseDiv(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } + currentWidth += widthForCharGlyph(ch); } - index = tagEnd; - } - else if (ch == NEWLINE) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - currentWordStart = index; - } - else if (ch == SPACE) - { - currentWidth += 3; // keep this in sync with the font's SpaceWidth property - currentWordStart = index; - } - else - { - currentWidth += widthForCharGlyph(ch); - } - if (currentWidth > width) - { - 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()); + if (currentWidth > width) + { + 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); } - index += UTFString::_utf16_char_length(ch); + const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) + ? currentWordStart : index; + + result.push_back(text.substr(0, pageEnd).asUTF8()); + text.erase(0, pageEnd); } - const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) - ? currentWordStart : index; + + std::vector nonEmptyPages; + boost::copy(result | boost::adaptors::filtered(is_not_empty), std::back_inserter(nonEmptyPages)); + return nonEmptyPages; + } - result.push_back(text.substr(0, pageEnd).asUTF8()); - text.erase(0, pageEnd); + 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; } - return result; -} + float BookTextParser::currentFontHeight() const + { + std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); + } -float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const -{ - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName) - ->getGlyphInfo(unicodeChar)->width; -} + 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); -float BookTextParser::currentFontHeight() const -{ - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); -} + mParent = parent; + mWidth = width; + mHeight = 0; -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); + 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"); - 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"); + boost::algorithm::replace_all(text, "

", "\n\n"); + 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) { - const size_t tagStart = 1; - const size_t tagEnd = text.find('>', 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, "DOV")) - parseDiv(tag); - - text.erase(0, tagEnd + 1); + 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); + + 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 (face != "Magic Cards") + mTextStyle.mFont = face; + } + if (tag.find("SIZE=") != std::string::npos) + { + /// \todo + } } - size_t tagStart = std::string::npos; - std::string realText; // real text, without tags - for (size_t i = 0; 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 - { - tagStart = i; - 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 (tagStart != std::string::npos) - { - parseSubText(text.substr(tagStart, 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 ab1ee3af4..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 diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 0a31a428b..edcd49738 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -1,548 +1,626 @@ #include "hud.hpp" -#include - -#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 "../mwworld/class.hpp" -#include "../mwgui/widgets.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.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) - , 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(); + HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) + : Layout("openmw_hud.layout") + , mHealth(NULL) + , mMagicka(NULL) + , mStamina(NULL) + , mDrowning(NULL) + , mDrowningFrame(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) + , mEnemyHealthTimer(0) + { + setCoord(0,0, width, height); + + // Energy bars + getWidget(mHealthFrame, "HealthFrame"); + getWidget(mHealth, "Health"); + getWidget(mMagicka, "Magicka"); + getWidget(mStamina, "Stamina"); + getWidget(mEnemyHealth, "EnemyHealth"); + 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); + + //Drowning bar + getWidget(mDrowningFrame, "DrowningFrame"); + getWidget(mDrowning, "Drowning"); + mDrowning->setProgressRange(200); + + 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(mSneakBox, "SneakBox"); + mSneakBoxBaseLeft = mSneakBox->getLeft(); + + 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(); + } - 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); + HUD::~HUD() + { + delete mSpellIcons; + } - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + void HUD::setFpsLevel(int level) + { + mFpsCounter = 0; - // 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); + MyGUI::Widget* fps; + getWidget(fps, "FPSBoxAdv"); + fps->setVisible(false); + getWidget(fps, "FPSBox"); + fps->setVisible(false); - getWidget(mSpellBox, "SpellBox"); - getWidget(mSpellImage, "SpellImage"); - getWidget(mSpellStatus, "SpellStatus"); - mSpellBoxBaseLeft = mSpellBox->getLeft(); - mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); + 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"); + } + } - getWidget(mEffectBox, "EffectBox"); - mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); + void HUD::setFPS(float fps) + { + if (mFpsCounter) + mFpsCounter->setCaption(boost::lexical_cast((int)fps)); + } - getWidget(mMinimapBox, "MiniMapBox"); - mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); - getWidget(mMinimap, "MiniMap"); - getWidget(mCompass, "Compass"); - getWidget(mMinimapButton, "MiniMapButton"); - mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); + void HUD::setTriangleCount(unsigned int count) + { + mTriangleCounter->setCaption(boost::lexical_cast(count)); + } - getWidget(mCellNameBox, "CellName"); - getWidget(mWeaponSpellBox, "WeaponSpellName"); + void HUD::setBatchCount(unsigned int count) + { + mBatchCounter->setCaption(boost::lexical_cast(count)); + } - getWidget(mCrosshair, "Crosshair"); + void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = + { + "HBar", "MBar", "FBar", 0 + }; - setFpsLevel(fpsLevel); + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + switch (i) + { + case 0: + mHealth->setProgressRange (value.getModified()); + mHealth->setProgressPosition (value.getCurrent()); + getWidget(w, "HealthFrame"); + w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + break; + case 1: + mMagicka->setProgressRange (value.getModified()); + mMagicka->setProgressPosition (value.getCurrent()); + getWidget(w, "MagickaFrame"); + w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + break; + case 2: + mStamina->setProgressRange (value.getModified()); + mStamina->setProgressPosition (value.getCurrent()); + getWidget(w, "FatigueFrame"); + w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + break; + } + } + } - getWidget(mTriangleCounter, "TriangleCounter"); - getWidget(mBatchCounter, "BatchCounter"); + void HUD::setDrowningTimeLeft(float time) + { + mDrowning->setProgressPosition(time/20.0*200.0); + } - LocalMapBase::init(mMinimap, mCompass, this); + void HUD::setDrowningBarVisible(bool visible) + { + mDrowningFrame->setVisible(visible); + } - mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); - mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); - mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); + void HUD::onWorldClicked(MyGUI::Widget* _sender) + { + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) + return; - mSpellIcons = new SpellIcons(); -} + if (mDragAndDrop->mIsOnDragAndDrop) + { + // drop item into the gameworld + MWWorld::Ptr object = mDragAndDrop->mItem.mBase; -HUD::~HUD() -{ - delete mSpellIcons; -} + MWBase::World* world = MWBase::Environment::get().getWorld(); -void HUD::setFpsLevel(int level) -{ - mFpsCounter = 0; + 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::Widget* fps; - getWidget(fps, "FPSBoxAdv"); - fps->setVisible(false); - getWidget(fps, "FPSBox"); - fps->setVisible(false); + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(mDragAndDrop->mDraggedCount); - 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"); - } -} + if (world->canPlaceObject(mouseX, mouseY)) + world->placeObject(object, mouseX, mouseY); + else + world->dropObjectOnGround(world->getPlayer().getPlayer(), object); -void HUD::setFPS(float fps) -{ - if (mFpsCounter) - mFpsCounter->setCaption(boost::lexical_cast((int)fps)); -} + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); -void HUD::setTriangleCount(unsigned int count) -{ - mTriangleCounter->setCaption(boost::lexical_cast(count)); -} + std::string sound = MWWorld::Class::get(object).getDownSoundId(object); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); -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[] = - { - "HBar", "MBar", "FBar", 0 - }; + object.getRefData().setCount(origCount); - for (int i=0; ids[i]; ++i) - if (ids[i]==id) + // remove object from the container it was coming from + mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); + mDragAndDrop->finish(); + mDragAndDrop->mSourceModel->update(); + } + else { - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - switch (i) + GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + + if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) + return; + + 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)) { - 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; + // pick up object + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); } } -} - -void HUD::onWorldClicked(MyGUI::Widget* _sender) -{ - if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) - return; + } - if (mDragAndDrop->mIsOnDragAndDrop) + void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) { - // drop item into the gameworld - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); + 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); - MWBase::World* world = MWBase::Environment::get().getWorld(); + MWBase::World* world = MWBase::Environment::get().getWorld(); - 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 we can't drop the object at the wanted position, show the "drop on ground" cursor. + bool canDrop = world->canPlaceObject(mouseX, mouseY); - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(mDragAndDrop->mDraggedCount); + if (!canDrop) + MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); + else + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY); + } else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object); + { + 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; + } - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - // remove object from the container it was coming from - object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); - - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget = 0; - - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); + void HUD::onHMSClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); } - else + + void HUD::onMapClicked(MyGUI::Widget* _sender) { - GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); + } - if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) + void HUD::onWeaponClicked(MyGUI::Widget* _sender) + { + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); return; + } - MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); + } - if (mode == GM_Console) - MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); - else if ((mode == GM_Container) || (mode == GM_Inventory)) + void HUD::onMagicClicked(MyGUI::Widget* _sender) + { + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) { - // pick up object - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + return; } + + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); } -} -void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) -{ - if (mDragAndDrop->mIsOnDragAndDrop) + void HUD::setCellName(const std::string& cellName) { - 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); - - 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 (mCellName != cellName) + { + mCellNameTimer = 5.0f; + mCellName = cellName; - if (!canDrop) - MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); - else - MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); + mCellNameBox->setVisible(mMapVisible); + } + } + void HUD::onFrame(float dt) + { + mCellNameTimer -= dt; + mWeaponSpellTimer -= dt; + if (mCellNameTimer < 0) + mCellNameBox->setVisible(false); + if (mWeaponSpellTimer < 0) + mWeaponSpellBox->setVisible(false); + + mEnemyHealthTimer -= dt; + if (mEnemyHealth->getVisible() && mEnemyHealthTimer < 0) + { + mEnemyHealth->setVisible(false); + mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20)); + } } - else + + void HUD::onResChange(int width, int height) { - MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - mWorldMouseOver = true; + setCoord(0, 0, width, height); } -} -void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) -{ - MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - mWorldMouseOver = false; -} + void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) + { + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); -void HUD::onHMSClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); -} + std::string spellName = spell->mName; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } -void HUD::onMapClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); -} + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(successChancePercent); -void HUD::onWeaponClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); -} + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); -void HUD::onMagicClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); -} + mSpellBox->setUserString("ToolTipType", "Spell"); + mSpellBox->setUserString("Spell", spellId); -void HUD::setCellName(const std::string& cellName) -{ - if (mCellName != cellName) - { - mCellNameTimer = 5.0f; - mCellName = cellName; + // use the icon of the first effect + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); - mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); - mCellNameBox->setVisible(mMapVisible); + 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); } -} - -void HUD::onFrame(float dt) -{ - mCellNameTimer -= dt; - mWeaponSpellTimer -= dt; - if (mCellNameTimer < 0) - mCellNameBox->setVisible(false); - if (mWeaponSpellTimer < 0) - mWeaponSpellBox->setVisible(false); -} -void HUD::onResChange(int width, int height) -{ - setCoord(0, 0, width, height); -} - -void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - - std::string spellName = spell->mName; - if (spellName != mSpellName && mSpellVisible) + void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) { - mWeaponSpellTimer = 5.0f; - mSpellName = spellName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); - } + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = itemName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(successChancePercent); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(chargePercent); - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - mSpellBox->setUserString("ToolTipType", "Spell"); - mSpellBox->setUserString("Spell", spellId); + mSpellBox->setUserString("ToolTipType", "ItemPtr"); + mSpellBox->setUserData(item); - // use the icon of the first effect - const ESM::MagicEffect* effect = - MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); + mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + , MyGUI::Align::Stretch); - 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); -} + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); + itemBox->setImageTexture(path); + itemBox->setNeedMouseFocus(false); + } -void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mSpellName && mSpellVisible) + void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) { - mWeaponSpellTimer = 5.0f; - mSpellName = itemName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); - } + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaption(mWeaponName); + mWeaponSpellBox->setVisible(true); + } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(chargePercent); + mWeapBox->setUserString("ToolTipType", "ItemPtr"); + mWeapBox->setUserData(item); - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(durabilityPercent); - mSpellBox->setUserString("ToolTipType", "ItemPtr"); - mSpellBox->setUserData(item); + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); - mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) - , MyGUI::Align::Stretch); + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); - itemBox->setImageTexture(path); - itemBox->setNeedMouseFocus(false); -} + 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); + } -void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mWeaponName && mWeaponVisible) + void HUD::unsetSelectedSpell() { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaption(mWeaponName); - mWeaponSpellBox->setVisible(true); + std::string spellName = "#{sNone}"; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaptionWithReplacing(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(0); + mSpellImage->setImageTexture(""); + mSpellBox->clearUserStrings(); } - mWeapBox->setUserString("ToolTipType", "ItemPtr"); - mWeapBox->setUserData(item); + void HUD::unsetSelectedWeapon() + { + std::string itemName = "#{sSkillHandtohand}"; + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); + mWeaponSpellBox->setVisible(true); + } - mWeapStatus->setProgressRange(100); - mWeapStatus->setProgressPosition(durabilityPercent); + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(0); - if (mWeapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) + mWeapImage->setImageTexture("icons\\k\\tx_werewolf_hand.dds"); + else + mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); + 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::setWeapVisible(bool visible) + { + mWeapBox->setVisible(visible); + updatePositions(); + } -void HUD::unsetSelectedWeapon() -{ - std::string itemName = "#{sSkillHandtohand}"; - if (itemName != mWeaponName && mWeaponVisible) + void HUD::setSpellVisible(bool visible) { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); - mWeaponSpellBox->setVisible(true); + mSpellBox->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::setSneakVisible(bool visible) + { + mSneakBox->setVisible(visible); + updatePositions(); + } -void HUD::setCrosshairVisible(bool visible) -{ - mCrosshair->setVisible (visible); -} + void HUD::setEffectVisible(bool visible) + { + mEffectBox->setVisible (visible); + updatePositions(); + } -void HUD::setHmsVisible(bool visible) -{ - mHealth->setVisible(visible); - mMagicka->setVisible(visible); - mStamina->setVisible(visible); - updatePositions(); -} + void HUD::setMinimapVisible(bool visible) + { + mMinimapBox->setVisible (visible); + updatePositions(); + } -void HUD::setWeapVisible(bool visible) -{ - mWeapBox->setVisible(visible); - updatePositions(); -} + void HUD::updatePositions() + { + int weapDx = 0, spellDx = 0, sneakDx = 0; + if (!mHealth->getVisible()) + sneakDx = spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; -void HUD::setSpellVisible(bool visible) -{ - mSpellBox->setVisible(visible); - updatePositions(); -} + if (!mWeapBox->getVisible()) + { + spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; + sneakDx = spellDx; + } -void HUD::setEffectVisible(bool visible) -{ - mEffectBox->setVisible (visible); - updatePositions(); -} + if (!mSpellBox->getVisible()) + sneakDx += mSneakBoxBaseLeft - mSpellBoxBaseLeft; -void HUD::setMinimapVisible(bool visible) -{ - mMinimapBox->setVisible (visible); - updatePositions(); -} + mWeaponVisible = mWeapBox->getVisible(); + mSpellVisible = mSpellBox->getVisible(); + if (!mWeaponVisible && !mSpellVisible) + mWeaponSpellBox->setVisible(false); -void HUD::updatePositions() -{ - int weapDx = 0, spellDx = 0; - if (!mHealth->getVisible()) - spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; + mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); + mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + mSneakBox->setPosition(mSneakBoxBaseLeft - sneakDx, mSneakBox->getTop()); - if (!mWeapBox->getVisible()) - spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - mWeaponVisible = mWeapBox->getVisible(); - mSpellVisible = mSpellBox->getVisible(); - if (!mWeaponVisible && !mSpellVisible) - mWeaponSpellBox->setVisible(false); + // effect box can have variable width -> variable left coordinate + int effectsDx = 0; + if (!mMinimapBox->getVisible ()) + effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); - mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); - mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + mMapVisible = mMinimapBox->getVisible (); + mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); + } - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + void HUD::update() + { + mSpellIcons->updateWidgets(mEffectBox, true); - // effect box can have variable width -> variable left coordinate - int effectsDx = 0; - if (!mMinimapBox->getVisible ()) - effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); + if (!mEnemy.isEmpty() && mEnemyHealth->getVisible()) + { + MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); + mEnemyHealth->setProgressRange(100); + mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); + } + } - mMapVisible = mMinimapBox->getVisible (); - mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); -} + void HUD::setEnemy(const MWWorld::Ptr &enemy) + { + mEnemy = enemy; + mEnemyHealthTimer = 5; + if (!mEnemyHealth->getVisible()) + mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); + mEnemyHealth->setVisible(true); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); + mEnemyHealth->setProgressRange(100); + mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); + } -void HUD::update() -{ - mSpellIcons->updateWidgets(mEffectBox, true); } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index aab9e62a4..c40742a60 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" @@ -20,9 +18,15 @@ namespace MWGui void setTriangleCount(unsigned int count); void setBatchCount(unsigned int count); + /// Set time left for the player to start drowning + /// @param time value from [0,20] + void setDrowningTimeLeft(float time); + void setDrowningBarVisible(bool visible); + void setHmsVisible(bool visible); void setWeapVisible(bool visible); void setSpellVisible(bool visible); + void setSneakVisible(bool visible); void setEffectVisible(bool visible); void setMinimapVisible(bool visible); @@ -48,12 +52,14 @@ namespace MWGui void update(); + void setEnemy(const MWWorld::Ptr& enemy); + private: - MyGUI::ProgressPtr mHealth, mMagicka, mStamina; + MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::Widget* mHealthFrame; - MyGUI::Widget *mWeapBox, *mSpellBox; + MyGUI::Widget *mWeapBox, *mSpellBox, *mSneakBox; MyGUI::ImageBox *mWeapImage, *mSpellImage; - MyGUI::ProgressPtr mWeapStatus, mSpellStatus; + MyGUI::ProgressBar *mWeapStatus, *mSpellStatus; MyGUI::Widget *mEffectBox, *mMinimapBox; MyGUI::Button* mMinimapButton; MyGUI::ScrollView* mMinimap; @@ -61,6 +67,7 @@ namespace MWGui MyGUI::ImageBox* mCrosshair; MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; + MyGUI::Widget* mDrowningFrame; MyGUI::Widget* mDummy; @@ -70,7 +77,7 @@ namespace MWGui MyGUI::TextBox* mBatchCounter; // bottom left elements - int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft; + int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft; // bottom right elements int mMinimapBoxBaseRight, mEffectBoxBaseRight; @@ -91,6 +98,9 @@ namespace MWGui SpellIcons* mSpellIcons; + MWWorld::Ptr mEnemy; + float mEnemyHealthTimer; + 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/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp new file mode 100644 index 000000000..62a5a75f0 --- /dev/null +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -0,0 +1,103 @@ +#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) +{ + if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) + throw std::runtime_error("Item to copy needs to be from a different container!"); + int origCount = item.mBase.getRefData().getCount(); + item.mBase.getRefData().setCount(count); + mActor.getClass().getContainerStore(mActor).add(item.mBase, mActor); + item.mBase.getRefData().setCount(origCount); +} + + +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) + { + MWWorld::Ptr item = *it; + // NOTE: Don't show WerewolfRobe objects in the inventory, or allow them to be taken. + // Vanilla likely uses a hack like this since there's no other way to prevent it from + // being shown or taken. + if(item.getCellRef().mRefID == "WerewolfRobe") + continue; + + ItemStack newItem (item, this, item.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 ab7615c0e..4f616b312 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1,48 +1,53 @@ #include "inventorywindow.hpp" -#include -#include -#include -#include +#include #include -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #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) + , mSelectedItem(-1) + , mPositionInventory(0, 342, 498, 258) + , mPositionContainer(0, 342, 498, 258) + , mPositionCompanion(0, 342, 498, 258) + , mPositionBarter(0, 342, 498, 258) + , mGuiMode(GM_Inventory) { 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"); @@ -50,14 +55,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); @@ -67,24 +73,163 @@ namespace MWGui mFilterAll->setStateSelected(true); - setCoord(0, 342, 498, 258); + setCoord(mPositionInventory.left, mPositionInventory.top, mPositionInventory.width, mPositionInventory.height); + onWindowResize(static_cast(mMainWidget)); + + mPreview.setup(); + } + 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(); + } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - openContainer(player); + void InventoryWindow::setGuiMode(GuiMode mode) + { + mGuiMode = mode; + switch(mode) { + case GM_Container: + setPinButtonVisible(false); + mMainWidget->setCoord(mPositionContainer); + break; + case GM_Companion: + setPinButtonVisible(false); + mMainWidget->setCoord(mPositionCompanion); + break; + case GM_Barter: + setPinButtonVisible(false); + mMainWidget->setCoord(mPositionBarter); + break; + case GM_Inventory: + default: + setPinButtonVisible(true); + mMainWidget->setCoord(mPositionInventory); + break; + } + onWindowResize(static_cast(mMainWidget)); + } + + 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; - - mBoughtItems.clear(); + mItemView->update(); - onWindowResize(static_cast(mMainWidget)); - drawItems(); + notifyContentChanged(); } void InventoryWindow::onWindowResize(MyGUI::Window* _sender) @@ -96,26 +241,41 @@ namespace MWGui _sender->getSize().width - 12 - (_sender->getSize().height-44) * aspect - 15, _sender->getSize().height-44 ); + switch(mGuiMode) { + case GM_Container: + mPositionContainer = _sender->getCoord(); + break; + case GM_Companion: + mPositionCompanion = _sender->getCoord(); + break; + case GM_Barter: + mPositionBarter = _sender->getCoord(); + break; + case GM_Inventory: + default: + mPositionInventory = _sender->getCoord(); + } + 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); @@ -123,35 +283,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); - it = invStore.add(ptr); - (*it).getRefData().setCount(mDragAndDrop->mDraggedCount); + ptr.getRefData().setCount(mDragAndDrop->mDraggedCount); + it = invStore.add(ptr, mPtr); + 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()); @@ -160,15 +323,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 - mWindowManager.getBookWindow()->setTakeButtonShow(false); - mWindowManager.getScrollWindow()->setTakeButtonShow(false); - - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); + MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); - mWindowManager.setDragDrop(false); - - drawItems(); + mItemView->update(); notifyContentChanged(); } @@ -183,16 +341,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"); } } @@ -203,15 +360,22 @@ namespace MWGui if (slot == -1) return MWWorld::Ptr(); - MWWorld::Ptr player = mPtr; - MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); - if (invStore.getSlot(slot) != invStore.end()) - return *invStore.getSlot (slot); - else - return MWWorld::Ptr(); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + if(invStore.getSlot(slot) != invStore.end()) + { + MWWorld::Ptr item = *invStore.getSlot(slot); + // NOTE: Don't allow users to select WerewolfRobe objects in the inventory. Vanilla + // likely uses a hack like this since there's no other way to prevent it from being + // taken. + if(item.getCellRef().mRefID == "WerewolfRobe") + return MWWorld::Ptr(); + return item; + } + + return MWWorld::Ptr(); } - void InventoryWindow::_unequipItem(MWWorld::Ptr item) + void InventoryWindow::unequipItem(const MWWorld::Ptr& item) { MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); @@ -222,11 +386,11 @@ namespace MWGui { 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); - + (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + return; } } @@ -238,9 +402,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() @@ -264,36 +426,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()) @@ -303,7 +475,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()) @@ -313,44 +485,28 @@ 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 MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::Ptr newObject = *MWWorld::Class::get (player).getContainerStore (player).add (object); + MWWorld::Ptr newObject = *MWWorld::Class::get (player).getContainerStore (player).add (object, player); // 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 7c59bab50..35140437d 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -3,20 +3,29 @@ #include "../mwrender/characterpreview.hpp" -#include "container.hpp" -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" +#include "widgets.hpp" +#include "mode.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 +41,31 @@ namespace MWGui mPreview.rebuild(); } - protected: + TradeItemModel* getTradeModel(); + ItemModel* getModel(); + + void updateItemView(); + + void updatePlayer(); + + void setGuiMode(GuiMode mode); + + 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; @@ -48,6 +76,13 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; + MyGUI::IntCoord mPositionInventory; + MyGUI::IntCoord mPositionContainer; + MyGUI::IntCoord mPositionCompanion; + MyGUI::IntCoord mPositionBarter; + + GuiMode mGuiMode; + int mLastXSize; int mLastYSize; @@ -55,20 +90,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 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..01ea3429c 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -1,19 +1,19 @@ #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") + , mSortModel(NULL) + , mModel(NULL) { - 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 +26,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..f9a900eba --- /dev/null +++ b/apps/openmw/mwgui/itemview.cpp @@ -0,0 +1,202 @@ +#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) + , mScrollView(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); + std::string::size_type pos = path.rfind("."); + if(pos != std::string::npos) + 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..ab8dc1584 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -1,201 +1,474 @@ #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)) + { + getWidget (name) -> + eventMouseButtonClick += newDelegate(this, Handler); + } + + MWGui::BookPage* getPage (char const * name) + { + return getWidget (name); + } + + JournalWindowImpl (MWGui::JournalViewModel::Ptr Model) + : WindowBase("openmw_journal.layout"), JournalBooks (Model) { - cString = cString + cText; - mBook.pages.push_back(cString); - //TODO:finnish this - break; + 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); + } + + { + 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; } - if(static_cast (firstSpace) + cLineSize <= lineSize) + + void adjustButton (char const * name) { - cLineSize = firstSpace + cLineSize; - cString = cString + cText.substr(0,firstSpace +1); + 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)); } - else + + void open() { - cLineSize = firstSpace; - if(cLine +1 <= maxLine) + if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) { - cLine = cLine + 1; - cString = cString + std::string("\n") + cText.substr(0,firstSpace +1); + 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 (); + } - for(std::deque::const_iterator it = MWBase::Environment::get().getJournal()->begin();it!=MWBase::Environment::get().getJournal()->end();++it) + void replaceBook (Book book, unsigned int page) { - 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"); + assert (!mStates.empty ()); + mStates.top ().mBook = book; + mStates.top ().mPage = page; + updateShowingPages (); } - //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 popBook () + { + mStates.pop (); + updateShowingPages (); + updateCloseJournalButton (); + } + + void updateCloseJournalButton () + { + setVisible (CloseBTN, mStates.size () < 2); + setVisible (JournalBTN, mStates.size () >= 2); + } + + 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 cd1ff7ebb..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::EditBox* mLeftTextWidget; - MyGUI::EditBox* 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..a9fb6daab --- /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.empty()) + 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..7c0191d49 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 @@ -160,15 +173,10 @@ namespace MWGui attribute.setBase(100); } - // "When you gain a level, in addition to increasing three primary attributes, your Health - // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, - // the Health increase is calculated from the increased Endurance" - creatureStats.increaseLevelHealthBonus (creatureStats.getAttribute(ESM::Attribute::Endurance).getBase() * 0.1f); - - creatureStats.setLevel (creatureStats.getLevel()+1); + creatureStats.levelUp(); 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 c7b720730..8dda041ca 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -5,159 +5,165 @@ #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; - size_t scrollbarPosition = mScrollView->getScrollPosition(); + 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; - int i=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); - - int height = button->getTextSize().height; - button->setSize(MyGUI::IntSize(button->getSize().width, height)); - button->setUserData(i); - - mItemHeight += height + spacing; + mItems.push_back(name); } - else + + void MWList::addSeparator() { - 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); + mItems.push_back(""); + } - mItemHeight += 18 + spacing; + void MWList::adjustSize() + { + redraw(); } - ++i; - } - mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); - if (!scrollbarShown && mItemHeight > mClient->getSize().height) - redraw(true); + void MWList::redraw(bool scrollbarShown) + { + 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); + } - size_t scrollbarRange = mScrollView->getScrollRange(); - if(scrollbarPosition > scrollbarRange) - scrollbarPosition = scrollbarRange; - mScrollView->setScrollPosition(scrollbarPosition); -} + bool MWList::hasItem(const std::string& name) + { + return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); + } -bool MWList::hasItem(const std::string& name) -{ - return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); -} + unsigned int MWList::getItemCount() + { + return mItems.size(); + } -unsigned int MWList::getItemCount() -{ - return mItems.size(); -} + std::string MWList::getItemNameAt(unsigned int at) + { + assert(at < mItems.size() && "List item out of bounds"); + return mItems[at]; + } -std::string MWList::getItemNameAt(unsigned int at) -{ - assert(at < mItems.size() && "List item out of bounds"); - return mItems[at]; -} + 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::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::clear() + { + mItems.clear(); + } -void MWList::clear() -{ - mItems.clear(); -} + 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::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::onItemSelected(MyGUI::Widget* _sender) + { + std::string name = static_cast(_sender)->getCaption(); + int id = *_sender->getUserData(); + eventItemSelected(name, id); + eventWidgetSelected(_sender); + } -void MWList::onItemSelected(MyGUI::Widget* _sender) -{ - std::string name = static_cast(_sender)->getCaption(); - int id = *_sender->getUserData(); - eventItemSelected(name, id); - eventWidgetSelected(_sender); -} + MyGUI::Widget* MWList::getItemWidget(const std::string& name) + { + return mScrollView->findWidget (getName() + "_item_" + name); + } -MyGUI::Widget* MWList::getItemWidget(const std::string& name) -{ - return mScrollView->findWidget (getName() + "_item_" + name); -} + size_t MWScrollView::getScrollPosition() + { + return getVScroll()->getScrollPosition(); + } -size_t MWScrollView::getScrollPosition() -{ - return getVScroll()->getScrollPosition(); -} + void MWScrollView::setScrollPosition(size_t position) + { + getVScroll()->setScrollPosition(position); + } + size_t MWScrollView::getScrollRange() + { + return getVScroll()->getScrollRange(); + } -void MWScrollView::setScrollPosition(size_t position) -{ - getVScroll()->setScrollPosition(position); -} -size_t MWScrollView::getScrollRange() -{ - return getVScroll()->getScrollRange(); + } } diff --git a/apps/openmw/mwgui/list.hpp b/apps/openmw/mwgui/list.hpp index 09e42e865..956523c0d 100644 --- a/apps/openmw/mwgui/list.hpp +++ b/apps/openmw/mwgui/list.hpp @@ -1,7 +1,6 @@ #ifndef MWGUI_LIST_HPP #define MWGUI_LIST_HPP -#include #include namespace MWGui diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index e7c7acb53..9b63dfa76 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,41 +1,34 @@ #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 - +#include "../mwbase/inputmanager.hpp" 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) - , mLoadingOn(false) + , WindowBase("openmw_loading_screen.layout") , mLastRenderTime(0.f) , mLastWallpaperChangeTime(0.f) , mFirstLoad(true) + , mProgress(0) { getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); getWidget(mBackgroundImage, "BackgroundImage"); + mProgressBar->setScrollViewPage(1); mBackgroundMaterial = Ogre::MaterialManager::getSingleton().create("BackgroundMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); mBackgroundMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); @@ -57,6 +50,11 @@ namespace MWGui mRectangle->setVisible(false); } + void LoadingScreen::setLabel(const std::string &label) + { + mLoadingText->setCaptionWithReplacing(label); + } + LoadingScreen::~LoadingScreen() { delete mRectangle; @@ -67,56 +65,108 @@ namespace MWGui setCoord(0,0,w,h); } - void LoadingScreen::setLoadingProgress (const std::string& stage, int depth, int current, int total) + void LoadingScreen::loadingOn() { - if (!mLoadingOn) - loadingOn(); - - const int numRefLists = 20; + setVisible(true); - if (depth == 0) + if (mFirstLoad) { - mCurrentCellLoading = current; - mTotalCellsLoading = total; - - mCurrentRefLoading = 0; - mCurrentRefList = 0; + changeWallpaper(); } - else if (depth == 1) + else + { + mBackgroundImage->setImageTexture(""); + } + + MWBase::Environment::get().getWindowManager()->pushGuiMode(mFirstLoad ? GM_LoadingWallpaper : GM_Loading); + } + + void LoadingScreen::loadingOff() + { + setVisible(false); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); + } + + void LoadingScreen::changeWallpaper () + { + if (mResources.empty()) { - mCurrentRefLoading = current; - mTotalRefsLoading = total; + 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()); + } } - assert (mTotalCellsLoading != 0); + if (!mResources.empty()) + { + std::string const & randomSplash = mResources.at (rand() % mResources.size()); + + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); - float refProgress; - if (mTotalRefsLoading <= 1) - refProgress = 1; + mBackgroundImage->setImageTexture (randomSplash); + } else - refProgress = float(mCurrentRefLoading) / float(mTotalRefsLoading-1); - refProgress += mCurrentRefList; - refProgress /= numRefLists; + std::cerr << "No loading screens found!" << std::endl; + } + + void LoadingScreen::setProgressRange (size_t range) + { + mProgressBar->setScrollRange(range+1); + mProgressBar->setScrollPosition(0); + mProgressBar->setTrackSize(0); + mProgress = 0; + } - assert(refProgress <= 1 && refProgress >= 0); + void LoadingScreen::setProgress (size_t value) + { + assert(value < mProgressBar->getScrollRange()); + if (value - mProgress < mProgressBar->getScrollRange()/100.f) + return; + mProgress = value; + mProgressBar->setScrollPosition(0); + mProgressBar->setTrackSize(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize()); + draw(); + } + + void LoadingScreen::increaseProgress (size_t increase) + { + mProgressBar->setScrollPosition(0); + size_t value = mProgress + increase; + mProgress = value; + assert(mProgress < mProgressBar->getScrollRange()); + mProgressBar->setTrackSize(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize()); + draw(); + } - if (depth == 1 && mCurrentRefLoading == mTotalRefsLoading-1) - ++mCurrentRefList; + void LoadingScreen::indicateProgress() + { + float time = (mTimer.getMilliseconds() % 2001) / 1000.f; + if (time > 1) + time = (time-2)*-1; - float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading); - assert(progress <= 1 && progress >= 0); + mProgressBar->setTrackSize(50); + mProgressBar->setScrollPosition(time * (mProgressBar->getScrollRange()-1)); + draw(); + } - mLoadingText->setCaption(stage); - mProgressBar->setProgressPosition (static_cast(progress * 1000)); + void LoadingScreen::removeWallpaper() + { + mFirstLoad = false; + } - static float loadingScreenFps = 30.f; + void LoadingScreen::draw() + { + const float loadingScreenFps = 20.f; if (mTimer.getMilliseconds () > mLastRenderTime + (1.f/loadingScreenFps) * 1000.f) { - float dt = mTimer.getMilliseconds () - mLastRenderTime; mLastRenderTime = mTimer.getMilliseconds (); - if (mFirstLoad && mTimer.getMilliseconds () > mLastWallpaperChangeTime + 3000*1) + if (mFirstLoad && mTimer.getMilliseconds () > mLastWallpaperChangeTime + 5000*1) { mLastWallpaperChangeTime = mTimer.getMilliseconds (); changeWallpaper(); @@ -132,9 +182,7 @@ namespace MWGui } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - // always update input before rendering something, otherwise mygui goes crazy when something was entered in the frame before - // (e.g. when using "coc" console command, it would enter an infinite loop and crash due to overflow) - //MWBase::Environment::get().getInputManager()->update(0, true); + MWBase::Environment::get().getInputManager()->update(0, true); Ogre::CompositorChain* chain = Ogre::CompositorManager::getSingleton().getCompositorChain(mWindow->getViewport(0)); @@ -159,9 +207,13 @@ namespace MWGui } } - MWBase::Environment::get().getWorld ()->getFader ()->update (dt); - - mWindow->update(); + // First, swap buffers from last draw, then, queue an update of the + // window contents, but don't swap buffers (which would have + // caused a sync / flush and would be expensive). + // We're doing this so we can do some actual loading while the GPU is busy with the render. + // This means the render is lagging a frame behind, but this is hardly noticable. + mWindow->swapBuffers(false); // never Vsync, makes no sense here + mWindow->update(false); if (!hasCompositor) mWindow->getViewport(0)->setClearEveryFrame(true); @@ -180,56 +232,4 @@ namespace MWGui mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); } } - - void LoadingScreen::loadingDone() - { - loadingOff(); - } - - void LoadingScreen::loadingOn() - { - setVisible(true); - mLoadingOn = true; - - if (mFirstLoad) - { - changeWallpaper(); - - mWindowManager.pushGuiMode(GM_LoadingWallpaper); - } - else - { - mBackgroundImage->setImageTexture(""); - mWindowManager.pushGuiMode(GM_Loading); - } - } - - - void LoadingScreen::loadingOff() - { - setVisible(false); - mLoadingOn = false; - mFirstLoad = false; - - mWindowManager.removeGuiMode(GM_Loading); - mWindowManager.removeGuiMode(GM_LoadingWallpaper); - } - - void LoadingScreen::changeWallpaper () - { - if (mResources.isNull ()) - mResources = Ogre::ResourceGroupManager::getSingleton ().findResourceNames ("General", "Splash_*.tga"); - - - if (mResources->size()) - { - std::string const & randomSplash = mResources->at (rand() % mResources->size()); - - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); - - mBackgroundImage->setImageTexture (randomSplash); - } - else - std::cerr << "No loading screens found!" << std::endl; - } } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 24b385071..dde8ff63a 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -2,16 +2,31 @@ #define MWGUI_LOADINGSCREEN_H #include -#include -#include "window_base.hpp" +#include "windowbase.hpp" + +#include namespace MWGui { - class LoadingScreen : public WindowBase + class LoadingScreen : public WindowBase, public Loading::Listener { public: - LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw, MWBase::WindowManager& parWindowManager); + virtual void setLabel (const std::string& label); + + /// Indicate that some progress has been made, without specifying how much + virtual void indicateProgress (); + + virtual void loadingOn(); + virtual void loadingOff(); + + virtual void setProgressRange (size_t range); + virtual void setProgress (size_t value); + virtual void increaseProgress (size_t increase); + + virtual void removeWallpaper(); + + LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw); virtual ~LoadingScreen(); void setLoadingProgress (const std::string& stage, int depth, int current, int total); @@ -31,27 +46,20 @@ namespace MWGui unsigned long mLastRenderTime; Ogre::Timer mTimer; + size_t mProgress; + MyGUI::TextBox* mLoadingText; - MyGUI::ProgressBar* mProgressBar; + MyGUI::ScrollBar* mProgressBar; MyGUI::ImageBox* mBackgroundImage; - int mCurrentCellLoading; - int mTotalCellsLoading; - int mCurrentRefLoading; - int mTotalRefsLoading; - int mCurrentRefList; - Ogre::Rectangle2D* mRectangle; Ogre::MaterialPtr mBackgroundMaterial; - Ogre::StringVectorPtr mResources; - - bool mLoadingOn; - - void loadingOn(); - void loadingOff(); + Ogre::StringVector mResources; void changeWallpaper(); + + void draw(); }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5402d3542..1db6e9ecd 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -3,9 +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 { @@ -30,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"); @@ -68,11 +70,21 @@ namespace MWGui { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); if (sender == mButtons["return"]) + { + MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); 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 52b108f85..000000000 --- a/apps/openmw/mwgui/map_window.cpp +++ /dev/null @@ -1,446 +0,0 @@ -#include "map_window.hpp" - -#include - -#include -#include -#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" - -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 + (-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", "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().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/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp new file mode 100644 index 000000000..5ed002d7b --- /dev/null +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -0,0 +1,446 @@ +#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) + { + } + + LocalMapBase::~LocalMapBase() + { + } + + 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 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))); + + 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) + , mGlobalMap(0) + { + setCoord(500,0,320,300); + + getWidget(mLocalMap, "LocalMap"); + getWidget(mGlobalMap, "GlobalMap"); + getWidget(mGlobalMapImage, "GlobalMapImage"); + getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); + getWidget(mPlayerArrowLocal, "CompassLocal"); + getWidget(mPlayerArrowGlobal, "CompassGlobal"); + + 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); + } + + void MapWindow::renderGlobalMap(Loading::Listener* loadingListener) + { + mGlobalMapRender = new MWRender::GlobalMap(""); + mGlobalMapRender->render(loadingListener); + mGlobalMapImage->setImageTexture("GlobalMap.png"); + mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); + } + + 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 92% rename from apps/openmw/mwgui/map_window.hpp rename to apps/openmw/mwgui/mapwindow.hpp index 39770a7a2..5518ab4a8 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -1,19 +1,25 @@ #ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWRender { class GlobalMap; } +namespace Loading +{ + class Listener; +} + namespace MWGui { class LocalMapBase { public: LocalMapBase(); + virtual ~LocalMapBase(); void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop=false); void setCellPrefix(const std::string& prefix); @@ -65,11 +71,13 @@ 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); + void renderGlobalMap(Loading::Listener* loadingListener); + void addVisitedLocation(const std::string& name, int x, int y); // adds the marker to the global map void cellExplored(int x, int y); 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 b8a34c457..45da1bf17 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -3,411 +3,427 @@ #include "messagebox.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/inputmanager.hpp" -using namespace MWGui; - -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 () -{ - mInterMessageBoxe->enterPressed(); -} + void MessageBoxManager::okayPressed () + { + if(mInterMessageBoxe != NULL) + mInterMessageBoxe->okayPressed(); + } -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) + , mTextButtonPadding(0) + { + 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::Button* 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) + void InteractiveMessageBox::okayPressed() { - if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) + + std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) { - buttonActivated(*button); - MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); - break; + 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) -{ - buttonActivated (pressed); -} + void InteractiveMessageBox::mousePressed (MyGUI::Widget* 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) + void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed) { - if(*button == 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 149aa7e7f..4ef645f5e 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -1,9 +1,7 @@ #ifndef MWGUI_MESSAGE_BOX_H #define MWGUI_MESSAGE_BOX_H -#include - -#include "window_base.hpp" +#include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" @@ -31,24 +29,31 @@ 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 (); void removeMessageBox (float time, MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - - void enterPressed(); + + void okayPressed(); 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; }; @@ -73,11 +78,11 @@ namespace MWGui 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 okayPressed (); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); @@ -85,7 +90,7 @@ namespace MWGui private: void buttonActivated (MyGUI::Widget* _widget); - + MessageBoxManager& mMessageBoxManager; MyGUI::EditBox* mMessageWidget; MyGUI::Widget* mButtonsWidget; 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..5f749d3d3 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,11 +39,12 @@ 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) + , mSelectedIndex(-1) { getWidget(mOkButton, "OKButton"); getWidget(mInstructionLabel, "InstructionLabel"); @@ -109,14 +106,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 +121,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 +135,7 @@ namespace MWGui { if (!mMagicSelectionDialog ) { - mMagicSelectionDialog = new MagicSelectionDialog(mWindowManager, this); + mMagicSelectionDialog = new MagicSelectionDialog(this); } mMagicSelectionDialog->setVisible(true); @@ -281,7 +277,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 +287,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 +299,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 +313,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 +337,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 +395,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) @@ -609,7 +605,6 @@ namespace MWGui void MagicSelectionDialog::onEnchantedItemSelected(MyGUI::Widget* _sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::Ptr item = *_sender->getUserData(); mParent->onAssignMagicItem (item); 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 1436995c5..2c73226e3 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -1,404 +1,400 @@ #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) - : WindowModal("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::Button *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("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", mWindowManager.getGameSettingString("sBonusSkillTitle", "Skill Bonus")); - getWidget(mSkillList, "SkillList"); - setText("SpellPowerT", mWindowManager.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(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); - - 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::Button* 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) + , mPreviewDirty(true) + , mPreview(NULL) + { + // 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() -{ - WindowModal::open(); + void RaceDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); - updateRaces(); - updateSkills(); - updateSpellPowers(); + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } - mPreview = new MWRender::RaceSelectionPreview(); - mPreview->setup(); - mPreview->update (0); + void RaceDialog::open() + { + WindowModal::open(); - 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; + updateRaces(); + updateSkills(); + updateSpellPowers(); - index = proto.mHair.substr(proto.mHair.size() - 2, 2); - mHairIndex = boost::lexical_cast(index) - 1; + mPreview = new MWRender::RaceSelectionPreview(); + mPreview->setup(); + mPreview->update (0); - mPreviewImage->setImageTexture ("CharacterHeadPreview"); -} + 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::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; - } - } + index = proto.mHair.substr(proto.mHair.size() - 2, 2); + mHairIndex = 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; -} + mPreviewImage->setImageTexture ("CharacterHeadPreview"); -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(); + mPreviewDirty = true; } - 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); + + updateSkills(); + updateSpellPowers(); } - return count; -} -void RaceDialog::close() -{ - delete mPreview; - mPreview = 0; -} + void RaceDialog::close() + { + delete mPreview; + mPreview = 0; + } -// widget controls + // widget controls -void RaceDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} + void RaceDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } -void RaceDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + void RaceDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } -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::onHeadRotate(MyGUI::ScrollBar*, size_t _position) + { + float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; + float diff = angle - mCurrentAngle; + mPreview->update (diff); + mPreviewDirty = true; + mCurrentAngle += diff; + } -void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex - 1, 2); + void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex - 1, 2); - recountParts(); - updatePreview(); -} + recountParts(); + updatePreview(); + } -void RaceDialog::onSelectNextGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex + 1, 2); + void RaceDialog::onSelectNextGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex + 1, 2); - recountParts(); - updatePreview(); -} + recountParts(); + updatePreview(); + } -void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) -{ - mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); - updatePreview(); -} + void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) + { + mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size()); + updatePreview(); + } -void RaceDialog::onSelectNextFace(MyGUI::Widget*) -{ - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); - updatePreview(); -} + void RaceDialog::onSelectNextFace(MyGUI::Widget*) + { + mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size()); + updatePreview(); + } -void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) -{ - mHairIndex = wrap(mHairIndex - 1, mHairCount); - updatePreview(); -} + void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) + { + mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size()); + updatePreview(); + } -void RaceDialog::onSelectNextHair(MyGUI::Widget*) -{ - mHairIndex = wrap(mHairIndex + 1, mHairCount); - updatePreview(); -} + void RaceDialog::onSelectNextHair(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::onSelectRace(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - const std::string *raceId = mRaceList->getItemDataAt(_index); - if (boost::iequals(mCurrentRaceId, *raceId)) - return; + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + const std::string *raceId = mRaceList->getItemDataAt(_index); + if (boost::iequals(mCurrentRaceId, *raceId)) + return; - mCurrentRaceId = *raceId; + mCurrentRaceId = *raceId; - recountParts(); + recountParts(); - updatePreview(); - updateSkills(); - updateSpellPowers(); -} + updatePreview(); + updateSkills(); + updateSpellPowers(); + } -void RaceDialog::recountParts() -{ - mFaceIndex = 0; - mHairIndex = 0; + void RaceDialog::getBodyParts (int part, std::vector& out) + { + out.clear(); + const MWWorld::Store &store = + MWBase::Environment::get().getWorld()->getStore().get(); - mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); - mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); -} + 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); + } + } -// update widget content + void RaceDialog::recountParts() + { + getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs); + getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads); -void RaceDialog::updatePreview() -{ - ESM::NPC record = mPreview->getPrototype(); - record.mRace = mCurrentRaceId; - record.setIsMale(mGenderIndex == 0); + mFaceIndex = 0; + mHairIndex = 0; + } - std::string prefix = - "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); + // update widget content - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); + void RaceDialog::updatePreview() + { + ESM::NPC record = mPreview->getPrototype(); + record.mRace = mCurrentRaceId; + record.setIsMale(mGenderIndex == 0); - record.mHead = prefix + "head_" + headIndex; - record.mHair = prefix + "hair_" + hairIndex; + record.mHead = mAvailableHeads[mFaceIndex]; + record.mHair = mAvailableHairs[mHairIndex]; - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); + mPreview->setPrototype(record); + mPreviewDirty = true; + } - if (parts.search(record.mHair) == 0) { - record.mHair = prefix + "hair" + hairIndex; + void RaceDialog::doRenderUpdate() + { + if (mPreviewDirty) + { + mPreview->render(); + mPreviewDirty = false; + } } - 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 efd08f439..914ae8096 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 @@ -26,7 +21,7 @@ namespace MWGui class RaceDialog : public WindowModal { public: - RaceDialog(MWBase::WindowManager& parWindowManager); + RaceDialog(); enum Gender { @@ -57,6 +52,8 @@ namespace MWGui */ EventHandle_Void eventBack; + void doRenderUpdate(); + protected: void onHeadRotate(MyGUI::ScrollBar* _sender, size_t _position); @@ -81,6 +78,11 @@ 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; @@ -92,13 +94,14 @@ namespace MWGui std::vector mSpellPowerItems; int mGenderIndex, mFaceIndex, mHairIndex; - int mFaceCount, mHairCount; std::string mCurrentRaceId; float mCurrentAngle; MWRender::RaceSelectionPreview* mPreview; + + bool mPreviewDirty; }; } #endif 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 50508cc5f..dfc86a547 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -1,374 +1,366 @@ #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) - : WindowModal("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") { - 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::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); + // Setup skills + getWidget(mSkillView, "SkillView"); + mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - MyGUI::Button* 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() -{ - WindowModal::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; + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); + } - 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 4f41ec42d..1c24fec74 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 @@ -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); @@ -80,7 +79,6 @@ namespace MWGui MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget; MyGUI::ScrollView* mSkillView; - int mLastPos, mClientHeight; Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 3bd3a4743..48931b18e 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -10,71 +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) - , mTakeButtonShow(true) - , mTakeButtonAllowed(true) +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) -{ - mTakeButtonShow = show; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} + mTextView->setViewOffset(MyGUI::IntPoint(0,0)); -void ScrollWindow::setInventoryAllowed(bool allowed) -{ - mTakeButtonAllowed = allowed; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} + setTakeButtonShow(true); + } -void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + void ScrollWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } - mWindowManager.removeGuiMode(GM_Scroll); -} + void ScrollWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + 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::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); - 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 42b6395a9..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,7 +11,7 @@ namespace MWGui class ScrollWindow : public WindowBase { public: - ScrollWindow (MWBase::WindowManager& parWindowManager); + ScrollWindow (); void open (MWWorld::Ptr scroll); void setTakeButtonShow(bool show); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 04856c3ed..3dfa17bad 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1,15 +1,13 @@ #include "settingswindow.hpp" #include -#include #include -#include #include #include #include -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -17,8 +15,6 @@ #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwrender/renderingmanager.hpp" - #include "confirmationdialog.hpp" namespace @@ -93,10 +89,11 @@ namespace namespace MWGui { - SettingsWindow::SettingsWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_settings_window.layout", parWindowManager) + SettingsWindow::SettingsWindow() : + WindowBase("openmw_settings_window.layout") { getWidget(mOkButton, "OkButton"); + getWidget(mBestAttackButton, "BestAttackButton"); getWidget(mSubtitlesButton, "SubtitlesButton"); getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mResolutionList, "ResolutionList"); @@ -128,16 +125,16 @@ namespace MWGui getWidget(mActorShadows, "ActorShadows"); getWidget(mStaticsShadows, "StaticsShadows"); getWidget(mMiscShadows, "MiscShadows"); - getWidget(mShadowsDebug, "ShadowsDebug"); + getWidget(mTerrainShadows, "TerrainShadows"); getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mInvertYButton, "InvertYButton"); - getWidget(mUISensitivitySlider, "UISensitivitySlider"); getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); getWidget(mRefractionButton, "RefractionButton"); mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mBestAttackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -164,7 +161,7 @@ namespace MWGui mActorShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mStaticsShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mMiscShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mShadowsDebug->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mTerrainShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mMasterVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mVoiceVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); @@ -177,16 +174,14 @@ namespace MWGui mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); // fill resolution list - Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); - Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues; + int screen = Settings::Manager::getInt("screen", "Video"); + int numDisplayModes = SDL_GetNumDisplayModes(screen); std::vector < std::pair > resolutions; - for (Ogre::StringVector::const_iterator it=videoModes.begin(); - it!=videoModes.end(); ++it) + for (int i = 0; i < numDisplayModes; i++) { - - int resX, resY; - parseResolution (resX, resY, *it); - resolutions.push_back(std::make_pair(resX, resY)); + SDL_DisplayMode mode; + SDL_GetDisplayMode(screen, i, &mode); + resolutions.push_back(std::make_pair(mode.w, mode.h)); } std::sort(resolutions.begin(), resolutions.end(), sortResolutions); for (std::vector < std::pair >::const_iterator it=resolutions.begin(); @@ -207,6 +202,7 @@ namespace MWGui mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}"); + mBestAttackButton->setCaptionWithReplacing(Settings::Manager::getBool("best attack", "Game") ? "#{sOn}" : "#{sOff}"); float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin); mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); @@ -236,23 +232,17 @@ namespace MWGui mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); - //mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); - mShadowsLargeDistance->setCaptionWithReplacing("#{sOff}"); - mShadowsLargeDistance->setEnabled (false); + 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}"); mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); - mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + mTerrainShadows->setCaptionWithReplacing(Settings::Manager::getBool("terrain shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); float cameraSens = (Settings::Manager::getFloat("camera sensitivity", "Input")-0.2)/(5.0-0.2); mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1)); - float uiSens = (Settings::Manager::getFloat("ui sensitivity", "Input")-0.2)/(5.0-0.2); - mUISensitivitySlider->setScrollPosition (uiSens * (mUISensitivitySlider->getScrollRange()-1)); mCameraSensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mUISensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); @@ -274,7 +264,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) @@ -282,7 +272,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); @@ -300,7 +290,6 @@ namespace MWGui Settings::Manager::setInt("resolution y", "Video", resY); apply(); - mResolutionList->setIndexSelected(MyGUI::ITEM_NONE); } void SettingsWindow::onResolutionCancel() @@ -329,8 +318,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) { @@ -362,7 +351,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 @@ -405,14 +394,16 @@ namespace MWGui Settings::Manager::setBool("statics shadows", "Shadows", newState); else if (_sender == mMiscShadows) Settings::Manager::setBool("misc shadows", "Shadows", newState); - else if (_sender == mShadowsDebug) - Settings::Manager::setBool("debug", "Shadows", newState); + else if (_sender == mTerrainShadows) + Settings::Manager::setBool("terrain shadows", "Shadows", newState); else if (_sender == mInvertYButton) Settings::Manager::setBool("invert y axis", "Input", newState); else if (_sender == mCrosshairButton) Settings::Manager::setBool("crosshair", "HUD", newState); else if (_sender == mSubtitlesButton) Settings::Manager::setBool("subtitles", "GUI", newState); + else if (_sender == mBestAttackButton) + Settings::Manager::setBool("best attack", "Game", newState); apply(); } @@ -437,8 +428,8 @@ namespace MWGui void SettingsWindow::onShadersToggled(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"); std::string val = static_cast(_sender)->getCaption(); if (val == off) @@ -537,8 +528,6 @@ namespace MWGui Settings::Manager::setFloat("footsteps volume", "Sound", val); else if (scroller == mMusicVolumeSlider) Settings::Manager::setFloat("music volume", "Sound", val); - else if (scroller == mUISensitivitySlider) - Settings::Manager::setFloat("ui sensitivity", "Input", (1-val) * 0.2 + val * 5.f); else if (scroller == mCameraSensitivitySlider) Settings::Manager::setFloat("camera sensitivity", "Input", (1-val) * 0.2 + val * 5.f); @@ -559,6 +548,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; @@ -593,7 +584,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); @@ -610,7 +601,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 fc1ec9e36..a585bda7e 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(); @@ -32,6 +32,7 @@ namespace MWGui MyGUI::ScrollBar* mToolTipDelaySlider; MyGUI::Button* mSubtitlesButton; MyGUI::Button* mCrosshairButton; + MyGUI::Button* mBestAttackButton; // graphics MyGUI::ListBox* mResolutionList; @@ -58,7 +59,7 @@ namespace MWGui MyGUI::Button* mActorShadows; MyGUI::Button* mStaticsShadows; MyGUI::Button* mMiscShadows; - MyGUI::Button* mShadowsDebug; + MyGUI::Button* mTerrainShadows; // audio MyGUI::ScrollBar* mMasterVolumeSlider; @@ -71,7 +72,6 @@ namespace MWGui MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; MyGUI::Button* mInvertYButton; - MyGUI::ScrollBar* mUISensitivitySlider; MyGUI::ScrollBar* mCameraSensitivitySlider; void onOkButtonClicked(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 40fcf2988..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, @@ -131,13 +117,13 @@ namespace MWGui { 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); @@ -146,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, @@ -161,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 c4988fff3..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; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 8a079e40c..dc86fd825 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() @@ -88,6 +81,15 @@ namespace MWGui mDeleteButton->setVisible (false); mEffect.mRange = ESM::RT_Self; + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf)) + mEffect.mRange = ESM::RT_Touch; + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch)) + mEffect.mRange = ESM::RT_Target; + mEffect.mMagnMin = 1; + mEffect.mMagnMax = 1; + mEffect.mDuration = 1; + mEffect.mArea = 0; + eventEffectAdded(mEffect); onRangeButtonClicked(mRangeButton); @@ -100,11 +102,6 @@ namespace MWGui mMagnitudeMinValue->setCaption("1"); mMagnitudeMaxValue->setCaption("- 1"); mAreaValue->setCaption("0"); - - mEffect.mMagnMin = 1; - mEffect.mMagnMax = 1; - mEffect.mDuration = 1; - mEffect.mArea = 0; } void EditEffectDialog::editEffect (ESM::ENAMstruct effect) @@ -113,7 +110,7 @@ namespace MWGui MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); setMagicEffect(magicEffect); - + mOldEffect = effect; mEffect = effect; mEditing = true; @@ -128,6 +125,7 @@ namespace MWGui onMagnitudeMaxChanged (mMagnitudeMinSlider, effect.mMagnMax-1); onAreaChanged (mAreaSlider, effect.mArea); onDurationChanged (mDurationSlider, effect.mDuration-1); + eventEffectModified(mEffect); } void EditEffectDialog::setMagicEffect (const ESM::MagicEffect *effect) @@ -164,13 +162,13 @@ 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); curY += mDurationBox->getSize().height; } - if (mEffect.mRange == ESM::RT_Target) + if (mEffect.mRange != ESM::RT_Self) { mAreaBox->setPosition(mAreaBox->getPosition().left, curY); mAreaBox->setVisible (true); @@ -189,9 +187,6 @@ namespace MWGui else if (mEffect.mRange == ESM::RT_Touch) mRangeButton->setCaptionWithReplacing ("#{sRangeTouch}"); - mAreaSlider->setVisible (mEffect.mRange != ESM::RT_Self); - mAreaText->setVisible (mEffect.mRange != ESM::RT_Self); - // cycle through range types until we find something that's allowed if (mEffect.mRange == ESM::RT_Target && !(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget)) onRangeButtonClicked(sender); @@ -200,7 +195,13 @@ namespace MWGui if (mEffect.mRange == ESM::RT_Touch && !(mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch)) onRangeButtonClicked(sender); + if(mEffect.mRange == ESM::RT_Self) + { + mAreaSlider->setScrollPosition(0); + onAreaChanged(mAreaSlider,0); + } updateBoxes(); + eventEffectModified(mEffect); } void EditEffectDialog::onDeleteButtonClicked (MyGUI::Widget* sender) @@ -213,26 +214,27 @@ namespace MWGui void EditEffectDialog::onOkButtonClicked (MyGUI::Widget* sender) { setVisible(false); - - if (mEditing) - eventEffectModified(mEffect); - else - eventEffectAdded(mEffect); } void EditEffectDialog::onCancelButtonClicked (MyGUI::Widget* sender) { setVisible(false); + if(mEditing) + eventEffectModified(mOldEffect); + else + eventEffectRemoved(mEffect); } void EditEffectDialog::setSkill (int skill) { mEffect.mSkill = skill; + eventEffectModified(mEffect); } void EditEffectDialog::setAttribute (int attribute) { mEffect.mAttribute = attribute; + eventEffectModified(mEffect); } void EditEffectDialog::onMagnitudeMinChanged (MyGUI::ScrollBar* sender, size_t pos) @@ -242,6 +244,7 @@ namespace MWGui // trigger the check again (see below) onMagnitudeMaxChanged(mMagnitudeMaxSlider, mMagnitudeMaxSlider->getScrollPosition ()); + eventEffectModified(mEffect); } void EditEffectDialog::onMagnitudeMaxChanged (MyGUI::ScrollBar* sender, size_t pos) @@ -257,25 +260,29 @@ namespace MWGui mEffect.mMagnMax = pos+1; mMagnitudeMaxValue->setCaption("- " + boost::lexical_cast(pos+1)); + + eventEffectModified(mEffect); } void EditEffectDialog::onDurationChanged (MyGUI::ScrollBar* sender, size_t pos) { mDurationValue->setCaption(boost::lexical_cast(pos+1)); mEffect.mDuration = pos+1; + eventEffectModified(mEffect); } void EditEffectDialog::onAreaChanged (MyGUI::ScrollBar* sender, size_t pos) { mAreaValue->setCaption(boost::lexical_cast(pos)); mEffect.mArea = pos; + eventEffectModified(mEffect); } // ------------------------------------------------------------------------------------------------ - 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 +309,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 +353,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 +363,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 +418,8 @@ namespace MWGui // ------------------------------------------------------------------------------------------------ - EffectEditorBase::EffectEditorBase(MWBase::WindowManager& parWindowManager) - : mAddEffectDialog(parWindowManager) + EffectEditorBase::EffectEditorBase() + : mAddEffectDialog() , mSelectAttributeDialog(NULL) , mSelectSkillDialog(NULL) { @@ -423,6 +430,10 @@ namespace MWGui mAddEffectDialog.setVisible (false); } + EffectEditorBase::~EffectEditorBase() + { + } + void EffectEditorBase::startEditing () { // get the list of magic effects that are known to the player @@ -516,7 +527,7 @@ namespace MWGui { if (mEffects.size() >= 8) { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); return; } @@ -527,7 +538,7 @@ namespace MWGui { if (it->mEffectID == effectId) { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}"); return; } } @@ -540,7 +551,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); @@ -548,7 +559,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); @@ -600,7 +611,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); @@ -619,6 +629,7 @@ namespace MWGui void EffectEditorBase::onEffectAdded (ESM::ENAMstruct effect) { mEffects.push_back(effect); + mSelectedEffect=mEffects.size()-1; updateEffectsView(); } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 393d9c3ec..e424d7395 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,13 +69,13 @@ 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(); protected: ESM::ENAMstruct mEffect; + ESM::ENAMstruct mOldEffect; const ESM::MagicEffect* mMagicEffect; }; @@ -84,8 +84,8 @@ namespace MWGui class EffectEditorBase { public: - EffectEditorBase(MWBase::WindowManager& parWindowManager); - + EffectEditorBase(); + virtual ~EffectEditorBase(); protected: std::map mButtonMapping; // maps button ID to effect ID @@ -124,7 +124,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 index 16e02ebba..9812c0f8a 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -1,9 +1,5 @@ #include "spellicons.hpp" -#include -#include -#include - #include #include "../mwbase/world.hpp" @@ -14,7 +10,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwmechanics/activespells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "tooltips.hpp" @@ -134,47 +129,17 @@ namespace MWGui effectInfo.mMagnitude = static_cast (0.05*it->second.second / (0.1 * magicEffect->mData.mBaseCost)); } - effects[effectIt->mEffectID].push_back (effectInfo); } } - parent->setVisible(effects.size() != 0); - int w=2; - if (adjustSize) - { - int s = effects.size() * 16+4; - 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; @@ -214,20 +179,61 @@ namespace MWGui } } - std::string name = ESM::MagicEffect::effectIdToString (it->first); + if (remainingDuration > 0.f) + { + MyGUI::ImageBox* image; + if (mWidgetMap.find(it->first) == mWidgetMap.end()) + { + image = parent->createWidget + ("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default); + mWidgetMap[it->first] = image; + + 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); - ToolTipInfo tooltipInfo; - tooltipInfo.caption = "#{" + name + "}"; - tooltipInfo.icon = effect->mIcon; - tooltipInfo.text = sourcesDescription; - tooltipInfo.imageSize = 16; - tooltipInfo.wordWrap = false; + std::string name = ESM::MagicEffect::effectIdToString (it->first); - image->setUserData(tooltipInfo); - image->setUserString("ToolTipType", "ToolTipInfo"); + ToolTipInfo tooltipInfo; + tooltipInfo.caption = "#{" + name + "}"; + tooltipInfo.icon = effect->mIcon; + tooltipInfo.imageSize = 16; + tooltipInfo.wordWrap = false; - // Fade out during the last 5 seconds - image->setAlpha(std::min(remainingDuration/fadeTime, 1.f)); + image->setUserData(tooltipInfo); + image->setUserString("ToolTipType", "ToolTipInfo"); + } + else + image = mWidgetMap[it->first]; + + image->setPosition(w,2); + image->setVisible(true); + w += 16; + + ToolTipInfo* tooltipInfo = image->getUserData(); + tooltipInfo->text = sourcesDescription; + + // Fade out during the last 5 seconds + image->setAlpha(std::min(remainingDuration/fadeTime, 1.f)); + } + else if (mWidgetMap.find(it->first) != mWidgetMap.end()) + { + mWidgetMap[it->first]->setVisible(false); + } + } + + if (adjustSize) + { + int s = w + 2; + if (effects.empty()) + s = 0; + int diff = parent->getWidth() - s; + parent->setSize(s, parent->getHeight()); + parent->setPosition(parent->getLeft()+diff, parent->getTop()); } // hide inactive effects @@ -236,7 +242,6 @@ namespace MWGui if (effects.find(it->first) == effects.end()) it->second->setVisible(false); } - } diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index af600e347..818d67b5b 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -22,7 +22,11 @@ namespace MWGui // information about a single magic effect source as required for display in the tooltip struct MagicEffectInfo { - MagicEffectInfo() : mPermanent(false) {} + MagicEffectInfo() + : mPermanent(false) + , mMagnitude(0) + , mRemainingTime(0) + {} std::string mSource; // display name for effect source (e.g. potion name) MWMechanics::EffectKey mKey; int mMagnitude; diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 50691d554..d5e3abc11 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -1,22 +1,14 @@ #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" @@ -47,8 +39,8 @@ 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) { @@ -71,7 +63,7 @@ namespace MWGui void SpellWindow::onPinToggled() { - mWindowManager.setSpellVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned); } void SpellWindow::open() @@ -140,7 +132,7 @@ namespace MWGui { store.setSelectedEnchantItem(store.end()); spells.setSelectedSpell(""); - mWindowManager.unsetSelectedSpell(); + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); selectedItem = MWWorld::Ptr(); } } @@ -377,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(); } @@ -404,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(); @@ -423,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(); @@ -454,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 1963d4346..521e73d76 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SPELLWINDOW_H #define MWGUI_SPELLWINDOW_H -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWGui { @@ -10,7 +10,7 @@ namespace MWGui class SpellWindow : public WindowPinnableBase { public: - SpellWindow(MWBase::WindowManager& parWindowManager); + SpellWindow(); virtual ~SpellWindow(); void updateSpells(); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp deleted file mode 100644 index 0fa4127b5..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*)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(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..9facdac40 --- /dev/null +++ b/apps/openmw/mwgui/statswindow.cpp @@ -0,0 +1,573 @@ +#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) + , 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(), mSkillView->getCanvasSize().height); + } + + 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(); + const 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)); + + 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(); + const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); + const 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}"); + } + + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); + } + + 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 93% rename from apps/openmw/mwgui/stats_window.hpp rename to apps/openmw/mwgui/statswindow.hpp index 3befc1f00..ac8319bdc 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(); @@ -62,7 +57,6 @@ namespace MWGui MyGUI::Widget* mRightPane; MyGUI::ScrollView* mSkillView; - int mLastPos, mClientHeight; SkillList mMajorSkills, mMinorSkills, mMiscSkills; std::map > mSkillValues; diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp deleted file mode 100644 index 9265cadf9..000000000 --- a/apps/openmw/mwgui/text_input.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "text_input.hpp" - -#include "../mwbase/windowmanager.hpp" - -using namespace MWGui; - -TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_text_input.layout", parWindowManager) -{ - // 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(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.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() == "") - { - 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..954bc41ab --- /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 + MWBase::Environment::get().getWindowManager()->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 + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); + } + + // widget controls + + void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + + void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + +} diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/textinput.hpp similarity index 87% rename from apps/openmw/mwgui/text_input.hpp rename to apps/openmw/mwgui/textinput.hpp index 29de7388b..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 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); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 8dcf398ca..3a609aa91 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -2,791 +2,763 @@ #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); +namespace MWGui +{ - // 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); + ToolTips::ToolTips() : + Layout("openmw_tooltips.layout") + , 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"); - mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); - mRemainingDelay = mDelay; -} + mDynamicToolTipBox->setVisible(false); -void ToolTips::setEnabled(bool enabled) -{ - mEnabled = enabled; -} + // 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); -void ToolTips::onFrame(float frameDuration) -{ + mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); + mRemainingDelay = mDelay; - while (mDynamicToolTipBox->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) + { + mMainWidget->getChildAt(i)->setVisible(false); + } } - // start by hiding everything - for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) + void ToolTips::setEnabled(bool enabled) { - mMainWidget->getChildAt(i)->setVisible(false); + mEnabled = enabled; } - const IntSize &viewSize = RenderManager::getInstance().getViewSize(); - - if (!mEnabled) + void ToolTips::onFrame(float frameDuration) { - return; - } - if (!mGameMode) - { - const MyGUI::IntPoint& mousePos = InputManager::getInstance().getMousePosition(); + while (mDynamicToolTipBox->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); + } - if (mWindowManager->getWorldMouseOver() && ((mWindowManager->getMode() == GM_Console) - || (mWindowManager->getMode() == GM_Container) - || (mWindowManager->getMode() == GM_Inventory))) + // start by hiding everything + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) { - mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); + mMainWidget->getChildAt(i)->setVisible(false); + } - if (mFocusObject.isEmpty ()) - return; + const MyGUI::IntSize &viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntSize tooltipSize = getToolTipViaPtr(true); + if (!mEnabled) + { + return; + } - IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); + bool gameMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); - // 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) + if (gameMode) + { + const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition(); + + 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))) { - tooltipPosition.top = viewSize.height - tooltipSize.height; - } + mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); - } + if (mFocusObject.isEmpty ()) + return; - else - { - const MyGUI::IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject); + + 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); - if (mousePos == lastPressed) // mouseclick makes tooltip disappear - return; + MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition(); + position(tooltipPosition, tooltipSize, viewSize); - if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) - { - mRemainingDelay -= frameDuration; + setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } + else { - mHorizontalScrollIndex = 0; - mRemainingDelay = mDelay; - } - mLastMouseX = mousePos.left; - mLastMouseY = mousePos.top; + const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); - - if (mRemainingDelay > 0) - return; + if (mousePos == lastPressed) // mouseclick makes tooltip disappear + return; - Widget* focus = InputManager::getInstance().getMouseFocusWidget(); - if (focus == 0) - return; + if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) + { + mRemainingDelay -= frameDuration; + } + else + { + mHorizontalScrollIndex = 0; + mRemainingDelay = mDelay; + } + mLastMouseX = mousePos.left; + mLastMouseY = mousePos.top; - 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) + if (mRemainingDelay > 0) return; - ++i; - } - - std::string type = focus->getUserString("ToolTipType"); - std::string text = focus->getUserString("ToolTipText"); - - if (type == "") - { - return; - } - - - // 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(); - if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); + if (focus == 0) return; - } - if (type == "ItemPtr") - { - mFocusObject = *focus->getUserData(); - tooltipSize = getToolTipViaPtr(false); - } - else if (type == "ToolTipInfo") - { - tooltipSize = createToolTip(*focus->getUserData()); - } - 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) - { - 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); - } - info.effects = effects; - tooltipSize = createToolTip(info); - } - else if (type == "Layout") - { - // tooltip defined in the layout - MyGUI::Widget* tooltip; - getWidget(tooltip, focus->getUserString("ToolTipLayout")); + MyGUI::IntSize tooltipSize; - tooltip->setVisible(true); - if (!tooltip->isUserString("DontResize")) + // 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")) { - tooltip->setCoord(0, 0, 450, 300); // this is the maximum width of the tooltip before it starts word-wrapping - - tooltipSize = MyGUI::IntSize(0, tooltip->getSize().height); + 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"); + + 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")); + + tooltip->setVisible(true); - if (w->isUserString("AutoResizeVertical")) + 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(); - // make the tooltip stay completely in the viewport - if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) - { - tooltipPosition.left = viewSize.width - tooltipSize.width; + position(tooltipPosition, tooltipSize, viewSize); + + 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(viewSize.width/2 - tooltipSize.width/2, + std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), + tooltipSize.width, + tooltipSize.height); - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + mDynamicToolTipBox->setVisible(true); + } } } - else - { - if (!mFocusObject.isEmpty()) - { - IntSize tooltipSize = getToolTipViaPtr(); - setCoord(viewSize.width/2 - tooltipSize.width/2, - std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), - tooltipSize.width, - tooltipSize.height); + void ToolTips::position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize) + { + position += MyGUI::IntPoint(0, 32) + - MyGUI::IntPoint((MyGUI::InputManager::getInstance().getMousePosition().left / float(viewportSize.width) * size.width), 0); - mDynamicToolTipBox->setVisible(true); + if ((position.left + size.width) > viewportSize.width) + { + position.left = viewportSize.width - size.width; + } + if ((position.top + size.height) > viewportSize.height) + { + position.top = MyGUI::InputManager::getInstance().getMousePosition().top - size.height - 8; } } -} -void ToolTips::enterGameMode() -{ - mGameMode = true; -} + void ToolTips::setFocusObject(const MWWorld::Ptr& focus) + { + mFocusObject = focus; + } -void ToolTips::enterGuiMode() -{ - mGameMode = false; -} + 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::setFocusObject(const MWWorld::Ptr& focus) -{ - mFocusObject = focus; -} + MyGUI::IntSize tooltipSize; -IntSize ToolTips::getToolTipViaPtr (bool image) -{ - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); + const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); + if (!object.hasToolTip(mFocusObject)) + { + mDynamicToolTipBox->setVisible(false); + } + else + { + mDynamicToolTipBox->setVisible(true); - IntSize tooltipSize; + ToolTipInfo info = object.getToolTipInfo(mFocusObject); + if (!image) + info.icon = ""; + tooltipSize = createToolTip(info); + } - const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); - if (!object.hasToolTip(mFocusObject)) - { - mDynamicToolTipBox->setVisible(false); + return tooltipSize; } - else + + void ToolTips::findImageExtension(std::string& image) { - mDynamicToolTipBox->setVisible(true); + int len = image.size(); + if (len < 4) return; - ToolTipInfo info = object.getToolTipInfo(mFocusObject); - if (!image) - info.icon = ""; - tooltipSize = createToolTip(info); + 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'; + } } - return tooltipSize; -} - -void ToolTips::findImageExtension(std::string& image) -{ - int len = image.size(); - if (len < 4) return; - - if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image)) + MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) { - // Change texture extension to .dds - image[len-3] = 'd'; - image[len-2] = 'd'; - image[len-1] = 's'; - } -} - -IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) -{ - mDynamicToolTipBox->setVisible(true); - - std::string caption = info.caption; - std::string image = info.icon; - int imageSize = (image != "") ? info.imageSize : 0; - std::string text = info.text; + mDynamicToolTipBox->setVisible(true); - // remove the first newline (easier this way) - if (text.size() > 0 && text[0] == '\n') - text.erase(0, 1); + std::string caption = info.caption; + std::string image = info.icon; + int imageSize = (image != "") ? info.imageSize : 0; + std::string text = info.text; - if(caption.size() > 0 && isalnum(caption[0])) - caption[0] = toupper(caption[0]); + // remove the first newline (easier this way) + if (text.size() > 0 && text[0] == '\n') + text.erase(0, 1); - 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}"; - } + 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}"; + } - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); + // this the maximum width of the tooltip before it starts word-wrapping + setCoord(0, 0, 300, 300); - const IntPoint padding(8, 8); - - const int maximumWidth = 500; + const MyGUI::IntPoint padding(8, 8); - const int imageCaptionHPadding = (caption != "" ? 8 : 0); - const int imageCaptionVPadding = (caption != "" ? 4 : 0); + const int maximumWidth = 500; - std::string realImage = "icons\\" + image; - findImageExtension(realImage); + const int imageCaptionHPadding = (caption != "" ? 8 : 0); + const int imageCaptionVPadding = (caption != "" ? 4 : 0); - EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", IntCoord(0, 0, 300, 300), Align::Left | Align::Top, "ToolTipCaption"); - captionWidget->setProperty("Static", "true"); - captionWidget->setCaptionWithReplacing(caption); - IntSize captionSize = captionWidget->getTextSize(); + std::string realImage = "icons\\" + image; + findImageExtension(realImage); - int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); + 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(); - 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", info.wordWrap ? "true" : "false"); - textWidget->setCaptionWithReplacing(text); - textWidget->setTextAlign(Align::HCenter | Align::Top); - IntSize textSize = textWidget->getTextSize(); + int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); - 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 ); + 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(); - 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); - } + 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 ); - 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) + if (!info.effects.empty()) { - /// \todo store the current enchantment charge somewhere - int charge = enchant->mData.mCharge; + MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEffectArea"); - const int chargeWidth = 204; + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); - TextBox* chargeText = enchantArea->createWidget("SandText", IntCoord(0, 0, 10, 18), Align::Default, "ToolTipEnchantChargeText"); - chargeText->setCaptionWithReplacing("#{sCharges}"); + Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEffectsWidget"); + effectsWidget->setEffectList(info.effects); - const int chargeTextWidth = chargeText->getTextSize().width + 5; + 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); + } - const int chargeAndTextWidth = chargeWidth + chargeTextWidth; + if (info.enchant != "") + { + assert(enchant); + MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEnchantArea"); - totalSize.width = std::max(totalSize.width, chargeAndTextWidth); + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); - chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); + Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEnchantWidget"); + enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); - IntCoord chargeCoord; - if (totalSize.width < chargeWidth) - { - totalSize.width = chargeWidth; - chargeCoord = IntCoord(0, coord.top+6, chargeWidth, 18); - } - else + 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) { - chargeCoord = IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); - } - Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget - ("MW_ChargeBar", chargeCoord, Align::Default, "ToolTipEnchantCharge"); - chargeWidget->setValue(charge, charge); - totalSize.height += 24; - } - } + int maxCharge = enchant->mData.mCharge; + int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge; - 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); - } + const int chargeWidth = 204; - textWidget->setPosition (textWidget->getPosition() + IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter + MyGUI::TextBox* chargeText = enchantArea->createWidget("SandText", MyGUI::IntCoord(0, 0, 10, 18), MyGUI::Align::Default, "ToolTipEnchantChargeText"); + chargeText->setCaptionWithReplacing("#{sCharges}"); - 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); - } + const int chargeTextWidth = chargeText->getTextSize().width + 5; - totalSize += IntSize(padding.left*2, padding.top*2); + const int chargeAndTextWidth = chargeWidth + chargeTextWidth; - return totalSize; -} + totalSize.width = std::max(totalSize.width, chargeAndTextWidth); -std::string ToolTips::toString(const float value) -{ - std::ostringstream stream; - stream << std::setprecision(3) << value; - return stream.str(); -} + chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); -std::string ToolTips::toString(const int value) -{ - std::ostringstream stream; - stream << value; - return stream.str(); -} + 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; + } + } -std::string ToolTips::getValueString(const int value, const std::string& prefix) -{ - if (value == 0) - return ""; - else - return "\n" + prefix + ": " + toString(value); -} + captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, + (captionHeight-captionSize.height)/2, + captionSize.width-imageSize, + captionSize.height); -std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) -{ - if (text == "") - return ""; - else - return "\n" + prefix + ": " + text; -} + //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); + } -std::string ToolTips::getCountString(const int value) -{ - if (value == 1) - return ""; - else - return " (" + boost::lexical_cast(value) + ")"; -} + textWidget->setPosition (textWidget->getPosition() + MyGUI::IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter -void ToolTips::toggleFullHelp() -{ - mFullHelp = !mFullHelp; -} + 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); + } -bool ToolTips::getFullHelp() const -{ - return mFullHelp; -} + totalSize += MyGUI::IntSize(padding.left*2, padding.top*2); -void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) -{ - mFocusToolTipX = (min_x + max_x) / 2; - mFocusToolTipY = min_y; -} + return totalSize; + } -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); -} + std::string ToolTips::toString(const float value) + { + std::ostringstream stream; + stream << std::setprecision(3) << value; + return stream.str(); + } -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); -} + std::string ToolTips::toString(const int value) + { + std::ostringstream stream; + stream << value; + return stream.str(); + } -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) + std::string ToolTips::getValueString(const int value, const std::string& prefix) { - if (it->mData.mSpecialization == specId) - specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; + if (value == 0) + return ""; + else + return "\n" + prefix + ": " + toString(value); } - 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(); + std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) + { + if (text == "") + return ""; + else + return "\n" + prefix + ": " + text; + } - const ESM::BirthSign *sign = store.get().find(birthsignId); + std::string ToolTips::getCountString(const int value) + { + if (value == 1) + return ""; + else + return " (" + boost::lexical_cast(value) + ")"; + } - 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; + void ToolTips::toggleFullHelp() + { + mFullHelp = !mFullHelp; + } - text += sign->mName; - text += "\n#BF9959" + sign->mDescription; + bool ToolTips::getFullHelp() const + { + return mFullHelp; + } - std::vector abilities, powers, spells; + void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) + { + mFocusToolTipX = (min_x + max_x) / 2; + mFocusToolTipY = min_y; + } - std::vector::const_iterator it = sign->mPowers.mList.begin(); - std::vector::const_iterator end = sign->mPowers.mList.end(); - for (; it != end; ++it) + void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) { - 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); + 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); } - struct { - const std::vector &spells; - std::string label; + 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); } - categories[3] = { - {abilities, "sBirthsignmenu1"}, - {powers, "sPowers"}, - {spells, "sBirthsignmenu2"} - }; - for (int category = 0; category < 3; ++category) + void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) { - for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) + 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 == categories[category].spells.begin()) - { - text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; - } + 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"); + } + + void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::BirthSign *sign = store.get().find(birthsignId); + 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; + + text += sign->mName; + text += "\n#BF9959" + sign->mDescription; + + std::vector abilities, powers, spells; + + 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); + } + + struct { + const std::vector &spells; + std::string label; + } + categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; + + for (int category = 0; category < 3; ++category) + { + for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) + { + if (it == categories[category].spells.begin()) + { + text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; + } - const ESM::Spell *spell = store.get().find(spellId); - text += "\n#BF9959" + spell->mName; + const std::string &spellId = *it; + + 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 ba94915cc..be5c63191 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -17,6 +17,7 @@ namespace MWGui : isPotion(false) , imageSize(32) , wordWrap(true) + , remainingEnchantCharge(-1) {} std::string caption; @@ -26,6 +27,7 @@ namespace MWGui // enchantment (for cloth, armor, weapons) std::string enchant; + int remainingEnchantCharge; // effects (for potions, ingredients) Widgets::SpellEffectList effects; @@ -37,13 +39,10 @@ namespace MWGui class ToolTips : public OEngine::GUI::Layout { public: - ToolTips(MWBase::WindowManager* windowManager); + ToolTips(); void onFrame(float frameDuration); - void enterGameMode(); - void enterGuiMode(); - void setEnabled(bool enabled); void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) @@ -80,8 +79,6 @@ namespace MWGui private: MyGUI::Widget* mDynamicToolTipBox; - MWBase::WindowManager* mWindowManager; - MWWorld::Ptr mFocusObject; void findImageExtension(std::string& image); @@ -94,9 +91,12 @@ namespace MWGui float mFocusToolTipX; float mFocusToolTipY; - + + /// Adjust position for a tooltip so that it doesn't leave the screen and does not obscure the mouse cursor + void position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize); + int mHorizontalScrollIndex; - + float mDelay; float mRemainingDelay; // remaining time until tooltip will show @@ -104,8 +104,6 @@ namespace MWGui int mLastMouseX; int mLastMouseY; - bool mGameMode; - bool mEnabled; bool mFullHelp; diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp new file mode 100644 index 000000000..e836355d3 --- /dev/null +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -0,0 +1,201 @@ +#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); + if(!mMerchant.isEmpty()) + { + MWWorld::Ptr base = item.mBase; + if(Misc::StringUtils::ciEqual(base.getCellRef().mRefID, "gold_001")) + continue; + if(!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 0fd24601a..94141b1a0 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,28 +19,27 @@ #include "../mwworld/player.hpp" #include "inventorywindow.hpp" +#include "itemview.hpp" +#include "sortfilteritemmodel.hpp" +#include "containeritemmodel.hpp" +#include "tradeitemmodel.hpp" +#include "countdialog.hpp" namespace MWGui { const float TradeWindow::sBalanceChangeInitialPause = 0.5; const float TradeWindow::sBalanceChangeInterval = 0.1; - TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_trade_window.layout", parWindowManager) - , ContainerBase(NULL) // no drag&drop + TradeWindow::TradeWindow() + : WindowBase("openmw_trade_window.layout") , mCurrentBalance(0) , mBalanceButtonsState(BBS_None) , mBalanceChangePause(0.0) + , mItemToSell(-1) + , mTradeModel(NULL) + , mSortModel(NULL) + , mCurrentMerchantOffer(0) { - // items the NPC is wearing should not be for trade - mDisplayEquippedItems = false; - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); @@ -57,6 +57,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); @@ -74,40 +77,42 @@ namespace MWGui 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); @@ -116,26 +121,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::borrowItem (int index, size_t count) + { + TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + mTradeModel->borrowItemToUs(index, playerTradeModel, count); + mItemView->update(); + sellToNpc(playerTradeModel->getItem(index).mBase, count, false); } - void TradeWindow::onWindowResize(MyGUI::Window* _sender) + void TradeWindow::returnItem (int index, size_t count) { - drawItems(); + 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; @@ -150,7 +225,7 @@ namespace MWGui assert(amount > 0); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); ref.getPtr().getRefData().setCount(amount); - playerStore.add(ref.getPtr()); + playerStore.add(ref.getPtr(), player); } } @@ -171,26 +246,28 @@ namespace MWGui 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; } @@ -199,7 +276,7 @@ namespace MWGui { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog2}", std::vector()); + messageBox("#{sBarterDialog2}"); return; } @@ -209,43 +286,45 @@ 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)); - MWMechanics::NpcStats sellerSkill = MWWorld::Class::get(mPtr).getNpcStats(mPtr); - MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(mPtr).getCreatureStats(mPtr); + const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); + const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - float a1 = std::min(playerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c1 = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d1 = std::min(sellerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); float e1 = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f1 = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); 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); @@ -254,14 +333,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) @@ -270,17 +349,14 @@ 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)); - - mWindowManager.removeGuiMode(GM_Barter); + mTradeModel->abort(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->abort(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender) @@ -324,7 +400,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) { @@ -340,66 +416,7 @@ namespace MWGui 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); @@ -409,7 +426,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); @@ -422,8 +439,8 @@ 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() diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 2e05d03d5..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,23 +17,32 @@ 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); - - void startTrade(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 + TradeWindow(); - bool npcAcceptsItem(MWWorld::Ptr item); + void startTrade(const MWWorld::Ptr& actor); void addOrRemoveGold(int gold); void onFrame(float frameDuration); - protected: + 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 @@ -59,6 +65,8 @@ namespace MWGui MyGUI::TextBox* mPlayerGold; MyGUI::TextBox* mMerchantGold; + int mItemToSell; + int mCurrentBalance; int mCurrentMerchantOffer; @@ -70,7 +78,12 @@ namespace MWGui /// pause before next balance change will trigger while user holds +/- button pressed float mBalanceChangePause; - void onWindowResize(MyGUI::Window* _sender); + 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); @@ -82,16 +95,10 @@ namespace MWGui void onIncreaseButtonTriggered(); void onDecreaseButtonTriggered(); - virtual bool isTrading() { return true; } - virtual bool isTradeWindow() { return true; } - - virtual std::vector itemsToIgnore(); - void updateLabels(); virtual void onReferenceUnavailable(); - private: 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 465f588b8..93ac8299d 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"); @@ -106,8 +100,6 @@ namespace MWGui mPtr = actor; clearDestinations(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - for(unsigned int i = 0;i()->mBase->mTransport.size();i++) { std::string cellname = mPtr.get()->mBase->mTransport[i].mCellName; @@ -129,10 +121,10 @@ namespace MWGui int price; iss >> price; - if (mWindowManager.getInventoryWindow()->getPlayerGold()getInventoryWindow()->getPlayerGold()addOrRemoveGold (-price); + MWBase::Environment::get().getWindowManager()->getTradeWindow ()->addOrRemoveGold (-price); MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -141,35 +133,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, @@ -178,8 +173,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 794a9ab55..2467f6c40 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -1,7 +1,5 @@ #include "waitdialog.hpp" -#include - #include #include @@ -11,22 +9,18 @@ #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" - 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"); @@ -46,26 +40,27 @@ 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) , mRemainingTime(0.05) + , mCurHour(0) + , mManualHours(1) { getWidget(mDateTimeText, "DateTimeText"); getWidget(mRestText, "RestText"); getWidget(mHourText, "HourText"); - getWidget(mHourSlider, "HourSlider"); getWidget(mUntilHealedButton, "UntilHealedButton"); getWidget(mWaitButton, "WaitButton"); getWidget(mCancelButton, "CancelButton"); + getWidget(mHourSlider, "HourSlider"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onCancelButtonClicked); mUntilHealedButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onUntilHealedButtonClicked); mWaitButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onWaitButtonClicked); - mHourSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &WaitDialog::onHourSliderChangedPosition); mProgressBar.setVisible (false); @@ -75,7 +70,7 @@ namespace MWGui { if (!MWBase::Environment::get().getWindowManager ()->getRestEnabled ()) { - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } int canRest = MWBase::Environment::get().getWorld ()->canRest (); @@ -83,8 +78,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); @@ -132,7 +127,7 @@ namespace MWGui case 11: month = "#{sMonthEveningstar}"; break; - default: + default: break; } int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour (); @@ -142,7 +137,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); @@ -156,13 +151,13 @@ namespace MWGui 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(136)).mMagnitude > 0); + + 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(); @@ -174,7 +169,7 @@ namespace MWGui 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; @@ -185,9 +180,9 @@ namespace MWGui 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); } @@ -201,18 +196,18 @@ namespace MWGui 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) @@ -223,9 +218,19 @@ namespace MWGui void WaitDialog::setCanRest (bool canRest) { - mUntilHealedButton->setVisible(canRest); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified()) + && (stats.getHealth().getCurrent() >= stats.getHealth().getModified()) + && (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified()); + MWMechanics::NpcStats& npcstats = MWWorld::Class::get(player).getNpcStats(player); + bool werewolf = npcstats.isWerewolf(); + + mUntilHealedButton->setVisible(canRest && !full); mWaitButton->setCaptionWithReplacing (canRest ? "#{sRest}" : "#{sWait}"); - mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : "#{sRestIllegal}"); + mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" + : (werewolf ? "#{sWerewolfRestMessage}" + : "#{sRestIllegal}")); mSleeping = canRest; @@ -263,17 +268,17 @@ namespace MWGui { 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(); - MWMechanics::NpcStats pcstats = MWWorld::Class::get(player).getNpcStats(player); + const MWMechanics::NpcStats &pcstats = MWWorld::Class::get(player).getNpcStats(player); // 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 c102d0fc6..2723f7a80 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -1,7 +1,8 @@ #ifndef MWGUI_WAIT_DIALOG_H #define MWGUI_WAIT_DIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" +#include "widgets.hpp" namespace MWGui { @@ -9,7 +10,7 @@ namespace MWGui class WaitDialogProgressBar : public WindowBase { public: - WaitDialogProgressBar(MWBase::WindowManager& parWindowManager); + WaitDialogProgressBar(); virtual void open(); @@ -23,7 +24,7 @@ namespace MWGui class WaitDialog : public WindowBase { public: - WaitDialog(MWBase::WindowManager& parWindowManager); + WaitDialog(); virtual void open(); @@ -38,10 +39,10 @@ namespace MWGui MyGUI::TextBox* mDateTimeText; MyGUI::TextBox* mRestText; MyGUI::TextBox* mHourText; - MyGUI::ScrollBar* mHourSlider; MyGUI::Button* mUntilHealedButton; MyGUI::Button* mWaitButton; MyGUI::Button* mCancelButton; + MWGui::Widgets::MWScrollBar* mHourSlider; bool mWaiting; bool mSleeping; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 506b90698..dea64ae8c 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -4,878 +4,1004 @@ #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) +namespace MWGui { - int offset = path.rfind("."); - if (offset < 0) - return; - path.replace(offset, path.length() - offset, ".dds"); -} + namespace Widgets + { -/* MWSkill */ + /* Helper functions */ -MWSkill::MWSkill() - : mManager(NULL) - , mSkillId(ESM::Skill::Length) - , mSkillNameWidget(NULL) - , mSkillValueWidget(NULL) -{ -} + /* + * 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::setSkillId(ESM::Skill::SkillEnum skill) -{ - mSkillId = skill; - updateWidgets(); -} + /* 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"); -} + MWSkill::MWSkill() + : mSkillId(ESM::Skill::Length) + , mSkillNameWidget(NULL) + , mSkillValueWidget(NULL) + { + } -void MWSkill::setSkillValue(const SkillValue& value) -{ - mValue = value; - updateWidgets(); -} + void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) + { + mSkillId = skill; + updateWidgets(); + } -void MWSkill::updateWidgets() -{ - if (mSkillNameWidget && mManager) - { - if (mSkillId == ESM::Skill::Length) + void MWSkill::setSkillNumber(int skill) { - static_cast(mSkillNameWidget)->setCaption(""); + 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"); } - else + + void MWSkill::setSkillValue(const SkillValue& value) { - const std::string &name = mManager->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); - static_cast(mSkillNameWidget)->setCaption(name); + mValue = value; + 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); -} + 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"); + } + } -MWSkill::~MWSkill() -{ -} + void MWSkill::onClicked(MyGUI::Widget* _sender) + { + eventClicked(this); + } -void MWSkill::initialiseOverride() -{ - Base::initialiseOverride(); + MWSkill::~MWSkill() + { + } - assignWidget(mSkillNameWidget, "StatName"); - assignWidget(mSkillValueWidget, "StatValue"); + void MWSkill::initialiseOverride() + { + Base::initialiseOverride(); - MyGUI::Button* button; - assignWidget(button, "StatNameButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } + assignWidget(mSkillNameWidget, "StatName"); + assignWidget(mSkillValueWidget, "StatValue"); - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } -} + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } -/* MWAttribute */ + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } + } -MWAttribute::MWAttribute() - : mManager(NULL) - , mId(-1) - , mAttributeNameWidget(NULL) - , mAttributeValueWidget(NULL) -{ -} + /* MWAttribute */ -void MWAttribute::setAttributeId(int attributeId) -{ - mId = attributeId; - updateWidgets(); -} + MWAttribute::MWAttribute() + : mId(-1) + , mAttributeNameWidget(NULL) + , mAttributeValueWidget(NULL) + { + } -void MWAttribute::setAttributeValue(const AttributeValue& value) -{ - mValue = value; - updateWidgets(); -} + void MWAttribute::setAttributeId(int attributeId) + { + mId = attributeId; + updateWidgets(); + } -void MWAttribute::onClicked(MyGUI::Widget* _sender) -{ - eventClicked(this); -} + void MWAttribute::setAttributeValue(const AttributeValue& value) + { + mValue = value; + updateWidgets(); + } -void MWAttribute::updateWidgets() -{ - if (mAttributeNameWidget && mManager) - { - if (mId < 0 || mId >= 8) + void MWAttribute::onClicked(MyGUI::Widget* _sender) { - static_cast(mAttributeNameWidget)->setCaption(""); + eventClicked(this); } - else + + void MWAttribute::updateWidgets() { - 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); + 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"); + } } - } - 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() -{ -} + MWAttribute::~MWAttribute() + { + } -void MWAttribute::initialiseOverride() -{ - Base::initialiseOverride(); + void MWAttribute::initialiseOverride() + { + Base::initialiseOverride(); - assignWidget(mAttributeNameWidget, "StatName"); - assignWidget(mAttributeValueWidget, "StatValue"); + assignWidget(mAttributeNameWidget, "StatName"); + assignWidget(mAttributeValueWidget, "StatValue"); - MyGUI::Button* button; - assignWidget(button, "StatNameButton"); - if (button) - { - mAttributeNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mAttributeNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mAttributeValueWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } -} + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mAttributeValueWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } + } -/* MWSpell */ + /* MWSpell */ -MWSpell::MWSpell() - : mWindowManager(NULL) - , mSpellNameWidget(NULL) -{ -} + MWSpell::MWSpell() + : mSpellNameWidget(NULL) + { + } -void MWSpell::setSpellId(const std::string &spellId) -{ - mId = spellId; - updateWidgets(); -} + void MWSpell::setSpellId(const std::string &spellId) + { + mId = spellId; + updateWidgets(); + } -void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) -{ - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); + void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); - const ESM::Spell *spell = store.get().search(mId); - MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); + const ESM::Spell *spell = store.get().search(mId); + MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); - 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); - 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()); - } -} + 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()); + } + } -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::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(""); + } + } -void MWSpell::initialiseOverride() -{ - Base::initialiseOverride(); + void MWSpell::initialiseOverride() + { + Base::initialiseOverride(); - assignWidget(mSpellNameWidget, "StatName"); -} + assignWidget(mSpellNameWidget, "StatName"); + } -MWSpell::~MWSpell() -{ -} + MWSpell::~MWSpell() + { + } -/* MWEffectList */ + /* MWEffectList */ -MWEffectList::MWEffectList() - : mWindowManager(NULL) - , mEffectList(0) -{ -} + MWEffectList::MWEffectList() + : mEffectList(0) + { + } -void MWEffectList::setEffectList(const SpellEffectList& list) -{ - mEffectList = list; - updateWidgets(); -} + void MWEffectList::setEffectList(const SpellEffectList& list) + { + mEffectList = list; + updateWidgets(); + } -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; + 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; - 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(); - } + 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(); + } - // ... 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) + // ... 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); + } + } + + // inform the parent about width + coord.width = maxwidth; + } + + void MWEffectList::updateWidgets() { - effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); } - else + + void MWEffectList::initialiseOverride() { - effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + Base::initialiseOverride(); } - } - // inform the parent about width - coord.width = maxwidth; -} + MWEffectList::~MWEffectList() + { + } -void MWEffectList::updateWidgets() -{ -} + 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; + } -void MWEffectList::initialiseOverride() -{ - Base::initialiseOverride(); -} + /* MWSpellEffect */ -MWEffectList::~MWEffectList() -{ -} + MWSpellEffect::MWSpellEffect() + : mImageWidget(NULL) + , mTextWidget(NULL) + , mRequestedWidth(0) + { + } -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; -} + void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) + { + mEffectParams = params; + updateWidgets(); + } -/* MWSpellEffect */ + void MWSpellEffect::updateWidgets() + { + if (!mEffectParams.mKnown) + { + mTextWidget->setCaption ("?"); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + mImageWidget->setImageTexture (""); + return; + } -MWSpellEffect::MWSpellEffect() - : mWindowManager(NULL) - , mImageWidget(NULL) - , mTextWidget(NULL) - , mRequestedWidth(0) -{ -} + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); -void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) -{ - mEffectParams = params; - updateWidgets(); -} + const ESM::MagicEffect *magicEffect = + store.get().search(mEffectParams.mEffectID); -void MWSpellEffect::updateWidgets() -{ - if (!mEffectParams.mKnown) - { - mTextWidget->setCaption ("?"); - mRequestedWidth = mTextWidget->getTextSize().width + 24; - mImageWidget->setImageTexture (""); - return; - } + assert(magicEffect); - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); + 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", ""); - const ESM::MagicEffect *magicEffect = - store.get().search(mEffectParams.mEffectID); + std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); + std::string spellLine = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, ""); - assert(magicEffect); + 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], ""); + } - 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", ""); + 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; + } + } - std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); - std::string spellLine = mWindowManager->getGameSettingString(effectIDStr, ""); + // 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 (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) - { - spellLine += " " + mWindowManager->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); - } - if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) - { - spellLine += " " + mWindowManager->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); - } + static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); + mRequestedWidth = mTextWidget->getTextSize().width + 24; - 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; + std::string path = std::string("icons\\") + magicEffect->mIcon; + fixTexturePath(path); + mImageWidget->setImageTexture(path); } - } - // constant effects have no duration and no target - if (!mEffectParams.mIsConstant) - { - if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + MWSpellEffect::~MWSpellEffect() { - spellLine += " " + mWindowManager->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); } - if (mEffectParams.mArea > 0) + void MWSpellEffect::initialiseOverride() { - spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; + Base::initialiseOverride(); + + assignWidget(mTextWidget, "Text"); + assignWidget(mImageWidget, "Image"); } - // potions have no target - if (!mEffectParams.mNoTarget) + /* MWDynamicStat */ + + MWDynamicStat::MWDynamicStat() + : mValue(0) + , mMax(1) + , mTextWidget(NULL) + , mBarWidget(NULL) + , mBarTextWidget(NULL) { - 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", ""); } - } - static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); - mRequestedWidth = mTextWidget->getTextSize().width + 24; + void MWDynamicStat::setValue(int cur, int max) + { + mValue = cur; + mMax = max; - std::string path = std::string("icons\\") + magicEffect->mIcon; - fixTexturePath(path); - mImageWidget->setImageTexture(path); -} + if (mBarWidget) + { + mBarWidget->setProgressRange(mMax); + mBarWidget->setProgressPosition(mValue); + } -MWSpellEffect::~MWSpellEffect() -{ -} -void MWSpellEffect::initialiseOverride() -{ - Base::initialiseOverride(); + if (mBarTextWidget) + { + if (mValue >= 0 && mMax > 0) + { + std::stringstream out; + out << mValue << "/" << mMax; + static_cast(mBarTextWidget)->setCaption(out.str().c_str()); + } + else + static_cast(mBarTextWidget)->setCaption(""); + } + } + void MWDynamicStat::setTitle(const std::string& text) + { + if (mTextWidget) + static_cast(mTextWidget)->setCaption(text); + } - assignWidget(mTextWidget, "Text"); - assignWidget(mImageWidget, "Image"); -} + MWDynamicStat::~MWDynamicStat() + { + } -/* MWDynamicStat */ + void MWDynamicStat::initialiseOverride() + { + Base::initialiseOverride(); -MWDynamicStat::MWDynamicStat() -: mValue(0) -, mMax(1) -, mTextWidget(NULL) -, mBarWidget(NULL) -, mBarTextWidget(NULL) -{ -} + assignWidget(mTextWidget, "Text"); + assignWidget(mBarWidget, "Bar"); + assignWidget(mBarTextWidget, "BarText"); + } -void MWDynamicStat::setValue(int cur, int max) -{ - mValue = cur; - mMax = max; - if (mBarWidget) - { - mBarWidget->setProgressRange(mMax); - mBarWidget->setProgressPosition(mValue); - } - if (mBarTextWidget) - { - if (mValue >= 0 && mMax > 0) + // --------------------------------------------------------------------------------------------------------------------- + + void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) { - std::stringstream out; - out << mValue << "/" << mMax; - static_cast(mBarTextWidget)->setCaption(out.str().c_str()); + 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 ()); + } + } } - else - static_cast(mBarTextWidget)->setCaption(""); - } -} -void MWDynamicStat::setTitle(const std::string& text) -{ - if (mTextWidget) - static_cast(mTextWidget)->setCaption(text); -} -MWDynamicStat::~MWDynamicStat() -{ -} -void MWDynamicStat::initialiseOverride() -{ - Base::initialiseOverride(); + MyGUI::IntSize AutoSizedTextBox::getRequestedSize() + { + return getTextSize(); + } - assignWidget(mTextWidget, "Text"); - assignWidget(mBarWidget, "Bar"); - assignWidget(mBarTextWidget, "BarText"); -} + void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) + { + TextBox::setCaption(_value); + notifySizeChange (this); + } + void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + TextBox::setPropertyOverride (_key, _value); + } + } + + MyGUI::IntSize AutoSizedEditBox::getRequestedSize() + { + if (getAlign().isHStretch()) + throw std::runtime_error("AutoSizedEditBox can't have HStretch align (" + getName() + ")"); + return MyGUI::IntSize(getSize().width, getTextSize().height); + } + void AutoSizedEditBox::setCaption(const MyGUI::UString& _value) + { + EditBox::setCaption(_value); -// --------------------------------------------------------------------------------------------------------------------- + notifySizeChange (this); + } -void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) -{ - if (w->getParent () != 0) - { - Box* b = dynamic_cast(w->getParent()); - if (b) - b->notifyChildrenSizeChanged (); - else + void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value) { - if (mExpandDirection == MyGUI::Align::Left) + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else { - int hdiff = getRequestedSize ().width - w->getSize().width; - w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + EditBox::setPropertyOverride (_key, _value); } - w->setSize(getRequestedSize ()); } - } -} -MyGUI::IntSize AutoSizedTextBox::getRequestedSize() -{ - return getTextSize(); -} + MyGUI::IntSize AutoSizedButton::getRequestedSize() + { + MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); + size.height = std::max(24, size.height); + return size; + } -void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) -{ - TextBox::setCaption(_value); + void AutoSizedButton::setCaption(const MyGUI::UString& _value) + { + Button::setCaption(_value); - notifySizeChange (this); -} + notifySizeChange (this); + } -void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - TextBox::setPropertyOverride (_key, _value); - } -} + void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + Button::setPropertyOverride (_key, _value); + } + } + Box::Box() + : mSpacing(4) + , mPadding(0) + , mAutoResize(false) + { -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); + void Box::notifyChildrenSizeChanged () + { + align(); + } - notifySizeChange (this); -} + 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); + } -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::align () + { + unsigned int count = getChildCount (); + size_t h_stretched_count = 0; + int total_width = 0; + int total_height = 0; + std::vector< std::pair > sizes; -Box::Box() - : mSpacing(4) - , mPadding(0) - , mAutoResize(false) -{ + 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; + } -void Box::notifyChildrenSizeChanged () -{ - align(); -} -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); -} + 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 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 HBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + Box::_setPropertyImpl (_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) + void HBox::setSize (const MyGUI::IntSize& _value) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); - total_width += aw->getRequestedSize ().width; - total_height = std::max(total_height, aw->getRequestedSize ().height); + MyGUI::Widget::setSize (_value); + align(); } - else + + void HBox::setCoord (const MyGUI::IntCoord& _value) { - sizes.push_back (std::make_pair(w->getSize(), hstretch)); - total_width += w->getSize().width; + MyGUI::Widget::setCoord (_value); + align(); } - if (i != count-1) - total_width += mSpacing; - } + void HBox::onWidgetCreated(MyGUI::Widget* _widget) + { + align(); + } - 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 HBox::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.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; + } - 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 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 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 HBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} + 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 HBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} -void HBox::onWidgetDestroy(MyGUI::Widget* _widget) -{ - 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 HBox::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.height = std::max(size.height, requested.height); - size.width = size.width + requested.width; - if (i != getChildCount()-1) - size.width += mSpacing; + Box::_setPropertyImpl (_key, _value); } - 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; + void VBox::setSize (const MyGUI::IntSize& _value) + { + MyGUI::Widget::setSize (_value); + align(); + } - if (i != getChildCount()-1) - size.width += mSpacing; + void VBox::setCoord (const MyGUI::IntCoord& _value) + { + MyGUI::Widget::setCoord (_value); + align(); } - size.height += mPadding*2; - size.width += mPadding*2; - } - return size; -} + 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::onWidgetCreated(MyGUI::Widget* _widget) + { + align(); + } + MWScrollBar::MWScrollBar() + : mEnableRepeat(true) + , mRepeatTriggerTime(0.5) + , mRepeatStepTime(0.1) + , mIsIncreasing(true) + { + } -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) + MWScrollBar::~MWScrollBar() { - sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); - total_height += aw->getRequestedSize ().height; - total_width = std::max(total_width, aw->getRequestedSize ().width); } - else + + void MWScrollBar::initialiseOverride() { - sizes.push_back (std::make_pair(w->getSize(), vstretch)); - total_height += w->getSize().height; + ScrollBar::initialiseOverride(); + + if(mWidgetStart) + { + mWidgetStart->eventMouseButtonPressed += MyGUI::newDelegate(this, &MWScrollBar::onDecreaseButtonPressed); + mWidgetStart->eventMouseButtonReleased += MyGUI::newDelegate(this, &MWScrollBar::onDecreaseButtonReleased); + } + if(mWidgetEnd) + { + mWidgetEnd->eventMouseButtonPressed += MyGUI::newDelegate(this, &MWScrollBar::onIncreaseButtonPressed); + mWidgetEnd->eventMouseButtonReleased += MyGUI::newDelegate(this, &MWScrollBar::onIncreaseButtonReleased); + } } - if (i != count-1) - total_height += mSpacing; - } + void MWScrollBar::setEnableRepeat(bool enable) + { + mEnableRepeat = enable; + } - 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; - } + bool MWScrollBar::getEnableRepeat() + { + return mEnableRepeat; + } + void MWScrollBar::getRepeat(float &trigger, float &step) + { + trigger = mRepeatTriggerTime; + step = mRepeatStepTime; + } - 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 MWScrollBar::setRepeat(float trigger, float step) + { + mRepeatTriggerTime = trigger; + mRepeatStepTime = step; + } -void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - Box::_setPropertyImpl (_key, _value); -} + void MWScrollBar::repeatClick(MyGUI::Widget* _widget, MyGUI::ControllerItem* _controller) + { + int stepSize = mScrollPage; -void VBox::setSize (const MyGUI::IntSize& _value) -{ - MyGUI::Widget::setSize (_value); - align(); -} + if(mIsIncreasing && mScrollPosition < mScrollRange-1) + { + if(mScrollPosition + stepSize > mScrollRange-1) + mScrollPosition = mScrollRange-1; + else + mScrollPosition += stepSize; -void VBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} + eventScrollChangePosition(this, mScrollPosition); + updateTrack(); + } + else if(!mIsIncreasing && mScrollPosition > 0) + { + int newPos = mScrollPosition - stepSize; + if(newPos < 0) + mScrollPosition = 0; + else + mScrollPosition -= stepSize; + + eventScrollChangePosition(this, mScrollPosition); + updateTrack(); + } + } -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 MWScrollBar::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { - 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; + mIsIncreasing = false; + MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(MWGui::Controllers::ControllerRepeatClick::getClassTypeName()); + MWGui::Controllers::ControllerRepeatClick* controller = item->castType(); + controller->eventRepeatClick += newDelegate(this, &MWScrollBar::repeatClick); + controller->setEnabled(mEnableRepeat); + controller->setRepeat(mRepeatTriggerTime, mRepeatStepTime); + MyGUI::ControllerManager::getInstance().addItem(this, controller); } - else + + void MWScrollBar::onDecreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { - MyGUI::IntSize requested = getChildAt(i)->getSize (); - size.width = std::max(size.width, requested.width); + MyGUI::ControllerManager::getInstance().removeItem(this); + } - if (getChildAt(i)->getUserString("VStretch") != "true") - size.height = size.height + requested.height; + void MWScrollBar::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + mIsIncreasing = true; + MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(MWGui::Controllers::ControllerRepeatClick::getClassTypeName()); + MWGui::Controllers::ControllerRepeatClick* controller = item->castType(); + controller->eventRepeatClick += newDelegate(this, &MWScrollBar::repeatClick); + controller->setEnabled(mEnableRepeat); + controller->setRepeat(mRepeatTriggerTime, mRepeatStepTime); + MyGUI::ControllerManager::getInstance().addItem(this, controller); + } - if (i != getChildCount()-1) - size.height += mSpacing; + void MWScrollBar::onIncreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + MyGUI::ControllerManager::getInstance().removeItem(this); } - size.height += mPadding*2; - size.width += mPadding*2; } - return size; -} - -void VBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} - -void VBox::onWidgetDestroy(MyGUI::Widget* _widget) -{ - align(); } diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 597bcbe32..1630ab3c9 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -3,10 +3,11 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/stat.hpp" +#include "controllers.hpp" -#include -#include #include +#include +#include namespace MyGUI { @@ -93,12 +94,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; } @@ -121,7 +120,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; ESM::Skill::SkillEnum mSkillId; SkillValue mValue; MyGUI::Widget* mSkillNameWidget; @@ -137,11 +135,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; } @@ -164,7 +160,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; int mId; AttributeValue mValue; MyGUI::Widget* mAttributeNameWidget; @@ -184,7 +179,6 @@ namespace MWGui typedef MWMechanics::Stat SpellValue; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellId(const std::string &id); /** @@ -206,7 +200,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; std::string mId; MyGUI::TextBox* mSpellNameWidget; }; @@ -226,7 +219,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); @@ -248,7 +240,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; @@ -261,7 +252,6 @@ namespace MWGui typedef ESM::ENAMstruct SpellEffectValue; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellEffect(const SpellEffectParams& params); int getRequestedWidth() const { return mRequestedWidth; } @@ -275,7 +265,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; @@ -340,6 +329,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 ) @@ -390,7 +391,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 @@ -408,7 +408,35 @@ 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 MWScrollBar : public MyGUI::ScrollBar + { + MYGUI_RTTI_DERIVED(MWScrollBar) + + public: + MWScrollBar(); + virtual ~MWScrollBar(); + + void setEnableRepeat(bool enable); + bool getEnableRepeat(); + void getRepeat(float &trigger, float &step); + void setRepeat(float trigger, float step); + + protected: + virtual void initialiseOverride(); + void repeatClick(MyGUI::Widget* _widget, MyGUI::ControllerItem* _controller); + + bool mEnableRepeat; + float mRepeatTriggerTime; + float mRepeatStepTime; + bool mIsIncreasing; + + private: + void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onDecreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onIncreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); }; } } diff --git a/apps/openmw/mwgui/window_pinnable_base.cpp b/apps/openmw/mwgui/window_pinnable_base.cpp deleted file mode 100644 index 651b3a1e9..000000000 --- a/apps/openmw/mwgui/window_pinnable_base.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "window_pinnable_base.hpp" - -#include "../mwbase/windowmanager.hpp" - -#include "exposedwindow.hpp" - -using namespace MWGui; - -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) - : WindowBase(parLayout, parWindowManager), 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_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 0e4c3a608..bf8b664da 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -3,37 +3,30 @@ #include #include -#include +#include "MyGUI_UString.h" +#include "MyGUI_IPointer.h" +#include "MyGUI_ResourceImageSetPointer.h" +#include "MyGUI_TextureUtility.h" #include #include -#include -#include +#include -#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" @@ -48,787 +41,898 @@ #include "loadingscreen.hpp" #include "levelupdialog.hpp" #include "waitdialog.hpp" -#include "spellcreationdialog.hpp" #include "enchantingdialog.hpp" #include "trainingwindow.hpp" -#include "imagebutton.hpp" #include "exposedwindow.hpp" #include "cursor.hpp" -#include "spellicons.hpp" - -using namespace MWGui; - -WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *ogre, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage) - : 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) - , 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) -{ - // Set up the GUI system - mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); - mGui = mGuiManager->getGui(); - - //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"); - - 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(*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(mRendering->getScene (), mRendering->getWindow (), *this); - mLoadingScreen->onResChange (w,h); - - mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); - - mCursor = new Cursor(); - - mHud->setVisible(mHudEnabled); - - 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(); - - if (newGame) - disallowAll (); - - // Set up visibility - updateVisible(); -} - -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 mCursor; - - cleanupGarbage(); - - delete mGuiManager; -} +#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) + , mConsoleOnlyScripts(consoleOnlyScripts) + , 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) + , mForceHidden(GW_None) + , 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")) + , mCursorVisible(true) + { + // 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"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + BookPage::registerMyGUIComponents (); + ItemView::registerComponents(); + + MyGUI::FactoryManager::getInstance().registerFactory("Controller"); + + 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 + int w = MyGUI::RenderManager::getInstance().getViewSize().width; + int h = MyGUI::RenderManager::getInstance().getViewSize().height; + + mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ()); + mLoadingScreen->onResChange (w,h); + + //set up the hardware cursor manager + mSoftwareCursor = new Cursor(); + mCursorManager = new SFO::SDLCursorManager(); + + MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange); + + MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); + + setUseHardwareCursors(mUseHardwareCursors); + onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); + mCursorManager->cursorVisibilityChange(false); + } -void WindowManager::cleanupGarbage() -{ - // Delete any dialogs which are no longer in use - if (!mGarbageDialogs.empty()) + void WindowManager::initUI() { - for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + // Get size info from the Gui object + 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(""); + mStatsWindow = new StatsWindow(); + mConsole = new Console(w,h, mConsoleOnlyScripts); + 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); + + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + + mHud->setVisible(mHudEnabled); + + mCharGen = new CharacterCreation(); + + // Setup player stats + for (int i = 0; i < ESM::Attribute::Length; ++i) { - delete *it; + mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); } - mGarbageDialogs.clear(); - } -} -void WindowManager::update() -{ - cleanupGarbage(); + for (int i = 0; i < ESM::Skill::Length; ++i) + { + mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); + } - mHud->setFPS(mFPS); - mHud->setTriangleCount(mTriangleCount); - mHud->setBatchCount(mBatchCount); + unsetSelectedSpell(); + unsetSelectedWeapon(); - mHud->update(); + // Set up visibility + updateVisible(); - mCursor->update(); -} + MWBase::Environment::get().getInputManager()->changeInputMode(false); + } -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(mHudEnabled); - - // Mouse is visible whenever we're not in game mode - mCursor->setVisible(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, 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: + void WindowManager::renderWorldMap() + { + mMap->renderGlobalMap(mLoadingScreen); + } + + void WindowManager::setNewGame(bool newgame) + { + if (newgame) { - // 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; + disallowAll(); + delete mCharGen; + mCharGen = new CharacterCreation(); + mGuiModes.clear(); + mHud->unsetSelectedWeapon(); + mHud->unsetSelectedSpell(); + unsetForceHide(GW_ALL); } - 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); - mCursor->setVisible(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()); - - mCursor->setVisible(false); - break; - case GM_Video: - mCursor->setVisible(false); - mHud->setVisible(false); - break; - default: - // Unsupported mode, switch back to game - break; - } -} + else + allow(GW_ALL); -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; + mRestAllowed = !newgame; } -} + 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::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::cleanupGarbage() + { + // Delete any dialogs which are no longer in use + if (!mGarbageDialogs.empty()) + { + for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + { + delete *it; + } + mGarbageDialogs.clear(); + } + } -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") + void WindowManager::update() { - mPlayerHealth = value; - mCharGen->setPlayerHealth (value); + cleanupGarbage(); + + mHud->setFPS(mFPS); + mHud->setTriangleCount(mTriangleCount); + mHud->setBatchCount(mBatchCount); + + mHud->update(); + + mSoftwareCursor->update(); } - else if (id == "MBar") + + void WindowManager::updateVisible() { - mPlayerMagicka = value; - mCharGen->setPlayerMagicka (value); + if (!mMap) + return; // UI not created yet + // Start out by hiding everything except the HUD + mMap->setVisible(false); + mMenu->setVisible(false); + mStatsWindow->setVisible(false); + mConsole->setVisible(false); + 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) + setKeyFocusWidget (NULL); + + // Icons of forced hidden windows are displayed + setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); + setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory))); + setSpellVisibility((mAllowed & GW_Magic) && (!mSpellWindow->pinned() || (mForceHidden & GW_Magic))); + setHMSVisibility((mAllowed & GW_Stats) && (!mStatsWindow->pinned() || (mForceHidden & GW_Stats))); + + // If in game mode, show only the pinned windows + if (gameMode) + { + mInventoryWindow->setGuiMode(GM_None); + mMap->setVisible(mMap->pinned() && !(mForceHidden & GW_Map)); + mStatsWindow->setVisible(mStatsWindow->pinned() && !(mForceHidden & GW_Stats)); + mInventoryWindow->setVisible(mInventoryWindow->pinned() && !(mForceHidden & GW_Inventory)); + mSpellWindow->setVisible(mSpellWindow->pinned() && !(mForceHidden & GW_Magic)); + + 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->setVisible(true); + 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 & ~mForceHidden; + + // Show the windows we want + mMap ->setVisible(eff & GW_Map); + mStatsWindow ->setVisible(eff & GW_Stats); + mInventoryWindow->setVisible(eff & GW_Inventory); + mInventoryWindow->setGuiMode(mode); + mSpellWindow ->setVisible(eff & GW_Magic); + break; + } + case GM_Container: + mContainerWindow->setVisible(true); + mInventoryWindow->setVisible(true); + mInventoryWindow->setGuiMode(mode); + break; + case GM_Companion: + mCompanionWindow->setVisible(true); + mInventoryWindow->setVisible(true); + mInventoryWindow->setGuiMode(mode); + break; + case GM_Dialogue: + mDialogueWindow->setVisible(true); + break; + case GM_Barter: + mInventoryWindow->setVisible(true); + mInventoryWindow->setTrading(true); + mInventoryWindow->setGuiMode(mode); + 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() && !(mForceHidden & GW_Map)); + mStatsWindow->setVisible(mStatsWindow->pinned() && !(mForceHidden & GW_Stats)); + mInventoryWindow->setVisible(mInventoryWindow->pinned() && !(mForceHidden & GW_Inventory)); + mSpellWindow->setVisible(mSpellWindow->pinned() && !(mForceHidden & GW_Magic)); + + 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::setDrowningTimeLeft (float time) + { + mHud->setDrowningTimeLeft(time); + } -void WindowManager::updateSkillArea() -{ - mStatsWindow->updateSkillArea(); -} + void WindowManager::setPlayerClass (const ESM::Class &class_) + { + mStatsWindow->setValue("class", class_.mName); + } -void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) -{ - if (!dialog) - return; - dialog->setVisible(false); - mGarbageDialogs.push_back(dialog); -} + void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) + { + mStatsWindow->configureSkills (major, minor); + mCharGen->configureSkills(major, minor); + mPlayerMajorSkills = major; + mPlayerMinorSkills = minor; + } -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::setReputation (int reputation) + { + mStatsWindow->setReputation (reputation); } - else + void WindowManager::setBounty (int bounty) { - mMessageBoxManager->createInteractiveMessageBox(message, buttons); - pushGuiMode(GM_InterMessageBox); + mStatsWindow->setBounty (bounty); } -} -void WindowManager::enterPressed () -{ - mMessageBoxManager->enterPressed(); -} + void WindowManager::updateSkillArea() + { + 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->mValue.getType()==ESM::VT_String) - return setting->mValue.getString(); + void WindowManager::staticMessageBox(const std::string& message) + { + mMessageBoxManager->createMessageBox(message, true); + } - return default_; -} + void WindowManager::removeStaticMessageBox() + { + mMessageBoxManager->removeStaticMessageBox(); + } -void WindowManager::onDialogueWindowBye() -{ - if (mDialogueWindow) + void WindowManager::enterPressed () { - //FIXME set some state and stuff? - //removeDialog(dialogueWindow); - mDialogueWindow->setVisible(false); + mMessageBoxManager->okayPressed(); } - removeGuiMode(GM_Dialogue); -} -void WindowManager::onFrame (float frameDuration) -{ - mMessageBoxManager->onFrame(frameDuration); - mToolTips->onFrame(frameDuration); + void WindowManager::activateKeyPressed () + { + mMessageBoxManager->okayPressed(); + mCountDialog->cancel(); + } - 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); - mInventoryWindow->onFrame(); + if (setting && setting->mValue.getType()==ESM::VT_String) + return setting->mValue.getString(); - mStatsWindow->onFrame(); + return default_; + } - mWaitDialog->onFrame(frameDuration); + void WindowManager::onFrame (float frameDuration) + { + mMessageBoxManager->onFrame(frameDuration); - mHud->onFrame(frameDuration); + mToolTips->onFrame(frameDuration); - mTrainingWindow->onFrame (frameDuration); - mTradeWindow->onFrame(frameDuration); + if (mDragAndDrop->mIsOnDragAndDrop) + { + assert(mDragAndDrop->mDraggedWidget); + mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); + } - mTrainingWindow->checkReferenceAvailable(); - mDialogueWindow->checkReferenceAvailable(); - mTradeWindow->checkReferenceAvailable(); - mSpellBuyingWindow->checkReferenceAvailable(); - mSpellCreationDialog->checkReferenceAvailable(); - mEnchantingDialog->checkReferenceAvailable(); - mContainerWindow->checkReferenceAvailable(); - mConsole->checkReferenceAvailable(); -} + mDialogueWindow->onFrame(); -void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) -{ - if (cell->mCell->isExterior()) + mInventoryWindow->onFrame(); + + 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() ); } - else + + void WindowManager::setInteriorMapTexture(const int x, const int y) { - mMap->setCellName( cell->mCell->mName ); - mHud->setCellName( cell->mCell->mName ); - mMap->setCellPrefix( cell->mCell->mName ); - mHud->setCellPrefix( cell->mCell->mName ); + mMap->setActiveCell(x,y, true); + mHud->setActiveCell(x,y, true); } -} + void WindowManager::setPlayerPos(const float x, const float y) + { + mMap->setPlayerPos(x,y); + mHud->setPlayerPos(x,y); + } -void WindowManager::setInteriorMapTexture(const int x, const int y) -{ - mMap->setActiveCell(x,y, true); - mHud->setActiveCell(x,y, true); -} + void WindowManager::setPlayerDir(const float x, const float y) + { + mMap->setPlayerDir(x,y); + mHud->setPlayerDir(x,y); + } -void WindowManager::setPlayerPos(const float x, const float y) -{ - mMap->setPlayerPos(x,y); - mHud->setPlayerPos(x,y); -} + void WindowManager::setDrowningBarVisibility(bool visible) + { + mHud->setDrowningBarVisible(visible); + } -void WindowManager::setPlayerDir(const float x, const float y) -{ - mMap->setPlayerDir(x,y); - mHud->setPlayerDir(x,y); -} + void WindowManager::setHMSVisibility(bool visible) + { + mHud->setHmsVisible (visible); + } -void WindowManager::setHMSVisibility(bool visible) -{ - mHud->setHmsVisible (visible); -} + void WindowManager::setMinimapVisibility(bool visible) + { + mHud->setMinimapVisible (visible); + } -void WindowManager::setMinimapVisibility(bool visible) -{ - mHud->setMinimapVisible (visible); -} + void WindowManager::toggleFogOfWar() + { + mMap->toggleFogOfWar(); + mHud->toggleFogOfWar(); + } -void WindowManager::toggleFogOfWar() -{ - mMap->toggleFogOfWar(); - mHud->toggleFogOfWar(); -} + void WindowManager::setFocusObject(const MWWorld::Ptr& focus) + { + mToolTips->setFocusObject(focus); + } -void WindowManager::setFocusObject(const MWWorld::Ptr& focus) -{ - mToolTips->setFocusObject(focus); -} + 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::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::toggleFullHelp() + { + mToolTips->toggleFullHelp(); + } -void WindowManager::toggleFullHelp() -{ - mToolTips->toggleFullHelp(); -} + bool WindowManager::getFullHelp() const + { + return mToolTips->getFullHelp(); + } -bool WindowManager::getFullHelp() const -{ - return mToolTips->getFullHelp(); -} + void WindowManager::setWeaponVisibility(bool visible) + { + mHud->setWeapVisible (visible); + } -void WindowManager::setWeaponVisibility(bool visible) -{ - mHud->setWeapVisible (visible); -} + void WindowManager::setSpellVisibility(bool visible) + { + mHud->setSpellVisible (visible); + mHud->setEffectVisible (visible); + } -void WindowManager::setSpellVisibility(bool visible) -{ - mHud->setSpellVisible (visible); - mHud->setEffectVisible (visible); -} + void WindowManager::setSneakVisibility(bool visible) + { + mHud->setSneakVisible(visible); + } -void WindowManager::setMouseVisible(bool visible) -{ - mCursor->setVisible(visible); -} + void WindowManager::setDragDrop(bool dragDrop) + { + mToolTips->setEnabled(!dragDrop); + MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); + } -void WindowManager::setDragDrop(bool dragDrop) -{ - mToolTips->setEnabled(!dragDrop); - MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); -} + void WindowManager::setUseHardwareCursors(bool use) + { + mCursorManager->setEnabled(use); + mSoftwareCursor->setVisible(!use && mCursorVisible); + } -void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) -{ - 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)); + mSoftwareCursor->setVisible(!mUseHardwareCursors && visible); } - else + + void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) { - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().find(tag); + std::string tag(_tag); - if (setting && setting->mValue.getType()==ESM::VT_String) - _result = setting->mValue.getString(); + std::string tokenToFind = "sCell="; + size_t tokenLength = tokenToFind.length(); + + if (tag.substr(0, tokenLength) == tokenToFind) + { + _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); + } else - _result = tag; - } -} + { + const ESM::GameSetting *setting = + MWBase::Environment::get().getWorld()->getStore().get().find(tag); -void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) -{ - mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); - mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); + if (setting && setting->mValue.getType()==ESM::VT_String) + _result = setting->mValue.getString(); + else + _result = tag; + } + } - bool changeRes = false; - bool windowRecreated = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) + void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) + mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); + mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); + + setUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")); + + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); + it != changed.end(); ++it) { - changeRes = true; + 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"); } - 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 (changeRes) + void WindowManager::windowResized(int x, int y) { - int x = Settings::Manager::getInt("resolution x", "Video"); - int y = Settings::Manager::getInt("resolution y", "Video"); + mGuiManager->windowResized(); + mLoadingScreen->onResChange (x,y); + if (!mHud) + return; // UI not initialized yet mHud->onResChange(x, y); mConsole->onResChange(x, y); mMenu->onResChange(x, y); @@ -838,298 +942,421 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector 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) + + void WindowManager::pushGuiMode(GuiMode mode) { - mGuiManager->updateWindow (mRendering->getWindow ()); - mLoadingScreen->updateWindow (mRendering->getWindow ()); + if (mode==GM_Inventory && mAllowed==GW_None) + return; + + + // 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::pushGuiMode(GuiMode mode) -{ - if (mode==GM_Inventory && mAllowed==GW_None) - return; + void WindowManager::onCursorChange(const std::string &name) + { + mSoftwareCursor->onCursorChange(name); + + 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(); + + std::string tex_name = imgSet->getIndexInfo(0,0).texture; + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(tex_name); - // 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()) + //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; + int rotation = imgSetPtr->getRotation(); + + mCursorManager->receiveCursorInfo(name, rotation, tex, size_x, size_y, hotspot_x, hotspot_y); + } + } + } + + void WindowManager::popGuiMode() { - mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + if (!mGuiModes.empty()) + mGuiModes.pop_back(); + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); } - mGuiModes.push_back(mode); + void WindowManager::removeGuiMode(GuiMode mode) + { + std::vector::iterator it = mGuiModes.begin(); + while (it != mGuiModes.end()) + { + if (*it == mode) + it = mGuiModes.erase(it); + else + ++it; + } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - updateVisible(); -} + updateVisible(); + } -void WindowManager::popGuiMode() -{ - if (!mGuiModes.empty()) - mGuiModes.pop_back(); + void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) + { + mHud->setSelectedSpell(spellId, successChancePercent); - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - updateVisible(); -} + mSpellWindow->setTitle(spell->mName); + } -void WindowManager::removeGuiMode(GuiMode mode) -{ - std::vector::iterator it = mGuiModes.begin(); - while (it != mGuiModes.end()) + void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { - if (*it == mode) - it = mGuiModes.erase(it); - else - ++it; + const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() + .find(MWWorld::Class::get(item).getEnchantment(item)); + + 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)); } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + 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)); + } - updateVisible(); -} + void WindowManager::unsetSelectedSpell() + { + mHud->unsetSelectedSpell(); + mSpellWindow->setTitle("#{sNone}"); + } -void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - mHud->setSelectedSpell(spellId, successChancePercent); + void WindowManager::unsetSelectedWeapon() + { + mHud->unsetSelectedWeapon(); + mInventoryWindow->setTitle("#{sSkillHandtohand}"); + } + + void WindowManager::getMousePosition(int &x, int &y) + { + const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); + x = pos.left; + y = pos.top; + } - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + 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; + } - mSpellWindow->setTitle(spell->mName); -} + bool WindowManager::getWorldMouseOver() + { + return mHud->getWorldMouseOver(); + } -void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) -{ - mHud->setSelectedEnchantItem(item, chargePercent); - mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} + void WindowManager::executeInConsole (const std::string& path) + { + mConsole->executeFile (path); + } -void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) -{ - mHud->setSelectedWeapon(item, durabilityPercent); - mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} + void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) + { + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; + } -void WindowManager::unsetSelectedSpell() -{ - mHud->unsetSelectedSpell(); - mSpellWindow->setTitle("#{sNone}"); -} + 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; + } -void WindowManager::unsetSelectedWeapon() -{ - mHud->unsetSelectedWeapon(); - mInventoryWindow->setTitle("#{sSkillHandtohand}"); -} + void WindowManager::allow (GuiWindow wnd) + { + mAllowed = (GuiWindow)(mAllowed | wnd); -void WindowManager::getMousePosition(int &x, int &y) -{ - const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); - x = pos.left; - y = pos.top; -} + if (wnd & GW_Inventory) + { + mBookWindow->setInventoryAllowed (true); + mScrollWindow->setInventoryAllowed (true); + } -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; -} + updateVisible(); + } -bool WindowManager::getWorldMouseOver() -{ - return mHud->getWorldMouseOver(); -} + void WindowManager::disallowAll() + { + mAllowed = GW_None; -void WindowManager::executeInConsole (const std::string& path) -{ - mConsole->executeFile (path); -} + mBookWindow->setInventoryAllowed (false); + mScrollWindow->setInventoryAllowed (false); -void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) -{ - mFPS = fps; - mTriangleCount = triangleCount; - mBatchCount = batchCount; -} + updateVisible(); + } -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; -} + void WindowManager::toggleVisible (GuiWindow wnd) + { + if (getMode() != GM_Inventory) + return; -void WindowManager::allow (GuiWindow wnd) -{ - mAllowed = (GuiWindow)(mAllowed | wnd); + mShown = (GuiWindow)(mShown ^ wnd); + updateVisible(); + } - if (wnd & GW_Inventory) + void WindowManager::forceHide(GuiWindow wnd) { - mBookWindow->setInventoryAllowed (true); - mScrollWindow->setInventoryAllowed (true); + mForceHidden = (GuiWindow)(mForceHidden | wnd); + updateVisible(); } - updateVisible(); -} + void WindowManager::unsetForceHide(GuiWindow wnd) + { + mForceHidden = (GuiWindow)(mForceHidden & ~wnd); + updateVisible(); + } -void WindowManager::disallowAll() -{ - mAllowed = GW_None; + bool WindowManager::isGuiMode() const + { + return !mGuiModes.empty() || (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); + } - mBookWindow->setInventoryAllowed (false); - mScrollWindow->setInventoryAllowed (false); + bool WindowManager::isConsoleMode() const + { + if (!mGuiModes.empty() && mGuiModes.back()==GM_Console) + return true; + return false; + } - updateVisible(); -} + MWGui::GuiMode WindowManager::getMode() const + { + if (mGuiModes.empty()) + return GM_None; + return mGuiModes.back(); + } -void WindowManager::toggleVisible (GuiWindow wnd) -{ - mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); - updateVisible(); -} + std::map > WindowManager::getPlayerSkillValues() + { + return mPlayerSkillValues; + } -bool WindowManager::isGuiMode() const -{ - return !mGuiModes.empty(); -} + std::map > WindowManager::getPlayerAttributeValues() + { + return mPlayerAttributes; + } -MWGui::GuiMode WindowManager::getMode() const -{ - if (mGuiModes.empty()) - throw std::runtime_error ("getMode() called, but there is no active mode"); + WindowManager::SkillList WindowManager::getPlayerMinorSkills() + { + return mPlayerMinorSkills; + } - return mGuiModes.back(); -} + WindowManager::SkillList WindowManager::getPlayerMajorSkills() + { + return mPlayerMajorSkills; + } -std::map > WindowManager::getPlayerSkillValues() -{ - return mPlayerSkillValues; -} + void WindowManager::disallowMouse() + { + mInputBlocker->setVisible (true); + } -std::map > WindowManager::getPlayerAttributeValues() -{ - return mPlayerAttributes; -} + void WindowManager::allowMouse() + { + mInputBlocker->setVisible (!isGuiMode ()); + } -WindowManager::SkillList WindowManager::getPlayerMinorSkills() -{ - return mPlayerMinorSkills; -} + void WindowManager::notifyInputActionBound () + { + mSettingsWindow->updateControlsBox (); + allowMouse(); + } -WindowManager::SkillList WindowManager::getPlayerMajorSkills() -{ - return mPlayerMajorSkills; -} + bool WindowManager::containsMode(GuiMode mode) const + { + if(mGuiModes.empty()) + return false; -void WindowManager::disallowMouse() -{ - mInputBlocker->setVisible (true); -} + return std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end(); + } -void WindowManager::allowMouse() -{ - mInputBlocker->setVisible (!isGuiMode ()); -} + void WindowManager::showCrosshair (bool show) + { + if (mHud) + mHud->setCrosshairVisible (show && mCrosshairEnabled); + } -void WindowManager::notifyInputActionBound () -{ - mSettingsWindow->updateControlsBox (); - allowMouse(); -} + void WindowManager::activateQuickKey (int index) + { + mQuickKeysMenu->activateQuickKey(index); + } -void WindowManager::showCrosshair (bool show) -{ - mHud->setCrosshairVisible (show && mCrosshairEnabled); -} + bool WindowManager::getSubtitlesEnabled () + { + return mSubtitlesEnabled; + } -void WindowManager::activateQuickKey (int index) -{ - mQuickKeysMenu->activateQuickKey(index); -} + void WindowManager::toggleHud () + { + mHudEnabled = !mHudEnabled; + mHud->setVisible (mHudEnabled); + } -bool WindowManager::getSubtitlesEnabled () -{ - return mSubtitlesEnabled; -} + 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::toggleHud () -{ - mHudEnabled = !mHudEnabled; - mHud->setVisible (mHudEnabled); -} + bool WindowManager::getPlayerSleeping () + { + return mWaitDialog->getSleeping(); + } -void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) -{ - mLoadingScreen->setLoadingProgress (stage, depth, current, total); -} + void WindowManager::wakeUpPlayer() + { + mWaitDialog->wakeUp(); + } -void WindowManager::loadingDone () -{ - mLoadingScreen->loadingDone (); -} + void WindowManager::addVisitedLocation(const std::string& name, int x, int y) + { + mMap->addVisitedLocation (name, x, y); + } -bool WindowManager::getPlayerSleeping () -{ - return mWaitDialog->getSleeping(); -} + void WindowManager::startSpellMaking(MWWorld::Ptr actor) + { + mSpellCreationDialog->startSpellMaking (actor); + } -void WindowManager::wakeUpPlayer() -{ - mWaitDialog->wakeUp(); -} + void WindowManager::startEnchanting (MWWorld::Ptr actor) + { + mEnchantingDialog->startEnchanting (actor); + } -void WindowManager::addVisitedLocation(const std::string& name, int x, int y) -{ - mMap->addVisitedLocation (name, x, y); -} + void WindowManager::startSelfEnchanting(MWWorld::Ptr soulgem) + { + mEnchantingDialog->startSelfEnchanting(soulgem); + } -void WindowManager::startSpellMaking(MWWorld::Ptr actor) -{ - mSpellCreationDialog->startSpellMaking (actor); -} + void WindowManager::startTraining(MWWorld::Ptr actor) + { + mTrainingWindow->startTraining(actor); + } -void WindowManager::startEnchanting (MWWorld::Ptr actor) -{ - mEnchantingDialog->startEnchanting (actor); -} + void WindowManager::startRepair(MWWorld::Ptr actor) + { + mMerchantRepair->startRepair(actor); + } -void WindowManager::startTraining(MWWorld::Ptr actor) -{ - mTrainingWindow->startTraining(actor); -} + void WindowManager::startRepairItem(MWWorld::Ptr item) + { + mRepair->startRepairItem(item); + } -const Translation::Storage& WindowManager::getTranslationDataStorage() const -{ - return mTranslationDataStorage; -} + const Translation::Storage& WindowManager::getTranslationDataStorage() const + { + return mTranslationDataStorage; + } + + void WindowManager::showCompanionWindow(MWWorld::Ptr actor) + { + mCompanionWindow->open(actor); + } + + void WindowManager::changePointer(const std::string &name) + { + onCursorChange(name); + } + + void WindowManager::showSoulgemDialog(MWWorld::Ptr item) + { + mSoulgemDialog->show(item); + } + + void WindowManager::frameStarted (float dt) + { + mInventoryWindow->doRenderUpdate (); + mCharGen->doRenderUpdate(); + } + + void WindowManager::updatePlayer() + { + mInventoryWindow->updatePlayer(); + } + + void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget) + { + if (widget == NULL) + MyGUI::InputManager::getInstance().resetKeyFocusWidget(); + else + MyGUI::InputManager::getInstance().setKeyFocusWidget(widget); + onKeyFocusChanged(widget); + } + + void WindowManager::onKeyFocusChanged(MyGUI::Widget *widget) + { + if (widget && widget->castType(false)) + SDL_StartTextInput(); + else + SDL_StopTextInput(); + } + + void WindowManager::setEnemy(const MWWorld::Ptr &enemy) + { + mHud->setEnemy(enemy); + } + + Loading::Listener* WindowManager::getLoadingScreen() + { + return mLoadingScreen; + } -void WindowManager::changePointer(const std::string &name) -{ - mCursor->onCursorChange(name); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 122b10cc3..badb333a7 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 @@ -48,6 +43,11 @@ namespace OEngine } } +namespace SFO +{ + class CursorManager; +} + namespace MWGui { class WindowBase; @@ -74,6 +74,10 @@ namespace MWGui class TrainingWindow; class Cursor; class SpellIcons; + class MerchantRepair; + class Repair; + class SoulgemDialog; + class CompanionWindow; class WindowManager : public MWBase::WindowManager { @@ -81,12 +85,17 @@ 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(); + void initUI(); + void renderWorldMap(); + + virtual Loading::Listener* getLoadingScreen(); + /** * Should be called each frame to update windows/gui elements. * This could mean updating sizes of gui elements or opening @@ -94,16 +103,26 @@ namespace MWGui */ virtual void update(); + virtual void setKeyFocusWidget (MyGUI::Widget* widget); + + virtual void setNewGame(bool newgame); + virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack virtual GuiMode getMode() const; + virtual bool containsMode(GuiMode mode) const; virtual bool isGuiMode() const; + virtual bool isConsoleMode() const; + virtual void toggleVisible(GuiWindow wnd); + virtual void forceHide(MWGui::GuiWindow wnd); + virtual void unsetForceHide(MWGui::GuiWindow wnd); + // Disallow all inventory mode windows virtual void disallowAll(); @@ -137,6 +156,10 @@ namespace MWGui virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); + /// Set time left for the player to start drowning (update the drowning bar) + /// @param time value from [0,20] + virtual void setDrowningTimeLeft (float time); + virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. virtual void setReputation (int reputation); ///< set the current reputation value @@ -150,7 +173,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 setMouseVisible(bool visible); virtual void getMousePosition(int &x, int &y); virtual void getMousePosition(float &x, float &y); virtual void setDragDrop(bool dragDrop); @@ -163,18 +185,22 @@ namespace MWGui virtual void setInteriorMapTexture(const int x, const int y); ///< set the index of the map texture that should be used (for interiors) + /// sets the visibility of the drowning bar + virtual void setDrowningBarVisibility(bool visible); + // sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible); // sets the visibility of the hud minimap virtual void setMinimapVisibility(bool visible); virtual void setWeaponVisibility(bool visible); virtual void setSpellVisibility(bool visible); + virtual void setSneakVisibility(bool visible); 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(); @@ -190,8 +216,11 @@ namespace MWGui virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector()); + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false); + virtual void staticMessageBox(const std::string& message); + virtual void removeStaticMessageBox(); virtual void enterPressed (); + virtual void activateKeyPressed (); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual void onFrame (float frameDuration); @@ -213,26 +242,43 @@ namespace MWGui virtual void processChangedSettings(const Settings::CategorySettingVector& changed); - virtual void executeInConsole (const std::string& path); + virtual void windowResized(int x, int y); - virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total); - virtual void loadingDone(); + virtual void executeInConsole (const std::string& path); 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 void setEnemy (const MWWorld::Ptr& enemy); + virtual const Translation::Storage& getTranslationDataStorage() const; + void onSoulgemDialogButtonPressed (int button); + private: + bool mConsoleOnlyScripts; + OEngine::GUI::MyGUIManager *mGuiManager; OEngine::Render::OgreRenderer *mRendering; HUD *mHud; @@ -264,8 +310,13 @@ namespace MWGui SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; + MerchantRepair* mMerchantRepair; + SoulgemDialog* mSoulgemDialog; + Repair* mRepair; + CompanionWindow* mCompanionWindow; + Translation::Storage& mTranslationDataStorage; - Cursor* mCursor; + Cursor* mSoftwareCursor; CharacterCreation* mCharGen; @@ -274,6 +325,9 @@ namespace MWGui bool mCrosshairEnabled; bool mSubtitlesEnabled; 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 @@ -288,10 +342,13 @@ namespace MWGui MyGUI::Gui *mGui; // Gui std::vector mGuiModes; + SFO::CursorManager* mCursorManager; + std::vector mGarbageDialogs; void cleanupGarbage(); GuiWindow mShown; // Currently shown windows in inventory mode + GuiWindow mForceHidden; // Hidden windows (overrides mShown) /* Currently ALLOWED windows in inventory mode. This is used at the start of the game, when windows are enabled one by one @@ -309,13 +366,17 @@ namespace MWGui unsigned int mTriangleCount; unsigned int mBatchCount; - void onDialogueWindowBye(); + bool mUseHardwareCursors; + void setUseHardwareCursors(bool use); /** * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result */ void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); + + void onCursorChange(const std::string& name); + void onKeyFocusChanged(MyGUI::Widget* widget); }; } diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp new file mode 100644 index 000000000..47364337c --- /dev/null +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -0,0 +1,32 @@ +#include "windowpinnablebase.hpp" + +#include "exposedwindow.hpp" + +namespace MWGui +{ + WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) + : WindowBase(parLayout), mPinned(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(); + } + + void WindowPinnableBase::setPinButtonVisible(bool visible) + { + mPinButton->setVisible(visible); + } +} diff --git a/apps/openmw/mwgui/window_pinnable_base.hpp b/apps/openmw/mwgui/windowpinnablebase.hpp similarity index 74% rename from apps/openmw/mwgui/window_pinnable_base.hpp rename to apps/openmw/mwgui/windowpinnablebase.hpp index 50259858e..cd393f918 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,8 +10,9 @@ namespace MWGui class WindowPinnableBase: public WindowBase { public: - WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + WindowPinnableBase(const std::string& parLayout); bool pinned() { return mPinned; } + void setPinButtonVisible(bool visible); private: void onPinButtonClicked(MyGUI::Widget* _sender); @@ -21,7 +22,6 @@ namespace MWGui MyGUI::Widget* mPinButton; bool mPinned; - bool mVisible; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f18c02a0e..43f2bcc15 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,16 +1,10 @@ #include "inputmanagerimp.hpp" -#if defined(__APPLE__) && !defined(__LP64__) -#include -#endif - #include #include #include -#include - #include #include #include @@ -25,18 +19,77 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwgui/bookwindow.hpp" +#include "../mwmechanics/creaturestats.hpp" + +using namespace ICS; + +namespace +{ + std::vector utf8ToUnicode(const std::string& utf8) + { + std::vector unicode; + size_t i = 0; + while (i < utf8.size()) + { + unsigned long uni; + size_t todo; + unsigned char ch = utf8[i++]; + if (ch <= 0x7F) + { + uni = ch; + todo = 0; + } + else if (ch <= 0xBF) + { + throw std::logic_error("not a UTF-8 string"); + } + else if (ch <= 0xDF) + { + uni = ch&0x1F; + todo = 1; + } + else if (ch <= 0xEF) + { + uni = ch&0x0F; + todo = 2; + } + else if (ch <= 0xF7) + { + uni = ch&0x07; + todo = 3; + } + else + { + throw std::logic_error("not a UTF-8 string"); + } + for (size_t j = 0; j < todo; ++j) + { + if (i == utf8.size()) + throw std::logic_error("not a UTF-8 string"); + unsigned char ch = utf8[i++]; + if (ch < 0x80 || ch > 0xBF) + throw std::logic_error("not a UTF-8 string"); + uni <<= 6; + uni += ch & 0x3F; + } + if (uni >= 0xD800 && uni <= 0xDFFF) + throw std::logic_error("not a UTF-8 string"); + if (uni > 0x10FFFF) + throw std::logic_error("not a UTF-8 string"); + unicode.push_back(uni); + } + return unicode; + } +} namespace MWInput { InputManager::InputManager(OEngine::Render::OgreRenderer &ogre, - MWWorld::Player &player, - MWBase::WindowManager &windows, - bool debug, OMW::Engine& engine, const std::string& userFile, bool userFileExists) : mOgre(ogre) - , mPlayer(player) - , mWindows(windows) + , mPlayer(NULL) , mEngine(engine) , mMouseLookEnabled(true) , mMouseX(ogre.getWindow()->getWidth ()/2.f) @@ -44,88 +97,35 @@ namespace MWInput , mMouseWheel(0) , mDragDrop(false) , mGuiCursorEnabled(false) - , mDebug(debug) , 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) { - Ogre::RenderWindow* window = mOgre.getWindow (); - size_t windowHnd; - - resetIdleTime(); - - window->getCustomAttribute("WINDOW", &windowHnd); - - std::ostringstream windowHndStr; - OIS::ParamList pl; - windowHndStr << windowHnd; - pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); + Ogre::RenderWindow* window = ogre.getWindow (); - // Set non-exclusive mouse and keyboard input if the user requested - // it. - if (mDebug) - { - #if defined OIS_WIN32_PLATFORM - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_FOREGROUND" ))); - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_NONEXCLUSIVE"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_FOREGROUND"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_NONEXCLUSIVE"))); - #elif defined OIS_LINUX_PLATFORM - pl.insert(std::make_pair(std::string("x11_mouse_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_mouse_hide"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_keyboard_grab"), - std::string("false"))); - #endif - } -#if defined OIS_LINUX_PLATFORM - pl.insert(std::make_pair(std::string("XAutoRepeatOn"), - std::string("true"))); -#endif - -#if defined(__APPLE__) && !defined(__LP64__) - // Give the application window focus to receive input events - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - SetFrontProcess(&psn); -#endif - - mInputManager = OIS::InputManager::createInputSystem( pl ); - - // Create all devices - mKeyboard = static_cast(mInputManager->createInputObject - ( OIS::OISKeyboard, true )); - mMouse = static_cast(mInputManager->createInputObject - ( OIS::OISMouse, true )); + mInputManager = new SFO::InputWrapper(mOgre.getSDLWindow(), mOgre.getWindow()); + mInputManager->setMouseEventCallback (this); + mInputManager->setKeyboardEventCallback (this); + mInputManager->setWindowEventCallback(this); - mKeyboard->setEventCallback (this); - mMouse->setEventCallback (this); + std::string file = userFileExists ? userFile : ""; + mInputBinder = new ICS::InputControlSystem(file, true, this, NULL, A_Last); adjustMouseRegion (window->getWidth(), window->getHeight()); - MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - - std::string file = mUserFileExists ? mUserFile : ""; - mInputCtrl = new ICS::InputControlSystem(file, true, this, NULL, A_Last); - loadKeyDefaults(); for (int i = 0; i < A_Last; ++i) { - mInputCtrl->getChannel (i)->addListener (this); + mInputBinder->getChannel (i)->addListener (this); } mControlSwitch["playercontrols"] = true; @@ -135,19 +135,15 @@ namespace MWInput mControlSwitch["playermagic"] = true; mControlSwitch["playerviewswitch"] = true; mControlSwitch["vanitymode"] = true; - - changeInputMode(false); } InputManager::~InputManager() { - mInputCtrl->save (mUserFile); + mInputBinder->save (mUserFile); - delete mInputCtrl; + delete mInputBinder; - mInputManager->destroyInputObject(mKeyboard); - mInputManager->destroyInputObject(mMouse); - OIS::InputManager::destroyInputSystem(mInputManager); + delete mInputManager; } void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) @@ -158,6 +154,26 @@ namespace MWInput resetIdleTime (); int action = channel->getNumber(); + + if (action == A_Use) + { + MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).setAttackingOrSpell(currentValue); + if (currentValue == 1) + { + int type = MWMechanics::CreatureStats::AT_Chop; + bool forward = (mInputBinder->getChannel(A_MoveForward)->getValue() > 0 + || mInputBinder->getChannel(A_MoveBackward)->getValue() > 0); + bool side = (mInputBinder->getChannel(A_MoveLeft)->getValue() > 0 + || mInputBinder->getChannel(A_MoveRight)->getValue() > 0); + if (side && !forward) + type = MWMechanics::CreatureStats::AT_Slash; + if (forward && !side) + type = MWMechanics::CreatureStats::AT_Thrust; + + MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).setAttackType(type); + } + } + if (currentValue == 1) { // trigger action activated @@ -180,12 +196,16 @@ namespace MWInput break; case A_Activate: resetIdleTime(); - activate(); - if( MWBase::Environment::get().getWindowManager()->isGuiMode() - && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_InterMessageBox ) { - // Pressing the activation key when a messagebox is prompting for "ok" will activate the ok button - MWBase::Environment::get().getWindowManager()->enterPressed(); - } + + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container) + toggleContainer (); + else + MWBase::Environment::get().getWindowManager()->activateKeyPressed(); + } + else + activate(); break; case A_Journal: toggleJournal (); @@ -193,7 +213,7 @@ namespace MWInput case A_AutoMove: toggleAutoMove (); break; - case A_ToggleWalk: + case A_AlwaysRun: toggleWalking (); break; case A_ToggleWeapon: @@ -239,7 +259,7 @@ namespace MWInput showQuickKeysMenu(); break; case A_ToggleHUD: - mWindows.toggleHud(); + MWBase::Environment::get().getWindowManager()->toggleHud(); break; } } @@ -247,10 +267,7 @@ namespace MWInput void InputManager::update(float dt, bool loading) { - // Tell OIS to handle all input events - mKeyboard->capture(); - mMouse->capture(); - + mInputManager->capture(loading); // inject some fake mouse movement to force updating MyGUI's widget states // this shouldn't do any harm since we're moving back to the original position afterwards MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX+1), int(mMouseY+1), mMouseWheel); @@ -258,18 +275,32 @@ namespace MWInput // update values of channels (as a result of pressed keys) if (!loading) - mInputCtrl->update(dt); - - // Update windows/gui as a result of input events - // For instance this could mean opening a new window/dialog, - // by doing this after the input events are handled we - // ensure that window/gui changes appear quickly while - // avoiding that window/gui changes does not happen in - // event callbacks (which may crash) - mWindows.update(); + mInputBinder->update(dt); + + bool main_menu = MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu); + + bool was_relative = mInputManager->getMouseRelative(); + bool is_relative = !MWBase::Environment::get().getWindowManager()->isGuiMode(); + + // don't keep the pointer away from the window edge in gui mode + // stop using raw mouse motions and switch to system cursor movements + mInputManager->setMouseRelative(is_relative); + + //we let the mouse escape in the main menu + mInputManager->setGrabPointer(!main_menu); + + //we switched to non-relative mode, move our cursor to where the in-game + //cursor is + if( !is_relative && was_relative != is_relative ) + { + mInputManager->warpMouse(mMouseX, mMouseY); + } + + if (loading) + return; // Disable movement in Gui mode - if (mWindows.isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Configure player movement according to keyboard input. Actual movement will @@ -280,45 +311,45 @@ namespace MWInput if (actionIsActive(A_MoveLeft)) { triedToMove = true; - mPlayer.setLeftRight (-1); + mPlayer->setLeftRight (-1); } else if (actionIsActive(A_MoveRight)) { triedToMove = true; - mPlayer.setLeftRight (1); + mPlayer->setLeftRight (1); } - else - mPlayer.setLeftRight (0); if (actionIsActive(A_MoveForward)) { triedToMove = true; - mPlayer.setAutoMove (false); - mPlayer.setForwardBackward (1); + mPlayer->setAutoMove (false); + mPlayer->setForwardBackward (1); } else if (actionIsActive(A_MoveBackward)) { triedToMove = true; - mPlayer.setAutoMove (false); - mPlayer.setForwardBackward (-1); + mPlayer->setAutoMove (false); + mPlayer->setForwardBackward (-1); } - else - mPlayer.setForwardBackward (0); - mPlayer.setSneak(actionIsActive(A_Sneak)); + else if(mPlayer->getAutoMove()) + { + triedToMove = true; + mPlayer->setForwardBackward (1); + } + + mPlayer->setSneak(actionIsActive(A_Sneak)); if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) { - mPlayer.setUpDown (1); + mPlayer->setUpDown (1); triedToMove = true; } - else - mPlayer.setUpDown (0); - if(actionIsActive(A_Run)) - mPlayer.setRunState(true); + if (mAlwaysRunActive) + mPlayer->setRunState(!actionIsActive(A_Run)); else - mPlayer.setRunState(false); + mPlayer->setRunState(actionIsActive(A_Run)); // if player tried to start moving, but can't (due to being overencumbered), display a notification. if (triedToMove) @@ -327,6 +358,7 @@ namespace MWInput 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}"); @@ -338,7 +370,7 @@ namespace MWInput if (mControlSwitch["playerviewswitch"]) { // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mKeyboard->isModifierDown (OIS::Keyboard::Alt)) { + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += dt) > 0.5) { @@ -377,38 +409,19 @@ 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) + MWBase::Environment::get().getWindowManager()->showCrosshair(false); + MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode); + // if not in gui mode, the camera decides whether to show crosshair or not. } void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - bool changeRes = 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; - if (it->first == "Input" && it->second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); @@ -419,9 +432,6 @@ namespace MWInput mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); } - - if (changeRes) - adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); } bool InputManager::getControlSwitch (const std::string& sw) @@ -436,13 +446,13 @@ namespace MWInput } /// \note 7 switches at all, if-else is relevant if (sw == "playercontrols" && !value) { - mPlayer.setLeftRight(0); - mPlayer.setForwardBackward(0); - mPlayer.setAutoMove(false); - mPlayer.setUpDown(0); + mPlayer->setLeftRight(0); + mPlayer->setForwardBackward(0); + mPlayer->setAutoMove(false); + mPlayer->setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time - mPlayer.setUpDown(0); + mPlayer->setUpDown(0); } else if (sw == "vanitymode") { MWBase::Environment::get().getWorld()->allowVanityMode(value); } else if (sw == "playerlooking") { @@ -453,50 +463,54 @@ namespace MWInput void InputManager::adjustMouseRegion(int width, int height) { - const OIS::MouseState &ms = mMouse->getMouseState(); - ms.width = width; - ms.height = height; + mInputBinder->adjustMouseRegion(width, height); } - bool InputManager::keyPressed( const OIS::KeyEvent &arg ) + bool InputManager::keyPressed( const SDL_KeyboardEvent &arg ) { - if(arg.key == OIS::KC_RETURN - && MWBase::Environment::get().getWindowManager()->isGuiMode() - && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_InterMessageBox ) + mInputBinder->keyPressed (arg); + + 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(); } - mInputCtrl->keyPressed (arg); - unsigned int text = arg.text; -#ifdef __APPLE__ // filter \016 symbol for F-keys on OS X - if ((arg.key >= OIS::KC_F1 && arg.key <= OIS::KC_F10) || - (arg.key >= OIS::KC_F11 && arg.key <= OIS::KC_F15)) { - text = 0; - } -#endif - - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), text); + OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); + if (kc != OIS::KC_UNASSIGNED) + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); return true; } - bool InputManager::keyReleased( const OIS::KeyEvent &arg ) + void InputManager::textInput(const SDL_TextInputEvent &arg) + { + const char* text = &arg.text[0]; + std::vector unicode = utf8ToUnicode(std::string(text)); + for (std::vector::iterator it = unicode.begin(); it != unicode.end(); ++it) + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it); + } + + bool InputManager::keyReleased(const SDL_KeyboardEvent &arg ) { - mInputCtrl->keyReleased (arg); + mInputBinder->keyReleased (arg); + + OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); - MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)); return true; } - bool InputManager::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + bool InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) { - mInputCtrl->mousePressed (arg, id); + mInputBinder->mousePressed (arg, id); - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + if (id != SDL_BUTTON_LEFT && id != SDL_BUTTON_RIGHT) + return true; // MyGUI has no use for these events + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)); if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) { MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); @@ -509,18 +523,18 @@ namespace MWInput return true; } - bool InputManager::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + bool InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) { - mInputCtrl->mouseReleased (arg, id); + mInputBinder->mouseReleased (arg, id); - MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)); return true; } - bool InputManager::mouseMoved( const OIS::MouseEvent &arg ) + bool InputManager::mouseMoved(const SFO::MouseMotionEvent &arg ) { - mInputCtrl->mouseMoved (arg); + mInputBinder->mouseMoved (arg); resetIdleTime (); @@ -530,86 +544,127 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - mMouseX += float(arg.state.X.rel) * mUISensitivity; - mMouseY += float(arg.state.Y.rel) * mUISensitivity * mUIYMultiplier; + mMouseX = arg.x; + mMouseY = arg.y; + mMouseX = std::max(0.f, std::min(mMouseX, float(viewSize.width))); mMouseY = std::max(0.f, std::min(mMouseY, float(viewSize.height))); - mMouseWheel = arg.state.Z.abs; + + mMouseWheel = int(arg.z); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); + + //if the player is reading a book and flicking the mouse wheel + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Book && arg.zrel) + { + if (arg.zrel < 0) + MWBase::Environment::get().getWindowManager()->getBookWindow()->nextPage(); + else + MWBase::Environment::get().getWindowManager()->getBookWindow()->prevPage(); + } } if (mMouseLookEnabled) { resetIdleTime(); - float x = arg.state.X.rel * mCameraSensitivity * 0.2; - float y = arg.state.Y.rel * 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; - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + float rot[3]; + rot[0] = -y; + rot[1] = 0.0f; + rot[2] = x; - if (arg.state.Z.rel) - MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.state.Z.rel); + // Only actually turn player when we're not in vanity mode + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) + { + mPlayer->yaw(x/scale); + mPlayer->pitch(-y/scale); + } + + if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change + { + MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel); + MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true); + } } return true; } + void InputManager::windowFocusChange(bool have_focus) + { + } + + void InputManager::windowVisibilityChange(bool visible) + { + //TODO: Pause game? + } + + void InputManager::windowResized(int x, int y) + { + mOgre.windowResized(x,y); + } + void InputManager::toggleMainMenu() { 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) + if (MWBase::Environment::get().getWindowManager()->isGuiMode () && MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_Video) MWBase::Environment::get().getWorld ()->stopVideo (); + else if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getSoundManager()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); + } else - mWindows.pushGuiMode (MWGui::GM_MainMenu); + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx); + } } void InputManager::toggleSpell() { - if (mWindows.isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + + // Not allowed before the magic window is accessible + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic)) + return; - MWMechanics::DrawState_ state = mPlayer.getDrawState(); + 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; - } + mPlayer->setDrawState(MWMechanics::DrawState_Spell); else - { - mPlayer.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; - } + mPlayer->setDrawState(MWMechanics::DrawState_Nothing); } void InputManager::toggleWeapon() { - if (mWindows.isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - MWMechanics::DrawState_ state = mPlayer.getDrawState(); + // Not allowed before the inventory window is accessible + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return; + + 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; - } + mPlayer->setDrawState(MWMechanics::DrawState_Weapon); else - { - mPlayer.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; - } + mPlayer->setDrawState(MWMechanics::DrawState_Nothing); } void InputManager::rest() { - if (!mWindows.getRestEnabled () || mWindows.isGuiMode ()) + if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) return; /// \todo check if resting is currently allowed (enemies nearby?) - mWindows.pushGuiMode (MWGui::GM_Rest); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); } void InputManager::screenshot() @@ -617,69 +672,91 @@ namespace MWInput mEngine.screenshot(); std::vector empty; - mWindows.messageBox ("Screenshot saved", empty); + MWBase::Environment::get().getWindowManager()->messageBox ("Screenshot saved", empty); } void InputManager::toggleInventory() { - bool gameMode = !mWindows.isGuiMode(); + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; // Toggle between game mode and inventory mode - if(gameMode) - mWindows.pushGuiMode(MWGui::GM_Inventory); + if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory); else { - MWGui::GuiMode mode = mWindows.getMode(); + MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); if(mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) - mWindows.popGuiMode(); + MWBase::Environment::get().getWindowManager()->popGuiMode(); } // .. but don't touch any other mode, except container. } - void InputManager::toggleConsole() + void InputManager::toggleContainer() { if (MyGUI::InputManager::getInstance ().isModalAny()) return; - bool gameMode = !mWindows.isGuiMode(); + if(MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container) + MWBase::Environment::get().getWindowManager()->popGuiMode(); + else + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container); + } + + } + + void InputManager::toggleConsole() + { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; // Switch to console mode no matter what mode we are currently // in, except of course if we are already in console mode - if (!gameMode) + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { - if (mWindows.getMode() == MWGui::GM_Console) - mWindows.popGuiMode(); + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console) + MWBase::Environment::get().getWindowManager()->popGuiMode(); else - mWindows.pushGuiMode(MWGui::GM_Console); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Console); } else - mWindows.pushGuiMode(MWGui::GM_Console); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Console); } void InputManager::toggleJournal() { - // Toggle between game mode and journal mode - bool gameMode = !mWindows.isGuiMode(); + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; - if(gameMode) - mWindows.pushGuiMode(MWGui::GM_Journal); - else if(mWindows.getMode() == MWGui::GM_Journal) - mWindows.popGuiMode(); + // Toggle between game mode and journal mode + if(!MWBase::Environment::get().getWindowManager()->isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) + { + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); + } + else if(MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Journal) + { + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } // .. but don't touch any other mode. } void InputManager::quickKey (int index) { - mWindows.activateQuickKey (index); + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWindowManager()->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); + if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()) + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); + else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) + MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_QuickKeysMenu); } void InputManager::activate() @@ -690,47 +767,45 @@ namespace MWInput void InputManager::toggleAutoMove() { - if (mWindows.isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; if (mControlSwitch["playercontrols"]) - mPlayer.setAutoMove (!mPlayer.getAutoMove()); + mPlayer->setAutoMove (!mPlayer->getAutoMove()); } void InputManager::toggleWalking() { - if (mWindows.isGuiMode()) return; - mPlayer.toggleRunning(); + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + mAlwaysRunActive = !mAlwaysRunActive; } // Exit program now button (which is disabled in GUI mode) void InputManager::exitNow() { - if(!mWindows.isGuiMode()) + if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) Ogre::Root::getSingleton().queueEndRendering (); } 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; } } bool InputManager::actionIsActive (int id) { - return mInputCtrl->getChannel (id)->getValue () == 1; + return mInputBinder->getChannel (id)->getValue () == 1; } void InputManager::loadKeyDefaults (bool force) @@ -739,74 +814,84 @@ namespace MWInput // across different versions of OpenMW (in the case where another input action is added) std::map defaultKeyBindings; - defaultKeyBindings[A_Activate] = OIS::KC_SPACE; - defaultKeyBindings[A_MoveBackward] = OIS::KC_S; - defaultKeyBindings[A_MoveForward] = OIS::KC_W; - defaultKeyBindings[A_MoveLeft] = OIS::KC_A; - defaultKeyBindings[A_MoveRight] = OIS::KC_D; - defaultKeyBindings[A_ToggleWeapon] = OIS::KC_F; - defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; - defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1; - defaultKeyBindings[A_Console] = OIS::KC_F2; - defaultKeyBindings[A_Run] = OIS::KC_LSHIFT; - defaultKeyBindings[A_Sneak] = OIS::KC_LCONTROL; - defaultKeyBindings[A_AutoMove] = OIS::KC_Q; - defaultKeyBindings[A_Jump] = OIS::KC_E; - defaultKeyBindings[A_Journal] = OIS::KC_J; - defaultKeyBindings[A_Rest] = OIS::KC_T; - defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; - defaultKeyBindings[A_TogglePOV] = OIS::KC_TAB; - defaultKeyBindings[A_QuickKey1] = OIS::KC_1; - defaultKeyBindings[A_QuickKey2] = OIS::KC_2; - defaultKeyBindings[A_QuickKey3] = OIS::KC_3; - defaultKeyBindings[A_QuickKey4] = OIS::KC_4; - defaultKeyBindings[A_QuickKey5] = OIS::KC_5; - defaultKeyBindings[A_QuickKey6] = OIS::KC_6; - defaultKeyBindings[A_QuickKey7] = OIS::KC_7; - defaultKeyBindings[A_QuickKey8] = OIS::KC_8; - defaultKeyBindings[A_QuickKey9] = OIS::KC_9; - defaultKeyBindings[A_QuickKey10] = OIS::KC_0; - defaultKeyBindings[A_Screenshot] = OIS::KC_SYSRQ; - defaultKeyBindings[A_ToggleHUD] = OIS::KC_F12; + defaultKeyBindings[A_Activate] = SDLK_SPACE; + defaultKeyBindings[A_MoveBackward] = SDLK_s; + defaultKeyBindings[A_MoveForward] = SDLK_w; + defaultKeyBindings[A_MoveLeft] = SDLK_a; + defaultKeyBindings[A_MoveRight] = SDLK_d; + defaultKeyBindings[A_ToggleWeapon] = SDLK_f; + defaultKeyBindings[A_ToggleSpell] = SDLK_r; + defaultKeyBindings[A_QuickKeysMenu] = SDLK_F1; + defaultKeyBindings[A_Console] = SDLK_F2; + 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; + defaultKeyBindings[A_Rest] = SDLK_t; + defaultKeyBindings[A_GameMenu] = SDLK_ESCAPE; + defaultKeyBindings[A_TogglePOV] = SDLK_TAB; + defaultKeyBindings[A_QuickKey1] = SDLK_1; + defaultKeyBindings[A_QuickKey2] = SDLK_2; + defaultKeyBindings[A_QuickKey3] = SDLK_3; + defaultKeyBindings[A_QuickKey4] = SDLK_4; + defaultKeyBindings[A_QuickKey5] = SDLK_5; + defaultKeyBindings[A_QuickKey6] = SDLK_6; + defaultKeyBindings[A_QuickKey7] = SDLK_7; + defaultKeyBindings[A_QuickKey8] = SDLK_8; + defaultKeyBindings[A_QuickKey9] = SDLK_9; + defaultKeyBindings[A_QuickKey10] = SDLK_0; + defaultKeyBindings[A_Screenshot] = SDLK_F12; + defaultKeyBindings[A_ToggleHUD] = SDLK_F11; + defaultKeyBindings[A_AlwaysRun] = SDLK_y; std::map defaultMouseButtonBindings; - defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; - defaultMouseButtonBindings[A_Use] = OIS::MB_Left; + defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; + defaultMouseButtonBindings[A_Use] = SDL_BUTTON_LEFT; for (int i = 0; i < A_Last; ++i) { ICS::Control* control; - bool controlExists = mInputCtrl->getChannel(i)->getControlsCount () != 0; + bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; if (!controlExists) { control = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); - mInputCtrl->addControl(control); - control->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + mInputBinder->addControl(control); + control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT); } else { - control = mInputCtrl->getChannel(i)->getAttachedControls ().front().control; + control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; } if (!controlExists || force || - ( mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) == OIS::KC_UNASSIGNED - && mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS + ( mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) == SDLK_UNKNOWN + && mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS )) { clearAllBindings (control); if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) - mInputCtrl->addKeyBinding(control, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); + mInputBinder->addKeyBinding(control, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) - mInputCtrl->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } } + + // Printscreen key should not be allowed because it's captured by system screenshot function + // We check this explicitely here to fix up pre-0.26 config files. Can be removed after a few versions + if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Screenshot), ICS::Control::INCREASE) == SDLK_PRINTSCREEN) + mInputBinder->addKeyBinding(mInputBinder->getControl(A_Screenshot), SDLK_F12, ICS::Control::INCREASE); } std::string InputManager::getActionDescription (int action) { std::map descriptions; + if (action == A_Screenshot) + return "Screenshot"; + + descriptions[A_Use] = "sUse"; descriptions[A_Activate] = "sActivate"; descriptions[A_MoveBackward] = "sBack"; descriptions[A_MoveForward] = "sForward"; @@ -834,6 +919,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 @@ -843,15 +929,15 @@ namespace MWInput std::string InputManager::getActionBindingName (int action) { - if (mInputCtrl->getChannel (action)->getControlsCount () == 0) + if (mInputBinder->getChannel (action)->getControlsCount () == 0) return "#{sNone}"; - ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; - if (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) - return mInputCtrl->keyCodeToString (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE)); - else if (mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) - return "#{sMouse} " + boost::lexical_cast(mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE)); + if (mInputBinder->getKeyBinding (c, ICS::Control::INCREASE) != SDLK_UNKNOWN) + return mInputBinder->keyCodeToString (mInputBinder->getKeyBinding (c, ICS::Control::INCREASE)); + else if (mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + return "#{sMouse} " + boost::lexical_cast(mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE)); else return "#{sNone}"; } @@ -865,8 +951,10 @@ namespace MWInput ret.push_back(A_MoveRight); ret.push_back(A_TogglePOV); 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); @@ -875,6 +963,7 @@ namespace MWInput ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); + ret.push_back(A_Screenshot); ret.push_back(A_QuickKeysMenu); ret.push_back(A_QuickKey1); ret.push_back(A_QuickKey2); @@ -892,9 +981,9 @@ namespace MWInput void InputManager::enableDetectingBindingMode (int action) { - ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; - mInputCtrl->enableDetectingBindingState (c, ICS::Control::INCREASE); + mInputBinder->enableDetectingBindingState (c, ICS::Control::INCREASE); } void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control @@ -905,8 +994,12 @@ namespace MWInput } void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction) + , SDL_Keycode key, ICS::Control::ControlChangingDirection direction) { + //Disallow binding escape key + if(key==SDLK_ESCAPE) + return; + clearAllBindings(control); ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); @@ -955,10 +1048,10 @@ namespace MWInput void InputManager::clearAllBindings (ICS::Control* control) { // right now we don't really need multiple bindings for the same action, so remove all others first - if (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) - mInputCtrl->removeKeyBinding (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE)); - if (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) - mInputCtrl->removeMouseButtonBinding (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE)); + if (mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) != SDLK_UNKNOWN) + mInputBinder->removeKeyBinding (mInputBinder->getKeyBinding (control, ICS::Control::INCREASE)); + if (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + mInputBinder->removeMouseButtonBinding (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE)); /// \todo add joysticks here once they are added } @@ -967,4 +1060,16 @@ namespace MWInput { loadKeyDefaults(true); } + + MyGUI::MouseButton InputManager::sdlButtonToMyGUI(Uint8 button) + { + //The right button is the second button, according to MyGUI + if(button == SDL_BUTTON_RIGHT) + button = SDL_BUTTON_MIDDLE; + else if(button == SDL_BUTTON_MIDDLE) + button = SDL_BUTTON_RIGHT; + + //MyGUI's buttons are 0 indexed + return MyGUI::MouseButton::Enum(button - 1); + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 8bb20b7be..5f9a752d7 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -6,6 +6,7 @@ #include #include "../mwbase/inputmanager.hpp" +#include namespace OEngine { @@ -35,16 +36,11 @@ namespace ICS class InputControlSystem; } -namespace OIS +namespace MyGUI { - class Keyboard; - class Mouse; - class InputManager; + class MouseButton; } -#include -#include - #include #include @@ -54,13 +50,16 @@ namespace MWInput /** * @brief Class that handles all input and key bindings for OpenMW. */ - class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener, public ICS::DetectingBindingListener + class InputManager : + public MWBase::InputManager, + public SFO::KeyListener, + public SFO::MouseListener, + public SFO::WindowListener, + public ICS::ChannelListener, + public ICS::DetectingBindingListener { public: InputManager(OEngine::Render::OgreRenderer &_ogre, - MWWorld::Player&_player, - MWBase::WindowManager &_windows, - bool debug, OMW::Engine& engine, const std::string& userFile, bool userFileExists); @@ -68,6 +67,8 @@ namespace MWInput virtual void update(float dt, bool loading); + void setPlayer (MWWorld::Player* player) { mPlayer = player; } + virtual void changeInputMode(bool guiMode); virtual void processChangedSettings(const Settings::CategorySettingVector& changed); @@ -85,12 +86,17 @@ namespace MWInput virtual void resetToDefaultBindings(); public: - virtual bool keyPressed( const OIS::KeyEvent &arg ); - virtual bool keyReleased( const OIS::KeyEvent &arg ); + virtual bool keyPressed(const SDL_KeyboardEvent &arg ); + virtual bool keyReleased( const SDL_KeyboardEvent &arg ); + virtual void textInput (const SDL_TextInputEvent &arg); - virtual bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); - virtual bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); - virtual bool mouseMoved( const OIS::MouseEvent &arg ); + virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual bool mouseMoved( const SFO::MouseMotionEvent &arg ); + + virtual void windowVisibilityChange( bool visible ); + virtual void windowFocusChange( bool have_focus ); + virtual void windowResized (int x, int y); virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); @@ -98,7 +104,7 @@ namespace MWInput , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction); virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction); + , SDL_Keycode key, ICS::Control::ControlChangingDirection direction); virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , unsigned int button, ICS::Control::ControlChangingDirection direction); @@ -119,15 +125,13 @@ namespace MWInput private: OEngine::Render::OgreRenderer &mOgre; - MWWorld::Player &mPlayer; - MWBase::WindowManager &mWindows; + MWWorld::Player* mPlayer; OMW::Engine& mEngine; - ICS::InputControlSystem* mInputCtrl; + ICS::InputControlSystem* mInputBinder; + - OIS::Keyboard* mKeyboard; - OIS::Mouse* mMouse; - OIS::InputManager* mInputManager; + SFO::InputWrapper* mInputManager; std::string mUserFile; @@ -138,7 +142,6 @@ namespace MWInput float mCameraSensitivity; float mUISensitivity; float mCameraYMultiplier; - float mUIYMultiplier; float mPreviewPOVDelay; float mTimeIdle; @@ -150,13 +153,14 @@ namespace MWInput float mMouseX; float mMouseY; int mMouseWheel; - bool mDebug; bool mUserFileExists; + bool mAlwaysRunActive; std::map mControlSwitch; private: void adjustMouseRegion(int width, int height); + MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button); void resetIdleTime(); void updateIdleTime(float dt); @@ -166,6 +170,7 @@ namespace MWInput void toggleSpell(); void toggleWeapon(); void toggleInventory(); + void toggleContainer(); void toggleConsole(); void screenshot(); void toggleJournal(); @@ -217,7 +222,7 @@ namespace MWInput A_CycleWeaponLeft,//Cycling through weapons A_CycleWeaponRight, A_ToggleSneak, //Toggles Sneak - A_ToggleWalk, //Toggle Walking/Running + A_AlwaysRun, //Toggle Walking/Running A_Sneak, A_QuickSave, diff --git a/apps/openmw/mwmechanics/activators.cpp b/apps/openmw/mwmechanics/activators.cpp deleted file mode 100644 index b67fcb216..000000000 --- a/apps/openmw/mwmechanics/activators.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "activators.hpp" - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -namespace MWMechanics -{ - -Activators::Activators() -{ -} - -void Activators::addActivator(const MWWorld::Ptr& ptr) -{ - MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); - if(anim != NULL) - mActivators.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true))); -} - -void Activators::removeActivator (const MWWorld::Ptr& ptr) -{ - PtrControllerMap::iterator iter = mActivators.find(ptr); - if(iter != mActivators.end()) - mActivators.erase(iter); -} - -void Activators::updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) -{ - PtrControllerMap::iterator iter = mActivators.find(old); - if(iter != mActivators.end()) - { - CharacterController ctrl = iter->second; - mActivators.erase(iter); - - ctrl.updatePtr(ptr); - mActivators.insert(std::make_pair(ptr, ctrl)); - } -} - -void Activators::dropActivators (const MWWorld::Ptr::CellStore *cellStore) -{ - PtrControllerMap::iterator iter = mActivators.begin(); - while(iter != mActivators.end()) - { - if(iter->first.getCell()==cellStore) - mActivators.erase(iter++); - else - ++iter; - } -} - -void Activators::update(float duration, bool paused) -{ - if(!paused) - { - for(PtrControllerMap::iterator iter(mActivators.begin());iter != mActivators.end();++iter) - iter->second.update(duration); - } -} - -void Activators::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) -{ - PtrControllerMap::iterator iter = mActivators.find(ptr); - if(iter != mActivators.end()) - iter->second.playGroup(groupName, mode, number); -} -void Activators::skipAnimation(const MWWorld::Ptr& ptr) -{ - PtrControllerMap::iterator iter = mActivators.find(ptr); - if(iter != mActivators.end()) - iter->second.skipAnim(); -} - -} diff --git a/apps/openmw/mwmechanics/activators.hpp b/apps/openmw/mwmechanics/activators.hpp deleted file mode 100644 index 137674a57..000000000 --- a/apps/openmw/mwmechanics/activators.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef GAME_MWMECHANICS_ACTIVATORS_H -#define GAME_MWMECHANICS_ACTOVATRS_H - -#include -#include - -#include "character.hpp" - -namespace MWWorld -{ - class Ptr; - class CellStore; -} - -namespace MWMechanics -{ - class Activators - { - typedef std::map PtrControllerMap; - PtrControllerMap mActivators; - - public: - Activators(); - - void addActivator (const MWWorld::Ptr& ptr); - ///< Register an animated activator - - void removeActivator (const MWWorld::Ptr& ptr); - ///< Deregister an activator - - void updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); - ///< Updates an activator with a new Ptr - - void dropActivators (const MWWorld::CellStore *cellStore); - ///< Deregister all activators in the given cell. - - void update (float duration, bool paused); - ///< Update activator 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/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9632bdf76..ca26e88ce 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -11,12 +11,16 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" +#include "npcstats.hpp" #include "creaturestats.hpp" +#include "movement.hpp" namespace MWMechanics { @@ -28,15 +32,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)); + if(!paused) + updateDrowning(ptr, duration); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) @@ -64,26 +70,24 @@ namespace MWMechanics { CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - int strength = creatureStats.getAttribute(0).getBase(); - int intelligence = creatureStats.getAttribute(1).getBase(); - int willpower = creatureStats.getAttribute(2).getBase(); - int agility = creatureStats.getAttribute(3).getBase(); - int endurance = creatureStats.getAttribute(5).getBase(); + int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); + int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase(); + int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getBase(); + int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getBase(); + int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); double magickaFactor = - creatureStats.getMagicEffects().get (EffectKey (84)).mMagnitude * 0.1 + 0.5; - - DynamicStat health = creatureStats.getHealth(); - health.setBase (static_cast (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ()); - creatureStats.setHealth (health); + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5; DynamicStat magicka = creatureStats.getMagicka(); - magicka.setBase (static_cast (intelligence + magickaFactor * intelligence)); - creatureStats.setMagicka (magicka); - + float diff = (static_cast(intelligence + magickaFactor*intelligence)) - magicka.getBase(); + magicka.modify(diff); + creatureStats.setMagicka(magicka); + DynamicStat fatigue = creatureStats.getFatigue(); - fatigue.setBase (strength+willpower+agility+endurance); - creatureStats.setFatigue (fatigue); + diff = (strength+willpower+agility+endurance) - fatigue.getBase(); + fatigue.modify(diff); + creatureStats.setFatigue(fatigue); } void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) @@ -92,11 +96,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 +118,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()); @@ -134,49 +137,86 @@ namespace MWMechanics void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr) { - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + CreatureStats &creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + const MagicEffects &effects = creatureStats.getMagicEffects(); // attributes - for (int i=0; i<8; ++i) + for(int i = 0;i < ESM::Attribute::Length;++i) { - int modifier = - creatureStats.getMagicEffects().get (EffectKey (79, i)).mMagnitude; + Stat stat = creatureStats.getAttribute(i); + stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyAttribute, i)).mMagnitude - + effects.get(EffectKey(ESM::MagicEffect::DrainAttribute, i)).mMagnitude); - modifier -= creatureStats.getMagicEffects().get (EffectKey (17, i)).mMagnitude; - - creatureStats.getAttribute(i).setModifier (modifier); + creatureStats.setAttribute(i, stat); } // dynamic stats - MagicEffects effects = creatureStats.getMagicEffects(); - - for (int i=0; i<3; ++i) + 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); - } + DynamicStat stat = creatureStats.getDynamic(i); + stat.setModifier(effects.get(EffectKey(80+i)).mMagnitude - + effects.get(EffectKey(18+i)).mMagnitude); + + creatureStats.setDynamic(i, stat); + } + } + + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + NpcStats &stats = ptr.getClass().getNpcStats(ptr); + if(world->isSubmerged(ptr) && + stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).mMagnitude == 0) + { + float timeLeft = 0.0f; + if(stats.getFatigue().getCurrent() == 0) + stats.setTimeToStartDrowning(0); + else + { + timeLeft = stats.getTimeToStartDrowning() - duration; + if(timeLeft < 0.0f) + timeLeft = 0.0f; + stats.setTimeToStartDrowning(timeLeft); + } + if(timeLeft == 0.0f) + { + // If drowning, apply 3 points of damage per second + ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration); + + // Play a drowning sound as necessary for the player + if(ptr == world->getPlayer().getPlayer()) + { + MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager(); + if(!sndmgr->getSoundPlaying(MWWorld::Ptr(), "drown")) + sndmgr->playSound("drown", 1.0f, 1.0f); + } + } + } + else + stats.setTimeToStartDrowning(20); } Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) { + // erase previous death events since we are currently only tracking them while in an active cell + MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied(); + + removeActor(ptr); + 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, true))); - else - mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1, false))); + mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); } void Actors::removeActor (const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) + { + delete iter->second; mActors.erase(iter); + } } void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) @@ -184,10 +224,10 @@ namespace MWMechanics PtrControllerMap::iterator iter = mActors.find(old); if(iter != mActors.end()) { - CharacterController ctrl = iter->second; + CharacterController *ctrl = iter->second; mActors.erase(iter); - ctrl.updatePtr(ptr); + ctrl->updatePtr(ptr); mActors.insert(std::make_pair(ptr, ctrl)); } } @@ -198,7 +238,10 @@ namespace MWMechanics while(iter != mActors.end()) { if(iter->first.getCell()==cellStore) + { + delete iter->second; mActors.erase(iter++); + } else ++iter; } @@ -208,68 +251,61 @@ namespace MWMechanics { mDuration += duration; - if (mDuration>=0.25) + //if (mDuration>=0.25) { float totalDuration = mDuration; mDuration = 0; for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { - if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) + const MWWorld::Class &cls = MWWorld::Class::get(iter->first); + CreatureStats &stats = cls.getCreatureStats(iter->first); + + stats.setLastHitObject(std::string()); + if(!stats.isDead()) { - if(iter->second.getState() >= CharState_Death1) - iter->second.setState(CharState_Idle, true); + if(iter->second->isDead()) + iter->second->resurrect(); updateActor(iter->first, totalDuration); if(iter->first.getTypeName() == typeid(ESM::NPC).name()) updateNpc(iter->first, totalDuration, paused); - if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) + if(!stats.isDead()) continue; } - // workaround: always keep player alive for now - // \todo remove workaround, once player death can be handled - if(iter->first.getRefData().getHandle()=="player") + // If it's the player and God Mode is turned on, keep it alive + if(iter->first.getRefData().getHandle()=="player" && MWBase::Environment::get().getWorld()->getGodModeState()) { - MWMechanics::DynamicStat stat ( - MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth()); + MWMechanics::DynamicStat stat(stats.getHealth()); - if (stat.getModified()<1) + if(stat.getModified()<1) { - stat.setModified (1, 0); - MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat); + stat.setModified(1, 0); + stats.setHealth(stat); } - MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect(); + stats.resurrect(); continue; } - if(iter->second.getState() >= CharState_Death1) + if(iter->second->isDead()) continue; - iter->second.setState(CharState_Death1, false); + iter->second->kill(); - ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; + ++mDeathCount[cls.getId(iter->first)]; - if(MWWorld::Class::get(iter->first).isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox( - "#{sKilledEssential}", std::vector()); + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } } if(!paused) { - mMovement.reserve(mActors.size()); - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - { - Ogre::Vector3 movement = iter->second.update(duration); - mMovement.push_back(std::make_pair(iter->first, movement)); - } - MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration); - - mMovement.clear(); + iter->second->update(duration); } } @@ -278,7 +314,7 @@ namespace MWMechanics 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); @@ -287,16 +323,31 @@ namespace MWMechanics 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); + 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(); + 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 fc4af8dd6..69878a000 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -7,6 +7,7 @@ #include #include "character.hpp" +#include "movement.hpp" #include "../mwbase/world.hpp" namespace Ogre @@ -24,16 +25,14 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; + typedef std::map PtrControllerMap; PtrControllerMap mActors; - MWWorld::PtrMovementList mMovement; - std::map mDeathCount; float mDuration; - void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused); + void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); void adjustMagicEffects (const MWWorld::Ptr& creature); @@ -43,6 +42,7 @@ namespace MWMechanics void calculateRestoration (const MWWorld::Ptr& ptr, float duration); + void updateDrowning (const MWWorld::Ptr& ptr, float duration); public: @@ -77,8 +77,11 @@ 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..556e0b126 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,30 +1,184 @@ #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) -{ -} - -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) + +#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; + 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. +*/ + +namespace MWMechanics { + AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) + : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) + , cellX(std::numeric_limits::max()) + , cellY(std::numeric_limits::max()) + { + 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); + } + } + + AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) + : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) + , cellX(std::numeric_limits::max()) + , cellY(std::numeric_limits::max()) + { + 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); + } + } + + + AiEscort *MWMechanics::AiEscort::clone() const + { + return new AiEscort(*this); + } + + bool 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; + } + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + ESM::Position pos = actor.getRefData().getPosition(); + bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) + { + 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.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 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.0 - 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, true); + } + + if(mPathFinder.checkPathCompleted(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 counter = 0; counter < 3; counter++) + differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]); + + 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]); + 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 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/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index c4dcf89af..2f06b849a 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -3,6 +3,12 @@ #include "aipackage.hpp" +#include "aiwander.hpp" +#include "aiescort.hpp" +#include "aitravel.hpp" +#include "aifollow.hpp" +#include "aiactivate.hpp" + void MWMechanics::AiSequence::copy (const AiSequence& sequence) { for (std::list::const_iterator iter (sequence.mPackages.begin()); @@ -77,3 +83,40 @@ void MWMechanics::AiSequence::queue (const AiPackage& package) { mPackages.push_back (package.clone()); } + +void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) +{ + for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) + { + MWMechanics::AiPackage* package; + if (it->mType == ESM::AI_Wander) + { + ESM::AIWander data = it->mWander; + std::vector idles; + for (int i=0; i<8; ++i) + idles.push_back(data.mIdle[i]); + package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mUnk); + } + else if (it->mType == ESM::AI_Escort) + { + ESM::AITarget data = it->mTarget; + package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + } + else if (it->mType == ESM::AI_Travel) + { + ESM::AITravel data = it->mTravel; + package = new MWMechanics::AiTravel(data.mX, data.mY, data.mZ); + } + else if (it->mType == ESM::AI_Activate) + { + ESM::AIActivate data = it->mActivate; + package = new MWMechanics::AiActivate(data.mName.toString()); + } + else //if (it->mType == ESM::AI_Follow) + { + ESM::AITarget data = it->mTarget; + package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + } + mPackages.push_back(package); + } +} diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index b9ec9d615..9f70daeb8 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -3,6 +3,8 @@ #include +#include + namespace MWWorld { class Ptr; @@ -48,6 +50,8 @@ namespace MWMechanics void queue (const AiPackage& package); ///< Add \a package to the end of the sequence (executed after all other packages have been /// completed) + + void fill (const ESM::AIPackageList& list); }; } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 897dd1748..d47a49c70 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,25 +1,111 @@ #include "aitravel.hpp" -#include -MWMechanics::AiTravel::AiTravel(float x, float y, float z) -: mX(x),mY(y),mZ(z) -{ -} +#include "movement.hpp" -MWMechanics::AiTravel * MWMechanics::AiTravel::clone() const -{ - return new AiTravel(*this); -} +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" -bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) +namespace { - std::cout << "AiTravel completed.\n"; - return true; + float sgn(float a) + { + if(a > 0) + return 1.0; + return -1.0; + } } -int MWMechanics::AiTravel::getTypeId() const +namespace MWMechanics { - return 1; -} + AiTravel::AiTravel(float x, float y, float z) + : mX(x),mY(y),mZ(z),mPathFinder() + , cellX(std::numeric_limits::max()) + , cellY(std::numeric_limits::max()) + { + } + + AiTravel *MWMechanics::AiTravel::clone() const + { + return new AiTravel(*this); + } + + bool AiTravel::execute (const MWWorld::Ptr& actor) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + ESM::Position pos = actor.getRefData().getPosition(); + Movement &movement = actor.getClass().getMovementSettings(actor); + const ESM::Cell *cell = actor.getCell()->mCell; + + MWWorld::Ptr player = world->getPlayer().getPlayer(); + if(cell->mData.mX != player.getCell()->mCell->mData.mX) + { + int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX); + //check if actor is near the border of an inactive cell. If so, stop walking. + if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > + sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) + { + movement.mPosition[1] = 0; + return false; + } + } + if(cell->mData.mY != player.getCell()->mCell->mData.mY) + { + int sideY = sgn(cell->mData.mY - player.getCell()->mCell->mData.mY); + //check if actor is near the border of an inactive cell. If so, stop walking. + if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > + sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) + { + movement.mPosition[1] = 0; + return false; + } + } + + const ESM::Pathgrid *pathgrid = world->getStore().get().search(*cell); + bool cellChange = cell->mData.mX != cellX || cell->mData.mY != cellY; + if(!mPathFinder.isPathConstructed() || cellChange) + { + cellX = cell->mData.mX; + cellY = cell->mData.mY; + float xCell = 0; + float yCell = 0; + if(cell->isExterior()) + { + xCell = cell->mData.mX * ESM::Land::REAL_SIZE; + yCell = cell->mData.mY * ESM::Land::REAL_SIZE; + } + + ESM::Pathgrid::Point dest; + dest.mX = mX; + 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, true); + } + + if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + movement.mPosition[1] = 0; + return true; + } + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + world->rotateObject(actor, 0, 0, zAngle, false); + movement.mPosition[1] = 1; + + return false; + } + + int 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..96a41883b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,23 +1,306 @@ #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" -MWMechanics::AiPackage * MWMechanics::AiWander::clone() const -{ - return new AiWander(*this); -} +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include -bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) +namespace { - std::cout << "AiWadner completed.\n"; - return true; + float sgn(float a) + { + if(a > 0) + return 1.0; + return -1.0; + } } -int MWMechanics::AiWander::getTypeId() const +namespace MWMechanics { - return 0; + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) + , mCellX(std::numeric_limits::max()) + , mCellY(std::numeric_limits::max()) + , mXCell(0) + , mYCell(0) + , mX(0) + , mY(0) + , mZ(0) + { + 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; + + 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; + } + + AiPackage * MWMechanics::AiWander::clone() const + { + return new AiWander(*this); + } + + bool AiWander::execute (const MWWorld::Ptr& actor) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + if(mDuration) + { + // End package if duration is complete or mid-night hits: + MWWorld::TimeStamp currentTime = world->getTimeStamp(); + if(currentTime.getHour() >= mStartTime.getHour() + mDuration) + { + if(!mRepeat) + { + stopWalking(actor); + return true; + } + else + mStartTime = currentTime; + } + else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()) + { + if(!mRepeat) + { + stopWalking(actor); + return true; + } + else + mStartTime = currentTime; + } + } + + ESM::Position pos = actor.getRefData().getPosition(); + + if(!mStoredAvailableNodes) + { + mStoredAvailableNodes = true; + mPathgrid = world->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; + } + } + + // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles. + if(mDistance && (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 = 0; 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+2; + 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 = world->getTimeStamp(); + mStartTime = currentTime; + playIdle(actor, mPlayedIdle); + mChooseAction = false; + mIdleNow = true; + } + } + + if(mIdleNow) + { + if(!checkIdle(actor, mPlayedIdle)) + { + 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); + + 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, false); + + if(mPathFinder.isPathConstructed()) + { + // 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; + + mMoveNow = false; + mWalking = true; + } + // Choose a different node and delete this one from possible nodes because it is uncreachable: + else + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + } + } + + if(mWalking) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + world->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mChooseAction = true; + } + } + + return false; + } + + int AiWander::getTypeId() const + { + return 0; + } + + void AiWander::stopWalking(const MWWorld::Ptr& actor) + { + mPathFinder.clearPath(); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + } + + void 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 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..c82ccc215 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); + 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 718200372..1d992be41 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -287,7 +287,7 @@ void MWMechanics::Alchemy::addPotion (const std::string& name) } MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), record->mId); - MWWorld::Class::get (mAlchemist).getContainerStore (mAlchemist).add (ref.getPtr()); + MWWorld::Class::get (mAlchemist).getContainerStore (mAlchemist).add (ref.getPtr(), mAlchemist); } void MWMechanics::Alchemy::increaseSkill() diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 62958db8d..ec2bb1b59 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -21,33 +21,59 @@ #include +#include "movement.hpp" +#include "npcstats.hpp" +#include "creaturestats.hpp" +#include "security.hpp" + #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" +namespace +{ + +int getBestAttack (const ESM::Weapon* weapon) +{ + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + if (slash >= chop && slash >= thrust) + return MWMechanics::CreatureStats::AT_Slash; + else if (chop >= slash && chop >= thrust) + return MWMechanics::CreatureStats::AT_Chop; + else + return MWMechanics::CreatureStats::AT_Thrust; +} + +} namespace MWMechanics { -static const struct { +struct StateInfo { CharacterState state; const char groupname[32]; -} sStateList[] = { - { CharState_Idle, "idle" }, - { CharState_Idle2, "idle2" }, - { CharState_Idle3, "idle3" }, - { CharState_Idle4, "idle4" }, - { CharState_Idle5, "idle5" }, - { CharState_Idle6, "idle6" }, - { CharState_Idle7, "idle7" }, - { CharState_Idle8, "idle8" }, - { CharState_Idle9, "idle9" }, - { CharState_IdleSwim, "idleswim" }, - { CharState_IdleSneak, "idlesneak" }, +}; +static const StateInfo sDeathList[] = { + { CharState_Death1, "death1" }, + { CharState_Death2, "death2" }, + { CharState_Death3, "death3" }, + { CharState_Death4, "death4" }, + { CharState_Death5, "death5" }, + { CharState_SwimDeath, "swimdeath" }, +}; +static const StateInfo *sDeathListEnd = &sDeathList[sizeof(sDeathList)/sizeof(sDeathList[0])]; + +static const StateInfo sMovementList[] = { { CharState_WalkForward, "walkforward" }, { CharState_WalkBack, "walkback" }, { CharState_WalkLeft, "walkleft" }, @@ -75,61 +101,303 @@ static const struct { { CharState_Jump, "jump" }, - { CharState_Death1, "death1" }, - { CharState_Death2, "death2" }, - { CharState_Death3, "death3" }, - { CharState_Death4, "death4" }, - { CharState_Death5, "death5" }, + { CharState_TurnLeft, "turnleft" }, + { CharState_TurnRight, "turnright" }, +}; +static const StateInfo *sMovementListEnd = &sMovementList[sizeof(sMovementList)/sizeof(sMovementList[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 shortgroup[16]; + const char longgroup[16]; +} sWeaponTypeList[] = { + { WeapType_HandToHand, "hh", "handtohand" }, + { WeapType_OneHand, "1h", "weapononehand" }, + { WeapType_TwoHand, "2c", "weapontwohand" }, + { WeapType_TwoWide, "2w", "weapontwowide" }, + { WeapType_BowAndArrow, "1h", "bowandarrow" }, + { WeapType_Crossbow, "crossbow", "crossbow" }, + { WeapType_ThowWeapon, "1h", "throwweapon" }, + { WeapType_PickProbe, "1h", "pickprobe" }, + { WeapType_Spell, "spell", "spellcast" }, }; -static const size_t sStateListSize = sizeof(sStateList)/sizeof(sStateList[0]); +static const WeaponInfo *sWeaponTypeListEnd = &sWeaponTypeList[sizeof(sWeaponTypeList)/sizeof(sWeaponTypeList[0])]; + +class FindWeaponType { + WeaponType type; -static void getStateInfo(CharacterState state, std::string *group) +public: + FindWeaponType(WeaponType _type) : type(_type) { } + + bool operator()(const WeaponInfo &weap) const + { return weap.type == type; } +}; + + +void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { - for(size_t i = 0;i < sStateListSize;i++) + const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); + + if(force || idle != mIdleState) + { + mIdleState = idle; + + std::string idle; + // Only play "idleswim" or "idlesneak" if they exist. Otherwise, fallback to + // "idle"+weapon or "idle". + if(mIdleState == CharState_IdleSwim && mAnimation->hasAnimation("idleswim")) + idle = "idleswim"; + else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak")) + idle = "idlesneak"; + else if(mIdleState != CharState_None) + { + idle = "idle"; + if(weap != sWeaponTypeListEnd) + { + idle += weap->shortgroup; + if(!mAnimation->hasAnimation(idle)) + idle = "idle"; + } + } + + mAnimation->disable(mCurrentIdle); + mCurrentIdle = idle; + if(!mCurrentIdle.empty()) + mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false, + 1.0f, "start", "stop", 0.0f, ~0ul); + } + + if(force && mJumpState != JumpState_None) + { + std::string jump; + MWRender::Animation::Group jumpgroup = MWRender::Animation::Group_All; + if(mJumpState != JumpState_None) + { + jump = "jump"; + if(weap != sWeaponTypeListEnd) + { + jump += weap->shortgroup; + if(!mAnimation->hasAnimation(jump)) + { + jumpgroup = MWRender::Animation::Group_LowerBody; + jump = "jump"; + } + } + } + + if(mJumpState == JumpState_Falling) + { + int mode = ((jump == mCurrentJump) ? 2 : 1); + + mAnimation->disable(mCurrentJump); + mCurrentJump = jump; + mAnimation->play(mCurrentJump, Priority_Jump, jumpgroup, false, + 1.0f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); + } + else + { + mAnimation->disable(mCurrentJump); + mCurrentJump.clear(); + mAnimation->play(jump, Priority_Jump, jumpgroup, true, + 1.0f, "loop stop", "stop", 0.0f, 0); + } + } + + if(force || movement != mMovementState) { - if(sStateList[i].state == state) + mMovementState = movement; + + std::string movement; + MWRender::Animation::Group movegroup = MWRender::Animation::Group_All; + const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); + if(movestate != sMovementListEnd) + { + movement = movestate->groupname; + if(weap != sWeaponTypeListEnd && movement.find("swim") == std::string::npos) + { + movement += weap->shortgroup; + if(!mAnimation->hasAnimation(movement)) + { + movegroup = MWRender::Animation::Group_LowerBody; + movement = movestate->groupname; + } + } + + if(!mAnimation->hasAnimation(movement)) + { + std::string::size_type swimpos = movement.find("swim"); + if(swimpos == std::string::npos) + movement.clear(); + else + { + movegroup = MWRender::Animation::Group_LowerBody; + movement.erase(swimpos, 4); + if(!mAnimation->hasAnimation(movement)) + movement.clear(); + } + } + } + + /* If we're playing the same animation, restart from the loop start instead of the + * beginning. */ + int mode = ((movement == mCurrentMovement) ? 2 : 1); + + mAnimation->disable(mCurrentMovement); + mCurrentMovement = movement; + if(!mCurrentMovement.empty()) { - *group = sStateList[i].groupname; - return; + float vel, speedmult = 1.0f; + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + speedmult = mMovementSpeed / vel; + mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, + speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } } - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(state)); } -CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) - : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) +void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) { - if(!mAnimation) - return; + const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); + if(info != sWeaponTypeListEnd) + group = info->longgroup; +} - mAnimation->setController(this); - getStateInfo(mState, &mCurrentGroup); - if(ptr.getTypeName() == typeid(ESM::Activator).name()) +MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +{ + if(stats.getDrawState() == DrawState_Spell) { - /* Don't accumulate with activators (they don't get moved). */ - mAnimation->setAccumulation(Ogre::Vector3::ZERO); + *weaptype = WeapType_Spell; + return inv.end(); } - else + + if(stats.getDrawState() == MWMechanics::DrawState_Weapon) { - /* 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)); + MWWorld::ContainerStoreIterator 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; + } + } + } + + return weapon; } - if(mAnimation->hasAnimation(mCurrentGroup)) - mAnimation->play(mCurrentGroup, "stop", "stop", loop); + + return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); } -CharacterController::CharacterController(const CharacterController &rhs) - : mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue) - , mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState) - , mSkipAnim(rhs.mSkipAnim) + +CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) + : mPtr(ptr) + , mAnimation(anim) + , mIdleState(CharState_None) + , mMovementState(CharState_None) + , mMovementSpeed(0.0f) + , mDeathState(CharState_None) + , mUpperBodyState(UpperCharState_Nothing) + , mJumpState(JumpState_None) + , mWeaponType(WeapType_None) + , mSkipAnim(false) + , mSecondsOfRunning(0) + , mSecondsOfSwimming(0) { if(!mAnimation) return; - /* We've been copied. Update the animation with the new controller. */ - mAnimation->setController(this); + + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + if(cls.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)); + + if(mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + getActiveWeapon(cls.getNpcStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); + if(mWeaponType != WeapType_None) + { + getWeaponGroup(mWeaponType, mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } + } + + if(!cls.getCreatureStats(mPtr).isDead()) + mIdleState = CharState_Idle; + else + { + /* FIXME: Get the actual death state used. */ + mDeathState = CharState_Death1; + } + } + else + { + /* Don't accumulate with non-actors. */ + mAnimation->setAccumulation(Ogre::Vector3(0.0f)); + + mIdleState = CharState_Idle; + } + + refreshCurrentAnims(mIdleState, mMovementState, true); + if(mDeathState != CharState_None) + { + const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); + if(state == sDeathListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); + + mCurrentDeath = state->groupname; + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, + false, 1.0f, "start", "stop", 1.0f, 0); + } } CharacterController::~CharacterController() @@ -143,105 +411,504 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) } -void CharacterController::markerEvent(float time, const std::string &evt) +bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) { - if(evt == "stop") + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + NpcStats &stats = cls.getNpcStats(mPtr); + WeaponType weaptype = WeapType_None; + MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); + const bool isWerewolf = stats.isWerewolf(); + + bool forcestateupdate = false; + if(weaptype != mWeaponType) { - if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) + forcestateupdate = true; + + std::string weapgroup; + if(weaptype == WeapType_None) { - mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "loop start", "stop", false); + getWeaponGroup(mWeaponType, weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1.0f, "unequip start", "unequip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_UnEquipingWeap; } - else if(mAnimQueue.size() > 0) + else { - mAnimQueue.pop_front(); - if(mAnimQueue.size() > 0) + getWeaponGroup(weaptype, weapgroup); + mAnimation->showWeapons(false); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1.0f, "equip start", "equip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_EquipingWeap; + if(isWerewolf) { - mCurrentGroup = mAnimQueue.front(); - mAnimation->play(mCurrentGroup, "start", "stop", false); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfEquip"); + if(sound) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f); + } } } - return; + + if(weapon != inv.end() && !(weaptype == WeapType_None && mWeaponType == WeapType_Spell)) + { + std::string soundid = (weaptype == 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); + } + } + + mWeaponType = weaptype; + getWeaponGroup(mWeaponType, mCurrentWeapon); } - std::cerr<< "Unhandled animation event: "<getSoundPlaying(mPtr, "WolfRun")) + sndMgr->playSound3D(mPtr, "WolfRun", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, + MWBase::SoundManager::Play_Loop); + } + else + sndMgr->stopSound3D(mPtr, "WolfRun"); + } + + bool isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()); + float weapSpeed = 1.0f; + if(isWeapon) + weapSpeed = weapon->get()->mBase->mData.mSpeed; + + float complete; + bool animPlaying; + if(stats.getAttackingOrSpell()) + { + if(mUpperBodyState == UpperCharState_WeapEquiped) + { + mAttackType.clear(); + if(mWeaponType == WeapType_Spell) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + + const std::string spellid = stats.getSpells().getSelectedSpell(); + if(!spellid.empty()) + { + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + const ESM::Spell *spell = store.get().find(spellid); + const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); -Ogre::Vector3 CharacterController::update(float duration) + const ESM::MagicEffect *effect; + effect = store.get().find(effectentry.mEffectID); + + switch(effectentry.mRange) + { + case 0: mAttackType = "self"; break; + case 1: mAttackType = "touch"; break; + case 2: mAttackType = "target"; break; + } + + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + weapSpeed, mAttackType+" start", mAttackType+" stop", + 0.0f, 0); + mUpperBodyState = UpperCharState_CastingSpell; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mCastSound.empty()) + sndMgr->playSound3D(mPtr, effect->mCastSound, 1.0f, 1.0f); + else + sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); + } + } + else if(mWeaponType == WeapType_PickProbe) + { + MWWorld::Ptr item = *weapon; + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); + std::string resultMessage, resultSound; + + if(!target.isEmpty()) + { + if(item.getTypeName() == typeid(ESM::Lockpick).name()) + Security(mPtr).pickLock(target, item, resultMessage, resultSound); + else if(item.getTypeName() == typeid(ESM::Probe).name()) + Security(mPtr).probeTrap(target, item, resultMessage, resultSound); + } + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1.0f, "start", "stop", 0.0, 0); + mUpperBodyState = UpperCharState_FollowStartToFollowStop; + + if(!resultMessage.empty()) + MWBase::Environment::get().getWindowManager()->messageBox(resultMessage); + if(!resultSound.empty()) + MWBase::Environment::get().getSoundManager()->playSound(resultSound, 1.0f, 1.0f); + + // tool used up? + if(!item.getRefData().getCount()) + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); + else + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); + } + else + { + if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || + mWeaponType == WeapType_ThowWeapon) + mAttackType = "shoot"; + else + { + int attackType = stats.getAttackType(); + if(isWeapon && Settings::Manager::getBool("best attack", "Game")) + attackType = getBestAttack(weapon->get()->mBase); + + if (attackType == MWMechanics::CreatureStats::AT_Chop) + mAttackType = "chop"; + else if (attackType == MWMechanics::CreatureStats::AT_Slash) + mAttackType = "slash"; + else + mAttackType = "thrust"; + } + + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" start", mAttackType+" min attack", + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToMinAttack; + } + } + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); + } + else + { + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); + if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) + { + if(mAttackType != "shoot") + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + + if(isWerewolf) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfSwing"); + if(sound) + sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f); + } + else + { + std::string sound = "SwishM"; + if(complete < 0.5f) + sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack + else if(complete < 1.0f) + sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack + else + sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack + } + } + stats.setAttackStrength(complete); + + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", + 1.0f-complete, 0); + mUpperBodyState = UpperCharState_MaxAttackToMinHit; + } + } + + if(!animPlaying) + { + if(mUpperBodyState == UpperCharState_EquipingWeap || + mUpperBodyState == UpperCharState_FollowStartToFollowStop || + mUpperBodyState == UpperCharState_CastingSpell) + mUpperBodyState = UpperCharState_WeapEquiped; + else if(mUpperBodyState == UpperCharState_UnEquipingWeap) + mUpperBodyState = UpperCharState_Nothing; + } + else if(complete >= 1.0f) + { + if(mUpperBodyState == UpperCharState_StartToMinAttack) + { + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min attack", mAttackType+" max attack", + 0.0f, 0); + mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + } + else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit) + { + mAnimation->disable(mCurrentWeapon); + if(mAttackType == "shoot") + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min hit", mAttackType+" follow start", + 0.0f, 0); + else + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min hit", mAttackType+" hit", + 0.0f, 0); + mUpperBodyState = UpperCharState_MinHitToHit; + } + else if(mUpperBodyState == UpperCharState_MinHitToHit) + { + mAnimation->disable(mCurrentWeapon); + if(mAttackType == "shoot") + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + weapSpeed, mAttackType+" follow start", mAttackType+" follow stop", + 0.0f, 0); + else + { + float str = stats.getAttackStrength(); + std::string start = mAttackType+((str < 0.5f) ? " small follow start" + : (str < 1.0f) ? " medium follow start" + : " large follow start"); + std::string stop = mAttackType+((str < 0.5f) ? " small follow stop" + : (str < 1.0f) ? " medium follow stop" + : " large follow stop"); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + weapSpeed, start, stop, 0.0f, 0); + } + mUpperBodyState = UpperCharState_FollowStartToFollowStop; + } + } + + + 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, + 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } + else if(mAnimation->isPlaying("torch")) + mAnimation->disable("torch"); + + return forcestateupdate; +} + +void CharacterController::update(float duration) { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); Ogre::Vector3 movement(0.0f); - float speed = 0.0f; - if(!(getState() >= CharState_Death1)) + if(!cls.isActor()) { - const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Class &cls = MWWorld::Class::get(mPtr); - const Ogre::Vector3 &vec = cls.getMovementVector(mPtr); + 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, + 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); + } + } + } + else if(!cls.getCreatureStats(mPtr).isDead()) + { 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); - speed = cls.getSpeed(mPtr); + bool flying = world->isFlying(mPtr); + Ogre::Vector3 vec = cls.getMovementVector(mPtr); + Ogre::Vector3 rot = cls.getRotationVector(mPtr); + mMovementSpeed = cls.getSpeed(mPtr); - /* 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) + vec.x *= mMovementSpeed; + vec.y *= mMovementSpeed; + + CharacterState movestate = CharState_None; + CharacterState idlestate = CharState_SpecialIdle; + bool forcestateupdate = false; + + isrunning = isrunning && std::abs(vec[0])+std::abs(vec[1]) > 0.0f; + + // advance athletics + if(std::abs(vec[0])+std::abs(vec[1]) > 0.0f && mPtr.getRefData().getHandle() == "player") + { + 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; + } + } + } + + if(sneak || inwater || flying) + vec.z = 0.0f; + + if(!onground && !flying && !inwater) { - float x = cls.getJump(mPtr); + const MWWorld::Store &gmst = world->getStore().get(); + + forcestateupdate = (mJumpState != JumpState_Falling); + mJumpState = JumpState_Falling; + // This is a guess. All that seems to be known is that "While the player is in the + // air, fJumpMoveBase and fJumpMoveMult governs air control." Assuming Acrobatics + // plays a role, this makes the most sense. + float mult = 0.0f; + if(cls.isNpc()) + { + const NpcStats &stats = cls.getNpcStats(mPtr); + mult = gmst.find("fJumpMoveBase")->getFloat() + + (stats.getSkill(ESM::Skill::Acrobatics).getModified()/100.0f * + gmst.find("fJumpMoveMult")->getFloat()); + } + + vec.x *= mult; + vec.y *= mult; + vec.z = 0.0f; + } + else if(vec.z > 0.0f && mJumpState == JumpState_None) + { + float z = cls.getJump(mPtr); if(vec.x == 0 && vec.y == 0) - movement.z += x*duration; + vec = Ogre::Vector3(0.0f, 0.0f, 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(); - //movement += Ogre::Vector3(lat.x, lat.y, 1.0f) * x * 0.707f * duration; - movement.z += x * 0.707f * duration; + Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy(); + vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f; } //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; } - - if(std::abs(vec.x/2.0f) > std::abs(vec.y) && speed > 0.0f) + else if(mJumpState == JumpState_Falling) { - if(vec.x > 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) - : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true); - - else if(vec.x < 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) - : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true); + forcestateupdate = true; + mJumpState = JumpState_Landing; + vec.z = 0.0f; + } + else + { + if(!(vec.z > 0.0f)) + mJumpState = JumpState_None; + vec.z = 0.0f; - // Apply any forward/backward movement manually - movement.y += vec.y * (speed*duration); + if(std::abs(vec.x/2.0f) > std::abs(vec.y)) + { + if(vec.x > 0.0f) + movestate = (inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) + : (sneak ? CharState_SneakRight + : (isrunning ? CharState_RunRight : CharState_WalkRight))); + else if(vec.x < 0.0f) + movestate = (inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) + : (sneak ? CharState_SneakLeft + : (isrunning ? CharState_RunLeft : CharState_WalkLeft))); + } + else if(vec.y != 0.0f) + { + if(vec.y > 0.0f) + movestate = (inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) + : (sneak ? CharState_SneakForward + : (isrunning ? CharState_RunForward : CharState_WalkForward))); + else if(vec.y < 0.0f) + movestate = (inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) + : (sneak ? CharState_SneakBack + : (isrunning ? CharState_RunBack : CharState_WalkBack))); + } + else if(rot.z != 0.0f && !inwater && !sneak) + { + if(rot.z > 0.0f) + movestate = CharState_TurnRight; + else if(rot.z < 0.0f) + movestate = CharState_TurnLeft; + } } - else if(vec.y != 0.0f && speed > 0.0f) + + if(movestate != CharState_None) + clearAnimQueue(); + + if(mAnimQueue.empty()) + idlestate = (inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)); + else if(mAnimQueue.size() > 1) { - if(vec.y > 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) - : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true); - - else if(vec.y < 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) - : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true); - // Apply any sideways movement manually - movement.x += vec.x * (speed*duration); + 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, + 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); + } } - else if(mAnimQueue.size() == 0) - setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); + + if(cls.isNpc()) + forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; + + refreshCurrentAnims(idlestate, movestate, forcestateupdate); + + rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); + world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); + + world->queueMovement(mPtr, vec); + movement = vec; + } + else if(cls.getCreatureStats(mPtr).isDead()) + { + MWBase::Environment::get().getWorld()->enableActorCollision(mPtr, false); + world->queueMovement(mPtr, Ogre::Vector3(0.0f)); } if(mAnimation && !mSkipAnim) { - mAnimation->setSpeed(speed); - movement += mAnimation->runAnimation(duration); + Ogre::Vector3 moved = mAnimation->runAnimation(duration); + if(duration > 0.0f) + moved /= duration; + else + moved = Ogre::Vector3(0.0f); + + // Ensure we're moving in generally the right direction + if(mMovementSpeed > 0.f) + { + if((movement.x < 0.0f && movement.x < moved.x*2.0f) || + (movement.x > 0.0f && movement.x > moved.x*2.0f)) + moved.x = movement.x; + if((movement.y < 0.0f && movement.y < moved.y*2.0f) || + (movement.y > 0.0f && movement.y > moved.y*2.0f)) + moved.y = movement.y; + if((movement.z < 0.0f && movement.z < moved.z*2.0f) || + (movement.z > 0.0f && movement.z > moved.z*2.0f)) + moved.z = movement.z; + } + // Update movement + if(moved.squaredLength() > 1.0f) + world->queueMovement(mPtr, moved); } mSkipAnim = false; - - return movement; } @@ -252,20 +919,23 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int else { count = std::max(count, 1); - if(mode != 0 || mAnimQueue.size() == 0) + if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().first)) { - mAnimQueue.clear(); - while(count-- > 0) - mAnimQueue.push_back(groupname); - mCurrentGroup = groupname; - mState = CharState_SpecialIdle; - mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false); + clearAnimQueue(); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); + + mAnimation->disable(mCurrentIdle); + mCurrentIdle.clear(); + + mIdleState = CharState_SpecialIdle; + mAnimation->play(groupname, Priority_Default, + MWRender::Animation::Group_All, false, 1.0f, + ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) { mAnimQueue.resize(1); - while(count-- > 0) - mAnimQueue.push_back(groupname); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); } } } @@ -275,28 +945,101 @@ void CharacterController::skipAnim() mSkipAnim = true; } +bool CharacterController::isAnimPlaying(const std::string &groupName) +{ + if(mAnimation == NULL) + return false; + return mAnimation->isPlaying(groupName); +} -void CharacterController::setState(CharacterState state, bool loop) + +void CharacterController::clearAnimQueue() { - if(mState == state) - { - if(mAnimation) - mAnimation->setLooping(loop); + if(!mAnimQueue.empty()) + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.clear(); +} + + +void CharacterController::forceStateUpdate() +{ + if(!mAnimation) return; + clearAnimQueue(); + + refreshCurrentAnims(mIdleState, mMovementState, true); + if(mDeathState != CharState_None) + { + const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); + if(state == sDeathListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); + + mCurrentDeath = state->groupname; + if(!mAnimation->getInfo(mCurrentDeath)) + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, + false, 1.0f, "start", "stop", 0.0f, 0); } - mState = state; +} - if(!mAnimation) +void CharacterController::kill() +{ + if(mDeathState != CharState_None) return; - mAnimQueue.clear(); - std::string anim; - getStateInfo(mState, &anim); - if(mAnimation->hasAnimation(anim)) + if(mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + const StateInfo *state = NULL; + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) + { + mDeathState = CharState_SwimDeath; + state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); + if(state == sDeathListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); + } + + static const CharacterState deathstates[5] = { + CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5 + }; + std::vector states(&deathstates[0], &deathstates[5]); + + while(states.size() > 1 && (!state || !mAnimation->hasAnimation(state->groupname))) + { + int pos = (int)(rand()/((double)RAND_MAX+1.0)*states.size()); + mDeathState = states[pos]; + states.erase(states.begin()+pos); + + state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); + if(state == sDeathListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); + } + mCurrentDeath = state->groupname; + } + else + { + mDeathState = CharState_Death1; + mCurrentDeath = "death1"; + } + + if(mAnimation) { - mCurrentGroup = anim; - mAnimation->play(mCurrentGroup, "start", "stop", loop); + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, + false, 1.0f, "start", "stop", 0.0f, 0); + mAnimation->disable(mCurrentIdle); } + + mIdleState = CharState_None; + mCurrentIdle.clear(); +} + +void CharacterController::resurrect() +{ + if(mDeathState == CharState_None) + return; + + if(mAnimation) + mAnimation->disable(mCurrentDeath); + mCurrentDeath.empty(); + mDeathState = CharState_None; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 46f0690e7..c943b9597 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -5,6 +5,12 @@ #include "../mwworld/ptr.hpp" +namespace MWWorld +{ + class ContainerStoreIterator; + class InventoryStore; +} + namespace MWRender { class Animation; @@ -13,7 +19,24 @@ namespace MWRender namespace MWMechanics { +class Movement; +class NpcStats; + +enum Priority { + Priority_Default, + Priority_Jump, + Priority_Movement, + Priority_Weapon, + Priority_Torch, + + Priority_Death, + + Num_Priorities +}; + enum CharacterState { + CharState_None, + CharState_SpecialIdle, CharState_Idle, CharState_Idle2, @@ -52,14 +75,51 @@ enum CharacterState { 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 + CharState_Death5, + CharState_SwimDeath +}; + +enum WeaponType { + WeapType_None, + + WeapType_HandToHand, + WeapType_OneHand, + WeapType_TwoHand, + WeapType_TwoWide, + WeapType_BowAndArrow, + WeapType_Crossbow, + WeapType_ThowWeapon, + WeapType_PickProbe, + + WeapType_Spell +}; + +enum UpperBodyCharacterState { + UpperCharState_Nothing, + UpperCharState_EquipingWeap, + UpperCharState_UnEquipingWeap, + UpperCharState_WeapEquiped, + UpperCharState_StartToMinAttack, + UpperCharState_MinAttackToMaxAttack, + UpperCharState_MaxAttackToMinHit, + UpperCharState_MinHitToHit, + UpperCharState_FollowStartToFollowStop, + UpperCharState_CastingSpell +}; + +enum JumpingState { + JumpState_None, + JumpState_Falling, + JumpState_Landing }; class CharacterController @@ -67,34 +127,65 @@ class CharacterController MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; - typedef std::deque AnimationQueue; + typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; - std::string mCurrentGroup; - CharacterState mState; + CharacterState mIdleState; + std::string mCurrentIdle; + + CharacterState mMovementState; + std::string mCurrentMovement; + float mMovementSpeed; + + CharacterState mDeathState; + std::string mCurrentDeath; + + UpperBodyCharacterState mUpperBodyState; + + JumpingState mJumpState; + std::string mCurrentJump; + + WeaponType mWeaponType; + std::string mCurrentWeapon; + bool mSkipAnim; -protected: - /* Called by the animation whenever a new text key is reached. */ - void markerEvent(float time, const std::string &evt); + // counted for skill increase + float mSecondsOfSwimming; + float mSecondsOfRunning; + + std::string mAttackType; // slash, chop or thrust + + void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); + + static void getWeaponGroup(WeaponType weaptype, std::string &group); - friend class MWRender::Animation; + static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, + MWWorld::InventoryStore &inv, + WeaponType *weaptype); + + void clearAnimQueue(); + + bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); public: - CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); - CharacterController(const CharacterController &rhs); + CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); void updatePtr(const MWWorld::Ptr &ptr); - Ogre::Vector3 update(float duration); + void update(float duration); void playGroup(const std::string &groupname, int mode, int count); void skipAnim(); + bool isAnimPlaying(const std::string &groupName); + + void kill(); + void resurrect(); + bool isDead() const + { return mDeathState != CharState_None; } - void setState(CharacterState state, bool loop); - CharacterState getState() const - { return mState; } + void forceStateUpdate(); }; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 4be5d55b2..52ed43be3 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -10,21 +10,43 @@ namespace MWMechanics { CreatureStats::CreatureStats() - : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), - mAttacked (false), mHostile (false) + : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), + mTalkedTo (false), mAlarmed (false), + mAttacked (false), mHostile (false), + mAttackingOrSpell(false), mAttackType(AT_Chop), + mIsWerewolf(false) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; } - void CreatureStats::increaseLevelHealthBonus (float value) + float CreatureStats::getLevelHealthBonus () const { - mLevelHealthBonus += value; + return mLevelHealthBonus; } - float CreatureStats::getLevelHealthBonus () const + void CreatureStats::levelUp() { - return mLevelHealthBonus; + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); + + // "When you gain a level, in addition to increasing three primary attributes, your Health + // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, + // the Health increase is calculated from the increased Endurance" + mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat(); + updateHealth(); + + mLevel++; + } + + void CreatureStats::updateHealth() + { + const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); + const int strength = getAttribute(ESM::Attribute::Strength).getBase(); + + setHealth(static_cast (0.5 * (strength + endurance)) + mLevelHealthBonus); } const AiSequence& CreatureStats::getAiSequence() const @@ -56,7 +78,7 @@ namespace MWMechanics if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } - return mAttributes[index]; + return (!mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]); } const DynamicStat &CreatureStats::getHealth() const @@ -89,6 +111,11 @@ namespace MWMechanics return mMagicEffects; } + bool CreatureStats::getAttackingOrSpell() const + { + return mAttackingOrSpell; + } + int CreatureStats::getLevel() const { return mLevel; @@ -105,7 +132,7 @@ namespace MWMechanics if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } - return mAttributes[index]; + return (!mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]); } const DynamicStat &CreatureStats::getDynamic(int index) const @@ -141,7 +168,10 @@ namespace MWMechanics if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } - mAttributes[index] = value; + if(!mIsWerewolf) + mAttributes[index] = value; + else + mWerewolfAttributes[index] = value; } void CreatureStats::setHealth(const DynamicStat &value) @@ -167,7 +197,12 @@ namespace MWMechanics mDynamic[index] = value; if (index==0 && mDynamic[index].getCurrent()<1) + { + if (!mDead) + mDied = true; + mDead = true; + } } void CreatureStats::setLevel(int level) @@ -185,6 +220,11 @@ namespace MWMechanics mMagicEffects = effects; } + void CreatureStats::setAttackingOrSpell(bool attackingOrSpell) + { + mAttackingOrSpell = attackingOrSpell; + } + void CreatureStats::setAiSetting (int index, int value) { assert (index>=0 && index<4); @@ -196,6 +236,16 @@ namespace MWMechanics return mDead; } + bool CreatureStats::hasDied() const + { + return mDied; + } + + void CreatureStats::clearHasDied() + { + mDied = false; + } + void CreatureStats::resurrect() { if (mDead) @@ -272,4 +322,24 @@ namespace MWMechanics { return false; } + + float CreatureStats::getEvasion() const + { + float evasion = (getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + + (getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); + evasion *= getFatigueTerm(); + evasion += mMagicEffects.get(EffectKey(ESM::MagicEffect::Sanctuary)).mMagnitude; + + return evasion; + } + + void CreatureStats::setLastHitObject(const std::string& objectid) + { + mLastHitObject = objectid; + } + + const std::string &CreatureStats::getLastHitObject() const + { + return mLastHitObject; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 3375c1af8..1a7cb5d69 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -28,11 +28,21 @@ namespace MWMechanics AiSequence mAiSequence; float mLevelHealthBonus; bool mDead; + bool mDied; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; bool mAttacked; bool mHostile; + bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + + int mAttackType; + + std::string mLastHitObject; // The last object to hit this actor + + protected: + bool mIsWerewolf; + Stat mWerewolfAttributes[8]; public: CreatureStats(); @@ -53,6 +63,8 @@ namespace MWMechanics const MagicEffects & getMagicEffects() const; + bool getAttackingOrSpell() const; + int getLevel() const; int getAiSetting (int index) const; @@ -82,6 +94,17 @@ namespace MWMechanics void setMagicEffects(const MagicEffects &effects); + void setAttackingOrSpell(bool attackingOrSpell); + + enum AttackType + { + AT_Slash, + AT_Thrust, + AT_Chop + }; + void setAttackType(int attackType) { mAttackType = attackType; } + int getAttackType() { return mAttackType; } + void setLevel(int level); void setAiSetting (int index, int value); @@ -94,12 +117,20 @@ namespace MWMechanics float getFatigueTerm() const; ///< Return effective fatigue - // small hack to allow the fact that Health permanently increases by 10% of endurance on each level up - void increaseLevelHealthBonus(float value); float getLevelHealthBonus() const; + void levelUp(); + + void updateHealth(); + ///< Calculate health based on endurance and strength. + /// Called at character creation and at level up. + bool isDead() const; + bool hasDied() const; + + void clearHasDied(); + void resurrect(); bool hasCommonDisease() const; @@ -130,6 +161,11 @@ namespace MWMechanics void setHostile (bool hostile); bool getCreatureTargetted() const; + + float getEvasion() const; + + void setLastHitObject(const std::string &objectid); + const std::string &getLastHitObject() const; }; } diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp new file mode 100644 index 000000000..4e26b5027 --- /dev/null +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -0,0 +1,318 @@ +#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) + , mSelfEnchanting(false) + , mOldItemCount(0) + {} + + 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(), player); + } + + 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(), player); + 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/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index 1a7b34817..3ed458c3f 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -151,8 +151,7 @@ namespace MWMechanics for (Collection::const_iterator iter (prev.begin()); iter!=prev.end(); ++iter) { Collection::const_iterator other = now.mCollection.find (iter->first); - - if (other==prev.end()) + if (other==now.end()) { result.add (iter->first, EffectParam() - iter->second); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c9e4c77ea..8c13db790 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; isetValue (attributeNames[i], stats.getAttribute(i)); + mWatchedStats.setAttribute(i, stats.getAttribute(i)); + winMgr->setValue(attrname.str(), stats.getAttribute(i)); } } - if (stats.getHealth() != mWatchedCreature.getHealth()) { - mWatchedCreature.setHealth(stats.getHealth()); - MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[0], stats.getHealth()); + if(stats.getHealth() != mWatchedStats.getHealth()) + { + static const std::string hbar("HBar"); + mWatchedStats.setHealth(stats.getHealth()); + winMgr->setValue(hbar, stats.getHealth()); } - if (stats.getMagicka() != mWatchedCreature.getMagicka()) { - mWatchedCreature.setMagicka(stats.getMagicka()); - MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[1], stats.getMagicka()); + if(stats.getMagicka() != mWatchedStats.getMagicka()) + { + static const std::string mbar("MBar"); + mWatchedStats.setMagicka(stats.getMagicka()); + winMgr->setValue(mbar, stats.getMagicka()); } - if (stats.getFatigue() != mWatchedCreature.getFatigue()) { - mWatchedCreature.setFatigue(stats.getFatigue()); - MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[2], stats.getFatigue()); + if(stats.getFatigue() != mWatchedStats.getFatigue()) + { + static const std::string fbar("FBar"); + mWatchedStats.setFatigue(stats.getFatigue()); + winMgr->setValue(fbar, stats.getFatigue()); + } + + if(stats.getTimeToStartDrowning() != mWatchedStats.getTimeToStartDrowning()) + { + mWatchedStats.setTimeToStartDrowning(stats.getTimeToStartDrowning()); + if(stats.getTimeToStartDrowning() >= 20.0f) + winMgr->setDrowningBarVisibility(false); + else + { + winMgr->setDrowningBarVisibility(true); + winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning()); + } } bool update = false; //Loop over ESM::Skill::SkillEnum - for(int i = 0; i < 27; ++i) + for(int i = 0; i < ESM::Skill::Length; ++i) { - if(npcStats.getSkill (i) != mWatchedNpc.getSkill (i)) + if(stats.getSkill(i) != mWatchedStats.getSkill(i)) { update = true; - mWatchedNpc.getSkill (i) = npcStats.getSkill (i); - MWBase::Environment::get().getWindowManager()->setValue((ESM::Skill::SkillEnum)i, npcStats.getSkill (i)); + mWatchedStats.getSkill(i) = stats.getSkill(i); + winMgr->setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i)); } } - if (update) - MWBase::Environment::get().getWindowManager()->updateSkillArea(); + if(update) + winMgr->updateSkillArea(); - MWBase::Environment::get().getWindowManager()->setValue ("level", stats.getLevel()); + winMgr->setValue("level", stats.getLevel()); } if (mUpdatePlayer) { + MWBase::World *world = MWBase::Environment::get().getWorld(); + // basic player profile; should not change anymore after the creation phase is finished. MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); - MWBase::World *world = MWBase::Environment::get().getWorld(); const ESM::NPC *player = world->getPlayer().getPlayer().get()->mBase; @@ -318,7 +322,7 @@ namespace MWMechanics } mActors.update(duration, paused); - mActivators.update(duration, paused); + mObjects.update(duration, paused); } void MechanicsManager::restoreDynamicStats() @@ -405,10 +409,10 @@ namespace MWMechanics MWWorld::LiveCellRef* npc = ptr.get(); MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::LiveCellRef* player = playerPtr.get(); - MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); - MWMechanics::NpcStats playerNpcStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); + const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - if (Misc::StringUtils::lowerCase(npc->mBase->mRace) == Misc::StringUtils::lowerCase(player->mBase->mRace)) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispRaceMod")->getFloat(); + if (Misc::StringUtils::lowerCase(npc->mBase->mRace) == Misc::StringUtils::lowerCase(player->mBase->mRace)) + x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispRaceMod")->getFloat(); x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispPersonalityMult")->getFloat() * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - MWBase::Environment::get().getWorld()->getStore().get().find("fDispPersonalityBase")->getFloat()); @@ -418,21 +422,21 @@ namespace MWMechanics std::string npcFaction = ""; if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first; - if (playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction)) != playerNpcStats.getFactionRanks().end()) + if (playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction)) != playerStats.getFactionRanks().end()) { for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin(); - it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); it++) + it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); ++it) { if(Misc::StringUtils::lowerCase(it->mFaction) == Misc::StringUtils::lowerCase(npcFaction)) reaction = it->mReaction; } - rank = playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second; + rank = playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second; } else if (npcFaction != "") { for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin(); - it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end();it++) + it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end();++it) { - if(playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(it->mFaction)) != playerNpcStats.getFactionRanks().end() ) + if(playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(it->mFaction)) != playerStats.getFactionRanks().end() ) { if(it->mReactionmReaction; } @@ -448,11 +452,11 @@ namespace MWMechanics + MWBase::Environment::get().getWorld()->getStore().get().find("fDispFactionRankBase")->getFloat()) * MWBase::Environment::get().getWorld()->getStore().get().find("fDispFactionMod")->getFloat() * reaction; - x -= MWBase::Environment::get().getWorld()->getStore().get().find("fDispCrimeMod")->getFloat() * playerNpcStats.getBounty(); + x -= MWBase::Environment::get().getWorld()->getStore().get().find("fDispCrimeMod")->getFloat() * playerStats.getBounty(); if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease()) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispDiseaseMod")->getFloat(); - if (playerNpcStats.getDrawState() == MWMechanics::DrawState_Weapon) + if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispWeaponDrawn")->getFloat(); int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used @@ -464,21 +468,19 @@ namespace MWMechanics if (ptr.getTypeName() == typeid(ESM::Creature).name()) return basePrice; - MWMechanics::NpcStats sellerSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); - MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(ptr).getNpcStats(ptr); MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); + const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); // I suppose the temporary disposition change _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = std::max(0, std::min(getDerivedDisposition(ptr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(),100)); - float a = std::min(playerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = std::min(sellerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); @@ -511,13 +513,10 @@ namespace MWMechanics const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); - - MWMechanics::NpcStats npcSkill = MWWorld::Class::get(npc).getNpcStats(npc); - MWMechanics::CreatureStats npcStats = MWWorld::Class::get(npc).getCreatureStats(npc); + MWMechanics::NpcStats npcStats = MWWorld::Class::get(npc).getNpcStats(npc); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); float persTerm = playerStats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->getFloat(); @@ -525,19 +524,18 @@ namespace MWMechanics float luckTerm = playerStats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->getFloat(); - float repTerm = playerSkill.getReputation() * gmst.find("fReputationMod")->getFloat(); - + float repTerm = playerStats.getReputation() * gmst.find("fReputationMod")->getFloat(); float levelTerm = playerStats.getLevel() * gmst.find("fLevelMod")->getFloat(); float fatigueTerm = playerStats.getFatigueTerm(); - float playerRating1 = (repTerm + luckTerm + persTerm + playerSkill.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + float playerRating1 = (repTerm + luckTerm + persTerm + playerStats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; float playerRating2 = playerRating1 + levelTerm; - float playerRating3 = (playerSkill.getSkill(ESM::Skill::Mercantile).getModified() + luckTerm + persTerm) * fatigueTerm; + float playerRating3 = (playerStats.getSkill(ESM::Skill::Mercantile).getModified() + luckTerm + persTerm) * fatigueTerm; - float npcRating1 = (repTerm + luckTerm + persTerm + playerSkill.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; - float npcRating2 = (levelTerm + repTerm + luckTerm + persTerm + npcSkill.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; - float npcRating3 = (playerSkill.getSkill(ESM::Skill::Mercantile).getModified() + repTerm + luckTerm + persTerm) * fatigueTerm; + float npcRating1 = (repTerm + luckTerm + persTerm + playerStats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + float npcRating2 = (levelTerm + repTerm + luckTerm + persTerm + npcStats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + float npcRating3 = (playerStats.getSkill(ESM::Skill::Mercantile).getModified() + repTerm + luckTerm + persTerm) * fatigueTerm; int currentDisposition = std::min(100, std::max(0, int(getDerivedDisposition(npc) + currentTemporaryDispositionDelta))); @@ -547,7 +545,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; @@ -653,19 +651,32 @@ namespace MWMechanics } } + 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(ptr.getTypeName() == typeid(ESM::Activator).name()) - mActivators.playAnimationGroup(ptr, groupName, mode, number); - else + 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(ptr.getTypeName() == typeid(ESM::Activator).name()) - mActivators.skipAnimation(ptr); - else + 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 5ad870571..ad07562c7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -7,7 +7,7 @@ #include "creaturestats.hpp" #include "npcstats.hpp" -#include "activators.hpp" +#include "objects.hpp" #include "actors.hpp" namespace Ogre @@ -25,13 +25,12 @@ namespace MWMechanics class MechanicsManager : public MWBase::MechanicsManager { MWWorld::Ptr mWatched; - CreatureStats mWatchedCreature; - NpcStats mWatchedNpc; + NpcStats mWatchedStats; bool mUpdatePlayer; bool mClassSelected; bool mRaceSelected; - Activators mActivators; + Objects mObjects; Actors mActors; void buildPlayer(); @@ -96,8 +95,11 @@ namespace MWMechanics 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..0b3698289 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -13,7 +13,9 @@ #include #include +#include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -21,8 +23,18 @@ #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) +, mWerewolfKills (0) +, mProfit(0) +, mAttackStrength(0.0f) +, mTimeToStartDrowning(20.0) +, mLastDrowningHit(0) { mSkillIncreases.resize (ESM::Attribute::Length); for (int i=0; i& MWMechanics::NpcStats::getSkill (int index) cons if (index<0 || index>=27) throw std::runtime_error ("skill index out of range"); - return mSkill[index]; + return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); } MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) @@ -75,7 +97,12 @@ MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) if (index<0 || index>=27) throw std::runtime_error ("skill index out of range"); - return mSkill[index]; + return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); +} + +const std::map& MWMechanics::NpcStats::getFactionRanks() const +{ + return mFactionRank; } std::map& MWMechanics::NpcStats::getFactionRanks() @@ -83,14 +110,14 @@ std::map& MWMechanics::NpcStats::getFactionRanks() return mFactionRank; } -std::set& MWMechanics::NpcStats::getExpelled() +const std::set& MWMechanics::NpcStats::getExpelled() const { return mExpelled; } -const std::map& MWMechanics::NpcStats::getFactionRanks() const +std::set& MWMechanics::NpcStats::getExpelled() { - return mFactionRank; + return mExpelled; } bool MWMechanics::NpcStats::isSameFaction (const NpcStats& npcStats) const @@ -161,12 +188,15 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla if (specialisationFactor<=0) throw std::runtime_error ("invalid skill specialisation factor"); } - - return 1.0 / (level +1) * (1.0 / (skillFactor)) * typeFactor * specialisationFactor; + return 1.0 / ((level+1) * (1.0/skillFactor) * typeFactor * specialisationFactor); } void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType) { + // Don't increase skills as a werewolf + if(mIsWerewolf) + return; + float base = getSkill (skillIndex).getBase(); int level = static_cast (base); @@ -221,12 +251,12 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); - MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), 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 +356,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()); @@ -342,15 +372,62 @@ bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int bool MWMechanics::NpcStats::isWerewolf() const { - return mWerewolf; + return mIsWerewolf; } void MWMechanics::NpcStats::setWerewolf (bool set) { - mWerewolf = set; + if(set != false) + { + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + + for(size_t i = 0;i < ESM::Attribute::Length;i++) + { + mWerewolfAttributes[i] = getAttribute(i); + // Oh, Bethesda. It's "Intelligence". + std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : + ESM::Attribute::sAttributeNames[i]); + mWerewolfAttributes[i].setModified(int(gmst.find(name)->getFloat()), 0); + } + + for(size_t i = 0;i < ESM::Skill::Length;i++) + { + mWerewolfSkill[i] = getSkill(i); + + // Acrobatics is set separately for some reason. + if(i == ESM::Skill::Acrobatics) + continue; + + // "Mercantile"! >_< + std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : + ESM::Skill::sSkillNames[i]); + mWerewolfSkill[i].setModified(int(gmst.find(name)->getFloat()), 0); + } + } + mIsWerewolf = set; } int MWMechanics::NpcStats::getWerewolfKills() const { return mWerewolfKills; } + +int MWMechanics::NpcStats::getProfit() const +{ + return mProfit; +} + +void MWMechanics::NpcStats::modifyProfit(int diff) +{ + mProfit += diff; +} + +float MWMechanics::NpcStats::getTimeToStartDrowning() const +{ + return mTimeToStartDrowning; +} +void MWMechanics::NpcStats::setTimeToStartDrowning(float time) +{ + assert(time>=0 && time<=20); + mTimeToStartDrowning=time; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index af32bd294..6b7efa5b7 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -9,6 +9,8 @@ #include "stat.hpp" #include "drawstate.hpp" +#include "creaturestats.hpp" + namespace ESM { struct Class; @@ -18,12 +20,10 @@ namespace MWMechanics { /// \brief Additional stats for NPCs /// - /// For non-NPC-specific stats, see the CreatureStats struct. - /// /// \note For technical reasons the spell list and the currently selected spell is also handled by /// CreatureStats, even though they are actually NPC stats. - class NpcStats + class NpcStats : public CreatureStats { public: @@ -46,13 +46,15 @@ namespace MWMechanics int mDisposition; unsigned int mMovementFlags; Stat mSkill[27]; + Stat mWerewolfSkill[27]; int mBounty; std::set mExpelled; std::map mFactionReputation; bool mVampire; int mReputation; - bool mWerewolf; int mWerewolfKills; + int mProfit; + float mAttackStrength; int mLevelProgress; // 0-10 @@ -60,14 +62,26 @@ namespace MWMechanics std::set mUsedIds; + /// Countdown to getting damage while underwater + float mTimeToStartDrowning; + /// time since last hit from drowning + float mLastDrowningHit; + public: NpcStats(); - DrawState_ getDrawState() const; + /// 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); + /// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest) + float getAttackStrength() const; + void setAttackStrength(float value); + int getBaseDisposition() const; void setBaseDisposition(int disposition); @@ -81,18 +95,17 @@ namespace MWMechanics void setMovementFlag (Flag flag, bool state); const Stat& getSkill (int index) const; - Stat& getSkill (int index); + const std::map& getFactionRanks() const; std::map& getFactionRanks(); + const std::set& getExpelled() const; std::set& getExpelled(); bool isSameFaction (const NpcStats& npcStats) const; ///< Do *this and \a npcStats share a faction? - const std::map& getFactionRanks() const; - float getSkillGain (int skillIndex, const ESM::Class& class_, int usageType = -1, int level = -1) const; ///< \param usageType: Usage specific factor, specified in the respective skill record; @@ -130,9 +143,14 @@ namespace MWMechanics bool isWerewolf() const; - void setWerewolf (bool set); + void setWerewolf(bool set); int getWerewolfKills() const; + + float getTimeToStartDrowning() const; + /// Sets time left for the creature to drown if it stays underwater. + /// @param time value from [0,20] + void setTimeToStartDrowning(float time); }; } diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp new file mode 100644 index 000000000..694987855 --- /dev/null +++ b/apps/openmw/mwmechanics/objects.cpp @@ -0,0 +1,85 @@ +#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) +{ + removeObject(ptr); + + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); +} + +void Objects::removeObject(const MWWorld::Ptr& ptr) +{ + PtrControllerMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) + { + delete iter->second; + 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) + { + delete iter->second; + mObjects.erase(iter++); + } + else + ++iter; + } +} + +void Objects::update(float duration, bool paused) +{ + if(!paused) + { + for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter) + iter->second->update(duration); + } +} + +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..5cdcdaa0a --- /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..8ef0edab8 --- /dev/null +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -0,0 +1,221 @@ +#include "pathfinding.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "OgreMath.h" + +#include +#include + +namespace +{ + struct found_path {}; + + typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, + boost::property, boost::property > + PathGridGraph; + typedef boost::property_map::type WeightMap; + typedef PathGridGraph::vertex_descriptor PointID; + typedef PathGridGraph::edge_descriptor PointConnectionID; + + class goalVisited : public boost::default_dijkstra_visitor + { + public: + goalVisited(PointID goal) {mGoal = goal;}; + void examine_vertex(PointID u, const PathGridGraph g) {if(u == mGoal) throw found_path();}; + + private: + PointID mGoal; + }; + + float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z) + { + x -= point.mX; + y -= point.mY; + z -= point.mZ; + return sqrt(x * x + y * y + 0.1 * z * z); + } + + float distance(ESM::Pathgrid::Point point, float x, float y, float z) + { + x -= point.mX; + y -= point.mY; + z -= point.mZ; + return sqrt(x * x + y * y + z * z); + } + + float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b) + { + float x = a.mX - b.mX; + float y = a.mY - b.mY; + float z = a.mZ - b.mZ; + return sqrt(x * x + y * y + z * z); + } + + static float sgn(float a) + { + if(a > 0) + return 1.0; + return -1.0; + } + + int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z) + { + if(!grid || grid->mPoints.empty()) + return -1; + + float distanceBetween = distance(grid->mPoints[0], x, y, z); + int closestIndex = 0; + + for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++) + { + if(distance(grid->mPoints[counter], x, y, z) < distanceBetween) + { + distanceBetween = distance(grid->mPoints[counter], x, y, z); + closestIndex = counter; + } + } + + return closestIndex; + } + + PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0) + { + PathGridGraph graph; + + for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) + { + PointID pID = boost::add_vertex(graph); + graph[pID].mX = pathgrid->mPoints[counter].mX + xCell; + graph[pID].mY = pathgrid->mPoints[counter].mY + yCell; + graph[pID].mZ = pathgrid->mPoints[counter].mZ; + } + + for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++) + { + PointID u = pathgrid->mEdges[counterTwo].mV0; + PointID v = pathgrid->mEdges[counterTwo].mV1; + + PointConnectionID edge; + bool done; + boost::tie(edge, done) = boost::add_edge(u, v, graph); + WeightMap weightmap = boost::get(boost::edge_weight, graph); + weightmap[edge] = distance(graph[u], graph[v]); + } + + return graph; + } + + std::list findPath(PointID start, PointID end, PathGridGraph graph) + { + std::vector p(boost::num_vertices(graph)); + std::vector d(boost::num_vertices(graph)); + std::list shortest_path; + + try + { + boost::dijkstra_shortest_paths(graph, start, + boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))); + } + + catch(found_path& fg) + { + for(PointID v = end; ; v = p[v]) + { + shortest_path.push_front(graph[v]); + if(p[v] == v) + break; + } + } + + return shortest_path; + } +} + +namespace MWMechanics +{ + PathFinder::PathFinder() + { + mIsPathConstructed = false; + } + + void PathFinder::clearPath() + { + if(!mPath.empty()) + mPath.clear(); + mIsPathConstructed = false; + } + + void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, + const ESM::Pathgrid *pathGrid, float xCell, float yCell, bool allowShortcuts) + { + if(allowShortcuts) + { + if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, + endPoint.mX, endPoint.mY, endPoint.mZ)) + allowShortcuts = false; + } + + if(!allowShortcuts) + { + int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); + int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); + + if(startNode != -1 && endNode != -1) + { + PathGridGraph graph = buildGraph(pathGrid, xCell, yCell); + mPath = findPath(startNode, endNode, graph); + + if(!mPath.empty()) + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + } + } + } + else + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + } + + if(mPath.empty()) + mIsPathConstructed = false; + } + + float PathFinder::getZAngleToNext(float x, float y) const + { + // This should never happen (programmers should have an if statement checking mIsPathConstructed that prevents this call + // if otherwise). + if(mPath.empty()) + return 0; + + const ESM::Pathgrid::Point &nextPoint = *mPath.begin(); + float directionX = nextPoint.mX - x; + float directionY = nextPoint.mY - y; + float directionResult = sqrt(directionX * directionX + directionY * directionY); + + return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees(); + } + + bool PathFinder::checkPathCompleted(float x, float y, float z) + { + if(mPath.empty()) + return true; + + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + if(distanceZCorrected(nextPoint, x, y, z) < 64) + { + mPath.pop_front(); + if(mPath.empty()) + { + mIsPathConstructed = false; + return true; + } + } + + return false; + } +} + diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp new file mode 100644 index 000000000..35e0fa908 --- /dev/null +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -0,0 +1,34 @@ +#ifndef GAME_MWMECHANICS_PATHFINDING_H +#define GAME_MWMECHANICS_PATHFINDING_H + +#include +#include + +namespace MWMechanics +{ + class PathFinder + { + public: + PathFinder(); + + void clearPath(); + void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, + const ESM::Pathgrid* pathGrid, float xCell = 0, float yCell = 0, + bool allowShortcuts = true); + + bool checkPathCompleted(float x, float y, float z); + ///< \Returns true if the last point of the path has been reached. + float getZAngleToNext(float x, float y) const; + + bool isPathConstructed() const + { + return mIsPathConstructed; + } + + private: + std::list mPath; + bool mIsPathConstructed; + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp new file mode 100644 index 000000000..66c492bf8 --- /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, player); + it->getRefData().setCount(mTool.getRefData().getCount()-1); + it->getCellRef().mCharge = -1; + + mTool.getRefData().setCount(1); + } + + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player); + + 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 e9b7f4385..65d47c9c0 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -42,6 +42,18 @@ namespace MWMechanics mBase = mModified = value; } + void modify(const T& diff) + { + mBase += diff; + if(mBase >= static_cast(0)) + mModified += diff; + else + { + mModified += diff - mBase; + mBase = static_cast(0); + } + } + /// Set base and adjust modified accordingly. void setBase (const T& value) { @@ -98,8 +110,8 @@ namespace MWMechanics public: typedef T Type; - DynamicStat() : mCurrent (0) {} - DynamicStat(T current) : mCurrent (current) {} + DynamicStat() : mStatic (0), mCurrent (0) {} + DynamicStat(T base) : mStatic (base), mCurrent (base) {} DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {} DynamicStat(const Stat &stat, T current) : mStatic(stat), mCurrent (current) {} diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 961c07003..7f4be9a68 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -1,9 +1,5 @@ #include "activatoranimation.hpp" -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -16,29 +12,19 @@ ActivatorAnimation::~ActivatorAnimation() } ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) - : Animation(ptr) + : Animation(ptr, ptr.getRefData().getBaseNode()) { MWWorld::LiveCellRef *ref = mPtr.get(); - assert (ref->mBase != NULL); + assert(ref->mBase != NULL); if(!ref->mBase->mModel.empty()) { - std::string mesh = "meshes\\" + ref->mBase->mModel; - - createEntityList(mPtr.getRefData().getBaseNode(), mesh); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - { - Ogre::Entity *ent = mEntityList.mEntities[i]; - - for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = ent->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - - ent->setVisibilityFlags(RV_Misc); - } - setAnimationSource(mesh); + const std::string name = "meshes\\"+ref->mBase->mModel; + + setObjectRoot(name, false); + setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha); + + addAnimSource(name); } } diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 644d3613b..566b6fa81 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -94,6 +94,9 @@ void Actors::insertActivator (const MWWorld::Ptr& ptr) bool Actors::deleteObject (const MWWorld::Ptr& ptr) { + if (mAllActors.find(ptr) == mAllActors.end()) + return false; + mRendering->removeWaterRippleEmitter (ptr); delete mAllActors[ptr]; @@ -139,6 +142,7 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store) Ogre::SceneNode *base = celliter->second; base->removeAndDestroyAllChildren(); mRend.getScene()->destroySceneNode(base); + mCellSceneNodes.erase(celliter); } } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index bba2d945c..4547db9ad 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -27,15 +27,18 @@ namespace MWRender CellSceneNodeMap mCellSceneNodes; PtrAnimationMap mAllActors; + void insertBegin(const MWWorld::Ptr &ptr); + public: Actors(OEngine::Render::OgreRenderer& _rend, MWRender::RenderingManager* rendering) : mRend(_rend) , mRendering(rendering) + , mRootNode(NULL) {} ~Actors(); 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); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index cc926e685..545060fe3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -3,237 +3,503 @@ #include #include #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" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/fallback.hpp" + +#include "renderconst.hpp" namespace MWRender { -Animation::Animation(const MWWorld::Ptr &ptr) +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.mLights.size();i++) + { + Ogre::Light *light = objects.mLights[i]; + // If parent is a scene node, it was created specifically for this light. Destroy it now. + if(light->isAttached() && !light->isParentTagPoint()) + sceneMgr->destroySceneNode(light->getParentSceneNode()); + sceneMgr->destroyLight(light); + } + for(size_t i = 0;i < objects.mParticles.size();i++) + sceneMgr->destroyParticleSystem(objects.mParticles[i]); + for(size_t i = 0;i < objects.mEntities.size();i++) + sceneMgr->destroyEntity(objects.mEntities[i]); + objects.mControllers.clear(); + objects.mLights.clear(); + objects.mParticles.clear(); + objects.mEntities.clear(); + objects.mSkelBase = NULL; +} + +Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) : mPtr(ptr) - , mController(NULL) - , mInsert(NULL) + , mCamera(NULL) + , mInsert(node) + , mSkelBase(NULL) , mAccumRoot(NULL) , mNonAccumRoot(NULL) - , mAccumulate(Ogre::Vector3::ZERO) - , mLastPosition(0.0f) - , mCurrentKeys(NULL) - , mCurrentAnim(NULL) - , mCurrentTime(0.0f) - , mStopTime(0.0f) - , mPlaying(false) - , mLooping(false) - , mAnimVelocity(0.0f) - , mAnimSpeedMult(1.0f) + , mNonAccumCtrl(NULL) + , mAccumulate(0.0f) + , mNullAnimationValuePtr(OGRE_NEW NullAnimationValue) { + for(size_t i = 0;i < sNumGroups;i++) + mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); } Animation::~Animation() { - if(mInsert) - { - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - sceneMgr->destroyEntity(mEntityList.mEntities[i]); - } - mEntityList.mEntities.clear(); - mEntityList.mSkelBase = NULL; + mAnimSources.clear(); + + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + destroyObjectList(sceneMgr, mObjectRoot); } -void Animation::setAnimationSources(const std::vector &names) +void Animation::setObjectRoot(const std::string &model, bool baseonly) { - if(!mEntityList.mSkelBase) + OgreAssert(mAnimSources.empty(), "Setting object root while animation sources are set!"); + + mSkelBase = NULL; + destroyObjectList(mInsert->getCreator(), mObjectRoot); + + if(model.empty()) return; - mCurrentAnim = NULL; - mCurrentKeys = NULL; - mAnimVelocity = 0.0f; - mAccumRoot = NULL; - mNonAccumRoot = NULL; - mSkeletonSources.clear(); + 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)) + { + mdlname = model; + Misc::StringUtils::toLower(mdlname); + } - std::vector::const_iterator nameiter; - for(nameiter = names.begin();nameiter != names.end();nameiter++) + mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : + NifOgre::Loader::createObjectBase(mInsert, mdlname)); + if(mObjectRoot.mSkelBase) { - Ogre::SkeletonPtr skel = NifOgre::Loader::getSkeleton(*nameiter); - if(skel.isNull()) + mSkelBase = mObjectRoot.mSkelBase; + + Ogre::AnimationStateSet *aset = mObjectRoot.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); + while(asiter.hasMoreElements()) { - std::cerr<< "Failed to get skeleton source "<<*nameiter <setEnabled(false); + state->setLoop(false); } - skel->touch(); - Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); + // 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()) - { - Ogre::Bone *bone = boneiter.getNext(); - Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); - const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); - if(data.isEmpty() || !Ogre::any_cast(data)) - continue; + boneiter.getNext()->setManuallyControlled(true); - if(!mNonAccumRoot) + // Reattach any objects that have been attached to this one + ObjectAttachMap::iterator iter = mAttachedObjects.begin(); + while(iter != mAttachedObjects.end()) + { + if(!skelinst->hasBone(iter->second)) + mAttachedObjects.erase(iter++); + else { - mAccumRoot = mInsert; - mNonAccumRoot = mEntityList.mSkelBase->getSkeleton()->getBone(bone->getName()); + mSkelBase->attachObjectToBone(iter->second, iter->first); + ++iter; } + } + } + else + mAttachedObjects.clear(); - mSkeletonSources.push_back(skel); - for(int i = 0;i < skel->getNumAnimations();i++) - { - Ogre::Animation *anim = skel->getAnimation(i); - const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ - "@"+anim->getName()); - if(!groupdata.isEmpty()) - mTextKeys[anim->getName()] = Ogre::any_cast(groupdata); - } + for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) + { + if(mObjectRoot.mControllers[i].getSource().isNull()) + mObjectRoot.mControllers[i].setSource(mAnimationValuePtr[0]); + } +} - break; + +class VisQueueSet { + Ogre::uint32 mVisFlags; + Ogre::uint8 mSolidQueue, mTransQueue; + Ogre::Real mDist; + +public: + VisQueueSet(Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist) + : mVisFlags(visflags), mSolidQueue(solidqueue), mTransQueue(transqueue), mDist(dist) + { } + + void operator()(Ogre::Entity *entity) const + { + if(mVisFlags != 0) + entity->setVisibilityFlags(mVisFlags); + entity->setRenderingDistance(mDist); + + unsigned int numsubs = entity->getNumSubEntities(); + for(unsigned int i = 0;i < numsubs;++i) + { + Ogre::SubEntity* subEnt = entity->getSubEntity(i); + subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? mTransQueue : mSolidQueue); } } + + void operator()(Ogre::ParticleSystem *psys) const + { + if(mVisFlags != 0) + psys->setVisibilityFlags(mVisFlags); + psys->setRenderingDistance(mDist); + // TODO: Check particle material for actual transparency + psys->setRenderQueueGroup(mTransQueue); + } +}; + +void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist) +{ + std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(), + VisQueueSet(visflags, solidqueue, transqueue, dist)); + std::for_each(objlist.mParticles.begin(), objlist.mParticles.end(), + VisQueueSet(visflags, solidqueue, transqueue, dist)); } -void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model) + +size_t Animation::detectAnimGroup(const Ogre::Node *node) { - mInsert = node->createChildSceneNode(); - assert(mInsert); + 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; + } - mEntityList = NifOgre::Loader::createEntities(mInsert, model); - if(mEntityList.mSkelBase) + 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.empty() || ctrls.empty()) + return; + + mAnimSources.push_back(animsrc); + + std::vector > *grpctrls = animsrc->mControllers; + for(size_t i = 0;i < ctrls.size();i++) { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); - while(asiter.hasMoreElements()) + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + + size_t grp = detectAnimGroup(dstval->getNode()); + + if(!mAccumRoot && grp == 0) { - Ogre::AnimationState *state = asiter.getNext(); - state->setEnabled(false); - state->setLoop(false); + mNonAccumRoot = dstval->getNode(); + mAccumRoot = mNonAccumRoot->getParent(); + if(!mAccumRoot) + { + std::cerr<< "Non-Accum root for "<getSkeleton(); - Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); - while(boneiter.hasMoreElements()) - boneiter.getNext()->setManuallyControlled(true); + ctrls[i].setSource(mAnimationValuePtr[grp]); + grpctrls[grp].push_back(ctrls[i]); } } +void Animation::clearAnimSources() +{ + mStates.clear(); -bool Animation::hasAnimation(const std::string &anim) + for(size_t i = 0;i < sNumGroups;i++) + mAnimationValuePtr[i]->setAnimName(std::string()); + + mNonAccumCtrl = NULL; + + mAccumRoot = NULL; + mNonAccumRoot = NULL; + + mAnimSources.clear(); +} + + +void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light) { - for(std::vector::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++) + const MWWorld::Fallback *fallback = MWBase::Environment::get().getWorld()->getFallback(); + + const int clr = light->mData.mColor; + Ogre::ColourValue color(((clr >> 0) & 0xFF) / 255.0f, + ((clr >> 8) & 0xFF) / 255.0f, + ((clr >> 16) & 0xFF) / 255.0f); + const float radius = float(light->mData.mRadius); + + if((light->mData.mFlags&ESM::Light::Negative)) + color *= -1; + + objlist.mLights.push_back(sceneMgr->createLight()); + Ogre::Light *olight = objlist.mLights.back(); + olight->setDiffuseColour(color); + + Ogre::ControllerValueRealPtr src(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); + Ogre::ControllerValueRealPtr dest(OGRE_NEW OEngine::Render::LightValue(olight, color)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW OEngine::Render::LightFunction( + (light->mData.mFlags&ESM::Light::Flicker) ? OEngine::Render::LT_Flicker : + (light->mData.mFlags&ESM::Light::FlickerSlow) ? OEngine::Render::LT_FlickerSlow : + (light->mData.mFlags&ESM::Light::Pulse) ? OEngine::Render::LT_Pulse : + (light->mData.mFlags&ESM::Light::PulseSlow) ? OEngine::Render::LT_PulseSlow : + OEngine::Render::LT_Normal + )); + objlist.mControllers.push_back(Ogre::Controller(src, dest, func)); + + bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior()); + bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ? + !interior : fallback->getFallbackBool("LightAttenuation_UseQuadratic"); + + // 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) { - if((*iter)->hasAnimation(anim)) - return true; + float r = radius * fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); + float attenuation = fallback->getFallbackFloat("LightAttenuation_LinearValue") / r; + float activationRange = 1.0f / (threshold * attenuation); + olight->setAttenuation(activationRange, 0, attenuation, 0); + } + else + { + float r = radius * fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); + float attenuation = fallback->getFallbackFloat("LightAttenuation_QuadraticValue") / std::pow(r, 2); + float activationRange = std::sqrt(1.0f / (threshold * attenuation)); + olight->setAttenuation(activationRange, 0, 0, attenuation); + } + + // If there's an AttachLight bone, attach the light to that, otherwise put it in the center, + if(objlist.mSkelBase && objlist.mSkelBase->getSkeleton()->hasBone("AttachLight")) + objlist.mSkelBase->attachObjectToBone("AttachLight", olight); + else + { + Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; + for(size_t i = 0;i < objlist.mEntities.size();i++) + { + Ogre::Entity *ent = objlist.mEntities[i]; + bounds.merge(ent->getBoundingBox()); + } + + Ogre::SceneNode *node = bounds.isFinite() ? mInsert->createChildSceneNode(bounds.getCenter()) + : mInsert->createChildSceneNode(); + node->attachObject(olight); } - return false; } -void Animation::setController(MWMechanics::CharacterController *controller) +Ogre::Node *Animation::getNode(const std::string &name) { - mController = controller; + if(mSkelBase) + { + Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); + if(skel->hasBone(name)) + return skel->getBone(name); + } + return NULL; } -void Animation::setAccumulation(const Ogre::Vector3 &accum) +NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) { - mAccumulate = accum; + 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; } -void Animation::setSpeed(float speed) + +bool Animation::hasAnimation(const std::string &anim) { - mAnimSpeedMult = 1.0f; - if(mAnimVelocity > 1.0f && speed > 0.0f) - mAnimSpeedMult = speed / mAnimVelocity; + 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::setLooping(bool loop) + +void Animation::setAccumulation(const Ogre::Vector3 &accum) { - mLooping = loop; + mAccumulate = accum; } + void Animation::updatePtr(const MWWorld::Ptr &ptr) { mPtr = ptr; } -void Animation::calcAnimVelocity() +float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname) { - const Ogre::NodeAnimationTrack *track = 0; - - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) + 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()) { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - track = cur; + if(keyiter->second == start || keyiter->second == loopstart) + starttime = keyiter->first; + else if(keyiter->second == loopstop || keyiter->second == stop) + { + stoptime = keyiter->first; + break; + } + keyiter++; } - if(track && track->getNumKeyFrames() > 1) + if(stoptime > starttime) { - float loopstarttime = 0.0f; - float loopstoptime = mCurrentAnim->getLength(); - NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin(); - while(keyiter != mCurrentKeys->end()) - { - if(keyiter->second == "loop start") - loopstarttime = keyiter->first; - else if(keyiter->second == "loop stop") - { - loopstoptime = keyiter->first; - break; - } - keyiter++; - } + Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum; + Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum; - if(loopstoptime > loopstarttime) - { - Ogre::TransformKeyFrame startkf(0, loopstarttime); - Ogre::TransformKeyFrame endkf(0, loopstoptime); + return startpos.distance(endpos) / (stoptime - starttime); + } + + return 0.0f; +} - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf); +float Animation::getVelocity(const std::string &groupname) const +{ + /* Look in reverse; last-inserted source has priority. */ + AnimSourceList::const_reverse_iterator animsrc(mAnimSources.rbegin()); + for(;animsrc != mAnimSources.rend();animsrc++) + { + const NifOgre::TextKeyMap &keys = (*animsrc)->mTextKeys; + if(findGroupStart(keys, groupname) != keys.end()) + break; + } + if(animsrc == mAnimSources.rend()) + return 0.0f; - mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) / - (loopstoptime-loopstarttime); + float velocity = 0.0f; + 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) + { + velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); + break; } } -} -void Animation::applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel) -{ - Ogre::TimeIndex timeindex = anim->_getTimeIndex(time); - Ogre::Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator(); - while(tracks.hasMoreElements()) + // If there's no velocity, keep looking + if(!(velocity > 1.0f)) { - Ogre::NodeAnimationTrack *track = tracks.getNext(); - const Ogre::String &targetname = track->getAssociatedNode()->getName(); - if(!skel->hasBone(targetname)) - continue; - Ogre::Bone *bone = skel->getBone(targetname); - bone->setOrientation(Ogre::Quaternion::IDENTITY); - bone->setPosition(Ogre::Vector3::ZERO); - bone->setScale(Ogre::Vector3::UNIT_SCALE); - track->applyToNode(bone, timeindex); + AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); + while(*animiter != *animsrc) + ++animiter; + + while(!(velocity > 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++) + { + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); + break; + } + } + } } - // HACK: Dirty the animation state set so that Ogre will apply the - // transformations to entities this skeleton instance is shared with. - mEntityList.mSkelBase->getAllAnimationStates()->_notifyDirty(); + return velocity; } + static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) { if(skelsrc->hasBone(bone->getName())) @@ -272,182 +538,502 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og } -Ogre::Vector3 Animation::updatePosition(float time) +void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &position) { - if(mLooping) - mCurrentTime = std::fmod(std::max(time, 0.0f), mCurrentAnim->getLength()); - else - mCurrentTime = std::min(mCurrentAnim->getLength(), std::max(time, 0.0f)); - applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton()); - - Ogre::Vector3 posdiff = Ogre::Vector3::ZERO; - if(mNonAccumRoot) - { - /* Get the non-accumulation root's difference from the last update. */ - posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate; - - /* Translate the accumulation root back to compensate for the move. */ - mLastPosition += posdiff; - mAccumRoot->setPosition(-mLastPosition); - } - return posdiff; + /* 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); } -void Animation::reset(const std::string &start, const std::string &stop) +bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) { - mNextKey = mCurrentKeys->begin(); + const NifOgre::TextKeyMap::const_iterator groupstart = findGroupStart(keys, groupname); - while(mNextKey != mCurrentKeys->end() && mNextKey->second != start) - mNextKey++; - if(mNextKey != mCurrentKeys->end()) - mCurrentTime = mNextKey->first; - else + std::string starttag = groupname+": "+start; + NifOgre::TextKeyMap::const_iterator startkey(groupstart); + while(startkey != keys.end() && startkey->second != starttag) + startkey++; + if(startkey == keys.end() && start == "loop start") { - mNextKey = mCurrentKeys->begin(); - mCurrentTime = 0.0f; + starttag = groupname+": start"; + startkey = groupstart; + while(startkey != keys.end() && startkey->second != starttag) + startkey++; } - - if(stop.length() > 0) + if(startkey == keys.end()) + return false; + + const std::string stoptag = groupname+": "+stop; + NifOgre::TextKeyMap::const_iterator stopkey(groupstart); + while(stopkey != keys.end() && stopkey->second != stoptag) + stopkey++; + if(stopkey == keys.end()) + return false; + + if(startkey->first > stopkey->first) + return false; + + state.mStartTime = startkey->first; + state.mLoopStartTime = startkey->first; + state.mLoopStopTime = stopkey->first; + state.mStopTime = stopkey->first; + + state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint); + if(state.mTime > state.mStartTime) { - NifOgre::TextKeyMap::const_iterator stopKey = mNextKey; - while(stopKey != mCurrentKeys->end() && stopKey->second != stop) - stopKey++; - if(stopKey != mCurrentKeys->end()) - mStopTime = stopKey->first; - else - mStopTime = mCurrentAnim->getLength(); + const std::string loopstarttag = groupname+": loop start"; + const std::string loopstoptag = groupname+": loop stop"; + NifOgre::TextKeyMap::const_iterator key(groupstart); + while(key->first <= state.mTime && key != stopkey) + { + if(key->second == loopstarttag) + state.mLoopStartTime = key->first; + else if(key->second == loopstoptag) + state.mLoopStopTime = key->first; + key++; + } } - if(mNonAccumRoot) + return true; +} + + +void 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; + } + if(evt.compare(0, 10, "soundgen: ") == 0) { - const Ogre::NodeAnimationTrack *track = 0; - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) + std::string sound = MWWorld::Class::get(mPtr).getSoundIdFromSndGen(mPtr, evt.substr(10)); + if(!sound.empty()) { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - track = cur; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx; + if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) + type = MWBase::SoundManager::Play_TypeFoot; + sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type); } + return; + } - if(track) - { - Ogre::TransformKeyFrame kf(0, mCurrentTime); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf); - mLastPosition = kf.getTranslate() * mAccumulate; - } + if(evt.compare(0, groupname.size(), groupname) != 0 || + evt.compare(groupname.size(), 2, ": ") != 0) + { + // Not ours, skip it + return; } + size_t off = groupname.size()+2; + size_t len = evt.size() - off; + + if(evt.compare(off, len, "loop start") == 0) + state.mLoopStartTime = key->first; + else if(evt.compare(off, len, "loop stop") == 0) + state.mLoopStopTime = key->first; + else if(evt.compare(off, len, "equip attach") == 0) + showWeapons(true); + else if(evt.compare(off, len, "unequip detach") == 0) + showWeapons(false); + else if(evt.compare(off, len, "chop hit") == 0) + MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + else if(evt.compare(off, len, "slash hit") == 0) + MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + else if(evt.compare(off, len, "thrust hit") == 0) + MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + else if(evt.compare(off, len, "hit") == 0) + MWWorld::Class::get(mPtr).hit(mPtr); } -bool Animation::handleEvent(float time, const std::string &evt) +void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops) { - if(evt == "start" || evt == "loop start") + if(!mSkelBase || mAnimSources.empty()) + return; + + if(groupname.empty()) { - /* Do nothing */ - return true; + resetActiveGroups(); + return; } - if(evt.compare(0, 7, "sound: ") == 0) + priority = std::max(0, priority); + + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) { - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); - return true; + if(stateiter->second.mPriority == priority) + mStates.erase(stateiter++); + else + ++stateiter; } - if(evt.compare(0, 10, "soundgen: ") == 0) + + stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) { - // FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds - // to this actor type - return true; + stateiter->second.mPriority = priority; + resetActiveGroups(); + return; } - if(evt == "loop stop") + /* Look in reverse; last-inserted source has priority. */ + AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); + for(;iter != mAnimSources.rend();iter++) { - if(mLooping) + const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys; + AnimState state; + if(reset(state, textkeys, groupname, start, stop, startpoint)) { - reset("loop start", ""); - if(mCurrentTime >= time) - return false; + state.mSource = *iter; + state.mSpeedMult = speedmult; + state.mLoopCount = loops; + state.mPlaying = (state.mTime < state.mStopTime); + state.mPriority = priority; + state.mGroups = groups; + state.mAutoDisable = autodisable; + mStates[groupname] = state; + + NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, groupname, textkey); + textkey++; + } + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + { + state.mLoopCount--; + state.mTime = state.mLoopStartTime; + state.mPlaying = true; + if(state.mTime >= state.mLoopStopTime) + break; + + textkey = textkeys.lower_bound(state.mTime); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, groupname, textkey); + textkey++; + } + } + + break; } - return true; } - if(evt == "stop") + if(iter == mAnimSources.rend()) + std::cerr<< "Failed to find animation "<second.mPlaying; + return false; +} + +void Animation::resetActiveGroups() +{ + for(size_t grp = 0;grp < sNumGroups;grp++) { - if(mLooping) + AnimStateMap::const_iterator active = mStates.end(); + + AnimStateMap::const_iterator state = mStates.begin(); + for(;state != mStates.end();++state) { - reset("loop start", ""); - if(mCurrentTime >= time) - return false; - return true; + if(!(state->second.mGroups&(1<second.mPriority < state->second.mPriority) + active = state; } - // fall-through + + mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ? + std::string() : active->first); + } + mNonAccumCtrl = NULL; + + 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 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) + { + mNonAccumCtrl = dstval; + break; + } + } +} + + +bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const +{ + AnimStateMap::const_iterator iter = mStates.find(groupname); + if(iter == mStates.end()) + { + if(complete) *complete = 0.0f; + if(speedmult) *speedmult = 0.0f; + return false; + } + + if(complete) + { + if(iter->second.mStopTime > iter->second.mStartTime) + *complete = (iter->second.mTime - iter->second.mStartTime) / + (iter->second.mStopTime - iter->second.mStartTime); + else + *complete = (iter->second.mPlaying ? 0.0f : 1.0f); } - if(mController) - mController->markerEvent(time, evt); + if(speedmult) *speedmult = iter->second.mSpeedMult; return true; } -void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop) +void Animation::disable(const std::string &groupname) { - try { - bool found = false; - /* Look in reverse; last-inserted source has priority. */ - for(std::vector::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++) + 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); + + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) + { + AnimState &state = stateiter->second; + const NifOgre::TextKeyMap &textkeys = state.mSource->mTextKeys; + NifOgre::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.mTime)); + + float timepassed = duration * state.mSpeedMult; + while(state.mPlaying) { - if((*iter)->hasAnimation(groupname)) + float targetTime; + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + goto handle_loop; + + targetTime = state.mTime + timepassed; + if(textkey == textkeys.end() || textkey->first > targetTime) + { + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, targetTime, movement); + state.mTime = std::min(targetTime, state.mStopTime); + } + else { - mCurrentAnim = (*iter)->getAnimation(groupname); - mCurrentKeys = &mTextKeys[groupname]; - mAnimVelocity = 0.0f; + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, textkey->first, movement); + state.mTime = textkey->first; + } - if(mNonAccumRoot) - calcAnimVelocity(); + state.mPlaying = (state.mTime < state.mStopTime); + timepassed = targetTime - state.mTime; - found = true; - break; + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey); + textkey++; + } + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + { + handle_loop: + state.mLoopCount--; + state.mTime = state.mLoopStartTime; + state.mPlaying = true; + + textkey = textkeys.lower_bound(state.mTime); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey); + textkey++; + } + + if(state.mTime >= state.mLoopStopTime) + break; } + + if(timepassed <= 0.0f) + break; } - if(!found) - throw std::runtime_error("Failed to find animation "+groupname); - reset(start, stop); - setLooping(loop); - mPlaying = true; + 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(); + } } - catch(std::exception &e) { - std::cerr<< e.what() <getAllAnimationStates()->_notifyDirty(); } + + return movement; +} + +void Animation::showWeapons(bool showWeapon) +{ +} + + +class ToggleLight { + bool mEnable; + +public: + ToggleLight(bool enable) : mEnable(enable) { } + + void operator()(Ogre::Light *light) const + { light->setVisible(mEnable); } +}; + +void Animation::enableLights(bool enable) +{ + std::for_each(mObjectRoot.mLights.begin(), mObjectRoot.mLights.end(), ToggleLight(enable)); } -Ogre::Vector3 Animation::runAnimation(float timepassed) + +class MergeBounds { + Ogre::AxisAlignedBox *mBounds; + +public: + MergeBounds(Ogre::AxisAlignedBox *bounds) : mBounds(bounds) { } + + void operator()(Ogre::MovableObject *obj) + { + mBounds->merge(obj->getWorldBoundingBox(true)); + } +}; + +Ogre::AxisAlignedBox Animation::getWorldBounds() { - Ogre::Vector3 movement = Ogre::Vector3::ZERO; + Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; + std::for_each(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(), MergeBounds(&bounds)); + return bounds; +} - timepassed *= mAnimSpeedMult; - while(mCurrentAnim && mPlaying) + +Ogre::TagPoint *Animation::attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj) +{ + Ogre::TagPoint *tag = NULL; + Ogre::SkeletonInstance *skel = (mSkelBase ? mSkelBase->getSkeleton() : NULL); + if(skel && skel->hasBone(bonename)) { - float targetTime = std::min(mStopTime, mCurrentTime+timepassed); - if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime) - { - movement += updatePosition(targetTime); - mPlaying = (mLooping || mStopTime > targetTime); - break; - } + tag = mSkelBase->attachObjectToBone(bonename, obj); + mAttachedObjects[obj] = bonename; + } + return tag; +} - float time = mNextKey->first; - const std::string &evt = mNextKey->second; - mNextKey++; +void Animation::detachObjectFromBone(Ogre::MovableObject *obj) +{ + ObjectAttachMap::iterator iter = mAttachedObjects.find(obj); + if(iter != mAttachedObjects.end()) + mAttachedObjects.erase(iter); + mSkelBase->detachObjectFromBone(obj); +} - movement += updatePosition(time); - mPlaying = (mLooping || mStopTime > time); - timepassed = targetTime - time; +ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model) + : Animation(ptr, ptr.getRefData().getBaseNode()) +{ + setObjectRoot(model, false); - if(!handleEvent(time, evt)) - break; + Ogre::Vector3 extents = getWorldBounds().getSize(); + float size = std::max(std::max(extents.x, extents.y), extents.z); + + bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && + Settings::Manager::getBool("limit small object distance", "Viewing distance"); + // do not fade out doors. that will cause holes and look stupid + if(ptr.getTypeName().find("Door") != std::string::npos) + small = false; + + float dist = small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0.0f; + setRenderProperties(mObjectRoot, (mPtr.getTypeName() == typeid(ESM::Static).name()) ? + (small ? RV_StaticsSmall : RV_Statics) : RV_Misc, + RQG_Main, RQG_Alpha, dist); +} + +void ObjectAnimation::addLight(const ESM::Light *light) +{ + addExtraLight(mInsert->getCreator(), mObjectRoot, light); +} + + +class FindEntityTransparency { +public: + bool operator()(Ogre::Entity *ent) const + { + unsigned int numsubs = ent->getNumSubEntities(); + for(unsigned int i = 0;i < numsubs;++i) + { + if(ent->getSubEntity(i)->getMaterial()->isTransparent()) + return true; + } + return false; } +}; - return movement; +bool ObjectAnimation::canBatch() const +{ + if(!mObjectRoot.mParticles.empty() || !mObjectRoot.mLights.empty() || !mObjectRoot.mControllers.empty()) + return false; + return std::find_if(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(), + FindEntityTransparency()) == mObjectRoot.mEntities.end(); +} + +void ObjectAnimation::fillBatch(Ogre::StaticGeometry *sg) +{ + std::vector::reverse_iterator iter = mObjectRoot.mEntities.rbegin(); + for(;iter != mObjectRoot.mEntities.rend();++iter) + { + Ogre::Node *node = (*iter)->getParentNode(); + sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); + } } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7caf35169..2215fc582 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,84 +1,178 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H +#include +#include + #include #include "../mwworld/ptr.hpp" -namespace MWMechanics -{ - class CharacterController; -} namespace MWRender { +class Camera; class Animation { +public: + enum Group { + Group_LowerBody = 1<<0, + + Group_Torso = 1<<1, + Group_LeftArm = 1<<2, + Group_RightArm = 1<<3, + + Group_UpperBody = Group_Torso | Group_LeftArm | Group_RightArm, + + Group_All = Group_LowerBody | Group_UpperBody + }; + protected: + /* 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); + }; + + + class NullAnimationValue : public Ogre::ControllerValue + { + public: + virtual Ogre::Real getValue() const + { return 0.0f; } + 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; + float mStartTime; + float mLoopStartTime; + float mLoopStopTime; + float mStopTime; + + float mTime; + float mSpeedMult; + + bool mPlaying; + size_t mLoopCount; + + int mPriority; + int mGroups; + bool mAutoDisable; + + AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), + mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), + mPriority(0), mGroups(0), mAutoDisable(true) + { } + }; + typedef std::map AnimStateMap; + + typedef std::map ObjectAttachMap; + MWWorld::Ptr mPtr; - MWMechanics::CharacterController *mController; + Camera *mCamera; - Ogre::SceneNode* mInsert; - NifOgre::EntityList mEntityList; - std::map mTextKeys; + Ogre::SceneNode *mInsert; + Ogre::Entity *mSkelBase; + NifOgre::ObjectList mObjectRoot; + AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; - Ogre::Bone *mNonAccumRoot; + Ogre::Node *mNonAccumRoot; + NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; - Ogre::Vector3 mLastPosition; - std::vector mSkeletonSources; + AnimStateMap mStates; - NifOgre::TextKeyMap *mCurrentKeys; - NifOgre::TextKeyMap::const_iterator mNextKey; - Ogre::Animation *mCurrentAnim; - float mCurrentTime; - float mStopTime; - bool mPlaying; - bool mLooping; + Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; + Ogre::SharedPtr mNullAnimationValuePtr; - float mAnimVelocity; - float mAnimSpeedMult; + ObjectAttachMap mAttachedObjects; - void calcAnimVelocity(); + /* Sets the appropriate animations on the bone groups based on priority. + */ + void resetActiveGroups(); - /* Applies the given animation to the given skeleton instance, using the specified time. */ - void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel); + 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 animation to the specified time, and returns the movement - * vector since the last update or reset. */ - Ogre::Vector3 updatePosition(float time); + /* 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, it resets to the beginning or end respectively. + * the marker is not found, or if the markers are the same, it returns + * false. */ - void reset(const std::string &start, const std::string &stop); + bool reset(AnimState &state, const NifOgre::TextKeyMap &keys, + const std::string &groupname, const std::string &start, const std::string &stop, + float startpoint); - bool handleEvent(float time, const std::string &evt); + void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); - /* Specifies a list of skeleton names to use as animation sources. */ - void setAnimationSources(const std::vector &names); + /* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle + * systems in the model are ignored (useful for NPCs, where only the skeleton is needed for + * the root). + * + * Note that you must make sure all animation sources are cleared before reseting the object + * root. All nodes previously retrieved with getNode will also become invalidated. + */ + void setObjectRoot(const std::string &model, bool baseonly); - /* Specifies a single skeleton name to use as an animation source. */ - void setAnimationSource(const std::string &name) - { - std::vector names(1, name); - setAnimationSources(names); - } + /* Adds the keyframe controllers in the specified model as a new animation source. Note that + * the filename portion of the provided model name will be prepended with 'x', and the .nif + * extension will be replaced with .kf. */ + void addAnimSource(const std::string &model); + + /** Adds an additional light to the given object list using the specified ESM record. */ + void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light); + + static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); + + static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist=0.0f); - void createEntityList(Ogre::SceneNode *node, const std::string &model); + void clearAnimSources(); public: - Animation(const MWWorld::Ptr &ptr); + Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); virtual ~Animation(); - void setController(MWMechanics::CharacterController *controller); - void updatePtr(const MWWorld::Ptr &ptr); bool hasAnimation(const std::string &anim); @@ -88,12 +182,74 @@ public: // 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 speedmult Speed multiplier for the animation. + * \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, + float speedmult, 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 speedmult Stores the animation speed multiplier + * \return True if the animation is active, false otherwise. + */ + bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + + /** Disables the specified animation group; + * \param groupname Animation group to disable. + */ + void disable(const std::string &groupname); + + /** Retrieves the velocity (in units per second) that the animation will move. */ + float getVelocity(const std::string &groupname) const; + + virtual Ogre::Vector3 runAnimation(float duration); + + virtual void showWeapons(bool showWeapon); + + void enableLights(bool enable); + + Ogre::AxisAlignedBox getWorldBounds(); + + void setCamera(Camera *cam) + { mCamera = cam; } + + Ogre::Node *getNode(const std::string &name); + + // Attaches the given object to a bone on this object's base skeleton. If the bone doesn't + // exist, the object isn't attached and NULL is returned. The returned TagPoint is only + // valid until the next setObjectRoot call. + Ogre::TagPoint *attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj); + void detachObjectFromBone(Ogre::MovableObject *obj); +}; + +class ObjectAnimation : public Animation { +public: + ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model); - void setLooping(bool loop); + void addLight(const ESM::Light *light); - void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); - virtual Ogre::Vector3 runAnimation(float timepassed); + bool canBatch() const; + void fillBatch(Ogre::StaticGeometry *sg); }; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp new file mode 100644 index 000000000..36f53c0fe --- /dev/null +++ b/apps/openmw/mwrender/camera.cpp @@ -0,0 +1,346 @@ +#include "camera.hpp" + +#include +#include +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/refdata.hpp" + +#include "npcanimation.hpp" + +namespace MWRender +{ + Camera::Camera (Ogre::Camera *camera) + : mCamera(camera), + mCameraNode(NULL), + mFirstPersonView(true), + mPreviewMode(false), + mFreeLook(true), + mHeight(128.f), + mCameraDistance(300.f), + mDistanceAdjusted(false), + mAnimation(NULL), + mNearest(30.f), + mFurthest(800.f), + mIsNearest(false), + mIsFurthest(false) + { + mVanity.enabled = false; + mVanity.allowed = true; + + mPreviewCam.yaw = 0.f; + mPreviewCam.offset = 400.f; + mMainCam.yaw = 0.f; + mMainCam.offset = 400.f; + } + + Camera::~Camera() + { + } + + void Camera::reset() + { + togglePreviewMode(false); + toggleVanityMode(false); + if (!mFirstPersonView) + toggleViewMode(); + } + + void Camera::rotateCamera(const Ogre::Vector3 &rot, bool adjust) + { + if (adjust) { + setYaw(getYaw() + rot.z); + setPitch(getPitch() + rot.x); + } else { + setYaw(rot.z); + setPitch(rot.x); + } + + Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X); + if (!mVanity.enabled && !mPreviewMode) { + mCamera->getParentNode()->setOrientation(xr); + } else { + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); + mCamera->getParentNode()->setOrientation(zr * xr); + } + } + + const std::string &Camera::getHandle() const + { + return mTrackingPtr.getRefData().getHandle(); + } + + void Camera::attachTo(const MWWorld::Ptr &ptr) + { + 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; + if(!mCamera->isAttached()) + mCameraNode->attachObject(mCamera); + } + + void Camera::updateListener() + { + Ogre::Vector3 pos = mCamera->getRealPosition(); + Ogre::Vector3 dir = mCamera->getRealDirection(); + Ogre::Vector3 up = mCamera->getRealUp(); + + MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); + } + + void Camera::update(float duration, bool paused) + { + updateListener(); + if (paused) + return; + + // only show the crosshair in game mode and in first person mode. + MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); + wm->showCrosshair(!wm->isGuiMode() && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); + + if(mVanity.enabled) + { + Ogre::Vector3 rot(0.f, 0.f, 0.f); + rot.z = Ogre::Degree(3.f * duration).valueRadians(); + rotateCamera(rot, true); + } + } + + void Camera::toggleViewMode() + { + mFirstPersonView = !mFirstPersonView; + processViewChange(); + + if (mFirstPersonView) { + mCamera->setPosition(0.f, 0.f, 0.f); + } else { + mCamera->setPosition(0.f, 0.f, mCameraDistance); + } + } + + void Camera::allowVanityMode(bool allow) + { + if (!allow && mVanity.enabled) + toggleVanityMode(false); + mVanity.allowed = allow; + } + + bool Camera::toggleVanityMode(bool enable) + { + if(!mVanity.allowed && enable) + return false; + + if(mVanity.enabled == enable) + return true; + mVanity.enabled = enable; + + processViewChange(); + + float offset = mPreviewCam.offset; + Ogre::Vector3 rot(0.f, 0.f, 0.f); + if (mVanity.enabled) { + rot.x = Ogre::Degree(-30.f).valueRadians(); + mMainCam.offset = mCamera->getPosition().z; + } else { + rot.x = getPitch(); + offset = mMainCam.offset; + } + rot.z = getYaw(); + + mCamera->setPosition(0.f, 0.f, offset); + rotateCamera(rot, false); + + return true; + } + + void Camera::togglePreviewMode(bool enable) + { + if(mPreviewMode == enable) + return; + + mPreviewMode = enable; + processViewChange(); + + float offset = mCamera->getPosition().z; + if (mPreviewMode) { + mMainCam.offset = offset; + offset = mPreviewCam.offset; + } else { + mPreviewCam.offset = offset; + offset = mMainCam.offset; + } + + mCamera->setPosition(0.f, 0.f, offset); + rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); + } + + float Camera::getYaw() + { + if(mVanity.enabled || mPreviewMode) + return mPreviewCam.yaw; + return mMainCam.yaw; + } + + void Camera::setYaw(float angle) + { + if (angle > Ogre::Math::PI) { + angle -= Ogre::Math::TWO_PI; + } else if (angle < -Ogre::Math::PI) { + angle += Ogre::Math::TWO_PI; + } + if (mVanity.enabled || mPreviewMode) { + mPreviewCam.yaw = angle; + } else { + mMainCam.yaw = angle; + } + } + + float Camera::getPitch() + { + if (mVanity.enabled || mPreviewMode) { + return mPreviewCam.pitch; + } + return mMainCam.pitch; + } + + void Camera::setPitch(float angle) + { + 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 { + mMainCam.pitch = angle; + } + } + + void Camera::setCameraDistance(float dist, bool adjust, bool override) + { + if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) + return; + + mIsFurthest = false; + mIsNearest = false; + + Ogre::Vector3 v(0.f, 0.f, dist); + if (adjust) { + v += mCamera->getPosition(); + } + if (v.z >= mFurthest) { + v.z = mFurthest; + mIsFurthest = true; + } else if (!override && v.z < 10.f) { + v.z = 10.f; + } else if (override && v.z <= mNearest) { + v.z = mNearest; + mIsNearest = true; + } + mCamera->setPosition(v); + + if (override) { + if (mVanity.enabled || mPreviewMode) { + mPreviewCam.offset = v.z; + } else if (!mFirstPersonView) { + mCameraDistance = v.z; + } + } else { + mDistanceAdjusted = true; + } + } + + void Camera::setCameraDistance() + { + if (mDistanceAdjusted) { + if (mVanity.enabled || mPreviewMode) { + mCamera->setPosition(0, 0, mPreviewCam.offset); + } else if (!mFirstPersonView) { + mCamera->setPosition(0, 0, mCameraDistance); + } + } + mDistanceAdjusted = false; + } + + void Camera::setAnimation(NpcAnimation *anim) + { + // 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->setCamera(NULL); + mAnimation->detachObjectFromBone(mCamera); + } + mAnimation = anim; + mAnimation->setCamera(this); + + processViewChange(); + } + + void Camera::processViewChange() + { + mAnimation->detachObjectFromBone(mCamera); + mCamera->detachFromParent(); + + if(isFirstPerson()) + { + mAnimation->setViewMode(NpcAnimation::VM_FirstPerson); + Ogre::TagPoint *tag = mAnimation->attachObjectToBone("Head", mCamera); + tag->setInheritOrientation(false); + } + else + { + mAnimation->setViewMode(NpcAnimation::VM_Normal); + mCameraNode->attachObject(mCamera); + } + } + + void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera) + { + mCamera->getParentSceneNode()->needUpdate(true); + + camera = mCamera->getRealPosition(); + focal = Ogre::Vector3((mCamera->getParentNode()->_getFullTransform() * + Ogre::Vector4(0.0f, 0.0f, 0.0f, 1.0f)).ptr()); + } + + void Camera::togglePlayerLooking(bool enable) + { + mFreeLook = enable; + } + + bool Camera::isVanityOrPreviewModeEnabled() + { + return mPreviewMode || mVanity.enabled; + } + + bool Camera::isNearest() + { + return mIsNearest; + } + + bool Camera::isFurthest() + { + return mIsFurthest; + } +} diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/camera.hpp similarity index 57% rename from apps/openmw/mwrender/player.hpp rename to apps/openmw/mwrender/camera.hpp index 9de41823d..dc552371e 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; @@ -35,9 +33,13 @@ namespace MWRender bool mFirstPersonView; bool mPreviewMode; bool mFreeLook; + float mNearest; + float mFurthest; + bool mIsNearest; + bool mIsFurthest; struct { - bool enabled, allowed, forced; + bool enabled, allowed; } mVanity; float mHeight, mCameraDistance; @@ -48,18 +50,15 @@ namespace MWRender /// Updates sound manager listener data void updateListener(); - 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,23 +67,24 @@ 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); - void update(float duration); + bool isFirstPerson() const + { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } + + void processViewChange(); + + void update(float duration, bool paused=false); /// Set camera distance for current mode. Don't work on 1st person view. /// \param adjust Indicates should distance be adjusted or set. @@ -96,22 +96,17 @@ namespace MWRender void setCameraDistance(); void setAnimation(NpcAnimation *anim); - NpcAnimation *getAnimation() const - { return mAnimation; } - - void setHeight(float height); - float getHeight(); - /// Stores player and camera world positions in passed arguments - /// \return true if camera at the eye-place - bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera); - Ogre::Vector3 getPosition(); - - void getSightAngles(float &pitch, float &yaw); + /// Stores focal and camera world positions in passed arguments + void getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera); void togglePlayerLooking(bool enable); bool isVanityOrPreviewModeEnabled(); + + bool isNearest(); + + bool isFurthest(); }; } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c99e42662..e42218bc2 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -12,6 +12,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" #include "renderconst.hpp" #include "npcanimation.hpp" @@ -21,13 +22,18 @@ 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) + , mRenderTarget(NULL) + , mViewport(NULL) + , mCamera(NULL) + , mNode(NULL) { } @@ -41,9 +47,14 @@ namespace MWRender { mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); - /// \todo Read the fallback values from INIImporter (Inventory:Directional*) + // This is a dummy light to turn off shadows without having to use a separate set of shaders Ogre::Light* l = mSceneMgr->createLight(); l->setType (Ogre::Light::LT_DIRECTIONAL); + l->setDiffuseColour (Ogre::ColourValue(0,0,0)); + + /// \todo Read the fallback values from INIImporter (Inventory:Directional*) + 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)); @@ -59,10 +70,8 @@ namespace MWRender mNode = renderRoot->createChildSceneNode(); - mAnimation = new NpcAnimation(mCharacter, mNode, - MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), 0, renderHeadOnly()); - - 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); @@ -90,25 +99,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), 0, renderHeadOnly()); + 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(); } @@ -129,19 +143,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, 1.0f, "start", "stop", 0.0f, 0); + } + + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + if(!mAnimation->getInfo("torch")) + mAnimation->play("torch", 2, MWRender::Animation::Group_LeftArm, false, + 1.0f, "start", "stop", 0.0f, ~0ul); + } + else if(mAnimation->getInfo("torch")) + mAnimation->disable("torch"); + + mAnimation->updateParts(true); 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) @@ -151,10 +212,13 @@ namespace MWRender void InventoryPreview::onSetup () { - if (!mSelectionBuffer) - mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); + delete mSelectionBuffer; + mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); + + mAnimation->showWeapons(true); - mAnimation->play("inventoryhandtohand", "start", "stop", false); + mCurrentAnimGroup = "inventoryhandtohand"; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); } // -------------------------------------------------------------------------------------------------- @@ -174,10 +238,11 @@ namespace MWRender mNode->roll(Ogre::Radian(angle), Ogre::SceneNode::TS_LOCAL); updateCamera(); + } - mNode->setVisible (true); + void RaceSelectionPreview::render() + { mRenderTarget->update(); - mNode->setVisible (false); } void RaceSelectionPreview::setPrototype(const ESM::NPC &proto) @@ -190,7 +255,7 @@ namespace MWRender void RaceSelectionPreview::onSetup () { - mAnimation->play("idle", "start", "stop", false); + mAnimation->play("idle", 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); updateCamera(); } @@ -198,7 +263,7 @@ namespace MWRender void RaceSelectionPreview::updateCamera() { Ogre::Vector3 scale = mNode->getScale(); - Ogre::Vector3 headOffset = mAnimation->getHeadNode()->_getDerivedPosition(); + Ogre::Vector3 headOffset = mAnimation->getNode("Bip01 Head")->_getDerivedPosition(); headOffset = mNode->convertLocalToWorldPosition(headOffset); mCamera->setPosition(headOffset + mPosition * scale); diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index 08cbd5108..b2dfc9679 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -53,6 +53,7 @@ namespace MWRender MWWorld::Ptr mCharacter; MWRender::NpcAnimation* mAnimation; + std::string mCurrentAnimGroup; std::string mName; @@ -72,8 +73,6 @@ namespace MWRender int getSlotSelected(int posX, int posY); - void setNpcAnimation (NpcAnimation* anim); - private: OEngine::Render::SelectionBuffer* mSelectionBuffer; @@ -94,6 +93,7 @@ namespace MWRender RaceSelectionPreview(); virtual void onSetup(); + void render(); void update(float angle); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 22f84ee01..c3ad512dd 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,9 +1,5 @@ #include "creatureanimation.hpp" -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -16,7 +12,7 @@ CreatureAnimation::~CreatureAnimation() } CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) - : Animation(ptr) + : Animation(ptr, ptr.getRefData().getBaseNode()) { MWWorld::LiveCellRef *ref = mPtr.get(); @@ -25,24 +21,12 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) { std::string model = "meshes\\"+ref->mBase->mModel; - createEntityList(mPtr.getRefData().getBaseNode(), model); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - { - Ogre::Entity *ent = mEntityList.mEntities[i]; - ent->setVisibilityFlags(RV_Actors); - - for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = ent->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - } + setObjectRoot(model, false); + setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - std::vector names; if((ref->mBase->mFlags&ESM::Creature::Biped)) - names.push_back("meshes\\base_anim.nif"); - names.push_back(model); - setAnimationSources(names); + addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); } } diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 54f288bff..b318c2d56 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -20,7 +20,6 @@ #include "../mwworld/ptr.hpp" -#include "player.hpp" #include "renderconst.hpp" using namespace Ogre; diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index 6a4eef58f..4a574017c 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -39,8 +39,6 @@ namespace MWWorld namespace MWRender { - class Player; - class Debugging { OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 055faaa1f..120a83fae 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -22,11 +24,13 @@ namespace MWRender : mCacheDir(cacheDir) , mMinX(0), mMaxX(0) , mMinY(0), mMaxY(0) + , mWidth(0) + , mHeight(0) { } - void GlobalMap::render () + void GlobalMap::render (Loading::Listener* loadingListener) { Ogre::TexturePtr tex; @@ -51,13 +55,16 @@ namespace MWRender mWidth = cellSize*(mMaxX-mMinX+1); mHeight = cellSize*(mMaxY-mMinY+1); + loadingListener->loadingOn(); + loadingListener->setLabel("Creating map"); + loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); + loadingListener->setProgress(0); + mExploredBuffer.resize((mMaxX-mMinX+1) * (mMaxY-mMinY+1) * 4); //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) if (1) { - Ogre::Image image; - std::vector data (mWidth * mHeight * 3); for (int x = mMinX; x <= mMaxX; ++x) @@ -91,15 +98,13 @@ namespace MWRender Ogre::ColourValue mountainColour(0.05, 0.05, 0.05); Ogre::ColourValue hillColour(0.16, 0.12, 0.08); - float mountainHeight = 15000.f; - float hillHeight = 2500.f; - unsigned char r,g,b; if (land) { - float landHeight = land->mLandData->mHeights[vertexY * ESM::Land::LAND_SIZE + vertexX]; - + const float landHeight = land->mLandData->mHeights[vertexY * ESM::Land::LAND_SIZE + vertexX]; + const float mountainHeight = 15000.f; + const float hillHeight = 2500.f; if (landHeight >= 0) { @@ -144,24 +149,20 @@ namespace MWRender b = waterDeepColour.b * 255; } - // uncomment this line to outline cell borders - //if (cellX == 0 || cellX == cellSize-1 || cellY == 0|| cellY == cellSize-1) r = 255; - data[texelY * mWidth * 3 + texelX * 3] = r; data[texelY * mWidth * 3 + texelX * 3+1] = g; data[texelY * mWidth * 3 + texelX * 3+2] = b; } } + loadingListener->increaseProgress(1); } } - image.loadDynamicImage (&data[0], mWidth, mHeight, Ogre::PF_B8G8R8); - - //image.save (mCacheDir + "/GlobalMap.png"); + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_STATIC); - tex->loadImage(image); + tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8); } else tex = Ogre::TextureManager::getSingleton ().getByName ("GlobalMap.png"); @@ -184,6 +185,8 @@ namespace MWRender memcpy(mOverlayTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mWidth*mHeight*4); mOverlayTexture->getBuffer()->unlock(); + + loadingListener->loadingOff(); } void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY) diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index c01182c5b..dd3787b62 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -5,6 +5,11 @@ #include +namespace Loading +{ + class Listener; +} + namespace MWRender { @@ -13,7 +18,7 @@ namespace MWRender public: GlobalMap(const std::string& cacheDir); - void render(); + void render(Loading::Listener* loadingListener); int getWidth() { return mWidth; } int getHeight() { return mHeight; } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index c86a61cfa..5f4128978 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -2,6 +2,10 @@ #include #include +#include +#include +#include +#include #include "../mwworld/esmstore.hpp" @@ -104,7 +108,7 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell) } } -void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell) +void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, float zMin, float zMax) { mInterior = false; @@ -118,7 +122,7 @@ 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, zMin, zMax, sSize, sSize, name); } void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, @@ -197,11 +201,11 @@ 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) { - //mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 ); - mCellCamera->setFarClipDistance(0); // infinite + mCellCamera->setFarClipDistance( (zhigh-zlow) + 2000 ); + mCellCamera->setNearClipDistance(50); mCellCamera->setOrthoWindow(xw, yw); - mCameraNode->setPosition(Vector3(x, y, zhigh+100000)); + mCameraNode->setPosition(Vector3(x, y, zhigh+1000)); // disable fog (only necessary for fixed function, the shader based // materials already do this through local_map material configuration) @@ -366,8 +370,8 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni 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 exploreRadius = (mInterior ? 0.1 : 0.3) * (sFogOfWarResolution-1); // explore radius from 0 to sFogOfWarResolution-1 + const float sqrExploreRadius = Math::Sqr(exploreRadius); const float exploreRadiusUV = exploreRadius / sFogOfWarResolution; // explore radius from 0 to 1 (UV space) // change the affected fog of war textures (in a 3x3 grid around the player) @@ -402,11 +406,8 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni { for (int texU = 0; texU> 24); alpha = std::min( alpha, (uint8) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) ); diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 72e637d9a..538489640 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -28,16 +28,18 @@ namespace MWRender * Request the local map for an exterior cell. * @remarks It will either be loaded from a disk cache, * or rendered if it is not already cached. - * @param exterior cell + * @param cell exterior cell + * @param zMin min height of objects or terrain in cell + * @param zMax max height of objects or terrain in cell */ - void requestMap (MWWorld::CellStore* cell); + void requestMap (MWWorld::CellStore* cell, float zMin, float zMax); /** * Request the local map for an interior cell. * @remarks It will either be loaded from a disk cache, * or rendered if it is not already cached. - * @param interior cell - * @param bounding box of the cell + * @param cell interior cell + * @param bounds bounding box of the cell */ void requestMap (MWWorld::CellStore* cell, Ogre::AxisAlignedBox bounds); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 9da70beb4..13b5971e2 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -2,60 +2,70 @@ #include #include +#include #include #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" +#include "../mwmechanics/npcstats.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "renderconst.hpp" +#include "camera.hpp" 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" }, - { 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" }, - { ESM::PRT_Tail, "Tail" } -}; +static NpcAnimation::PartBoneMap createPartListMap() +{ + NpcAnimation::PartBoneMap result; + result.insert(std::make_pair(ESM::PRT_Head, "Head")); + result.insert(std::make_pair(ESM::PRT_Hair, "Head")); + result.insert(std::make_pair(ESM::PRT_Neck, "Neck")); + result.insert(std::make_pair(ESM::PRT_Cuirass, "Chest")); + result.insert(std::make_pair(ESM::PRT_Groin, "Groin")); + result.insert(std::make_pair(ESM::PRT_Skirt, "Groin")); + result.insert(std::make_pair(ESM::PRT_RHand, "Right Hand")); + result.insert(std::make_pair(ESM::PRT_LHand, "Left Hand")); + result.insert(std::make_pair(ESM::PRT_RWrist, "Right Wrist")); + result.insert(std::make_pair(ESM::PRT_LWrist, "Left Wrist")); + result.insert(std::make_pair(ESM::PRT_Shield, "Shield Bone")); + result.insert(std::make_pair(ESM::PRT_RForearm, "Right Forearm")); + result.insert(std::make_pair(ESM::PRT_LForearm, "Left Forearm")); + result.insert(std::make_pair(ESM::PRT_RUpperarm, "Right Upper Arm")); + result.insert(std::make_pair(ESM::PRT_LUpperarm, "Left Upper Arm")); + result.insert(std::make_pair(ESM::PRT_RFoot, "Right Foot")); + result.insert(std::make_pair(ESM::PRT_LFoot, "Left Foot")); + result.insert(std::make_pair(ESM::PRT_RAnkle, "Right Ankle")); + result.insert(std::make_pair(ESM::PRT_LAnkle, "Left Ankle")); + result.insert(std::make_pair(ESM::PRT_RKnee, "Right Knee")); + result.insert(std::make_pair(ESM::PRT_LKnee, "Left Knee")); + result.insert(std::make_pair(ESM::PRT_RLeg, "Right Upper Leg")); + result.insert(std::make_pair(ESM::PRT_LLeg, "Left Upper Leg")); + result.insert(std::make_pair(ESM::PRT_RPauldron, "Right Clavicle")); + result.insert(std::make_pair(ESM::PRT_LPauldron, "Left Clavicle")); + result.insert(std::make_pair(ESM::PRT_Weapon, "Weapon Bone")); + result.insert(std::make_pair(ESM::PRT_Tail, "Tail")); + return result; +} +const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap(); NpcAnimation::~NpcAnimation() { - for(size_t i = 0;i < sPartListSize;i++) - removeEntities(mEntityParts[i]); + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + for(size_t i = 0;i < ESM::PRT_Count;i++) + destroyObjectList(sceneMgr, mObjectParts[i]); } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, bool headOnly) - : Animation(ptr), +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, ViewMode viewMode) + : Animation(ptr, node), mStateID(-1), mTimeToChange(0), mVisibilityFlags(visibilityFlags), @@ -71,143 +81,131 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor mGloveL(inv.end()), mGloveR(inv.end()), mSkirtIter(inv.end()), - mHeadOnly(headOnly) + mWeapon(inv.end()), + mShield(inv.end()), + mViewMode(viewMode), + mShowWeapons(false) { mNpc = mPtr.get()->mBase; - for(size_t i = 0;i < sPartListSize;i++) + for(size_t i = 0;i < ESM::PRT_Count;i++) { mPartslots[i] = -1; //each slot is empty mPartPriorities[i] = 0; } - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - const ESM::Race *race = store.get().find(mNpc->mRace); + updateNpcBase(); +} - float scale = race->mData.mHeight.mMale; - if(!mNpc->isMale()) - scale = race->mData.mHeight.mFemale; - node->scale(Ogre::Vector3(scale)); +void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) +{ + assert(viewMode != VM_HeadOnly); + mViewMode = viewMode; + rebuild(); +} - mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; - mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; +void NpcAnimation::rebuild() +{ + updateNpcBase(); - mBodyPrefix = "b_n_" + mNpc->mRace; - Misc::StringUtils::toLower(mBodyPrefix); + MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); +} - bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); +void NpcAnimation::updateNpcBase() +{ + clearAnimSources(); - createEntityList(node, smodel); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - { - Ogre::Entity *base = mEntityList.mEntities[i]; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mNpc->mRace); + bool isWerewolf = MWWorld::Class::get(mPtr).getNpcStats(mPtr).isWerewolf(); - base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); - if (mVisibilityFlags != 0) - base->setVisibilityFlags(mVisibilityFlags); + if(!isWerewolf) + { + mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; + mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; + } + else + { + mHeadModel = "meshes\\" + store.get().find("WerewolfHead")->mModel; + mHairModel = "meshes\\" + store.get().find("WerewolfHair")->mModel; + } - for(unsigned int j=0; j < base->getNumSubEntities(); ++j) + bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; + std::string smodel = (mViewMode != VM_FirstPerson) ? + (!isWerewolf ? !isBeast ? "meshes\\base_anim.nif" + : "meshes\\base_animkna.nif" + : "meshes\\wolf\\skin.nif") : + (!isWerewolf ? !isBeast ? "meshes\\base_anim.1st.nif" + : "meshes\\base_animkna.1st.nif" + : "meshes\\wolf\\skin.1st.nif"); + setObjectRoot(smodel, true); + + if(mViewMode != VM_FirstPerson) + { + addAnimSource(smodel); + if(!isWerewolf) { - Ogre::SubEntity* subEnt = base->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); + if(Misc::StringUtils::lowerCase(mNpc->mRace).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); + } + } + else + { + if(isWerewolf) + addAnimSource(smodel); + else + { + /* 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"); } } - std::vector skelnames(1, smodel); - if(!mNpc->isMale() && !isBeast) - skelnames.push_back("meshes\\base_anim_female.nif"); - else if(mBodyPrefix.find("argonian") != std::string::npos) - skelnames.push_back("meshes\\argonian_swimkna.nif"); - if(mNpc->mModel.length() > 0) - skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); - setAnimationSources(skelnames); - + for(size_t i = 0;i < ESM::PRT_Count;i++) + removeIndividualPart((ESM::PartReferenceType)i); updateParts(true); } void NpcAnimation::updateParts(bool forceupdate) { static const struct { - int numRemoveParts; // Max: 1 - ESM::PartReferenceType removeParts[1]; - - MWWorld::ContainerStoreIterator NpcAnimation::*part; - int slot; - - int numReserveParts; // Max: 12 - ESM::PartReferenceType reserveParts[12]; + MWWorld::ContainerStoreIterator NpcAnimation::*mPart; + int mSlot; + int mBasePriority; } slotlist[] = { - { 0, { }, - &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, - 12, { 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 } - }, - - { 0, { }, - &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, - 3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg } - }, - - { 1, { ESM::PRT_Hair }, - &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, - 0, { } - }, + // 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 } }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); - MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); for(size_t i = 0;!forceupdate && i < slotlistsize;i++) { - MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); - if(this->*slotlist[i].part != iter) + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); + if(this->*slotlist[i].mPart != iter) { forceupdate = true; break; @@ -216,117 +214,207 @@ void NpcAnimation::updateParts(bool forceupdate) if(!forceupdate) return; - for(size_t i = 0;i < slotlistsize && !mHeadOnly;i++) + for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { - MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); + MWWorld::ContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot); - this->*slotlist[i].part = iter; - removePartGroup(slotlist[i].slot); + this->*slotlist[i].mPart = store; + removePartGroup(slotlist[i].mSlot); - if(this->*slotlist[i].part == inv.end()) + if(this->*slotlist[i].mPart == inv.end()) continue; - for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++) - removeIndividualPart(slotlist[i].removeParts[rem]); + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet) + removeIndividualPart(ESM::PRT_Hair); int prio = 1; - MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part; if(store->getTypeName() == typeid(ESM::Clothing).name()) { - prio = ((slotlist[i].numReserveParts+1)<<1) + 0; + prio = ((slotlist[i].mBasePriority+1)<<1) + 0; const ESM::Clothing *clothes = store->get()->mBase; - addPartGroup(slotlist[i].slot, prio, clothes->mParts.mParts); + addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts); } else if(store->getTypeName() == typeid(ESM::Armor).name()) { - prio = ((slotlist[i].numReserveParts+1)<<1) + 1; + prio = ((slotlist[i].mBasePriority+1)<<1) + 1; const ESM::Armor *armor = store->get()->mBase; - addPartGroup(slotlist[i].slot, prio, armor->mParts.mParts); + addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts); } - for(int res = 0;res < slotlist[i].numReserveParts;res++) - reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio); + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe) + { + 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); + } + else if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt) + { + 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(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 (mHeadOnly) + 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; - 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", "" } } - }; + if(mPartPriorities[ESM::PRT_Shield] < 1) + { + MWWorld::ContainerStoreIterator store = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + MWWorld::Ptr part; + if(store != inv.end() && (part=*store).getTypeName() == typeid(ESM::Light).name()) + { + const ESM::Light *light = part.get()->mBase; + addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, + 1, "meshes\\"+light->mModel); + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light); + } + } - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) + showWeapons(mShowWeapons); + + // 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; + + static const int Flag_Female = 1<<0; + static const int Flag_FirstPerson = 1<<1; + + bool isWerewolf = cls.getNpcStats(mPtr).isWerewolf(); + int flags = (isWerewolf ? -1 : 0); + if(!mNpc->isMale()) + flags |= Flag_Female; + if(mViewMode == VM_FirstPerson) + flags |= Flag_FirstPerson; + + std::string race = (isWerewolf ? "werewolf" : Misc::StringUtils::lowerCase(mNpc->mRace)); + std::pair thisCombination = std::make_pair(race, flags); + if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - if(mPartPriorities[PartTypeList[i].type] < 1) + typedef std::multimap BodyPartMapType; + static BodyPartMapType sBodyPartMap; + if(sBodyPartMap.empty()) { - const ESM::BodyPart *part = NULL; - const MWWorld::Store &partStore = store.get(); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); + } + + std::vector &parts = sRaceMapping[thisCombination]; + parts.resize(ESM::PRT_Count, NULL); - if(!mNpc->isMale()) + 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) + { + if(isWerewolf) + break; + 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)) { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]); - if(part == 0) - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]); + if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || + bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart.mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm)) + { + /* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */ + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) + { + if(!parts[bIt->second]) + parts[bIt->second] = &*it; + ++bIt; + } + } + continue; + } + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) + { + parts[bIt->second] = &*it; + ++bIt; } - 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); } } -} -NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename) -{ - NifOgre::EntityList entities = NifOgre::Loader::createEntities(mEntityList.mSkelBase, bonename, - mInsert, mesh); - std::vector &parts = entities.mEntities; - for(size_t i = 0;i < parts.size();i++) + const std::vector &parts = sRaceMapping[thisCombination]; + for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) { - parts[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if (mVisibilityFlags != 0) - parts[i]->setVisibilityFlags(mVisibilityFlags); - - for(unsigned int j=0; j < parts[i]->getNumSubEntities(); ++j) + if(mPartPriorities[part] < 1) { - Ogre::SubEntity* subEnt = parts[i]->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); + const ESM::BodyPart* bodypart = parts[part]; + if(bodypart) + addOrReplaceIndividualPart((ESM::PartReferenceType)part, -1, 1, + "meshes\\"+bodypart->mModel); } } - if(entities.mSkelBase) +} + +class SetObjectGroup { + int mGroup; + +public: + SetObjectGroup(int group) : mGroup(group) { } + + void operator()(Ogre::MovableObject *obj) const { - Ogre::AnimationStateSet *aset = entities.mSkelBase->getAllAnimationStates(); + obj->getUserObjectBindings().setUserAny(Ogre::Any(mGroup)); + } +}; + +NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) +{ + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha); + + std::for_each(objects.mEntities.begin(), objects.mEntities.end(), SetObjectGroup(group)); + std::for_each(objects.mParticles.begin(), objects.mParticles.end(), SetObjectGroup(group)); + + if(objects.mSkelBase) + { + Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -334,12 +422,13 @@ NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int state->setEnabled(false); state->setLoop(false); } - Ogre::SkeletonInstance *skelinst = entities.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = objects.mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); } - return entities; + + return objects; } Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) @@ -352,47 +441,39 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) mTimeToChange -= timepassed; Ogre::Vector3 ret = Animation::runAnimation(timepassed); - const Ogre::SkeletonInstance *skelsrc = mEntityList.mSkelBase->getSkeleton(); - for(size_t i = 0;i < sPartListSize;i++) + + Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + if(mViewMode == VM_FirstPerson && mCamera) { - Ogre::Entity *ent = mEntityParts[i].mSkelBase; - if(!ent) continue; - updateSkeletonInstance(skelsrc, ent->getSkeleton()); - ent->getAllAnimationStates()->_notifyDirty(); + float pitch = mCamera->getPitch(); + Ogre::Node *node = baseinst->getBone("Bip01 Neck"); + node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); } - return ret; -} -void NpcAnimation::removeEntities(NifOgre::EntityList &entities) -{ - assert(&entities != &mEntityList); - - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < entities.mEntities.size();i++) + for(size_t i = 0;i < ESM::PRT_Count;i++) { - entities.mEntities[i]->detachFromParent(); - sceneMgr->destroyEntity(entities.mEntities[i]); + std::vector >::iterator ctrl(mObjectParts[i].mControllers.begin()); + for(;ctrl != mObjectParts[i].mControllers.end();ctrl++) + ctrl->update(); + + 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) +void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type) { mPartPriorities[type] = 0; mPartslots[type] = -1; - for(size_t i = 0;i < sPartListSize;i++) - { - if(type == sPartList[i].type) - { - removeEntities(mEntityParts[i]); - break; - } - } + destroyObjectList(mInsert->getCreator(), mObjectParts[type]); } -void NpcAnimation::reserveIndividualPart(int type, int group, int priority) +void NpcAnimation::reserveIndividualPart(ESM::PartReferenceType type, int group, int priority) { if(priority > mPartPriorities[type]) { @@ -404,14 +485,14 @@ void NpcAnimation::reserveIndividualPart(int type, int group, int priority) void NpcAnimation::removePartGroup(int group) { - for(int i = 0; i < 27; i++) + for(int i = 0; i < ESM::PRT_Count; i++) { if(mPartslots[i] == group) - removeIndividualPart(i); + removeIndividualPart((ESM::PartReferenceType)i); } } -bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh) +bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh) { if(priority <= mPartPriorities[type]) return false; @@ -420,42 +501,105 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, mPartslots[type] = group; mPartPriorities[type] = priority; - for(size_t i = 0;i < sPartListSize;i++) + mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type)); + if(mObjectParts[type].mSkelBase) { - if(type == sPartList[i].type) + Ogre::SkeletonInstance *skel = mObjectParts[type].mSkelBase->getSkeleton(); + if(mObjectParts[type].mSkelBase->isParentTagPoint()) { - mEntityParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); - break; + Ogre::Node *root = mObjectParts[type].mSkelBase->getParentNode(); + if(skel->hasBone("BoneOffset")) + { + Ogre::Bone *offset = skel->getBone("BoneOffset"); + root->translate(offset->getPosition()); + root->rotate(offset->getOrientation()); + // HACK: Why an extra -90 degree rotation? + root->pitch(Ogre::Degree(-90.0f)); + root->scale(offset->getScale()); + root->setInitialState(); + } } + + updateSkeletonInstance(mSkelBase->getSkeleton(), skel); } + + // TODO: + // type == ESM::PRT_Head should get an animation source based on the current output of + // the actor's 'say' sound (0 = silent, 1 = loud?). + // type == ESM::PRT_Weapon should get an animation source based on the current offset + // of the weapon attack animation (from its beginning, or start marker?) + std::vector >::iterator ctrl(mObjectParts[type].mControllers.begin()); + for(;ctrl != mObjectParts[type].mControllers.end();ctrl++) + { + if(ctrl->getSource().isNull()) + ctrl->setSource(mNullAnimationValuePtr); + } + return true; } void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts) { - for(std::size_t i = 0; i < parts.size(); i++) - { - const 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 && mViewMode == VM_FirstPerson) + { + bodypart = partStore.search(part->mFemale); + if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || + bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) + bodypart = NULL; + } + } + if(!bodypart && !part->mMale.empty()) + { + bodypart = partStore.search(part->mMale+ext); + if(!bodypart && mViewMode == VM_FirstPerson) + { + bodypart = partStore.search(part->mMale); + if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || + bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) + bodypart = NULL; + } + } if(bodypart) - addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel); + addOrReplaceIndividualPart((ESM::PartReferenceType)part->mPart, group, priority, "meshes\\"+bodypart->mModel); else - reserveIndividualPart(part.mPart, group, priority); + reserveIndividualPart((ESM::PartReferenceType)part->mPart, group, priority); } } -Ogre::Node* NpcAnimation::getHeadNode() +void NpcAnimation::showWeapons(bool showWeapon) { - return mEntityList.mSkelBase->getSkeleton()->getBone("Bip01 Head"); + mShowWeapons = showWeapon; + if(showWeapon) + { + MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(mWeapon != inv.end()) // special case for weapons + { + MWWorld::Ptr weapon = *mWeapon; + std::string mesh = MWWorld::Class::get(weapon).getModel(weapon); + 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 5da4afef8..24205acaf 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -21,25 +21,27 @@ namespace MWRender class NpcAnimation : public Animation { public: -struct PartInfo { - ESM::PartReferenceType type; - const char name[32]; -}; + typedef std::map PartBoneMap; + + enum ViewMode { + VM_Normal, + VM_FirstPerson, + VM_HeadOnly + }; private: - static const size_t sPartListSize = 27; - static const PartInfo sPartList[sPartListSize]; + static const PartBoneMap sPartList; int mStateID; // Bounded Parts - NifOgre::EntityList mEntityParts[sPartListSize]; + NifOgre::ObjectList mObjectParts[ESM::PRT_Count]; - const ESM::NPC *mNpc; - std::string mHeadModel; - std::string mHairModel; - std::string mBodyPrefix; - bool mHeadOnly; + const ESM::NPC *mNpc; + std::string mHeadModel; + std::string mHairModel; + ViewMode mViewMode; + bool mShowWeapons; float mTimeToChange; MWWorld::ContainerStoreIterator mRobe; @@ -54,35 +56,41 @@ private: MWWorld::ContainerStoreIterator mGloveL; MWWorld::ContainerStoreIterator mGloveR; MWWorld::ContainerStoreIterator mSkirtIter; + MWWorld::ContainerStoreIterator mWeapon; + MWWorld::ContainerStoreIterator mShield; int mVisibilityFlags; - int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty - int mPartPriorities[sPartListSize]; + int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty + int mPartPriorities[ESM::PRT_Count]; - NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename); + void updateNpcBase(); - void updateParts(bool forceupdate = false); + NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename); - void removeEntities(NifOgre::EntityList &entities); - void removeIndividualPart(int type); - void reserveIndividualPart(int type, int group, int priority); + void removeIndividualPart(ESM::PartReferenceType type); + void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); - bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); + bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh); void removePartGroup(int group); void addPartGroup(int group, int priority, const std::vector &parts); public: NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, - MWWorld::InventoryStore& inv, int visibilityFlags, bool headOnly=false); + MWWorld::InventoryStore& inv, int visibilityFlags, + ViewMode viewMode=VM_Normal); virtual ~NpcAnimation(); virtual Ogre::Vector3 runAnimation(float timepassed); - Ogre::Node* getHeadNode(); + virtual void showWeapons(bool showWeapon); + + void setViewMode(ViewMode viewMode); + + void updateParts(bool forceupdate = false); - void forceUpdate() - { updateParts(true); } + /// Rebuilds the NPC, updating their root model, animation sources, and equipment. + void rebuild(); }; } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index cb1dfa75b..fd81baf6e 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -1,10 +1,14 @@ #include "objects.hpp" +#include + #include #include #include #include #include +#include +#include #include #include @@ -14,55 +18,18 @@ #include "../mwworld/class.hpp" #include "renderconst.hpp" +#include "animation.hpp" using namespace MWRender; -/// \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; - int Objects::uniqueID = 0; -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::setRootNode(Ogre::SceneNode* root) { mRootNode = root; } -void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) +void Objects::insertBegin(const MWWorld::Ptr& ptr) { Ogre::SceneNode* root = mRootNode; Ogre::SceneNode* cellnode; @@ -99,70 +66,45 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); - if (!enabled) - insert->setVisible (false); ptr.getRefData().setBaseNode(insert); - mIsStatic = static_; } -void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light) +void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) { - Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); - assert(insert); + insertBegin(ptr); - Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - NifOgre::EntityList entities = NifOgre::Loader::createEntities(insert, mesh); - for(size_t i = 0;i < entities.mEntities.size();i++) - bounds.merge(entities.mEntities[i]->getWorldBoundingBox(true)); + std::auto_ptr anim(new ObjectAnimation(ptr, mesh)); + Ogre::AxisAlignedBox bounds = anim->getWorldBounds(); Ogre::Vector3 extents = bounds.getSize(); - extents *= insert->getScale(); + extents *= ptr.getRefData().getBaseNode()->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); - bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && Settings::Manager::getBool("limit small object distance", "Viewing distance"); - + bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && + Settings::Manager::getBool("limit small object distance", "Viewing distance"); // do not fade out doors. that will cause holes and look stupid - if (ptr.getTypeName().find("Door") != std::string::npos) + if(ptr.getTypeName().find("Door") != std::string::npos) small = false; if (mBounds.find(ptr.getCell()) == mBounds.end()) mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; mBounds[ptr.getCell()].merge(bounds); - bool anyTransparency = false; - for(size_t i = 0;!anyTransparency && i < entities.mEntities.size();i++) - { - Ogre::Entity *ent = entities.mEntities[i]; - for(unsigned int i=0;!anyTransparency && i < ent->getNumSubEntities(); ++i) - { - anyTransparency = ent->getSubEntity(i)->getMaterial()->isTransparent(); - } - } + if(ptr.getTypeName() == typeid(ESM::Light).name()) + anim->addLight(ptr.get()->mBase); - if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || anyTransparency) - { - for(size_t i = 0;i < entities.mEntities.size();i++) - { - Ogre::Entity *ent = entities.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); - } - } - else + if(ptr.getTypeName() == typeid(ESM::Static).name() && + Settings::Manager::getBool("use static geometry", "Objects") && + anim->canBatch()) { Ogre::StaticGeometry* sg = 0; if (small) { - if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end()) + if(mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end()) { - uniqueID = uniqueID +1; - sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); + uniqueID = uniqueID+1; + sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); mStaticGeometrySmall[ptr.getCell()] = sg; sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance")); @@ -172,11 +114,10 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool } else { - if( mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end()) + if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end()) { - - uniqueID = uniqueID +1; - sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); + uniqueID = uniqueID+1; + sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); mStaticGeometry[ptr.getCell()] = sg; } else @@ -189,7 +130,10 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool // - the culling will be more inefficient // If it is set too low: // - there will be too many batches. - sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500)); + if(ptr.getCell()->isExterior()) + sg->setRegionDimensions(Ogre::Vector3(2048,2048,2048)); + else + sg->setRegionDimensions(Ogre::Vector3(1024,1024,1024)); sg->setVisibilityFlags(small ? RV_StaticsSmall : RV_Statics); @@ -197,158 +141,75 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool sg->setRenderQueueGroup(RQG_Main); - std::vector::reverse_iterator iter = entities.mEntities.rbegin(); - while(iter != entities.mEntities.rend()) - { - Ogre::Node *node = (*iter)->getParentNode(); - sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); - - (*iter)->detachFromParent(); - mRenderer.getScene()->destroyEntity(*iter); - iter++; - } + anim->fillBatch(sg); + /* TODO: We could hold on to this and just detach it from the scene graph, so if the Ptr + * ever needs to modify we can reattach it and rebuild the StaticGeometry object without + * it. Would require associating the Ptr with the StaticGeometry. */ + anim.reset(); } - if (light) - { - insertLight(ptr, entities.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition()); - } + if(anim.get() != NULL) + mObjects.insert(std::make_pair(ptr, anim.release())); } -void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter) +bool Objects::deleteObject (const MWWorld::Ptr& ptr) { - Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle()); - assert(insert); - - 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; - info.colour = Ogre::ColourValue(r, g, b); - - if (ref->mBase->mData.mFlags & ESM::Light::Negative) - info.colour *= -1; - - info.interior = !ptr.getCell()->mCell->isExterior(); - - if (ref->mBase->mData.mFlags & ESM::Light::Flicker) - info.type = LT_Flicker; - else if (ref->mBase->mData.mFlags & ESM::Light::FlickerSlow) - info.type = LT_FlickerSlow; - else if (ref->mBase->mData.mFlags & ESM::Light::Pulse) - info.type = LT_Pulse; - else if (ref->mBase->mData.mFlags & ESM::Light::PulseSlow) - info.type = LT_PulseSlow; - else - info.type = LT_Normal; - - // randomize lights animations - info.time = Ogre::Math::RangeRandom(-500, +500); - info.phase = Ogre::Math::RangeRandom(-500, +500); + if(!ptr.getRefData().getBaseNode()) + return true; - // changed to linear to look like morrowind - bool quadratic = false; - /* - if (!lightOutQuadInLin) - quadratic = lightQuadratic; - else + PtrAnimationMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) { - quadratic = !info.interior; - } - */ + delete iter->second; + mObjects.erase(iter); - if (!quadratic) - { - float r = radius * lightLinearRadiusMult; - float attenuation = lightLinearValue / r; - light->setAttenuation(r*10, 0, attenuation, 0); - } - else - { - float r = radius * lightQuadraticRadiusMult; - float attenuation = lightQuadraticValue / pow(r, 2); - light->setAttenuation(r*10, 0, 0, attenuation); + mRenderer.getScene()->destroySceneNode(ptr.getRefData().getBaseNode()); + ptr.getRefData().setBaseNode(0); + return true; } - // 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")) - { - skelBase->attachObjectToBone ("AttachLight", light); - } - else - { - Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter); - childNode->attachObject(light); - } - - mLights.push_back(info); + return false; } -bool Objects::deleteObject (const MWWorld::Ptr& ptr) -{ - if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode()) - { - Ogre::SceneNode *parent = base->getParentSceneNode(); - - for (std::map::const_iterator iter ( - mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter) - if (iter->second==parent) - { - clearSceneNode (base); - base->removeAndDestroyAllChildren(); - mRenderer.getScene()->destroySceneNode (base); - ptr.getRefData().setBaseNode (0); - return true; - } - - return false; - } - - return true; -} void Objects::removeCell(MWWorld::Ptr::CellStore* store) { - if(mCellSceneNodes.find(store) != mCellSceneNodes.end()) + for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();) { - Ogre::SceneNode* base = mCellSceneNodes[store]; - - for (int i=0; inumChildren(); ++i) - clearSceneNode (static_cast (base->getChild (i))); - - base->removeAndDestroyAllChildren(); - mCellSceneNodes.erase(store); - mRenderer.getScene()->destroySceneNode(base); - base = 0; + if(iter->first.getCell() == store) + { + delete iter->second; + mObjects.erase(iter++); + } + else + ++iter; } - if(mStaticGeometry.find(store) != mStaticGeometry.end()) + std::map::iterator geom = mStaticGeometry.find(store); + if(geom != mStaticGeometry.end()) { - Ogre::StaticGeometry* sg = mStaticGeometry[store]; - mStaticGeometry.erase(store); - mRenderer.getScene()->destroyStaticGeometry (sg); - sg = 0; + Ogre::StaticGeometry *sg = geom->second; + mStaticGeometry.erase(geom); + mRenderer.getScene()->destroyStaticGeometry(sg); } - if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end()) + + geom = mStaticGeometrySmall.find(store); + if(geom != mStaticGeometrySmall.end()) { - Ogre::StaticGeometry* sg = mStaticGeometrySmall[store]; + Ogre::StaticGeometry *sg = geom->second; mStaticGeometrySmall.erase(store); - mRenderer.getScene()->destroyStaticGeometry (sg); - sg = 0; + mRenderer.getScene()->destroyStaticGeometry(sg); } - if(mBounds.find(store) != mBounds.end()) - mBounds.erase(store); + mBounds.erase(store); + + std::map::iterator cell = mCellSceneNodes.find(store); + if(cell != mCellSceneNodes.end()) + { + cell->second->removeAndDestroyAllChildren(); + mRenderer.getScene()->destroySceneNode(cell->second); + mCellSceneNodes.erase(cell); + } } void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell) @@ -372,146 +233,23 @@ Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell) void Objects::enableLights() { - std::vector::iterator it = mLights.begin(); - while (it != mLights.end()) - { - if (mRootNode->getCreator()->hasLight(it->name)) - { - mRootNode->getCreator()->getLight(it->name)->setVisible(true); - ++it; - } - else - it = mLights.erase(it); - } + PtrAnimationMap::const_iterator it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->enableLights(true); } void Objects::disableLights() { - std::vector::iterator it = mLights.begin(); - while (it != mLights.end()) - { - if (mRootNode->getCreator()->hasLight(it->name)) - { - mRootNode->getCreator()->getLight(it->name)->setVisible(false); - ++it; - } - else - it = mLights.erase(it); - } -} - -namespace MWRender -{ - namespace Pulse - { - static float amplitude (float phase) - { - return sin (phase); - } - } - - namespace Flicker - { - static const float fa = 0.785398f; - static const float fb = 1.17024f; - - static const float tdo = 0.94f; - static const float tdm = 2.48f; - - static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; - static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; - static const float m [3] = { 1.0f, 0.785f, 0.876f }; - static const float s = 0.394f; - - static const float phase_wavelength = 120.0f * 3.14159265359f / fa; - - static float frequency (float x) - { - return tdo + tdm * sin (fa * x); - } - - static float amplitude (float x) - { - float v = 0.0f; - for (int i = 0; i < 3; ++i) - v += sin (fb*x*f[i] + o[1])*m[i]; - return v * s; - } - } + PtrAnimationMap::const_iterator it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->enableLights(false); } void Objects::update(const float dt) { - std::vector::iterator it = mLights.begin(); - while (it != mLights.end()) - { - if (mRootNode->getCreator()->hasLight(it->name)) - { - Ogre::Light* light = mRootNode->getCreator()->getLight(it->name); - - float brightness; - float cycle_time; - float time_distortion; - - if ((it->type == LT_Pulse) && (it->type == LT_PulseSlow)) - { - cycle_time = 2 * Ogre::Math::PI; - time_distortion = 20.0f; - } - else - { - cycle_time = 500.0f; - it->phase = fmod (it->phase + dt, Flicker::phase_wavelength); - time_distortion = Flicker::frequency (it->phase); - } - - it->time += it->dir*dt*time_distortion; - if (it->dir > 0 && it->time > +cycle_time) - { - it->dir = -1.0f; - it->time = +2*cycle_time - it->time; - } - if (it->dir < 0 && it->time < -cycle_time) - { - it->dir = +1.0f; - it->time = -2*cycle_time - it->time; - } - - static const float fast = 4.0f/1.0f; - static const float slow = 1.0f/1.0f; - - // These formulas are just guesswork, but they work pretty well - if (it->type == LT_Normal) - { - // Less than 1/255 light modifier for a constant light: - brightness = (const float)(1.0 + Flicker::amplitude(it->time*slow) / 255.0 ); - } - else if (it->type == LT_Flicker) - { - brightness = (const float)(0.75 + Flicker::amplitude(it->time*fast) * 0.25); - } - else if (it->type == LT_FlickerSlow) - { - brightness = (const float)(0.75 + Flicker::amplitude(it->time*slow) * 0.25); - } - else if (it->type == LT_Pulse) - { - brightness = (const float)(1.0 + Pulse::amplitude (it->time*fast) * 0.25); - } - else if (it->type == LT_PulseSlow) - { - brightness = (const float)(1.0 + Pulse::amplitude (it->time*slow) * 0.25); - } - else - assert(0 && "Invalid light type"); - - light->setDiffuseColour(it->colour * brightness); - - ++it; - } - else - it = mLights.erase(it); - } + PtrAnimationMap::const_iterator it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->runAnimation(dt); } void Objects::rebuildStaticGeometry() diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 73e95a3c5..22dd1e4f5 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -14,67 +14,32 @@ namespace MWWorld namespace MWRender{ -/// information about light needed for rendering -enum LightType -{ - // These are all mutually exclusive - LT_Normal=0, - LT_Flicker=1, - LT_FlickerSlow=2, - LT_Pulse=3, - LT_PulseSlow=4 -}; - -struct LightInfo -{ - // Constants - std::string name; // ogre handle - Ogre::ColourValue colour; - float radius; - bool interior; // Does this light belong to an interior or exterior cell - LightType type; - - // Runtime variables - float dir; // direction time is running... - float time; // current time - float phase; // current phase - - LightInfo() : - dir(1.0f), time(0.0f), phase (0.0f), - interior(true), type(LT_Normal), radius(1.0) - { - } -}; +class ObjectAnimation; class Objects{ + typedef std::map PtrAnimationMap; + OEngine::Render::OgreRenderer &mRenderer; - std::map mCellSceneNodes; - std::map mStaticGeometry; - std::map mStaticGeometrySmall; - std::map mBounds; - std::vector mLights; - Ogre::SceneNode* mRootNode; - bool mIsStatic; - static int uniqueID; - static float lightLinearValue; - static float lightLinearRadiusMult; + std::map mCellSceneNodes; + std::map mStaticGeometry; + std::map mStaticGeometrySmall; + std::map mBounds; + PtrAnimationMap mObjects; - static bool lightQuadratic; - static float lightQuadraticValue; - static float lightQuadraticRadiusMult; + Ogre::SceneNode* mRootNode; - static bool lightOutQuadInLin; + static int uniqueID; - void clearSceneNode (Ogre::SceneNode *node); - ///< Remove all movable objects from \a node. + void insertBegin(const MWWorld::Ptr& ptr); public: - Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer), mIsStatic(false) {} + Objects(OEngine::Render::OgreRenderer &renderer) + : mRenderer(renderer) + , mRootNode(NULL) + {} ~Objects(){} - void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); - 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 insertModel(const MWWorld::Ptr& ptr, const std::string &model); void enableLights(); void disableLights(); diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index eaa155b06..a69511acd 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,8 +6,11 @@ #include #include #include +#include #include +#include "renderconst.hpp" + using namespace MWRender; using namespace Ogre; @@ -16,7 +18,8 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mActiveQuery(0), mDoQuery(0), mSunVisibility(0), mWasVisible(false), - mBBNode(0), mActive(false) + mActive(false), + mFirstFrame(true) { mRendering = renderer; mSunNode = sunNode; @@ -29,7 +32,7 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0); } - catch (Ogre::Exception e) + catch (Ogre::Exception& e) { mSupported = false; } @@ -40,39 +43,24 @@ 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(); - 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); mRendering->getScene()->addRenderObjectListener(this); @@ -116,12 +104,12 @@ 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; } @@ -160,6 +148,12 @@ void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocati 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; @@ -170,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 @@ -209,6 +202,4 @@ void OcclusionQuery::update(float duration) void OcclusionQuery::setSunNode(Ogre::SceneNode* node) { mSunNode = node; - if (!mBBNode) - mBBNode = node->getParentSceneNode()->createChildSceneNode(); } diff --git a/apps/openmw/mwrender/occlusionquery.hpp b/apps/openmw/mwrender/occlusionquery.hpp index 145d77355..983361c18 100644 --- a/apps/openmw/mwrender/occlusionquery.hpp +++ b/apps/openmw/mwrender/occlusionquery.hpp @@ -49,17 +49,17 @@ namespace MWRender Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery; Ogre::HardwareOcclusionQuery* mActiveQuery; - Ogre::BillboardSet* mBBQueryVisible; - Ogre::BillboardSet* mBBQueryTotal; + Ogre::Entity* mBBQueryVisible; + Ogre::Entity* mBBQueryTotal; Ogre::SceneNode* mSunNode; - Ogre::SceneNode* mBBNode; Ogre::SceneNode* mBBNodeReal; float mSunVisibility; bool mWasVisible; bool mActive; + bool mFirstFrame; bool mSupported; bool mDoQuery; diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp deleted file mode 100644 index 63396378d..000000000 --- a/apps/openmw/mwrender/player.cpp +++ /dev/null @@ -1,372 +0,0 @@ -#include "player.hpp" - -#include -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/soundmanager.hpp" - -#include "../mwworld/ptr.hpp" -#include "../mwworld/refdata.hpp" - -#include "npcanimation.hpp" - -namespace MWRender -{ - Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) - : mCamera(camera), - mPlayerNode(node), - mCameraNode(mPlayerNode->createChildSceneNode()), - mFirstPersonView(true), - mPreviewMode(false), - mFreeLook(true), - mHeight(128.f), - mCameraDistance(300.f), - mDistanceAdjusted(false), - mAnimation(NULL) - { - 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() - { - 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 Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) - { - if (adjust) { - setYaw(getYaw() + rot.z); - setPitch(getPitch() + rot.x); - } else { - 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 - ); - if (!mVanity.enabled && !mPreviewMode) { - mPlayerNode->setOrientation(zr); - mCameraNode->setOrientation(xr); - } else { - mCameraNode->setOrientation(zr * xr); - } - } - - std::string Player::getHandle() const - { - return mPlayerNode->getName(); - } - - void Player::attachTo(const MWWorld::Ptr &ptr) - { - ptr.getRefData().setBaseNode(mPlayerNode); - } - - void Player::updateListener() - { - Ogre::Vector3 pos = mCamera->getRealPosition(); - Ogre::Vector3 dir = mCamera->getRealDirection(); - Ogre::Vector3 up = mCamera->getRealUp(); - - MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); - } - - void Player::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)); - - /// \fixme We shouldn't hide the whole model, just certain components of the character (head, chest, feet, etc) - mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView); - if (mFirstPersonView && !mVanity.enabled) { - return; - } - 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() - { - mFirstPersonView = !mFirstPersonView; - if (mFirstPersonView) { - mCamera->setPosition(0.f, 0.f, 0.f); - setLowHeight(false); - } else { - mCamera->setPosition(0.f, 0.f, mCameraDistance); - setLowHeight(true); - } - } - - void Player::allowVanityMode(bool allow) - { - if (!allow && mVanity.enabled && !mVanity.forced) { - toggleVanityMode(false); - } - mVanity.allowed = allow; - } - - bool Player::toggleVanityMode(bool enable, bool force) - { - if ((mVanity.forced && !force) || - (!mVanity.allowed && (force || enable))) - { - return false; - } else if (mVanity.enabled == enable) { - return true; - } - mVanity.enabled = enable; - mVanity.forced = force && enable; - - float offset = mPreviewCam.offset; - Ogre::Vector3 rot(0.f, 0.f, 0.f); - if (mVanity.enabled) { - rot.x = Ogre::Degree(-30.f).valueRadians(); - mMainCam.offset = mCamera->getPosition().z; - - setLowHeight(true); - } else { - rot.x = getPitch(); - offset = mMainCam.offset; - - setLowHeight(!mFirstPersonView); - } - rot.z = getYaw(); - mCamera->setPosition(0.f, 0.f, offset); - rotateCamera(rot, false); - - return true; - } - - void Player::togglePreviewMode(bool enable) - { - if (mPreviewMode == enable) { - return; - } - mPreviewMode = enable; - float offset = mCamera->getPosition().z; - if (mPreviewMode) { - mMainCam.offset = offset; - offset = mPreviewCam.offset; - - setLowHeight(true); - } else { - mPreviewCam.offset = offset; - offset = mMainCam.offset; - - setLowHeight(!mFirstPersonView); - } - mCamera->setPosition(0.f, 0.f, offset); - rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); - } - - float Player::getYaw() - { - if (mVanity.enabled || mPreviewMode) { - return mPreviewCam.yaw; - } - return mMainCam.yaw; - } - - void Player::setYaw(float angle) - { - if (angle > Ogre::Math::PI) { - angle -= Ogre::Math::TWO_PI; - } else if (angle < -Ogre::Math::PI) { - angle += Ogre::Math::TWO_PI; - } - if (mVanity.enabled || mPreviewMode) { - mPreviewCam.yaw = angle; - } else { - mMainCam.yaw = angle; - } - } - - float Player::getPitch() - { - if (mVanity.enabled || mPreviewMode) { - return mPreviewCam.pitch; - } - return mMainCam.pitch; - } - - void Player::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; - } - if (mVanity.enabled || mPreviewMode) { - mPreviewCam.pitch = angle; - } else { - mMainCam.pitch = angle; - } - } - - void Player::setCameraDistance(float dist, bool adjust, bool override) - { - if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { - return; - } - Ogre::Vector3 v(0.f, 0.f, dist); - if (adjust) { - v += mCamera->getPosition(); - } - if (v.z > 800.f) { - 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); - - if (override) { - if (mVanity.enabled || mPreviewMode) { - mPreviewCam.offset = v.z; - } else if (!mFirstPersonView) { - mCameraDistance = v.z; - } - } else { - mDistanceAdjusted = true; - } - } - - void Player::setCameraDistance() - { - if (mDistanceAdjusted) { - if (mVanity.enabled || mPreviewMode) { - mCamera->setPosition(0, 0, mPreviewCam.offset); - } else if (!mFirstPersonView) { - mCamera->setPosition(0, 0, mCameraDistance); - } - } - mDistanceAdjusted = false; - } - - void Player::setAnimation(NpcAnimation *anim) - { - delete mAnimation; - mAnimation = anim; - - mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView); - } - - void Player::setHeight(float height) - { - mHeight = height; - mCameraNode->setPosition(0.f, 0.f, mHeight); - } - - float Player::getHeight() - { - return mHeight * mPlayerNode->getScale().z; - } - - bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) - { - mCamera->getParentSceneNode ()->needUpdate(true); - camera = mCamera->getRealPosition(); - player = mPlayerNode->getPosition(); - - return mFirstPersonView && !mVanity.enabled && !mPreviewMode; - } - - Ogre::Vector3 Player::getPosition() - { - return mPlayerNode->getPosition(); - } - - void Player::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) - { - mFreeLook = enable; - } - - void Player::setLowHeight(bool low) - { - if (low) { - mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); - } else { - mCameraNode->setPosition(0.f, 0.f, mHeight); - } - } - - bool Player::isVanityOrPreviewModeEnabled() - { - return mPreviewMode || mVanity.enabled; - } -} diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 1d2cdf1ea..44599ebee 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -59,6 +59,9 @@ enum VisibilityFlags // overlays, we only want these on the main render target RV_Overlay = 1024, + // First person meshes do not cast shadows + RV_FirstPerson = 2048, + 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 e6216b537..e03b2ccfc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -14,6 +14,9 @@ #include #include #include +#include + +#include #include #include @@ -22,6 +25,8 @@ #include #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" @@ -30,6 +35,8 @@ #include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" @@ -41,36 +48,44 @@ #include "externalrendering.hpp" #include "globalmap.hpp" #include "videoplayer.hpp" +#include "terrainstorage.hpp" using namespace MWRender; 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) +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) , mActors(mRendering, this) + , mPlayerAnimation(NULL) , mAmbientMode(0) , mSunEnabled(0) , mPhysicsEngine(engine) + , mTerrain(NULL) { // 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.adjustCamera(Settings::Manager::getFloat("field of view", "General"), 5); mRendering.getWindow()->addListener(this); + mRendering.setWindowListener(this); mCompositors = new Compositors(mRendering.getViewport()); @@ -87,6 +102,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 @@ -115,12 +132,10 @@ 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); + Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); // disable unsupported effects if (!Settings::Manager::getBool("shaders", "Objects")) @@ -130,7 +145,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setGlobalSetting ("fog", "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 ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); sh::Factory::getInstance ().setGlobalSetting ("render_refraction", "false"); @@ -145,24 +159,21 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const applyCompositors(); - SceneNode *rt = mRendering.getScene()->getRootSceneNode(); - mRootNode = rt; + mRootNode = mRendering.getScene()->getRootSceneNode(); + mRootNode->createChildSceneNode("player"); mObjects.setRootNode(mRootNode); mActors.setRootNode(mRootNode); - Ogre::SceneNode *playerNode = mRootNode->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(mRootNode, mRendering.getCamera()); mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - mVideoPlayer = new VideoPlayer(mRendering.getScene ()); + mVideoPlayer = new VideoPlayer(mRendering.getScene (), mRendering.getWindow()); mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video")); mSun = 0; @@ -178,13 +189,13 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const RenderingManager::~RenderingManager () { mRendering.getWindow()->removeListener(this); - mRendering.removeWindowEventListener(this); - delete mPlayer; + delete mPlayerAnimation; + delete mCamera; delete mSkyManager; delete mDebugging; delete mShadows; - delete mTerrainManager; + delete mTerrain; delete mLocalMap; delete mOcclusionQuery; delete mCompositors; @@ -215,8 +226,6 @@ void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store) mObjects.removeCell(store); mActors.removeCell(store); mDebugging->cellRemoved(store); - if (store->mCell->isExterior()) - mTerrainManager->cellRemoved(store); } void RenderingManager::removeWater () @@ -232,9 +241,8 @@ void RenderingManager::toggleWater() void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) { mObjects.buildStaticGeometry (*store); + sh::Factory::getInstance().unloadUnreferencedMaterials(); mDebugging->cellAdded(store); - if (store->mCell->isExterior()) - mTerrainManager->cellAdded(store); waterAdded(store); } @@ -261,45 +269,20 @@ 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); - - 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::Vector3 rot(ptr.getRefData().getPosition().rot); - Ogre::Quaternion xref(Ogre::Radian(-ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yref(Ogre::Radian(-ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zref(Ogre::Radian(-ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::UNIT_Z); + if(ptr.getRefData().getHandle() == mCamera->getHandle() && + !mCamera->isVanityOrPreviewModeEnabled()) + mCamera->rotateCamera(rot, false); - Ogre::Quaternion newo = adjust ? (xr * yr * zr) * (xref*yref*zref) : xr * yr * zr; + 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; - Ogre::Matrix3 mat; - newo.ToRotationMatrix(mat); - Ogre::Radian ax,ay,az; - mat.ToEulerAnglesXYZ(ax,ay,az); - rot.x = -ax.valueRadians(); - rot.y = -ay.valueRadians(); - rot.z = -az.valueRadians(); - - ptr.getRefData().getBaseNode()->setOrientation(newo); - } - else if(isPlayer) - { - rot.x = -mPlayer->getPitch(); - rot.z = mPlayer->getYaw(); - } - return force; + ptr.getRefData().getBaseNode()->setOrientation(newo); } void @@ -318,32 +301,57 @@ RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr & } } +void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) +{ + if(mPlayerAnimation) + mPlayerAnimation->updatePtr(ptr); + if(mCamera->getHandle() == ptr.getRefData().getHandle()) + mCamera->attachTo(ptr); +} + +void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) +{ + NpcAnimation *anim = NULL; + if(ptr.getRefData().getHandle() == "player") + anim = mPlayerAnimation; + else if(MWWorld::Class::get(ptr).isActor()) + anim = dynamic_cast(mActors.getAnimation(ptr)); + if(anim) + { + anim->rebuild(); + if(mCamera->getHandle() == ptr.getRefData().getHandle()) + { + mCamera->attachTo(ptr); + mCamera->setAnimation(anim); + } + } +} + 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(std::max(0.f, 1.f-(blind / 100.f))); + setAmbientMode(); + // player position - MWWorld::RefData &data = - MWBase::Environment::get() - .getWorld() - ->getPlayer() - .getPlayer() - .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() * mRootNode->getScale().z; + MWWorld::RefData &data = player.getRefData(); + Ogre::Vector3 playerPos(data.getPosition().pos); + + mCamera->setCameraDistance(); + if(!mCamera->isFirstPerson()) + { + Ogre::Vector3 orig, dest; + mCamera->getPosition(orig, dest); 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); @@ -356,14 +364,12 @@ void RenderingManager::update (float duration, bool paused) Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); - applyFog(world->isUnderwater (world->getPlayer().getPlayer().getCell(), cam)); + applyFog(world->isUnderwater(player.getCell(), cam)); + + mCamera->update(duration, paused); if(paused) - { return; - } - - mPlayer->update(duration); mActors.update (duration); mObjects.update (duration); @@ -374,18 +380,11 @@ void RenderingManager::update (float duration, bool paused) mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); Ogre::SceneNode *node = data.getBaseNode(); - //Ogre::Quaternion orient = - //node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); - Ogre::Quaternion orient = -node->_getDerivedOrientation(); + Ogre::Quaternion orient = node->_getDerivedOrientation(); mLocalMap->updatePlayer(playerPos, orient); - mWater->updateUnderwater( - world->isUnderwater( - world->getPlayer().getPlayer().getCell(), - cam) - ); + mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam)); mWater->update(duration, playerPos); } @@ -425,29 +424,24 @@ void RenderingManager::setWaterHeight(const float height) void RenderingManager::skyEnable () { - if(mSkyManager) mSkyManager->enable(); - mOcclusionQuery->setSunNode(mSkyManager->getSunNode()); } void RenderingManager::skyDisable () { - if(mSkyManager) - mSkyManager->disable(); + mSkyManager->disable(); } void RenderingManager::skySetHour (double hour) { - if(mSkyManager) - mSkyManager->setHour(hour); + mSkyManager->setHour(hour); } void RenderingManager::skySetDate (int day, int month) { - if(mSkyManager) - mSkyManager->setDate(day, month); + mSkyManager->setDate(day, month); } int RenderingManager::skyGetMasserPhase() const @@ -462,8 +456,7 @@ int RenderingManager::skyGetSecundaPhase() const } void RenderingManager::skySetMoonColour (bool red){ - if(mSkyManager) - mSkyManager->setMoonColour(red); + mSkyManager->setMoonColour(red); } bool RenderingManager::toggleRenderMode(int mode) @@ -536,28 +529,26 @@ void RenderingManager::applyFog (bool underwater) 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; + } } void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell) { - mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient); + if (mCell.mCell->mData.mFlags & ESM::Cell::Interior) + mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient); setAmbientMode(); // Create a "sun" that shines light downwards. It doesn't look @@ -565,12 +556,15 @@ void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell) if(!mSun) { mSun = mRendering.getScene()->createLight(); + mSun->setType(Ogre::Light::LT_DIRECTIONAL); + } + if (mCell.mCell->mData.mFlags & ESM::Cell::Interior) + { + Ogre::ColourValue colour; + colour.setAsABGR (mCell.mCell->mAmbi.mSunlight); + mSun->setDiffuseColour (colour); + mSun->setDirection(0,-1,0); } - Ogre::ColourValue colour; - colour.setAsABGR (mCell.mCell->mAmbi.mSunlight); - mSun->setDiffuseColour (colour); - mSun->setType(Ogre::Light::LT_DIRECTIONAL); - mSun->setDirection(0,-1,0); } // Switch through lighting modes. @@ -596,13 +590,18 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour) if (!mSunEnabled) return; mSun->setDiffuseColour(colour); mSun->setSpecularColour(colour); - mTerrainManager->setDiffuse(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); } void RenderingManager::sunEnable(bool real) @@ -646,7 +645,18 @@ void RenderingManager::setGlare(bool glare) void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell) { if (cell->mCell->isExterior()) - mLocalMap->requestMap(cell); + { + assert(mTerrain); + + Ogre::AxisAlignedBox dims = mObjects.getDimensions(cell); + Ogre::Vector2 center(cell->mCell->getGridX() + 0.5, cell->mCell->getGridY() + 0.5); + dims.merge(mTerrain->getWorldBoundingBox(center)); + + if (dims.isFinite()) + mTerrain->update(dims.getCenter()); + + mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z); + } else mLocalMap->requestMap(cell, mObjects.getDimensions(cell)); } @@ -807,18 +817,37 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec { unsigned int x = Settings::Manager::getInt("resolution x", "Video"); unsigned int y = Settings::Manager::getInt("resolution y", "Video"); + bool fullscreen = Settings::Manager::getBool("fullscreen", "Video"); + + SDL_Window* window = mRendering.getSDLWindow(); + + SDL_SetWindowFullscreen(window, 0); - if (x != mRendering.getWindow()->getWidth() || y != mRendering.getWindow()->getHeight()) + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) + SDL_RestoreWindow(window); + + if (fullscreen) { - mRendering.getWindow()->resize(x, y); + SDL_DisplayMode mode; + SDL_GetWindowDisplayMode(window, &mode); + mode.w = x; + mode.h = y; + SDL_SetWindowDisplayMode(window, &mode); + SDL_SetWindowFullscreen(window, fullscreen); } - mRendering.getWindow()->setFullscreen(Settings::Manager::getBool("fullscreen", "Video"), x, y); + else + SDL_SetWindowSize(window, x, y); } mWater->processChangedSettings(settings); if (rebuild) + { mObjects.rebuildStaticGeometry(); + if (mTerrain) + mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), + Settings::Manager::getBool("split", "Shadows")); + } } void RenderingManager::setMenuTransparency(float val) @@ -831,25 +860,14 @@ void RenderingManager::setMenuTransparency(float val) tex->getBuffer()->unlock(); } -void RenderingManager::windowResized(Ogre::RenderWindow* rw) +void RenderingManager::windowResized(int x, int y) { - Settings::Manager::setInt("resolution x", "Video", rw->getWidth()); - Settings::Manager::setInt("resolution y", "Video", rw->getHeight()); - - mRendering.adjustViewport(); mCompositors->recreate(); - mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight()); + mVideoPlayer->setResolution (x, y); - const Settings::CategorySettingVector& changed = Settings::Manager::apply(); - MWBase::Environment::get().getInputManager()->processChangedSettings(changed); - MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); -} - -void RenderingManager::windowClosed(Ogre::RenderWindow* rw) -{ - Ogre::Root::getSingleton ().queueEndRendering (); + MWBase::Environment::get().getWindowManager()->windowResized(x,y); } void RenderingManager::applyCompositors() @@ -869,28 +887,59 @@ 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); - mWater->removeEmitter (ptr); - mWater->addEmitter (ptr); + 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) +bool RenderingManager::vanityRotateCamera(const float *rot) { - eyepos = mPlayer->getPosition(); - eyepos.z += mPlayer->getHeight(); - mPlayer->getSightAngles(pitch, yaw); + if(!mCamera->isVanityOrPreviewModeEnabled()) + return false; + + Ogre::Vector3 vRot(rot); + mCamera->rotateCamera(vRot, true); + return true; +} + +void RenderingManager::setCameraDistance(float dist, bool adjust, bool override) +{ + if(!mCamera->isVanityOrPreviewModeEnabled() && !mCamera->isFirstPerson()) + { + if(mCamera->isNearest() && dist > 0.f) + mCamera->toggleViewMode(); + else + mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override); + } + else if(mCamera->isFirstPerson() && dist < 0.f) + { + mCamera->toggleViewMode(); + mCamera->setCameraDistance(0.f, false, override); + } } void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) @@ -912,7 +961,7 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) { Animation *anim = mActors.getAnimation(ptr); if(!anim && ptr.getRefData().getHandle() == "player") - anim = mPlayer->getAnimation(); + anim = mPlayerAnimation; return anim; } @@ -942,9 +991,48 @@ void RenderingManager::updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, con mWater->updateEmitterPtr(old, ptr); } -void RenderingManager::frameStarted(float dt) +void RenderingManager::frameStarted(float dt, bool paused) +{ + if (mTerrain) + mTerrain->update(mRendering.getCamera()->getRealPosition()); + + if (!paused) + mWater->frameStarted(dt); +} + +void RenderingManager::resetCamera() { - mWater->frameStarted(dt); + mCamera->reset(); +} + +float RenderingManager::getTerrainHeightAt(Ogre::Vector3 worldPos) +{ + if (!mTerrain || !mTerrain->getVisible()) + return -std::numeric_limits::max(); + return mTerrain->getHeightAt(worldPos); +} + +void RenderingManager::enableTerrain(bool enable) +{ + if (enable) + { + if (!mTerrain) + { + Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(listener); + mTerrain = new Terrain::World(listener, mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + Settings::Manager::getBool("distant land", "Terrain"), + Settings::Manager::getBool("shader", "Terrain")); + mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), + Settings::Manager::getBool("split", "Shadows")); + mTerrain->update(mRendering.getCamera()->getRealPosition()); + mTerrain->setLoadingListener(NULL); + } + mTerrain->setVisible(true); + } + else + if (mTerrain) + mTerrain->setVisible(false); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 1777a72c3..2d0813912 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -2,7 +2,6 @@ #define _GAME_RENDERING_MANAGER_H #include "sky.hpp" -#include "terrain.hpp" #include "debugging.hpp" #include @@ -17,7 +16,7 @@ #include "objects.hpp" #include "actors.hpp" -#include "player.hpp" +#include "camera.hpp" #include "occlusionquery.hpp" namespace Ogre @@ -39,6 +38,11 @@ namespace sh class Factory; } +namespace Terrain +{ + class World; +} + namespace MWRender { class Shadows; @@ -50,47 +54,45 @@ namespace MWRender class VideoPlayer; class Animation; -class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener { - - private: - - +class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener +{ +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) { - mPlayer->togglePlayerLooking(enable); - } + void togglePlayerLooking(bool enable) + { mCamera->togglePlayerLooking(enable); } - void changeVanityModeScale(float factor) { - if (mPlayer->isVanityOrPreviewModeEnabled()) - mPlayer->setCameraDistance(-factor/120.f*10, true, true); + 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(); + + bool vanityRotateCamera(const float *rot); + void setCameraDistance(float dist, bool adjust = false, bool override = true); - void attachCameraTo(const MWWorld::Ptr &ptr); + void setupPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); @@ -108,6 +110,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void cellAdded (MWWorld::CellStore *store); void waterAdded(MWWorld::CellStore *store); + void enableTerrain(bool enable); + void removeWater(); void preCellChange (MWWorld::CellStore* store); @@ -119,11 +123,8 @@ 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(); @@ -133,6 +134,13 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList /// \param cur Object reference in new cell void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); + /// Specifies an updated Ptr object for the player (used on cell change). + void updatePlayerPtr(const MWWorld::Ptr &ptr); + + /// Currently for NPCs only. Rebuilds the NPC, updating their root model, animation sources, + /// and equipment. + void rebuildPtr(const MWWorld::Ptr &ptr); + void update (float duration, bool paused); void setAmbientColour(const Ogre::ColourValue& colour); @@ -151,6 +159,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList bool occlusionQuerySupported() { return mOcclusionQuery->supported(); } OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; } + float getTerrainHeightAt (Ogre::Vector3 worldPos); + Shadows* getShadows(); void switchToInterior(); @@ -201,14 +211,12 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void playVideo(const std::string& name, bool allowSkipping); void stopVideo(); - void frameStarted(float dt); - - protected: - virtual void windowResized(Ogre::RenderWindow* rw); - virtual void windowClosed(Ogre::RenderWindow* rw); + void frameStarted(float dt, bool paused); - private: +protected: + virtual void windowResized(int x, int y); +private: sh::Factory* mFactory; void setAmbientMode(); @@ -220,11 +228,13 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList bool mSunEnabled; + MWWorld::Fallback* mFallback; + SkyManager* mSkyManager; OcclusionQuery* mOcclusionQuery; - TerrainManager* mTerrainManager; + Terrain::World* mTerrain; MWRender::Water *mWater; @@ -235,6 +245,8 @@ 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; @@ -249,7 +261,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList OEngine::Physic::PhysicEngine* mPhysicsEngine; - MWRender::Player *mPlayer; + MWRender::Camera *mCamera; MWRender::Debugging *mDebugging; diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 595a82294..21bbe51b6 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -28,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; + bool split = Settings::Manager::getBool("split", "Shadows"); sh::Factory::getInstance ().setGlobalSetting ("shadows", enabled && !split ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("shadows_pssm", enabled && split ? "true" : "false"); @@ -110,8 +107,8 @@ void Shadows::recreate() // Set visibility mask for the shadow render textures int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows") + (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows") - + RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows"); - + + RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows") + + RV_Terrain * (Settings::Manager::getBool("terrain shadows", "Shadows")); for (int i = 0; i < (split ? 3 : 1); ++i) { TexturePtr shadowTexture = mSceneMgr->getShadowTexture(i); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 604ad50d2..03e14dc07 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -21,6 +23,8 @@ #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 p; + 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,7 +204,7 @@ unsigned int Moon::getPhaseInt() const return 0; } -SkyManager::SkyManager (SceneNode* root, Camera* pCamera) +SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) : mHour(0.0f) , mDay(0) , mMonth(0) @@ -216,7 +217,6 @@ SkyManager::SkyManager (SceneNode* root, Camera* pCamera) , mSceneMgr(NULL) , mAtmosphereDay(NULL) , mAtmosphereNight(NULL) - , mCloudFragmentShader() , mClouds() , mNextClouds() , mCloudBlendFactor(0.0f) @@ -266,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); @@ -280,15 +281,14 @@ void SkyManager::create() mSunGlare->setRenderQueue(RQG_SkiesLate); mSunGlare->setVisibilityFlags(RV_NoReflection); - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite (); + Ogre::AxisAlignedBox aabInf = Ogre::AxisAlignedBox::BOX_INFINITE; // Stars mAtmosphereNight = mRootNode->createChildSceneNode(); - NifOgre::EntityList entities = NifOgre::Loader::createEntities(mAtmosphereNight, "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); @@ -311,10 +311,10 @@ void SkyManager::create() // Atmosphere (day) mAtmosphereDay = mRootNode->createChildSceneNode(); - entities = NifOgre::Loader::createEntities(mAtmosphereDay, "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); @@ -329,10 +329,10 @@ void SkyManager::create() // Clouds SceneNode* clouds_node = mRootNode->createChildSceneNode(); - entities = NifOgre::Loader::createEntities(clouds_node, "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); for(unsigned int j = 0;j < clouds_ent->getNumSubEntities();j++) @@ -368,9 +368,7 @@ 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; @@ -381,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) @@ -404,8 +402,7 @@ void SkyManager::update(float duration) 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); } @@ -534,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() @@ -641,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 66557a6c1..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; }; @@ -117,9 +117,6 @@ namespace MWRender void update(float duration); - void create(); - ///< no need to call this, automatically done on first enable() - void enable(); void disable(); @@ -173,11 +170,10 @@ namespace MWRender 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; @@ -200,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; @@ -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 deleted file mode 100644 index 438366873..000000000 --- a/apps/openmw/mwrender/terrain.cpp +++ /dev/null @@ -1,541 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "../mwworld/esmstore.hpp" - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -#include "terrainmaterial.hpp" -#include "terrain.hpp" -#include "renderconst.hpp" -#include "shadows.hpp" -#include "renderingmanager.hpp" - -using namespace Ogre; - -namespace MWRender -{ - - //---------------------------------------------------------------------------------------------- - - TerrainManager::TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend) : - mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Y, mLandSize, mWorldSize)), mRendering(rend) - { - mTerrainGlobals = OGRE_NEW TerrainGlobalOptions(); - - TerrainMaterialGeneratorPtr matGen; - TerrainMaterial* matGenP = new TerrainMaterial(); - matGen.bind(matGenP); - mTerrainGlobals->setDefaultMaterialGenerator(matGen); - - TerrainMaterialGenerator::Profile* const activeProfile = - mTerrainGlobals->getDefaultMaterialGenerator() - ->getActiveProfile(); - mActiveProfile = static_cast(activeProfile); - - // 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); - - //10 (default) didn't seem to be quite enough - mTerrainGlobals->setSkirtSize(128); - - //due to the sudden flick between composite and non composite textures, - //this seemed the distance where it wasn't too noticeable - mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); - - mTerrainGroup.setOrigin(Vector3(mWorldSize/2, - mWorldSize/2, - 0)); - - Terrain::ImportData& importSettings = mTerrainGroup.getDefaultImportSettings(); - - importSettings.inputBias = 0; - importSettings.terrainSize = mLandSize; - importSettings.worldSize = mWorldSize; - importSettings.minBatchSize = 9; - importSettings.maxBatchSize = mLandSize; - - importSettings.deleteInputData = true; - } - - //---------------------------------------------------------------------------------------------- - - TerrainManager::~TerrainManager() - { - OGRE_DELETE mTerrainGlobals; - } - - //---------------------------------------------------------------------------------------------- - - void TerrainManager::setDiffuse(const ColourValue& diffuse) - { - mTerrainGlobals->setCompositeMapDiffuse(diffuse); - } - - //---------------------------------------------------------------------------------------------- - - void TerrainManager::setAmbient(const ColourValue& ambient) - { - mTerrainGlobals->setCompositeMapAmbient(ambient); - } - - //---------------------------------------------------------------------------------------------- - - void TerrainManager::cellAdded(MWWorld::Ptr::CellStore *store) - { - const int cellX = store->mCell->getGridX(); - const int cellY = store->mCell->getGridY(); - - ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search(cellX, cellY); - if (land == NULL) // no land data means we're not going to create any terrain. - return; - - int dataRequired = ESM::Land::DATA_VHGT | ESM::Land::DATA_VCLR; - if (!land->isDataLoaded(dataRequired)) - { - land->loadData(dataRequired); - } - - //split the cell terrain into four segments - const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2; - - for ( int x = 0; x < 2; x++ ) - { - for ( int y = 0; y < 2; y++ ) - { - Terrain::ImportData terrainData = - mTerrainGroup.getDefaultImportSettings(); - - const int terrainX = cellX * 2 + x; - const int terrainY = cellY * 2 + y; - - //it makes far more sense to reallocate the memory here, - //and let Ogre deal with it due to the issues with deleting - //it at the wrong time if using threads (Which Terrain does) - terrainData.inputFloat = OGRE_ALLOC_T(float, - mLandSize*mLandSize, - MEMCATEGORY_GEOMETRY); - - //copy the height data row by row - for ( int terrainCopyY = 0; terrainCopyY < mLandSize; terrainCopyY++ ) - { - //the offset of the current segment - const size_t yOffset = y * (mLandSize-1) * ESM::Land::LAND_SIZE + - //offset of the row - terrainCopyY * ESM::Land::LAND_SIZE; - const size_t xOffset = x * (mLandSize-1); - - memcpy(&terrainData.inputFloat[terrainCopyY*mLandSize], - &land->mLandData->mHeights[yOffset + xOffset], - mLandSize*sizeof(float)); - } - - std::map indexes; - initTerrainTextures(&terrainData, cellX, cellY, - x * numTextures, y * numTextures, - numTextures, indexes, land->mPlugin); - - if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL) - { - mTerrainGroup.defineTerrain(terrainX, terrainY, &terrainData); - - mTerrainGroup.loadTerrain(terrainX, terrainY, true); - - Terrain* terrain = mTerrainGroup.getTerrain(terrainX, terrainY); - initTerrainBlendMaps(terrain, - cellX, cellY, - x * numTextures, y * numTextures, - numTextures, - indexes); - terrain->setVisibilityFlags(RV_Terrain); - terrain->setRenderQueueGroup(RQG_Main); - - // disable or enable global colour map (depends on available vertex colours) - if ( land->mLandData->mUsingColours ) - { - TexturePtr vertex = getVertexColours(land, - cellX, cellY, - x*(mLandSize-1), - y*(mLandSize-1), - mLandSize); - - mActiveProfile->setGlobalColourMapEnabled(true); - mActiveProfile->setGlobalColourMap (terrain, vertex->getName()); - } - else - mActiveProfile->setGlobalColourMapEnabled (false); - } - } - } - - // 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(); - } - - //---------------------------------------------------------------------------------------------- - - void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store) - { - for ( int x = 0; x < 2; x++ ) - { - for ( int y = 0; y < 2; y++ ) - { - int terrainX = store->mCell->getGridX() * 2 + x; - int terrainY = store->mCell->getGridY() * 2 + y; - if (mTerrainGroup.getTerrain(terrainX, terrainY) != NULL) - mTerrainGroup.unloadTerrain(terrainX, terrainY); - } - } - } - - //---------------------------------------------------------------------------------------------- - - void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData, - int cellX, int cellY, - int fromX, int fromY, int size, - 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"); - assert(fromX+size <= ESM::Land::LAND_TEXTURE_SIZE && - fromY+size <= ESM::Land::LAND_TEXTURE_SIZE && - "Can't get a terrain texture on terrain outside the current cell"); - - //this ensures that the ltex indexes are sorted (or retrived as sorted - //which simplifies shading between cells). - // - //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++ ) - { - 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); - } - } - - //there is one texture that we want to use as a base (i.e. it won't have - //a blend map). This holds the ltex index of that base texture so that - //we know not to include it in the output map - int baseTexture = -1; - for ( std::set::iterator iter = ltexIndexes.begin(); - iter != ltexIndexes.end(); - ++iter ) - { - uint16_t ltexIndex = *iter; - //this is the base texture, so we can ignore this at present - if ( ltexIndex == baseTexture ) - { - continue; - } - - const std::map::const_iterator it = indexes.find(ltexIndex); - - if ( it == indexes.end() ) - { - //NB: All vtex ids are +1 compared to the ltex ids - - const MWWorld::Store <exStore = - MWBase::Environment::get().getWorld()->getStore().get(); - - // 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 ) - { - texture = "_land_default.dds"; - } - else - { - 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"; - } - - const size_t position = terrainData->layerList.size(); - terrainData->layerList.push_back(Terrain::LayerInstance()); - - terrainData->layerList[position].worldSize = 256; - terrainData->layerList[position].textureNames.push_back("textures\\" + texture); - - if ( baseTexture == -1 ) - { - baseTexture = ltexIndex; - } - else - { - indexes[ltexIndex] = position; - } - } - } - } - - //---------------------------------------------------------------------------------------------- - - void TerrainManager::initTerrainBlendMaps(Terrain* terrain, - int cellX, int cellY, - int fromX, int fromY, int size, - const std::map& indexes) - { - assert(terrain != NULL && "Must have valid terrain"); - assert(fromX >= 0 && fromY >= 0 && - "Can't get a terrain texture on terrain outside the current cell"); - assert(fromX+size <= ESM::Land::LAND_TEXTURE_SIZE && - fromY+size <= ESM::Land::LAND_TEXTURE_SIZE && - "Can't get a terrain texture on terrain outside the current cell"); - - //size must be a power of 2 as we do divisions with a power of 2 number - //that need to result in an integer for correct splatting - assert( (size & (size - 1)) == 0 && "Size must be a power of 2"); - - const int blendMapSize = terrain->getLayerBlendMapSize(); - const int splatSize = blendMapSize / size; - - //zero out every map - std::map::const_iterator iter; - for ( iter = indexes.begin(); iter != indexes.end(); ++iter ) - { - float* pBlend = terrain->getLayerBlendMap(iter->second) - ->getBlendPointer(); - memset(pBlend, 0, sizeof(float) * blendMapSize * blendMapSize); - } - - //covert the ltex data into a set of blend maps - for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ ) - { - for ( int texX = fromX - 1; texX < fromX + size + 1; texX++ ) - { - const uint16_t ltexIndex = getLtexIndexAt(cellX, cellY, texX, texY); - - //check if it is the base texture (which isn't in the map) and - //if it is don't bother altering the blend map for it - if ( indexes.find(ltexIndex) == indexes.end() ) - { - continue; - } - - //while texX is the splat index relative to the entire cell, - //relX is relative to the current segment we are splatting - const int relX = texX - fromX; - const int relY = texY - fromY; - - const int layerIndex = indexes.find(ltexIndex)->second; - - float* const pBlend = terrain->getLayerBlendMap(layerIndex) - ->getBlendPointer(); - - for ( int y = -1; y < splatSize + 1; y++ ) - { - for ( int x = -1; x < splatSize + 1; x++ ) - { - - //Note: Y is reversed - const int splatY = blendMapSize - 1 - relY * splatSize - y; - const int splatX = relX * splatSize + x; - - if ( splatX >= 0 && splatX < blendMapSize && - splatY >= 0 && splatY < blendMapSize ) - { - const int index = (splatY)*blendMapSize + splatX; - - if ( y >= 0 && y < splatSize && - x >= 0 && x < splatSize ) - { - pBlend[index] = 1; - } - else - { - //this provides a transition shading but also - //rounds off the corners slightly - pBlend[index] = std::min(1.0f, pBlend[index] + 0.5f); - } - } - - } - } - } - } - - for ( int i = 1; i < terrain->getLayerCount(); i++ ) - { - TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i); - blend->dirty(); - blend->update(); - } - - } - - //---------------------------------------------------------------------------------------------- - - int TerrainManager::getLtexIndexAt(int cellX, int cellY, - int x, int y) - { - //check texture index falls within the 9 cell bounds - //as this function can't cope with anything above that - assert(x >= -ESM::Land::LAND_TEXTURE_SIZE && - y >= -ESM::Land::LAND_TEXTURE_SIZE && - "Trying to get land textures that are out of bounds"); - - assert(x < 2*ESM::Land::LAND_TEXTURE_SIZE && - y < 2*ESM::Land::LAND_TEXTURE_SIZE && - "Trying to get land textures that are out of bounds"); - - if ( x < 0 ) - { - cellX--; - x += ESM::Land::LAND_TEXTURE_SIZE; - } - else if ( x >= ESM::Land::LAND_TEXTURE_SIZE ) - { - cellX++; - x -= ESM::Land::LAND_TEXTURE_SIZE; - } - - if ( y < 0 ) - { - cellY--; - y += ESM::Land::LAND_TEXTURE_SIZE; - } - else if ( y >= ESM::Land::LAND_TEXTURE_SIZE ) - { - cellY++; - y -= ESM::Land::LAND_TEXTURE_SIZE; - } - - - ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search(cellX, cellY); - if ( land != NULL ) - { - if (!land->isDataLoaded(ESM::Land::DATA_VTEX)) - { - land->loadData(ESM::Land::DATA_VTEX); - } - - return land->mLandData - ->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; - } - else - { - return 0; - } - } - - //---------------------------------------------------------------------------------------------- - - TexturePtr TerrainManager::getVertexColours(ESM::Land* land, - int cellX, int cellY, - int fromX, int fromY, int size) - { - TextureManager* const texMgr = TextureManager::getSingletonPtr(); - - const std::string colourTextureName = "VtexColours_" + - boost::lexical_cast(cellX) + - "_" + - boost::lexical_cast(cellY) + - "_" + - boost::lexical_cast(fromX) + - "_" + - boost::lexical_cast(fromY); - - TexturePtr tex = texMgr->getByName(colourTextureName); - if ( !tex.isNull() ) - { - return tex; - } - - tex = texMgr->createManual(colourTextureName, - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, size, size, 0, PF_BYTE_BGR); - - HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); - - pixelBuffer->lock(HardwareBuffer::HBL_DISCARD); - const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - uint8* pDest = static_cast(pixelBox.data); - - if ( land != NULL ) - { - const char* const colours = land->mLandData->mColours; - for ( int y = 0; y < size; y++ ) - { - for ( int x = 0; x < size; x++ ) - { - const size_t colourOffset = (y+fromY)*3*65 + (x+fromX)*3; - - assert( colourOffset < 65*65*3 && - "Colour offset is out of the expected bounds of record" ); - - const unsigned char r = colours[colourOffset + 0]; - const unsigned char g = colours[colourOffset + 1]; - const unsigned char b = colours[colourOffset + 2]; - - //as is the case elsewhere we need to flip the y - const size_t imageOffset = (size - 1 - y)*size*4 + x*4; - pDest[imageOffset + 0] = b; - pDest[imageOffset + 1] = g; - pDest[imageOffset + 2] = r; - } - } - } - else - { - for ( int y = 0; y < size; y++ ) - { - for ( int x = 0; x < size; x++ ) - { - for ( int k = 0; k < 3; k++ ) - { - *pDest++ = 0; - } - } - } - } - - pixelBuffer->unlock(); - - return tex; - } - -} diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp deleted file mode 100644 index 484a0dbe3..000000000 --- a/apps/openmw/mwrender/terrain.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef _GAME_RENDER_TERRAIN_H -#define _GAME_RENDER_TERRAIN_H - -#include -#include - -#include - -#include "terrainmaterial.hpp" - -namespace Ogre{ - class SceneManager; - class TerrainGroup; - class TerrainGlobalOptions; - class Terrain; -} - -namespace MWWorld -{ - class CellStore; -} - -namespace MWRender{ - - class RenderingManager; - - /** - * Implements the Morrowind terrain using the Ogre Terrain Component - * - * Each terrain cell is split into four blocks as this leads to an increase - * in performance and means we don't hit splat limits quite as much - */ - class TerrainManager{ - public: - TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend); - virtual ~TerrainManager(); - - void setDiffuse(const Ogre::ColourValue& diffuse); - void setAmbient(const Ogre::ColourValue& ambient); - - void cellAdded(MWWorld::CellStore* store); - void cellRemoved(MWWorld::CellStore* store); - private: - Ogre::TerrainGlobalOptions* mTerrainGlobals; - Ogre::TerrainGroup mTerrainGroup; - - RenderingManager* mRendering; - - TerrainMaterial::Profile* mActiveProfile; - - /** - * The length in verticies of a single terrain block. - */ - static const int mLandSize = (ESM::Land::LAND_SIZE - 1)/2 + 1; - - /** - * The length in game units of a single terrain block. - */ - static const int mWorldSize = ESM::Land::REAL_SIZE/2; - - /** - * Setups up the list of textures for part of a cell, using indexes as - * an output to create a mapping of MW LtexIndex to the relevant terrain - * layer - * - * @param terrainData the terrain data to setup the textures for - * @param cellX the coord of the cell - * @param cellY the coord of the cell - * @param fromX the ltex index in the current cell to start making the texture from - * @param fromY the ltex index in the current cell to start making the texture from - * @param size the size (number of splats) to get - * @param indexes a mapping of ltex index to the terrain texture layer that - * can be used by initTerrainBlendMaps - */ - void initTerrainTextures(Ogre::Terrain::ImportData* terrainData, - int cellX, int cellY, - int fromX, int fromY, int size, - std::map& indexes, size_t plugin); - - /** - * Creates the blend (splatting maps) for the given terrain from the ltex data. - * - * @param terrain the terrain object for the current cell - * @param cellX the coord of the cell - * @param cellY the coord of the cell - * @param fromX the ltex index in the current cell to start making the texture from - * @param fromY the ltex index in the current cell to start making the texture from - * @param size the size (number of splats) to get - * @param indexes the mapping of ltex to blend map produced by initTerrainTextures - */ - void initTerrainBlendMaps(Ogre::Terrain* terrain, - int cellX, int cellY, - int fromX, int fromY, int size, - const std::map& indexes); - - /** - * Gets a LTEX index at the given point, assuming the current cell - * starts at (0,0). This supports getting values from the surrounding - * cells so negative x, y is acceptable - * - * @param cellX the coord of the cell - * @param cellY the coord of the cell - * @param x, y the splat position of the ltex index to get relative to the - * first splat of the current cell - */ - int getLtexIndexAt(int cellX, int cellY, int x, int y); - - /** - * Due to the fact that Ogre terrain doesn't support vertex colours - * we have to generate them manually - * - * @param cellX the coord of the cell - * @param cellY the coord of the cell - * @param fromX the *vertex* index in the current cell to start making texture from - * @param fromY the *vertex* index in the current cell to start making the texture from - * @param size the size (number of vertexes) to get - */ - Ogre::TexturePtr getVertexColours(ESM::Land* land, - int cellX, int cellY, - int fromX, int fromY, int size); - }; - -} - -#endif // _GAME_RENDER_TERRAIN_H diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp deleted file mode 100644 index 8a568883d..000000000 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "terrainmaterial.hpp" - -#include - -#include - -#include - -namespace -{ - Ogre::String getComponent (int num) - { - if (num == 0) - return "x"; - else if (num == 1) - return "y"; - else if (num == 2) - return "z"; - else - return "w"; - } -} - - -namespace MWRender -{ - - TerrainMaterial::TerrainMaterial() - { - mLayerDecl.samplers.push_back(Ogre::TerrainLayerSampler("albedo_specular", Ogre::PF_BYTE_RGBA)); - //mLayerDecl.samplers.push_back(Ogre::TerrainLayerSampler("normal_height", Ogre::PF_BYTE_RGBA)); - - mLayerDecl.elements.push_back( - Ogre::TerrainLayerSamplerElement(0, Ogre::TLSS_ALBEDO, 0, 3)); - //mLayerDecl.elements.push_back( - // Ogre::TerrainLayerSamplerElement(0, Ogre::TLSS_SPECULAR, 3, 1)); - //mLayerDecl.elements.push_back( - // Ogre::TerrainLayerSamplerElement(1, Ogre::TLSS_NORMAL, 0, 3)); - //mLayerDecl.elements.push_back( - // Ogre::TerrainLayerSamplerElement(1, Ogre::TLSS_HEIGHT, 3, 1)); - - - mProfiles.push_back(OGRE_NEW Profile(this, "SM2", "Profile for rendering on Shader Model 2 capable cards")); - setActiveProfile("SM2"); - } - - // ----------------------------------------------------------------------------------------------------------------------- - - TerrainMaterial::Profile::Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc) - : Ogre::TerrainMaterialGenerator::Profile(parent, name, desc) - , mGlobalColourMap(false) - { - } - - TerrainMaterial::Profile::~Profile() - { - } - - - Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain) - { - const Ogre::String& matName = terrain->getMaterialName(); - - sh::Factory::getInstance().destroyMaterialInstance (matName); - - Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(matName); - if (!mat.isNull()) - 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))))); - } - - // 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 + numLayers + 2)))); - - return Ogre::MaterialManager::getSingleton().getByName(matName); - } - - void TerrainMaterial::Profile::setGlobalColourMapEnabled (bool enabled) - { - mGlobalColourMap = enabled; - mParent->_markChanged(); - } - - void TerrainMaterial::Profile::setGlobalColourMap (Ogre::Terrain* terrain, const std::string& name) - { - sh::Factory::getInstance ().setTextureAlias (terrain->getMaterialName () + "_colourMap", name); - } - - Ogre::MaterialPtr TerrainMaterial::Profile::generateForCompositeMap(const Ogre::Terrain* terrain) - { - throw std::runtime_error ("composite map not supported"); - } - - Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const - { - // count the texture units free - Ogre::uint8 freeTextureUnits = 16; - // normalmap - --freeTextureUnits; - // colourmap - --freeTextureUnits; - // shadow - --freeTextureUnits; - - // each layer needs 1.25 units (1xdiffusespec, 0.25xblend) - return static_cast(freeTextureUnits / (1.25f)); - } - - void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) - { - } - - void TerrainMaterial::Profile::updateParamsForCompositeMap(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) - { - } - - void TerrainMaterial::Profile::requestOptions(Ogre::Terrain* terrain) - { - terrain->_setMorphRequired(true); - terrain->_setNormalMapRequired(true); // global normal map - terrain->_setLightMapRequired(false); - terrain->_setCompositeMapRequired(false); - } - -} diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp deleted file mode 100644 index fe1214cf5..000000000 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* ------------------------------------------------------------------------------ -This source file is part of OGRE -(Object-oriented Graphics Rendering Engine) -For the latest info, see http://www.ogre3d.org/ - -Copyright (c) 2000-2011 Torus Knot Software Ltd - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ------------------------------------------------------------------------------ -*/ - -#ifndef MWRENDER_TERRAINMATERIAL_H -#define MWRENDER_TERRAINMATERIAL_H - -#include "OgreTerrainPrerequisites.h" -#include "OgreTerrainMaterialGenerator.h" -#include "OgreGpuProgramParams.h" - -namespace sh -{ - class MaterialInstance; -} - -namespace MWRender -{ - - class TerrainMaterial : public Ogre::TerrainMaterialGenerator - { - public: - - class Profile : public Ogre::TerrainMaterialGenerator::Profile - { - public: - Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc); - virtual ~Profile(); - - virtual bool isVertexCompressionSupported() const { return false; } - - virtual Ogre::MaterialPtr generate(const Ogre::Terrain* terrain); - - virtual Ogre::MaterialPtr generateForCompositeMap(const Ogre::Terrain* terrain); - - virtual Ogre::uint8 getMaxLayers(const Ogre::Terrain* terrain) const; - - virtual void updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain); - - virtual void updateParamsForCompositeMap(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain); - - virtual void requestOptions(Ogre::Terrain* terrain); - - void setGlobalColourMapEnabled(bool enabled); - void setGlobalColourMap (Ogre::Terrain* terrain, const std::string& name); - virtual void setLightmapEnabled(bool) {} - - private: - sh::MaterialInstance* mMaterial; - - bool mGlobalColourMap; - - }; - - TerrainMaterial(); - }; - -} - - -#endif diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp new file mode 100644 index 000000000..318627fc7 --- /dev/null +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -0,0 +1,56 @@ +#include "terrainstorage.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/esmstore.hpp" + +namespace MWRender +{ + + Ogre::AxisAlignedBox TerrainStorage::getBounds() + { + int minX = 0, minY = 0, maxX = 0, maxY = 0; + + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + + MWWorld::Store::iterator it = esmStore.get().extBegin(); + for (; it != esmStore.get().extEnd(); ++it) + { + if (it->getGridX() < minX) + minX = it->getGridX(); + if (it->getGridX() > maxX) + maxX = it->getGridX(); + if (it->getGridY() < minY) + minY = it->getGridY(); + if (it->getGridY() > maxY) + maxY = it->getGridY(); + } + + // since grid coords are at cell origin, we need to add 1 cell + maxX += 1; + maxY += 1; + + return Ogre::AxisAlignedBox(minX, minY, 0, maxX, maxY, 0); + } + + ESM::Land* TerrainStorage::getLand(int cellX, int cellY) + { + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + ESM::Land* land = esmStore.get().search(cellX, cellY); + // Load the data we are definitely going to need + int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; + if (land && !land->isDataLoaded(mask)) + land->loadData(mask); + return land; + } + + const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) + { + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + return esmStore.get().find(index, plugin); + } + +} diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp new file mode 100644 index 000000000..ebf5e26ab --- /dev/null +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -0,0 +1,22 @@ +#ifndef MWRENDER_TERRAINSTORAGE_H +#define MWRENDER_TERRAINSTORAGE_H + +#include + +namespace MWRender +{ + + class TerrainStorage : public Terrain::Storage + { + private: + virtual ESM::Land* getLand (int cellX, int cellY); + virtual const ESM::LandTexture* getLandTexture(int index, short plugin); + public: + virtual Ogre::AxisAlignedBox getBounds(); + ///< Get bounds in cell units + }; + +} + + +#endif diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2cbc85cd3..ee2b80f73 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -16,6 +17,7 @@ #include "../mwbase/soundmanager.hpp" #include "../mwsound/sound_decoder.hpp" #include "../mwsound/sound.hpp" +#include "../mwbase/inputmanager.hpp" #include "renderconst.hpp" @@ -32,9 +34,24 @@ namespace MWRender extern "C" { -#include -#include -#include + #include + #include + #include + + // From libavformat version 55.0.100 and onward the declaration of av_gettime() is removed from libavformat/avformat.h and moved + // to libavutil/time.h + // https://github.com/FFmpeg/FFmpeg/commit/06a83505992d5f49846c18507a6c3eb8a47c650e + #if AV_VERSION_INT(55, 0, 100) <= AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO) + #include + #endif + + // From libavutil version 52.2.0 and onward the declaration of + // AV_CH_LAYOUT_* is removed from libavcodec/avcodec.h and moved to + // libavutil/channel_layout.h + #if AV_VERSION_INT(52, 2, 0) <= AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO) + #include + #endif } #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) @@ -788,8 +805,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; @@ -1017,13 +1034,15 @@ public: #endif // defined OPENMW_USE_FFMPEG -VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) +VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* window) : mState(NULL) , mSceneMgr(sceneMgr) - , mVideoMaterial(NULL) , mRectangle(NULL) , mNode(NULL) , mAllowSkipping(false) + , mWindow(window) + , mWidth(0) + , mHeight(0) { mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General"); if (mVideoMaterial.isNull ()) @@ -1114,6 +1133,14 @@ void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping) try { mState = new VideoState; mState->init(resourceName); + + while (isPlaying()) + { + MWBase::Environment::get().getInputManager()->update(0, false); + update(); + mWindow->update(); + } + } catch(std::exception& e) { std::cerr<< "Failed to play video: "<setFOVy(mParentCamera->getFOVy()); mRenderActive = true; - Vector3 pos = mParentCamera->getRealPosition(); - pos.y = (mWaterPlane).d*2 - pos.y; - mSky->setSkyPosition(pos); mCamera->enableReflection(mWaterPlane); // for depth calculation, we want the original viewproj matrix _without_ the custom near clip plane. @@ -153,7 +150,6 @@ void PlaneReflection::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) void PlaneReflection::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) { - mSky->resetSkyPosition(); mCamera->disableReflection(); mCamera->disableCustomNearClipPlane(); mRenderActive = false; @@ -206,7 +202,10 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend) : 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_Y); + int waterScale = 300; + + MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, + CELL_SIZE*5*waterScale, CELL_SIZE*5*waterScale, 10, 10, true, 1, 3*waterScale,3*waterScale, Vector3::UNIT_Y); mWater = mSceneMgr->createEntity("water"); mWater->setVisibilityFlags(RV_Water); diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 6c0323637..bc15b4980 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -41,7 +41,11 @@ namespace MWRender { { public: Reflection(Ogre::SceneManager* sceneManager) - : mSceneMgr(sceneManager) {} + : mSceneMgr(sceneManager) + , mIsUnderwater(false) + , mCamera(NULL) + , mParentCamera(NULL) + {} virtual ~Reflection() {} virtual void setHeight (float height) {} diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8402a5b4c..fac44c08f 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -2,6 +2,7 @@ #include "aiextensions.hpp" #include +#include #include #include @@ -181,23 +182,30 @@ namespace MWScript runtime.pop(); std::vector idleList; - idleList.push_back (0); // why MW, why? + bool repeat = false; - for (int i=2; i<10 && arg0; ++i) + for(int i=1; i < 10 && arg0; ++i) { - Interpreter::Type_Integer idleValue = runtime[0].mFloat; + if(!repeat) + repeat = true; + Interpreter::Type_Integer idleValue = runtime[0].mInteger; idleList.push_back(idleValue); runtime.pop(); --arg0; } + if(arg0) + { + repeat = runtime[0].mInteger != 0; + runtime.pop(); + --arg0; + } + // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i); - interpreter.installSegment3 (opcodeAIActivateExplicit, new OpAiActivate); - interpreter.installSegment3 (opcodeAiTravel, new OpAiTravel); - interpreter.installSegment3 (opcodeAiTravelExplicit, new OpAiTravel); - interpreter.installSegment3 (opcodeAiEscort, new OpAiEscort); - interpreter.installSegment3 (opcodeAiEscortExplicit, new OpAiEscort); - interpreter.installSegment3 (opcodeAiEscortCell, new OpAiEscortCell); - interpreter.installSegment3 (opcodeAiEscortCellExplicit, new OpAiEscortCell); - interpreter.installSegment3 (opcodeAiWander, new OpAiWander); - interpreter.installSegment3 (opcodeAiWanderExplicit, new OpAiWander); - interpreter.installSegment3 (opcodeAiFollow, new OpAiFollow); - interpreter.installSegment3 (opcodeAiFollowExplicit, new OpAiFollow); - interpreter.installSegment3 (opcodeAiFollowCell, new OpAiFollowCell); - interpreter.installSegment3 (opcodeAiFollowCellExplicit, new OpAiFollowCell); - interpreter.installSegment5 (opcodeGetAiPackageDone, new OpGetAiPackageDone); - - interpreter.installSegment5 (opcodeGetAiPackageDoneExplicit, + interpreter.installSegment3 (Compiler::Ai::opcodeAIActivate, new OpAiActivate); + interpreter.installSegment3 (Compiler::Ai::opcodeAIActivateExplicit, new OpAiActivate); + interpreter.installSegment3 (Compiler::Ai::opcodeAiTravel, new OpAiTravel); + interpreter.installSegment3 (Compiler::Ai::opcodeAiTravelExplicit, new OpAiTravel); + interpreter.installSegment3 (Compiler::Ai::opcodeAiEscort, new OpAiEscort); + interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortExplicit, new OpAiEscort); + interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortCell, new OpAiEscortCell); + interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortCellExplicit, new OpAiEscortCell); + interpreter.installSegment3 (Compiler::Ai::opcodeAiWander, new OpAiWander); + interpreter.installSegment3 (Compiler::Ai::opcodeAiWanderExplicit, new OpAiWander); + interpreter.installSegment3 (Compiler::Ai::opcodeAiFollow, new OpAiFollow); + interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowExplicit, new OpAiFollow); + interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowCell, new OpAiFollowCell); + interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowCellExplicit, new OpAiFollowCell); + interpreter.installSegment5 (Compiler::Ai::opcodeGetAiPackageDone, new OpGetAiPackageDone); + + interpreter.installSegment5 (Compiler::Ai::opcodeGetAiPackageDoneExplicit, new OpGetAiPackageDone); - interpreter.installSegment5 (opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage); - interpreter.installSegment5 (opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage); - interpreter.installSegment3 (opcodeGetDetected, new OpGetDetected); - interpreter.installSegment3 (opcodeGetDetectedExplicit, new OpGetDetected); - interpreter.installSegment5 (opcodeSetHello, new OpSetAiSetting(0)); - interpreter.installSegment5 (opcodeSetHelloExplicit, new OpSetAiSetting(0)); - interpreter.installSegment5 (opcodeSetFight, new OpSetAiSetting(1)); - interpreter.installSegment5 (opcodeSetFightExplicit, new OpSetAiSetting(1)); - interpreter.installSegment5 (opcodeSetFlee, new OpSetAiSetting(2)); - interpreter.installSegment5 (opcodeSetFleeExplicit, new OpSetAiSetting(2)); - interpreter.installSegment5 (opcodeSetAlarm, new OpSetAiSetting(3)); - interpreter.installSegment5 (opcodeSetAlarmExplicit, new OpSetAiSetting(3)); - - interpreter.installSegment5 (opcodeModHello, new OpModAiSetting(0)); - interpreter.installSegment5 (opcodeModHelloExplicit, new OpModAiSetting(0)); - interpreter.installSegment5 (opcodeModFight, new OpModAiSetting(1)); - interpreter.installSegment5 (opcodeModFightExplicit, new OpModAiSetting(1)); - interpreter.installSegment5 (opcodeModFlee, new OpModAiSetting(2)); - interpreter.installSegment5 (opcodeModFleeExplicit, new OpModAiSetting(2)); - interpreter.installSegment5 (opcodeModAlarm, new OpModAiSetting(3)); - interpreter.installSegment5 (opcodeModAlarmExplicit, new OpModAiSetting(3)); - - interpreter.installSegment5 (opcodeGetHello, new OpGetAiSetting(0)); - interpreter.installSegment5 (opcodeGetHelloExplicit, new OpGetAiSetting(0)); - interpreter.installSegment5 (opcodeGetFight, new OpGetAiSetting(1)); - interpreter.installSegment5 (opcodeGetFightExplicit, new OpGetAiSetting(1)); - interpreter.installSegment5 (opcodeGetFlee, new OpGetAiSetting(2)); - interpreter.installSegment5 (opcodeGetFleeExplicit, new OpGetAiSetting(2)); - interpreter.installSegment5 (opcodeGetAlarm, new OpGetAiSetting(3)); - interpreter.installSegment5 (opcodeGetAlarmExplicit, new OpGetAiSetting(3)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage); + interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage); + interpreter.installSegment3 (Compiler::Ai::opcodeGetDetected, new OpGetDetected); + interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); + interpreter.installSegment5 (Compiler::Ai::opcodeSetHello, new OpSetAiSetting(0)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetHelloExplicit, new OpSetAiSetting(0)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFight, new OpSetAiSetting(1)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFightExplicit, new OpSetAiSetting(1)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFlee, new OpSetAiSetting(2)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFleeExplicit, new OpSetAiSetting(2)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarm, new OpSetAiSetting(3)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarmExplicit, new OpSetAiSetting(3)); + + interpreter.installSegment5 (Compiler::Ai::opcodeModHello, new OpModAiSetting(0)); + interpreter.installSegment5 (Compiler::Ai::opcodeModHelloExplicit, new OpModAiSetting(0)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFight, new OpModAiSetting(1)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFightExplicit, new OpModAiSetting(1)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFlee, new OpModAiSetting(2)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFleeExplicit, new OpModAiSetting(2)); + interpreter.installSegment5 (Compiler::Ai::opcodeModAlarm, new OpModAiSetting(3)); + interpreter.installSegment5 (Compiler::Ai::opcodeModAlarmExplicit, new OpModAiSetting(3)); + + interpreter.installSegment5 (Compiler::Ai::opcodeGetHello, new OpGetAiSetting(0)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetHelloExplicit, new OpGetAiSetting(0)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFight, new OpGetAiSetting(1)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFightExplicit, new OpGetAiSetting(1)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFlee, new OpGetAiSetting(2)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting(2)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting(3)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting(3)); } } } diff --git a/apps/openmw/mwscript/aiextensions.hpp b/apps/openmw/mwscript/aiextensions.hpp index 547341476..e9e36113c 100644 --- a/apps/openmw/mwscript/aiextensions.hpp +++ b/apps/openmw/mwscript/aiextensions.hpp @@ -16,8 +16,6 @@ namespace MWScript /// \brief AI-related script functionality namespace Ai { - void registerExtensions (Compiler::Extensions& extensions); - void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/animationextensions.cpp b/apps/openmw/mwscript/animationextensions.cpp index fc52e5e16..52de8e042 100644 --- a/apps/openmw/mwscript/animationextensions.cpp +++ b/apps/openmw/mwscript/animationextensions.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -90,29 +91,16 @@ namespace MWScript MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops); } }; - - const int opcodeSkipAnim = 0x2000138; - const int opcodeSkipAnimExplicit = 0x2000139; - const int opcodePlayAnim = 0x20006; - const int opcodePlayAnimExplicit = 0x20007; - const int opcodeLoopAnim = 0x20008; - const int opcodeLoopAnimExplicit = 0x20009; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerInstruction ("skipanim", "", opcodeSkipAnim, opcodeSkipAnimExplicit); - extensions.registerInstruction ("playgroup", "c/l", opcodePlayAnim, opcodePlayAnimExplicit); - extensions.registerInstruction ("loopgroup", "cl/l", opcodeLoopAnim, opcodeLoopAnimExplicit); - } + void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeSkipAnim, new OpSkipAnim); - interpreter.installSegment5 (opcodeSkipAnimExplicit, new OpSkipAnim); - interpreter.installSegment3 (opcodePlayAnim, new OpPlayAnim); - interpreter.installSegment3 (opcodePlayAnimExplicit, new OpPlayAnim); - interpreter.installSegment3 (opcodeLoopAnim, new OpLoopAnim); - interpreter.installSegment3 (opcodeLoopAnimExplicit, new OpLoopAnim); + interpreter.installSegment5 (Compiler::Animation::opcodeSkipAnim, new OpSkipAnim); + interpreter.installSegment5 (Compiler::Animation::opcodeSkipAnimExplicit, new OpSkipAnim); + interpreter.installSegment3 (Compiler::Animation::opcodePlayAnim, new OpPlayAnim); + interpreter.installSegment3 (Compiler::Animation::opcodePlayAnimExplicit, new OpPlayAnim); + interpreter.installSegment3 (Compiler::Animation::opcodeLoopAnim, new OpLoopAnim); + interpreter.installSegment3 (Compiler::Animation::opcodeLoopAnimExplicit, new OpLoopAnim); } } } diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index a4a738ca7..316f912da 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -4,6 +4,7 @@ #include "../mwworld/esmstore.hpp" #include +#include #include #include @@ -40,19 +41,16 @@ namespace MWScript runtime.pop(); ESM::Position pos; - pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - pos.pos[2] = 0; + MWBase::World *world = MWBase::Environment::get().getWorld(); - if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (cell)) - { - MWBase::Environment::get().getWorld()->indexToPosition (exterior->mData.mX, exterior->mData.mY, - pos.pos[0], pos.pos[1], true); - MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + if (world->findExteriorPosition(cell, pos)) { + world->changeToExteriorCell(pos); } - else - { - pos.pos[0] = pos.pos[1] = 0; - MWBase::Environment::get().getWorld()->changeToInteriorCell (cell, pos); + else { + // Change to interior even if findInteriorPosition() + // yields false. In this case position will be zero-point. + world->findInteriorPosition(cell, pos); + world->changeToInteriorCell(cell, pos); } } }; @@ -168,39 +166,17 @@ namespace MWScript } }; - const int opcodeCellChanged = 0x2000000; - const int opcodeCOC = 0x2000026; - const int opcodeCOE = 0x200008e; - const int opcodeGetInterior = 0x2000131; - const int opcodeGetPCCell = 0x2000136; - const int opcodeGetWaterLevel = 0x2000141; - const int opcodeSetWaterLevel = 0x2000142; - const int opcodeModWaterLevel = 0x2000143; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged); - extensions.registerInstruction ("coc", "S", opcodeCOC); - extensions.registerInstruction ("centeroncell", "S", opcodeCOC); - extensions.registerInstruction ("coe", "ll", opcodeCOE); - extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE); - extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel); - extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel); - extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior); - extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell); - extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel); - } void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeCellChanged, new OpCellChanged); - interpreter.installSegment5 (opcodeCOC, new OpCOC); - interpreter.installSegment5 (opcodeCOE, new OpCOE); - interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior); - interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell); - interpreter.installSegment5 (opcodeGetWaterLevel, new OpGetWaterLevel); - interpreter.installSegment5 (opcodeSetWaterLevel, new OpSetWaterLevel); - interpreter.installSegment5 (opcodeModWaterLevel, new OpModWaterLevel); + interpreter.installSegment5 (Compiler::Cell::opcodeCellChanged, new OpCellChanged); + interpreter.installSegment5 (Compiler::Cell::opcodeCOC, new OpCOC); + interpreter.installSegment5 (Compiler::Cell::opcodeCOE, new OpCOE); + interpreter.installSegment5 (Compiler::Cell::opcodeGetInterior, new OpGetInterior); + interpreter.installSegment5 (Compiler::Cell::opcodeGetPCCell, new OpGetPCCell); + interpreter.installSegment5 (Compiler::Cell::opcodeGetWaterLevel, new OpGetWaterLevel); + interpreter.installSegment5 (Compiler::Cell::opcodeSetWaterLevel, new OpSetWaterLevel); + interpreter.installSegment5 (Compiler::Cell::opcodeModWaterLevel, new OpModWaterLevel); } } } diff --git a/apps/openmw/mwscript/cellextensions.hpp b/apps/openmw/mwscript/cellextensions.hpp index 4aef25d1a..0891cb9dc 100644 --- a/apps/openmw/mwscript/cellextensions.hpp +++ b/apps/openmw/mwscript/cellextensions.hpp @@ -15,9 +15,7 @@ namespace MWScript { /// \brief cell-related script functionality namespace Cell - { - void registerExtensions (Compiler::Extensions& extensions); - + { void installOpcodes (Interpreter::Interpreter& interpreter); } } 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/consoleextensions.cpp b/apps/openmw/mwscript/consoleextensions.cpp index 00b4f74e5..30956d429 100644 --- a/apps/openmw/mwscript/consoleextensions.cpp +++ b/apps/openmw/mwscript/consoleextensions.cpp @@ -2,6 +2,7 @@ #include "consoleextensions.hpp" #include +#include #include #include @@ -11,11 +12,6 @@ namespace MWScript { namespace Console { - void registerExtensions (Compiler::Extensions& extensions) - { - - } - void installOpcodes (Interpreter::Interpreter& interpreter) { diff --git a/apps/openmw/mwscript/consoleextensions.hpp b/apps/openmw/mwscript/consoleextensions.hpp index b10bf06a8..5571a5469 100644 --- a/apps/openmw/mwscript/consoleextensions.hpp +++ b/apps/openmw/mwscript/consoleextensions.hpp @@ -16,8 +16,6 @@ namespace MWScript /// \brief Script functionality limited to the console namespace Console { - void registerExtensions (Compiler::Extensions& extensions); - void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 81639b5be..2f3ef2d79 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -64,10 +65,10 @@ namespace MWScript ref.getPtr().getRefData().setLocals(*esmscript); } - MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr()); + MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr(), ptr); - // Spawn a messagebox (only for items added to player's inventory) - if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer()) + // 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; @@ -82,8 +83,8 @@ namespace MWScript msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage61}"); msgBox = boost::str(boost::format(msgBox) % count % itemName); } - - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, std::vector()); + std::vector noButtons; + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); } } }; @@ -161,12 +162,15 @@ namespace MWScript } } - // Spawn a messagebox (only for items added to player's inventory) + // Spawn a messagebox (only for items removed from player's inventory) 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 removed from their inventory std::string msgBox; int numRemoved = (originalCount - count); + if (numRemoved == 0) + return; + if(numRemoved > 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); @@ -177,9 +181,8 @@ namespace MWScript msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); msgBox = boost::str (boost::format(msgBox) % itemName); } - - if (numRemoved > 0) - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, std::vector()); + std::vector noButtons; + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); } } }; @@ -358,55 +361,25 @@ namespace MWScript } }; - const int opcodeAddItem = 0x2000076; - const int opcodeAddItemExplicit = 0x2000077; - const int opcodeGetItemCount = 0x2000078; - const int opcodeGetItemCountExplicit = 0x2000079; - const int opcodeRemoveItem = 0x200007a; - const int opcodeRemoveItemExplicit = 0x200007b; - const int opcodeEquip = 0x20001b3; - const int opcodeEquipExplicit = 0x20001b4; - const int opcodeGetArmorType = 0x20001d1; - const int opcodeGetArmorTypeExplicit = 0x20001d2; - const int opcodeHasItemEquipped = 0x20001d5; - const int opcodeHasItemEquippedExplicit = 0x20001d6; - const int opcodeHasSoulGem = 0x20001de; - const int opcodeHasSoulGemExplicit = 0x20001df; - const int opcodeGetWeaponType = 0x20001e0; - const int opcodeGetWeaponTypeExplicit = 0x20001e1; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerInstruction ("additem", "cl", opcodeAddItem, opcodeAddItemExplicit); - extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount, - opcodeGetItemCountExplicit); - extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, - opcodeRemoveItemExplicit); - extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); - extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); - extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); - extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit); - extensions.registerFunction ("getweapontype", 'l', "", opcodeGetWeaponType, opcodeGetWeaponTypeExplicit); - } void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeAddItem, new OpAddItem); - interpreter.installSegment5 (opcodeAddItemExplicit, new OpAddItem); - interpreter.installSegment5 (opcodeGetItemCount, new OpGetItemCount); - interpreter.installSegment5 (opcodeGetItemCountExplicit, new OpGetItemCount); - interpreter.installSegment5 (opcodeRemoveItem, new OpRemoveItem); - interpreter.installSegment5 (opcodeRemoveItemExplicit, new OpRemoveItem); - interpreter.installSegment5 (opcodeEquip, new OpEquip); - interpreter.installSegment5 (opcodeEquipExplicit, new OpEquip); - interpreter.installSegment5 (opcodeGetArmorType, new OpGetArmorType); - interpreter.installSegment5 (opcodeGetArmorTypeExplicit, new OpGetArmorType); - interpreter.installSegment5 (opcodeHasItemEquipped, new OpHasItemEquipped); - interpreter.installSegment5 (opcodeHasItemEquippedExplicit, new OpHasItemEquipped); - interpreter.installSegment5 (opcodeHasSoulGem, new OpHasSoulGem); - interpreter.installSegment5 (opcodeHasSoulGemExplicit, new OpHasSoulGem); - interpreter.installSegment5 (opcodeGetWeaponType, new OpGetWeaponType); - interpreter.installSegment5 (opcodeGetWeaponTypeExplicit, new OpGetWeaponType); + interpreter.installSegment5 (Compiler::Container::opcodeAddItem, new OpAddItem); + interpreter.installSegment5 (Compiler::Container::opcodeAddItemExplicit, new OpAddItem); + interpreter.installSegment5 (Compiler::Container::opcodeGetItemCount, new OpGetItemCount); + interpreter.installSegment5 (Compiler::Container::opcodeGetItemCountExplicit, new OpGetItemCount); + interpreter.installSegment5 (Compiler::Container::opcodeRemoveItem, new OpRemoveItem); + interpreter.installSegment5 (Compiler::Container::opcodeRemoveItemExplicit, new OpRemoveItem); + interpreter.installSegment5 (Compiler::Container::opcodeEquip, new OpEquip); + interpreter.installSegment5 (Compiler::Container::opcodeEquipExplicit, new OpEquip); + interpreter.installSegment5 (Compiler::Container::opcodeGetArmorType, new OpGetArmorType); + interpreter.installSegment5 (Compiler::Container::opcodeGetArmorTypeExplicit, new OpGetArmorType); + interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquipped, new OpHasItemEquipped); + interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquippedExplicit, new OpHasItemEquipped); + interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGem, new OpHasSoulGem); + interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGemExplicit, new OpHasSoulGem); + interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponType, new OpGetWeaponType); + interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponTypeExplicit, new OpGetWeaponType); } } } diff --git a/apps/openmw/mwscript/containerextensions.hpp b/apps/openmw/mwscript/containerextensions.hpp index 92c52e5c1..d5be8fb2a 100644 --- a/apps/openmw/mwscript/containerextensions.hpp +++ b/apps/openmw/mwscript/containerextensions.hpp @@ -16,8 +16,6 @@ namespace MWScript /// \brief Container-related script functionality (chests, NPCs, creatures) namespace Container { - void registerExtensions (Compiler::Extensions& extensions); - void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 8d65dfdd5..e46302b18 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -2,6 +2,7 @@ #include "controlextensions.hpp" #include +#include #include #include @@ -12,6 +13,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" #include "../mwmechanics/npcstats.hpp" @@ -158,97 +160,41 @@ namespace MWScript } }; - const int numberOfControls = 7; - - const int opcodeEnable = 0x200007e; - const int opcodeDisable = 0x2000085; - const int opcodeToggleCollision = 0x2000130; - const int opcodeClearForceRun = 0x2000154; - const int opcodeClearForceRunExplicit = 0x2000155; - const int opcodeForceRun = 0x2000156; - const int opcodeForceRunExplicit = 0x2000157; - const int opcodeClearForceSneak = 0x2000158; - const int opcodeClearForceSneakExplicit = 0x2000159; - const int opcodeForceSneak = 0x200015a; - const int opcodeForceSneakExplicit = 0x200015b; - const int opcodeGetDisabled = 0x2000175; - const int opcodeGetPcRunning = 0x20001c9; - const int opcodeGetPcSneaking = 0x20001ca; - const int opcodeGetForceRun = 0x20001cb; - const int opcodeGetForceSneak = 0x20001cc; - const int opcodeGetForceRunExplicit = 0x20001cd; - const int opcodeGetForceSneakExplicit = 0x20001ce; - - const char *controls[numberOfControls] = - { - "playercontrols", "playerfighting", "playerjumping", "playerlooking", "playermagic", - "playerviewswitch", "vanitymode" - }; - - void registerExtensions (Compiler::Extensions& extensions) - { - std::string enable ("enable"); - std::string disable ("disable"); - - for (int i=0; i (MWMechanics::NpcStats::Flag_ForceRun)); - interpreter.installSegment5 (opcodeForceRun, + interpreter.installSegment5 (Compiler::Control::opcodeForceRun, new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); - interpreter.installSegment5 (opcodeClearForceSneak, + interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak, new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); - interpreter.installSegment5 (opcodeForceSneak, + interpreter.installSegment5 (Compiler::Control::opcodeForceSneak, new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); - interpreter.installSegment5 (opcodeClearForceRunExplicit, + interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit, new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); - interpreter.installSegment5 (opcodeForceRunExplicit, + interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit, new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); - interpreter.installSegment5 (opcodeClearForceSneakExplicit, + interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit, new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); - interpreter.installSegment5 (opcodeForceSneakExplicit, + interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit, new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); - interpreter.installSegment5 (opcodeGetPcRunning, new OpGetPcRunning); - interpreter.installSegment5 (opcodeGetPcSneaking, new OpGetPcSneaking); - interpreter.installSegment5 (opcodeGetForceRun, new OpGetForceRun); - interpreter.installSegment5 (opcodeGetForceRunExplicit, new OpGetForceRun); - interpreter.installSegment5 (opcodeGetForceSneak, new OpGetForceSneak); - interpreter.installSegment5 (opcodeGetForceSneakExplicit, new OpGetForceSneak); + interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning); + interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking); + interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun); + interpreter.installSegment5 (Compiler::Control::opcodeGetForceRunExplicit, new OpGetForceRun); + interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneak, new OpGetForceSneak); + interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneakExplicit, new OpGetForceSneak); } } } diff --git a/apps/openmw/mwscript/controlextensions.hpp b/apps/openmw/mwscript/controlextensions.hpp index 8e6111705..b9c6654fe 100644 --- a/apps/openmw/mwscript/controlextensions.hpp +++ b/apps/openmw/mwscript/controlextensions.hpp @@ -16,8 +16,6 @@ namespace MWScript /// \brief player controls-related script functionality namespace Control { - void registerExtensions (Compiler::Extensions& extensions); - void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index f63623275..5e797ee58 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -2,6 +2,7 @@ #include "dialogueextensions.hpp" #include +#include #include #include @@ -188,62 +189,25 @@ namespace MWScript } }; - const int opcodeJournal = 0x2000133; - const int opcodeSetJournalIndex = 0x2000134; - const int opcodeGetJournalIndex = 0x2000135; - const int opcodeAddTopic = 0x200013a; - const int opcodeChoice = 0x2000a; - const int opcodeForceGreeting = 0x200014f; - const int opcodeForceGreetingExplicit = 0x2000150; - const int opcodeGoodbye = 0x2000152; - const int opcodeSetReputation = 0x20001ad; - const int opcodeModReputation = 0x20001ae; - const int opcodeSetReputationExplicit = 0x20001af; - const int opcodeModReputationExplicit = 0x20001b0; - const int opcodeGetReputation = 0x20001b1; - const int opcodeGetReputationExplicit = 0x20001b2; - const int opcodeSameFaction = 0x20001b5; - const int opcodeSameFactionExplicit = 0x20001b6; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerInstruction ("journal", "cl", opcodeJournal); - extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex); - extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex); - extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic); - extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice); - extensions.registerInstruction("forcegreeting","",opcodeForceGreeting); - extensions.registerInstruction("forcegreeting","",opcodeForceGreeting, - opcodeForceGreetingExplicit); - extensions.registerInstruction("goodbye", "", opcodeGoodbye); - extensions.registerInstruction("setreputation", "l", opcodeSetReputation, - opcodeSetReputationExplicit); - extensions.registerInstruction("modreputation", "l", opcodeModReputation, - opcodeModReputationExplicit); - extensions.registerFunction("getreputation", 'l', "", opcodeGetReputation, - opcodeGetReputationExplicit); - extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, - opcodeSameFactionExplicit); - } void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeJournal, new OpJournal); - interpreter.installSegment5 (opcodeSetJournalIndex, new OpSetJournalIndex); - interpreter.installSegment5 (opcodeGetJournalIndex, new OpGetJournalIndex); - interpreter.installSegment5 (opcodeAddTopic, new OpAddTopic); - interpreter.installSegment3 (opcodeChoice,new OpChoice); - interpreter.installSegment5 (opcodeForceGreeting, new OpForceGreeting); - interpreter.installSegment5 (opcodeForceGreetingExplicit, new OpForceGreeting); - interpreter.installSegment5 (opcodeGoodbye, new OpGoodbye); - interpreter.installSegment5 (opcodeGetReputation, new OpGetReputation); - interpreter.installSegment5 (opcodeSetReputation, new OpSetReputation); - interpreter.installSegment5 (opcodeModReputation, new OpModReputation); - interpreter.installSegment5 (opcodeSetReputationExplicit, new OpSetReputation); - interpreter.installSegment5 (opcodeModReputationExplicit, new OpModReputation); - interpreter.installSegment5 (opcodeGetReputationExplicit, new OpGetReputation); - interpreter.installSegment5 (opcodeSameFaction, new OpSameFaction); - interpreter.installSegment5 (opcodeSameFactionExplicit, new OpSameFaction); + interpreter.installSegment5 (Compiler::Dialogue::opcodeJournal, new OpJournal); + interpreter.installSegment5 (Compiler::Dialogue::opcodeSetJournalIndex, new OpSetJournalIndex); + interpreter.installSegment5 (Compiler::Dialogue::opcodeGetJournalIndex, new OpGetJournalIndex); + interpreter.installSegment5 (Compiler::Dialogue::opcodeAddTopic, new OpAddTopic); + interpreter.installSegment3 (Compiler::Dialogue::opcodeChoice,new OpChoice); + interpreter.installSegment5 (Compiler::Dialogue::opcodeForceGreeting, new OpForceGreeting); + interpreter.installSegment5 (Compiler::Dialogue::opcodeForceGreetingExplicit, new OpForceGreeting); + interpreter.installSegment5 (Compiler::Dialogue::opcodeGoodbye, new OpGoodbye); + interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputation, new OpGetReputation); + interpreter.installSegment5 (Compiler::Dialogue::opcodeSetReputation, new OpSetReputation); + interpreter.installSegment5 (Compiler::Dialogue::opcodeModReputation, new OpModReputation); + interpreter.installSegment5 (Compiler::Dialogue::opcodeSetReputationExplicit, new OpSetReputation); + interpreter.installSegment5 (Compiler::Dialogue::opcodeModReputationExplicit, new OpModReputation); + interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputationExplicit, new OpGetReputation); + interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction); + interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction); } } diff --git a/apps/openmw/mwscript/dialogueextensions.hpp b/apps/openmw/mwscript/dialogueextensions.hpp index ece7d62ce..7b03154df 100644 --- a/apps/openmw/mwscript/dialogueextensions.hpp +++ b/apps/openmw/mwscript/dialogueextensions.hpp @@ -16,8 +16,6 @@ namespace MWScript /// \brief Dialogue/Journal-related script functionality namespace Dialogue { - void registerExtensions (Compiler::Extensions& extensions); - void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 283f149de..ff3b60ca6 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -51,7 +51,8 @@ op 0x20022: AiFollow op 0x20023: AiFollow, explicit reference op 0x20024: AiFollowCell op 0x20025: AiFollowCell, explicit reference -op s 0x20026-0x3ffff unused +op 0x20026: ModRegion +opcodes 0x20027-0x3ffff unused Segment 4: (not implemented yet) @@ -315,5 +316,41 @@ 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 +op 0x2000213: HitOnMe +op 0x2000214: HitOnMe, explicit +op 0x2000215: DisableTeleporting +op 0x2000216: EnableTeleporting +op 0x2000217: BecomeWerewolf +op 0x2000218: BecomeWerewolfExplicit +op 0x2000219: UndoWerewolf +op 0x200021a: UndoWerewolfExplicit +op 0x200021b: SetWerewolfAcrobatics +op 0x200021c: SetWerewolfAcrobaticsExplicit +op 0x200021d: ShowVars +op 0x200021e: ShowVarsExplicit +op 0x200021f: ToggleGodMode -opcodes 0x20001fa-0x3ffffff unused +opcodes 0x2000220-0x3ffffff unused diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp index 1b1560820..2170ba4fb 100644 --- a/apps/openmw/mwscript/extensions.cpp +++ b/apps/openmw/mwscript/extensions.cpp @@ -21,28 +21,6 @@ namespace MWScript { - void registerExtensions (Compiler::Extensions& extensions, bool consoleOnly) - { - Cell::registerExtensions (extensions); - Misc::registerExtensions (extensions); - Gui::registerExtensions (extensions); - Sound::registerExtensions (extensions); - Sky::registerExtensions (extensions); - Stats::registerExtensions (extensions); - Container::registerExtensions (extensions); - Ai::registerExtensions (extensions); - Control::registerExtensions (extensions); - Dialogue::registerExtensions (extensions); - Animation::registerExtensions (extensions); - Transformation::registerExtensions (extensions); - - if (consoleOnly) - { - Console::registerExtensions (extensions); - User::registerExtensions (extensions); - } - } - void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly) { Interpreter::installOpcodes (interpreter); diff --git a/apps/openmw/mwscript/extensions.hpp b/apps/openmw/mwscript/extensions.hpp index cb1aaf9db..67f6de5c5 100644 --- a/apps/openmw/mwscript/extensions.hpp +++ b/apps/openmw/mwscript/extensions.hpp @@ -13,9 +13,6 @@ namespace Interpreter namespace MWScript { - void registerExtensions (Compiler::Extensions& extensions, bool consoleOnly = false); - ///< \param consoleOnly include console only extensions - void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly = false); ///< \param consoleOnly include console only opcodes } 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/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index bfe69c79e..6c89a0d1c 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -66,11 +67,6 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - InterpreterContext& context = - static_cast (runtime.getContext()); - - MWWorld::Ptr ptr = context.getReference(); - runtime.push (MWBase::Environment::get().getWindowManager()->readPressedButton()); } }; @@ -150,90 +146,42 @@ namespace MWScript }; - const int opcodeEnableBirthMenu = 0x200000e; - const int opcodeEnableClassMenu = 0x200000f; - const int opcodeEnableNameMenu = 0x2000010; - const int opcodeEnableRaceMenu = 0x2000011; - const int opcodeEnableStatsReviewMenu = 0x2000012; - const int opcodeEnableInventoryMenu = 0x2000013; - const int opcodeEnableMagicMenu = 0x2000014; - const int opcodeEnableMapMenu = 0x2000015; - const int opcodeEnableStatsMenu = 0x2000016; - const int opcodeEnableRest = 0x2000017; - const int opcodeShowRestMenu = 0x2000018; - const int opcodeGetButtonPressed = 0x2000137; - const int opcodeToggleFogOfWar = 0x2000145; - const int opcodeToggleFullHelp = 0x2000151; - const int opcodeShowMap = 0x20001a0; - const int opcodeFillMap = 0x20001a1; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerInstruction ("enablebirthmenu", "", opcodeEnableBirthMenu); - extensions.registerInstruction ("enableclassmenu", "", opcodeEnableClassMenu); - extensions.registerInstruction ("enablenamemenu", "", opcodeEnableNameMenu); - extensions.registerInstruction ("enableracemenu", "", opcodeEnableRaceMenu); - extensions.registerInstruction ("enablestatreviewmenu", "", -opcodeEnableStatsReviewMenu); - - extensions.registerInstruction ("enableinventorymenu", "", opcodeEnableInventoryMenu); - extensions.registerInstruction ("enablemagicmenu", "", opcodeEnableMagicMenu); - extensions.registerInstruction ("enablemapmenu", "", opcodeEnableMapMenu); - extensions.registerInstruction ("enablestatsmenu", "", opcodeEnableStatsMenu); - - extensions.registerInstruction ("enablerest", "", opcodeEnableRest); - extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest); - - extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu); - - extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed); - - extensions.registerInstruction ("togglefogofwar", "", opcodeToggleFogOfWar); - extensions.registerInstruction ("tfow", "", opcodeToggleFogOfWar); - - extensions.registerInstruction ("togglefullhelp", "", opcodeToggleFullHelp); - extensions.registerInstruction ("tfh", "", opcodeToggleFullHelp); - - extensions.registerInstruction ("showmap", "S", opcodeShowMap); - extensions.registerInstruction ("fillmap", "", opcodeFillMap); - } - void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeEnableBirthMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableBirthMenu, new OpShowDialogue (MWGui::GM_Birth)); - interpreter.installSegment5 (opcodeEnableClassMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableClassMenu, new OpShowDialogue (MWGui::GM_Class)); - interpreter.installSegment5 (opcodeEnableNameMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableNameMenu, new OpShowDialogue (MWGui::GM_Name)); - interpreter.installSegment5 (opcodeEnableRaceMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableRaceMenu, new OpShowDialogue (MWGui::GM_Race)); - interpreter.installSegment5 (opcodeEnableStatsReviewMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableStatsReviewMenu, new OpShowDialogue (MWGui::GM_Review)); - interpreter.installSegment5 (opcodeEnableInventoryMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableInventoryMenu, new OpEnableWindow (MWGui::GW_Inventory)); - interpreter.installSegment5 (opcodeEnableMagicMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableMagicMenu, new OpEnableWindow (MWGui::GW_Magic)); - interpreter.installSegment5 (opcodeEnableMapMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableMapMenu, new OpEnableWindow (MWGui::GW_Map)); - interpreter.installSegment5 (opcodeEnableStatsMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableStatsMenu, new OpEnableWindow (MWGui::GW_Stats)); - interpreter.installSegment5 (opcodeEnableRest, + interpreter.installSegment5 (Compiler::Gui::opcodeEnableRest, new OpEnableRest ()); - interpreter.installSegment5 (opcodeShowRestMenu, + interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenu, new OpShowDialogue (MWGui::GM_RestBed)); - interpreter.installSegment5 (opcodeGetButtonPressed, new OpGetButtonPressed); + interpreter.installSegment5 (Compiler::Gui::opcodeGetButtonPressed, new OpGetButtonPressed); - interpreter.installSegment5 (opcodeToggleFogOfWar, new OpToggleFogOfWar); + interpreter.installSegment5 (Compiler::Gui::opcodeToggleFogOfWar, new OpToggleFogOfWar); - interpreter.installSegment5 (opcodeToggleFullHelp, new OpToggleFullHelp); + interpreter.installSegment5 (Compiler::Gui::opcodeToggleFullHelp, new OpToggleFullHelp); - interpreter.installSegment5 (opcodeShowMap, new OpShowMap); - interpreter.installSegment5 (opcodeFillMap, new OpFillMap); + interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap); + interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap); } } } diff --git a/apps/openmw/mwscript/guiextensions.hpp b/apps/openmw/mwscript/guiextensions.hpp index 83447e8fb..ec775a51c 100644 --- a/apps/openmw/mwscript/guiextensions.hpp +++ b/apps/openmw/mwscript/guiextensions.hpp @@ -15,9 +15,7 @@ namespace MWScript { /// \brief GUI-related script functionality namespace Gui - { - void registerExtensions (Compiler::Extensions& extensions); - + { void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index c74e3f163..b8fc9ed47 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.empty()) + { + 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 index 53f744323..180a2791b 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -1,8 +1,11 @@ #include "locals.hpp" +#include + +#include + #include "../mwbase/environment.hpp" #include "../mwbase/scriptmanager.hpp" -#include namespace MWScript { @@ -15,9 +18,33 @@ namespace MWScript 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); @@ -27,10 +54,10 @@ namespace MWScript { case 's': mShorts.at (index) = val; break; - + case 'l': mLongs.at (index) = val; break; - + case 'f': mFloats.at (index) = val; break; } diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp index e933c727f..deae0d44e 100644 --- a/apps/openmw/mwscript/locals.hpp +++ b/apps/openmw/mwscript/locals.hpp @@ -3,9 +3,13 @@ #include -#include #include +namespace ESM +{ + struct Script; +} + namespace MWScript { class Locals @@ -14,10 +18,11 @@ namespace MWScript 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..a8d8a5f2b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1,9 +1,13 @@ #include "miscextensions.hpp" +#include + #include #include +#include +#include #include #include @@ -11,6 +15,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -97,8 +102,6 @@ namespace MWScript InterpreterContext& context = static_cast (runtime.getContext()); - MWWorld::Ptr ptr = context.getReference(); - context.executeActivation(); } }; @@ -282,10 +285,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"); @@ -316,10 +317,15 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - int key = runtime[0].mInteger; + std::string effect = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); - runtime.push (MWWorld::Class::get(ptr).getCreatureStats (ptr).getMagicEffects ().get ( + char *end; + long key = strtol(effect.c_str(), &end, 10); + if(key < 0 || key > 32767 || *end != '\0') + key = ESM::MagicEffect::effectStringToId(effect); + + runtime.push(MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get( MWMechanics::EffectKey(key)).mMagnitude > 0); } }; @@ -348,7 +354,7 @@ namespace MWScript ref.getPtr().getCellRef().mSoul = creature; - MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr()); + MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr(), ptr); } }; @@ -399,6 +405,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,134 +563,232 @@ namespace MWScript } }; - const int opcodeXBox = 0x200000c; - const int opcodeOnActivate = 0x200000d; - const int opcodeActivate = 0x2000075; - const int opcodeLock = 0x20004; - const int opcodeLockExplicit = 0x20005; - const int opcodeUnlock = 0x200008c; - const int opcodeUnlockExplicit = 0x200008d; - const int opcodeToggleCollisionDebug = 0x2000132; - const int opcodeToggleCollisionBoxes = 0x20001ac; - const int opcodeToggleWireframe = 0x200013b; - const int opcodeFadeIn = 0x200013c; - const int opcodeFadeOut = 0x200013d; - const int opcodeFadeTo = 0x200013e; - const int opcodeToggleWater = 0x2000144; - const int opcodeTogglePathgrid = 0x2000146; - const int opcodeDontSaveObject = 0x2000153; - const int opcodeToggleVanityMode = 0x2000174; - const int opcodeGetPcSleep = 0x200019f; - const int opcodeWakeUpPc = 0x20001a2; - const int opcodeGetLocked = 0x20001c7; - const int opcodeGetLockedExplicit = 0x20001c8; - const int opcodeGetEffect = 0x20001cf; - const int opcodeGetEffectExplicit = 0x20001d0; - const int opcodeAddSoulGem = 0x20001f3; - const int opcodeAddSoulGemExplicit = 0x20001f4; - const int opcodeRemoveSoulGem = 0x20001f5; - const int opcodeRemoveSoulGemExplicit = 0x20001f6; - const int opcodeDrop = 0x20001f8; - const int opcodeDropExplicit = 0x20001f9; - const int opcodeDropSoulGem = 0x20001fa; - const int opcodeDropSoulGemExplicit = 0x20001fb; - const int opcodeGetAttacked = 0x20001d3; - const int opcodeGetAttackedExplicit = 0x20001d4; - const int opcodeGetWeaponDrawn = 0x20001d7; - const int opcodeGetWeaponDrawnExplicit = 0x20001d8; - const int opcodeGetSpellEffects = 0x20001db; - const int opcodeGetSpellEffectsExplicit = 0x20001dc; - const int opcodeGetCurrentTime = 0x20001dd; - const int opcodeSetDelete = 0x20001e5; - const int opcodeSetDeleteExplicit = 0x20001e6; - const int opcodeGetSquareRoot = 0x20001e7; - - const int opcodePlayBink = 0x20001f7; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerFunction ("xbox", 'l', "", opcodeXBox); - extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate); - extensions.registerInstruction ("activate", "", opcodeActivate); - extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit); - extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit); - extensions.registerInstruction ("togglecollisionboxes", "", opcodeToggleCollisionBoxes); - extensions.registerInstruction ("togglecollisiongrid", "", opcodeToggleCollisionDebug); - extensions.registerInstruction ("tcb", "", opcodeToggleCollisionBoxes); - extensions.registerInstruction ("tcg", "", opcodeToggleCollisionDebug); - extensions.registerInstruction ("twf", "", opcodeToggleWireframe); - extensions.registerInstruction ("togglewireframe", "", opcodeToggleWireframe); - extensions.registerInstruction ("fadein", "f", opcodeFadeIn); - extensions.registerInstruction ("fadeout", "f", opcodeFadeOut); - extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo); - extensions.registerInstruction ("togglewater", "", opcodeToggleWater); - extensions.registerInstruction ("twa", "", opcodeToggleWater); - extensions.registerInstruction ("togglepathgrid", "", opcodeTogglePathgrid); - extensions.registerInstruction ("tpg", "", opcodeTogglePathgrid); - extensions.registerInstruction ("dontsaveobject", "", opcodeDontSaveObject); - extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); - extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); - extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); - extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); - extensions.registerInstruction ("playbink", "Sl", opcodePlayBink); - extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); - extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); - extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); - extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); - extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit); - extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); - extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); - extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); - extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); - extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); - extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); - extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot); - } + 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()); + } + }; + + template + class OpHitOnMe : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string objectID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWMechanics::CreatureStats &stats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitObject())); + } + }; + + template + class OpEnableTeleporting : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->enableTeleporting(Enable); + } + }; + + + template + class OpShowVars : public Interpreter::Opcode0 + { + void printLocalVars(Interpreter::Runtime &runtime, const MWWorld::Ptr &ptr) + { + std::stringstream str; + + const std::string script = MWWorld::Class::get(ptr).getScript(ptr); + if(script.empty()) + str<< ptr.getCellRef().mRefID<<" ("<getLocals(script); + + const std::vector *names = &complocals.get('s'); + for(size_t i = 0;i < names->size();++i) + { + if(i >= locals.mShorts.size()) + break; + str<size();++i) + { + if(i >= locals.mLongs.size()) + break; + str<size();++i) + { + if(i >= locals.mFloats.size()) + break; + str< names = world->getGlobals(); + for(size_t i = 0;i < names.size();++i) + { + char type = world->getGlobalVariableType(names[i]); + if(type == 's') + str< (runtime.getContext()); + + bool enabled = MWBase::Environment::get().getWorld()->toggleGodMode(); + + context.report (enabled ? "God Mode -> On" : "God Mode -> Off"); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeXBox, new OpXBox); - interpreter.installSegment5 (opcodeOnActivate, new OpOnActivate); - interpreter.installSegment5 (opcodeActivate, new OpActivate); - interpreter.installSegment3 (opcodeLock, new OpLock); - interpreter.installSegment3 (opcodeLockExplicit, new OpLock); - interpreter.installSegment5 (opcodeUnlock, new OpUnlock); - interpreter.installSegment5 (opcodeUnlockExplicit, new OpUnlock); - interpreter.installSegment5 (opcodeToggleCollisionDebug, new OpToggleCollisionDebug); - interpreter.installSegment5 (opcodeToggleCollisionBoxes, new OpToggleCollisionBoxes); - interpreter.installSegment5 (opcodeToggleWireframe, new OpToggleWireframe); - interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn); - interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut); - interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo); - interpreter.installSegment5 (opcodeTogglePathgrid, new OpTogglePathgrid); - interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater); - interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject); - interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); - interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep); - interpreter.installSegment5 (opcodeWakeUpPc, new OpWakeUpPc); - interpreter.installSegment5 (opcodePlayBink, new OpPlayBink); - interpreter.installSegment5 (opcodeGetLocked, new OpGetLocked); - interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked); - interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect); - interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); - interpreter.installSegment5 (opcodeAddSoulGem, new OpAddSoulGem); - interpreter.installSegment5 (opcodeAddSoulGemExplicit, new OpAddSoulGem); - interpreter.installSegment5 (opcodeRemoveSoulGem, new OpRemoveSoulGem); - interpreter.installSegment5 (opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); - interpreter.installSegment5 (opcodeDrop, new OpDrop); - interpreter.installSegment5 (opcodeDropExplicit, new OpDrop); - interpreter.installSegment5 (opcodeDropSoulGem, new OpDropSoulGem); - interpreter.installSegment5 (opcodeDropSoulGemExplicit, new OpDropSoulGem); - interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); - interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); - interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn); - interpreter.installSegment5 (opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); - interpreter.installSegment5 (opcodeGetSpellEffects, new OpGetSpellEffects); - interpreter.installSegment5 (opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); - interpreter.installSegment5 (opcodeGetCurrentTime, new OpGetCurrentTime); - interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete); - interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete); - interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot); + interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); + interpreter.installSegment5 (Compiler::Misc::opcodeOnActivate, new OpOnActivate); + interpreter.installSegment5 (Compiler::Misc::opcodeActivate, new OpActivate); + interpreter.installSegment3 (Compiler::Misc::opcodeLock, new OpLock); + interpreter.installSegment3 (Compiler::Misc::opcodeLockExplicit, new OpLock); + interpreter.installSegment5 (Compiler::Misc::opcodeUnlock, new OpUnlock); + interpreter.installSegment5 (Compiler::Misc::opcodeUnlockExplicit, new OpUnlock); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleCollisionDebug, new OpToggleCollisionDebug); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleCollisionBoxes, new OpToggleCollisionBoxes); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleWireframe, new OpToggleWireframe); + interpreter.installSegment5 (Compiler::Misc::opcodeFadeIn, new OpFadeIn); + interpreter.installSegment5 (Compiler::Misc::opcodeFadeOut, new OpFadeOut); + interpreter.installSegment5 (Compiler::Misc::opcodeFadeTo, new OpFadeTo); + interpreter.installSegment5 (Compiler::Misc::opcodeTogglePathgrid, new OpTogglePathgrid); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleWater, new OpToggleWater); + interpreter.installSegment5 (Compiler::Misc::opcodeDontSaveObject, new OpDontSaveObject); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleVanityMode, new OpToggleVanityMode); + interpreter.installSegment5 (Compiler::Misc::opcodeGetPcSleep, new OpGetPcSleep); + interpreter.installSegment5 (Compiler::Misc::opcodeWakeUpPc, new OpWakeUpPc); + interpreter.installSegment5 (Compiler::Misc::opcodePlayBink, new OpPlayBink); + interpreter.installSegment5 (Compiler::Misc::opcodeGetLocked, new OpGetLocked); + interpreter.installSegment5 (Compiler::Misc::opcodeGetLockedExplicit, new OpGetLocked); + interpreter.installSegment5 (Compiler::Misc::opcodeGetEffect, new OpGetEffect); + interpreter.installSegment5 (Compiler::Misc::opcodeGetEffectExplicit, new OpGetEffect); + interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem); + interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem); + interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem); + interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); + interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop); + interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop); + interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem); + interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGemExplicit, new OpDropSoulGem); + interpreter.installSegment5 (Compiler::Misc::opcodeGetAttacked, new OpGetAttacked); + interpreter.installSegment5 (Compiler::Misc::opcodeGetAttackedExplicit, new OpGetAttacked); + interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawn, new OpGetWeaponDrawn); + interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffects, new OpGetSpellEffects); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); + interpreter.installSegment5 (Compiler::Misc::opcodeGetCurrentTime, new OpGetCurrentTime); + interpreter.installSegment5 (Compiler::Misc::opcodeSetDelete, new OpSetDelete); + interpreter.installSegment5 (Compiler::Misc::opcodeSetDeleteExplicit, new OpSetDelete); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSquareRoot, new OpGetSquareRoot); + interpreter.installSegment5 (Compiler::Misc::opcodeFall, new OpFall); + interpreter.installSegment5 (Compiler::Misc::opcodeFallExplicit, new OpFall); + interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPc, new OpGetStandingPc); + interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPcExplicit, new OpGetStandingPc); + interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActor, new OpGetStandingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActorExplicit, new OpGetStandingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeGetWindSpeed, new OpGetWindSpeed); + interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMe, new OpHitOnMe); + interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMeExplicit, new OpHitOnMe); + interpreter.installSegment5 (Compiler::Misc::opcodeDisableTeleporting, new OpEnableTeleporting); + interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting); + interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars); + interpreter.installSegment5 (Compiler::Misc::opcodeShowVarsExplicit, new OpShowVars); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode); } } } diff --git a/apps/openmw/mwscript/miscextensions.hpp b/apps/openmw/mwscript/miscextensions.hpp index 69818f466..16ed9301e 100644 --- a/apps/openmw/mwscript/miscextensions.hpp +++ b/apps/openmw/mwscript/miscextensions.hpp @@ -14,9 +14,7 @@ namespace Interpreter namespace MWScript { namespace Misc - { - void registerExtensions (Compiler::Extensions& extensions); - + { void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index fed5877c4..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; @@ -209,6 +209,7 @@ namespace MWScript offset = script->mData.mNumShorts+script->mData.mNumLongs; size = script->mData.mNumFloats; + break; default: @@ -221,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 1a856e0c5..7bb98ffbd 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -61,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/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index e16c2ec23..8b9efd74e 100644 --- a/apps/openmw/mwscript/skyextensions.cpp +++ b/apps/openmw/mwscript/skyextensions.cpp @@ -2,6 +2,7 @@ #include "skyextensions.hpp" #include +#include #include #include @@ -96,35 +97,39 @@ namespace MWScript } }; - const int opcodeToggleSky = 0x2000021; - const int opcodeTurnMoonWhite = 0x2000022; - const int opcodeTurnMoonRed = 0x2000023; - const int opcodeGetMasserPhase = 0x2000024; - const int opcodeGetSecundaPhase = 0x2000025; - const int opcodeGetCurrentWeather = 0x200013f; - const int opcodeChangeWeather = 0x2000140; - - void registerExtensions (Compiler::Extensions& extensions) + class OpModRegion : public Interpreter::Opcode1 { - extensions.registerInstruction ("togglesky", "", opcodeToggleSky); - extensions.registerInstruction ("ts", "", opcodeToggleSky); - extensions.registerInstruction ("turnmoonwhite", "", opcodeTurnMoonWhite); - extensions.registerInstruction ("turnmoonred", "", opcodeTurnMoonRed); - extensions.registerInstruction ("changeweather", "Sl", opcodeChangeWeather); - extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase); - extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase); - extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather); - } + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + std::string region = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::vector chances; + chances.reserve(10); + while(arg0 > 0) + { + chances.push_back(std::max(0, std::min(127, runtime[0].mInteger))); + runtime.pop(); + arg0--; + } + + MWBase::Environment::get().getWorld()->modRegion(region, chances); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeToggleSky, new OpToggleSky); - interpreter.installSegment5 (opcodeTurnMoonWhite, new OpTurnMoonWhite); - interpreter.installSegment5 (opcodeTurnMoonRed, new OpTurnMoonRed); - interpreter.installSegment5 (opcodeGetMasserPhase, new OpGetMasserPhase); - interpreter.installSegment5 (opcodeGetSecundaPhase, new OpGetSecundaPhase); - interpreter.installSegment5 (opcodeGetCurrentWeather, new OpGetCurrentWeather); - interpreter.installSegment5 (opcodeChangeWeather, new OpChangeWeather); + interpreter.installSegment5 (Compiler::Sky::opcodeToggleSky, new OpToggleSky); + interpreter.installSegment5 (Compiler::Sky::opcodeTurnMoonWhite, new OpTurnMoonWhite); + interpreter.installSegment5 (Compiler::Sky::opcodeTurnMoonRed, new OpTurnMoonRed); + interpreter.installSegment5 (Compiler::Sky::opcodeGetMasserPhase, new OpGetMasserPhase); + interpreter.installSegment5 (Compiler::Sky::opcodeGetSecundaPhase, new OpGetSecundaPhase); + interpreter.installSegment5 (Compiler::Sky::opcodeGetCurrentWeather, new OpGetCurrentWeather); + interpreter.installSegment5 (Compiler::Sky::opcodeChangeWeather, new OpChangeWeather); + interpreter.installSegment3 (Compiler::Sky::opcodeModRegion, new OpModRegion); } } } diff --git a/apps/openmw/mwscript/skyextensions.hpp b/apps/openmw/mwscript/skyextensions.hpp index b9bffb27e..003f2fb19 100644 --- a/apps/openmw/mwscript/skyextensions.hpp +++ b/apps/openmw/mwscript/skyextensions.hpp @@ -15,9 +15,7 @@ namespace MWScript { /// \brief sky-related script functionality namespace Sky - { - void registerExtensions (Compiler::Extensions& extensions); - + { void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 408a6f29d..73c3ec93a 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -2,6 +2,7 @@ #include "extensions.hpp" #include +#include #include #include @@ -118,8 +119,10 @@ namespace MWScript std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : - MWBase::SoundManager::Play_Normal); + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, 1.0, 1.0, + MWBase::SoundManager::Play_TypeSfx, + mLoop ? MWBase::SoundManager::Play_Loop : + MWBase::SoundManager::Play_Normal); } }; @@ -145,8 +148,10 @@ namespace MWScript Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : - MWBase::SoundManager::Play_Normal); + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, volume, pitch, + MWBase::SoundManager::Play_TypeSfx, + mLoop ? MWBase::SoundManager::Play_Loop : + MWBase::SoundManager::Play_Normal); } }; @@ -184,75 +189,34 @@ namespace MWScript } }; - const int opcodeSay = 0x2000001; - const int opcodeSayDone = 0x2000002; - const int opcodeStreamMusic = 0x2000003; - const int opcodePlaySound = 0x2000004; - const int opcodePlaySoundVP = 0x2000005; - const int opcodePlaySound3D = 0x2000006; - const int opcodePlaySound3DVP = 0x2000007; - const int opcodePlayLoopSound3D = 0x2000008; - const int opcodePlayLoopSound3DVP = 0x2000009; - const int opcodeStopSound = 0x200000a; - const int opcodeGetSoundPlaying = 0x200000b; - - const int opcodeSayExplicit = 0x2000019; - const int opcodeSayDoneExplicit = 0x200001a; - const int opcodePlaySound3DExplicit = 0x200001b; - const int opcodePlaySound3DVPExplicit = 0x200001c; - const int opcodePlayLoopSound3DExplicit = 0x200001d; - const int opcodePlayLoopSound3DVPExplicit = 0x200001e; - const int opcodeStopSoundExplicit = 0x200001f; - const int opcodeGetSoundPlayingExplicit = 0x2000020; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerInstruction ("say", "SS", opcodeSay, opcodeSayExplicit); - extensions.registerFunction ("saydone", 'l', "", opcodeSayDone, opcodeSayDoneExplicit); - extensions.registerInstruction ("streammusic", "S", opcodeStreamMusic); - extensions.registerInstruction ("playsound", "c", opcodePlaySound); - extensions.registerInstruction ("playsoundvp", "cff", opcodePlaySoundVP); - extensions.registerInstruction ("playsound3d", "c", opcodePlaySound3D, - opcodePlaySound3DExplicit); - extensions.registerInstruction ("playsound3dvp", "cff", opcodePlaySound3DVP, - opcodePlaySound3DVPExplicit); - extensions.registerInstruction ("playloopsound3d", "c", opcodePlayLoopSound3D, - opcodePlayLoopSound3DExplicit); - extensions.registerInstruction ("playloopsound3dvp", "cff", opcodePlayLoopSound3DVP, - opcodePlayLoopSound3DVPExplicit); - extensions.registerInstruction ("stopsound", "c", opcodeStopSound, - opcodeStopSoundExplicit); - extensions.registerFunction ("getsoundplaying", 'l', "c", opcodeGetSoundPlaying, - opcodeGetSoundPlayingExplicit); - } void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeSay, new OpSay); - interpreter.installSegment5 (opcodeSayDone, new OpSayDone); - interpreter.installSegment5 (opcodeStreamMusic, new OpStreamMusic); - interpreter.installSegment5 (opcodePlaySound, new OpPlaySound); - interpreter.installSegment5 (opcodePlaySoundVP, new OpPlaySoundVP); - interpreter.installSegment5 (opcodePlaySound3D, new OpPlaySound3D (false)); - interpreter.installSegment5 (opcodePlaySound3DVP, new OpPlaySoundVP3D (false)); - interpreter.installSegment5 (opcodePlayLoopSound3D, new OpPlaySound3D (true)); - interpreter.installSegment5 (opcodePlayLoopSound3DVP, + interpreter.installSegment5 (Compiler::Sound::opcodeSay, new OpSay); + interpreter.installSegment5 (Compiler::Sound::opcodeSayDone, new OpSayDone); + interpreter.installSegment5 (Compiler::Sound::opcodeStreamMusic, new OpStreamMusic); + interpreter.installSegment5 (Compiler::Sound::opcodePlaySound, new OpPlaySound); + interpreter.installSegment5 (Compiler::Sound::opcodePlaySoundVP, new OpPlaySoundVP); + interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3D, new OpPlaySound3D (false)); + interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DVP, new OpPlaySoundVP3D (false)); + interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3D, new OpPlaySound3D (true)); + interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DVP, new OpPlaySoundVP3D (true)); - interpreter.installSegment5 (opcodeStopSound, new OpStopSound); - interpreter.installSegment5 (opcodeGetSoundPlaying, new OpGetSoundPlaying); + interpreter.installSegment5 (Compiler::Sound::opcodeStopSound, new OpStopSound); + interpreter.installSegment5 (Compiler::Sound::opcodeGetSoundPlaying, new OpGetSoundPlaying); - interpreter.installSegment5 (opcodeSayExplicit, new OpSay); - interpreter.installSegment5 (opcodeSayDoneExplicit, new OpSayDone); - interpreter.installSegment5 (opcodePlaySound3DExplicit, + interpreter.installSegment5 (Compiler::Sound::opcodeSayExplicit, new OpSay); + interpreter.installSegment5 (Compiler::Sound::opcodeSayDoneExplicit, new OpSayDone); + interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DExplicit, new OpPlaySound3D (false)); - interpreter.installSegment5 (opcodePlaySound3DVPExplicit, + interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DVPExplicit, new OpPlaySoundVP3D (false)); - interpreter.installSegment5 (opcodePlayLoopSound3DExplicit, + interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DExplicit, new OpPlaySound3D (true)); - interpreter.installSegment5 (opcodePlayLoopSound3DVPExplicit, + interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DVPExplicit, new OpPlaySoundVP3D (true)); - interpreter.installSegment5 (opcodeStopSoundExplicit, new OpStopSound); - interpreter.installSegment5 (opcodeGetSoundPlayingExplicit, + interpreter.installSegment5 (Compiler::Sound::opcodeStopSoundExplicit, new OpStopSound); + interpreter.installSegment5 (Compiler::Sound::opcodeGetSoundPlayingExplicit, new OpGetSoundPlaying); } } diff --git a/apps/openmw/mwscript/soundextensions.hpp b/apps/openmw/mwscript/soundextensions.hpp index 85416a6df..b92d7ea1b 100644 --- a/apps/openmw/mwscript/soundextensions.hpp +++ b/apps/openmw/mwscript/soundextensions.hpp @@ -15,10 +15,7 @@ namespace MWScript { namespace Sound { - // Script-extensions related to sound - - void registerExtensions (Compiler::Extensions& extensions); - + // Script-extensions related to sound void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 530d44c9d..603515ff4 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -10,6 +10,7 @@ #include "../mwworld/esmstore.hpp" #include +#include #include #include @@ -34,7 +35,7 @@ namespace { MWWorld::Ptr actor = MWBase::Environment::get().getDialogueManager()->getActor(); - MWMechanics::NpcStats stats = MWWorld::Class::get (actor).getNpcStats (actor); + const MWMechanics::NpcStats &stats = MWWorld::Class::get (actor).getNpcStats (actor); if (stats.getFactionRanks().empty()) throw std::runtime_error ( @@ -1025,322 +1026,187 @@ namespace MWScript } }; - const int numberOfAttributes = 8; - - const int opcodeGetAttribute = 0x2000027; - const int opcodeGetAttributeExplicit = 0x200002f; - const int opcodeSetAttribute = 0x2000037; - const int opcodeSetAttributeExplicit = 0x200003f; - const int opcodeModAttribute = 0x2000047; - const int opcodeModAttributeExplicit = 0x200004f; - - const int numberOfDynamics = 3; - - const int opcodeGetDynamic = 0x2000057; - const int opcodeGetDynamicExplicit = 0x200005a; - const int opcodeSetDynamic = 0x200005d; - const int opcodeSetDynamicExplicit = 0x2000060; - const int opcodeModDynamic = 0x2000063; - const int opcodeModDynamicExplicit = 0x2000066; - const int opcodeModCurrentDynamic = 0x2000069; - const int opcodeModCurrentDynamicExplicit = 0x200006c; - const int opcodeGetDynamicGetRatio = 0x200006f; - const int opcodeGetDynamicGetRatioExplicit = 0x2000072; - - const int numberOfSkills = 27; - - const int opcodeGetSkill = 0x200008e; - const int opcodeGetSkillExplicit = 0x20000a9; - const int opcodeSetSkill = 0x20000c4; - const int opcodeSetSkillExplicit = 0x20000df; - const int opcodeModSkill = 0x20000fa; - const int opcodeModSkillExplicit = 0x2000115; - - const int opcodeGetPCCrimeLevel = 0x20001ec; - const int opcodeSetPCCrimeLevel = 0x20001ed; - const int opcodeModPCCrimeLevel = 0x20001ee; - - const int opcodeAddSpell = 0x2000147; - const int opcodeAddSpellExplicit = 0x2000148; - const int opcodeRemoveSpell = 0x2000149; - const int opcodeRemoveSpellExplicit = 0x200014a; - const int opcodeGetSpell = 0x200014b; - const int opcodeGetSpellExplicit = 0x200014c; - - const int opcodePCRaiseRank = 0x2000b; - const int opcodePCLowerRank = 0x2000c; - const int opcodePCJoinFaction = 0x2000d; - const int opcodeGetPCRank = 0x2000e; - const int opcodeGetPCRankExplicit = 0x2000f; - const int opcodeModDisposition = 0x200014d; - const int opcodeModDispositionExplicit = 0x200014e; - const int opcodeSetDisposition = 0x20001a4; - const int opcodeSetDispositionExplicit = 0x20001a5; - const int opcodeGetDisposition = 0x20001a6; - const int opcodeGetDispositionExplicit = 0x20001a7; - - const int opcodeGetLevel = 0x200018c; - const int opcodeGetLevelExplicit = 0x200018d; - const int opcodeSetLevel = 0x200018e; - const int opcodeSetLevelExplicit = 0x200018f; - - const int opcodeGetDeadCount = 0x20001a3; - - const int opcodeGetPCFacRep = 0x20012; - const int opcodeGetPCFacRepExplicit = 0x20013; - const int opcodeSetPCFacRep = 0x20014; - const int opcodeSetPCFacRepExplicit = 0x20015; - const int opcodeModPCFacRep = 0x20016; - const int opcodeModPCFacRepExplicit = 0x20017; - - const int opcodeGetCommonDisease = 0x20001a8; - const int opcodeGetCommonDiseaseExplicit = 0x20001a9; - const int opcodeGetBlightDisease = 0x20001aa; - const int opcodeGetBlightDiseaseExplicit = 0x20001ab; - - const int opcodeGetRace = 0x20001d9; - const int opcodeGetRaceExplicit = 0x20001da; - - const int opcodeGetWerewolfKills = 0x20001e2; - - const int opcodePcExpelled = 0x20018; - const int opcodePcExpelledExplicit = 0x20019; - const int opcodePcExpell = 0x2001a; - const int opcodePcExpellExplicit = 0x2001b; - const int opcodePcClearExpelled = 0x2001c; - const int opcodePcClearExpelledExplicit = 0x2001d; - const int opcodeRaiseRank = 0x20001e8; - const int opcodeRaiseRankExplicit = 0x20001e9; - const int opcodeLowerRank = 0x20001ea; - const int opcodeLowerRankExplicit = 0x20001eb; - - void registerExtensions (Compiler::Extensions& extensions) + template + class OpOnDeath : public Interpreter::Opcode0 { - static const char *attributes[numberOfAttributes] = - { - "strength", "intelligence", "willpower", "agility", "speed", "endurance", - "personality", "luck" - }; - - static const char *dynamics[numberOfDynamics] = - { - "health", "magicka", "fatigue" - }; - - static const char *skills[numberOfSkills] = - { - "block", "armorer", "mediumarmor", "heavyarmor", "bluntweapon", - "longblade", "axe", "spear", "athletics", "enchant", "destruction", - "alteration", "illusion", "conjuration", "mysticism", - "restoration", "alchemy", "unarmored", "security", "sneak", - "acrobatics", "lightarmor", "shortblade", "marksman", - "mercantile", "speechcraft", "handtohand" - }; - - std::string get ("get"); - std::string set ("set"); - std::string mod ("mod"); - std::string modCurrent ("modcurrent"); - std::string getRatio ("getratio"); - - for (int i=0; i + class OpIsWerewolf : public Interpreter::Opcode0 + { + public: - extensions.registerFunction (get + dynamics[i] + getRatio, 'f', "", - opcodeGetDynamicGetRatio+i, opcodeGetDynamicGetRatioExplicit+i); - } + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push(MWWorld::Class::get(ptr).getNpcStats(ptr).isWerewolf()); + } + }; - for (int i=0; i + class OpSetWerewolf : public Interpreter::Opcode0 + { + public: - extensions.registerInstruction (set + skills[i], "l", - opcodeSetSkill+i, opcodeSetSkillExplicit+i); + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + MWBase::Environment::get().getWorld()->setWerewolf(ptr, set); + } + }; - extensions.registerInstruction (mod + skills[i], "l", - opcodeModSkill+i, opcodeModSkillExplicit+i); - } + template + class OpSetWerewolfAcrobatics : public Interpreter::Opcode0 + { + public: - extensions.registerFunction ("getpccrimelevel", 'f', "", opcodeGetPCCrimeLevel); - extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); - extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); - - extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); - extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, - opcodeRemoveSpellExplicit); - extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); - - extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); - extensions.registerInstruction("pclowerrank","/S",opcodePCLowerRank); - extensions.registerInstruction("pcjoinfaction","/S",opcodePCJoinFaction); - extensions.registerInstruction ("moddisposition","l",opcodeModDisposition, - opcodeModDispositionExplicit); - extensions.registerInstruction ("setdisposition","l",opcodeSetDisposition, - opcodeSetDispositionExplicit); - extensions.registerFunction ("getdisposition",'l', "",opcodeGetDisposition, - opcodeGetDispositionExplicit); - extensions.registerFunction("getpcrank",'l',"/S",opcodeGetPCRank,opcodeGetPCRankExplicit); - - extensions.registerInstruction("setlevel", "l", opcodeSetLevel, opcodeSetLevelExplicit); - extensions.registerFunction("getlevel", 'l', "", opcodeGetLevel, opcodeGetLevelExplicit); - - extensions.registerFunction ("getdeadcount", 'l', "c", opcodeGetDeadCount); - - extensions.registerFunction ("getpcfacrep", 'l', "/c", opcodeGetPCFacRep, opcodeGetPCFacRepExplicit); - extensions.registerInstruction ("setpcfacrep", "l/c", opcodeSetPCFacRep, opcodeSetPCFacRepExplicit); - extensions.registerInstruction ("modpcfacrep", "l/c", opcodeModPCFacRep, opcodeModPCFacRepExplicit); - - extensions.registerFunction ("getcommondisease", 'l', "", opcodeGetCommonDisease, - opcodeGetCommonDiseaseExplicit); - extensions.registerFunction ("getblightdisease", 'l', "", opcodeGetBlightDisease, - opcodeGetBlightDiseaseExplicit); - - extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace, - opcodeGetRaceExplicit); - extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills); - extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit); - extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit); - extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit); - extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit); - extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); - } + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + MWBase::Environment::get().getWorld()->applyWerewolfAcrobatics(ptr); + } + }; void installOpcodes (Interpreter::Interpreter& interpreter) { - for (int i=0; i (i)); - interpreter.installSegment5 (opcodeGetAttributeExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeGetAttribute+i, new OpGetAttribute (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeGetAttributeExplicit+i, new OpGetAttribute (i)); - interpreter.installSegment5 (opcodeSetAttribute+i, new OpSetAttribute (i)); - interpreter.installSegment5 (opcodeSetAttributeExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeSetAttribute+i, new OpSetAttribute (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeSetAttributeExplicit+i, new OpSetAttribute (i)); - interpreter.installSegment5 (opcodeModAttribute+i, new OpModAttribute (i)); - interpreter.installSegment5 (opcodeModAttributeExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeModAttribute+i, new OpModAttribute (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeModAttributeExplicit+i, new OpModAttribute (i)); } - for (int i=0; i (i)); - interpreter.installSegment5 (opcodeGetDynamicExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamic+i, new OpGetDynamic (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicExplicit+i, new OpGetDynamic (i)); - interpreter.installSegment5 (opcodeSetDynamic+i, new OpSetDynamic (i)); - interpreter.installSegment5 (opcodeSetDynamicExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeSetDynamic+i, new OpSetDynamic (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeSetDynamicExplicit+i, new OpSetDynamic (i)); - interpreter.installSegment5 (opcodeModDynamic+i, new OpModDynamic (i)); - interpreter.installSegment5 (opcodeModDynamicExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeModDynamic+i, new OpModDynamic (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeModDynamicExplicit+i, new OpModDynamic (i)); - interpreter.installSegment5 (opcodeModCurrentDynamic+i, + interpreter.installSegment5 (Compiler::Stats::opcodeModCurrentDynamic+i, new OpModCurrentDynamic (i)); - interpreter.installSegment5 (opcodeModCurrentDynamicExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeModCurrentDynamicExplicit+i, new OpModCurrentDynamic (i)); - interpreter.installSegment5 (opcodeGetDynamicGetRatio+i, + interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicGetRatio+i, new OpGetDynamicGetRatio (i)); - interpreter.installSegment5 (opcodeGetDynamicGetRatioExplicit+i, + interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicGetRatioExplicit+i, new OpGetDynamicGetRatio (i)); } - for (int i=0; i (i)); - interpreter.installSegment5 (opcodeGetSkillExplicit+i, new OpGetSkill (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeGetSkill+i, new OpGetSkill (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeGetSkillExplicit+i, new OpGetSkill (i)); - interpreter.installSegment5 (opcodeSetSkill+i, new OpSetSkill (i)); - interpreter.installSegment5 (opcodeSetSkillExplicit+i, new OpSetSkill (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeSetSkill+i, new OpSetSkill (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeSetSkillExplicit+i, new OpSetSkill (i)); - interpreter.installSegment5 (opcodeModSkill+i, new OpModSkill (i)); - interpreter.installSegment5 (opcodeModSkillExplicit+i, new OpModSkill (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeModSkill+i, new OpModSkill (i)); + interpreter.installSegment5 (Compiler::Stats::opcodeModSkillExplicit+i, new OpModSkill (i)); } - interpreter.installSegment5 (opcodeGetPCCrimeLevel, new OpGetPCCrimeLevel); - interpreter.installSegment5 (opcodeSetPCCrimeLevel, new OpSetPCCrimeLevel); - interpreter.installSegment5 (opcodeModPCCrimeLevel, new OpModPCCrimeLevel); + interpreter.installSegment5 (Compiler::Stats::opcodeGetPCCrimeLevel, new OpGetPCCrimeLevel); + interpreter.installSegment5 (Compiler::Stats::opcodeSetPCCrimeLevel, new OpSetPCCrimeLevel); + interpreter.installSegment5 (Compiler::Stats::opcodeModPCCrimeLevel, new OpModPCCrimeLevel); - interpreter.installSegment5 (opcodeAddSpell, new OpAddSpell); - interpreter.installSegment5 (opcodeAddSpellExplicit, new OpAddSpell); - interpreter.installSegment5 (opcodeRemoveSpell, new OpRemoveSpell); - interpreter.installSegment5 (opcodeRemoveSpellExplicit, + interpreter.installSegment5 (Compiler::Stats::opcodeAddSpell, new OpAddSpell); + interpreter.installSegment5 (Compiler::Stats::opcodeAddSpellExplicit, new OpAddSpell); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpell, new OpRemoveSpell); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellExplicit, new OpRemoveSpell); - interpreter.installSegment5 (opcodeGetSpell, new OpGetSpell); - interpreter.installSegment5 (opcodeGetSpellExplicit, new OpGetSpell); - - interpreter.installSegment3(opcodePCRaiseRank,new OpPCRaiseRank); - interpreter.installSegment3(opcodePCLowerRank,new OpPCLowerRank); - interpreter.installSegment3(opcodePCJoinFaction,new OpPCJoinFaction); - interpreter.installSegment3(opcodeGetPCRank,new OpGetPCRank); - interpreter.installSegment3(opcodeGetPCRankExplicit,new OpGetPCRank); - - interpreter.installSegment5(opcodeModDisposition,new OpModDisposition); - interpreter.installSegment5(opcodeModDispositionExplicit,new OpModDisposition); - interpreter.installSegment5(opcodeSetDisposition,new OpSetDisposition); - interpreter.installSegment5(opcodeSetDispositionExplicit,new OpSetDisposition); - interpreter.installSegment5(opcodeGetDisposition,new OpGetDisposition); - interpreter.installSegment5(opcodeGetDispositionExplicit,new OpGetDisposition); - - interpreter.installSegment5 (opcodeGetLevel, new OpGetLevel); - interpreter.installSegment5 (opcodeGetLevelExplicit, new OpGetLevel); - interpreter.installSegment5 (opcodeSetLevel, new OpSetLevel); - interpreter.installSegment5 (opcodeSetLevelExplicit, new OpSetLevel); - - interpreter.installSegment5 (opcodeGetDeadCount, new OpGetDeadCount); - - interpreter.installSegment3 (opcodeGetPCFacRep, new OpGetPCFacRep); - interpreter.installSegment3 (opcodeGetPCFacRepExplicit, new OpGetPCFacRep); - interpreter.installSegment3 (opcodeSetPCFacRep, new OpSetPCFacRep); - interpreter.installSegment3 (opcodeSetPCFacRepExplicit, new OpSetPCFacRep); - interpreter.installSegment3 (opcodeModPCFacRep, new OpModPCFacRep); - interpreter.installSegment3 (opcodeModPCFacRepExplicit, new OpModPCFacRep); - - interpreter.installSegment5 (opcodeGetCommonDisease, new OpGetCommonDisease); - interpreter.installSegment5 (opcodeGetCommonDiseaseExplicit, new OpGetCommonDisease); - interpreter.installSegment5 (opcodeGetBlightDisease, new OpGetBlightDisease); - interpreter.installSegment5 (opcodeGetBlightDiseaseExplicit, new OpGetBlightDisease); - - interpreter.installSegment5 (opcodeGetRace, new OpGetRace); - interpreter.installSegment5 (opcodeGetRaceExplicit, new OpGetRace); - interpreter.installSegment5 (opcodeGetWerewolfKills, new OpGetWerewolfKills); - - interpreter.installSegment3 (opcodePcExpelled, new OpPcExpelled); - interpreter.installSegment3 (opcodePcExpelledExplicit, new OpPcExpelled); - interpreter.installSegment3 (opcodePcExpell, new OpPcExpell); - interpreter.installSegment3 (opcodePcExpellExplicit, new OpPcExpell); - interpreter.installSegment3 (opcodePcClearExpelled, new OpPcClearExpelled); - interpreter.installSegment3 (opcodePcClearExpelledExplicit, new OpPcClearExpelled); - interpreter.installSegment5 (opcodeRaiseRank, new OpRaiseRank); - interpreter.installSegment5 (opcodeRaiseRankExplicit, new OpRaiseRank); - interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank); - interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank); + + interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell); + interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell); + + interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRank,new OpPCRaiseRank); + interpreter.installSegment3(Compiler::Stats::opcodePCLowerRank,new OpPCLowerRank); + interpreter.installSegment3(Compiler::Stats::opcodePCJoinFaction,new OpPCJoinFaction); + interpreter.installSegment3(Compiler::Stats::opcodeGetPCRank,new OpGetPCRank); + interpreter.installSegment3(Compiler::Stats::opcodeGetPCRankExplicit,new OpGetPCRank); + + interpreter.installSegment5(Compiler::Stats::opcodeModDisposition,new OpModDisposition); + interpreter.installSegment5(Compiler::Stats::opcodeModDispositionExplicit,new OpModDisposition); + interpreter.installSegment5(Compiler::Stats::opcodeSetDisposition,new OpSetDisposition); + interpreter.installSegment5(Compiler::Stats::opcodeSetDispositionExplicit,new OpSetDisposition); + interpreter.installSegment5(Compiler::Stats::opcodeGetDisposition,new OpGetDisposition); + interpreter.installSegment5(Compiler::Stats::opcodeGetDispositionExplicit,new OpGetDisposition); + + interpreter.installSegment5 (Compiler::Stats::opcodeGetLevel, new OpGetLevel); + interpreter.installSegment5 (Compiler::Stats::opcodeGetLevelExplicit, new OpGetLevel); + interpreter.installSegment5 (Compiler::Stats::opcodeSetLevel, new OpSetLevel); + interpreter.installSegment5 (Compiler::Stats::opcodeSetLevelExplicit, new OpSetLevel); + + interpreter.installSegment5 (Compiler::Stats::opcodeGetDeadCount, new OpGetDeadCount); + + interpreter.installSegment3 (Compiler::Stats::opcodeGetPCFacRep, new OpGetPCFacRep); + interpreter.installSegment3 (Compiler::Stats::opcodeGetPCFacRepExplicit, new OpGetPCFacRep); + interpreter.installSegment3 (Compiler::Stats::opcodeSetPCFacRep, new OpSetPCFacRep); + interpreter.installSegment3 (Compiler::Stats::opcodeSetPCFacRepExplicit, new OpSetPCFacRep); + interpreter.installSegment3 (Compiler::Stats::opcodeModPCFacRep, new OpModPCFacRep); + interpreter.installSegment3 (Compiler::Stats::opcodeModPCFacRepExplicit, new OpModPCFacRep); + + interpreter.installSegment5 (Compiler::Stats::opcodeGetCommonDisease, new OpGetCommonDisease); + interpreter.installSegment5 (Compiler::Stats::opcodeGetCommonDiseaseExplicit, new OpGetCommonDisease); + interpreter.installSegment5 (Compiler::Stats::opcodeGetBlightDisease, new OpGetBlightDisease); + interpreter.installSegment5 (Compiler::Stats::opcodeGetBlightDiseaseExplicit, new OpGetBlightDisease); + + interpreter.installSegment5 (Compiler::Stats::opcodeGetRace, new OpGetRace); + interpreter.installSegment5 (Compiler::Stats::opcodeGetRaceExplicit, new OpGetRace); + interpreter.installSegment5 (Compiler::Stats::opcodeGetWerewolfKills, new OpGetWerewolfKills); + + interpreter.installSegment3 (Compiler::Stats::opcodePcExpelled, new OpPcExpelled); + interpreter.installSegment3 (Compiler::Stats::opcodePcExpelledExplicit, new OpPcExpelled); + interpreter.installSegment3 (Compiler::Stats::opcodePcExpell, new OpPcExpell); + interpreter.installSegment3 (Compiler::Stats::opcodePcExpellExplicit, new OpPcExpell); + interpreter.installSegment3 (Compiler::Stats::opcodePcClearExpelled, new OpPcClearExpelled); + interpreter.installSegment3 (Compiler::Stats::opcodePcClearExpelledExplicit, new OpPcClearExpelled); + interpreter.installSegment5 (Compiler::Stats::opcodeRaiseRank, new OpRaiseRank); + interpreter.installSegment5 (Compiler::Stats::opcodeRaiseRankExplicit, new OpRaiseRank); + interpreter.installSegment5 (Compiler::Stats::opcodeLowerRank, new OpLowerRank); + interpreter.installSegment5 (Compiler::Stats::opcodeLowerRankExplicit, new OpLowerRank); + + interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath); + interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath); + + interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolf, new OpIsWerewolf); + interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolfExplicit, new OpIsWerewolf); + + interpreter.installSegment5 (Compiler::Stats::opcodeBecomeWerewolf, new OpSetWerewolf); + interpreter.installSegment5 (Compiler::Stats::opcodeBecomeWerewolfExplicit, new OpSetWerewolf); + interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolf, new OpSetWerewolf); + interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolfExplicit, new OpSetWerewolf); + interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobatics, new OpSetWerewolfAcrobatics); + interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit, new OpSetWerewolfAcrobatics); } } } diff --git a/apps/openmw/mwscript/statsextensions.hpp b/apps/openmw/mwscript/statsextensions.hpp index 372266d01..213b54967 100644 --- a/apps/openmw/mwscript/statsextensions.hpp +++ b/apps/openmw/mwscript/statsextensions.hpp @@ -15,9 +15,7 @@ namespace MWScript { /// \brief stats-related script functionality (creatures and NPCs) namespace Stats - { - void registerExtensions (Compiler::Extensions& extensions); - + { void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index d86a6e348..6246daee2 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -84,21 +85,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); @@ -148,15 +155,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()+Ogre::Radian(ptr.getRefData().getLocalRotation().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()+Ogre::Radian(ptr.getRefData().getLocalRotation().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()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees()); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -241,15 +248,15 @@ namespace MWScript if(axis == "x") { - runtime.push(ptr.getRefData().getPosition().pos[0]); + runtime.push(ptr.getCellRef().mPos.pos[0]); } else if(axis == "y") { - runtime.push(ptr.getRefData().getPosition().pos[1]); + runtime.push(ptr.getCellRef().mPos.pos[1]); } else if(axis == "z") { - runtime.push(ptr.getRefData().getPosition().pos[2]); + runtime.push(ptr.getCellRef().mPos.pos[2]); } else throw std::runtime_error ("invalid axis: " + axis); @@ -303,6 +310,8 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + + MWWorld::Class::get(ptr).adjustPosition(ptr); } else { @@ -341,6 +350,7 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + MWWorld::Class::get(ptr).adjustPosition(ptr); } }; @@ -457,6 +467,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 +517,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,78 +549,204 @@ namespace MWScript } }; - const int opcodeSetScale = 0x2000164; - const int opcodeSetScaleExplicit = 0x2000165; - const int opcodeSetAngle = 0x2000166; - const int opcodeSetAngleExplicit = 0x2000167; - const int opcodeGetScale = 0x2000168; - const int opcodeGetScaleExplicit = 0x2000169; - const int opcodeGetAngle = 0x200016a; - const int opcodeGetAngleExplicit = 0x200016b; - const int opcodeGetPos = 0x2000190; - const int opcodeGetPosExplicit = 0x2000191; - const int opcodeSetPos = 0x2000192; - const int opcodeSetPosExplicit = 0x2000193; - const int opcodeGetStartingPos = 0x2000194; - const int opcodeGetStartingPosExplicit = 0x2000195; - const int opcodePosition = 0x2000196; - const int opcodePositionExplicit = 0x2000197; - const int opcodePositionCell = 0x2000198; - const int opcodePositionCellExplicit = 0x2000199; - - const int opcodePlaceItemCell = 0x200019a; - const int opcodePlaceItem = 0x200019b; - const int opcodePlaceAtPc = 0x200019c; - const int opcodePlaceAtMe = 0x200019d; - const int opcodePlaceAtMeExplicit = 0x200019e; - const int opcodeModScale = 0x20001e3; - const int opcodeModScaleExplicit = 0x20001e4; - - void registerExtensions (Compiler::Extensions& extensions) + template + class OpRotate : public Interpreter::Opcode0 { - extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); - extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); - extensions.registerInstruction("setangle","cf",opcodeSetAngle,opcodeSetAngleExplicit); - extensions.registerFunction("getangle",'f',"c",opcodeGetAngle,opcodeGetAngleExplicit); - extensions.registerInstruction("setpos","cf",opcodeSetPos,opcodeSetPosExplicit); - extensions.registerFunction("getpos",'f',"c",opcodeGetPos,opcodeGetPosExplicit); - extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit); - extensions.registerInstruction("position","ffff",opcodePosition,opcodePositionExplicit); - extensions.registerInstruction("positioncell","ffffc",opcodePositionCell,opcodePositionCellExplicit); - extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell); - extensions.registerInstruction("placeitem","cffff",opcodePlaceItem); - extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); - extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); - extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit); - } + 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); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5(opcodeSetScale,new OpSetScale); - interpreter.installSegment5(opcodeSetScaleExplicit,new OpSetScale); - interpreter.installSegment5(opcodeSetAngle,new OpSetAngle); - interpreter.installSegment5(opcodeSetAngleExplicit,new OpSetAngle); - interpreter.installSegment5(opcodeGetScale,new OpGetScale); - interpreter.installSegment5(opcodeGetScaleExplicit,new OpGetScale); - interpreter.installSegment5(opcodeGetAngle,new OpGetAngle); - interpreter.installSegment5(opcodeGetAngleExplicit,new OpGetAngle); - interpreter.installSegment5(opcodeGetPos,new OpGetPos); - interpreter.installSegment5(opcodeGetPosExplicit,new OpGetPos); - interpreter.installSegment5(opcodeSetPos,new OpSetPos); - interpreter.installSegment5(opcodeSetPosExplicit,new OpSetPos); - interpreter.installSegment5(opcodeGetStartingPos,new OpGetStartingPos); - interpreter.installSegment5(opcodeGetStartingPosExplicit,new OpGetStartingPos); - interpreter.installSegment5(opcodePosition,new OpPosition); - interpreter.installSegment5(opcodePositionExplicit,new OpPosition); - interpreter.installSegment5(opcodePositionCell,new OpPositionCell); - interpreter.installSegment5(opcodePositionCellExplicit,new OpPositionCell); - interpreter.installSegment5(opcodePlaceItemCell,new OpPlaceItemCell); - interpreter.installSegment5(opcodePlaceItem,new OpPlaceItem); - interpreter.installSegment5(opcodePlaceAtPc,new OpPlaceAtPc); - interpreter.installSegment5(opcodePlaceAtMe,new OpPlaceAtMe); - interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe); - interpreter.installSegment5(opcodeModScale,new OpModScale); - interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale); + interpreter.installSegment5(Compiler::Transformation::opcodeSetScale,new OpSetScale); + interpreter.installSegment5(Compiler::Transformation::opcodeSetScaleExplicit,new OpSetScale); + interpreter.installSegment5(Compiler::Transformation::opcodeSetAngle,new OpSetAngle); + interpreter.installSegment5(Compiler::Transformation::opcodeSetAngleExplicit,new OpSetAngle); + interpreter.installSegment5(Compiler::Transformation::opcodeGetScale,new OpGetScale); + interpreter.installSegment5(Compiler::Transformation::opcodeGetScaleExplicit,new OpGetScale); + interpreter.installSegment5(Compiler::Transformation::opcodeGetAngle,new OpGetAngle); + interpreter.installSegment5(Compiler::Transformation::opcodeGetAngleExplicit,new OpGetAngle); + interpreter.installSegment5(Compiler::Transformation::opcodeGetPos,new OpGetPos); + interpreter.installSegment5(Compiler::Transformation::opcodeGetPosExplicit,new OpGetPos); + interpreter.installSegment5(Compiler::Transformation::opcodeSetPos,new OpSetPos); + interpreter.installSegment5(Compiler::Transformation::opcodeSetPosExplicit,new OpSetPos); + interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingPos,new OpGetStartingPos); + interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingPosExplicit,new OpGetStartingPos); + interpreter.installSegment5(Compiler::Transformation::opcodePosition,new OpPosition); + interpreter.installSegment5(Compiler::Transformation::opcodePositionExplicit,new OpPosition); + interpreter.installSegment5(Compiler::Transformation::opcodePositionCell,new OpPositionCell); + interpreter.installSegment5(Compiler::Transformation::opcodePositionCellExplicit,new OpPositionCell); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAtPc); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAtMe); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAtMe); + interpreter.installSegment5(Compiler::Transformation::opcodeModScale,new OpModScale); + interpreter.installSegment5(Compiler::Transformation::opcodeModScaleExplicit,new OpModScale); + interpreter.installSegment5(Compiler::Transformation::opcodeRotate,new OpRotate); + interpreter.installSegment5(Compiler::Transformation::opcodeRotateExplicit,new OpRotate); + interpreter.installSegment5(Compiler::Transformation::opcodeRotateWorld,new OpRotateWorld); + interpreter.installSegment5(Compiler::Transformation::opcodeRotateWorldExplicit,new OpRotateWorld); + interpreter.installSegment5(Compiler::Transformation::opcodeSetAtStart,new OpSetAtStart); + interpreter.installSegment5(Compiler::Transformation::opcodeSetAtStartExplicit,new OpSetAtStart); + interpreter.installSegment5(Compiler::Transformation::opcodeMove,new OpMove); + interpreter.installSegment5(Compiler::Transformation::opcodeMoveExplicit,new OpMove); + interpreter.installSegment5(Compiler::Transformation::opcodeMoveWorld,new OpMoveWorld); + interpreter.installSegment5(Compiler::Transformation::opcodeMoveWorldExplicit,new OpMoveWorld); + interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle); + interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle); } } } diff --git a/apps/openmw/mwscript/transformationextensions.hpp b/apps/openmw/mwscript/transformationextensions.hpp index 6ee1db1b8..a25cc0c3d 100644 --- a/apps/openmw/mwscript/transformationextensions.hpp +++ b/apps/openmw/mwscript/transformationextensions.hpp @@ -15,9 +15,7 @@ namespace MWScript { /// \brief stats-related script functionality (creatures and NPCs) namespace Transformation - { - void registerExtensions (Compiler::Extensions& extensions); - + { void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwscript/userextensions.cpp b/apps/openmw/mwscript/userextensions.cpp index 9c9738815..538133c65 100644 --- a/apps/openmw/mwscript/userextensions.cpp +++ b/apps/openmw/mwscript/userextensions.cpp @@ -2,6 +2,7 @@ #include "userextensions.hpp" #include +#include #include #include @@ -62,30 +63,16 @@ namespace MWScript runtime.getContext().report ("user4: not in use"); } }; - - const int opcodeUser1 = 0x200016c; - const int opcodeUser2 = 0x200016d; - const int opcodeUser3 = 0x200016e; - const int opcodeUser3Explicit = 0x200016f; - const int opcodeUser4 = 0x2000170; - const int opcodeUser4Explicit = 0x2000171; - - void registerExtensions (Compiler::Extensions& extensions) - { - extensions.registerInstruction ("user1", "", opcodeUser1); - extensions.registerInstruction ("user2", "", opcodeUser2); - extensions.registerInstruction ("user3", "", opcodeUser3, opcodeUser3); - extensions.registerInstruction ("user4", "", opcodeUser4, opcodeUser4); - } + void installOpcodes (Interpreter::Interpreter& interpreter) { - interpreter.installSegment5 (opcodeUser1, new OpUser1); - interpreter.installSegment5 (opcodeUser2, new OpUser2); - interpreter.installSegment5 (opcodeUser3, new OpUser3); - interpreter.installSegment5 (opcodeUser3Explicit, new OpUser3); - interpreter.installSegment5 (opcodeUser4, new OpUser4); - interpreter.installSegment5 (opcodeUser4Explicit, new OpUser4); + interpreter.installSegment5 (Compiler::User::opcodeUser1, new OpUser1); + interpreter.installSegment5 (Compiler::User::opcodeUser2, new OpUser2); + interpreter.installSegment5 (Compiler::User::opcodeUser3, new OpUser3); + interpreter.installSegment5 (Compiler::User::opcodeUser3Explicit, new OpUser3); + interpreter.installSegment5 (Compiler::User::opcodeUser4, new OpUser4); + interpreter.installSegment5 (Compiler::User::opcodeUser4Explicit, new OpUser4); } } } diff --git a/apps/openmw/mwscript/userextensions.hpp b/apps/openmw/mwscript/userextensions.hpp index 4bc3b46ec..da6e0faa6 100644 --- a/apps/openmw/mwscript/userextensions.hpp +++ b/apps/openmw/mwscript/userextensions.hpp @@ -16,8 +16,6 @@ namespace MWScript /// \brief Temporary script functionality limited to the console namespace User { - void registerExtensions (Compiler::Extensions& extensions); - void installOpcodes (Interpreter::Interpreter& interpreter); } } diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 32b2797ed..d0d73379d 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... @@ -12,8 +10,18 @@ extern "C" { #include #include + +// From libavutil version 52.2.0 and onward the declaration of +// AV_CH_LAYOUT_* is removed from libavcodec/avcodec.h and moved to +// libavutil/channel_layout.h +#if AV_VERSION_INT(52, 2, 0) <= AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO) + #include +#endif } +#include + #include "sound_decoder.hpp" diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 1dc5f9974..4ee754b35 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -225,7 +225,7 @@ struct OpenAL_Output::StreamThread { if((*iter)->process() == false) iter = mStreams.erase(iter); else - iter++; + ++iter; } mMutex.unlock(); boost::this_thread::sleep(boost::posix_time::milliseconds(50)); @@ -491,6 +491,7 @@ public: virtual void stop(); virtual bool isPlaying(); virtual double getTimeOffset(); + virtual double getLength(); virtual void update(); }; @@ -554,6 +555,17 @@ double OpenAL_Sound::getTimeOffset() return t; } +double OpenAL_Sound::getLength() +{ + ALint bufferSize, frequency, channels, bitsPerSample; + alGetBufferi(mBuffer, AL_SIZE, &bufferSize); + alGetBufferi(mBuffer, AL_FREQUENCY, &frequency); + alGetBufferi(mBuffer, AL_CHANNELS, &channels); + alGetBufferi(mBuffer, AL_BITS, &bitsPerSample); + + return (8.0*bufferSize)/(frequency*channels*bitsPerSample); +} + void OpenAL_Sound::updateAll(bool local) { alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); @@ -586,7 +598,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(); @@ -606,7 +618,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(); @@ -797,7 +809,7 @@ ALuint OpenAL_Output::getBuffer(const std::string &fname) if(nameiter->second == oldbuf) mBufferCache.erase(nameiter++); else - nameiter++; + ++nameiter; } bufsize = 0; @@ -817,8 +829,7 @@ void OpenAL_Output::bufferFinished(ALuint buf) } } - -MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, float basevol, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, float basevol, float pitch, int flags,float offset) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -843,8 +854,13 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, f } sound->updateAll(true); + if(offset<0) + offset=0; + if(offset>1) + offset=1; alSourcei(src, AL_BUFFER, buf); + alSourcef(src, AL_SEC_OFFSET, sound->getLength()*offset/pitch); alSourcePlay(src); throwALerror(); @@ -852,7 +868,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, f } MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float vol, float basevol, float pitch, - float min, float max, int flags) + float min, float max, int flags, float offset) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -878,7 +894,14 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre sound->updateAll(false); + if(offset<0) + offset=0; + if(offset>1) + offset=1; + alSourcei(src, AL_BUFFER, buf); + alSourcef(src, AL_SEC_OFFSET, sound->getLength()*offset/pitch); + alSourcePlay(src); throwALerror(); @@ -923,10 +946,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(); } @@ -951,9 +974,9 @@ void OpenAL_Output::pauseSounds(int types) if(sound && sound->mSource && (sound->getPlayType()&types)) sources.push_back(sound->mSource); } - iter++; + ++iter; } - if(sources.size() > 0) + if(!sources.empty()) { alSourcePausev(sources.size(), &sources[0]); throwALerror(); @@ -978,9 +1001,9 @@ void OpenAL_Output::resumeSounds(int types) if(sound && sound->mSource && (sound->getPlayType()&types)) sources.push_back(sound->mSource); } - iter++; + ++iter; } - if(sources.size() > 0) + if(!sources.empty()) { alSourcePlayv(sources.size(), &sources[0]); throwALerror(); diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 02706b50c..31edf7359 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -45,9 +45,11 @@ namespace MWSound virtual void init(const std::string &devname=""); virtual void deinit(); - virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags); + /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset); + /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float vol, float basevol, float pitch, float min, float max, int flags); + float vol, float basevol, float pitch, float min, float max, int flags, float offset); virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 8deaf26a6..670002a30 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -22,6 +22,7 @@ namespace MWSound float mMinDistance; float mMaxDistance; int mFlags; + float mFadeOutTime; public: virtual void stop() = 0; @@ -29,7 +30,7 @@ namespace MWSound virtual double getTimeOffset() = 0; void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } - + void setFadeout(float duration) { mFadeOutTime=duration; } MWBase::SoundManager::PlayType getPlayType() const { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); } @@ -42,6 +43,7 @@ namespace MWSound , mMinDistance(mindist) , mMaxDistance(maxdist) , mFlags(flags) + , mFadeOutTime(0) { } virtual ~Sound() { } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index b5ccc946a..91e25db52 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -24,9 +24,11 @@ namespace MWSound virtual void init(const std::string &devname="") = 0; virtual void deinit() = 0; - virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags) = 0; + /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. + virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset) = 0; + /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float vol, float basevol, float pitch, float min, float max, int flags) = 0; + float vol, float basevol, float pitch, float min, float max, int flags, float offset) = 0; virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 1b07dfe62..372be8393 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -4,11 +4,10 @@ #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" #include "sound_output.hpp" @@ -53,6 +52,9 @@ namespace MWSound , mFootstepsVolume(1.0f) , mVoiceVolume(1.0f) , mPausedSoundTypes(0) + , mListenerPos(0,0,0) + , mListenerDir(1,0,0) + , mListenerUp(0,0,1) { if(!useSound) return; @@ -100,6 +102,7 @@ namespace MWSound SoundManager::~SoundManager() { + mUnderwaterSound.reset(); mActiveSounds.clear(); mMusic.reset(); mOutput.reset(); @@ -150,6 +153,9 @@ namespace MWSound case Play_TypeVoice: volume *= mVoiceVolume; break; + case Play_TypeFoot: + volume *= mFootstepsVolume; + break; case Play_TypeMusic: case Play_TypeMovie: volume *= mMusicVolume; @@ -167,7 +173,7 @@ namespace MWSound { if(snditer->second.first == ptr && snditer->second.second == id) return snditer->first->isPlaying(); - snditer++; + ++snditer; } return false; } @@ -208,14 +214,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() @@ -242,7 +255,7 @@ namespace MWSound const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - 20.0f, 12750.0f, Play_Normal|Play_TypeVoice); + 20.0f, 12750.0f, Play_Normal|Play_TypeVoice, 0); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) @@ -260,7 +273,7 @@ namespace MWSound float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; - MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice); + MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice, 0); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); } catch(std::exception &e) @@ -285,7 +298,7 @@ namespace MWSound mActiveSounds.erase(snditer++); } else - snditer++; + ++snditer; } } @@ -307,18 +320,18 @@ namespace MWSound } - MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, PlayMode mode) + MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, PlayType type, PlayMode mode, float offset) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) return sound; try { - float basevol = volumeFromType(Play_TypeSfx); + float basevol = volumeFromType(type); float min, max; std::string file = lookup(soundId, volume, min, max); - sound = mOutput->playSound(file, volume, basevol, pitch, mode|Play_TypeSfx); + sound = mOutput->playSound(file, volume, basevol, pitch, mode|type, offset); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); } catch(std::exception &e) @@ -329,7 +342,7 @@ namespace MWSound } MWBase::SoundPtr SoundManager::playSound3D(const MWWorld::Ptr &ptr, const std::string& soundId, - float volume, float pitch, PlayMode mode) + float volume, float pitch, PlayType type, PlayMode mode, float offset) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) @@ -337,13 +350,13 @@ namespace MWSound try { // Look up the sound in the ESM data - float basevol = volumeFromType(Play_TypeSfx); + float basevol = volumeFromType(type); float min, max; std::string file = lookup(soundId, volume, min, max); - const ESM::Position &pos = ptr.getRefData().getPosition();; + const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|Play_TypeSfx); + sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|type, offset); if((mode&Play_NoTrack)) mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); else @@ -367,7 +380,7 @@ namespace MWSound mActiveSounds.erase(snditer++); } else - snditer++; + ++snditer; } } @@ -382,7 +395,7 @@ namespace MWSound mActiveSounds.erase(snditer++); } else - snditer++; + ++snditer; } } @@ -398,7 +411,7 @@ namespace MWSound mActiveSounds.erase(snditer++); } else - snditer++; + ++snditer; } } @@ -414,7 +427,21 @@ namespace MWSound mActiveSounds.erase(snditer++); } else - snditer++; + ++snditer; + } + } + + void SoundManager::fadeOutSound3D(const MWWorld::Ptr &ptr, + const std::string& soundId, float duration) + { + SoundMap::iterator snditer = mActiveSounds.begin(); + while(snditer != mActiveSounds.end()) + { + if(snditer->second.first == ptr && snditer->second.second == soundId) + { + snditer->first->setFadeout(duration); + } + snditer++; } } @@ -447,27 +474,32 @@ namespace MWSound void SoundManager::updateRegionSound(float duration) { - MWWorld::Ptr::CellStore *current = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + static float sTimeToNextEnvSound = 0.0f; static int total = 0; static std::string regionName = ""; - static float timePassed = 0.0; + static float sTimePassed = 0.0; + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Ptr player = world->getPlayer().getPlayer(); + const ESM::Cell *cell = player.getCell()->mCell; - //If the region has changed - timePassed += duration; - if(!current->mCell->isExterior() || timePassed < 10) + sTimePassed += duration; + if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound) return; - timePassed = 0; - if(regionName != current->mCell->mRegion) + float a = std::rand() / (double)RAND_MAX; + // NOTE: We should use the "Minimum Time Between Environmental Sounds" and + // "Maximum Time Between Environmental Sounds" fallback settings here. + sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a); + sTimePassed = 0; + + if(regionName != cell->mRegion) { - regionName = current->mCell->mRegion; + regionName = cell->mRegion; total = 0; } - const ESM::Region *regn = - MWBase::Environment::get().getWorld()->getStore().get().search(regionName); - - if (regn == NULL) + const ESM::Region *regn = world->getStore().get().search(regionName); + if(regn == NULL) return; std::vector::const_iterator soundIter; @@ -477,7 +509,7 @@ namespace MWSound while(soundIter != regn->mSoundList.end()) { total += (int)soundIter->mChance; - soundIter++; + ++soundIter; } if(total == 0) return; @@ -489,18 +521,14 @@ namespace MWSound soundIter = regn->mSoundList.begin(); while(soundIter != regn->mSoundList.end()) { - const std::string go = soundIter->mSound.toString(); - int chance = (int) soundIter->mChance; - //std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; - soundIter++; - if(r - pos < chance) + if(r - pos < soundIter->mChance) { - //play sound - std::cout << "Sound: " << go <<" Chance:" << chance << "\n"; - playSound(go, 1.0f, 1.0f); + playSound(soundIter->mSound.toString(), 1.0f, 1.0f); break; } - pos += chance; + pos += soundIter->mChance; + + ++soundIter; } } @@ -511,6 +539,7 @@ namespace MWSound timePassed += duration; if(timePassed < (1.0f/30.0f)) return; + duration = timePassed; timePassed = 0.0f; // Make sure music is still playing @@ -523,7 +552,17 @@ namespace MWSound Environment env = Env_Normal; if((cell->mData.mFlags&cell->HasWater) && mListenerPos.z < cell->mWater) + { env = Env_Underwater; + //play underwater sound + if(!(mUnderwaterSound && mUnderwaterSound->isPlaying())) + mUnderwaterSound = playSound("Underwater", 1.0f, 1.0f, Play_TypeSfx, Play_LoopNoEnv); + } + else if(mUnderwaterSound) + { + mUnderwaterSound->stop(); + mUnderwaterSound.reset(); + } mOutput->updateListener( mListenerPos, @@ -533,6 +572,7 @@ namespace MWSound ); // Check if any sounds are finished playing, and trash them + // Lower volume on fading out sounds SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { @@ -540,8 +580,25 @@ namespace MWSound mActiveSounds.erase(snditer++); else { + const MWWorld::Ptr &ptr = snditer->second.first; + if(!ptr.isEmpty()) + { + const ESM::Position &pos = ptr.getRefData().getPosition(); + const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + snditer->first->setPosition(objpos); + } + //update fade out + if(snditer->first->mFadeOutTime>0) + { + float soundDuration=duration; + if(soundDuration>snditer->first->mFadeOutTime) + soundDuration=snditer->first->mFadeOutTime; + snditer->first->setVolume(snditer->first->mVolume + - soundDuration / snditer->first->mFadeOutTime * snditer->first->mVolume); + snditer->first->mFadeOutTime -= soundDuration; + } snditer->first->update(); - snditer++; + ++snditer; } } } @@ -568,7 +625,7 @@ namespace MWSound { snditer->first->mBaseVolume = volumeFromType(snditer->first->getPlayType()); snditer->first->update(); - snditer++; + ++snditer; } if(mMusic) { diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 2af26d3db..f62e62d50 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -35,8 +35,6 @@ namespace MWSound float mSFXVolume; float mMusicVolume; float mVoiceVolume; - - // not implemented float mFootstepsVolume; boost::shared_ptr mMusic; @@ -46,6 +44,8 @@ namespace MWSound typedef std::map SoundMap; SoundMap mActiveSounds; + MWBase::SoundPtr mUnderwaterSound; + Ogre::Vector3 mListenerPos; Ogre::Vector3 mListenerDir; Ogre::Vector3 mListenerUp; @@ -108,12 +108,15 @@ namespace MWSound virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder - virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal); + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); ///< Play a sound, independently of 3D-position + ///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end. virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, - float volume, float pitch, PlayMode mode=Play_Normal); + float volume, float pitch, PlayType type=Play_TypeSfx, + PlayMode mode=Play_Normal, float offset=0); ///< Play a sound from an object + ///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end. virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId); ///< Stop the given object from playing the given sound, @@ -127,6 +130,12 @@ namespace MWSound virtual void stopSound(const std::string& soundId); ///< Stop a non-3d looping sound + virtual void fadeOutSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float duration); + ///< Fade out given sound (that is already playing) of given object + ///< @param reference Reference to object, whose sound is faded out + ///< @param soundId ID of the sound to fade out. + ///< @param duration Time until volume reaches 0. + virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index a5199fb3e..0fe061e5c 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -11,7 +11,7 @@ const MWWorld::Ptr& MWWorld::Action::getTarget() const return mTarget; } -MWWorld::Action::Action (bool keepSound, const Ptr& target) : mKeepSound (keepSound), mTarget (target) +MWWorld::Action::Action (bool keepSound, const Ptr& target) : mKeepSound (keepSound), mTarget (target), mSoundOffset(0) {} MWWorld::Action::~Action() {} @@ -21,18 +21,16 @@ void MWWorld::Action::execute (const Ptr& actor) if (!mSoundId.empty()) { if (mKeepSound && actor.getRefData().getHandle()=="player") - { - // sound moves with player when teleporting MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); - } + MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Normal,mSoundOffset); else { bool local = mTarget.isEmpty() || !mTarget.isInCell(); // no usable target - MWBase::Environment::get().getSoundManager()->playSound3D (local ? actor : mTarget, - mSoundId, 1.0, 1.0, - mKeepSound ? MWBase::SoundManager::Play_NoTrack : MWBase::SoundManager::Play_Normal); + MWBase::Environment::get().getSoundManager()->playSound3D(local ? actor : mTarget, + mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, + mKeepSound ? MWBase::SoundManager::Play_NoTrack : MWBase::SoundManager::Play_Normal, + mSoundOffset); } } @@ -43,3 +41,8 @@ void MWWorld::Action::setSound (const std::string& id) { mSoundId = id; } + +void MWWorld::Action::setSoundOffset(float offset) +{ + mSoundOffset=offset; +} diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index d8e5d93bb..3e0e8ad1b 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -12,6 +12,7 @@ namespace MWWorld { std::string mSoundId; bool mKeepSound; + float mSoundOffset; Ptr mTarget; // not implemented @@ -34,6 +35,7 @@ namespace MWWorld void execute (const Ptr& actor); void setSound (const std::string& id); + void setSoundOffset(float offset); }; } 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 eb2ae9dca..0f1a85dda 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -18,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()); @@ -27,7 +46,7 @@ namespace MWWorld MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (*it == getTarget()) + if (*it == object) { break; } @@ -35,8 +54,6 @@ namespace MWWorld assert(it != invStore.end()); - std::string npcRace = actor.get()->mBase->mRace; - bool equipped = false; // equip the item in the first free slot @@ -44,70 +61,6 @@ namespace MWWorld 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) - { - 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::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) - { - bool allow = true; - std::vector parts; - if(it.getType() == MWWorld::ContainerStore::Type_Clothing) - parts = it->get()->mBase->mParts.mParts; - else - parts = it->get()->mBase->mParts.mParts; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - { - allow = false; - // 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(!allow) - break; - } - - } - // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) { @@ -125,10 +78,10 @@ namespace MWWorld } } - std::string script = MWWorld::Class::get(*it).getScript(*it); + 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 != "") - (*it).mRefData->getLocals().setVarByInt(script, "onpcequip", 1); + object.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); } } 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 cdd19b46e..d3c4aa2f6 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -17,7 +17,7 @@ namespace MWWorld // insert into player's inventory MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPtr ("player", true); - MWWorld::Class::get (player).getContainerStore (player).add (getTarget()); + MWWorld::Class::get (player).getContainerStore (player).add (getTarget(), player); MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } 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 f95d30df1..37c4b6a3f 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,44 +36,19 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) } } -void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) +void MWWorld::Cells::clear() { - for (CellRefList::List::iterator iter ( - cellStore.mContainers.mList.begin()); - iter!=cellStore.mContainers.mList.end(); ++iter) - { - Ptr container (&*iter, &cellStore); - - Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); - } - - for (CellRefList::List::iterator iter ( - cellStore.mCreatures.mList.begin()); - iter!=cellStore.mCreatures.mList.end(); ++iter) - { - Ptr container (&*iter, &cellStore); - - Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); - } - - for (CellRefList::List::iterator iter ( - cellStore.mNpcs.mList.begin()); - iter!=cellStore.mNpcs.mList.end(); ++iter) - { - Ptr container (&*iter, &cellStore); - - Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); - } + mInteriors.clear(); + mExteriors.clear(); + std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::Ptr::CellStore*)0)); + mIdCacheIndex = 0; } MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellStore& cellStore) { Ptr ptr = getPtr (name, cellStore); - if (!ptr.isEmpty()) + if (!ptr.isEmpty() && ptr.isInCell()) { mIdCache[mIdCacheIndex].first = name; mIdCache[mIdCacheIndex].second = &cellStore; @@ -121,7 +96,6 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) { // Multiple plugin support for landscape data is much easier than for references. The last plugin wins. result->second.load (mStore, mReader); - fillContainers (result->second); } return &result->second; @@ -142,13 +116,13 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) if (result->second.mState!=Ptr::CellStore::State_Loaded) { result->second.load (mStore, mReader); - fillContainers (result->second); } return &result->second; } -MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& cell) +MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& cell, + bool searchInContainers) { if (cell.mState==Ptr::CellStore::State_Unloaded) cell.preload (mStore, mReader); @@ -160,76 +134,74 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), lowerCase)) { cell.load (mStore, mReader); - fillContainers (cell); } else return Ptr(); } - MWWorld::Ptr ptr; if (MWWorld::LiveCellRef *ref = cell.mActivators.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mPotions.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mAppas.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mArmors.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mBooks.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mClothes.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mContainers.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mCreatures.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mDoors.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mIngreds.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mCreatureLists.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mItemLists.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mLights.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) - ptr = Ptr (ref, &cell); + if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mMiscItems.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mNpcs.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mProbes.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mRepairs.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mStatics.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mWeapons.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); + + if (searchInContainers) + return cell.searchInContainer (name); - if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) { - return ptr; - } return Ptr(); } @@ -246,16 +218,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()) @@ -266,7 +238,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)); @@ -276,7 +248,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 f999fedde..0c51cf452 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -31,12 +31,12 @@ namespace MWWorld CellStore *getCellStore (const ESM::Cell *cell); - void fillContainers (CellStore& cellStore); - Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); public: + void clear(); + Cells (const MWWorld::ESMStore& store, std::vector& reader); ///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole /// world @@ -45,7 +45,8 @@ namespace MWWorld CellStore *getInterior (const std::string& name); - Ptr getPtr (const std::string& name, CellStore& cellStore); + Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); + ///< \param searchInContainers Only affect loaded cells. Ptr getPtr (const std::string& name); }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index baf4fea32..0c145ab60 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -7,6 +7,29 @@ #include "ptr.hpp" #include "esmstore.hpp" +#include "class.hpp" +#include "containerstore.hpp" + +namespace +{ + template + MWWorld::Ptr searchInContainerList (MWWorld::CellRefList& containerList, const std::string& id) + { + for (typename MWWorld::CellRefList::List::iterator iter (containerList.mList.begin()); + iter!=containerList.mList.end(); ++iter) + { + MWWorld::Ptr container (&*iter, 0); + + MWWorld::Ptr ptr = + MWWorld::Class::get (container).getContainerStore (container).search (id); + + if (!ptr.isEmpty()) + return ptr; + } + + return MWWorld::Ptr(); + } +} namespace MWWorld { @@ -16,14 +39,15 @@ namespace MWWorld { // 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); + if (iter != mList.end()) + mList.erase(iter); return; } - + // for throwing exception on unhandled record type const MWWorld::Store &store = esmStore.get(); const X *ptr = store.search(ref.mRefID); @@ -39,7 +63,7 @@ namespace MWWorld mList.push_back(LiveRef(ref, ptr)); } } - + template bool operator==(const LiveCellRef& ref, int pRefnum) { return (ref.mRef.mRefnum == pRefnum); @@ -80,7 +104,7 @@ namespace MWWorld { assert (mCell); - if (mCell->mContextList.size() == 0) + if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. // Load references from all plugins that do something with this cell. @@ -113,7 +137,7 @@ namespace MWWorld { assert (mCell); - if (mCell->mContextList.size() == 0) + if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. // Load references from all plugins that do something with this cell. @@ -133,7 +157,7 @@ namespace MWWorld 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; @@ -174,7 +198,7 @@ namespace MWWorld } // Load moved references, from separately tracked list. - for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); it++) + for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { // Doesn't seem to work in one line... huh? Too sleepy to check... ESM::CellRef &ref = const_cast(*it); @@ -186,7 +210,7 @@ namespace MWWorld (int(*)(int)) std::tolower); int rec = store.find(ref.mRefID); - + ref.mRefID = lowerCase; /* We can optimize this further by storing the pointer to the @@ -221,7 +245,33 @@ namespace MWWorld default: std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; } - + } } + + Ptr CellStore::searchInContainer (const std::string& id) + { + { + Ptr ptr = searchInContainerList (mContainers, id); + + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchInContainerList (mCreatures, id); + + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchInContainerList (mNpcs, id); + + if (!ptr.isEmpty()) + return ptr; + } + + return Ptr(); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index c182f196b..bcbc5e415 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -1,50 +1,14 @@ #ifndef GAME_MWWORLD_CELLSTORE_H #define GAME_MWWORLD_CELLSTORE_H -#include - #include #include -#include "refdata.hpp" +#include "livecellref.hpp" #include "esmstore.hpp" -struct C; 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); /// A list of cell references template @@ -112,7 +76,7 @@ namespace MWWorld CellRefList mCreatureLists; CellRefList mItemLists; CellRefList mLights; - CellRefList mLockpicks; + CellRefList mLockpicks; CellRefList mMiscItems; CellRefList mNpcs; CellRefList mProbes; @@ -167,6 +131,8 @@ namespace MWWorld return mCell->isExterior(); } + Ptr searchInContainer (const std::string& id); + private: template @@ -184,6 +150,7 @@ namespace MWWorld void listRefs(const MWWorld::ESMStore &store, std::vector &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 71b24b65d..c739ea831 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -7,13 +7,23 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + #include "ptr.hpp" #include "refdata.hpp" #include "nullaction.hpp" +#include "failedaction.hpp" +#include "actiontake.hpp" #include "containerstore.hpp" #include "../mwgui/tooltips.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/magiceffects.hpp" + namespace MWWorld { std::map > Class::sClasses; @@ -47,6 +57,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"); @@ -67,6 +87,21 @@ namespace MWWorld throw std::runtime_error ("class does not have item health"); } + void Class::hit(const Ptr& ptr, int type) const + { + throw std::runtime_error("class cannot hit"); + } + + void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const + { + throw std::runtime_error("class cannot be hit"); + } + + void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const + { + throw std::runtime_error("class does not have actor health"); + } + boost::shared_ptr Class::activate (const Ptr& ptr, const Ptr& actor) const { return boost::shared_ptr (new NullAction); @@ -127,6 +162,11 @@ namespace MWWorld 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"); @@ -137,6 +177,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); @@ -157,6 +202,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"); @@ -167,29 +217,38 @@ 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; } - const Class& Class::get (const Ptr& ptr) + bool Class::isPersistent(const Ptr &ptr) const { - return get (ptr.getTypeName()); + throw std::runtime_error ("class does not support persistence"); } - void Class::registerClass (const std::string& key, boost::shared_ptr instance) + void Class::registerClass(const std::string& key, boost::shared_ptr instance) { - sClasses.insert (std::make_pair (key, instance)); + instance->mTypeName = key; + sClasses.insert(std::make_pair(key, instance)); } std::string Class::getUpSoundId (const Ptr& ptr) const @@ -202,6 +261,10 @@ namespace MWWorld throw std::runtime_error ("class does not have an down sound"); } + std::string Class::getSoundIdFromSndGen(const Ptr &ptr, const std::string &type) const + { + throw std::runtime_error("class does not support soundgen look up"); + } std::string Class::getInventoryIcon (const MWWorld::Ptr& ptr) const { @@ -236,6 +299,42 @@ 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 + { + } + + boost::shared_ptr Class::defaultItemActivate(const Ptr &ptr, const Ptr &actor) const + { + if(!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr(new NullAction()); + + if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfItem"); + + boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); + if(sound) action->setSound(sound->mId); + + return action; + } + + boost::shared_ptr action(new ActionTake(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; + } + 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 1a6a16ca0..28e37cbf3 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -7,7 +7,7 @@ #include -#include "action.hpp" +#include "ptr.hpp" namespace Ogre { @@ -43,12 +43,15 @@ namespace MWWorld class InventoryStore; class PhysicsSystem; class CellStore; + class Action; /// \brief Base class for referenceable esm records class Class { static std::map > sClasses; + std::string mTypeName; + // not implemented Class (const Class&); Class& operator= (const Class&); @@ -57,6 +60,9 @@ namespace MWWorld Class(); + boost::shared_ptr defaultItemActivate(const Ptr &ptr, const Ptr &actor) const; + ///< Generate default action for activating inventory items + virtual Ptr copyToCellImpl(const Ptr &ptr, CellStore &cell) const; public: @@ -69,6 +75,10 @@ namespace MWWorld virtual ~Class(); + const std::string& getTypeName() const { + return mTypeName; + } + virtual std::string getId (const Ptr& ptr) const; ///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) @@ -81,6 +91,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) @@ -102,6 +115,25 @@ namespace MWWorld ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exceoption) + virtual void hit(const Ptr& ptr, int type=-1) const; + ///< Execute a melee hit, using the current weapon. This will check the relevant skills + /// of the given attacker, and whoever is hit. + /// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType + /// enums. ignored for creature attacks. + /// (default implementation: throw an exceoption) + + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + ///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is + /// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the + /// actor responsible for the attack, and \a successful specifies if the hit is + /// successful or not. + + virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; + ///< Sets a new current health value for the actor, optionally specifying the object causing + /// the change. Use this instead of using CreatureStats directly as this will make sure the + /// correct dialog and actor states are properly handled when being hurt or healed. + /// (default implementation: throw an exceoption) + virtual boost::shared_ptr activate (const Ptr& ptr, const Ptr& actor) const; ///< Generate action for activation (default implementation: return a null action). @@ -150,6 +182,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? @@ -199,14 +234,6 @@ namespace MWWorld /// /// (default implementation: return false) - static const Class& get (const std::string& key); - ///< If there is no class for this \a key, an exception is thrown. - - static const Class& get (const Ptr& ptr); - ///< If there is no class for this pointer, an exception is thrown. - - static void registerClass (const std::string& key, boost::shared_ptr instance); - virtual std::string getUpSoundId (const Ptr& ptr) const; ///< Return the up sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) @@ -215,6 +242,12 @@ 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 std::string getSoundIdFromSndGen(const Ptr &ptr, const std::string &type) const; + ///< Returns the sound ID for \a ptr of the given soundgen \a type. + + 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. @@ -222,22 +255,54 @@ 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; virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; - - virtual bool - isActor() const { + + virtual bool isActor() const { + return false; + } + + virtual bool isNpc() const { return false; } + + static const Class& get (const std::string& key); + ///< If there is no class for this \a key, an exception is thrown. + + static const Class& get (const Ptr& ptr) + { + return ptr.getClass(); + } + ///< If there is no class for this pointer, an exception is thrown. + + static void registerClass (const std::string& key, boost::shared_ptr instance); }; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8a7884e9e..c6768f5fd 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -9,11 +9,14 @@ #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" @@ -39,10 +42,24 @@ namespace return sum; } - bool compare_string_ci(std::string str1, std::string str2) + template + MWWorld::Ptr searchId (MWWorld::CellRefList& list, const std::string& id, + MWWorld::ContainerStore *store) { - Misc::StringUtils::toLower(str1); - return str1 == str2; + std::string id2 = Misc::StringUtils::lowerCase (id); + + for (typename MWWorld::CellRefList::List::iterator iter (list.mList.begin()); + iter!=list.mList.end(); ++iter) + { + if (Misc::StringUtils::lowerCase (iter->mBase->mId)==id2) + { + MWWorld::Ptr ptr (&*iter, 0); + ptr.setContainerStore (store); + return ptr; + } + } + + return MWWorld::Ptr(); } } @@ -62,21 +79,25 @@ 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 - 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) - && ptr1.mCellRef->mOwner == ptr2.mCellRef->mOwner - && ptr1.mCellRef->mSoul == ptr2.mCellRef->mSoul - && ptr1.mCellRef->mCharge == ptr2.mCellRef->mCharge) + /// \todo add current enchantment charge here when it is implemented + if ( Misc::StringUtils::ciEqual(ptr1.getCellRef().mRefID, ptr2.getCellRef().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) + && ptr1.getCellRef().mOwner == ptr2.getCellRef().mOwner + && ptr1.getCellRef().mSoul == ptr2.getCellRef().mSoul + // item that is already partly used up never stacks + && (!MWWorld::Class::get(ptr1).hasItemHealth(ptr1) || ptr1.getCellRef().mCharge == -1 + || MWWorld::Class::get(ptr1).getItemMaxHealth(ptr1) == ptr1.getCellRef().mCharge) + && (!MWWorld::Class::get(ptr2).hasItemHealth(ptr2) || ptr2.getCellRef().mCharge == -1 + || MWWorld::Class::get(ptr2).getItemMaxHealth(ptr2) == ptr2.getCellRef().mCharge)) return true; return false; } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, const Ptr& actorPtr) { - MWWorld::ContainerStoreIterator it = addImp(ptr); + MWWorld::ContainerStoreIterator it = addImp(itemPtr); MWWorld::Ptr item = *it; std::string script = MWWorld::Class::get(item).getScript(item); @@ -85,13 +106,13 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) 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); + + // Set OnPCAdd special variable, if it is declared + item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1); } else cell = player.getCell(); @@ -113,33 +134,28 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (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 = (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) + int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); + + 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 @@ -171,7 +187,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; @@ -182,24 +198,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) - addImp (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.empty()) + 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() @@ -272,7 +349,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()) @@ -291,6 +368,83 @@ int MWWorld::ContainerStore::getType (const Ptr& ptr) "Object of type " + ptr.getTypeName() + " can not be placed into a container"); } +MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) +{ + { + Ptr ptr = searchId (potions, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (appas, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (armors, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (books, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (clothes, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (ingreds, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (lights, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (lockpicks, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (miscItems, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (probes, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (repairs, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchId (weapons, id, this); + if (!ptr.isEmpty()) + return ptr; + } + + return Ptr(); +} + MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container) : mType (-1), mMask (0), mContainer (container) @@ -321,7 +475,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){} diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index e4f75d547..9a11f1603 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; @@ -53,6 +53,7 @@ namespace MWWorld 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: @@ -64,7 +65,7 @@ namespace MWWorld ContainerStoreIterator end(); - ContainerStoreIterator add (const Ptr& ptr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, const Ptr& actorPtr); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. @@ -78,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(); @@ -106,6 +106,8 @@ namespace MWWorld ///< This function throws an exception, if ptr does not point to an object, that can be /// put into a container. + Ptr search (const std::string& id); + friend class ContainerStoreIterator; }; @@ -127,7 +129,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; @@ -149,7 +151,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 257676076..7703f2d23 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -5,6 +5,8 @@ #include +#include + namespace MWWorld { @@ -21,20 +23,23 @@ static bool isCacheableRecord(int id) return false; } -void ESMStore::load(ESM::ESMReader &esm) +void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) { + listener->setProgressRange(1000); + std::set missing; 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 ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { - ESM::MasterData &mast = const_cast(masters[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; @@ -108,6 +113,7 @@ void ESMStore::load(ESM::ESMReader &esm) mIds[id] = n.val; } } + listener->setProgress(esm.getFileOffset() / (float)esm.getFileSize() * 1000); } /* This information isn't needed on screen. But keep the code around @@ -133,15 +139,6 @@ void ESMStore::setUp() mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); - - ESM::NPC item; - item.mId = "player"; - - const ESM::NPC *pIt = mNpcs.find("player"); - assert(pIt != NULL); - - mNpcs.insert(*pIt); - mNpcs.eraseStatic(pIt->mId); } } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index b9b95e4f4..ebb086cee 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -6,6 +6,11 @@ #include #include "store.hpp" +namespace Loading +{ + class Listener; +} + namespace MWWorld { class ESMStore @@ -32,7 +37,7 @@ namespace MWWorld Store mCreatureLists; Store mItemLists; Store mLights; - Store mLockpicks; + Store mLockpicks; Store mMiscItems; Store mNpcs; Store mNpcChange; @@ -67,6 +72,8 @@ namespace MWWorld std::map mIds; std::map mStores; + ESM::NPC mPlayerTemplate; + unsigned int mDynamicCount; public: @@ -141,7 +148,22 @@ namespace MWWorld mStores[ESM::REC_WEAP] = &mWeapons; } - void load(ESM::ESMReader &esm); + 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, Loading::Listener* listener); template const Store &get() const { @@ -171,6 +193,25 @@ namespace MWWorld return ptr; } + 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(); @@ -312,7 +353,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..7c8ef8c7b 100644 --- a/apps/openmw/mwworld/failedaction.cpp +++ b/apps/openmw/mwworld/failedaction.cpp @@ -7,15 +7,13 @@ namespace MWWorld { - FailedAction::FailedAction (const std::string& msg) : Action (false), message(msg) + FailedAction::FailedAction(const std::string &msg) + : Action(false), mMessage(msg) { } - - void FailedAction::executeImp (const Ptr& actor) + void FailedAction::executeImp(const Ptr &actor) { - if ( actor.getRefData().getHandle()=="player" && !(message.empty())) - { - MWBase::Environment::get().getWindowManager() ->messageBox(message, std::vector()); - } + if(actor.getRefData().getHandle() == "player" && !mMessage.empty()) + MWBase::Environment::get().getWindowManager()->messageBox(mMessage); } } diff --git a/apps/openmw/mwworld/failedaction.hpp b/apps/openmw/mwworld/failedaction.hpp index e736bfb63..c69d62023 100644 --- a/apps/openmw/mwworld/failedaction.hpp +++ b/apps/openmw/mwworld/failedaction.hpp @@ -8,12 +8,12 @@ namespace MWWorld { class FailedAction : public Action { - std::string message; - - virtual void executeImp (const Ptr& actor); + std::string mMessage; - public: - FailedAction (const std::string& message = std::string()); + virtual void executeImp(const Ptr &actor); + + public: + FailedAction(const std::string &message = std::string()); }; } 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 9e57910ee..a905f8aae 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -74,15 +74,6 @@ namespace MWWorld 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 diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index dd518ff6a..98cb6d347 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -64,6 +64,22 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor return *this; } +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, const Ptr& actorPtr) +{ + const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, actorPtr); + + // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves + if ((actorPtr.getRefData().getHandle() != "player") + && !(MWWorld::Class::get(actorPtr).getNpcStats(actorPtr).isWerewolf())) + { + std::string type = itemPtr.getTypeName(); + if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) + autoEquip(actorPtr); + } + + return retVal; +} + void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& iterator) { if (slot<0 || slot>=static_cast (mSlots.size())) @@ -110,6 +126,23 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite flagAsModified(); } +void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) +{ + for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator it = getSlot(slot); + if (it != end()) + { + equip(slot, 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((actor.getRefData().getHandle() == "player") && (script != "")) + (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + } + } +} + MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) { if (slot<0 || slot>=static_cast (mSlots.size())) @@ -128,8 +161,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 +219,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 +309,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..f0cba0f9f 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -65,6 +65,17 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); + virtual ContainerStoreIterator add (const Ptr& itemPtr, const Ptr& actorPtr); + ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) + /// Auto-equip items if specific conditions are fulfilled (see the implementation). + /// + /// \note The item pointed to is not required to exist beyond this function call. + /// + /// \attention Do not add items to an existing stack by increasing the count instead of + /// calling this function! + /// + /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. + void equip (int slot, const ContainerStoreIterator& iterator); ///< \note \a iterator can be an end-iterator @@ -78,7 +89,10 @@ namespace MWWorld ContainerStoreIterator getSlot (int slot); - void autoEquip (const MWMechanics::NpcStats& stats); + void unequipAll(const MWWorld::Ptr& actor); + ///< Unequip all currently equipped items. + + void autoEquip (const MWWorld::Ptr& npc); ///< Auto equip items according to stats and item value. const MWMechanics::MagicEffects& getMagicEffects(); @@ -90,8 +104,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..415351e78 --- /dev/null +++ b/apps/openmw/mwworld/livecellref.hpp @@ -0,0 +1,58 @@ +#ifndef GAME_MWWORLD_LIVECELLREF_H +#define GAME_MWWORLD_LIVECELLREF_H + +#include + +#include + +#include "refdata.hpp" + +namespace MWWorld +{ + class Ptr; + class ESMStore; + class Class; + + /// Used to create pointers to hold any type of LiveCellRef<> object. + struct LiveCellRefBase + { + const Class *mClass; + + /** Information about this instance, such as 3D location and rotation + * and individual type-dependent data. + */ + ESM::CellRef mRef; + + /** runtime-data */ + RefData mData; + + LiveCellRefBase(std::string type, const ESM::CellRef &cref=ESM::CellRef()); + /* Need this for the class to be recognized as polymorphic */ + virtual ~LiveCellRefBase() { } + }; + + /// 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 : public LiveCellRefBase + { + LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) + : LiveCellRefBase(typeid(X).name(), cref), mBase(b) + {} + + LiveCellRef(const X* b = NULL) + : LiveCellRefBase(typeid(X).name()), mBase(b) + {} + + // The object that this instance is based on. + const X* mBase; + }; + +// template bool operator==(const LiveCellRef& ref, int pRefnum); +} + +#endif diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 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 7edd20293..433fe0892 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -15,9 +15,12 @@ #include -//#include "../mwbase/world.hpp" // FIXME +#include "../mwbase/world.hpp" // FIXME #include "../mwbase/environment.hpp" +#include +#include "../mwworld/esmstore.hpp" + #include "ptr.hpp" #include "class.hpp" @@ -26,70 +29,84 @@ namespace MWWorld { static const float sMaxSlope = 60.0f; - static const float sStepSize = 30.0f; + static const float sStepSize = 32.0f; // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. - static const int sMaxIterations = 50; + static const int sMaxIterations = 8; class MovementSolver { private: - static bool stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime, - const Ogre::Vector3 &halfExtents, bool isInterior, + static float getSlope(const Ogre::Vector3 &normal) + { + return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees(); + } + + static bool stepMove(btCollisionObject *colobj, Ogre::Vector3 &position, + const Ogre::Vector3 &velocity, float &remainingTime, OEngine::Physic::PhysicEngine *engine) { - traceResults trace; // no initialization needed + OEngine::Physic::ActorTracer tracer, stepper; - newtrace(&trace, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), - halfExtents, isInterior, engine); - if(trace.fraction == 0.0f) + stepper.doTrace(colobj, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), engine); + if(stepper.mFraction < std::numeric_limits::epsilon()) return false; - newtrace(&trace, trace.endpos, trace.endpos + velocity*remainingTime, - halfExtents, isInterior, engine); - if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope)) + tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + velocity*remainingTime, engine); + if(tracer.mFraction < std::numeric_limits::epsilon()) return false; - newtrace(&trace, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine); - if(getSlope(trace.planenormal) <= sMaxSlope) + stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-Ogre::Vector3(0.0f,0.0f,sStepSize), engine); + if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) { // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. - position = trace.endpos; + position = stepper.mEndPos; + remainingTime *= (1.0f-tracer.mFraction); 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; + ///Project a vector u on another vector v + static inline Ogre::Vector3 project(const Ogre::Vector3 u, const Ogre::Vector3 &v) + { + return v * u.dotProduct(v); } - static void projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction) + ///Helper for computing the character sliding + static inline Ogre::Vector3 slide(Ogre::Vector3 direction, const Ogre::Vector3 &planeNormal) { - Ogre::Vector3 normalizedDirection(direction); - normalizedDirection.normalise(); - - // no divide by normalizedDirection.length necessary because it's normalized - velocity = normalizedDirection * velocity.dotProduct(normalizedDirection); + return direction - project(direction, planeNormal); } - static float getSlope(const Ogre::Vector3 &normal) + + public: + static Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, OEngine::Physic::PhysicEngine *engine) { - return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees(); + const ESM::Position &refpos = ptr.getRefData().getPosition(); + Ogre::Vector3 position(refpos.pos); + + OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + if (!physicActor) + return position; + + const int maxHeight = 200.f; + OEngine::Physic::ActorTracer tracer; + tracer.findGround(physicActor->getCollisionBody(), position, position-Ogre::Vector3(0,0,maxHeight), engine); + if(tracer.mFraction >= 1.0f) + { + physicActor->setOnGround(false); + return position; + } + + physicActor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope); + + return tracer.mEndPos; } - public: static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, - bool gravity, OEngine::Physic::PhysicEngine *engine) + bool isFlying, float waterlevel, OEngine::Physic::PhysicEngine *engine) { const ESM::Position &refpos = ptr.getRefData().getPosition(); Ogre::Vector3 position(refpos.pos); @@ -99,105 +116,130 @@ namespace MWWorld 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; + 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 * time; } - 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); + btCollisionObject *colobj = physicActor->getCollisionBody(); + Ogre::Vector3 halfExtents = physicActor->getHalfExtents(); + position.z += halfExtents.z; + + waterlevel -= halfExtents.z * 0.5; + OEngine::Physic::ActorTracer tracer; + bool wasOnGround = false; + bool isOnGround = false; + Ogre::Vector3 inertia(0.0f); Ogre::Vector3 velocity; - if(!gravity) + if(position.z < waterlevel || isFlying) { - 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; + 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; } else { + velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z) * movement; + if(!physicActor->getOnGround()) + { + // If falling, add part of the incoming velocity with the current inertia + velocity = velocity*time + physicActor->getInertialForce(); + } + inertia = velocity; + if(!(movement.z > 0.0f)) { - newtrace(&trace, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine); - if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope) - onground = true; + wasOnGround = physicActor->getOnGround(); + tracer.doTrace(colobj, position, position-Ogre::Vector3(0,0,2), engine); + if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) + isOnGround = 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(isOnGround) { - // 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); + // if we're on the ground, don't try to fall + velocity.z = std::max(0.0f, velocity.z); } - const Ogre::Vector3 up(0.0f, 0.0f, 1.0f); Ogre::Vector3 newPosition = position; - int iterations = 0; - do { + float remainingTime = time; + for(int iterations = 0;iterations < sMaxIterations && remainingTime > 0.01f;++iterations) + { + Ogre::Vector3 nextpos = newPosition + velocity*remainingTime; + + if(newPosition.z < waterlevel && !isFlying && + nextpos.z > waterlevel && newPosition.z <= waterlevel) + { + const Ogre::Vector3 down(0,0,-1); + Ogre::Real movelen = velocity.normalise(); + Ogre::Vector3 reflectdir = velocity.reflect(down); + reflectdir.normalise(); + velocity = slide(reflectdir, down)*movelen; + continue; + } + // trace to where character would go if there were no obstructions - newtrace(&trace, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, isInterior, engine); - newPosition = trace.endpos; - remainingTime = remainingTime * (1.0f-trace.fraction); + tracer.doTrace(colobj, newPosition, nextpos, engine); // check for obstructions - if(trace.fraction < 1.0f) + if(tracer.mFraction >= 1.0f) { - //std::cout<<"angle: "< 0.0f); + // We hit something. Try to step up onto it. + if(stepMove(colobj, newPosition, velocity, remainingTime, engine)) + isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity + else + { + // Can't move this way, try to find another spot along the plane + Ogre::Real movelen = velocity.normalise(); + Ogre::Vector3 reflectdir = velocity.reflect(tracer.mPlaneNormal); + reflectdir.normalise(); + velocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + + // Do not allow sliding upward if there is gravity. Stepping will have taken + // care of that. + if(!(newPosition.z < waterlevel || isFlying)) + velocity.z = std::min(velocity.z, 0.0f); + } + } - if(onground) + if(isOnGround || wasOnGround) { - newtrace(&trace, 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; + tracer.doTrace(colobj, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+2.0f), engine); + if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) + { + newPosition.z = tracer.mEndPos.z + 1.0f; + isOnGround = true; + } else - onground = false; + isOnGround = false; } - physicActor->setOnGround(onground); - physicActor->setVerticalForce(!onground ? clippedVelocity.z - time*627.2f : 0.0f); - physicActor->enableCollisions(true); + + if(isOnGround || newPosition.z < waterlevel || isFlying) + physicActor->setInertialForce(Ogre::Vector3(0.0f)); + else + { + inertia.z += time*-627.2f; + physicActor->setInertialForce(inertia); + } + physicActor->setOnGround(isOnGround); + + newPosition.z -= halfExtents.z; return newPosition; } }; PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : - mRender(_rend), mEngine(0) + mRender(_rend), mEngine(0), mTimeAccum(0.0f) { // Create physics. shapeLoader is deleted by the physic engine NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); @@ -214,38 +256,30 @@ namespace MWWorld return mEngine; } - std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) + std::pair PhysicsSystem::getFacedHandle(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.setX(-dir.x()); + Ray ray = mRender.getCamera()->getCameraToViewportRay(0.5, 0.5); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); - origin += dir * 5; + Ogre::Vector3 origin_ = ray.getOrigin(); + btVector3 origin(origin_.x, origin_.y, origin_.z); + Ogre::Vector3 dir_ = ray.getDirection().normalisedCopy(); + btVector3 dir(dir_.x, dir_.y, dir_.z); btVector3 dest = origin + dir * queryDistance; - std::pair result; - /*auto*/ result = mEngine->rayTest(origin, dest); + std::pair result = mEngine->rayTest(origin, dest); result.second *= queryDistance; + return std::make_pair (result.second, result.first); } 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.setX(-dir.x()); + Ray ray = mRender.getCamera()->getCameraToViewportRay(0.5, 0.5); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); - origin += dir * 5; + Ogre::Vector3 origin_ = ray.getOrigin(); + btVector3 origin(origin_.x, origin_.y, origin_.z); + Ogre::Vector3 dir_ = ray.getDirection().normalisedCopy(); + btVector3 dir(dir_.x, dir_.y, dir_.z); btVector3 dest = origin + dir * queryDistance; std::vector < std::pair > results; @@ -274,37 +308,42 @@ namespace MWWorld return results; } - void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight) + std::pair PhysicsSystem::getHitContact(const std::string &name, + const Ogre::Vector3 &origin, + const Ogre::Quaternion &orient, + float queryDistance) { - // TODO: store and use + const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); + + btConeShape shape(Ogre::Degree(store.find("fCombatAngleXY")->getFloat()/2.0f).valueRadians(), + queryDistance); + shape.setLocalScaling(btVector3(1, 1, Ogre::Degree(store.find("fCombatAngleZ")->getFloat()/2.0f).valueRadians() / + shape.getRadius())); + + // The shape origin is its center, so we have to move it forward by half the length. The + // real origin will be provided to getFilteredContact to find the closest. + Ogre::Vector3 center = origin + (orient * Ogre::Vector3(0.0f, queryDistance*0.5f, 0.0f)); + + btCollisionObject object; + object.setCollisionShape(&shape); + object.setWorldTransform(btTransform(btQuaternion(orient.x, orient.y, orient.z, orient.w), + btVector3(center.x, center.y, center.z))); + + std::pair result = mEngine->getFilteredContact( + name, btVector3(origin.x, origin.y, origin.z), &object); + if(!result.first) + return std::make_pair(std::string(), Ogre::Vector3(&result.second[0])); + return std::make_pair(result.first->mName, Ogre::Vector3(&result.second[0])); } - btVector3 PhysicsSystem::getRayPoint(float extent) - { - //get a ray pointing to the center of the viewport - Ray centerRay = mRender.getCamera()->getCameraToViewportRay( - mRender.getViewport()->getWidth()/2, - mRender.getViewport()->getHeight()/2); - btVector3 result(centerRay.getPoint(extent).x,centerRay.getPoint(extent).y,centerRay.getPoint(extent).z); - return result; - } - btVector3 PhysicsSystem::getRayPoint(float extent, float mouseX, float mouseY) - { - //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).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 == ""); } @@ -318,7 +357,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)); @@ -346,11 +385,15 @@ namespace MWWorld } } - Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity) + std::vector PhysicsSystem::getCollisions(const Ptr &ptr) { - return MovementSolver::move(ptr, movement, time, gravity, mEngine); + return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName()); } + Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr) + { + return MovementSolver::traceDown(ptr, mEngine); + } void PhysicsSystem::addHeightField (float* heights, int x, int y, float yoffset, @@ -364,14 +407,15 @@ 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()); - OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody - (mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true); + 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); } @@ -385,8 +429,6 @@ namespace MWWorld void PhysicsSystem::removeObject (const std::string& handle) { - //TODO:check if actor??? - mEngine->removeCharacter(handle); mEngine->removeRigidBody(handle); mEngine->deleteRigidBody(handle); @@ -440,8 +482,13 @@ namespace MWWorld 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)) @@ -450,7 +497,7 @@ namespace MWWorld bool PhysicsSystem::toggleCollisionMode() { - 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") { @@ -494,10 +541,48 @@ namespace MWWorld return true; } - void PhysicsSystem::updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw) + + void PhysicsSystem::queueObjectMovement(const Ptr &ptr, const Ogre::Vector3 &movement) + { + PtrVelocityList::iterator iter = mMovementQueue.begin(); + for(;iter != mMovementQueue.end();iter++) + { + if(iter->first == ptr) + { + iter->second = movement; + return; + } + } + + mMovementQueue.push_back(std::make_pair(ptr, movement)); + } + + const PtrVelocityList& PhysicsSystem::applyQueuedMovement(float dt) { - mPlayerData.eyepos = eyepos; - mPlayerData.pitch = pitch; - mPlayerData.yaw = yaw; + mMovementResults.clear(); + + mTimeAccum += dt; + if(mTimeAccum >= 1.0f/60.0f) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + PtrVelocityList::iterator iter = mMovementQueue.begin(); + for(;iter != mMovementQueue.end();iter++) + { + float waterlevel = -std::numeric_limits::max(); + const ESM::Cell *cell = iter->first.getCell()->mCell; + if(cell->hasWater()) + waterlevel = cell->mWater; + + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, + world->isFlying(iter->first), + waterlevel, mEngine); + mMovementResults.push_back(std::make_pair(iter->first, newpos)); + } + + mTimeAccum = 0.0f; + } + mMovementQueue.clear(); + + return mMovementResults; } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 60c8246ae..3dcd088f5 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -5,6 +5,8 @@ #include +#include "ptr.hpp" + namespace OEngine { @@ -21,7 +23,8 @@ namespace OEngine namespace MWWorld { class World; - class Ptr; + + typedef std::vector > PtrVelocityList; class PhysicsSystem { @@ -29,7 +32,7 @@ namespace MWWorld PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); - void addObject (const MWWorld::Ptr& ptr); + void addObject (const MWWorld::Ptr& ptr, bool placeable=false); void addActor (const MWWorld::Ptr& ptr); @@ -49,19 +52,20 @@ namespace MWWorld void scaleObject (const MWWorld::Ptr& ptr); bool toggleCollisionMode(); - - Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity); - std::pair getFacedHandle (MWWorld::World& world, float queryDistance); + std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with + Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); + + std::pair getFacedHandle(float queryDistance); + std::pair getHitContact(const std::string &name, + const Ogre::Vector3 &origin, + const Ogre::Quaternion &orientation, + float queryDistance); std::vector < std::pair > getFacedHandles (float queryDistance); std::vector < std::pair > getFacedHandles (float mouseX, float mouseY, float queryDistance); - btVector3 getRayPoint(float extent); - 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); @@ -71,22 +75,25 @@ namespace MWWorld OEngine::Physic::PhysicEngine* getEngine(); - void setCurrentWater(bool hasWater, int waterHeight); - bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); - void updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw); + /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will + /// be overwritten. Valid until the next call to applyQueuedMovement. + void queueObjectMovement(const Ptr &ptr, const Ogre::Vector3 &velocity); + + const PtrVelocityList& applyQueuedMovement(float dt); private: - struct { - Ogre::Vector3 eyepos; - float pitch, yaw; - } mPlayerData; OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; std::map handleToMesh; + PtrVelocityList mMovementQueue; + PtrVelocityList mMovementResults; + + float mTimeAccum; + PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); }; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 03dd1abc7..e26c2e2a5 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,14 +1,17 @@ #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 "esmstore.hpp" #include "class.hpp" namespace MWWorld @@ -25,12 +28,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 +79,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,14 +98,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::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) @@ -77,20 +114,30 @@ namespace MWWorld 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); - MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); + // TODO show sneak indicator only when the player is not detected by any actor + MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); } - void Player::setSneak(bool sneak) + void Player::yaw(float yaw) { MWWorld::Ptr ptr = getPlayer(); - - MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Sneak, sneak); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[2] += yaw; + } + void Player::pitch(float pitch) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[0] += pitch; + } + void Player::roll(float roll) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[1] += roll; } MWMechanics::DrawState_ Player::getDrawState() diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 5f2fc3a08..d78b1901c 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 @@ -66,8 +59,11 @@ namespace MWWorld void setUpDown(int value); void setRunState(bool run); - void toggleRunning(); void setSneak(bool sneak); + + void yaw(float yaw); + void pitch(float pitch); + void roll(float roll); }; } #endif diff --git a/apps/openmw/mwworld/ptr.cpp b/apps/openmw/mwworld/ptr.cpp index 7c0010c04..127ab1364 100644 --- a/apps/openmw/mwworld/ptr.cpp +++ b/apps/openmw/mwworld/ptr.cpp @@ -4,25 +4,41 @@ #include #include "containerstore.hpp" +#include "class.hpp" + + +/* This shouldn't really be here. */ +MWWorld::LiveCellRefBase::LiveCellRefBase(std::string type, const ESM::CellRef &cref) + : mClass(&Class::get(type)), mRef(cref), mData(mRef) +{ +} + + +const std::string& MWWorld::Ptr::getTypeName() const +{ + if(mRef != 0) + return mRef->mClass->getTypeName(); + throw std::runtime_error("Can't get type name from an empty object."); +} ESM::CellRef& MWWorld::Ptr::getCellRef() const { - assert (mCellRef); + assert(mRef); if (mContainerStore) mContainerStore->flagAsModified(); - return *mCellRef; + return mRef->mRef; } MWWorld::RefData& MWWorld::Ptr::getRefData() const { - assert (mRefData); + assert(mRef); if (mContainerStore) mContainerStore->flagAsModified(); - return *mRefData; + return mRef->mData; } void MWWorld::Ptr::setContainerStore (ContainerStore *store) diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index d97ebcc6e..e5352da28 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -1,9 +1,8 @@ #ifndef GAME_MWWORLD_PTR_H #define GAME_MWWORLD_PTR_H -#include - #include "cellstore.hpp" +#include "livecellref.hpp" namespace MWWorld { @@ -18,48 +17,42 @@ namespace MWWorld typedef MWWorld::CellStore CellStore; ///< \deprecated - boost::any mPtr; - ESM::CellRef *mCellRef; - RefData *mRefData; + MWWorld::LiveCellRefBase *mRef; CellStore *mCell; - std::string mTypeName; ContainerStore *mContainerStore; public: - - Ptr() : mCellRef (0), mRefData (0), mCell (0), mContainerStore (0) {} - - bool isEmpty() const + Ptr(MWWorld::LiveCellRefBase *liveCellRef=0, CellStore *cell=0) + : mRef(liveCellRef), mCell(cell), mContainerStore(0) { - return mPtr.empty(); } - const std::type_info& getType() const + bool isEmpty() const { - assert (!mPtr.empty()); - return mPtr.type(); + return mRef == 0; } - const std::string& getTypeName() const - { - return mTypeName; - } + const std::string& getTypeName() const; - template - Ptr (MWWorld::LiveCellRef *liveCellRef, CellStore *cell) - : mContainerStore (0) + const Class& getClass() const { - mPtr = liveCellRef; - mCellRef = &liveCellRef->mRef; - mRefData = &liveCellRef->mData; - mCell = cell; - mTypeName = typeid (T).name(); + if(mRef != 0) + return *(mRef->mClass); + throw std::runtime_error("Cannot get class of an empty object"); } template MWWorld::LiveCellRef *get() const { - return boost::any_cast*> (mPtr); + MWWorld::LiveCellRef *ref = dynamic_cast*>(mRef); + if(ref) return ref; + + std::stringstream str; + str<< "Bad LiveCellRef cast to "<= (const Ptr& left, const Ptr& right) diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 4be287810..c1a3ae785 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -19,6 +19,7 @@ namespace MWWorld mEnabled = refData.mEnabled; mCount = refData.mCount; mPosition = refData.mPosition; + mLocalRotation = refData.mLocalRotation; mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; } @@ -34,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) @@ -76,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(); } @@ -141,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 592fb5c80..0c98ca504 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,7 +1,11 @@ #include "scene.hpp" +#include + #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" @@ -21,13 +25,12 @@ namespace template void insertCellRefList(MWRender::RenderingManager& rendering, - T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics, bool rescale) + T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics, bool rescale, Loading::Listener* loadingListener) { if (!cellRefList.mList.empty()) { const MWWorld::Class& class_ = MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.mList.begin(), &cell)); - int current = 0; for (typename T::List::iterator it = cellRefList.mList.begin(); it != cellRefList.mList.end(); it++) { @@ -39,9 +42,7 @@ namespace it->mRef.mScale = 2; } - ++current; - - if (it->mData.getCount() || it->mData.isEnabled()) + if (it->mData.getCount() && it->mData.isEnabled()) { MWWorld::Ptr ptr (&*it, &cell); @@ -49,8 +50,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) { @@ -58,6 +65,8 @@ namespace std::cerr << error + e.what() << std::endl; } } + + loadingListener->increaseProgress(1); } } } @@ -75,31 +84,31 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListHandles functor; + ListAndResetHandles functor; - (*iter)->forEach(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()->drop (*iter); @@ -107,7 +116,7 @@ namespace MWWorld mActiveCells.erase(*iter); } - void Scene::loadCell (Ptr::CellStore *cell) + void Scene::loadCell (Ptr::CellStore *cell, Loading::Listener* loadingListener) { // register local scripts MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); @@ -115,14 +124,10 @@ namespace MWWorld if(result.second) { - /// \todo rescale depending on the state of a new GMST - insertCell (*cell, true); - - 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 = @@ -142,6 +147,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, loadingListener); + + mRendering.cellAdded (cell); + mRendering.configureAmbient(*cell); mRendering.requestMap(cell); mRendering.configureAmbient(*cell); @@ -150,13 +161,11 @@ namespace MWWorld void Scene::playerCellChange(MWWorld::CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) { - bool hasWater = cell->mCell->mData.mFlags & ESM::Cell::HasWater; - mPhysics->setCurrentWater(hasWater, cell->mCell->mWater); - MWBase::World *world = MWBase::Environment::get().getWorld(); world->getPlayer().setCell(cell); MWWorld::Ptr player = world->getPlayer().getPlayer(); + mRendering.updatePlayerPtr(player); if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); @@ -165,6 +174,8 @@ 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 = @@ -176,20 +187,27 @@ namespace MWWorld 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); + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); // remove active MWBase::Environment::get().getMechanicsManager()->remove(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - std::string loadingExteriorText; - - loadingExteriorText = gmst.find ("sLoadingMessage3")->getString(); + std::string loadingExteriorText = "#{sLoadingMessage3}"; + loadingListener->setLabel(loadingExteriorText); CellStoreCollection::iterator active = mActiveCells.begin(); @@ -211,7 +229,6 @@ namespace MWWorld ++numUnload; } - int current = 0; active = mActiveCells.begin(); while (active!=mActiveCells.end()) { @@ -226,11 +243,10 @@ namespace MWWorld } } unloadCell (active++); - ++current; } - int numLoad = 0; - // get the number of cells to load + int refsToLoad = 0; + // get the number of refs to load for (int x=X-1; x<=X+1; ++x) for (int y=Y-1; y<=Y+1; ++y) { @@ -248,11 +264,12 @@ namespace MWWorld } if (iter==mActiveCells.end()) - ++numLoad; + refsToLoad += countRefs(*MWBase::Environment::get().getWorld()->getExterior(x, y)); } + loadingListener->setProgressRange(refsToLoad); + // Load cells - current = 0; for (int x=X-1; x<=X+1; ++x) for (int y=Y-1; y<=Y+1; ++y) { @@ -273,11 +290,7 @@ namespace MWWorld { CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); - //Loading Exterior loading text - MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingExteriorText, 0, current, numLoad); - - loadCell (cell); - ++current; + loadCell (cell, loadingListener); } } @@ -309,7 +322,7 @@ namespace MWWorld mCellChanged = true; - MWBase::Environment::get().getWindowManager ()->loadingDone (); + loadingListener->removeWallpaper(); } //We need the ogre renderer and a scene node. @@ -334,12 +347,15 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.5); - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + mRendering.enableTerrain(false); - std::string loadingInteriorText; - loadingInteriorText = gmst.find ("sLoadingMessage2")->getString(); + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + + std::string loadingInteriorText = "#{sLoadingMessage2}"; + loadingListener->setLabel(loadingInteriorText); CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); bool loadcell = (mCurrentCell == NULL); @@ -355,6 +371,9 @@ 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()); + world->getFader()->fadeIn(0.5f); return; } @@ -380,13 +399,15 @@ namespace MWWorld ++current; } + int refsToLoad = countRefs(*cell); + loadingListener->setProgressRange(refsToLoad); + // Load cell. std::cout << "cellName: " << cell->mCell->mName << std::endl; //Loading Interior loading text - MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingInteriorText, 0, 0, 1); - loadCell (cell); + loadCell (cell, loadingListener); mCurrentCell = cell; @@ -401,8 +422,9 @@ namespace MWWorld MWBase::Environment::get().getWorld()->adjustSky(); mCellChanged = true; + MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.5); - MWBase::Environment::get().getWindowManager ()->loadingDone (); + loadingListener->removeWallpaper(); } void Scene::changeToExteriorCell (const ESM::Position& position) @@ -412,6 +434,8 @@ namespace MWWorld MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); + mRendering.enableTerrain(true); + changeCell (x, y, position, true); } @@ -425,29 +449,54 @@ namespace MWWorld mCellChanged = false; } - void Scene::insertCell (Ptr::CellStore &cell, bool rescale) + int Scene::countRefs (const Ptr::CellStore& cell) + { + return cell.mActivators.mList.size() + + cell.mPotions.mList.size() + + cell.mAppas.mList.size() + + cell.mArmors.mList.size() + + cell.mBooks.mList.size() + + cell.mClothes.mList.size() + + cell.mContainers.mList.size() + + cell.mDoors.mList.size() + + cell.mIngreds.mList.size() + + cell.mCreatureLists.mList.size() + + cell.mItemLists.mList.size() + + cell.mLights.mList.size() + + cell.mLockpicks.mList.size() + + cell.mMiscItems.mList.size() + + cell.mProbes.mList.size() + + cell.mRepairs.mList.size() + + cell.mStatics.mList.size() + + cell.mWeapons.mList.size() + + cell.mCreatures.mList.size() + + cell.mNpcs.mList.size(); + } + + void Scene::insertCell (Ptr::CellStore &cell, bool rescale, Loading::Listener* loadingListener) { // Loop through all references in the cell - 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.mCreatures, 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.mNpcs, 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); + insertCellRefList(mRendering, cell.mActivators, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mPotions, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mAppas, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mArmors, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mBooks, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mClothes, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mItemLists, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mLights, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mMiscItems, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mProbes, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mRepairs, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mStatics, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mWeapons, cell, *mPhysics, rescale, loadingListener); + // Load NPCs and creatures _after_ everything else (important for adjustPosition to work correctly) + insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale, loadingListener); + insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale, loadingListener); } void Scene::addObjectToScene (const Ptr& ptr) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index dc08d6f9b..e3edad352 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -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,7 +56,9 @@ namespace MWWorld void playerCellChange (CellStore *cell, const ESM::Position& position, bool adjustPlayerPos = true); - void insertCell (Ptr::CellStore &cell, bool rescale); + void insertCell (Ptr::CellStore &cell, bool rescale, Loading::Listener* loadingListener); + + int countRefs (const Ptr::CellStore& cell); public: @@ -66,7 +68,7 @@ namespace MWWorld void unloadCell (CellStoreCollection::iterator iter); - void loadCell (CellStore *cell); + void loadCell (CellStore *cell, Loading::Listener* loadingListener); void changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos); ///< Move from exterior to interior or from interior cell to a different @@ -85,6 +87,9 @@ namespace MWWorld void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. + void changeToVoid(); + ///< Change into a void + void markCellAsUnchanged(); void update (float duration, bool paused); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 005601cd1..512883f1a 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1,4 +1,5 @@ #include "store.hpp" +#include "esmstore.hpp" namespace MWWorld { @@ -15,8 +16,39 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) ESM::Cell *cell = new ESM::Cell; cell->mName = id; - // The cell itself takes care of some of the hairy details - cell->load(esm, *mEsmStore); + //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) { @@ -42,7 +74,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // 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++) { + 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()) { @@ -62,4 +94,4 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) delete cell; } -} \ No newline at end of file +} diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index f69f606b4..2ee23dbd6 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -21,6 +21,7 @@ namespace MWWorld virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; virtual bool eraseStatic(const std::string &id) {return false;} + virtual void clearDynamic() {} }; template @@ -48,7 +49,7 @@ namespace MWWorld SharedIterator operator++(int) { SharedIterator iter = *this; - mIter++; + ++mIter; return iter; } @@ -60,7 +61,7 @@ namespace MWWorld SharedIterator operator--(int) { SharedIterator iter = *this; - mIter--; + --mIter; return iter; } @@ -92,6 +93,24 @@ namespace MWWorld std::map mDynamic; typedef std::map Dynamic; + typedef std::map Static; + + class GetRecords { + const std::string mFind; + std::vector *mRecords; + + public: + GetRecords(const std::string &str, std::vector *records) + : mFind(Misc::StringUtils::lowerCase(str)), mRecords(records) + { } + + void operator()(const T *item) + { + if(Misc::StringUtils::ciCompareLen(mFind, item->mId, mFind.size()) == 0) + mRecords->push_back(item); + } + }; + friend class ESMStore; @@ -105,6 +124,13 @@ 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); @@ -123,6 +149,16 @@ namespace MWWorld return 0; } + /** Returns a random record that starts with the named ID, or NULL if not found. */ + const T *searchRandom(const std::string &id) const + { + std::vector results; + std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); + if(!results.empty()) + return results[int(std::rand()/((double)RAND_MAX+1)*results.size())]; + return NULL; + } + const T *find(const std::string &id) const { const T *ptr = search(id); if (ptr == 0) { @@ -133,6 +169,20 @@ namespace MWWorld return ptr; } + /** Returns a random record that starts with the named ID. An exception is thrown if none + * are found. */ + const T *findRandom(const std::string &id) const + { + const T *ptr = searchRandom(id); + if(ptr == 0) + { + std::ostringstream msg; + msg << "Object starting with '"< 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); @@ -250,6 +314,14 @@ namespace MWWorld mStatic[scpt.mId] = scpt; } + template <> + inline void Store::load(ESM::ESMReader &esm, const std::string &id) { + ESM::StartScript s; + s.load(esm); + s.mId = Misc::StringUtils::toLower(s.mScript); + mStatic[s.mId] = s; + } + template <> class Store : public StoreBase { @@ -394,6 +466,18 @@ namespace MWWorld ESM::Land *ptr = new ESM::Land(); ptr->load(esm); + // Same area defined in multiple plugins? -> last plugin wins + // Can't use search() because we aren't sorted yet - is there any other way to speed this up? + for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) + { + if ((*it)->mX == ptr->mX && (*it)->mY == ptr->mY) + { + delete *it; + mStatic.erase(it); + break; + } + } + mStatic.push_back(ptr); } @@ -415,8 +499,18 @@ namespace MWWorld } }; - typedef std::map DynamicInt; - typedef std::map, ESM::Cell> DynamicExt; + 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; @@ -465,7 +559,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -483,7 +577,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -524,7 +618,7 @@ namespace MWWorld void setUp() { //typedef std::vector::iterator Iterator; - typedef std::map, ESM::Cell>::iterator ExtIterator; + typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; //std::sort(mInt.begin(), mInt.end(), RecordCmp()); @@ -862,7 +956,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 { diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index c74150bf8..6f5dbe23f 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,10 +1,6 @@ #include "weather.hpp" -#include -#include - #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" @@ -13,6 +9,7 @@ #include "player.hpp" #include "esmstore.hpp" +#include "fallback.hpp" using namespace Ogre; using namespace MWWorld; @@ -24,97 +21,105 @@ namespace { 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; } } -std::string WeatherManager::getFallback (const std::string& key) const -{ - std::map::const_iterator it; - if((it = mFallback.find(key)) == mFallback.end()) - { - return ""; - } - return it->second; -} -std::string WeatherManager::getFallbackString(const std::string& fall) const +void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name) { - return getFallback(fall); + 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; } -float WeatherManager::getFallbackFloat(const std::string& fall) const -{ - std::string fallback=getFallbackString(fall); - return boost::lexical_cast(fallback); -} -ColourValue WeatherManager::getFallbackColour(const std::string& fall) const +float WeatherManager::calculateHourFade (const std::string& moonName) const { - std::string sum; - std::string ret[3]; - sum=getFallback(fall); - 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); + 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; } -void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name) +float WeatherManager::calculateAngleFade (const std::string& moonName, float angle) const { - std::string upper=name; - upper[0]=toupper(name[0]); - weather.mCloudsMaximumPercent = getFallbackFloat("Weather_"+upper+"_Clouds_Maximum_Percent"); - weather.mTransitionDelta = getFallbackFloat("Weather_"+upper+"_Transition_Delta"); - weather.mSkySunriseColor=getFallbackColour("Weather_"+upper+"_Sky_Sunrise_Color"); - weather.mSkyDayColor = getFallbackColour("Weather_"+upper+"_Sky_Day_Color"); - weather.mSkySunsetColor = getFallbackColour("Weather_"+upper+"_Sky_Sunset_Color"); - weather.mSkyNightColor = getFallbackColour("Weather_"+upper+"_Sky_Night_Color"); - weather.mFogSunriseColor = getFallbackColour("Weather_"+upper+"_Fog_Sunrise_Color"); - weather.mFogDayColor = getFallbackColour("Weather_"+upper+"_Fog_Day_Color"); - weather.mFogSunsetColor = getFallbackColour("Weather_"+upper+"_Fog_Sunset_Color"); - weather.mFogNightColor = getFallbackColour("Weather_"+upper+"_Fog_Night_Color"); - weather.mAmbientSunriseColor = getFallbackColour("Weather_"+upper+"_Ambient_Sunrise_Color"); - weather.mAmbientDayColor = getFallbackColour("Weather_"+upper+"_Ambient_Day_Color"); - weather.mAmbientSunsetColor = getFallbackColour("Weather_"+upper+"_Ambient_Sunset_Color"); - weather.mAmbientNightColor = getFallbackColour("Weather_"+upper+"_Ambient_Night_Color"); - weather.mSunSunriseColor = getFallbackColour("Weather_"+upper+"_Sun_Sunrise_Color"); - weather.mSunDayColor = getFallbackColour("Weather_"+upper+"_Sun_Day_Color"); - weather.mSunSunsetColor = getFallbackColour("Weather_"+upper+"_Sun_Sunset_Color"); - weather.mSunNightColor = getFallbackColour("Weather_"+upper+"_Sun_Night_Color"); - weather.mSunDiscSunsetColor = getFallbackColour("Weather_"+upper+"_Sun_Disc_Sunset_Color"); - weather.mLandFogDayDepth = getFallbackFloat("Weather_"+upper+"_Land_Fog_Day_Depth"); - weather.mLandFogNightDepth = getFallbackFloat("Weather_"+upper+"_Land_Fog_Night_Depth"); - weather.mWindSpeed = getFallbackFloat("Weather_"+upper+"_Wind_Speed"); - weather.mCloudSpeed = getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); - weather.mGlareView = getFallbackFloat("Weather_"+upper+"_Glare_View"); - mWeatherSettings[name] = weather; + 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,const std::map& fallbackMap) : - mHour(14), mCurrentWeather("clear"), mFirstUpdate(true), mWeatherUpdateTime(0), - mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), - mRemainingTransitionTime(0), mMonth(0), mDay(0), - mTimePassed(0), mFallback(fallbackMap) +WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fallback* fallback) : + mHour(14), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true), + mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), + mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), + mMonth(0), mDay(0), mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), + mRendering(rendering) { - mRendering = rendering; //Globals - mThunderSoundID0 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); - mThunderSoundID1 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_1"); - mThunderSoundID2 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_2"); - mThunderSoundID3 = getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_3"); - mSunriseTime = getFallbackFloat("Weather_Sunrise_Time"); - mSunsetTime = getFallbackFloat("Weather_Sunset_Time"); - mSunriseDuration = getFallbackFloat("Weather_Sunrise_Duration"); - mSunsetDuration = getFallbackFloat("Weather_Sunset_Duration"); - mHoursBetweenWeatherChanges = getFallbackFloat("Weather_Hours_Between_Weather_Changes"); - mWeatherUpdateTime = mHoursBetweenWeatherChanges*3600; - mThunderFrequency = getFallbackFloat("Weather_Thunderstorm_Thunder_Frequency"); - mThunderThreshold = getFallbackFloat("Weather_Thunderstorm_Thunder_Threshold"); + 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"; @@ -152,64 +157,19 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,const std:: blight.mAmbientLoopSoundID = "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"); +} + +WeatherManager::~WeatherManager() +{ + stopSounds(true); } void WeatherManager::setWeather(const String& weather, bool instant) @@ -230,135 +190,131 @@ 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; } -WeatherResult WeatherManager::getResult(const String& weather) +void WeatherManager::setResult(const String& weatherType) { - const Weather& current = mWeatherSettings[weather]; - WeatherResult result; + const Weather& current = mWeatherSettings[weatherType]; - result.mCloudTexture = current.mCloudTexture; - result.mCloudBlendFactor = 0; - result.mCloudOpacity = current.mCloudsMaximumPercent; - result.mWindSpeed = current.mWindSpeed; - result.mCloudSpeed = current.mCloudSpeed; - result.mGlareView = current.mGlareView; - result.mAmbientLoopSoundID = current.mAmbientLoopSoundID; - result.mSunColor = current.mSunDiscSunsetColor; + mResult.mCloudTexture = current.mCloudTexture; + mResult.mCloudBlendFactor = 0; + mResult.mCloudOpacity = current.mCloudsMaximumPercent; + mResult.mWindSpeed = current.mWindSpeed; + mResult.mCloudSpeed = current.mCloudSpeed; + mResult.mGlareView = current.mGlareView; + mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; + mResult.mSunColor = current.mSunDiscSunsetColor; - result.mNight = (mHour < 6 || mHour > 19); + mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); - result.mFogDepth = result.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; + mResult.mFogDepth = mResult.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; - result.mSunColor = current.mSunNightColor; - result.mSkyColor = current.mSkyNightColor; - result.mNightFade = 1.f; + mResult.mFogColor = current.mFogNightColor; + mResult.mAmbientColor = current.mAmbientNightColor; + mResult.mSunColor = current.mSunNightColor; + mResult.mSkyColor = current.mSkyNightColor; + mResult.mNightFade = 1.f; } // 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, 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; + mResult.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor, factor); + mResult.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor, factor); + mResult.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor, factor); + mResult.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor, factor); + mResult.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, 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); + mResult.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor, factor); + mResult.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor, factor); + mResult.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor, factor); + mResult.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; - result.mSunColor = current.mSunDayColor; - result.mSkyColor = current.mSkyDayColor; + mResult.mFogColor = current.mFogDayColor; + mResult.mAmbientColor = current.mAmbientDayColor; + mResult.mSunColor = current.mSunDayColor; + mResult.mSkyColor = current.mSkyDayColor; } // 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, 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); + mResult.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor, factor); + mResult.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor, factor); + mResult.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor, factor); + mResult.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, 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; + mResult.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor, factor); + mResult.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor, factor); + mResult.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor, factor); + mResult.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor, factor); + mResult.mNightFade = factor; } } - - return result; } -WeatherResult WeatherManager::transition(float factor) +void WeatherManager::transition(float factor) { - const WeatherResult& current = getResult(mCurrentWeather); - const WeatherResult& other = getResult(mNextWeather); - WeatherResult result; - - result.mCloudTexture = current.mCloudTexture; - result.mNextCloudTexture = other.mCloudTexture; - result.mCloudBlendFactor = factor; - - 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, 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; - - return result; + setResult(mCurrentWeather); + const WeatherResult current = mResult; + setResult(mNextWeather); + const WeatherResult other = mResult; + + mResult.mCloudTexture = current.mCloudTexture; + mResult.mNextCloudTexture = other.mCloudTexture; + mResult.mCloudBlendFactor = factor; + + mResult.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor); + mResult.mFogColor = lerp(current.mFogColor, other.mFogColor, factor); + mResult.mSunColor = lerp(current.mSunColor, other.mSunColor, factor); + mResult.mSkyColor = lerp(current.mSkyColor, other.mSkyColor, factor); + + mResult.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor); + mResult.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor); + mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor); + mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor); + mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor); + mResult.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor); + mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor); + mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor); + + mResult.mNight = current.mNight; } void WeatherManager::update(float duration) @@ -368,272 +324,227 @@ void WeatherManager::update(float duration) mWeatherUpdateTime -= timePassed; - bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); - - if (exterior) + const bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); + if (!exterior) { - std::string regionstr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; - Misc::StringUtils::toLower(regionstr); + mRendering->sunDisable(false); + mRendering->skyDisable(); + mRendering->getSkyManager()->setLightningStrength(0.f); + stopSounds(true); + return; + } - if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) - { - mCurrentRegion = regionstr; - mWeatherUpdateTime = mHoursBetweenWeatherChanges*3600; + // Exterior + std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); - std::string weather = "clear"; + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) + { + mCurrentRegion = regionstr; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) - weather = mRegionOverrides[regionstr]; - else - { - // get weather probabilities for the current region - const ESM::Region *region = + std::string weatherType = "clear"; + + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + weatherType = mRegionOverrides[regionstr]; + else + { + // get weather probabilities for the current region + const ESM::Region *region = MWBase::Environment::get().getWorld()->getStore().get().search (regionstr); - if (region != 0) - { - float clear = region->mData.mClear/255.f; - float cloudy = region->mData.mCloudy/255.f; - float foggy = region->mData.mFoggy/255.f; - float overcast = region->mData.mOvercast/255.f; - float rain = region->mData.mRain/255.f; - 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; - - // re-scale to 100 percent - 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) - weather = "blight"; - else if (random >= thunder+rain+overcast+foggy+cloudy+clear) - weather = "ashstorm"; - else if (random >= rain+overcast+foggy+cloudy+clear) - weather = "thunderstorm"; - else if (random >= overcast+foggy+cloudy+clear) - weather = "rain"; - else if (random >= foggy+cloudy+clear) - weather = "overcast"; - else if (random >= cloudy+clear) - weather = "foggy"; - else if (random >= clear) - weather = "cloudy"; - else - weather = "clear"; - } + if (region != 0) + { + weatherType = nextWeather(region); } - - setWeather(weather, false); } - WeatherResult result; + setWeather(weatherType, false); + } - if (mNextWeather != "") + if (mNextWeather != "") + { + mRemainingTransitionTime -= timePassed; + if (mRemainingTransitionTime < 0) { - mRemainingTransitionTime -= timePassed; - if (mRemainingTransitionTime < 0) - { - mCurrentWeather = mNextWeather; - mNextWeather = ""; - } + mCurrentWeather = mNextWeather; + mNextWeather = ""; } + } - if (mNextWeather != "") - result = transition(1-(mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600))); - else - result = getResult(mCurrentWeather); + if (mNextWeather != "") + transition(1 - (mRemainingTransitionTime / (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600))); + else + setResult(mCurrentWeather); - mRendering->configureFog(result.mFogDepth, result.mFogColor); + mWindSpeed = mResult.mWindSpeed; - // disable sun during night - if (mHour >= 20 || mHour <= 6.f) - mRendering->getSkyManager()->sunDisable(); - else - mRendering->getSkyManager()->sunEnable(); + mRendering->configureFog(mResult.mFogDepth, mResult.mFogColor); - // sun angle - float height; + // disable sun during night + if (mHour >= mNightStart || mHour <= mSunriseTime) + mRendering->getSkyManager()->sunDisable(); + else + mRendering->getSkyManager()->sunEnable(); - // 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; - else //if (mHour > 0 && mHour < 6) - height = 1-(mHour/6.f); + // sun angle + float height; - int facing = (mHour > 13.f) ? 1 : -1; + //Day duration + float dayDuration = (mNightStart - 1) - mSunriseTime; - Vector3 final( - -(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; - else - night = 0; + // rise at 6, set at 20 + 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 / mSunriseTime); - night /= 20.f; + int facing = (mHour > 13.f) ? 1 : -1; - if (night != 0) - { - float moonHeight = 1-std::abs((night-0.5)*2); - int facing = (mHour > 0.f && mHour<12.f) ? 1 : -1; - Vector3 masser( - (1-moonHeight)*facing, - (1-moonHeight)*facing, - moonHeight); + Vector3 final( + (height - 1) * facing, + (height - 1) * facing, + height); + mRendering->setSunDirection(final); - Vector3 secunda( - (1-moonHeight)*facing*0.8, - (1-moonHeight)*facing*1.25, - moonHeight); + /* + * 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 + moonHeight = 0; - mRendering->getSkyManager()->setMasserDirection(masser); - mRendering->getSkyManager()->setSecundaDirection(secunda); - mRendering->getSkyManager()->masserEnable(); - mRendering->getSkyManager()->secundaEnable(); + moonHeight /= (24.f - (fadeInStart - fadeOutFinish)); - 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; + if (moonHeight != 0) + { + int facing = (moonHeight <= 1) ? 1 : -1; + Vector3 masser( + (moonHeight - 1) * facing, + (1 - moonHeight) * facing, + moonHeight); - float secunda_angle_fade; - float masser_angle_fade; - float angle = moonHeight*90.f; + Vector3 secunda( + (moonHeight - 1) * facing * 1.25, + (1 - moonHeight) * facing * 0.8, + moonHeight); - 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; + mRendering->getSkyManager()->setMasserDirection(masser); + mRendering->getSkyManager()->setSecundaDirection(secunda); + mRendering->getSkyManager()->masserEnable(); + mRendering->getSkyManager()->secundaEnable(); - 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; + 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); - masser_angle_fade *= hour_fade; - secunda_angle_fade *= hour_fade; + masserAngleFade *= masserHourFade; + secundaAngleFade *= secundaHourFade; - mRendering->getSkyManager()->setMasserFade(masser_angle_fade); - mRendering->getSkyManager()->setSecundaFade(secunda_angle_fade); - } - else - { - mRendering->getSkyManager()->masserDisable(); - mRendering->getSkyManager()->secundaDisable(); - } + mRendering->getSkyManager()->setMasserFade(masserAngleFade); + mRendering->getSkyManager()->setSecundaFade(secundaAngleFade); + } + else + { + mRendering->getSkyManager()->masserDisable(); + mRendering->getSkyManager()->secundaDisable(); + } - if (mCurrentWeather == "thunderstorm" && mNextWeather == "" && exterior) + if (mCurrentWeather == "thunderstorm" && mNextWeather == "") + { + if (mThunderFlash > 0) { - if (mThunderFlash > 0) + // play the sound after a delay + mThunderSoundDelay -= duration; + if (mThunderSoundDelay <= 0) { - // play the sound after a delay - mThunderSoundDelay -= duration; - if (mThunderSoundDelay <= 0) - { - // pick a random sound - int sound = rand() % 4; - std::string soundname; - 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 / mThunderThreshold ); - else - { - srand(time(NULL)); - mThunderChanceNeeded = rand() % 100; - mThunderChance = 0; - mRendering->getSkyManager()->setLightningStrength( 0.f ); - } + // pick a random sound + int sound = rand() % 4; + std::string* soundName; + 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 / mThunderThreshold ); else { - // no thunder active - mThunderChance += duration*4; // chance increases by 4 percent every second - if (mThunderChance >= mThunderChanceNeeded) - { - mThunderFlash = mThunderThreshold; - - mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold ); - - mThunderSoundDelay = 0.25; - } + mThunderChanceNeeded = rand() % 100; + mThunderChance = 0; + mRendering->getSkyManager()->setLightningStrength( 0.f ); } } else - mRendering->getSkyManager()->setLightningStrength(0.f); + { + // no thunder active + mThunderChance += duration*4; // chance increases by 4 percent every second + if (mThunderChance >= mThunderChanceNeeded) + { + mThunderFlash = mThunderThreshold; - mRendering->setAmbientColour(result.mAmbientColor); - mRendering->sunEnable(false); - mRendering->setSunColour(result.mSunColor); + mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold ); - mRendering->getSkyManager()->setWeather(result); + mThunderSoundDelay = 0.25; + } + } } else - { - mRendering->sunDisable(false); - mRendering->skyDisable(); mRendering->getSkyManager()->setLightningStrength(0.f); - } - // play sounds - std::string ambientSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID : ""); - if (!exterior) ambientSnd = ""; - if (ambientSnd != "") + mRendering->setAmbientColour(mResult.mAmbientColor); + mRendering->sunEnable(false); + mRendering->setSunColour(mResult.mSunColor); + + mRendering->getSkyManager()->setWeather(mResult); + + + // Play sounds + if (mNextWeather == "") { - if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) + std::string ambientSnd = mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID; + if (!ambientSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) { mSoundsPlaying.push_back(ambientSnd); - MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop); + MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); } - } - std::string rainSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mRainLoopSoundID : ""); - if (!exterior) rainSnd = ""; - if (rainSnd != "") - { - if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) + std::string rainSnd = mWeatherSettings[mCurrentWeather].mRainLoopSoundID; + if (!rainSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) { mSoundsPlaying.push_back(rainSnd); - MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop); + MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); } } - // stop sounds - std::vector::iterator it=mSoundsPlaying.begin(); + stopSounds(false); +} + +void WeatherManager::stopSounds(bool stopAll) +{ + std::vector::iterator it = mSoundsPlaying.begin(); while (it!=mSoundsPlaying.end()) { - if ( *it != ambientSnd && *it != rainSnd) + if (stopAll || + !((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) || + (*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID))) { MWBase::Environment::get().getSoundManager()->stopSound(*it); it = mSoundsPlaying.erase(it); @@ -643,6 +554,70 @@ void WeatherManager::update(float duration) } } +Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const +{ + std::vector probability; + + RegionModMap::const_iterator iter = mRegionMods.find(Misc::StringUtils::lowerCase(region->mId)); + if(iter != mRegionMods.end()) + probability = iter->second; + else + { + probability.reserve(10); + probability.push_back(region->mData.mClear); + probability.push_back(region->mData.mCloudy); + probability.push_back(region->mData.mFoggy); + probability.push_back(region->mData.mOvercast); + probability.push_back(region->mData.mRain); + probability.push_back(region->mData.mThunder); + probability.push_back(region->mData.mAsh); + probability.push_back(region->mData.mBlight); + probability.push_back(region->mData.mA); + probability.push_back(region->mData.mB); + } + + /* + * All probabilities must add to 100 (responsibility of the user). + * If chances A and B has values 30 and 70 then by generating + * 100 numbers 1..100, 30% will be lesser or equal 30 and + * 70% will be greater than 30 (in theory). + */ + + int chance = (rand() % 100) + 1; // 1..100 + int sum = 0; + unsigned int i = 0; + for (; i < probability.size(); ++i) + { + sum += probability[i]; + if (chance < sum) + break; + } + + switch (i) + { + case 1: + return "cloudy"; + case 2: + return "foggy"; + case 3: + return "overcast"; + case 4: + return "rain"; + case 5: + return "thunderstorm"; + case 6: + return "ashstorm"; + case 7: + return "blight"; + case 8: + return "snow"; + case 9: + return "blizzard"; + default: // case 0 + return "clear"; + } +} + void WeatherManager::setHour(const float hour) { mHour = hour; @@ -685,6 +660,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"; @@ -709,5 +687,23 @@ 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); +} + +void WeatherManager::modRegion(const std::string ®ionid, const std::vector &chances) +{ + mRegionMods[Misc::StringUtils::lowerCase(regionid)] = chances; + // Start transitioning right away if the region no longer supports the current weather type + unsigned int current = getWeatherID(); + if(current >= chances.size() || chances[current] == 0) + mWeatherUpdateTime = 0.0f; +} + +float WeatherManager::getWindSpeed() const +{ + return mWindSpeed; } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 284c0e050..80cbe0418 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -4,6 +4,11 @@ #include #include +namespace ESM +{ + struct Region; +} + namespace MWRender { class RenderingManager; @@ -11,6 +16,8 @@ namespace MWRender namespace MWWorld { + class Fallback; + /// Defines the actual weather that results from weather setting (see below), time of day and weather transition struct WeatherResult { @@ -112,7 +119,8 @@ namespace MWWorld class WeatherManager { public: - WeatherManager(MWRender::RenderingManager*,const std::map& fallbackMap); + WeatherManager(MWRender::RenderingManager*,MWWorld::Fallback* fallback); + ~WeatherManager(); /** * Change the weather in the specified region @@ -127,8 +135,12 @@ namespace MWWorld */ void update(float duration); + void stopSounds(bool stopAll); + void setHour(const float hour); + float getWindSpeed() const; + void setDate(const int day, const int month); void advanceTime(double hours) @@ -138,14 +150,13 @@ namespace MWWorld unsigned int getWeatherID() const; + void modRegion(const std::string ®ionid, const std::vector &chances); + private: float mHour; int mDay, mMonth; - std::map mFallback; - std::string getFallback (const std::string& key) const; - std::string getFallbackString(const std::string& fall) const; - float getFallbackFloat(const std::string& fall) const; - Ogre::ColourValue getFallbackColour(const std::string& fall) const; + float mWindSpeed; + MWWorld::Fallback* mFallback; void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; @@ -170,10 +181,19 @@ namespace MWWorld double mTimePassed; // time passed since last update - WeatherResult transition(const float factor); - WeatherResult getResult(const Ogre::String& weather); + void transition(const float factor); + void setResult(const Ogre::String& weatherType); + + float calculateHourFade (const std::string& moonName) const; + float calculateAngleFade (const std::string& moonName, float angle) const; + + void setWeather(const Ogre::String& weatherType, bool instant=false); + Ogre::String nextWeather(const ESM::Region* region) const; + WeatherResult mResult; + + typedef std::map > RegionModMap; + RegionModMap mRegionMods; - void setWeather(const Ogre::String& weather, bool instant=false); float mSunriseTime; float mSunsetTime; float mSunriseDuration; @@ -183,6 +203,10 @@ namespace MWWorld float mThunderFrequency; float mThunderThreshold; float mThunderSoundDelay; + float mNightStart; + float mNightEnd; + float mDayStart; + float mDayEnd; std::string mThunderSoundID0; std::string mThunderSoundID1; std::string mThunderSoundID2; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a933713dd..f3d4c81b7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,11 +1,15 @@ #include "worldimp.hpp" +#include + #include #include #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -13,9 +17,11 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/movement.hpp" +#include "../mwmechanics/npcstats.hpp" #include "../mwrender/sky.hpp" -#include "../mwrender/player.hpp" +#include "../mwrender/animation.hpp" #include "../mwclass/door.hpp" @@ -23,6 +29,7 @@ #include "manualref.hpp" #include "cellfunctors.hpp" #include "containerstore.hpp" +#include "inventorystore.hpp" using namespace Ogre; @@ -101,7 +108,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); @@ -154,53 +161,37 @@ namespace MWWorld mRendering->skyDisable(); } - void World::setFallbackValues (const std::map& fallbackMap) - { - mFallback = fallbackMap; - } - - std::string World::getFallback (const std::string& key) const - { - return getFallback(key, ""); - } - - 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()) - { - return def; - } - return it->second; - } - 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, bool newGame, + 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) + mActivationDistanceOverride (mActivationDistanceOverride), + mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), + mFacedDistance(FLT_MAX), mGodMode(false) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); - mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine); + mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback); mPhysEngine->setSceneManager(renderer.getScene()); - mWeatherManager = new MWWorld::WeatherManager(mRendering,fallbackMap); + 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()); + Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + listener->loadingOn(); 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"; + listener->setLabel(masterPath.filename().string()); // This parses the ESM file ESM::ESMReader lEsm; @@ -209,7 +200,7 @@ namespace MWWorld lEsm.setGlobalReaderList(&mEsm); lEsm.open (masterPath.string()); mEsm[idx] = lEsm; - mStore.load (mEsm[idx]); + mStore.load (mEsm[idx], listener); } for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) @@ -217,6 +208,7 @@ namespace MWWorld boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); std::cout << "Loading ESP " << pluginPath.string() << "\n"; + listener->setLabel(pluginPath.filename().string()); // This parses the ESP file ESM::ESMReader lEsm; @@ -225,31 +217,115 @@ namespace MWWorld lEsm.setGlobalReaderList(&mEsm); lEsm.open (pluginPath.string()); mEsm[idx] = lEsm; - mStore.load (mEsm[idx]); + mStore.load (mEsm[idx], listener); } + listener->loadingOff(); + + // 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); + } + + void World::startNewGame() + { + mWorldScene->changeToVoid(); + mStore.clearDynamic(); mStore.setUp(); - mPlayer = new MWWorld::Player (mStore.get().find ("player"), *this); - mRendering->attachCameraTo(mPlayer->getPlayer()); + mCells.clear(); + + // Rebuild player + setupPlayer(); + MWWorld::Ptr player = mPlayer->getPlayer(); + + // removes NpcStats, ContainerStore etc + player.getRefData().setCustomData(NULL); + + renderPlayer(); + mRendering->resetCamera(); + + // make sure to do this so that local scripts from items that were in the players inventory are removed + mLocalScripts.clear(); + + MWBase::Environment::get().getWindowManager()->updatePlayer(); + + ESM::Position pos; + 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); + // 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; @@ -283,6 +359,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); @@ -387,12 +468,18 @@ namespace MWWorld return mPlayer->getPlayer(); } + Ptr ptr = Class::get (mPlayer->getPlayer()). + getContainerStore (mPlayer->getPlayer()).search (name); + + if (!ptr.isEmpty()) + return ptr; + // active cells for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { Ptr::CellStore* cellstore = *iter; - Ptr ptr = mCells.getPtr (name, *cellstore); + Ptr ptr = mCells.getPtr (name, *cellstore, true); if (!ptr.isEmpty()) return ptr; @@ -679,7 +766,7 @@ namespace MWWorld std::pair result; if (!mRendering->occlusionQuerySupported()) - result = mPhysics->getFacedHandle (*this, getMaxActivationDistance ()); + result = mPhysics->getFacedHandle (getMaxActivationDistance ()); else result = std::make_pair (mFacedDistance, mFacedHandle); @@ -687,10 +774,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 (); @@ -701,6 +789,29 @@ namespace MWWorld return object; } + std::pair World::getHitContact(const MWWorld::Ptr &ptr, float distance) + { + const ESM::Position &posdata = ptr.getRefData().getPosition(); + Ogre::Vector3 pos(posdata.pos); + Ogre::Quaternion rot = Ogre::Quaternion(Ogre::Radian(posdata.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::UNIT_X); + + MWRender::Animation *anim = mRendering->getAnimation(ptr); + if(anim != NULL) + { + Ogre::Node *node = anim->getNode("Head"); + if(node != NULL) + pos += node->_getDerivedPosition(); + } + + std::pair result = mPhysics->getHitContact(ptr.getRefData().getHandle(), + pos, rot, distance); + if(result.first.empty()) + return std::make_pair(MWWorld::Ptr(), Ogre::Vector3(0.0f)); + + return std::make_pair(searchPtrViaHandle(result.first), result.second); + } + void World::deleteObject (const Ptr& ptr) { if (ptr.getRefData().getCount()>0) @@ -720,18 +831,20 @@ 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; + 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); + removeContainerScripts(ptr); if (isPlayer) { @@ -763,7 +876,7 @@ namespace MWWorld else { MWWorld::Ptr copy = - MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos); mRendering->updateObjectCell(ptr, copy); @@ -793,12 +906,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(); @@ -811,8 +926,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; @@ -820,25 +935,129 @@ 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; - if (mRendering->rotateObject(ptr, rot, adjust)) + float *objRot = ptr.getRefData().getPosition().rot; + if(adjust) { - // rotate physically iff renderer confirm so - float *objRot = ptr.getRefData().getPosition().rot; - objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; + 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 (ptr.getRefData().getBaseNode() != 0) { - mPhysics->rotateObject(ptr); - } + 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) + { + 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; + } + + 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) { copyObjectToCell(ptr,Cell,pos); @@ -862,25 +1081,26 @@ namespace MWWorld { const int cellSize = 8192; - cellX = static_cast (x/cellSize); - - if (x<0) - --cellX; - - cellY = static_cast (y/cellSize); + cellX = std::floor(x/cellSize); + cellY = std::floor(y/cellSize); + } - if (y<0) - --cellY; + void World::queueMovement(const Ptr &ptr, const Vector3 &velocity) + { + mPhysics->queueObjectMovement(ptr, velocity); } - void World::doPhysics(const PtrMovementList &actors, float duration) + void World::doPhysics(float duration) { /* No duration? Shouldn't be any movement, then. */ if(duration <= 0.0f) return; - PtrMovementList::const_iterator player(actors.end()); - for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++) + processDoors(duration); + + const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); + PtrVelocityList::const_iterator player(results.end()); + for(PtrVelocityList::const_iterator iter(results.begin());iter != results.end();iter++) { if(iter->first.getRefData().getHandle() == "player") { @@ -888,23 +1108,59 @@ namespace MWWorld player = iter; continue; } - Ogre::Vector3 vec = mPhysics->move(iter->first, iter->second, duration, - !isSwimming(iter->first) && !isFlying(iter->first)); - moveObjectImp(iter->first, vec.x, vec.y, vec.z); + moveObjectImp(iter->first, iter->second.x, iter->second.y, iter->second.z); } - if(player != actors.end()) + if(player != results.end()) + moveObjectImp(player->first, player->second.x, player->second.y, player->second.z); + + mPhysEngine->stepSimulation(duration); + } + + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) + { + Ogre::Vector3 a(x1,y1,z1); + Ogre::Vector3 b(x2,y2,z2); + return mPhysics->castRay(a,b,false,true); + } + + void World::processDoors(float duration) + { + std::map::iterator it = mDoorStates.begin(); + while (it != mDoorStates.end()) { - Ogre::Vector3 vec = mPhysics->move(player->first, player->second, duration, - !isSwimming(player->first) && !isFlying(player->first)); - moveObjectImp(player->first, vec.x, vec.y, vec.z); + if (!mWorldScene->isCellActive(*it->first.getCell())) + mDoorStates.erase(it++); + else + { + 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); + + /// \todo should use convexSweepTest here + std::vector collisions = mPhysics->getCollisions(it->first); + for (std::vector::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) + { + MWWorld::Ptr ptr = getPtrViaHandle(*cit); + if (MWWorld::Class::get(ptr).isActor()) + { + // we collided with an actor, we need to undo the rotation + localRotateObject(it->first, 0, 0, oldRot); + break; + } + } + + if ((targetRot == 90.f && it->second) || targetRot == 0.f) + mDoorStates.erase(it++); + else + ++it; + } } - // the only purpose this has currently is to update the debug drawer - mPhysEngine->stepSimulation (duration); } bool World::toggleCollisionMode() { - return mPhysics->toggleCollisionMode();; + return mPhysics->toggleCollisionMode(); } bool World::toggleRenderMode (RenderMode mode) @@ -964,16 +1220,45 @@ namespace MWWorld return ret; } + const ESM::Armor *World::createRecord (const ESM::Armor& record) + { + return mStore.insert(record); + } + + const ESM::Weapon *World::createRecord (const ESM::Weapon& record) + { + 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); + doPhysics (duration); performUpdateSceneQueries (); @@ -1026,6 +1311,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 { @@ -1045,7 +1332,7 @@ namespace MWWorld ++it; } - if (results.size() == 0) + if (results.empty()) { mFacedHandle = ""; mFacedDistance = FLT_MAX; @@ -1090,6 +1377,11 @@ namespace MWWorld mWeatherManager->changeWeather(region, id); } + void World::modRegion(const std::string ®ionid, const std::vector &chances) + { + mWeatherManager->modRegion(regionid, chances); + } + OEngine::Render::Fader* World::getFader() { return mRendering->getFader(); @@ -1159,7 +1451,7 @@ namespace MWWorld // 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); + item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } bool World::placeObject (const Ptr& object, float cursorX, float cursorY) @@ -1274,12 +1566,33 @@ namespace MWWorld bool 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(10/*levitate*/)).mMagnitude > 0) + if(!ptr.getClass().isActor()) + return false; + + const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); + if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0) + return true; + + // TODO: Check if flying creature + + const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(ptr.getRefData().getHandle()); + if(!actor || !actor->getCollisionMode()) return true; + return false; } + bool World::isSubmerged(const MWWorld::Ptr &object) const + { + float *fpos = object.getRefData().getPosition().pos; + Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + + const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); + if(actor) pos.z += 1.85*actor->getHalfExtents().z; + + return isUnderwater(object.getCell(), pos); + } + bool World::isSwimming(const MWWorld::Ptr &object) const { @@ -1310,6 +1623,28 @@ namespace MWWorld return physactor && physactor->getOnGround(); } + bool World::vanityRotateCamera(float * rot) + { + return mRendering->vanityRotateCamera(rot); + } + + void World::setCameraDistance(float dist, bool adjust, bool override) + { + return mRendering->setCameraDistance(dist, adjust, override);; + } + + 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()); @@ -1325,13 +1660,15 @@ namespace MWWorld { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); - RefData &refdata = mPlayer->getPlayer().getRefData(); + Ptr player = mPlayer->getPlayer(); + RefData &refdata = player.getRefData(); Ogre::Vector3 playerPos(refdata.getPosition().pos); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); - if(!physactor->getOnGround() || isUnderwater(currentCell, playerPos)) + 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) || + Class::get(player).getNpcStats(player).isWerewolf()) return 1; return 0; @@ -1352,8 +1689,275 @@ namespace MWWorld mRendering->stopVideo(); } - void World::frameStarted (float dt) + void World::frameStarted (float dt, bool paused) { - mRendering->frameStarted(dt); + mRendering->frameStarted(dt, paused); + } + + 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) + { + 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).getCellRef().mOwner, npc.getCellRef().mRefID)) + out.push_back(searchPtrViaHandle(*it)); + } + } + + void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable) + { + OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle()); + + physicActor->enableCollisions(enable); + } + + bool World::findInteriorPosition(const std::string &name, ESM::Position &pos) + { + typedef MWWorld::CellRefList::List DoorList; + + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; + pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; + + MWWorld::CellStore *cellStore = getInterior(name); + + if (0 == cellStore) { + return false; + } + const DoorList &doors = cellStore->mDoors.mList; + for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { + if (!it->mRef.mTeleport) { + continue; + } + + MWWorld::CellStore *source = 0; + + // door to exterior + if (it->mRef.mDestCell.empty()) { + int x, y; + const float *pos = it->mRef.mDoorDest.pos; + positionToIndex(pos[0], pos[1], x, y); + source = getExterior(x, y); + } + // door to interior + else { + source = getInterior(it->mRef.mDestCell); + } + if (0 != source) { + // Find door leading to our current teleport door + // and use it destination to position inside cell. + const DoorList &doors = source->mDoors.mList; + for (DoorList::const_iterator jt = doors.begin(); jt != doors.end(); ++jt) { + if (it->mRef.mTeleport && + Misc::StringUtils::ciEqual(name, jt->mRef.mDestCell)) + { + /// \note Using _any_ door pointed to the interior, + /// not the one pointed to current door. + pos = jt->mRef.mDoorDest; + return true; + } + } + } + } + return false; + } + + bool World::findExteriorPosition(const std::string &name, ESM::Position &pos) + { + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; + + if (const ESM::Cell *ext = getExterior(name)) { + int x = ext->getGridX(); + int y = ext->getGridY(); + indexToPosition(x, y, pos.pos[0], pos.pos[1], true); + + ESM::Land* land = getStore().get().search(x, y); + if (land) { + if (!land->isDataLoaded(ESM::Land::DATA_VHGT)) { + land->loadData(ESM::Land::DATA_VHGT); + } + pos.pos[2] = land->mLandData->mHeights[ESM::Land::LAND_NUM_VERTS / 2 + 1]; + } + else { + std::cerr << "Land data for cell at (" << x << ", " << y << ") not found\n"; + pos.pos[2] = 0; + } + + return true; + } + return false; + } + + void World::enableTeleporting(bool enable) + { + mTeleportEnabled = enable; + } + + bool World::isTeleportingEnabled() const + { + return mTeleportEnabled; + } + + void World::setWerewolf(const MWWorld::Ptr& actor, bool werewolf) + { + MWMechanics::NpcStats& npcStats = Class::get(actor).getNpcStats(actor); + + // The actor does not have to change state + if (npcStats.isWerewolf() == werewolf) + return; + + npcStats.setWerewolf(werewolf); + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); + invStore.unequipAll(actor); + + if(werewolf) + { + ManualRef ref(getStore(), "WerewolfRobe"); + ref.getPtr().getRefData().setCount(1); + + // Configure item's script variables + std::string script = Class::get(ref.getPtr()).getScript(ref.getPtr()); + if(script != "") + { + const ESM::Script *esmscript = getStore().get().find(script); + ref.getPtr().getRefData().setLocals(*esmscript); + } + + // Not sure this is right + InventoryStore &inv = Class::get(actor).getInventoryStore(actor); + inv.equip(InventoryStore::Slot_Robe, inv.add(ref.getPtr(), actor)); + } + else + { + ContainerStore &store = Class::get(actor).getContainerStore(actor); + + const std::string item = "WerewolfRobe"; + for(ContainerStoreIterator iter(store.begin());iter != store.end();++iter) + { + if(Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) + iter->getRefData().setCount(0); + } + } + + if(actor.getRefData().getHandle() == "player") + { + // Update the GUI only when called on the player + MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); + windowManager->unsetSelectedWeapon(); + + if (werewolf) + { + windowManager->forceHide(MWGui::GW_Inventory); + windowManager->forceHide(MWGui::GW_Magic); + } + else + { + windowManager->unsetForceHide(MWGui::GW_Inventory); + windowManager->unsetForceHide(MWGui::GW_Magic); + } + } + + mRendering->rebuildPtr(actor); + } + + void World::applyWerewolfAcrobatics(const Ptr &actor) + { + const Store &gmst = getStore().get(); + MWMechanics::NpcStats &stats = Class::get(actor).getNpcStats(actor); + + stats.getSkill(ESM::Skill::Acrobatics).setModified(gmst.find("fWerewolfAcrobatics")->getFloat(), 0); + } + + bool World::getGodModeState() + { + return mGodMode; + } + + bool World::toggleGodMode() + { + mGodMode = !mGodMode; + + return mGodMode; + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e06b89f60..53b01f1ab 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" @@ -49,6 +50,7 @@ namespace MWWorld class World : public MWBase::World { + MWWorld::Fallback mFallback; MWRender::RenderingManager* mRendering; MWWorld::WeatherManager* mWeatherManager; @@ -66,6 +68,8 @@ namespace MWWorld OEngine::Physic::PhysicEngine* mPhysEngine; + bool mGodMode; + // not implemented World (const World&); World& operator= (const World&); @@ -75,20 +79,14 @@ namespace MWWorld int mActivationDistanceOverride; std::string mFacedHandle; float mFacedDistance; - Ptr mFaced1; - Ptr mFaced2; - std::string mFaced1Name; - std::string mFaced2Name; - 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 @@ -107,16 +105,30 @@ namespace MWWorld 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 doPhysics(float duration); + ///< Run physics simulation and modify \a world accordingly. + + void ensureNeededRecords(); + + int mPlayIntro; + + bool mTeleportEnabled; + public: 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, bool newGame, + 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. @@ -132,11 +144,7 @@ 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(); @@ -189,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); @@ -224,6 +235,8 @@ namespace MWWorld virtual void setMoonColour (bool red); + virtual void modRegion(const std::string ®ionid, const std::vector &chances); + virtual float getTimeScaleFactor() const; virtual void changeToInteriorCell (const std::string& cellName, @@ -241,6 +254,11 @@ namespace MWWorld virtual MWWorld::Ptr getFacedObject(); ///< Return pointer to the object the player is looking at, if it is within activation range + /// Returns a pointer to the object the provided object would hit (if within the + /// specified distance), and the point where the hit occurs. This will attempt to + /// use the "Head" node as a basis. + virtual std::pair getHitContact(const MWWorld::Ptr &ptr, float distance); + virtual void deleteObject (const Ptr& ptr); virtual void moveObject (const Ptr& ptr, float x, float y, float z); @@ -252,6 +270,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. @@ -262,8 +282,12 @@ namespace MWWorld virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const; ///< Convert position to cell numbers - virtual void doPhysics(const PtrMovementList &actors, float duration); - ///< Run physics simulation and modify \a world accordingly. + virtual void queueMovement(const Ptr &ptr, const Ogre::Vector3 &velocity); + ///< Queues movement for \a ptr (in local space), to be applied in the next call to + /// doPhysics. + + 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 @@ -275,25 +299,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 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 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); @@ -312,6 +355,8 @@ namespace MWWorld virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual bool isFlying(const MWWorld::Ptr &ptr) const; + ///Is the head of the creature underwater? + virtual bool isSubmerged(const MWWorld::Ptr &object) const; virtual bool isSwimming(const MWWorld::Ptr &object) const; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; @@ -324,8 +369,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) { @@ -340,8 +385,28 @@ namespace MWWorld mRendering->changeVanityModeScale(factor); } + virtual bool vanityRotateCamera(float * rot); + virtual void setCameraDistance(float dist, bool adjust = false, bool override = true); + + 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 enableActorCollision(const MWWorld::Ptr& actor, bool enable); + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); virtual int canRest(); @@ -357,7 +422,29 @@ namespace MWWorld /// \todo this does not belong here virtual void playVideo(const std::string& name, bool allowSkipping); virtual void stopVideo(); - virtual void frameStarted (float dt); + virtual void frameStarted (float dt, bool paused); + + /// Find center of exterior cell above land surface + /// \return false if exterior with given name not exists, true otherwise + virtual bool findExteriorPosition(const std::string &name, ESM::Position &pos); + + /// Find position in interior cell near door entrance + /// \return false if interior with given name not exists, true otherwise + virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos); + + /// Enables or disables use of teleport spell effects (recall, intervention, etc). + virtual void enableTeleporting(bool enable); + + /// Returns true if teleport spell effects are allowed. + virtual bool isTeleportingEnabled() const; + + virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf); + + virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor); + + virtual bool getGodModeState(); + + virtual bool toggleGodMode(); }; } diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index cdf8bc74b..682010340 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -11,14 +11,15 @@ if (GTEST_FOUND AND GMOCK_FOUND) include_directories(${GMOCK_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES - components/misc/*.cpp + components/misc/test_*.cpp + components/file_finder/test_*.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) - target_link_libraries(openmw_test_suite ${GMOCK_BOTH_LIBRARIES} ${GTEST_BOTH_LIBRARIES}) + target_link_libraries(openmw_test_suite ${GMOCK_BOTH_LIBRARIES} ${GTEST_BOTH_LIBRARIES} components) # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) diff --git a/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp b/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp new file mode 100644 index 000000000..2d151988b --- /dev/null +++ b/apps/openmw_test_suite/components/file_finder/test_filefinder.cpp @@ -0,0 +1,66 @@ +#include +#include +#include "components/file_finder/file_finder.hpp" + +struct FileFinderTest : public ::testing::Test +{ + protected: + FileFinderTest() + : mTestDir("./filefinder_test_dir/") + , mTestFile("test.txt") + , mTestFileUppercase("TEST.TXT") + , mTestFileNotFound("foobarbaz.txt") + { + } + + virtual void SetUp() + { + boost::filesystem::create_directory(boost::filesystem::path(mTestDir)); + + std::ofstream ofs(std::string(mTestDir + mTestFile).c_str(), std::ofstream::out); + ofs << std::endl; + ofs.close(); + } + + virtual void TearDown() + { + boost::filesystem::remove_all(boost::filesystem::path(mTestDir)); + } + + std::string mTestDir; + std::string mTestFile; + std::string mTestFileUppercase; + std::string mTestFileNotFound; +}; + +TEST_F(FileFinderTest, FileFinder_has_file) +{ + FileFinder::FileFinder fileFinder(mTestDir); + ASSERT_TRUE(fileFinder.has(mTestFile)); + ASSERT_TRUE(fileFinder.has(mTestFileUppercase)); + ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile)); + ASSERT_TRUE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile)); +} + +TEST_F(FileFinderTest, FileFinder_does_not_have_file) +{ + FileFinder::FileFinder fileFinder(mTestDir); + ASSERT_FALSE(fileFinder.has(mTestFileNotFound)); + ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty()); +} + +TEST_F(FileFinderTest, FileFinderStrict_has_file) +{ + FileFinder::FileFinderStrict fileFinder(mTestDir); + ASSERT_TRUE(fileFinder.has(mTestFile)); + ASSERT_FALSE(fileFinder.has(mTestFileUppercase)); + ASSERT_TRUE(fileFinder.lookup(mTestFile) == std::string(mTestDir + mTestFile)); + ASSERT_FALSE(fileFinder.lookup(mTestFileUppercase) == std::string(mTestDir + mTestFile)); +} + +TEST_F(FileFinderTest, FileFinderStrict_does_not_have_file) +{ + FileFinder::FileFinderStrict fileFinder(mTestDir); + ASSERT_FALSE(fileFinder.has(mTestFileNotFound)); + ASSERT_TRUE(fileFinder.lookup(mTestFileNotFound).empty()); +} diff --git a/apps/openmw_test_suite/components/file_finder/test_search.cpp b/apps/openmw_test_suite/components/file_finder/test_search.cpp new file mode 100644 index 000000000..63745b625 --- /dev/null +++ b/apps/openmw_test_suite/components/file_finder/test_search.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include "components/file_finder/search.hpp" + +struct SearchTest : public ::testing::Test +{ + protected: + SearchTest() + : mTestDir("./search_test_dir/") + { + } + + virtual void SetUp() + { + boost::filesystem::create_directory(boost::filesystem::path(mTestDir)); + + std::ofstream ofs(std::string(mTestDir + "test2.txt").c_str(), std::ofstream::out); + ofs << std::endl; + ofs.close(); + } + + virtual void TearDown() + { + boost::filesystem::remove_all(boost::filesystem::path(mTestDir)); + } + + std::string mTestDir; +}; + +TEST_F(SearchTest, file_not_found) +{ + struct Result : public FileFinder::ReturnPath + { + Result(const boost::filesystem::path& expectedPath) + : mExpectedPath(expectedPath) + { + } + + void add(const boost::filesystem::path& p) + { + ASSERT_FALSE(p == mExpectedPath); + } + + private: + boost::filesystem::path mExpectedPath; + + } r(boost::filesystem::path(mTestDir + "test.txt")); + + FileFinder::find(mTestDir, r, false); +} + +TEST_F(SearchTest, file_found) +{ + struct Result : public FileFinder::ReturnPath + { + Result(const boost::filesystem::path& expectedPath) + : mExpectedPath(expectedPath) + { + } + + void add(const boost::filesystem::path& p) + { + ASSERT_TRUE(p == mExpectedPath); + } + + private: + boost::filesystem::path mExpectedPath; + + } r(boost::filesystem::path(mTestDir + "test2.txt")); + + FileFinder::find(mTestDir, r, false); +} diff --git a/apps/openmw_test_suite/components/misc/test_slicearray.cpp b/apps/openmw_test_suite/components/misc/test_slicearray.cpp new file mode 100644 index 000000000..ab63e56c4 --- /dev/null +++ b/apps/openmw_test_suite/components/misc/test_slicearray.cpp @@ -0,0 +1,33 @@ +#include +#include "components/misc/slice_array.hpp" + +struct SliceArrayTest : public ::testing::Test +{ + protected: + virtual void SetUp() + { + } + + virtual void TearDown() + { + } +}; + +TEST_F(SliceArrayTest, hello_string) +{ + Misc::SString s("hello"); + ASSERT_EQ(sizeof("hello") - 1, s.length); + ASSERT_FALSE(s=="hel"); + ASSERT_FALSE(s=="hell"); + ASSERT_TRUE(s=="hello"); +} + +TEST_F(SliceArrayTest, othello_string_with_offset_2_and_size_4) +{ + Misc::SString s("othello" + 2, 4); + ASSERT_EQ(sizeof("hell") - 1, s.length); + ASSERT_FALSE(s=="hel"); + ASSERT_TRUE(s=="hell"); + ASSERT_FALSE(s=="hello"); +} + diff --git a/apps/openmw_test_suite/components/misc/test_stringops.cpp b/apps/openmw_test_suite/components/misc/test_stringops.cpp index b12584196..44587c445 100644 --- a/apps/openmw_test_suite/components/misc/test_stringops.cpp +++ b/apps/openmw_test_suite/components/misc/test_stringops.cpp @@ -1,7 +1,79 @@ #include +#include "components/misc/stringops.hpp" -TEST(simple_test, dummy) +struct StringOpsTest : public ::testing::Test { - EXPECT_EQ(true, true); + protected: + virtual void SetUp() + { + } + + virtual void TearDown() + { + } +}; + +TEST_F(StringOpsTest, begins_matching) +{ + ASSERT_TRUE(Misc::begins("abc", "a")); + ASSERT_TRUE(Misc::begins("abc", "ab")); + ASSERT_TRUE(Misc::begins("abc", "abc")); + ASSERT_TRUE(Misc::begins("abcd", "abc")); +} + +TEST_F(StringOpsTest, begins_not_matching) +{ + ASSERT_FALSE(Misc::begins("abc", "b")); + ASSERT_FALSE(Misc::begins("abc", "bc")); + ASSERT_FALSE(Misc::begins("abc", "bcd")); + ASSERT_FALSE(Misc::begins("abc", "abcd")); +} + +TEST_F(StringOpsTest, ibegins_matching) +{ + ASSERT_TRUE(Misc::ibegins("Abc", "a")); + ASSERT_TRUE(Misc::ibegins("aBc", "ab")); + ASSERT_TRUE(Misc::ibegins("abC", "abc")); + ASSERT_TRUE(Misc::ibegins("abcD", "abc")); +} + +TEST_F(StringOpsTest, ibegins_not_matching) +{ + ASSERT_FALSE(Misc::ibegins("abc", "b")); + ASSERT_FALSE(Misc::ibegins("abc", "bc")); + ASSERT_FALSE(Misc::ibegins("abc", "bcd")); + ASSERT_FALSE(Misc::ibegins("abc", "abcd")); +} + +TEST_F(StringOpsTest, ends_matching) +{ + ASSERT_TRUE(Misc::ends("abc", "c")); + ASSERT_TRUE(Misc::ends("abc", "bc")); + ASSERT_TRUE(Misc::ends("abc", "abc")); + ASSERT_TRUE(Misc::ends("abcd", "abcd")); +} + +TEST_F(StringOpsTest, ends_not_matching) +{ + ASSERT_FALSE(Misc::ends("abc", "b")); + ASSERT_FALSE(Misc::ends("abc", "ab")); + ASSERT_FALSE(Misc::ends("abc", "bcd")); + ASSERT_FALSE(Misc::ends("abc", "abcd")); +} + +TEST_F(StringOpsTest, iends_matching) +{ + ASSERT_TRUE(Misc::iends("Abc", "c")); + ASSERT_TRUE(Misc::iends("aBc", "bc")); + ASSERT_TRUE(Misc::iends("abC", "abc")); + ASSERT_TRUE(Misc::iends("abcD", "abcd")); +} + +TEST_F(StringOpsTest, iends_not_matching) +{ + ASSERT_FALSE(Misc::iends("abc", "b")); + ASSERT_FALSE(Misc::iends("abc", "ab")); + ASSERT_FALSE(Misc::iends("abc", "bcd")); + ASSERT_FALSE(Misc::iends("abc", "abcd")); } diff --git a/apps/openmw_test_suite/openmw_test_suite.cpp b/apps/openmw_test_suite/openmw_test_suite.cpp index 0a09b3028..81476325e 100644 --- a/apps/openmw_test_suite/openmw_test_suite.cpp +++ b/apps/openmw_test_suite/openmw_test_suite.cpp @@ -5,8 +5,8 @@ int main(int argc, char** argv) { // The following line causes Google Mock to throw an exception on failure, // which will be interpreted by your testing framework as a test failure. - ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::GTEST_FLAG(throw_on_failure) = false; ::testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/cmake/FindLIBUNSHIELD.cmake b/cmake/FindLIBUNSHIELD.cmake new file mode 100644 index 000000000..4f4e98a1c --- /dev/null +++ b/cmake/FindLIBUNSHIELD.cmake @@ -0,0 +1,48 @@ +# Locate LIBUNSHIELD +# This module defines +# LIBUNSHIELD_LIBRARY +# LIBUNSHIELD_FOUND, if false, do not try to link to LibUnshield +# LIBUNSHIELD_INCLUDE_DIR, where to find the headers +# +# Created by Tom Mason (wheybags) for OpenMW (http://openmw.com), based on FindMPG123.cmake +# +# Ripped off from other sources. In fact, this file is so generic (I +# just did a search and replace on another file) that I wonder why the +# CMake guys haven't wrapped this entire thing in a single +# function. Do we really need to repeat this stuff for every single +# library when they all work the same? + +FIND_PATH(LIBUNSHIELD_INCLUDE_DIR libunshield.h + HINTS + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + /usr/include +) + +FIND_LIBRARY(LIBUNSHIELD_LIBRARY + unshield + HINTS +# PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw + /opt/local + /opt/csw + /opt + /usr/lib +) + +IF(LIBUNSHIELD_LIBRARY AND LIBUNSHIELD_INCLUDE_DIR) + SET(LIBUNSHIELD_FOUND "YES") +ENDIF(LIBUNSHIELD_LIBRARY AND LIBUNSHIELD_INCLUDE_DIR) + diff --git a/cmake/FindOIS.cmake b/cmake/FindOIS.cmake deleted file mode 100644 index e0278d8a2..000000000 --- a/cmake/FindOIS.cmake +++ /dev/null @@ -1,87 +0,0 @@ -#------------------------------------------------------------------- -# This file is part of the CMake build system for OGRE -# (Object-oriented Graphics Rendering Engine) -# For the latest info, see http://www.ogre3d.org/ -# -# The contents of this file are placed in the public domain. Feel -# free to make use of it in any way you like. -#------------------------------------------------------------------- - -# - Try to find OIS -# Once done, this will define -# -# OIS_FOUND - system has OIS -# OIS_INCLUDE_DIRS - the OIS include directories -# OIS_LIBRARIES - link these to use OIS -# OIS_BINARY_REL / OIS_BINARY_DBG - DLL names (windows only) - -include(FindPkgMacros) -findpkg_begin(OIS) - -# Get path, convert backslashes as ${ENV_${var}} -getenv_path(OIS_HOME) -getenv_path(OGRE_SDK) -getenv_path(OGRE_HOME) -getenv_path(OGRE_SOURCE) -getenv_path(OGRE_DEPENDENCIES_DIR) - -# construct search paths -set(OIS_PREFIX_PATH ${OIS_HOME} ${ENV_OIS_HOME} - ${OGRE_DEPENDENCIES_DIR} ${ENV_OGRE_DEPENDENCIES_DIR} - ${OGRE_SOURCE}/iPhoneDependencies ${ENV_OGRE_SOURCE}/iPhoneDependencies - ${OGRE_SOURCE}/Dependencies ${ENV_OGRE_SOURCE}/Dependencies - ${OGRE_SDK} ${ENV_OGRE_SDK} - ${OGRE_HOME} ${ENV_OGRE_HOME}) -create_search_paths(OIS) -# redo search if prefix path changed -clear_if_changed(OIS_PREFIX_PATH - OIS_LIBRARY_FWK - OIS_LIBRARY_REL - OIS_LIBRARY_DBG - OIS_INCLUDE_DIR -) - -set(OIS_LIBRARY_NAMES OIS) -get_debug_names(OIS_LIBRARY_NAMES) - -use_pkgconfig(OIS_PKGC OIS) - -# For OIS, prefer static library over framework (important when referencing OIS source build) -set(CMAKE_FIND_FRAMEWORK "LAST") - -findpkg_framework(OIS) -if (OIS_HOME) - # OIS uses the 'includes' path for its headers in the source release, not 'include' - set(OIS_INC_SEARCH_PATH ${OIS_INC_SEARCH_PATH} ${OIS_HOME}/includes) -endif() -if (APPLE AND OIS_HOME) - # OIS source build on Mac stores libs in a different location - # Also this is for static build - set(OIS_LIB_SEARCH_PATH ${OIS_LIB_SEARCH_PATH} ${OIS_HOME}/Mac/XCode-2.2/build) -endif() -find_path(OIS_INCLUDE_DIR NAMES OIS.h HINTS ${OIS_INC_SEARCH_PATH} ${OIS_PKGC_INCLUDE_DIRS} PATH_SUFFIXES OIS) -find_library(OIS_LIBRARY_REL NAMES ${OIS_LIBRARY_NAMES} HINTS ${OIS_LIB_SEARCH_PATH} ${OIS_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel) -find_library(OIS_LIBRARY_DBG NAMES ${OIS_LIBRARY_NAMES_DBG} HINTS ${OIS_LIB_SEARCH_PATH} ${OIS_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug) -make_library_set(OIS_LIBRARY) - -if (WIN32) - set(OIS_BIN_SEARCH_PATH ${OIS_HOME}/dll ${ENV_OIS_HOME}/dll - ${OGRE_DEPENDENCIES_DIR}/bin ${ENV_OGRE_DEPENDENCIES_DIR}/bin - ${OGRE_SOURCE}/Dependencies/bin ${ENV_OGRE_SOURCE}/Dependencies/bin - ${OGRE_SDK}/bin ${ENV_OGRE_SDK}/bin - ${OGRE_HOME}/bin ${ENV_OGRE_HOME}/bin) - find_file(OIS_BINARY_REL NAMES "OIS.dll" HINTS ${OIS_BIN_SEARCH_PATH} - PATH_SUFFIXES "" release relwithdebinfo minsizerel) - find_file(OIS_BINARY_DBG NAMES "OIS_d.dll" HINTS ${OIS_BIN_SEARCH_PATH} - PATH_SUFFIXES "" debug ) -endif() -mark_as_advanced(OIS_BINARY_REL OIS_BINARY_DBG) - - -findpkg_finish(OIS) - -# add parent of OIS folder to support OIS/OIS.h -add_parent_dir(OIS_INCLUDE_DIRS OIS_INCLUDE_DIR) - -# Reset framework finding -set(CMAKE_FIND_FRAMEWORK "FIRST") diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake new file mode 100644 index 000000000..70e607a89 --- /dev/null +++ b/cmake/FindSDL2.cmake @@ -0,0 +1,193 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2_main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDL2main.h and SDL2main.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL2 guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL2 convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). +# +# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake +# module with the minor edit of changing "SDL" to "SDL2" where necessary. This +# was not created for redistribution, and exists temporarily pending official +# SDL2 CMake modules. + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES include/SDL2 include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL2 + /usr/include/SDL2 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) +#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt +) + +#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}") + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL2_FOUND "NO") +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + IF(WIN32) + SET(SDL2_LIBRARY_TEMP winmm imm32 version msimg32 ${SDL2_LIBRARY_TEMP}) + ENDIF(WIN32) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL2_FOUND "YES") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 + REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) + +IF(SDL2_STATIC) + if (UNIX AND NOT APPLE) + EXECUTE_PROCESS(COMMAND sdl2-config --static-libs OUTPUT_VARIABLE SDL2_LINK_FLAGS) + STRING(REGEX REPLACE "(\r?\n)+$" "" SDL2_LINK_FLAGS "${SDL2_LINK_FLAGS}") + SET(SDL2_LIBRARY ${SDL2_LINK_FLAGS}) + ENDIF() +ENDIF(SDL2_STATIC) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 38625fb52..04423dc6f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -19,7 +19,7 @@ add_component_dir (nif ) add_component_dir (nifogre - ogrenifloader + ogrenifloader skeleton material mesh ) add_component_dir (nifbullet @@ -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 variant variantimp + loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter ) 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 opcodes extensions0 ) add_component_dir (interpreter @@ -65,6 +65,14 @@ add_component_dir (interpreter add_component_dir (translation translation ) + +add_component_dir (terrain + quadtreenode chunk world storage material + ) + +add_component_dir (loadinglistener + loadinglistener + ) find_package(Qt4 COMPONENTS QtCore QtGui) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index c97ca4562..8f07b9e50 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -130,7 +130,7 @@ public: { std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); StringVectorPtr ptr = StringVectorPtr(new StringVector()); - for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();iter++) + 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))) @@ -169,7 +169,7 @@ public: } else { - for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();iter++) + 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))) @@ -249,7 +249,7 @@ public: std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); const Bsa::BSAFile::FileList &filelist = arc.getList(); StringVectorPtr ptr = StringVectorPtr(new StringVector()); - for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();iter++) + 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) || @@ -266,7 +266,7 @@ public: FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); const Bsa::BSAFile::FileList &filelist = arc.getList(); - for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();iter++) + 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) || @@ -313,21 +313,26 @@ public: 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; } }; 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/extensions0.cpp b/components/compiler/extensions0.cpp new file mode 100644 index 000000000..415f8d168 --- /dev/null +++ b/components/compiler/extensions0.cpp @@ -0,0 +1,469 @@ +#include "extensions0.hpp" + +#include "opcodes.hpp" +#include "extensions.hpp" + +namespace Compiler +{ + void registerExtensions (Extensions& extensions, bool consoleOnly) + { + Ai::registerExtensions (extensions); + Animation::registerExtensions (extensions); + Cell::registerExtensions (extensions); + Container::registerExtensions (extensions); + Control::registerExtensions (extensions); + Dialogue::registerExtensions (extensions); + Gui::registerExtensions (extensions); + Misc::registerExtensions (extensions); + Sky::registerExtensions (extensions); + Sound::registerExtensions (extensions); + Stats::registerExtensions (extensions); + Transformation::registerExtensions (extensions); + + if (consoleOnly) + { + Console::registerExtensions (extensions); + User::registerExtensions (extensions); + } + } + + namespace Ai + { + void registerExtensions (Extensions& extensions) + { + extensions.registerInstruction ("aiactivate", "c/l", opcodeAIActivate, + opcodeAIActivateExplicit); + extensions.registerInstruction ("aitravel", "fff/l", opcodeAiTravel, + opcodeAiTravelExplicit); + extensions.registerInstruction ("aiescort", "cffff/l", opcodeAiEscort, + opcodeAiEscortExplicit); + extensions.registerInstruction ("aiescortcell", "ccffff/l", opcodeAiEscortCell, + opcodeAiEscortCellExplicit); + extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander, + opcodeAiWanderExplicit); + extensions.registerInstruction ("aifollow", "cffff/l", opcodeAiFollow, + opcodeAiFollowExplicit); + extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell, + opcodeAiFollowCellExplicit); + extensions.registerFunction ("getaipackagedone", 'l', "", opcodeGetAiPackageDone, + opcodeGetAiPackageDoneExplicit); + extensions.registerFunction ("getcurrentaipackage", 'l', "", opcodeGetCurrentAiPackage, + opcodeGetAiPackageDoneExplicit); + extensions.registerFunction ("getdetected", 'l', "c", opcodeGetDetected, + opcodeGetDetectedExplicit); + extensions.registerInstruction ("sethello", "l", opcodeSetHello, opcodeSetHelloExplicit); + extensions.registerInstruction ("setfight", "l", opcodeSetFight, opcodeSetFightExplicit); + extensions.registerInstruction ("setflee", "l", opcodeSetFlee, opcodeSetFleeExplicit); + extensions.registerInstruction ("setalarm", "l", opcodeSetAlarm, opcodeSetAlarmExplicit); + extensions.registerInstruction ("modhello", "l", opcodeModHello, opcodeModHelloExplicit); + extensions.registerInstruction ("modfight", "l", opcodeModFight, opcodeModFightExplicit); + extensions.registerInstruction ("modflee", "l", opcodeModFlee, opcodeModFleeExplicit); + extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); + extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); + extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); + extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); + extensions.registerFunction ("getalarm", 'l', "", opcodeGetAlarm, opcodeGetAlarmExplicit); + } + } + + namespace Animation + { + void registerExtensions (Extensions& extensions) + { + extensions.registerInstruction ("skipanim", "", opcodeSkipAnim, opcodeSkipAnimExplicit); + extensions.registerInstruction ("playgroup", "c/l", opcodePlayAnim, opcodePlayAnimExplicit); + extensions.registerInstruction ("loopgroup", "cl/l", opcodeLoopAnim, opcodeLoopAnimExplicit); + } + } + + namespace Cell + { + void registerExtensions (Extensions& extensions) + { + extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged); + extensions.registerInstruction ("coc", "S", opcodeCOC); + extensions.registerInstruction ("centeroncell", "S", opcodeCOC); + extensions.registerInstruction ("coe", "ll", opcodeCOE); + extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE); + extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel); + extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel); + extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior); + extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell); + extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel); + } + } + + namespace Console + { + void registerExtensions (Extensions& extensions) + { + + } + } + + namespace Container + { + void registerExtensions (Extensions& extensions) + { + extensions.registerInstruction ("additem", "cl", opcodeAddItem, opcodeAddItemExplicit); + extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount, + opcodeGetItemCountExplicit); + extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, + opcodeRemoveItemExplicit); + extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); + extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); + extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); + extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit); + extensions.registerFunction ("getweapontype", 'l', "", opcodeGetWeaponType, opcodeGetWeaponTypeExplicit); + } + } + + namespace Control + { + void registerExtensions (Extensions& extensions) + { + std::string enable ("enable"); + std::string disable ("disable"); + + for (int i=0; i& code, bool allowExpression) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code), mState (BeginState), mExprParser (errorHandler, context, locals, literals), - mAllowExpression (allowExpression) + mAllowExpression (allowExpression), mButtons(0), mType(0) {} bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) @@ -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/locals.hpp b/components/compiler/locals.hpp index fdd0a6b29..e54b7798c 100644 --- a/components/compiler/locals.hpp +++ b/components/compiler/locals.hpp @@ -15,8 +15,6 @@ namespace Compiler std::vector mLongs; std::vector mFloats; - const std::vector& get (char type) const; - int searchIndex (char type, const std::string& name) const; bool search (char type, const std::string& name) const; @@ -31,6 +29,8 @@ namespace Compiler int getIndex (const std::string& name) const; ///< return index for local variable \a name (-1: does not exist). + const std::vector& get (char type) const; + void write (std::ostream& localFile) const; ///< write declarations to file. 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/opcodes.cpp b/components/compiler/opcodes.cpp new file mode 100644 index 000000000..8617062d6 --- /dev/null +++ b/components/compiler/opcodes.cpp @@ -0,0 +1,13 @@ +#include "opcodes.hpp" + +namespace Compiler +{ + namespace Control + { + const char *controls[numberOfControls] = + { + "playercontrols", "playerfighting", "playerjumping", "playerlooking", "playermagic", + "playerviewswitch", "vanitymode" + }; + } +} \ No newline at end of file diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp new file mode 100644 index 000000000..5eb54208a --- /dev/null +++ b/components/compiler/opcodes.hpp @@ -0,0 +1,417 @@ +#ifndef COMPILER_OPCODES_H +#define COMPILER_OPCODES_H + +namespace Compiler +{ + namespace Ai + { + const int opcodeAiTravel = 0x20000; + const int opcodeAiTravelExplicit = 0x20001; + const int opcodeAiEscort = 0x20002; + const int opcodeAiEscortExplicit = 0x20003; + const int opcodeGetAiPackageDone = 0x200007c; + const int opcodeGetAiPackageDoneExplicit = 0x200007d; + const int opcodeGetCurrentAiPackage = 0x20001ef; + const int opcodeGetCurrentAiPackageExplicit = 0x20001f0; + const int opcodeGetDetected = 0x20001f1; + const int opcodeGetDetectedExplicit = 0x20001f2; + const int opcodeAiWander = 0x20010; + const int opcodeAiWanderExplicit = 0x20011; + const int opcodeAIActivate = 0x2001e; + const int opcodeAIActivateExplicit = 0x2001f; + const int opcodeAiEscortCell = 0x20020; + const int opcodeAiEscortCellExplicit = 0x20021; + const int opcodeAiFollow = 0x20022; + const int opcodeAiFollowExplicit = 0x20023; + const int opcodeAiFollowCell = 0x20024; + const int opcodeAiFollowCellExplicit = 0x20025; + const int opcodeSetHello = 0x200015e; + const int opcodeSetHelloExplicit = 0x200015d; + const int opcodeSetFight = 0x200015e; + const int opcodeSetFightExplicit = 0x200015f; + const int opcodeSetFlee = 0x2000160; + const int opcodeSetFleeExplicit = 0x2000161; + const int opcodeSetAlarm = 0x2000162; + const int opcodeSetAlarmExplicit = 0x2000163; + const int opcodeModHello = 0x20001b7; + const int opcodeModHelloExplicit = 0x20001b8; + const int opcodeModFight = 0x20001b9; + const int opcodeModFightExplicit = 0x20001ba; + const int opcodeModFlee = 0x20001bb; + const int opcodeModFleeExplicit = 0x20001bc; + const int opcodeModAlarm = 0x20001bd; + const int opcodeModAlarmExplicit = 0x20001be; + const int opcodeGetHello = 0x20001bf; + const int opcodeGetHelloExplicit = 0x20001c0; + const int opcodeGetFight = 0x20001c1; + const int opcodeGetFightExplicit = 0x20001c2; + const int opcodeGetFlee = 0x20001c3; + const int opcodeGetFleeExplicit = 0x20001c4; + const int opcodeGetAlarm = 0x20001c5; + const int opcodeGetAlarmExplicit = 0x20001c6; + } + + namespace Animation + { + const int opcodeSkipAnim = 0x2000138; + const int opcodeSkipAnimExplicit = 0x2000139; + const int opcodePlayAnim = 0x20006; + const int opcodePlayAnimExplicit = 0x20007; + const int opcodeLoopAnim = 0x20008; + const int opcodeLoopAnimExplicit = 0x20009; + } + + namespace Cell + { + const int opcodeCellChanged = 0x2000000; + const int opcodeCOC = 0x2000026; + const int opcodeCOE = 0x200008e; + const int opcodeGetInterior = 0x2000131; + const int opcodeGetPCCell = 0x2000136; + const int opcodeGetWaterLevel = 0x2000141; + const int opcodeSetWaterLevel = 0x2000142; + const int opcodeModWaterLevel = 0x2000143; + } + + namespace Console + { + + } + + namespace Container + { + const int opcodeAddItem = 0x2000076; + const int opcodeAddItemExplicit = 0x2000077; + const int opcodeGetItemCount = 0x2000078; + const int opcodeGetItemCountExplicit = 0x2000079; + const int opcodeRemoveItem = 0x200007a; + const int opcodeRemoveItemExplicit = 0x200007b; + const int opcodeEquip = 0x20001b3; + const int opcodeEquipExplicit = 0x20001b4; + const int opcodeGetArmorType = 0x20001d1; + const int opcodeGetArmorTypeExplicit = 0x20001d2; + const int opcodeHasItemEquipped = 0x20001d5; + const int opcodeHasItemEquippedExplicit = 0x20001d6; + const int opcodeHasSoulGem = 0x20001de; + const int opcodeHasSoulGemExplicit = 0x20001df; + const int opcodeGetWeaponType = 0x20001e0; + const int opcodeGetWeaponTypeExplicit = 0x20001e1; + } + + namespace Control + { + const int numberOfControls = 7; + + extern const char *controls[numberOfControls]; + + const int opcodeEnable = 0x200007e; + const int opcodeDisable = 0x2000085; + const int opcodeToggleCollision = 0x2000130; + const int opcodeClearForceRun = 0x2000154; + const int opcodeClearForceRunExplicit = 0x2000155; + const int opcodeForceRun = 0x2000156; + const int opcodeForceRunExplicit = 0x2000157; + const int opcodeClearForceSneak = 0x2000158; + const int opcodeClearForceSneakExplicit = 0x2000159; + const int opcodeForceSneak = 0x200015a; + const int opcodeForceSneakExplicit = 0x200015b; + const int opcodeGetDisabled = 0x2000175; + const int opcodeGetPcRunning = 0x20001c9; + const int opcodeGetPcSneaking = 0x20001ca; + const int opcodeGetForceRun = 0x20001cb; + const int opcodeGetForceSneak = 0x20001cc; + const int opcodeGetForceRunExplicit = 0x20001cd; + const int opcodeGetForceSneakExplicit = 0x20001ce; + } + + namespace Dialogue + { + const int opcodeJournal = 0x2000133; + const int opcodeSetJournalIndex = 0x2000134; + const int opcodeGetJournalIndex = 0x2000135; + const int opcodeAddTopic = 0x200013a; + const int opcodeChoice = 0x2000a; + const int opcodeForceGreeting = 0x200014f; + const int opcodeForceGreetingExplicit = 0x2000150; + const int opcodeGoodbye = 0x2000152; + const int opcodeSetReputation = 0x20001ad; + const int opcodeModReputation = 0x20001ae; + const int opcodeSetReputationExplicit = 0x20001af; + const int opcodeModReputationExplicit = 0x20001b0; + const int opcodeGetReputation = 0x20001b1; + const int opcodeGetReputationExplicit = 0x20001b2; + const int opcodeSameFaction = 0x20001b5; + const int opcodeSameFactionExplicit = 0x20001b6; + } + + namespace Gui + { + const int opcodeEnableBirthMenu = 0x200000e; + const int opcodeEnableClassMenu = 0x200000f; + const int opcodeEnableNameMenu = 0x2000010; + const int opcodeEnableRaceMenu = 0x2000011; + const int opcodeEnableStatsReviewMenu = 0x2000012; + const int opcodeEnableInventoryMenu = 0x2000013; + const int opcodeEnableMagicMenu = 0x2000014; + const int opcodeEnableMapMenu = 0x2000015; + const int opcodeEnableStatsMenu = 0x2000016; + const int opcodeEnableRest = 0x2000017; + const int opcodeShowRestMenu = 0x2000018; + const int opcodeGetButtonPressed = 0x2000137; + const int opcodeToggleFogOfWar = 0x2000145; + const int opcodeToggleFullHelp = 0x2000151; + const int opcodeShowMap = 0x20001a0; + const int opcodeFillMap = 0x20001a1; + } + + namespace Misc + { + const int opcodeXBox = 0x200000c; + const int opcodeOnActivate = 0x200000d; + const int opcodeActivate = 0x2000075; + const int opcodeLock = 0x20004; + const int opcodeLockExplicit = 0x20005; + const int opcodeUnlock = 0x200008c; + const int opcodeUnlockExplicit = 0x200008d; + const int opcodeToggleCollisionDebug = 0x2000132; + const int opcodeToggleCollisionBoxes = 0x20001ac; + const int opcodeToggleWireframe = 0x200013b; + const int opcodeFadeIn = 0x200013c; + const int opcodeFadeOut = 0x200013d; + const int opcodeFadeTo = 0x200013e; + const int opcodeToggleWater = 0x2000144; + const int opcodeTogglePathgrid = 0x2000146; + const int opcodeDontSaveObject = 0x2000153; + const int opcodeToggleVanityMode = 0x2000174; + const int opcodeGetPcSleep = 0x200019f; + const int opcodeWakeUpPc = 0x20001a2; + const int opcodeGetLocked = 0x20001c7; + const int opcodeGetLockedExplicit = 0x20001c8; + const int opcodeGetEffect = 0x20001cf; + const int opcodeGetEffectExplicit = 0x20001d0; + const int opcodeAddSoulGem = 0x20001f3; + const int opcodeAddSoulGemExplicit = 0x20001f4; + const int opcodeRemoveSoulGem = 0x20001f5; + const int opcodeRemoveSoulGemExplicit = 0x20001f6; + const int opcodeDrop = 0x20001f8; + const int opcodeDropExplicit = 0x20001f9; + const int opcodeDropSoulGem = 0x20001fa; + const int opcodeDropSoulGemExplicit = 0x20001fb; + const int opcodeGetAttacked = 0x20001d3; + const int opcodeGetAttackedExplicit = 0x20001d4; + const int opcodeGetWeaponDrawn = 0x20001d7; + const int opcodeGetWeaponDrawnExplicit = 0x20001d8; + const int opcodeGetSpellEffects = 0x20001db; + const int opcodeGetSpellEffectsExplicit = 0x20001dc; + const int opcodeGetCurrentTime = 0x20001dd; + 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; + const int opcodeHitOnMe = 0x2000213; + const int opcodeHitOnMeExplicit = 0x2000214; + const int opcodeDisableTeleporting = 0x2000215; + const int opcodeEnableTeleporting = 0x2000216; + const int opcodeShowVars = 0x200021d; + const int opcodeShowVarsExplicit = 0x200021e; + const int opcodeToggleGodMode = 0x200021f; + } + + namespace Sky + { + const int opcodeToggleSky = 0x2000021; + const int opcodeTurnMoonWhite = 0x2000022; + const int opcodeTurnMoonRed = 0x2000023; + const int opcodeGetMasserPhase = 0x2000024; + const int opcodeGetSecundaPhase = 0x2000025; + const int opcodeGetCurrentWeather = 0x200013f; + const int opcodeChangeWeather = 0x2000140; + const int opcodeModRegion = 0x20026; + } + + namespace Sound + { + const int opcodeSay = 0x2000001; + const int opcodeSayDone = 0x2000002; + const int opcodeStreamMusic = 0x2000003; + const int opcodePlaySound = 0x2000004; + const int opcodePlaySoundVP = 0x2000005; + const int opcodePlaySound3D = 0x2000006; + const int opcodePlaySound3DVP = 0x2000007; + const int opcodePlayLoopSound3D = 0x2000008; + const int opcodePlayLoopSound3DVP = 0x2000009; + const int opcodeStopSound = 0x200000a; + const int opcodeGetSoundPlaying = 0x200000b; + + const int opcodeSayExplicit = 0x2000019; + const int opcodeSayDoneExplicit = 0x200001a; + const int opcodePlaySound3DExplicit = 0x200001b; + const int opcodePlaySound3DVPExplicit = 0x200001c; + const int opcodePlayLoopSound3DExplicit = 0x200001d; + const int opcodePlayLoopSound3DVPExplicit = 0x200001e; + const int opcodeStopSoundExplicit = 0x200001f; + const int opcodeGetSoundPlayingExplicit = 0x2000020; + } + + namespace Stats + { + const int numberOfAttributes = 8; + const int numberOfDynamics = 3; + const int numberOfSkills = 27; + + const int opcodeGetAttribute = 0x2000027; + const int opcodeGetAttributeExplicit = 0x200002f; + const int opcodeSetAttribute = 0x2000037; + const int opcodeSetAttributeExplicit = 0x200003f; + const int opcodeModAttribute = 0x2000047; + const int opcodeModAttributeExplicit = 0x200004f; + + const int opcodeGetDynamic = 0x2000057; + const int opcodeGetDynamicExplicit = 0x200005a; + const int opcodeSetDynamic = 0x200005d; + const int opcodeSetDynamicExplicit = 0x2000060; + const int opcodeModDynamic = 0x2000063; + const int opcodeModDynamicExplicit = 0x2000066; + const int opcodeModCurrentDynamic = 0x2000069; + const int opcodeModCurrentDynamicExplicit = 0x200006c; + const int opcodeGetDynamicGetRatio = 0x200006f; + const int opcodeGetDynamicGetRatioExplicit = 0x2000072; + + const int opcodeGetSkill = 0x200008e; + const int opcodeGetSkillExplicit = 0x20000a9; + const int opcodeSetSkill = 0x20000c4; + const int opcodeSetSkillExplicit = 0x20000df; + const int opcodeModSkill = 0x20000fa; + const int opcodeModSkillExplicit = 0x2000115; + + const int opcodeGetPCCrimeLevel = 0x20001ec; + const int opcodeSetPCCrimeLevel = 0x20001ed; + const int opcodeModPCCrimeLevel = 0x20001ee; + + const int opcodeAddSpell = 0x2000147; + const int opcodeAddSpellExplicit = 0x2000148; + const int opcodeRemoveSpell = 0x2000149; + const int opcodeRemoveSpellExplicit = 0x200014a; + const int opcodeGetSpell = 0x200014b; + const int opcodeGetSpellExplicit = 0x200014c; + + const int opcodePCRaiseRank = 0x2000b; + const int opcodePCLowerRank = 0x2000c; + const int opcodePCJoinFaction = 0x2000d; + const int opcodeGetPCRank = 0x2000e; + const int opcodeGetPCRankExplicit = 0x2000f; + const int opcodeModDisposition = 0x200014d; + const int opcodeModDispositionExplicit = 0x200014e; + const int opcodeSetDisposition = 0x20001a4; + const int opcodeSetDispositionExplicit = 0x20001a5; + const int opcodeGetDisposition = 0x20001a6; + const int opcodeGetDispositionExplicit = 0x20001a7; + + const int opcodeGetLevel = 0x200018c; + const int opcodeGetLevelExplicit = 0x200018d; + const int opcodeSetLevel = 0x200018e; + const int opcodeSetLevelExplicit = 0x200018f; + + const int opcodeGetDeadCount = 0x20001a3; + + const int opcodeGetPCFacRep = 0x20012; + const int opcodeGetPCFacRepExplicit = 0x20013; + const int opcodeSetPCFacRep = 0x20014; + const int opcodeSetPCFacRepExplicit = 0x20015; + const int opcodeModPCFacRep = 0x20016; + const int opcodeModPCFacRepExplicit = 0x20017; + + const int opcodeGetCommonDisease = 0x20001a8; + const int opcodeGetCommonDiseaseExplicit = 0x20001a9; + const int opcodeGetBlightDisease = 0x20001aa; + const int opcodeGetBlightDiseaseExplicit = 0x20001ab; + + const int opcodeGetRace = 0x20001d9; + const int opcodeGetRaceExplicit = 0x20001da; + + const int opcodePcExpelled = 0x20018; + const int opcodePcExpelledExplicit = 0x20019; + const int opcodePcExpell = 0x2001a; + const int opcodePcExpellExplicit = 0x2001b; + const int opcodePcClearExpelled = 0x2001c; + const int opcodePcClearExpelledExplicit = 0x2001d; + const int opcodeRaiseRank = 0x20001e8; + const int opcodeRaiseRankExplicit = 0x20001e9; + const int opcodeLowerRank = 0x20001ea; + const int opcodeLowerRankExplicit = 0x20001eb; + const int opcodeOnDeath = 0x20001fc; + const int opcodeOnDeathExplicit = 0x2000205; + + const int opcodeBecomeWerewolf = 0x2000217; + const int opcodeBecomeWerewolfExplicit = 0x2000218; + const int opcodeUndoWerewolf = 0x2000219; + const int opcodeUndoWerewolfExplicit = 0x200021a; + const int opcodeSetWerewolfAcrobatics = 0x200021b; + const int opcodeSetWerewolfAcrobaticsExplicit = 0x200021c; + const int opcodeIsWerewolf = 0x20001fd; + const int opcodeIsWerewolfExplicit = 0x20001fe; + + const int opcodeGetWerewolfKills = 0x20001e2; + } + + namespace Transformation + { + const int opcodeSetScale = 0x2000164; + const int opcodeSetScaleExplicit = 0x2000165; + const int opcodeSetAngle = 0x2000166; + const int opcodeSetAngleExplicit = 0x2000167; + const int opcodeGetScale = 0x2000168; + const int opcodeGetScaleExplicit = 0x2000169; + const int opcodeGetAngle = 0x200016a; + const int opcodeGetAngleExplicit = 0x200016b; + const int opcodeGetPos = 0x2000190; + const int opcodeGetPosExplicit = 0x2000191; + const int opcodeSetPos = 0x2000192; + 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; + const int opcodePositionCellExplicit = 0x2000199; + + const int opcodePlaceItemCell = 0x200019a; + const int opcodePlaceItem = 0x200019b; + const int opcodePlaceAtPc = 0x200019c; + const int opcodePlaceAtMe = 0x200019d; + 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; + } + + namespace User + { + const int opcodeUser1 = 0x200016c; + const int opcodeUser2 = 0x200016d; + const int opcodeUser3 = 0x200016e; + const int opcodeUser3Explicit = 0x200016f; + const int opcodeUser4 = 0x2000170; + const int opcodeUser4Explicit = 0x2000171; + } +} + +#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..816443c44 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); @@ -331,7 +343,11 @@ namespace Compiler } else if (!(c=='"' && name.empty())) { - if (!(std::isalpha (c) || std::isdigit (c) || c=='_')) + if (!(std::isalpha (c) || std::isdigit (c) || c=='_' || c=='`' || + /// \todo add an option to disable the following hack. Also, find out who is + /// responsible for allowing it in the first place and meet up with that person in + /// a dark alley. + (c=='-' && !name.empty() && std::isalpha (mStream.peek())))) { putback (c); break; @@ -359,7 +375,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 +442,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 +462,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); @@ -472,7 +509,7 @@ namespace Compiler Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream, const Extensions *extensions) : mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions), - mPutback (Putback_None) + mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0) { } 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/attr.cpp b/components/esm/attr.cpp index e20050bb3..75cf8b006 100644 --- a/components/esm/attr.cpp +++ b/components/esm/attr.cpp @@ -13,6 +13,17 @@ const Attribute::AttributeID Attribute::sAttributeIds[Attribute::Length] = { Attribute::Luck }; +const std::string Attribute::sAttributeNames[Attribute::Length] = { + "Strength", + "Intelligence", + "Willpower", + "Agility", + "Speed", + "Endurance", + "Personality", + "Luck" +}; + const std::string Attribute::sGmstAttributeIds[Attribute::Length] = { "sAttributeStrength", "sAttributeIntelligence", diff --git a/components/esm/attr.hpp b/components/esm/attr.hpp index 2ed9b4f41..993de6ccb 100644 --- a/components/esm/attr.hpp +++ b/components/esm/attr.hpp @@ -28,6 +28,7 @@ struct Attribute std::string mName, mDescription; static const AttributeID sAttributeIds[Length]; + static const std::string sAttributeNames[Length]; static const std::string sGmstAttributeIds[Length]; static const std::string sGmstAttributeDescIds[Length]; static const std::string sAttributeIcons[Length]; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp new file mode 100644 index 000000000..95cf24d33 --- /dev/null +++ b/components/esm/cellref.cpp @@ -0,0 +1,87 @@ + +#include "cellref.hpp" + +#include "esmwriter.hpp" + +void ESM::CellRef::save(ESMWriter &esm) +{ + esm.writeHNT("FRMR", mRefnum); + esm.writeHNCString("NAME", mRefID); + + if (mScale != 1.0) { + esm.writeHNT("XSCL", mScale); + } + + esm.writeHNOCString("ANAM", mOwner); + esm.writeHNOCString("BNAM", mGlob); + esm.writeHNOCString("XSOL", mSoul); + + esm.writeHNOCString("CNAM", mFaction); + if (mFactIndex != -2) { + esm.writeHNT("INDX", mFactIndex); + } + + if (mEnchantmentCharge != -1) + esm.writeHNT("XCHG", mEnchantmentCharge); + + if (mCharge != -1) + esm.writeHNT("INTV", mCharge); + + if (mGoldValue != 1) { + esm.writeHNT("NAM9", mGoldValue); + } + + if (mTeleport) + { + esm.writeHNT("DODT", mDoorDest); + esm.writeHNOCString("DNAM", mDestCell); + } + + if (mLockLevel != -1) { + esm.writeHNT("FLTV", mLockLevel); + } + esm.writeHNOCString("KNAM", mKey); + esm.writeHNOCString("TNAM", mTrap); + + if (mReferenceBlocked != -1) { + esm.writeHNT("UNAM", mReferenceBlocked); + } + if (mFltv != 0) { + esm.writeHNT("FLTV", mFltv); + } + + esm.writeHNT("DATA", mPos, 24); + if (mNam0 != 0) { + esm.writeHNT("NAM0", mNam0); + } +} + +void ESM::CellRef::blank() +{ + mRefnum = 0; + mRefID.clear(); + mScale = 1; + mOwner.clear(); + mGlob.clear(); + mSoul.clear(); + mFaction.clear(); + mFactIndex = -1; + mCharge = 0; + mEnchantmentCharge = 0; + mGoldValue = 0; + mDestCell.clear(); + mLockLevel = 0; + mKey.clear(); + mTrap.clear(); + mReferenceBlocked = 0; + mFltv = 0; + mNam0 = 0; + + for (int i=0; i<3; ++i) + { + mDoorDest.pos[i] = 0; + mDoorDest.rot[i] = 0; + mPos.pos[i] = 0; + mPos.rot[i] = 0; + } +} \ No newline at end of file diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp new file mode 100644 index 000000000..31889914c --- /dev/null +++ b/components/esm/cellref.hpp @@ -0,0 +1,92 @@ +#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); + + void blank(); + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index 335d12337..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,28 +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; - int index; // Position of the parent file in the global list of loaded files -}; - // Data that is only present in save game files struct SaveData { @@ -113,7 +76,6 @@ 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. diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index b4f581d7e..51d86a2ee 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -15,11 +15,20 @@ ESM_Context ESMReader::getContext() return mCtx; } -ESMReader::ESMReader(void): - mBuffer(50*1024) +ESMReader::ESMReader() + : mBuffer(50*1024) + , mRecordFlags(0) + , mIdx(0) + , mGlobalReaderList(NULL) + , mEncoder(NULL) { } +int ESMReader::getFormat() const +{ + return mHeader.mFormat; +} + void ESMReader::restoreContext(const ESM_Context &rc) { // Reopen the file if necessary @@ -51,18 +60,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,42 +71,7 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) getRecHeader(); - // Get the header - getHNT(mCtx.header, "HEDR", 300); - - // Some mods abuse the header.version field for the version of the mod instead of the version of the file format, so we can only ignore it. - - 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 ae876edf8..edc724cd2 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,14 +30,12 @@ public: * *************************************************************************/ - int getVer() const { return mCtx.header.version; } - float getFVer() const { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } - int getSpecial() const { return mSpf; } - int getType() const { return mCtx.header.type; } - const std::string getAuthor() const { return mCtx.header.author.toString(); } - const std::string getDesc() const { return mCtx.header.desc.toString(); } - const SaveData &getSaveData() const { return mSaveData; } - const MasterList &getMasters() const { return mMasters; } + 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,11 @@ public: void openRaw(const std::string &file); + /// Get the file size. Make sure that the file has been opened! + size_t getFileSize() { return mEsm->size(); } + /// Get the current position in the file. Make sure that the file has been opened! + size_t getFileOffset() { return mEsm->tell(); } + // 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 @@ -224,7 +221,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; } @@ -257,19 +254,23 @@ 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 b9dd5b57b..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; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 94e173b4c..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 @@ -22,11 +23,11 @@ class ESMWriter 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); @@ -80,7 +81,7 @@ public: { 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); @@ -90,14 +91,13 @@ public: 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/filter.cpp b/components/esm/filter.cpp new file mode 100644 index 000000000..7d4851a5f --- /dev/null +++ b/components/esm/filter.cpp @@ -0,0 +1,23 @@ + +#include "filter.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::Filter::load (ESMReader& esm) +{ + mFilter = esm.getHNString ("FILT"); + mDescription = esm.getHNString ("DESC"); +} + +void ESM::Filter::save (ESMWriter& esm) +{ + esm.writeHNCString ("FILT", mFilter); + esm.writeHNCString ("DESC", mDescription); +} + +void ESM::Filter::blank() +{ + mFilter.clear(); + mDescription.clear(); +} diff --git a/components/esm/filter.hpp b/components/esm/filter.hpp new file mode 100644 index 000000000..0fd564361 --- /dev/null +++ b/components/esm/filter.hpp @@ -0,0 +1,27 @@ +#ifndef COMPONENTS_ESM_FILTER_H +#define COMPONENTS_ESM_FILTER_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + struct Filter + { + std::string mId; + + std::string mDescription; + + std::string mFilter; + + 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/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 643fefda5..4b8d2b763 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -36,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 76a48e5ec..d8d0c1291 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -8,13 +8,10 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -#include -#include - namespace ESM { -/// Some overloaded copare operators. +/// Some overloaded compare operators. bool operator==(const MovedCellRef& ref, int pRefnum) { return (ref.mRefnum == pRefnum); @@ -25,59 +22,6 @@ bool operator==(const CellRef& ref, int pRefnum) return (ref.mRefnum == pRefnum); } -void CellRef::save(ESMWriter &esm) -{ - esm.writeHNT("FRMR", mRefnum); - esm.writeHNCString("NAME", mRefID); - - if (mScale != 1.0) { - esm.writeHNT("XSCL", mScale); - } - - esm.writeHNOCString("ANAM", mOwner); - esm.writeHNOCString("BNAM", mGlob); - esm.writeHNOCString("XSOL", mSoul); - - esm.writeHNOCString("CNAM", mFaction); - if (mFactIndex != -2) { - esm.writeHNT("INDX", mFactIndex); - } - - if (mCharge != -1.0) { - esm.writeHNT("XCHG", mCharge); - } - - if (mIntv != -1) { - esm.writeHNT("INTV", mIntv); - } - if (mNam9 != 0) { - esm.writeHNT("NAM9", mNam9); - } - - if (mTeleport) - { - esm.writeHNT("DODT", mDoorDest); - esm.writeHNOCString("DNAM", mDestCell); - } - - if (mLockLevel != -1) { - esm.writeHNT("FLTV", mLockLevel); - } - esm.writeHNOCString("KNAM", mKey); - esm.writeHNOCString("TNAM", mTrap); - - if (mUnam != -1) { - esm.writeHNT("UNAM", mUnam); - } - if (mFltv != 0) { - esm.writeHNT("FLTV", mFltv); - } - - esm.writeHNT("DATA", mPos, 24); - if (mNam0 != 0) { - esm.writeHNT("NAM0", mNam0); - } -} void Cell::load(ESMReader &esm, bool saveContext) { @@ -133,38 +77,13 @@ void Cell::load(ESMReader &esm, bool saveContext) } } -void Cell::load(ESMReader &esm, MWWorld::ESMStore &store) +void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool { this->load(esm, false); +} - // preload moved references - while (esm.isNextSub("MVRF")) { - CellRef ref; - MovedCellRef cMRef; - getNextMVRF(esm, cMRef); - - MWWorld::Store &cStore = const_cast&>(store.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. - 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. - 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; - } - +void Cell::postLoad(ESMReader &esm) +{ // Save position of the cell references and move on mContextList.push_back(esm.getContext()); esm.skipRecord(); @@ -203,7 +122,7 @@ void Cell::save(ESMWriter &esm) void Cell::restore(ESMReader &esm, int iCtx) const { - esm.restoreContext(mContextList[iCtx]); + esm.restoreContext(mContextList.at (iCtx)); } std::string Cell::getDescription() const @@ -234,6 +153,14 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // That should be it, I haven't seen any other fields yet. } + // NAM0 sometimes appears here, sometimes further on + ref.mNam0 = 0; + if (esm.isNextSub("NAM0")) + { + esm.getHT(ref.mNam0); + //esm.getHNOT(NAM0, "NAM0"); + } + esm.getHNT(ref.mRefnum, "FRMR"); ref.mRefID = esm.getHNString("NAME"); @@ -245,7 +172,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // 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 ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); global = masters[local-1].index + 1; ref.mRefnum |= global << 24; // insert global plugin ID } @@ -285,13 +212,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")) @@ -309,9 +238,9 @@ 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.getHNOT(ref.mPos, "DATA", 24); @@ -322,7 +251,6 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // 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")) { esm.getHT(ref.mNam0); @@ -348,11 +276,29 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) int local = (mref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; mref.mRefnum &= 0x00ffffff; // delete old plugin ID - const ESM::ESMReader::MasterList &masters = esm.getMasters(); + 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 7db6dbe77..51288b291 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -7,93 +7,19 @@ #include "esmcommon.hpp" #include "defs.hpp" - +#include "cellref.hpp" namespace MWWorld { class ESMStore; } - 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 -{ -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; - - // 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 - - // No idea - occurs ONCE in Morrowind.esm, for an activator - signed char mUnam; - - // 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); -}; - /* 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. @@ -170,7 +96,8 @@ struct Cell CellRefTracker mLeasedRefs; MovedCellRefTracker mMovedRefs; - void load(ESMReader &esm, MWWorld::ESMStore &store); + 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. @@ -192,6 +119,11 @@ struct Cell return mData.mY; } + bool hasWater() const + { + return (mData.mFlags&HasWater); + } + // Restore the given reader to the stored position. Will try to open // the file matching the stored file name. If you want to read from // somewhere other than the file system, you need to pre-open the @@ -214,6 +146,9 @@ struct Cell * 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/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/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 89b48c27d..60c475040 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -66,10 +66,11 @@ Land::Land() , mX(0) , mY(0) , mEsm(NULL) -// , hasData(false) , mDataTypes(0) , mDataLoaded(false) , mLandData(NULL) + , mPlugin(0) + , mHasData(false) { } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index c1cce5e7e..9c1fd1f5c 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -70,7 +70,7 @@ struct Land }; #pragma pack(pop) - typedef uint8_t VNML[LAND_NUM_VERTS * 3]; + typedef signed char VNML[LAND_NUM_VERTS * 3]; struct LandData { 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.cpp b/components/esm/loadmgef.cpp index 4aef97838..060645b5f 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -83,7 +83,8 @@ void MagicEffect::save(ESMWriter &esm) esm.writeHNOString("DESC", mDescription); } -std::string MagicEffect::effectIdToString(short effectID) + +static std::map genNameMap() { // Map effect ID to GMST name // http://www.uesp.net/morrow/hints/mweffects.shtml @@ -235,10 +236,43 @@ std::string MagicEffect::effectIdToString(short effectID) // tribunal names[137] ="sEffectSummonFabricant"; - if (names.find(effectID) == names.end()) - throw std::runtime_error( std::string("Unimplemented effect ID ") + boost::lexical_cast(effectID)); + return names; +} +const std::map MagicEffect::sNames = genNameMap(); + +const std::string &MagicEffect::effectIdToString(short effectID) +{ + std::map::const_iterator name = sNames.find(effectID); + if(name == sNames.end()) + throw std::runtime_error(std::string("Unimplemented effect ID ")+boost::lexical_cast(effectID)); + + return name->second; +} + +class FindSecond { + const std::string &mName; - return names[effectID]; +public: + FindSecond(const std::string &name) : mName(name) { } + + bool operator()(const std::pair &item) const + { + if(Misc::StringUtils::ciEqual(item.second, mName)) + return true; + return false; + } +}; + +short MagicEffect::effectStringToId(const std::string &effect) +{ + std::map::const_iterator name; + + name = std::find_if(sNames.begin(), sNames.end(), FindSecond(effect)); + if(name == sNames.end()) + throw std::runtime_error(std::string("Unimplemented effect ")+effect); + + return name->first; } + } diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 93c59f9cb..b74efb466 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -2,6 +2,7 @@ #define OPENMW_ESM_MGEF_H #include +#include namespace ESM { @@ -42,7 +43,10 @@ struct MagicEffect float mSpeed, mSize, mSizeCap; }; // 36 bytes - static std::string effectIdToString(short effectID); + static const std::map sNames; + + static const std::string &effectIdToString(short effectID); + static short effectStringToId(const std::string &effect); MEDTstruct mData; @@ -66,6 +70,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/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 56c2f8c74..882addcb9 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -56,14 +56,14 @@ void Pathgrid::load(ESMReader &esm) std::vector::const_iterator rawIt = rawConnections.begin(); int pointIndex = 0; mEdges.reserve(edgeCount); - for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); it++, pointIndex++) + for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex) { unsigned char connectionNum = (*it).mConnectionNum; for (int i = 0; i < connectionNum; ++i) { Edge edge; edge.mV0 = pointIndex; edge.mV1 = *rawIt; - rawIt++; + ++rawIt; mEdges.push_back(edge); } } 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..676a835c3 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -1,10 +1,43 @@ #include "loadskil.hpp" +#include + +#include + #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { + const std::string Skill::sSkillNames[Length] = { + "Block", + "Armorer", + "Mediumarmor", + "Heavyarmor", + "Bluntweapon", + "Longblade", + "Axe", + "Spear", + "Athletics", + "Enchant", + "Destruction", + "Alteration", + "Illusion", + "Conjuration", + "Mysticism", + "Restoration", + "Alchemy", + "Unarmored", + "Security", + "Sneak", + "Acrobatics", + "Lightarmor", + "Shortblade", + "Marksman", + "Mercantile", + "Speechcraft", + "Handtohand", + }; const std::string Skill::sSkillNameIds[Length] = { "sSkillBlock", "sSkillArmorer", @@ -98,11 +131,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 && index sSkillIds; void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). + + static std::string indexToId (int index); }; } #endif diff --git a/components/esm/loadsoun.cpp b/components/esm/loadsoun.cpp index 87a08d2d3..07af2b5e9 100644 --- a/components/esm/loadsoun.cpp +++ b/components/esm/loadsoun.cpp @@ -23,4 +23,12 @@ void Sound::save(ESMWriter &esm) esm.writeHNT("DATA", mData, 3); } + void Sound::blank() + { + mSound.clear(); + + mData.mVolume = 128; + mData.mMinRange = 0; + mData.mMaxRange = 255; + } } diff --git a/components/esm/loadsoun.hpp b/components/esm/loadsoun.hpp index 8f59f690a..f8e38ac09 100644 --- a/components/esm/loadsoun.hpp +++ b/components/esm/loadsoun.hpp @@ -21,6 +21,9 @@ struct Sound 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/loadspel.cpp b/components/esm/loadspel.cpp index b0f1ca64b..8149fe4ce 100644 --- a/components/esm/loadspel.cpp +++ b/components/esm/loadspel.cpp @@ -20,4 +20,14 @@ void Spell::save(ESMWriter &esm) mEffects.save(esm); } + void Spell::blank() + { + mData.mType = 0; + mData.mCost = 0; + mData.mFlags = 0; + + mName.clear(); + + mEffects.mList.clear(); + } } diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index 0d5e0be52..3a620962d 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -43,6 +43,9 @@ struct Spell 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/loadstat.cpp b/components/esm/loadstat.cpp index 92c9ebc71..c9346dafc 100644 --- a/components/esm/loadstat.cpp +++ b/components/esm/loadstat.cpp @@ -15,4 +15,8 @@ void Static::save(ESMWriter &esm) esm.writeHNCString("MODL", mModel); } + void Static::blank() + { + mModel.clear(); + } } diff --git a/components/esm/loadstat.hpp b/components/esm/loadstat.hpp index 790a71147..1adb7d05b 100644 --- a/components/esm/loadstat.hpp +++ b/components/esm/loadstat.hpp @@ -26,6 +26,9 @@ struct Static 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/loadtes3.cpp b/components/esm/loadtes3.cpp new file mode 100644 index 000000000..74d578ba7 --- /dev/null +++ b/components/esm/loadtes3.cpp @@ -0,0 +1,53 @@ + +#include "loadtes3.hpp" + +#include "esmcommon.hpp" +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::Header::blank() +{ + mData.version = ESM::VER_13; + mData.type = 0; + mData.author.assign (""); + mData.desc.assign (""); + mData.records = 0; + mFormat = CurrentFormat; +} + +void ESM::Header::load (ESMReader &esm) +{ + esm.getHNT (mData, "HEDR", 300); + + if (esm.isNextSub ("FORM")) + { + esm.getHT (mFormat); + if (mFormat<0) + esm.fail ("invalid format code"); + } + else + mFormat = 0; + + while (esm.isNextSub ("MAST")) + { + MasterData m; + m.name = esm.getHString(); + m.size = esm.getHNLong ("DATA"); + mMaster.push_back (m); + } +} + +void ESM::Header::save (ESMWriter &esm) +{ + esm.writeHNT ("HEDR", mData, 300); + + if (mFormat>0) + 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 index d25072e54..a7859d128 100644 --- a/components/esm/variant.cpp +++ b/components/esm/variant.cpp @@ -186,7 +186,7 @@ void ESM::Variant::write (std::ostream& stream) const case VT_String: - stream << "variant string: \"" << mData->getString() << "\2"; + stream << "variant string: \"" << mData->getString() << "\""; break; } } diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index 8c5f3b3d4..2bba60a15 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -11,7 +11,7 @@ namespace ESM enum VarType { - VT_Unknown, + VT_Unknown = 0, VT_None, VT_Short, // stored as a float, kinda VT_Int, diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index 316534124..02a6766b0 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -12,6 +11,8 @@ #include "datafilesmodel.hpp" +#include + DataFilesModel::DataFilesModel(QObject *parent) : QAbstractTableModel(parent) { @@ -293,8 +294,8 @@ void DataFilesModel::addFiles(const QString &path) fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); + std::vector mlist = fileReader.getMasters(); - ESM::ESMReader::MasterList mlist = fileReader.getMasters(); QStringList masters; for (unsigned int i = 0; i < mlist.size(); ++i) { diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 56e55a98d..75c877dc5 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -56,7 +56,7 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m } -void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) +void ConfigurationManager::processPaths(Files::PathContainer& dataDirs, bool create) { std::string path; for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) @@ -94,6 +94,18 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) if (!boost::filesystem::is_directory(*it)) { + if (create) + { + try + { + boost::filesystem::create_directories (*it); + } + catch (...) {} + + if (boost::filesystem::is_directory(*it)) + continue; + } + (*it).clear(); } } diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 9056e792d..4df871664 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 @@ -28,7 +30,9 @@ struct ConfigurationManager void readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description); - void processPaths(Files::PathContainer& dataDirs); + + void processPaths(Files::PathContainer& dataDirs, bool create = false); + ///< \param create Try creating the directory, if it does not exist. /**< Fixed paths */ const boost::filesystem::path& getGlobalPath() const; @@ -48,7 +52,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/interpreter/defines.cpp b/components/interpreter/defines.cpp index 18e5f81c1..5774c96ae 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -7,7 +7,7 @@ namespace Interpreter{ - bool Check(const std::string str, const std::string escword, unsigned int* i, unsigned int* start){ + bool Check(const std::string& str, const std::string& escword, unsigned int* i, unsigned int* start){ bool retval = str.find(escword) == 0; if(retval){ (*i) += escword.length(); @@ -18,12 +18,11 @@ namespace Interpreter{ std::vector globals; - bool longerStr(const std::string a, const std::string b){ + bool longerStr(const std::string& a, const std::string& b){ return a.length() > b.length(); } 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..bb0dffb87 100644 --- a/components/interpreter/runtime.cpp +++ b/components/interpreter/runtime.cpp @@ -7,7 +7,7 @@ namespace Interpreter { - Runtime::Runtime() : mContext (0), mCode (0), mPC (0) {} + Runtime::Runtime() : mContext (0), mCode (0), mPC (0), mCodeSize(0) {} int Runtime::getPC() const { @@ -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/loadinglistener/loadinglistener.hpp b/components/loadinglistener/loadinglistener.hpp new file mode 100644 index 000000000..483d52491 --- /dev/null +++ b/components/loadinglistener/loadinglistener.hpp @@ -0,0 +1,35 @@ +#ifndef COMPONENTS_LOADINGLISTENER_H +#define COMPONENTS_LOADINGLISTENER_H + +namespace Loading +{ + class Listener + { + public: + virtual void setLabel (const std::string& label) = 0; + + // Use ScopedLoad instead of using these directly + virtual void loadingOn() = 0; + virtual void loadingOff() = 0; + + /// Indicate that some progress has been made, without specifying how much + virtual void indicateProgress () = 0; + + virtual void setProgressRange (size_t range) = 0; + virtual void setProgress (size_t value) = 0; + virtual void increaseProgress (size_t increase) = 0; + + /// Indicate the scene is now ready to be shown + virtual void removeWallpaper() = 0; + }; + + // Used for stopping a loading sequence when the object goes out of scope + struct ScopedLoad + { + ScopedLoad(Listener* l) : mListener(l) { mListener->loadingOn(); } + ~ScopedLoad() { mListener->loadingOff(); } + Listener* mListener; + }; +} + +#endif diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 029b617e1..d41463cfc 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -35,6 +35,26 @@ public: return true; } + static int ciCompareLen(const std::string &x, const std::string &y, size_t len) + { + std::string::const_iterator xit = x.begin(); + std::string::const_iterator yit = y.begin(); + for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len) + { + int res = *xit - *yit; + if(res != 0 && std::tolower(*xit) != std::tolower(*yit)) + return (res > 0) ? 1 : -1; + } + if(len > 0) + { + if(xit != x.end()) + return 1; + if(yit != y.end()) + return -1; + } + return 0; + } + /// Transforms input string to lower case w/o copy static std::string &toLower(std::string &inout) { std::transform( 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 36c9a82ac..6acb8ff20 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -66,12 +66,14 @@ typedef Named NiSequenceStreamHelper; class NiParticleGrowFade : public Controlled { public: + float growTime; + float fadeTime; + void read(NIFStream *nif) { Controlled::read(nif); - - // Two floats. - nif->skip(8); + growTime = nif->getFloat(); + fadeTime = nif->getFloat(); } }; @@ -96,12 +98,23 @@ public: class NiGravity : public Controlled { public: + 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(); } }; diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 8331b93b7..011e0e445 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -62,20 +62,106 @@ public: } }; -class NiBSPArrayController : public Controller +class NiParticleSystemController : public Controller { public: + 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 { @@ -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 9bdba6396..f1f34184b 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -165,23 +165,28 @@ public: class NiAutoNormalParticlesData : public ShapeData { public: + int numParticles; + + float particleRadius; + int activeCount; + 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()); } } }; @@ -189,16 +194,16 @@ public: class NiRotatingParticlesData : public NiAutoNormalParticlesData { public: + 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()); } } }; @@ -294,13 +299,17 @@ public: float time; char isSet; }; + std::vector mVis; 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(); + } } }; @@ -389,16 +398,13 @@ struct NiMorphData : public Record { 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); } } }; diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index bf05e7576..402eadefb 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -207,9 +207,9 @@ struct RecordFactoryEntry { static const RecordFactoryEntry recordFactories [] = { { "NiNode", &construct , RC_NiNode }, - { "AvoidNode", &construct , RC_NiNode }, - { "NiBSParticleNode", &construct , RC_NiNode }, - { "NiBSAnimationNode", &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 }, @@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = { { "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 }, @@ -345,14 +346,18 @@ void NIFFile::parse() } } - /* 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. - */ + size_t rootNum = nif.getUInt(); + roots.resize(rootNum); - // Once parsing is done, do post-processing. - for(size_t i=0; ipost(this); + 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 @@ -380,9 +385,47 @@ void NiSkinInstance::post(NIFFile *nif) } } + +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 < records; + /// Root list + std::vector roots; + /// Parse the file void parse(); @@ -115,9 +118,18 @@ public: assert(res != NULL); return res; } - /// Number of records size_t numRecords() { return records.size(); } + + /// Get a given root + Record *getRoot(size_t index=0) + { + Record *res = roots.at(index); + assert(res != NULL); + return res; + } + /// Number of roots + size_t numRoots() { return roots.size(); } }; diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 02b931b7e..a2595d17b 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -168,6 +168,12 @@ public: 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(); + } }; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index ab92d74f8..917bc8add 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -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,13 +128,17 @@ 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(NIFStream *nif) { diff --git a/components/nif/property.hpp b/components/nif/property.hpp index cd1e0a5d1..06c8260ce 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -64,7 +64,7 @@ public: bool inUse; NiSourceTexturePtr texture; - int clamp, set, filter; + int clamp, uvSet, filter; short unknown2; void read(NIFStream *nif) @@ -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,6 +109,17 @@ 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(NIFStream *nif) @@ -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 @@ -313,10 +324,10 @@ struct S_StencilProperty } }; -typedef StructPropT NiAlphaProperty; -typedef StructPropT NiMaterialProperty; -typedef StructPropT NiVertexColorProperty; -typedef StructPropT NiStencilProperty; +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 3a3cd9b84..87e342dca 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -36,9 +36,11 @@ enum RecordType { RC_MISSING = 0, RC_NiNode, + RC_AvoidNode, RC_NiTriShape, RC_NiRotatingParticles, RC_NiAutoNormalParticles, + RC_NiBSParticleNode, RC_NiCamera, RC_NiTexturingProperty, RC_NiMaterialProperty, @@ -59,6 +61,8 @@ enum RecordType RC_NiMaterialColorController, RC_NiBSPArrayController, RC_NiParticleSystemController, + RC_NiFlipController, + RC_NiBSAnimationNode, RC_NiLight, RC_NiTextureEffect, RC_NiVertWeightsExtraData, diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index c5bafea12..1a4fc235b 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -71,16 +71,26 @@ public: } /// Look up the actual object from the index - X* getPtr() const + const X* getPtr() const { assert(ptr != NULL); return ptr; } - X& get() const + X* getPtr() + { + assert(ptr != NULL); + return ptr; + } + + const X& get() const + { return *getPtr(); } + X& get() { return *getPtr(); } /// Syntactic sugar - X* operator->() const + const X* operator->() const + { return getPtr(); } + X* operator->() { return getPtr(); } /// Pointers are allowed to be empty @@ -116,6 +126,8 @@ public: const Ptr& operator[](size_t index) const { return list.at(index); } + Ptr& operator[](size_t index) + { return list.at(index); } size_t length() const { return list.size(); } @@ -163,6 +175,7 @@ typedef RecordPtrT NiAutoNormalParticlesDataPtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; +typedef RecordListT NiSourceTextureList; } // Namespace #endif diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index af1243346..9c4fee7a0 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -75,12 +75,12 @@ btVector3 ManualBulletShapeLoader::getbtVector(Ogre::Vector3 const &v) void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) { - cShape = static_cast(resource); - resourceName = cShape->getName(); - cShape->mCollide = false; + mShape = static_cast(resource); + mResourceName = mShape->getName(); + mShape->mCollide = false; mBoundingBox = NULL; - cShape->mBoxTranslation = Ogre::Vector3(0,0,0); - cShape->mBoxRotation = Ogre::Quaternion::IDENTITY; + mShape->mBoxTranslation = Ogre::Vector3(0,0,0); + mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; mHasShape = false; btTriangleMesh* mesh1 = new btTriangleMesh(); @@ -89,63 +89,62 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) // 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::ptr pnif (Nif::NIFFile::create (mResourceName.substr(0, mResourceName.length()-7))); Nif::NIFFile & nif = *pnif.get (); - if (nif.numRecords() < 1) + if (nif.numRoots() < 1) { - warn("Found no records in NIF."); + warn("Found no root nodes in NIF."); return; } - // The first record is assumed to be the root node - Nif::Record *r = nif.getRecord(0); + Nif::Record *r = nif.getRoot(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."); + warn("First root in file was not a node, but a " + + r->recName + ". Skipping file."); return; } - bool hasCollisionNode = hasRootCollisionNode(node); + mShape->mHasCollisionNode = hasRootCollisionNode(node); //do a first pass - handleNode(mesh1, node,0,hasCollisionNode,false,false); + handleNode(mesh1, node,0,false,false,false); if(mBoundingBox != NULL) { - cShape->mCollisionShape = mBoundingBox; + mShape->mCollisionShape = mBoundingBox; delete mesh1; } - else if (mHasShape && !cShape->mIgnore && cShape->mCollide) + else if (mHasShape && mShape->mCollide) { - cShape->mCollisionShape = new TriangleMeshShape(mesh1,true); + mShape->mCollisionShape = new TriangleMeshShape(mesh1,true); } else delete mesh1; //second pass which create a shape for raycasting. - resourceName = cShape->getName(); - cShape->mCollide = false; + mResourceName = mShape->getName(); + mShape->mCollide = false; mBoundingBox = NULL; - cShape->mBoxTranslation = Ogre::Vector3(0,0,0); - cShape->mBoxRotation = Ogre::Quaternion::IDENTITY; + mShape->mBoxTranslation = Ogre::Vector3(0,0,0); + mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; mHasShape = false; btTriangleMesh* mesh2 = new btTriangleMesh(); - handleNode(mesh2, node,0,hasCollisionNode,true,true); + handleNode(mesh2, node,0,true,true,false); if(mBoundingBox != NULL) { - cShape->mRaycastingShape = mBoundingBox; + mShape->mRaycastingShape = mBoundingBox; delete mesh2; } - else if (mHasShape && !cShape->mIgnore) + else if (mHasShape) { - cShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); + mShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); } else delete mesh2; @@ -174,8 +173,8 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) } void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *node, int flags, - bool hasCollisionNode, bool isCollisionNode, - bool raycasting) + bool isCollisionNode, + bool raycasting, bool isMarker) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. @@ -186,14 +185,16 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * else isCollisionNode = isCollisionNode && (node->recType != Nif::RC_RootCollisionNode); - // Marker objects: no collision + // Don't collide with AvoidNode shapes + if(node->recType == Nif::RC_AvoidNode) + flags |= 0x800; + + // Marker objects /// \todo don't do this in the editor std::string nodename = node->name; Misc::StringUtils::toLower(nodename); if (nodename.find("marker") != std::string::npos) - { - return; - } + isMarker = true; // Check for extra data Nif::Extra const *e = node; @@ -219,21 +220,22 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * // 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; + isMarker = true; } } - if (isCollisionNode || (!hasCollisionNode && !raycasting)) + if ( (isCollisionNode || (!mShape->mHasCollisionNode && !raycasting)) + && (!isMarker || (mShape->mHasCollisionNode && !raycasting))) { if(node->hasBounds) { - cShape->mBoxTranslation = node->boundPos; - cShape->mBoxRotation = node->boundRot; + mShape->mBoxTranslation = node->boundPos; + mShape->mBoxRotation = node->boundRot; mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ)); } else if(node->recType == Nif::RC_NiTriShape) { - cShape->mCollide = !(flags&0x800); + mShape->mCollide = !(flags&0x800); handleNiTriShape(mesh, static_cast(node), flags, node->getWorldTransform(), raycasting); } } @@ -246,7 +248,7 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(mesh, list[i].getPtr(), flags, hasCollisionNode, isCollisionNode, raycasting); + handleNode(mesh, list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker); } } } @@ -257,9 +259,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif:: assert(shape != NULL); // Interpret flags - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision + bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; + bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; + bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 9cb5a3e86..d1e876305 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -50,7 +50,13 @@ namespace NifBullet class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader { public: - ManualBulletShapeLoader():resourceGroup("General"){} + ManualBulletShapeLoader() + : mShape(NULL) + , mBoundingBox(NULL) + , mHasShape(false) + { + } + virtual ~ManualBulletShapeLoader(); void warn(const std::string &msg) @@ -82,7 +88,7 @@ private: /** *Parse a node. */ - void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool hasCollisionNode, bool isCollisionNode, bool raycasting); + void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool isCollisionNode, bool raycasting, bool isMarker); /** *Helper function @@ -94,10 +100,9 @@ private: */ void handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting); - std::string resourceName; - std::string resourceGroup; + std::string mResourceName; - OEngine::Physic::BulletShape* cShape;//current shape + OEngine::Physic::BulletShape* mShape;//current shape btBoxShape *mBoundingBox; bool mHasShape; diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp new file mode 100644 index 000000000..55f064c55 --- /dev/null +++ b/components/nifogre/material.cpp @@ -0,0 +1,394 @@ +#include "material.hpp" + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + + +namespace NifOgre +{ + +// Conversion of blend / test mode from NIF +static const char *getBlendFactor(int mode) +{ + switch(mode) + { + case 0: return "one"; + case 1: return "zero"; + case 2: return "src_colour"; + case 3: return "one_minus_src_colour"; + case 4: return "dest_colour"; + case 5: return "one_minus_dest_colour"; + case 6: return "src_alpha"; + case 7: return "one_minus_src_alpha"; + case 8: return "dest_alpha"; + case 9: return "one_minus_dest_alpha"; + case 10: return "src_alpha_saturate"; + } + std::cerr<< "Unexpected blend mode: "<colors.size() != 0); + + // Texture + if(texprop) + { + for(int i = 0;i < 7;i++) + { + if(!texprop->textures[i].inUse) + continue; + if(texprop->textures[i].texture.empty()) + { + warn("Texture layer "+Ogre::StringConverter::toString(i)+" is in use but empty in "+name); + continue; + } + + const Nif::NiSourceTexture *st = texprop->textures[i].texture.getPtr(); + if(st->external) + texName[i] = findTextureName(st->filename); + else + warn("Found internal texture, ignoring."); + } + + Nif::ControllerPtr ctrls = texprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled texture controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + needTangents = !texName[Nif::NiTexturingProperty::BumpTexture].empty(); + + // Alpha modifiers + if(alphaprop) + { + alphaFlags = alphaprop->flags; + alphaTest = alphaprop->data.threshold; + + Nif::ControllerPtr ctrls = alphaprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled alpha controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + // Vertex color handling + if(vertprop) + { + vertMode = vertprop->data.vertmode; + // FIXME: Handle lightmode? + //lightMode = vertprop->data.lightmode; + + Nif::ControllerPtr ctrls = vertprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled vertex color controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + if(zprop) + { + depthFlags = zprop->flags; + // Depth function??? + + Nif::ControllerPtr ctrls = zprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled depth controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + if(specprop) + { + specFlags = specprop->flags; + + Nif::ControllerPtr ctrls = specprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled specular controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + if(wireprop) + { + wireFlags = wireprop->flags; + + Nif::ControllerPtr ctrls = wireprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled wireframe controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + // Material + if(matprop) + { + ambient = matprop->data.ambient; + diffuse = matprop->data.diffuse; + specular = matprop->data.specular; + emissive = matprop->data.emissive; + glossiness = matprop->data.glossiness; + alpha = matprop->data.alpha; + + Nif::ControllerPtr ctrls = matprop->controller; + while(!ctrls.empty()) + { + warn("Unhandled material controller "+ctrls->recName+" in "+name); + ctrls = ctrls->next; + } + } + + { + // Generate a hash out of all properties that can affect the material. + size_t h = 0; + boost::hash_combine(h, ambient.x); + boost::hash_combine(h, ambient.y); + boost::hash_combine(h, ambient.z); + boost::hash_combine(h, diffuse.x); + boost::hash_combine(h, diffuse.y); + boost::hash_combine(h, diffuse.z); + boost::hash_combine(h, alpha); + boost::hash_combine(h, specular.x); + boost::hash_combine(h, specular.y); + boost::hash_combine(h, specular.z); + boost::hash_combine(h, glossiness); + boost::hash_combine(h, emissive.x); + boost::hash_combine(h, emissive.y); + boost::hash_combine(h, emissive.z); + for(int i = 0;i < 7;i++) + { + if(!texName[i].empty()) + boost::hash_combine(h, texName[i]); + } + boost::hash_combine(h, vertexColour); + boost::hash_combine(h, alphaFlags); + boost::hash_combine(h, alphaTest); + boost::hash_combine(h, vertMode); + boost::hash_combine(h, depthFlags); + boost::hash_combine(h, specFlags); + boost::hash_combine(h, wireFlags); + + std::map::iterator itr = sMaterialMap.find(h); + if (itr != sMaterialMap.end()) + { + // a suitable material exists already - use it + return itr->second; + } + // not found, create a new one + sMaterialMap.insert(std::make_pair(h, name)); + } + + // No existing material like this. Create a new one. + sh::MaterialInstance *instance = sh::Factory::getInstance().createMaterialInstance(name, "openmw_objects_base"); + if(vertMode == 0 || !vertexColour) + { + instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); + instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); + instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); + instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("0"))); + } + else if(vertMode == 1) + { + instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); + instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); + instance->setProperty("emissive", sh::makeProperty(new sh::StringValue("vertexcolour"))); + instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("1"))); + } + else if(vertMode == 2) + { + instance->setProperty("ambient", sh::makeProperty(new sh::StringValue("vertexcolour"))); + instance->setProperty("diffuse", sh::makeProperty(new sh::StringValue("vertexcolour"))); + instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); + instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("2"))); + } + else + std::cerr<< "Unhandled vertex mode: "<setProperty("specular", sh::makeProperty( + new sh::Vector4(specular.x, specular.y, specular.z, glossiness))); + } + + if(wireFlags) + { + instance->setProperty("polygon_mode", sh::makeProperty(new sh::StringValue("wireframe"))); + } + + instance->setProperty("diffuseMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BaseTexture])); + instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture])); + instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture])); + instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture])); + if (!texName[Nif::NiTexturingProperty::GlowTexture].empty()) + { + instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("emissiveMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::GlowTexture].uvSet))); + } + if (!texName[Nif::NiTexturingProperty::DetailTexture].empty()) + { + instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet))); + } + + for(int i = 0;i < 7;i++) + { + if(i == Nif::NiTexturingProperty::BaseTexture || + i == Nif::NiTexturingProperty::DetailTexture || + i == Nif::NiTexturingProperty::BumpTexture || + i == Nif::NiTexturingProperty::GlowTexture) + continue; + if(!texName[i].empty()) + warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i)); + } + + if (vertexColour) + instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); + + // Add transparency if NiAlphaProperty was present + NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]); + if (result.first) + { + alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ + alphaTest = result.second; + depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on + } + + if((alphaFlags&1)) + { + std::string blend_mode; + blend_mode += getBlendFactor((alphaFlags>>1)&0xf); + blend_mode += " "; + blend_mode += getBlendFactor((alphaFlags>>5)&0xf); + instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue(blend_mode))); + } + + if((alphaFlags>>9)&1) + { + std::string reject; + reject += getTestMode((alphaFlags>>10)&0x7); + reject += " "; + reject += Ogre::StringConverter::toString(alphaTest); + instance->setProperty("alpha_rejection", sh::makeProperty(new sh::StringValue(reject))); + } + else + instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha"); + + // Ogre usually only sorts if depth write is disabled, so we want "force" instead of "on" + instance->setProperty("transparent_sorting", sh::makeProperty(new sh::StringValue( + ((alphaFlags&1) && !((alphaFlags>>13)&1)) ? "force" : "off"))); + + instance->setProperty("depth_check", sh::makeProperty(new sh::StringValue((depthFlags&1) ? "on" : "off"))); + instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); + // depth_func??? + + sh::Factory::getInstance()._ensureMaterial(name, "Default"); + return name; +} + +std::map NIFMaterialLoader::sMaterialMap; + +} diff --git a/components/nifogre/material.hpp b/components/nifogre/material.hpp new file mode 100644 index 000000000..8843ac6c6 --- /dev/null +++ b/components/nifogre/material.hpp @@ -0,0 +1,57 @@ +#ifndef COMPONENTS_NIFOGRE_MATERIAL_HPP +#define COMPONENTS_NIFOGRE_MATERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace Nif +{ + class ShapeData; + class NiTexturingProperty; + class NiMaterialProperty; + class NiAlphaProperty; + class NiVertexColorProperty; + class NiZBufferProperty; + class NiSpecularProperty; + class NiWireframeProperty; +} + +namespace NifOgre +{ + +class NIFMaterialLoader { + static void warn(const std::string &msg) + { + std::cerr << "NIFMaterialLoader: Warn: " << msg << std::endl; + } + + static void fail(const std::string &msg) + { + std::cerr << "NIFMaterialLoader: Fail: "<< msg << std::endl; + abort(); + } + + static std::map sMaterialMap; + + static std::string findTextureName(const std::string &filename); + +public: + static Ogre::String getMaterial(const Nif::ShapeData *shapedata, + const Ogre::String &name, const Ogre::String &group, + const Nif::NiTexturingProperty *texprop, + const Nif::NiMaterialProperty *matprop, + const Nif::NiAlphaProperty *alphaprop, + const Nif::NiVertexColorProperty *vertprop, + const Nif::NiZBufferProperty *zprop, + const Nif::NiSpecularProperty *specprop, + const Nif::NiWireframeProperty *wireprop, + bool &needTangents); +}; + +} + +#endif diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp new file mode 100644 index 000000000..ca92f62d4 --- /dev/null +++ b/components/nifogre/mesh.cpp @@ -0,0 +1,387 @@ +#include "mesh.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "material.hpp" + +namespace NifOgre +{ + +// Helper class that computes the bounding box and of a mesh +class BoundsFinder +{ + struct MaxMinFinder + { + float max, min; + + MaxMinFinder() + { + min = std::numeric_limits::infinity(); + max = -min; + } + + void add(float f) + { + if (f > max) max = f; + if (f < min) min = f; + } + + // Return Max(max**2, min**2) + float getMaxSquared() + { + float m1 = max*max; + float m2 = min*min; + if (m1 >= m2) return m1; + return m2; + } + }; + + MaxMinFinder X, Y, Z; + +public: + // Add 'verts' vertices to the calculation. The 'data' pointer is + // expected to point to 3*verts floats representing x,y,z for each + // point. + void add(float *data, int verts) + { + for (int i=0;idata.getPtr(); + const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); + std::vector srcVerts = data->vertices; + std::vector srcNorms = data->normals; + Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; + bool vertShadowBuffer = false; + + if(!shape->controller.empty()) + { + Nif::ControllerPtr ctrl = shape->controller; + do { + if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; + vertShadowBuffer = true; + break; + } + } while(!(ctrl=ctrl->next).empty()); + } + + if(skin != NULL) + { + vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; + vertShadowBuffer = true; + + // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be + // explicitly attached later. + mesh->setSkeletonName(mName); + + // Convert vertices and normals to bone space from bind position. It would be + // better to transform the bones into bind position, but there doesn't seem to + // be a reliable way to do that. + std::vector newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); + std::vector newNorms(srcNorms.size(), Ogre::Vector3(0.0f)); + + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t b = 0;b < bones.length();b++) + { + Ogre::Matrix4 mat; + mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), + Ogre::Quaternion(data->bones[b].trafo.rotation)); + mat = bones[b]->getWorldTransform() * mat; + + const std::vector &weights = data->bones[b].weights; + for(size_t i = 0;i < weights.size();i++) + { + size_t index = weights[i].vertex; + float weight = weights[i].weight; + + newVerts.at(index) += (mat*srcVerts[index]) * weight; + if(newNorms.size() > index) + { + Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f); + vec4 = mat*vec4 * weight; + newNorms[index] += Ogre::Vector3(&vec4[0]); + } + } + } + + srcVerts = newVerts; + srcNorms = newNorms; + } + else + { + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + if(skelMgr->getByName(mName).isNull()) + { + // No skinning and no skeleton, so just transform the vertices and + // normals into position. + Ogre::Matrix4 mat4 = shape->getWorldTransform(); + for(size_t i = 0;i < srcVerts.size();i++) + { + Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f); + vec4 = mat4*vec4; + srcVerts[i] = Ogre::Vector3(&vec4[0]); + } + for(size_t i = 0;i < srcNorms.size();i++) + { + Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); + vec4 = mat4*vec4; + srcNorms[i] = Ogre::Vector3(&vec4[0]); + } + } + } + + // Set the bounding box first + BoundsFinder bounds; + bounds.add(&srcVerts[0][0], srcVerts.size()); + if(!bounds.isValid()) + { + float v[3] = { 0.0f, 0.0f, 0.0f }; + bounds.add(&v[0], 1); + } + + mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f, + bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f)); + mesh->_setBoundingSphereRadius(bounds.getRadius()); + + // This function is just one long stream of Ogre-barf, but it works + // great. + Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareVertexBufferSharedPtr vbuf; + Ogre::HardwareIndexBufferSharedPtr ibuf; + Ogre::VertexBufferBinding *bind; + Ogre::VertexDeclaration *decl; + int nextBuf = 0; + + Ogre::SubMesh *sub = mesh->createSubMesh(); + + // Add vertices + sub->useSharedVertices = false; + sub->vertexData = new Ogre::VertexData(); + sub->vertexData->vertexStart = 0; + sub->vertexData->vertexCount = srcVerts.size(); + + decl = sub->vertexData->vertexDeclaration; + bind = sub->vertexData->vertexBufferBinding; + if(srcVerts.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcVerts.size(), vertUsage, vertShadowBuffer); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex normals + if(srcNorms.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcNorms.size(), vertUsage, vertShadowBuffer); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex colors + const std::vector &colors = data->colors; + if(colors.size()) + { + Ogre::RenderSystem *rs = Ogre::Root::getSingleton().getRenderSystem(); + std::vector colorsRGB(colors.size()); + for(size_t i = 0;i < colorsRGB.size();i++) + { + Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]); + rs->convertColourValue(clr, &colorsRGB[i]); + } + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC); + vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true); + decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); + bind->setBinding(nextBuf++, vbuf); + } + + // Texture UV coordinates + size_t numUVs = data->uvlist.size(); + if (numUVs) + { + size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); + + for(size_t i = 0; i < numUVs; i++) + decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); + + vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(), + Ogre::HardwareBuffer::HBU_STATIC); + + std::vector allUVs; + allUVs.reserve(srcVerts.size()*numUVs); + for (size_t vert = 0; vertuvlist[i][vert]); + + vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true); + + bind->setBinding(nextBuf++, vbuf); + } + + // Triangle faces + const std::vector &srcIdx = data->triangles; + if(srcIdx.size()) + { + ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(), + Ogre::HardwareBuffer::HBU_STATIC); + ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true); + sub->indexData->indexBuffer = ibuf; + sub->indexData->indexCount = srcIdx.size(); + sub->indexData->indexStart = 0; + } + + // Assign bone weights for this TriShape + if(skin != NULL) + { + Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName); + + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t i = 0;i < bones.length();i++) + { + Ogre::VertexBoneAssignment boneInf; + boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle(); + + const std::vector &weights = data->bones[i].weights; + for(size_t j = 0;j < weights.size();j++) + { + boneInf.vertexIndex = weights[j].vertex; + boneInf.weight = weights[j].weight; + sub->addBoneAssignment(boneInf); + } + } + } + + const Nif::NiTexturingProperty *texprop = NULL; + const Nif::NiMaterialProperty *matprop = NULL; + const Nif::NiAlphaProperty *alphaprop = NULL; + const Nif::NiVertexColorProperty *vertprop = NULL; + const Nif::NiZBufferProperty *zprop = NULL; + const Nif::NiSpecularProperty *specprop = NULL; + const Nif::NiWireframeProperty *wireprop = NULL; + bool needTangents = false; + + shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup, + texprop, matprop, alphaprop, + vertprop, zprop, specprop, + wireprop, needTangents); + if(matname.length() > 0) + sub->setMaterialName(matname); + + // build tangents if the material needs them + if (needTangents) + { + unsigned short src,dest; + if (!mesh->suggestTangentVectorBuildParams(Ogre::VES_TANGENT, src,dest)) + mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); + } +} + + +NIFMeshLoader::NIFMeshLoader(const std::string &name, const std::string &group, size_t idx) + : mName(name), mGroup(group), mShapeIndex(idx) +{ +} + +void NIFMeshLoader::loadResource(Ogre::Resource *resource) +{ + Ogre::Mesh *mesh = dynamic_cast(resource); + OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); + + Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); + if(mShapeIndex >= nif->numRecords()) + { + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + if(!skelMgr->getByName(mName).isNull()) + mesh->setSkeletonName(mName); + return; + } + + const Nif::Record *record = nif->getRecord(mShapeIndex); + createSubMesh(mesh, dynamic_cast(record)); +} + + +void NIFMeshLoader::createMesh(const std::string &name, const std::string &fullname, const std::string &group, size_t idx) +{ + NIFMeshLoader::LoaderMap::iterator loader; + loader = sLoaders.insert(std::make_pair(fullname, NIFMeshLoader(name, group, idx))).first; + + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + Ogre::MeshPtr mesh = meshMgr.createManual(fullname, group, &loader->second); + mesh->setAutoBuildEdgeLists(false); +} + +} diff --git a/components/nifogre/mesh.hpp b/components/nifogre/mesh.hpp new file mode 100644 index 000000000..731e49c90 --- /dev/null +++ b/components/nifogre/mesh.hpp @@ -0,0 +1,55 @@ +#ifndef COMPONENTS_NIFOGRE_MESH_HPP +#define COMPONENTS_NIFOGRE_MESH_HPP + +#include +#include +#include +#include + +#include + +namespace Nif +{ + class NiTriShape; +} + +namespace NifOgre +{ + +/** Manual resource loader for NiTriShapes. This is the main class responsible + * for translating the internal NIF meshes into something Ogre can use. + */ +class NIFMeshLoader : Ogre::ManualResourceLoader +{ + static void warn(const std::string &msg) + { + std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; + } + + static void fail(const std::string &msg) + { + std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + abort(); + } + + std::string mName; + std::string mGroup; + size_t mShapeIndex; + + // Convert NiTriShape to Ogre::SubMesh + void createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape); + + typedef std::map LoaderMap; + static LoaderMap sLoaders; + + NIFMeshLoader(const std::string &name, const std::string &group, size_t idx); + + virtual void loadResource(Ogre::Resource *resource); + +public: + static void createMesh(const std::string &name, const std::string &fullname, const std::string &group, size_t idx); +}; + +} + +#endif diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 67919f704..3bb9ea230 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -25,1123 +25,756 @@ #include -#include -#include -#include -#include #include -#include #include #include #include #include - -#include -#include -#include - -#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -typedef unsigned char ubyte; - -namespace std -{ - -// TODO: Do something useful -ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) -{ return o; } - -} +#include "skeleton.hpp" +#include "material.hpp" +#include "mesh.hpp" namespace NifOgre { -// Helper class that computes the bounding box and of a mesh -class BoundsFinder -{ - struct MaxMinFinder - { - float max, min; - - MaxMinFinder() - { - min = std::numeric_limits::infinity(); - max = -min; - } - - void add(float f) - { - if (f > max) max = f; - if (f < min) min = f; - } - - // Return Max(max**2, min**2) - float getMaxSquared() - { - float m1 = max*max; - float m2 = min*min; - if (m1 >= m2) return m1; - return m2; - } - }; - MaxMinFinder X, Y, Z; +// FIXME: Should not be here. +class DefaultFunction : public Ogre::ControllerFunction +{ +private: + float mFrequency; + float mPhase; + float mStartTime; + float mStopTime; public: - // Add 'verts' vertices to the calculation. The 'data' pointer is - // expected to point to 3*verts floats representing x,y,z for each - // point. - void add(float *data, int verts) + DefaultFunction(const Nif::Controller *ctrl, bool deltaInput) + : Ogre::ControllerFunction(deltaInput) + , mFrequency(ctrl->frequency) + , mPhase(ctrl->phase) + , mStartTime(ctrl->timeStart) + , mStopTime(ctrl->timeStop) { - for (int i=0;i &ctrls, const std::vector &targets, float startTime, float stopTime) +class VisController { - Ogre::Animation *anim = skel->createAnimation(name, stopTime-startTime); - - for(size_t i = 0;i < ctrls.size();i++) +public: + class Value : public NodeTargetValue { - const Nif::NiKeyframeController *kfc = ctrls[i]; - if(kfc->data.empty()) - continue; - const Nif::NiKeyframeData *kf = kfc->data.getPtr(); - - /* Get the keyframes and make sure they're sorted first to last */ - const Nif::QuaternionKeyList &quatkeys = kf->mRotations; - const Nif::Vector3KeyList &trankeys = kf->mTranslations; - const Nif::FloatKeyList &scalekeys = kf->mScales; - - Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); - Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); - Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); - - Ogre::Bone *bone = skel->getBone(targets[i]); - // NOTE: For some reason, Ogre doesn't like the node track ID being different from - // the bone ID - Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ? - anim->getNodeTrack(bone->getHandle()) : - anim->createNodeTrack(bone->getHandle(), bone); - - Ogre::Quaternion lastquat, curquat; - Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); - Ogre::Vector3 lastscale(1.0f), curscale(1.0f); - if(quatiter != quatkeys.mKeys.end()) - lastquat = curquat = quatiter->mValue; - if(traniter != trankeys.mKeys.end()) - lasttrans = curtrans = traniter->mValue; - if(scaleiter != scalekeys.mKeys.end()) - lastscale = curscale = Ogre::Vector3(scaleiter->mValue); - float begTime = std::max(kfc->timeStart, startTime); - float endTime = std::min(kfc->timeStop, stopTime); - bool didlast = false; - - while(!didlast) - { - float curtime = std::numeric_limits::max(); - - //Get latest time - if(quatiter != quatkeys.mKeys.end()) - curtime = std::min(curtime, quatiter->mTime); - if(traniter != trankeys.mKeys.end()) - curtime = std::min(curtime, traniter->mTime); - if(scaleiter != scalekeys.mKeys.end()) - curtime = std::min(curtime, scaleiter->mTime); - - curtime = std::max(curtime, begTime); - if(curtime >= endTime) - { - didlast = true; - curtime = endTime; - } + private: + std::vector mData; - // Get the latest quaternions, translations, and scales for the - // current time - while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) - { - lastquat = curquat; - if(++quatiter != quatkeys.mKeys.end()) - curquat = quatiter->mValue; - } - while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) - { - lasttrans = curtrans; - if(++traniter != trankeys.mKeys.end()) - curtrans = traniter->mValue; - } - while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) - { - lastscale = curscale; - if(++scaleiter != scalekeys.mKeys.end()) - curscale = Ogre::Vector3(scaleiter->mValue); - } + bool calculate(Ogre::Real time) const + { + if(mData.size() == 0) + return true; - Ogre::TransformKeyFrame *kframe; - kframe = nodetrack->createNodeKeyFrame(curtime-startTime); - if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) - kframe->setRotation(curquat); - else - { - Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; - float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); - kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); - } - if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) - kframe->setTranslate(curtrans); - else - { - Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; - float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); - kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); - } - if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) - kframe->setScale(curscale); - else + for(size_t i = 1;i < mData.size();i++) { - Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; - float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); - kframe->setScale(lastscale + ((curscale-lastscale)*diff)); + if(mData[i].time > time) + return mData[i-1].isSet; } + return mData.back().isSet; } - } - anim->optimise(); -} - -static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) -{ - TextKeyMap textkeys; - for(size_t i = 0;i < tk->list.size();i++) - { - const std::string &str = tk->list[i].text; - std::string::size_type pos = 0; - while(pos < str.length()) + // FIXME: We are not getting all objects here. Skinned meshes get + // attached to the object's root node, and won't be connected via a + // TagPoint. + static void setVisible(Ogre::Node *node, int vis) { - if(::isspace(str[pos])) + Ogre::Node::ChildNodeIterator iter = node->getChildIterator(); + while(iter.hasMoreElements()) { - pos++; - continue; + node = iter.getNext(); + setVisible(node, vis); + + Ogre::TagPoint *tag = dynamic_cast(node); + if(tag != NULL) + { + Ogre::MovableObject *obj = tag->getChildObject(); + if(obj != NULL) + obj->setVisible(vis); + } } + } - std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); - std::string result = str.substr(pos, nextpos-pos); - textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); + public: + Value(Ogre::Node *target, const Nif::NiVisData *data) + : NodeTargetValue(target) + , mData(data->mVis) + { } - pos = nextpos; - } - } - return textkeys; -} + virtual Ogre::Quaternion getRotation(float time) const + { return Ogre::Quaternion(); } + virtual Ogre::Vector3 getTranslation(float time) const + { return Ogre::Vector3(0.0f); } -void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent=NULL) -{ - Ogre::Bone *bone; - if(!skel->hasBone(node->name)) - bone = skel->createBone(node->name); - else - bone = skel->createBone(); - if(parent) parent->addChild(bone); - - bone->setOrientation(node->trafo.rotation); - bone->setPosition(node->trafo.pos); - bone->setScale(Ogre::Vector3(node->trafo.scale)); - bone->setBindingPose(); - - if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ - node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ - node->recType == Nif::RC_NiTriShape /* Handled in the mesh loader */ - )) - warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName()); - - Nif::ControllerPtr ctrl = node->controller; - while(!ctrl.empty()) - { - if(ctrl->recType == Nif::RC_NiKeyframeController) - ctrls.push_back(static_cast(ctrl.getPtr())); - else - warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); - ctrl = ctrl->next; - } + virtual Ogre::Vector3 getScale(float time) const + { return Ogre::Vector3(1.0f); } - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot) + virtual Ogre::Real getValue() const { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - textkeys = extractTextKeys(tk); - animroot = bone; + // Should not be called + return 0.0f; } - e = e->extra; - } - const Nif::NiNode *ninode = dynamic_cast(node); - if(ninode) - { - const Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();i++) + virtual void setValue(Ogre::Real time) { - if(!children[i].empty()) - buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone); + bool vis = calculate(time); + setVisible(mNode, vis); } - } -} - + }; -typedef std::map LoaderMap; -static LoaderMap sLoaders; + typedef DefaultFunction Function; +}; -public: -void loadResource(Ogre::Resource *resource) +class KeyframeController { - Ogre::Skeleton *skel = dynamic_cast(resource); - OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); - - Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); - const Nif::Node *node = static_cast(nif->getRecord(0)); +public: + class Value : public NodeTargetValue + { + private: + Nif::QuaternionKeyList mRotations; + Nif::Vector3KeyList mTranslations; + Nif::FloatKeyList mScales; - std::vector ctrls; - Ogre::Bone *animroot = NULL; - TextKeyMap textkeys; - try { - buildBones(skel, node, animroot, textkeys, ctrls); - } - catch(std::exception &e) { - std::cerr<< "Exception while loading "<getName() < targets; - // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file - if(ctrls.size() == 0) // No animations? Then we're done. - return; + Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; - float maxtime = 0.0f; - for(size_t i = 0;i < ctrls.size();i++) - { - const Nif::NiKeyframeController *ctrl = ctrls[i]; - maxtime = std::max(maxtime, ctrl->timeStop); - Nif::Named *target = dynamic_cast(ctrl->target.getPtr()); - if(target != NULL) - targets.push_back(target->name); - } + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } - if(targets.size() != ctrls.size()) - { - warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+ - Ogre::StringConverter::toString(ctrls.size())+" controllers)"); - return; - } + static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; - if(!animroot) - { - warn(Ogre::StringConverter::toString(ctrls.size())+" animated node(s) in "+ - skel->getName()+", but no text keys."); - return; - } + Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; - Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings(); - bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true)); + Nif::Vector3KeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } - std::string currentgroup; - TextKeyMap::const_iterator keyiter = textkeys.begin(); - for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++) - { - std::string::size_type sep = keyiter->second.find(':'); - if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) || - (sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) || - (sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0)) - continue; - currentgroup = keyiter->second.substr(0, sep); - - if(skel->hasAnimation(currentgroup)) - continue; - - TextKeyMap::const_iterator lastkeyiter = textkeys.end(); - while((--lastkeyiter)->first > keyiter->first) + static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time) { - if(lastkeyiter->second.find(':') == currentgroup.length() && - lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0) - break; - } + if(time <= keys.front().mTime) + return keys.front().mValue; - buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first); + Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; - TextKeyMap::const_iterator insiter = keyiter; - TextKeyMap groupkeys; - do { - sep = insiter->second.find(':'); - if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0) - groupkeys.insert(std::make_pair(insiter->first - keyiter->first, - insiter->second.substr(sep+2))); - else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) || - (sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0)) - groupkeys.insert(std::make_pair(insiter->first - keyiter->first, insiter->second)); - } while(insiter++ != lastkeyiter); - - bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys)); - } -} + Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue); + } + return keys.back().mValue; + } + public: + Value(Ogre::Node *target, const Nif::NiKeyframeData *data) + : NodeTargetValue(target) + , mRotations(data->mRotations) + , mTranslations(data->mTranslations) + , mScales(data->mScales) + { } -static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) -{ - /* We need to be a little aggressive here, since some NIFs have a crap-ton - * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: - * There are no bones used for skinning, there are no controllers on non- - * NiTriShape nodes, there are no nodes named "AttachLight", and the tree - * consists of NiNode, NiTriShape, and RootCollisionNode types only. - */ - if(!node->boneTrafo) - { - if(node->recType == Nif::RC_NiTriShape) - return Ogre::SkeletonPtr(); - if(node->controller.empty() && node->name != "AttachLight") + virtual Ogre::Quaternion getRotation(float time) const { - if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) - { - const Nif::NiNode *ninode = static_cast(node); - const Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();i++) - { - if(!children[i].empty()) - { - Ogre::SkeletonPtr skel = createSkeleton(name, group, children[i].getPtr()); - if(!skel.isNull()) - return skel; - } - } - return Ogre::SkeletonPtr(); - } + if(mRotations.mKeys.size() > 0) + return interpKey(mRotations.mKeys, time); + return mNode->getOrientation(); } - } - Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - return skelMgr.create(name, group, true, &sLoaders[name]); -} + virtual Ogre::Vector3 getTranslation(float time) const + { + if(mTranslations.mKeys.size() > 0) + return interpKey(mTranslations.mKeys, time); + return mNode->getPosition(); + } -}; -NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; + virtual Ogre::Vector3 getScale(float time) const + { + if(mScales.mKeys.size() > 0) + return Ogre::Vector3(interpKey(mScales.mKeys, time)); + return mNode->getScale(); + } + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } -// Conversion of blend / test mode from NIF -static const char *getBlendFactor(int mode) -{ - switch(mode) - { - case 0: return "one"; - case 1: return "zero"; - case 2: return "src_colour"; - case 3: return "one_minus_src_colour"; - case 4: return "dest_colour"; - case 5: return "one_minus_dest_colour"; - case 6: return "src_alpha"; - case 7: return "one_minus_src_alpha"; - case 8: return "dest_alpha"; - case 9: return "one_minus_dest_alpha"; - case 10: return "src_alpha_saturate"; - } - std::cerr<< "Unexpected blend mode: "< 0) + mNode->setOrientation(interpKey(mRotations.mKeys, time)); + if(mTranslations.mKeys.size() > 0) + mNode->setPosition(interpKey(mTranslations.mKeys, time)); + if(mScales.mKeys.size() > 0) + mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time))); + } + }; + + typedef DefaultFunction Function; +}; -static const char *getTestMode(int mode) +class UVController { - switch(mode) +public: + class Value : public Ogre::ControllerValue { - case 0: return "always_pass"; - case 1: return "less"; - case 2: return "equal"; - case 3: return "less_equal"; - case 4: return "greater"; - case 5: return "not_equal"; - case 6: return "greater_equal"; - case 7: return "always_fail"; - } - std::cerr<< "Unexpected test mode: "< MaterialMap; + if(time <= keys.mKeys.front().mTime) + return keys.mKeys.front().mValue; -static void warn(const std::string &msg) -{ - std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; -} + Nif::FloatKeyList::VecType::const_iterator iter(keys.mKeys.begin()+1); + for(;iter != keys.mKeys.end();iter++) + { + if(iter->mTime < time) + continue; -static void fail(const std::string &msg) -{ - std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; - abort(); -} + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.mKeys.back().mValue; + } + public: + Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data) + : mMaterial(material) + , mUTrans(data->mKeyList[0]) + , mVTrans(data->mKeyList[1]) + , mUScale(data->mKeyList[2]) + , mVScale(data->mKeyList[3]) + { } -public: -static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String &name, const Ogre::String &group, - const Nif::NiTexturingProperty *texprop, - const Nif::NiMaterialProperty *matprop, - const Nif::NiAlphaProperty *alphaprop, - const Nif::NiVertexColorProperty *vertprop, - const Nif::NiZBufferProperty *zprop, - const Nif::NiSpecularProperty *specprop) -{ - Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); - Ogre::MaterialPtr material = matMgr.getByName(name); - if(!material.isNull()) - return name; - - Ogre::Vector3 ambient(1.0f); - Ogre::Vector3 diffuse(1.0f); - Ogre::Vector3 specular(0.0f); - Ogre::Vector3 emissive(0.0f); - float glossiness = 0.0f; - float alpha = 1.0f; - int alphaFlags = 0; - int alphaTest = 0; - int vertMode = 2; - //int lightMode = 1; - int depthFlags = 3; - // Default should be 1, but Bloodmoon's models are broken - int specFlags = 0; - Ogre::String texName; - - bool vertexColour = (shape->data->colors.size() != 0); - - // Texture - if(texprop && texprop->textures[0].inUse) - { - const Nif::NiSourceTexture *st = texprop->textures[0].texture.getPtr(); - if(st->external) + virtual Ogre::Real getValue() const { - /* Bethesda at some point converted all their BSA - * textures from tga to dds for increased load speed, but all - * texture file name references were kept as .tga. - */ - static const char path[] = "textures\\"; - - texName = st->filename; - Misc::StringUtils::toLower(texName); + // Should not be called + return 1.0f; + } - if(texName.compare(0, sizeof(path)-1, path) != 0) - texName = path + texName; + virtual void setValue(Ogre::Real value) + { + float uTrans = lookupValue(mUTrans, value, 0.0f); + float vTrans = lookupValue(mVTrans, value, 0.0f); + float uScale = lookupValue(mUScale, value, 1.0f); + float vScale = lookupValue(mVScale, value, 1.0f); - Ogre::String::size_type pos = texName.rfind('.'); - if(pos != Ogre::String::npos && texName.compare(pos, texName.length() - pos, ".dds") != 0) + Ogre::Material::TechniqueIterator techs = mMaterial->getTechniqueIterator(); + while(techs.hasMoreElements()) { - // since we know all (GOTY edition or less) textures end - // in .dds, we change the extension - texName.replace(pos, texName.length(), ".dds"); - - // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) - // verify, and revert if false (this call succeeds quickly, but fails slowly) - if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + Ogre::Technique *tech = techs.getNext(); + Ogre::Technique::PassIterator passes = tech->getPassIterator(); + while(passes.hasMoreElements()) { - texName = st->filename; - Misc::StringUtils::toLower(texName); - if(texName.compare(0, sizeof(path)-1, path) != 0) - texName = path + texName; + Ogre::Pass *pass = passes.getNext(); + Ogre::TextureUnitState *tex = pass->getTextureUnitState(0); + tex->setTextureScroll(uTrans, vTrans); + tex->setTextureScale(uScale, vScale); } } } - else warn("Found internal texture, ignoring."); - } - - // Alpha modifiers - if(alphaprop) - { - alphaFlags = alphaprop->flags; - alphaTest = alphaprop->data.threshold; - } - - // Vertex color handling - if(vertprop) - { - vertMode = vertprop->data.vertmode; - // FIXME: Handle lightmode? - //lightMode = vertprop->data.lightmode; - } - - if(zprop) - { - depthFlags = zprop->flags; - // Depth function??? - } + }; - if(specprop) - { - specFlags = specprop->flags; - } + typedef DefaultFunction Function; +}; - // Material - if(matprop) +class ParticleSystemController +{ +public: + class Value : public Ogre::ControllerValue { - ambient = matprop->data.ambient; - diffuse = matprop->data.diffuse; - specular = matprop->data.specular; - emissive = matprop->data.emissive; - glossiness = matprop->data.glossiness; - alpha = matprop->data.alpha; - } + private: + Ogre::ParticleSystem *mParticleSys; + float mEmitStart; + float mEmitStop; - Ogre::String matname = name; - if(matprop || !texName.empty()) - { - // Generate a hash out of all properties that can affect the material. - size_t h = 0; - boost::hash_combine(h, ambient.x); - boost::hash_combine(h, ambient.y); - boost::hash_combine(h, ambient.z); - boost::hash_combine(h, diffuse.x); - boost::hash_combine(h, diffuse.y); - boost::hash_combine(h, diffuse.z); - boost::hash_combine(h, specular.x); - boost::hash_combine(h, specular.y); - boost::hash_combine(h, specular.z); - boost::hash_combine(h, emissive.x); - boost::hash_combine(h, emissive.y); - boost::hash_combine(h, emissive.z); - boost::hash_combine(h, texName); - boost::hash_combine(h, vertexColour); - boost::hash_combine(h, alphaFlags); - boost::hash_combine(h, alphaTest); - boost::hash_combine(h, vertMode); - boost::hash_combine(h, depthFlags); - boost::hash_combine(h, specFlags); - - std::map::iterator itr = MaterialMap.find(h); - if (itr != MaterialMap.end()) + public: + Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl) + : mParticleSys(psys) + , mEmitStart(pctrl->startTime) + , mEmitStop(pctrl->stopTime) { - // a suitable material exists already - use it - return itr->second; } - // not found, create a new one - MaterialMap.insert(std::make_pair(h, matname)); - } - - // No existing material like this. Create a new one. - sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (matname, "openmw_objects_base"); - if(vertMode == 0 || !vertexColour) - { - instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); - instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); - instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); - instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("0"))); - } - else if(vertMode == 1) - { - instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1))); - instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); - instance->setProperty("emissive", sh::makeProperty(new sh::StringValue("vertexcolour"))); - instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("1"))); - } - else if(vertMode == 2) - { - instance->setProperty("ambient", sh::makeProperty(new sh::StringValue("vertexcolour"))); - instance->setProperty("diffuse", sh::makeProperty(new sh::StringValue("vertexcolour"))); - instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1))); - instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("2"))); - } - else - std::cerr<< "Unhandled vertex mode: "<setProperty("specular", sh::makeProperty( - new sh::Vector4(specular.x, specular.y, specular.z, glossiness))); - } - - instance->setProperty("diffuseMap", sh::makeProperty(texName)); - if (vertexColour) - instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); + Ogre::Real getValue() const + { return 0.0f; } - // Add transparency if NiAlphaProperty was present - NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); - if (result.first) - { - alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ - alphaTest = result.second; - } + void setValue(Ogre::Real value) + { + mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop); + } + }; - if((alphaFlags&1)) - { - std::string blend_mode; - blend_mode += getBlendFactor((alphaFlags>>1)&0xf); - blend_mode += " "; - blend_mode += getBlendFactor((alphaFlags>>5)&0xf); - instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue(blend_mode))); - } + typedef DefaultFunction Function; +}; - if((alphaFlags>>9)&1) +class GeomMorpherController +{ +public: + class Value : public Ogre::ControllerValue { - std::string reject; - reject += getTestMode((alphaFlags>>10)&0x7); - reject += " "; - reject += Ogre::StringConverter::toString(alphaTest); - instance->setProperty("alpha_rejection", sh::makeProperty(new sh::StringValue(reject))); - } - else - instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha"); + private: + Ogre::SubEntity *mSubEntity; + std::vector mMorphs; - // Ogre usually only sorts if depth write is disabled, so we want "force" instead of "on" - instance->setProperty("transparent_sorting", sh::makeProperty(new sh::StringValue( - ((alphaFlags&1) && !((alphaFlags>>13)&1)) ? "force" : "off"))); + public: + Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) + : mSubEntity(subent) + , mMorphs(data->mMorphs) + { } - instance->setProperty("depth_check", sh::makeProperty(new sh::StringValue((depthFlags&1) ? "on" : "off"))); - instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); - // depth_func??? + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } - sh::Factory::getInstance()._ensureMaterial(matname, "Default"); - return matname; -} + virtual void setValue(Ogre::Real value) + { + // TODO: Implement + } + }; + typedef DefaultFunction Function; }; -std::map NIFMaterialLoader::MaterialMap; -/** Manual resource loader for NIF meshes. This is the main class - responsible for translating the internal NIF mesh structure into - something Ogre can use. +/** Object creator for NIFs. This is the main class responsible for creating + * "live" Ogre objects (entities, particle systems, controllers, etc) from + * their NIF equivalents. */ -class NIFMeshLoader : Ogre::ManualResourceLoader +class NIFObjectLoader { - std::string mName; - std::string mGroup; - size_t mShapeIndex; - - void warn(const std::string &msg) + static void warn(const std::string &msg) { - std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; + std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl; } - void fail(const std::string &msg) + static void fail(const std::string &msg) { - std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + std::cerr << "NIFObjectLoader: Fail: "<< msg << std::endl; abort(); } - // Convert NiTriShape to Ogre::SubMesh - void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape const *shape, - const Nif::NiTexturingProperty *texprop, - const Nif::NiMaterialProperty *matprop, - const Nif::NiAlphaProperty *alphaprop, - const Nif::NiVertexColorProperty *vertprop, - const Nif::NiZBufferProperty *zprop, - const Nif::NiSpecularProperty *specprop) + static void createEntity(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + const Nif::Node *node, int flags, int animflags) { - Ogre::SkeletonPtr skel; - const Nif::NiTriShapeData *data = shape->data.getPtr(); - const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); - std::vector srcVerts = data->vertices; - std::vector srcNorms = data->normals; - if(skin != NULL) - { - // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be - // explicitly attached later. - mesh->setSkeletonName(mName); - - // Get the skeleton resource, so vertices can be transformed into the bones' initial state. - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - skel = skelMgr->getByName(mName); - - // Convert vertices and normals to bone space from bind position. It would be - // better to transform the bones into bind position, but there doesn't seem to - // be a reliable way to do that. - std::vector newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); - std::vector newNorms(srcNorms.size(), Ogre::Vector3(0.0f)); - - const Nif::NiSkinData *data = skin->data.getPtr(); - const Nif::NodeList &bones = skin->bones; - for(size_t b = 0;b < bones.length();b++) - { - Ogre::Bone *bone = skel->getBone(bones[b]->name); - Ogre::Matrix4 mat; - mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), - Ogre::Quaternion(data->bones[b].trafo.rotation)); - mat = bone->_getFullTransform() * mat; - - const std::vector &weights = data->bones[b].weights; - for(size_t i = 0;i < weights.size();i++) - { - size_t index = weights[i].vertex; - float weight = weights[i].weight; - - newVerts.at(index) += (mat*srcVerts[index]) * weight; - if(newNorms.size() > index) - { - Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f); - vec4 = mat*vec4 * weight; - newNorms[index] += Ogre::Vector3(&vec4[0]); - } - } - } + const Nif::NiTriShape *shape = static_cast(node); - srcVerts = newVerts; - srcNorms = newNorms; - } - else + std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex); + if(shape->name.length() > 0) + fullname += "@shape="+shape->name; + Misc::StringUtils::toLower(fullname); + + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + if(meshMgr.getByName(fullname).isNull()) + NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex); + + Ogre::Entity *entity = sceneMgr->createEntity(fullname); + entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); + + objectlist.mEntities.push_back(entity); + if(objectlist.mSkelBase) { - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - if(skelMgr->getByName(mName).isNull()) + if(entity->hasSkeleton()) + entity->shareSkeletonInstanceWith(objectlist.mSkelBase); + else { - // No skinning and no skeleton, so just transform the vertices and - // normals into position. - Ogre::Matrix4 mat4 = shape->getWorldTransform(); - for(size_t i = 0;i < srcVerts.size();i++) - { - Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f); - vec4 = mat4*vec4; - srcVerts[i] = Ogre::Vector3(&vec4[0]); - } - for(size_t i = 0;i < srcNorms.size();i++) - { - Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); - vec4 = mat4*vec4; - srcNorms[i] = Ogre::Vector3(&vec4[0]); - } + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity); } } - // Set the bounding box first - BoundsFinder bounds; - bounds.add(&srcVerts[0][0], srcVerts.size()); - if(!bounds.isValid()) + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) { - float v[3] = { 0.0f, 0.0f, 0.0f }; - bounds.add(&v[0], 1); - } + if(ctrl->recType == Nif::RC_NiUVController) + { + const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); - mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f, - bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f)); - mesh->_setBoundingSphereRadius(bounds.getRadius()); - - // This function is just one long stream of Ogre-barf, but it works - // great. - Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareVertexBufferSharedPtr vbuf; - Ogre::HardwareIndexBufferSharedPtr ibuf; - Ogre::VertexBufferBinding *bind; - Ogre::VertexDeclaration *decl; - int nextBuf = 0; - - Ogre::SubMesh *sub = mesh->createSubMesh(); - - // Add vertices - sub->useSharedVertices = false; - sub->vertexData = new Ogre::VertexData(); - sub->vertexData->vertexStart = 0; - sub->vertexData->vertexCount = srcVerts.size(); - - decl = sub->vertexData->vertexDeclaration; - bind = sub->vertexData->vertexBufferBinding; - if(srcVerts.size()) - { - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - srcVerts.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, - true); - vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true); + const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial(); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - bind->setBinding(nextBuf++, vbuf); - } + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); - // Vertex normals - if(srcNorms.size()) - { - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - srcNorms.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, - true); - vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true); + Ogre::SubEntity *subent = entity->getSubEntity(0); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); - bind->setBinding(nextBuf++, vbuf); + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + ctrl = ctrl->next; } + } + + + static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) + { + Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); + emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, + partctrl->velocity + partctrl->velocityRandom*0.5f); + emitter->setEmissionRate(partctrl->emitRate); + emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f, + partctrl->lifetime + partctrl->lifetimeRandom*0.5f); + emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); + emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); + emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); + emitter->setParameter("vertical_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalDir).valueDegrees())); + emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees())); + emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees())); + emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees())); - // Vertex colors - const std::vector &colors = data->colors; - if(colors.size()) + Nif::ExtraPtr e = partctrl->extra; + while(!e.empty()) { - Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); - std::vector colorsRGB(colors.size()); - for(size_t i = 0;i < colorsRGB.size();i++) + if(e->recType == Nif::RC_NiParticleGrowFade) + { + const Nif::NiParticleGrowFade *gf = static_cast(e.getPtr()); + + Ogre::ParticleAffector *affector = partsys->addAffector("GrowFade"); + affector->setParameter("grow_time", Ogre::StringConverter::toString(gf->growTime)); + affector->setParameter("fade_time", Ogre::StringConverter::toString(gf->fadeTime)); + } + else if(e->recType == Nif::RC_NiGravity) { - Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]); - rs->convertColourValue(clr, &colorsRGB[i]); + const Nif::NiGravity *gr = static_cast(e.getPtr()); + + Ogre::ParticleAffector *affector = partsys->addAffector("Gravity"); + affector->setParameter("force", Ogre::StringConverter::toString(gr->mForce)); + affector->setParameter("force_type", (gr->mType==0) ? "wind" : "point"); + affector->setParameter("direction", Ogre::StringConverter::toString(gr->mDirection)); + affector->setParameter("position", Ogre::StringConverter::toString(gr->mPosition)); } - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), - colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, - true); - vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true); - decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); - bind->setBinding(nextBuf++, vbuf); - } + else if(e->recType == Nif::RC_NiParticleColorModifier) + { + const Nif::NiParticleColorModifier *cl = static_cast(e.getPtr()); + const Nif::NiColorData *clrdata = cl->data.getPtr(); - // Texture UV coordinates - size_t numUVs = data->uvlist.size(); - if(numUVs) - { - size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); - vbuf = hwBufMgr->createVertexBuffer(elemSize, srcVerts.size()*numUVs, - Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); - for(size_t i = 0;i < numUVs;i++) + Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator"); + size_t num_colors = std::min(6, clrdata->mKeyList.mKeys.size()); + for(size_t i = 0;i < num_colors;i++) + { + Ogre::ColourValue color; + color.r = clrdata->mKeyList.mKeys[i].mValue[0]; + color.g = clrdata->mKeyList.mKeys[i].mValue[1]; + color.b = clrdata->mKeyList.mKeys[i].mValue[2]; + color.a = clrdata->mKeyList.mKeys[i].mValue[3]; + affector->setParameter("colour"+Ogre::StringConverter::toString(i), + Ogre::StringConverter::toString(color)); + affector->setParameter("time"+Ogre::StringConverter::toString(i), + Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime)); + } + } + else if(e->recType == Nif::RC_NiParticleRotation) { - const std::vector &uvlist = data->uvlist[i]; - vbuf->writeData(i*srcVerts.size()*elemSize, elemSize*srcVerts.size(), &uvlist[0], true); - decl->addElement(nextBuf, i*srcVerts.size()*elemSize, Ogre::VET_FLOAT2, - Ogre::VES_TEXTURE_COORDINATES, i); + // TODO: Implement (Ogre::RotationAffector?) } - bind->setBinding(nextBuf++, vbuf); + else + warn("Unhandled particle modifier "+e->recName); + e = e->extra; } + } - // Triangle faces - const std::vector &srcIdx = data->triangles; - if(srcIdx.size()) - { - ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(), - Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); - ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true); - sub->indexData->indexBuffer = ibuf; - sub->indexData->indexCount = srcIdx.size(); - sub->indexData->indexStart = 0; - } + static void createParticleSystem(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + const Nif::Node *partnode, int flags, int partflags) + { + const Nif::NiAutoNormalParticlesData *particledata = NULL; + if(partnode->recType == Nif::RC_NiAutoNormalParticles) + particledata = static_cast(partnode)->data.getPtr(); + else if(partnode->recType == Nif::RC_NiRotatingParticles) + particledata = static_cast(partnode)->data.getPtr(); - // Assign bone weights for this TriShape - if(skin != NULL) - { - const Nif::NiSkinData *data = skin->data.getPtr(); - const Nif::NodeList &bones = skin->bones; - for(size_t i = 0;i < bones.length();i++) + std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); + if(partnode->name.length() > 0) + fullname += "@type="+partnode->name; + Misc::StringUtils::toLower(fullname); + + Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem(); + + const Nif::NiTexturingProperty *texprop = NULL; + const Nif::NiMaterialProperty *matprop = NULL; + const Nif::NiAlphaProperty *alphaprop = NULL; + const Nif::NiVertexColorProperty *vertprop = NULL; + const Nif::NiZBufferProperty *zprop = NULL; + const Nif::NiSpecularProperty *specprop = NULL; + const Nif::NiWireframeProperty *wireprop = NULL; + bool needTangents = false; + + partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, + texprop, matprop, alphaprop, + vertprop, zprop, specprop, + wireprop, needTangents)); + + partsys->setDefaultDimensions(particledata->particleRadius*2.0f, + particledata->particleRadius*2.0f); + partsys->setCullIndividually(false); + partsys->setParticleQuota(particledata->numParticles); + // TODO: There is probably a field or flag to specify this, as some + // particle effects have it and some don't. + partsys->setKeepParticlesInLocalSpace(true); + + Nif::ControllerPtr ctrl = partnode->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiParticleSystemController) { - Ogre::VertexBoneAssignment boneInf; - boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle(); + const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - const std::vector &weights = data->bones[i].weights; - for(size_t j = 0;j < weights.size();j++) + createParticleEmitterAffectors(partsys, partctrl); + if(!partctrl->emitter.empty() && !partsys->isAttached()) { - boneInf.vertexIndex = weights[j].vertex; - boneInf.weight = weights[j].weight; - sub->addBoneAssignment(boneInf); + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); } + + Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } + ctrl = ctrl->next; + } + + if(!partsys->isAttached()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); } - std::string matname = NIFMaterialLoader::getMaterial(shape, mesh->getName(), mGroup, - texprop, matprop, alphaprop, - vertprop, zprop, specprop); - if(matname.length() > 0) - sub->setMaterialName(matname); + partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); + objectlist.mParticles.push_back(partsys); } - bool findTriShape(Ogre::Mesh *mesh, const Nif::Node *node, - const Nif::NiTexturingProperty *texprop, - const Nif::NiMaterialProperty *matprop, - const Nif::NiAlphaProperty *alphaprop, - const Nif::NiVertexColorProperty *vertprop, - const Nif::NiZBufferProperty *zprop, - const Nif::NiSpecularProperty *specprop) - { - // Scan the property list for material information - const Nif::PropertyList &proplist = node->props; - for(size_t i = 0;i < proplist.length();i++) - { - // Entries may be empty - if(proplist[i].empty()) - continue; - const Nif::Property *pr = proplist[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 - warn("Unhandled property type: "+pr->recName); - } + static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags) + { + do { + if(ctrl->recType == Nif::RC_NiVisController) + { + const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); - if(node->recType == Nif::RC_NiTriShape && mShapeIndex == node->recIndex) - { - handleNiTriShape(mesh, dynamic_cast(node), texprop, matprop, alphaprop, vertprop, zprop, specprop); - return true; - } + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); - const Nif::NiNode *ninode = dynamic_cast(node); - if(ninode) - { - const Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();i++) + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiKeyframeController) { - if(!children[i].empty()) + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + if(!key->data.empty()) { - if(findTriShape(mesh, children[i].getPtr(), texprop, matprop, alphaprop, vertprop, zprop, specprop)) - return true; + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } } - } - return false; + ctrl = ctrl->next; + } while(!ctrl.empty()); } - typedef std::map LoaderMap; - static LoaderMap sLoaders; - -public: - NIFMeshLoader() - { } - NIFMeshLoader(const std::string &name, const std::string &group) - : mName(name), mGroup(group), mShapeIndex(~(size_t)0) - { } - - virtual void loadResource(Ogre::Resource *resource) + static void extractTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap &textkeys) { - Ogre::Mesh *mesh = dynamic_cast(resource); - OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); - - Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); - if(mShapeIndex >= nif->numRecords()) + for(size_t i = 0;i < tk->list.size();i++) { - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - if(!skelMgr->getByName(mName).isNull()) - mesh->setSkeletonName(mName); - return; - } + const std::string &str = tk->list[i].text; + std::string::size_type pos = 0; + while(pos < str.length()) + { + if(::isspace(str[pos])) + { + pos++; + continue; + } + + std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); + if(nextpos != std::string::npos) + { + do { + nextpos--; + } while(nextpos > pos && ::isspace(str[nextpos])); + nextpos++; + } + else if(::isspace(*str.rbegin())) + { + std::string::const_iterator last = str.end(); + do { + last--; + } while(last != str.begin() && ::isspace(*last)); + nextpos = std::distance(str.begin(), ++last); + } + std::string result = str.substr(pos, nextpos-pos); + textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); - const Nif::Node *node = dynamic_cast(nif->getRecord(0)); - findTriShape(mesh, node, NULL, NULL, NULL, NULL, NULL, NULL); + pos = nextpos; + } + } } - void createMeshes(const Nif::Node *node, MeshInfoList &meshes, int flags=0) + + static void createObjects(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, const Nif::Node *node, + ObjectList &objectlist, int flags, int animflags, int partflags) { - // Do not create meshes for the collision shape (includes all children) + // Do not create objects for the collision shape (includes all children) if(node->recType == Nif::RC_RootCollisionNode) return; - flags |= node->flags; - - // Marker objects: just skip the entire node + // Marker objects: just skip the entire node branch /// \todo don't do this in the editor if (node->name.find("marker") != std::string::npos) return; + if(node->recType == Nif::RC_NiBSAnimationNode) + animflags |= node->flags; + else if(node->recType == Nif::RC_NiBSParticleNode) + partflags |= node->flags; + else + flags |= node->flags; + Nif::ExtraPtr e = node->extra; while(!e.empty()) { - Nif::NiStringExtraData *sd; - if((sd=dynamic_cast(e.getPtr())) != NULL) + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + extractTextKeys(tk, objectlist.mTextKeys[trgtid]); + } + else if(e->recType == Nif::RC_NiStringExtraData) { + const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); // String markers may contain important information // affecting the entire subtree of this obj if(sd->string == "MRK") { - // Marker objects. These are only visible in the + // Marker objects. These meshes are only visible in the // editor. - flags |= 0x01; + flags |= 0x80000000; } } + e = e->extra; } - if(node->recType == Nif::RC_NiTriShape && !(flags&0x01)) // Not hidden - { - const Nif::NiTriShape *shape = dynamic_cast(node); - - Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex); - if(shape->name.length() > 0) - fullname += "@shape="+shape->name; + if(!node->controller.empty() && (node->parent || node->recType != Nif::RC_NiNode)) + createNodeControllers(name, node->controller, objectlist, animflags); - Misc::StringUtils::toLower(fullname); - Ogre::MeshPtr mesh = meshMgr.getByName(fullname); - if(mesh.isNull()) - { - NIFMeshLoader *loader = &sLoaders[fullname]; - *loader = *this; - loader->mShapeIndex = shape->recIndex; + if(node->recType == Nif::RC_NiCamera) + { + /* Ignored */ + } - mesh = meshMgr.createManual(fullname, mGroup, loader); - mesh->setAutoBuildEdgeLists(false); - } + if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) + { + createEntity(name, group, sceneMgr, objectlist, node, flags, animflags); + } - meshes.push_back(MeshInfo(mesh->getName(), shape->name)); + if((node->recType == Nif::RC_NiAutoNormalParticles || + node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) + { + createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags); } const Nif::NiNode *ninode = dynamic_cast(node); @@ -1151,146 +784,151 @@ public: for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createMeshes(children[i].getPtr(), meshes, flags); + createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags); } } } - void createEmptyMesh(const Nif::Node *node, MeshInfoList &meshes) + static void createSkelBase(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, const Nif::Node *node, + ObjectList &objectlist) { /* This creates an empty mesh to which a skeleton gets attached. This * is to ensure we have an entity with a skeleton instance, even if all - * other meshes are hidden or entities attached to a specific node - * instead of skinned. */ - std::string fullname = mName; - Misc::StringUtils::toLower(fullname); - + * other entities are attached to bones and not skinned. */ Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - Ogre::MeshPtr mesh = meshMgr.getByName(fullname); - if(mesh.isNull()) - { - NIFMeshLoader *loader = &sLoaders[fullname]; - *loader = *this; + if(meshMgr.getByName(name).isNull()) + NIFMeshLoader::createMesh(name, name, group, ~(size_t)0); - mesh = meshMgr.createManual(fullname, mGroup, loader); - mesh->setAutoBuildEdgeLists(false); - } - meshes.push_back(MeshInfo(mesh->getName(), node->name)); + objectlist.mSkelBase = sceneMgr->createEntity(name); + objectlist.mEntities.push_back(objectlist.mSkelBase); } -}; -NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; - - -typedef std::map MeshInfoMap; -static MeshInfoMap sMeshInfoMap; -MeshInfoList Loader::load(const std::string &name, const std::string &group) -{ - MeshInfoMap::const_iterator meshiter = sMeshInfoMap.find(name); - if(meshiter != sMeshInfoMap.end()) - return meshiter->second; - - MeshInfoList &meshes = sMeshInfoMap[name]; - Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name); - Nif::NIFFile &nif = *pnif.get(); - if(nif.numRecords() < 1) +public: + static void load(Ogre::SceneManager *sceneMgr, ObjectList &objectlist, const std::string &name, const std::string &group, int flags=0) { - nif.warn("Found no NIF records in "+name+"."); - return meshes; - } + Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes in "+name+"."); + return; + } - // The first record is assumed to be the root node - Nif::Record const *r = nif.getRecord(0); - assert(r != NULL); + const Nif::Record *r = nif->getRoot(0); + assert(r != NULL); - Nif::Node const *node = dynamic_cast(r); - if(node == NULL) - { - nif.warn("First record in "+name+" was not a node, but a "+ - r->recName+"."); - return meshes; - } + const Nif::Node *node = dynamic_cast(r); + if(node == NULL) + { + nif->warn("First root in "+name+" was not a node, but a "+ + r->recName+"."); + return; + } - bool hasSkel = Ogre::SkeletonManager::getSingleton().resourceExists(name); - if(!hasSkel) - hasSkel = !NIFSkeletonLoader::createSkeleton(name, group, node).isNull(); + if(Ogre::SkeletonManager::getSingleton().resourceExists(name) || + !NIFSkeletonLoader::createSkeleton(name, group, node).isNull()) + { + // Create a base skeleton entity if this NIF needs one + createSkelBase(name, group, sceneMgr, node, objectlist); + } + createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0); + } - NIFMeshLoader meshldr(name, group); - if(hasSkel) - meshldr.createEmptyMesh(node, meshes); - meshldr.createMeshes(node, meshes, 0); + static void loadKf(Ogre::Skeleton *skel, const std::string &name, + TextKeyMap &textKeys, std::vector > &ctrls) + { + Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes in "+name+"."); + return; + } - return meshes; -} + const Nif::Record *r = nif->getRoot(0); + assert(r != NULL); -EntityList Loader::createEntities(Ogre::SceneNode *parentNode, std::string name, const std::string &group) -{ - EntityList entitylist; + if(r->recType != Nif::RC_NiSequenceStreamHelper) + { + nif->warn("First root was not a NiSequenceStreamHelper, but a "+ + r->recName+"."); + return; + } + const Nif::NiSequenceStreamHelper *seq = static_cast(r); - Misc::StringUtils::toLower(name); - MeshInfoList meshes = load(name, group); - if(meshes.size() == 0) - return entitylist; + Nif::ExtraPtr extra = seq->extra; + if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) + { + nif->warn("First extra data was not a NiTextKeyExtraData, but a "+ + (extra.empty() ? std::string("nil") : extra->recName)+"."); + return; + } - Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - for(size_t i = 0;i < meshes.size();i++) - { - entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].mMeshName)); - Ogre::Entity *entity = entitylist.mEntities.back(); - if(!entitylist.mSkelBase && entity->hasSkeleton()) - entitylist.mSkelBase = entity; - } + extractTextKeys(static_cast(extra.getPtr()), textKeys); - if(entitylist.mSkelBase) - { - parentNode->attachObject(entitylist.mSkelBase); - for(size_t i = 0;i < entitylist.mEntities.size();i++) + extra = extra->extra; + Nif::ControllerPtr ctrl = seq->controller; + for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) { - Ogre::Entity *entity = entitylist.mEntities[i]; - if(entity != entitylist.mSkelBase && entity->hasSkeleton()) + if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) { - entity->shareSkeletonInstanceWith(entitylist.mSkelBase); - parentNode->attachObject(entity); + nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); + continue; } - else if(entity != entitylist.mSkelBase) - entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity); + + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + + if(key->data.empty()) + continue; + if(!skel->hasBone(strdata->string)) + continue; + + Ogre::Bone *trgtbone = skel->getBone(strdata->string); + Ogre::ControllerValueRealPtr srcval; + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false)); + + ctrls.push_back(Ogre::Controller(srcval, dstval, func)); } } - else +}; + + +ObjectList Loader::createObjects(Ogre::SceneNode *parentNode, std::string name, const std::string &group) +{ + ObjectList objectlist; + + Misc::StringUtils::toLower(name); + NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group); + + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - for(size_t i = 0;i < entitylist.mEntities.size();i++) - parentNode->attachObject(entitylist.mEntities[i]); + Ogre::Entity *entity = objectlist.mEntities[i]; + if(!entity->isAttached()) + parentNode->attachObject(entity); } - return entitylist; + return objectlist; } -EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneNode *parentNode, - std::string name, const std::string &group) +ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + std::string name, const std::string &group) { - EntityList entitylist; + ObjectList objectlist; Misc::StringUtils::toLower(name); - MeshInfoList meshes = load(name, group); - if(meshes.size() == 0) - return entitylist; + NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group); bool isskinned = false; - Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - std::string filter = "@shape=tri "+bonename; - Misc::StringUtils::toLower(filter); - for(size_t i = 0;i < meshes.size();i++) + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].mMeshName); - if(!entitylist.mSkelBase) + Ogre::Entity *ent = objectlist.mEntities[i]; + if(objectlist.mSkelBase != ent && ent->hasSkeleton()) { - if(ent->hasSkeleton()) - entitylist.mSkelBase = ent; - } - else if(!isskinned && ent->hasSkeleton()) isskinned = true; - entitylist.mEntities.push_back(ent); + break; + } } Ogre::Vector3 scale(1.0f); @@ -1299,107 +937,62 @@ EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonen if(isskinned) { - for(size_t i = 0;i < entitylist.mEntities.size();i++) + std::string filter = "@shape=tri "+bonename; + Misc::StringUtils::toLower(filter); + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - Ogre::Entity *entity = entitylist.mEntities[i]; + Ogre::Entity *entity = objectlist.mEntities[i]; if(entity->hasSkeleton()) { - if(entity != entitylist.mSkelBase) - entity->shareSkeletonInstanceWith(entitylist.mSkelBase); - if(entity->getMesh()->getName().find(filter) != std::string::npos) + if(entity == objectlist.mSkelBase || + entity->getMesh()->getName().find(filter) != std::string::npos) parentNode->attachObject(entity); } else { - if(entity->getMesh()->getName().find(filter) != std::string::npos) - entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity); + if(entity->getMesh()->getName().find(filter) == std::string::npos) + entity->detachFromParent(); } } } else { - for(size_t i = 0;i < entitylist.mEntities.size();i++) + for(size_t i = 0;i < objectlist.mEntities.size();i++) { - Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entitylist.mEntities[i]); - tag->setScale(scale); + Ogre::Entity *entity = objectlist.mEntities[i]; + if(!entity->isAttached()) + { + Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity); + tag->setScale(scale); + } } } - return entitylist; + return objectlist; } -Ogre::SkeletonPtr Loader::getSkeleton(std::string name, const std::string &group) +ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group) { - Ogre::SkeletonPtr skel; + ObjectList objectlist; Misc::StringUtils::toLower(name); - skel = Ogre::SkeletonManager::getSingleton().getByName(name); - if(!skel.isNull()) - return skel; - - Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); - if(nif->numRecords() < 1) - { - nif->warn("Found no NIF records in "+name+"."); - return skel; - } - - // The first record is assumed to be the root node - const Nif::Record *r = nif->getRecord(0); - assert(r != NULL); + NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000); - const Nif::Node *node = dynamic_cast(r); - if(node == NULL) - { - nif->warn("First record in "+name+" was not a node, but a "+ - r->recName+"."); - return skel; - } + if(objectlist.mSkelBase) + parentNode->attachObject(objectlist.mSkelBase); - return NIFSkeletonLoader::createSkeleton(name, group, node); + return objectlist; } -/* More code currently not in use, from the old D source. This was - used in the first attempt at loading NIF meshes, where each submesh - in the file was given a separate bone in a skeleton. Unfortunately - the OGRE skeletons can't hold more than 256 bones, and some NIFs go - way beyond that. The code might be of use if we implement animated - submeshes like this (the part of the NIF that is animated is - usually much less than the entire file, but the method might still - not be water tight.) - -// Insert a raw RGBA image into the texture system. -extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, void *data) +void Loader::createKfControllers(Ogre::Entity *skelBase, + const std::string &name, + TextKeyMap &textKeys, + std::vector > &ctrls) { - TexturePtr texture = TextureManager::getSingleton().createManual( - name, // name - "General", // group - TEX_TYPE_2D, // type - width, height, // width & height - 0, // number of mipmaps - PF_BYTE_RGBA, // pixel format - TU_DEFAULT); // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for - // textures updated very often (e.g. each frame) - - // Get the pixel buffer - HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); - - // Lock the pixel buffer and get a pixel box - pixelBuffer->lock(HardwareBuffer::HBL_NORMAL); // for best performance use HBL_DISCARD! - const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - void *dest = pixelBox.data; - - // Copy the data - memcpy(dest, data, width*height*4); - - // Unlock the pixel buffer - pixelBuffer->unlock(); + NIFObjectLoader::loadKf(skelBase->getSkeleton(), name, textKeys, ctrls); } -*/ - -} // nsmaepace NifOgre +} // namespace NifOgre diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index b8b2e3c00..edad13a9a 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -25,11 +25,11 @@ #define OPENMW_COMPONENTS_NIFOGRE_OGRENIFLOADER_HPP #include -#include -#include +#include #include #include +#include // FIXME: This namespace really doesn't do anything Nif-specific. Any supportable @@ -39,53 +39,64 @@ namespace NifOgre typedef std::multimap TextKeyMap; static const char sTextKeyExtraDataID[] = "TextKeyExtraData"; -struct EntityList { - std::vector mEntities; +struct ObjectList { Ogre::Entity *mSkelBase; + std::vector mEntities; + std::vector mParticles; + std::vector mLights; - EntityList() : mSkelBase(0) - { } -}; - + std::map mTextKeys; -/* This holds a list of mesh names, the names of their parent nodes, and the offset - * from their parent nodes. */ -struct MeshInfo { - std::string mMeshName; - std::string mTargetNode; + std::vector > mControllers; - MeshInfo(const std::string &name, const std::string &target) - : mMeshName(name), mTargetNode(target) + ObjectList() : mSkelBase(0) { } }; -typedef std::vector MeshInfoList; + class Loader { - static MeshInfoList load(const std::string &name, const std::string &group); - public: - static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneNode *parentNode, - std::string name, - const std::string &group="General"); - - static EntityList createEntities(Ogre::SceneNode *parentNode, - std::string name, - const std::string &group="General"); - - static Ogre::SkeletonPtr getSkeleton(std::string name, const std::string &group="General"); + static ObjectList createObjects(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + std::string name, + const std::string &group="General"); + + static ObjectList createObjects(Ogre::SceneNode *parentNode, + std::string name, + const std::string &group="General"); + + static ObjectList createObjectBase(Ogre::SceneNode *parentNode, + std::string name, + const std::string &group="General"); + + static void createKfControllers(Ogre::Entity *skelBase, + const std::string &name, + TextKeyMap &textKeys, + std::vector > &ctrls); }; -} - -namespace std +// FIXME: Should be with other general Ogre extensions. +template +class NodeTargetValue : public Ogre::ControllerValue { +protected: + Ogre::Node *mNode; -// These operators allow extra data types to be stored in an Ogre::Any -// object, which can then be stored in user object bindings on the nodes +public: + NodeTargetValue(Ogre::Node *target) : mNode(target) + { } + + virtual Ogre::Quaternion getRotation(T value) const = 0; + virtual Ogre::Vector3 getTranslation(T value) const = 0; + virtual Ogre::Vector3 getScale(T value) const = 0; -ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&); + void setNode(Ogre::Node *target) + { mNode = target; } + Ogre::Node *getNode() const + { return mNode; } +}; +typedef Ogre::SharedPtr > NodeTargetValueRealPtr; } diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp new file mode 100644 index 000000000..04bffdeab --- /dev/null +++ b/components/nifogre/skeleton.cpp @@ -0,0 +1,153 @@ +#include "skeleton.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace NifOgre +{ + +void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent) +{ + Ogre::Bone *bone; + if(!skel->hasBone(node->name)) + bone = skel->createBone(node->name); + else + bone = skel->createBone(); + if(parent) parent->addChild(bone); + mNifToOgreHandleMap[node->recIndex] = bone->getHandle(); + + bone->setOrientation(node->trafo.rotation); + bone->setPosition(node->trafo.pos); + bone->setScale(Ogre::Vector3(node->trafo.scale)); + bone->setBindingPose(); + + if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ + node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ + node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ + node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */ + node->recType == Nif::RC_NiBSParticleNode || + node->recType == Nif::RC_NiCamera || + node->recType == Nif::RC_NiAutoNormalParticles || + node->recType == Nif::RC_NiRotatingParticles + )) + warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName()); + + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(!(ctrl->recType == Nif::RC_NiParticleSystemController || + ctrl->recType == Nif::RC_NiVisController || + ctrl->recType == Nif::RC_NiUVController || + ctrl->recType == Nif::RC_NiKeyframeController || + ctrl->recType == Nif::RC_NiGeomMorpherController + )) + warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); + ctrl = ctrl->next; + } + + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + buildBones(skel, children[i].getPtr(), bone); + } + } +} + +void NIFSkeletonLoader::loadResource(Ogre::Resource *resource) +{ + Ogre::Skeleton *skel = dynamic_cast(resource); + OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); + + Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); + const Nif::Node *node = static_cast(nif->getRoot(0)); + + try { + buildBones(skel, node); + } + catch(std::exception &e) { + std::cerr<< "Exception while loading "<getName() <boneTrafo) + return true; + + if(!node->controller.empty() || node->name == "AttachLight") + return true; + + if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) + { + const Nif::NiNode *ninode = static_cast(node); + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + { + if(needSkeleton(children[i].getPtr())) + return true; + } + } + return false; + } + if(node->recType == Nif::RC_NiTriShape) + return false; + + return true; +} + +Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) +{ + bool forceskel = false; + std::string::size_type extpos = name.rfind('.'); + if(extpos != std::string::npos && name.compare(extpos, name.size()-extpos, ".nif") == 0) + { + Ogre::ResourceGroupManager &resMgr = Ogre::ResourceGroupManager::getSingleton(); + forceskel = resMgr.resourceExistsInAnyGroup(name.substr(0, extpos)+".kf"); + } + + if(forceskel || needSkeleton(node)) + { + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + return skelMgr.create(name, group, true, &sLoaders[name]); + } + + return Ogre::SkeletonPtr(); +} + +// Looks up an Ogre Bone handle ID from a NIF's record index. Should only be +// used when the bone name is insufficient as this is a relatively slow lookup +int NIFSkeletonLoader::lookupOgreBoneHandle(const std::string &nifname, int idx) +{ + LoaderMap::const_iterator loader = sLoaders.find(nifname); + if(loader != sLoaders.end()) + { + std::map::const_iterator entry = loader->second.mNifToOgreHandleMap.find(idx); + if(entry != loader->second.mNifToOgreHandleMap.end()) + return entry->second; + } + throw std::runtime_error("Invalid NIF record lookup ("+nifname+", index "+Ogre::StringConverter::toString(idx)+")"); +} + +NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; + +} diff --git a/components/nifogre/skeleton.hpp b/components/nifogre/skeleton.hpp new file mode 100644 index 000000000..9ec3a0c82 --- /dev/null +++ b/components/nifogre/skeleton.hpp @@ -0,0 +1,62 @@ +#ifndef COMPONENTS_NIFOGRE_SKELETON_HPP +#define COMPONENTS_NIFOGRE_SKELETON_HPP + +#include +#include +#include + +#include + +#include "ogrenifloader.hpp" + +namespace Nif +{ + class NiTextKeyExtraData; + class Node; + class NiKeyframeController; +} + +namespace NifOgre +{ + +/** Manual resource loader for NIF skeletons. This is the main class + responsible for translating the internal NIF skeleton structure into + something Ogre can use (includes animations and node TextKeyData). + */ +class NIFSkeletonLoader : public Ogre::ManualResourceLoader +{ + static void warn(const std::string &msg) + { + std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl; + } + + static void fail(const std::string &msg) + { + std::cerr << "NIFSkeletonLoader: Fail: "<< msg << std::endl; + abort(); + } + + void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL); + + static bool needSkeleton(const Nif::Node *node); + + // Lookup to retrieve an Ogre bone handle for a given Nif record index + std::map mNifToOgreHandleMap; + + typedef std::map LoaderMap; + static LoaderMap sLoaders; + +public: + void loadResource(Ogre::Resource *resource); + + static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node); + + // Looks up an Ogre Bone handle ID from a NIF's record index. Should only + // be used when the bone name is insufficient as this is a relatively slow + // lookup + static int lookupOgreBoneHandle(const std::string &nifname, int idx); +}; + +} + +#endif diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp new file mode 100644 index 000000000..ce2118cdb --- /dev/null +++ b/components/terrain/chunk.cpp @@ -0,0 +1,169 @@ +#include "chunk.hpp" + +#include +#include + +#include "quadtreenode.hpp" +#include "world.hpp" +#include "storage.hpp" + +namespace Terrain +{ + + Chunk::Chunk(QuadTreeNode* node, short lodLevel) + : mNode(node) + , mVertexLod(lodLevel) + , mAdditionalLod(0) + { + mVertexData = OGRE_NEW Ogre::VertexData; + mVertexData->vertexStart = 0; + + // Set the total number of vertices + size_t numVertsOneSide = mNode->getSize() * (ESM::Land::LAND_SIZE-1); + numVertsOneSide /= 1 << lodLevel; + numVertsOneSide += 1; + assert((int)numVertsOneSide == ESM::Land::LAND_SIZE); + mVertexData->vertexCount = numVertsOneSide * numVertsOneSide; + + // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) + Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration; + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + size_t nextBuffer = 0; + + // Positions + vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); + mVertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + // Normals + vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); + mNormalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + + // UV texture coordinates + vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT2, + Ogre::VES_TEXTURE_COORDINATES, 0); + Ogre::HardwareVertexBufferSharedPtr uvBuf = mNode->getTerrain()->getVertexBuffer(numVertsOneSide); + + // Colours + vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); + mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + + mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), + mVertexBuffer, mNormalBuffer, mColourBuffer); + + mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); + mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer); + mVertexData->vertexBufferBinding->setBinding(2, uvBuf); + mVertexData->vertexBufferBinding->setBinding(3, mColourBuffer); + + mIndexData = OGRE_NEW Ogre::IndexData(); + mIndexData->indexStart = 0; + } + + + + void Chunk::updateIndexBuffer() + { + // Fetch a suitable index buffer (which may be shared) + size_t ourLod = mVertexLod + mAdditionalLod; + + int flags = 0; + + for (int i=0; i<4; ++i) + { + QuadTreeNode* neighbour = mNode->getNeighbour((Direction)i); + + // If the neighbour isn't currently rendering itself, + // go up until we find one. NOTE: We don't need to go down, + // because in that case neighbour's detail would be higher than + // our detail and the neighbour would handle stitching by itself. + while (neighbour && !neighbour->hasChunk()) + neighbour = neighbour->getParent(); + + size_t lod = 0; + if (neighbour) + lod = neighbour->getActualLodLevel(); + + if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are - + lod = 0; // neighbours with more detail will do the stitching themselves + + // Use 4 bits for each LOD delta + if (lod > 0) + { + assert (lod - ourLod < (1 << 4)); + flags |= int(lod - ourLod) << (4*i); + } + } + + flags |= ((int)mAdditionalLod) << (4*4); + + size_t numIndices; + mIndexBuffer = mNode->getTerrain()->getIndexBuffer(flags, numIndices); + mIndexData->indexCount = numIndices; + mIndexData->indexBuffer = mIndexBuffer; + } + + Chunk::~Chunk() + { + OGRE_DELETE mVertexData; + OGRE_DELETE mIndexData; + } + + void Chunk::setMaterial(const Ogre::MaterialPtr &material) + { + mMaterial = material; + } + + const Ogre::AxisAlignedBox& Chunk::getBoundingBox(void) const + { + return mNode->getBoundingBox(); + } + + Ogre::Real Chunk::getBoundingRadius(void) const + { + return mNode->getBoundingBox().getHalfSize().length(); + } + + void Chunk::_updateRenderQueue(Ogre::RenderQueue* queue) + { + queue->addRenderable(this, mRenderQueueID); + } + + void Chunk::visitRenderables(Ogre::Renderable::Visitor* visitor, + bool debugRenderables) + { + visitor->visit(this, 0, false); + } + + const Ogre::MaterialPtr& Chunk::getMaterial(void) const + { + return mMaterial; + } + + void Chunk::getRenderOperation(Ogre::RenderOperation& op) + { + assert (!mIndexBuffer.isNull() && "Trying to render, but no index buffer set!"); + op.useIndexes = true; + op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; + op.vertexData = mVertexData; + op.indexData = mIndexData; + } + + void Chunk::getWorldTransforms(Ogre::Matrix4* xform) const + { + *xform = getParentSceneNode()->_getFullTransform(); + } + + Ogre::Real Chunk::getSquaredViewDepth(const Ogre::Camera* cam) const + { + return getParentSceneNode()->getSquaredViewDepth(cam); + } + + const Ogre::LightList& Chunk::getLights(void) const + { + return queryLights(); + } + +} diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp new file mode 100644 index 000000000..d74c65ba6 --- /dev/null +++ b/components/terrain/chunk.hpp @@ -0,0 +1,63 @@ +#ifndef COMPONENTS_TERRAIN_TERRAINBATCH_H +#define COMPONENTS_TERRAIN_TERRAINBATCH_H + +#include +#include + +namespace Terrain +{ + + class QuadTreeNode; + + /** + * @brief Renders a chunk of terrain, either using alpha splatting or a composite map. + */ + class Chunk : public Ogre::Renderable, public Ogre::MovableObject + { + public: + /// @param lodLevel LOD level for the vertex buffer. + Chunk (QuadTreeNode* node, short lodLevel); + virtual ~Chunk(); + + void setMaterial (const Ogre::MaterialPtr& material); + + /// Set additional LOD applied on top of vertex LOD. \n + /// This is achieved by changing the index buffer to omit vertices. + void setAdditionalLod (size_t lod) { mAdditionalLod = lod; } + size_t getAdditionalLod() { return mAdditionalLod; } + + void updateIndexBuffer(); + + // Inherited from MovableObject + virtual const Ogre::String& getMovableType(void) const { static Ogre::String t = "MW_TERRAIN"; return t; } + virtual const Ogre::AxisAlignedBox& getBoundingBox(void) const; + virtual Ogre::Real getBoundingRadius(void) const; + virtual void _updateRenderQueue(Ogre::RenderQueue* queue); + virtual void visitRenderables(Renderable::Visitor* visitor, + bool debugRenderables = false); + + // Inherited from Renderable + virtual const Ogre::MaterialPtr& getMaterial(void) const; + virtual void getRenderOperation(Ogre::RenderOperation& op); + virtual void getWorldTransforms(Ogre::Matrix4* xform) const; + virtual Ogre::Real getSquaredViewDepth(const Ogre::Camera* cam) const; + virtual const Ogre::LightList& getLights(void) const; + + private: + QuadTreeNode* mNode; + Ogre::MaterialPtr mMaterial; + + size_t mVertexLod; + size_t mAdditionalLod; + + Ogre::VertexData* mVertexData; + Ogre::IndexData* mIndexData; + Ogre::HardwareVertexBufferSharedPtr mVertexBuffer; + Ogre::HardwareVertexBufferSharedPtr mNormalBuffer; + Ogre::HardwareVertexBufferSharedPtr mColourBuffer; + Ogre::HardwareIndexBufferSharedPtr mIndexBuffer; + }; + +} + +#endif diff --git a/components/terrain/esm_land_factory.cpp b/components/terrain/esm_land_factory.cpp deleted file mode 100644 index 5cab7ed5d..000000000 --- a/components/terrain/esm_land_factory.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "esm_land_factory.hpp" - -// The first one already includes the others implicitly, but it -// doesn't hurt to be explicit. -#include "../esm_store/store.hpp" -#include "../esm/esmreader.hpp" -#include "../esm/loadland.hpp" - -using namespace Terrain; - -bool ESMLandFactory::has(int x, int y) -{ - return store.landscapes.has(x,y); -} diff --git a/components/terrain/esm_land_factory.hpp b/components/terrain/esm_land_factory.hpp deleted file mode 100644 index bb1f9a8c6..000000000 --- a/components/terrain/esm_land_factory.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TERRAIN_ESM_LAND_FACTORY_H -#define TERRAIN_ESM_LAND_FACTORY_H - -#include "land_factory.hpp" - -namespace ESMS -{ - struct ESMStore; -} - -namespace ESM -{ - class ESMReader; -} - -namespace Terrain -{ - /* - Land factory that loads data from ESM files. - */ - class ESMLandFactory - { - ESMS::ESMStore &store; - ESM::ESMReader &reader; - - public: - // Initialize the land factory. Note that refrences to the given - // store and reader are stored in the class, so the given objects - // must be valid for a long as you plan to use this factory. - ESMLandFactory(ESMS::ESMStore &st, ESM::ESMReader &rd) - : store(st), reader(rd) {} - - // True if this factory has any data for the given grid cell. - bool has(int x, int y); - }; -} -#endif diff --git a/components/terrain/heightmap.hpp b/components/terrain/heightmap.hpp deleted file mode 100644 index e395b541e..000000000 --- a/components/terrain/heightmap.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef TERRAIN_HEIGHTMAP_H -#define TERRAIN_HEIGHTMAP_H - -/* - Generic interface for a structure holding heightmap data. - - A HeightMap returns information about landscape data in the form of - a regular grid of float heights. - */ - -namespace Terrain -{ - struct HeightMap - { - // Get height from grid position, counted from 0 to getNumX/Y(). - virtual float getHeight(int x, int y) = 0; - - // Get heigth from vertex index, assumed to be y*getNumX() + x. - virtual float getHeight(int index) = 0; - - virtual float getMinX() = 0; - virtual float getMaxX() = 0; - virtual float getMinY() = 0; - virtual float getMaxY() = 0; - - virtual int getNumX() = 0; - virtual int getNumY() = 0; - - // True if the given coordinate is within the grid - bool isWithin(float x, float y) - { - return - x >= getMinX() && x < getMaxX() && - y >= getMinY() && y < getMaxY(); - } - }; -} -#endif diff --git a/components/terrain/heightmapbuf.hpp b/components/terrain/heightmapbuf.hpp deleted file mode 100644 index d147e6015..000000000 --- a/components/terrain/heightmapbuf.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef TERRAIN_HEIGHTMAPBUF_H -#define TERRAIN_HEIGHTMAPBUF_H - -/* - A HeightMap implementation that stores heigths in a buffer. - */ - -#include "heightmap.hpp" -#include "land_factory.hpp" -#include -#include - -namespace Terrain -{ - class HeightMapBuffer : public HeightMap - { - std::vector buf; - - float beginX, sizeX, endX; - float beginY, sizeY, endY; - - int numX, numY; - - public: - void load(LandDataPtr data, const LandInfo &info) - { - // We don't support other kinds of grid data yet. - assert(info.grid == LGT_Quadratic); - assert(info.data == LDT_Float); - - // Set up internal data - beginX = info.xoffset; - sizeX = info.xsize; - endX = beginX+sizeX; - numX = info.numx; - - beginY = info.yoffset; - sizeY = info.ysize; - endY = beginY+sizeY; - numY = info.numy; - - // Prepare the buffer and load it - buf.resize(numX*numY); - - data.read(&buf[0], buf.size()*sizeof(float)); - } - - // Functions inherited from HeightMap: - - float getHeight(int x, int y) - { - assert(x>=0 && x=0 && y= 0 && index < buf.size()); - return buf[index]; - } - - float getMinX() { return beginX; } - float getMaxX() { return endX; } - float getMinY() { return beginY; } - float getMaxY() { return endY; } - - int getNumX() { return numX; } - int getNumY() { return numY; } - }; -} -#endif diff --git a/components/terrain/land_factory.hpp b/components/terrain/land_factory.hpp deleted file mode 100644 index c4d160443..000000000 --- a/components/terrain/land_factory.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef TERRAIN_LAND_FACTORY_H -#define TERRAIN_LAND_FACTORY_H - -namespace Terrain -{ - enum LandInfoGridType - { - LGT_Quadratic - }; - - enum LandInfoDataType - { - LDT_Float - }; - - struct LandInfo - { - // Type information - LandInfoGridType grid; - LandInfoDataType data; - - // Landscape size and number of vertices. Note that xsize and - // ysize may be negative, signaling a flipped landscape in that - // direction. - float xsize, ysize; - int numx, numy; - - // World offset along the same x/y axes. Whether these are set or - // used depends on the client implementation. - float xoffset, yoffset; - }; - - /* - Factory class that provides streams to land data cells. Each - "cell" has a unique integer coordinate in the plane. - */ - struct LandFactory - { - // True if this factory has any data for the given grid cell. - virtual bool has(int x, int y) = 0; - }; -} -#endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp new file mode 100644 index 000000000..ebf6046ff --- /dev/null +++ b/components/terrain/material.cpp @@ -0,0 +1,300 @@ +#include "material.hpp" + +#include +#include +#include + +#include + +namespace +{ + +int getBlendmapIndexForLayer (int layerIndex) +{ + return std::floor((layerIndex-1)/4.f); +} + +std::string getBlendmapComponentForLayer (int layerIndex) +{ + int n = (layerIndex-1)%4; + if (n == 0) + return "x"; + if (n == 1) + return "y"; + if (n == 2) + return "z"; + else + return "w"; +} + +} + +namespace Terrain +{ + + MaterialGenerator::MaterialGenerator(bool shaders) + : mShaders(shaders) + , mShadows(false) + , mSplitShadows(false) + { + + } + + Ogre::MaterialPtr MaterialGenerator::generate(Ogre::MaterialPtr mat) + { + return create(mat, false, false); + } + + Ogre::MaterialPtr MaterialGenerator::generateForCompositeMapRTT(Ogre::MaterialPtr mat) + { + return create(mat, true, false); + } + + Ogre::MaterialPtr MaterialGenerator::generateForCompositeMap(Ogre::MaterialPtr mat) + { + return create(mat, false, true); + } + + Ogre::MaterialPtr MaterialGenerator::create(Ogre::MaterialPtr mat, bool renderCompositeMap, bool displayCompositeMap) + { + assert(!renderCompositeMap || !displayCompositeMap); + if (!mat.isNull()) + { + sh::Factory::getInstance().destroyMaterialInstance(mat->getName()); + Ogre::MaterialManager::getSingleton().remove(mat->getName()); + } + + static int count = 0; + std::stringstream name; + name << "terrain/mat" << count++; + + if (!mShaders) + { + mat = Ogre::MaterialManager::getSingleton().create(name.str(), + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + Ogre::Technique* technique = mat->getTechnique(0); + technique->removeAllPasses(); + + if (displayCompositeMap) + { + Ogre::Pass* pass = technique->createPass(); + pass->setVertexColourTracking(Ogre::TVC_AMBIENT|Ogre::TVC_DIFFUSE); + pass->createTextureUnitState(mCompositeMap)->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); + } + else + { + assert(mLayerList.size() == mBlendmapList.size()+1); + std::vector::iterator blend = mBlendmapList.begin(); + for (std::vector::iterator layer = mLayerList.begin(); layer != mLayerList.end(); ++layer) + { + Ogre::Pass* pass = technique->createPass(); + pass->setLightingEnabled(false); + pass->setVertexColourTracking(Ogre::TVC_NONE); + // TODO: How to handle fog? + pass->setFog(true, Ogre::FOG_NONE); + + bool first = (layer == mLayerList.begin()); + + Ogre::TextureUnitState* tus; + + if (!first) + { + pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); + pass->setDepthFunction(Ogre::CMPF_EQUAL); + + tus = pass->createTextureUnitState((*blend)->getName()); + tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA, + Ogre::LBS_TEXTURE, + Ogre::LBS_TEXTURE); + tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, + Ogre::LBS_TEXTURE, + Ogre::LBS_TEXTURE); + tus->setIsAlpha(true); + tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); + + float scale = (16/(16.f+1.f)); + tus->setTextureScale(1.f/scale,1.f/scale); + } + + // Add the actual layer texture on top of the alpha map. + tus = pass->createTextureUnitState("textures\\" + *layer); + if (!first) + tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, + Ogre::LBS_TEXTURE, + Ogre::LBS_CURRENT); + + tus->setTextureScale(1/16.f,1/16.f); + + if (!first) + ++blend; + } + + if (!renderCompositeMap) + { + Ogre::Pass* lightingPass = technique->createPass(); + lightingPass->setSceneBlending(Ogre::SBT_MODULATE); + lightingPass->setVertexColourTracking(Ogre::TVC_AMBIENT|Ogre::TVC_DIFFUSE); + lightingPass->setFog(true, Ogre::FOG_NONE); + } + } + + return mat; + } + else + { + sh::MaterialInstance* material = sh::Factory::getInstance().createMaterialInstance (name.str()); + material->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); + + if (displayCompositeMap) + { + sh::MaterialInstancePass* p = material->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 ("is_first_pass", sh::makeProperty(new sh::BooleanValue(true))); + p->mShaderProperties.setProperty ("render_composite_map", sh::makeProperty(new sh::BooleanValue(false))); + p->mShaderProperties.setProperty ("display_composite_map", sh::makeProperty(new sh::BooleanValue(true))); + p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue("0"))); + p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue("0"))); + + sh::MaterialInstanceTextureUnit* tex = p->createTextureUnit ("compositeMap"); + tex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(mCompositeMap))); + tex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + // shadow. TODO: repeated, put in function + if (mShadows) + { + for (Ogre::uint i = 0; i < (mSplitShadows ? 3 : 1); ++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(1)))); + + p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(0))); + } + else + { + + bool shadows = mShadows && !renderCompositeMap; + + int layerOffset = 0; + while (layerOffset < (int)mLayerList.size()) + { + int blendmapOffset = (layerOffset == 0) ? 1 : 0; // the first layer of the first pass is the base layer and does not need a blend map + + // Check how many layers we can fit in this pass + int numLayersInThisPass = 0; + int numBlendTextures = 0; + std::vector blendTextures; + int remainingTextureUnits = OGRE_MAX_TEXTURE_LAYERS; + if (shadows) + remainingTextureUnits -= (mSplitShadows ? 3 : 1); + while (remainingTextureUnits && layerOffset + numLayersInThisPass < (int)mLayerList.size()) + { + int layerIndex = numLayersInThisPass + layerOffset; + + int neededTextureUnits=0; + int neededBlendTextures=0; + + if (layerIndex != 0) + { + std::string blendTextureName = mBlendmapList[getBlendmapIndexForLayer(layerIndex)]->getName(); + if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end()) + { + blendTextures.push_back(blendTextureName); + ++neededBlendTextures; + ++neededTextureUnits; // blend texture + } + } + ++neededTextureUnits; // layer texture + if (neededTextureUnits <= remainingTextureUnits) + { + // We can fit another! + remainingTextureUnits -= neededTextureUnits; + numBlendTextures += neededBlendTextures; + ++numLayersInThisPass; + } + else + break; // We're full + } + + + sh::MaterialInstancePass* p = material->createPass (); + + p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); + p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); + if (layerOffset != 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 ("render_composite_map", sh::makeProperty(new sh::BooleanValue(renderCompositeMap))); + p->mShaderProperties.setProperty ("display_composite_map", sh::makeProperty(new sh::BooleanValue(displayCompositeMap))); + + 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 (mLayerList.size() == 1) // special case. if there's only one layer, we don't need blend maps at all + blendmapStart = 0; + else + blendmapStart = getBlendmapIndexForLayer(layerOffset+blendmapOffset); + for (int i = 0; i < numBlendTextures; ++i) + { + sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); + blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(mBlendmapList[blendmapStart+i]->getName()))); + blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + } + + // layer maps + for (int i = 0; i < numLayersInThisPass; ++i) + { + sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); + diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue("textures\\"+mLayerList[layerOffset+i]))); + + if (i+layerOffset > 0) + { + int blendTextureIndex = getBlendmapIndexForLayer(layerOffset+i); + std::string blendTextureComponent = getBlendmapComponentForLayer(layerOffset+i); + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(blendTextureIndex-blendmapStart) + "." + 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 + if (shadows) + { + for (Ogre::uint i = 0; i < (mSplitShadows ? 3 : 1); ++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)))); + + // Make sure the pass index is fed to the permutation handler, because blendmap components may be different + p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(layerOffset))); + + layerOffset += numLayersInThisPass; + } + } + } + return Ogre::MaterialManager::getSingleton().getByName(name.str()); + } + +} diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp new file mode 100644 index 000000000..330ed3d14 --- /dev/null +++ b/components/terrain/material.hpp @@ -0,0 +1,56 @@ +#ifndef COMPONENTS_TERRAIN_MATERIAL_H +#define COMPONENTS_TERRAIN_MATERIAL_H + +#include + +namespace Terrain +{ + + class MaterialGenerator + { + public: + /// @param layerList layer textures + /// @param blendmapList blend textures + /// @param shaders Whether to use shaders. With a shader, blendmap packing can be used (4 channels instead of one), + /// so if this parameter is true, then the supplied blend maps are expected to be packed. + MaterialGenerator (bool shaders); + + void setLayerList (const std::vector& layerList) { mLayerList = layerList; } + bool hasLayers() { return mLayerList.size(); } + void setBlendmapList (const std::vector& blendmapList) { mBlendmapList = blendmapList; } + const std::vector& getBlendmapList() { return mBlendmapList; } + void setCompositeMap (const std::string& name) { mCompositeMap = name; } + + void enableShadows(bool shadows) { mShadows = shadows; } + void enableSplitShadows(bool splitShadows) { mSplitShadows = splitShadows; } + + /// Creates a material suitable for displaying a chunk of terrain using alpha-blending. + /// @param mat Material that will be replaced by the generated material. May be empty as well, in which case + /// a new material is created. + Ogre::MaterialPtr generate (Ogre::MaterialPtr mat); + + /// Creates a material suitable for displaying a chunk of terrain using a ready-made composite map. + /// @param mat Material that will be replaced by the generated material. May be empty as well, in which case + /// a new material is created. + Ogre::MaterialPtr generateForCompositeMap (Ogre::MaterialPtr mat); + + /// Creates a material suitable for rendering composite maps, i.e. for "baking" several layer textures + /// into one. The main difference compared to a normal material is that no shading is applied at this point. + /// @param mat Material that will be replaced by the generated material. May be empty as well, in which case + /// a new material is created. + Ogre::MaterialPtr generateForCompositeMapRTT (Ogre::MaterialPtr mat); + + private: + Ogre::MaterialPtr create (Ogre::MaterialPtr mat, bool renderCompositeMap, bool displayCompositeMap); + + std::vector mLayerList; + std::vector mBlendmapList; + std::string mCompositeMap; + bool mShaders; + bool mShadows; + bool mSplitShadows; + }; + +} + +#endif diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp new file mode 100644 index 000000000..ef2c61013 --- /dev/null +++ b/components/terrain/quadtreenode.cpp @@ -0,0 +1,512 @@ +#include "quadtreenode.hpp" + +#include +#include + +#include "world.hpp" +#include "chunk.hpp" +#include "storage.hpp" + +#include "material.hpp" + +using namespace Terrain; + +namespace +{ + int Log2( int n ) + { + assert(n > 0); + int targetlevel = 0; + while (n >>= 1) ++targetlevel; + return targetlevel; + } + + // Utility functions for neighbour finding algorithm + ChildDirection reflect(ChildDirection dir, Direction dir2) + { + assert(dir != Root); + + const int lookupTable[4][4] = + { + // NW NE SW SE + { SW, SE, NW, NE }, // N + { NE, NW, SE, SW }, // E + { SW, SE, NW, NE }, // S + { NE, NW, SE, SW } // W + }; + return (ChildDirection)lookupTable[dir2][dir]; + } + + bool adjacent(ChildDirection dir, Direction dir2) + { + assert(dir != Root); + const bool lookupTable[4][4] = + { + // NW NE SW SE + { true, true, false, false }, // N + { false, true, false, true }, // E + { false, false, true, true }, // S + { true, false, true, false } // W + }; + return lookupTable[dir2][dir]; + } + + // Algorithm described by Hanan Samet - 'Neighbour Finding in Quadtrees' + // http://www.cs.umd.edu/~hjs/pubs/SametPRIP81.pdf + QuadTreeNode* searchNeighbourRecursive (QuadTreeNode* currentNode, Direction dir) + { + if (!currentNode->getParent()) + return NULL; // Arrived at root node, the root node does not have neighbours + + QuadTreeNode* nextNode; + if (adjacent(currentNode->getDirection(), dir)) + nextNode = searchNeighbourRecursive(currentNode->getParent(), dir); + else + nextNode = currentNode->getParent(); + + if (nextNode && nextNode->hasChildren()) + return nextNode->getChild(reflect(currentNode->getDirection(), dir)); + else + return NULL; + } + + + // Ogre::AxisAlignedBox::distance is broken in 1.8. + Ogre::Real distance(const Ogre::AxisAlignedBox& box, const Ogre::Vector3& v) + { + + if (box.contains(v)) + return 0; + else + { + Ogre::Vector3 maxDist(0,0,0); + const Ogre::Vector3& minimum = box.getMinimum(); + const Ogre::Vector3& maximum = box.getMaximum(); + + if (v.x < minimum.x) + maxDist.x = minimum.x - v.x; + else if (v.x > maximum.x) + maxDist.x = v.x - maximum.x; + + if (v.y < minimum.y) + maxDist.y = minimum.y - v.y; + else if (v.y > maximum.y) + maxDist.y = v.y - maximum.y; + + if (v.z < minimum.z) + maxDist.z = minimum.z - v.z; + else if (v.z > maximum.z) + maxDist.z = v.z - maximum.z; + + return maxDist.length(); + } + } + + // Create a 2D quad + void makeQuad(Ogre::SceneManager* sceneMgr, float left, float top, float right, float bottom, Ogre::MaterialPtr material) + { + Ogre::ManualObject* manual = sceneMgr->createManualObject(); + + // Use identity view/projection matrices to get a 2d quad + manual->setUseIdentityProjection(true); + manual->setUseIdentityView(true); + + manual->begin(material->getName()); + + float normLeft = left*2-1; + float normTop = top*2-1; + float normRight = right*2-1; + float normBottom = bottom*2-1; + + manual->position(normLeft, normTop, 0.0); + manual->textureCoord(0, 1); + manual->position(normRight, normTop, 0.0); + manual->textureCoord(1, 1); + manual->position(normRight, normBottom, 0.0); + manual->textureCoord(1, 0); + manual->position(normLeft, normBottom, 0.0); + manual->textureCoord(0, 0); + + manual->quad(0,1,2,3); + + manual->end(); + + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + manual->setBoundingBox(aabInf); + + sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual); + } +} + +QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) + : mSize(size) + , mCenter(center) + , mParent(parent) + , mDirection(dir) + , mIsDummy(false) + , mSceneNode(NULL) + , mTerrain(terrain) + , mChunk(NULL) + , mMaterialGenerator(NULL) + , mBounds(Ogre::AxisAlignedBox::BOX_NULL) + , mWorldBounds(Ogre::AxisAlignedBox::BOX_NULL) +{ + mBounds.setNull(); + for (int i=0; i<4; ++i) + mChildren[i] = NULL; + for (int i=0; i<4; ++i) + mNeighbours[i] = NULL; + + if (mDirection == Root) + mSceneNode = mTerrain->getRootSceneNode(); + else + mSceneNode = mTerrain->getSceneManager()->createSceneNode(); + Ogre::Vector2 pos (0,0); + if (mParent) + pos = mParent->getCenter(); + pos = mCenter - pos; + mSceneNode->setPosition(Ogre::Vector3(pos.x*8192, pos.y*8192, 0)); + + mLodLevel = Log2(mSize); + + mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); +} + +void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er) +{ + mChildren[id] = new QuadTreeNode(mTerrain, id, size, center, this); +} + +QuadTreeNode::~QuadTreeNode() +{ + for (int i=0; i<4; ++i) + delete mChildren[i]; + delete mChunk; + delete mMaterialGenerator; +} + +QuadTreeNode* QuadTreeNode::getNeighbour(Direction dir) +{ + return mNeighbours[static_cast(dir)]; +} + +void QuadTreeNode::initNeighbours() +{ + for (int i=0; i<4; ++i) + mNeighbours[i] = searchNeighbourRecursive(this, (Direction)i); + + if (hasChildren()) + for (int i=0; i<4; ++i) + mChildren[i]->initNeighbours(); +} + +void QuadTreeNode::initAabb() +{ + if (hasChildren()) + { + for (int i=0; i<4; ++i) + { + mChildren[i]->initAabb(); + mBounds.merge(mChildren[i]->getBoundingBox()); + } + mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*8192, -mSize/2*8192, mBounds.getMinimum().z), + Ogre::Vector3(mSize/2*8192, mSize/2*8192, mBounds.getMaximum().z)); + } + mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0), + mBounds.getMaximum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0)); +} + +void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box) +{ + mBounds = box; +} + +const Ogre::AxisAlignedBox& QuadTreeNode::getBoundingBox() +{ + return mBounds; +} + +void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loadingListener) +{ + const Ogre::AxisAlignedBox& bounds = getBoundingBox(); + if (bounds.isNull()) + return; + + float dist = distance(mWorldBounds, cameraPos); + + bool distantLand = mTerrain->getDistantLandEnabled(); + + // Make sure our scene node is attached + if (!mSceneNode->isInSceneGraph()) + { + mParent->getSceneNode()->addChild(mSceneNode); + } + + /// \todo implement error metrics or some other means of not using arbitrary values + /// (general quality needs to be user configurable as well) + size_t wantedLod = 0; + if (dist > 8192*1) + wantedLod = 1; + if (dist > 8192*2) + wantedLod = 2; + if (dist > 8192*5) + wantedLod = 3; + if (dist > 8192*12) + wantedLod = 4; + if (dist > 8192*32) + wantedLod = 5; + if (dist > 8192*64) + wantedLod = 6; + + bool hadChunk = hasChunk(); + + if (loadingListener) + loadingListener->indicateProgress(); + + if (!distantLand && dist > 8192*2) + { + if (mIsActive) + { + destroyChunks(true); + mIsActive = false; + } + return; + } + + mIsActive = true; + + if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod) + { + // Wanted LOD is small enough to render this node in one chunk + if (!mChunk) + { + mChunk = new Chunk(this, mLodLevel); + mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); + mChunk->setCastShadows(true); + mSceneNode->attachObject(mChunk); + + mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); + mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); + + if (mSize == 1) + { + ensureLayerInfo(); + mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + } + else + { + ensureCompositeMap(); + mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); + mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + } + } + + // Additional (index buffer) LOD is currently disabled. + // This is due to a problem with the LOD selection when a node splits. + // After splitting, the distance is measured from the children's bounding boxes, which are possibly + // further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD + // than the original node. + // In short, we'd sometimes get a switch to a lesser detail when actually moving closer. + // This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour + // node hasn't split yet, and has a higher LOD than our node's child: + // ----- ----- ------------ + // | LOD | LOD | | + // | 1 | 1 | | + // |-----|-----| LOD 0 | + // | LOD | LOD | | + // | 0 | 0 | | + // ----- ----- ------------ + // To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're + // doing here. + // But this "solution" does increase triangle overhead, so eventually we need to find a more clever way. + //mChunk->setAdditionalLod(wantedLod - mLodLevel); + + mChunk->setVisible(true); + + if (!hadChunk && hasChildren()) + { + // Make sure child scene nodes are detached + mSceneNode->removeAllChildren(); + + // If distant land is enabled, keep the chunks around in case we need them again, + // otherwise, prefer low memory usage + if (!distantLand) + for (int i=0; i<4; ++i) + mChildren[i]->destroyChunks(true); + } + } + else + { + // Wanted LOD is too detailed to be rendered in one chunk, + // so split it up by delegating to child nodes + if (hadChunk) + { + // If distant land is enabled, keep the chunks around in case we need them again, + // otherwise, prefer low memory usage + if (!distantLand) + destroyChunks(false); + else if (mChunk) + mChunk->setVisible(false); + } + assert(hasChildren() && "Leaf node's LOD needs to be 0"); + for (int i=0; i<4; ++i) + mChildren[i]->update(cameraPos, loadingListener); + } +} + +void QuadTreeNode::destroyChunks(bool children) +{ + if (mChunk) + { + Ogre::MaterialManager::getSingleton().remove(mChunk->getMaterial()->getName()); + mSceneNode->detachObject(mChunk); + + delete mChunk; + mChunk = NULL; + // destroy blendmaps + if (mMaterialGenerator) + { + const std::vector& list = mMaterialGenerator->getBlendmapList(); + for (std::vector::const_iterator it = list.begin(); it != list.end(); ++it) + Ogre::TextureManager::getSingleton().remove((*it)->getName()); + mMaterialGenerator->setBlendmapList(std::vector()); + mMaterialGenerator->setLayerList(std::vector()); + mMaterialGenerator->setCompositeMap(""); + } + + if (!mCompositeMap.isNull()) + { + Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName()); + mCompositeMap.setNull(); + } + } + else if (children && hasChildren()) + for (int i=0; i<4; ++i) + mChildren[i]->destroyChunks(true); +} + +void QuadTreeNode::updateIndexBuffers() +{ + if (hasChunk()) + mChunk->updateIndexBuffer(); + else if (hasChildren()) + { + for (int i=0; i<4; ++i) + mChildren[i]->updateIndexBuffers(); + } +} + +bool QuadTreeNode::hasChunk() +{ + return mSceneNode->isInSceneGraph() && mChunk && mChunk->getVisible(); +} + +size_t QuadTreeNode::getActualLodLevel() +{ + assert(hasChunk() && "Can't get actual LOD level if this node has no render chunk"); + return mLodLevel + mChunk->getAdditionalLod(); +} + +void QuadTreeNode::ensureLayerInfo() +{ + if (mMaterialGenerator->hasLayers()) + return; + + std::vector blendmaps; + std::vector layerList; + mTerrain->getStorage()->getBlendmaps(mSize, mCenter, mTerrain->getShadersEnabled(), blendmaps, layerList); + + mMaterialGenerator->setLayerList(layerList); + mMaterialGenerator->setBlendmapList(blendmaps); +} + +void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) +{ + Ogre::SceneManager* sceneMgr = mTerrain->getCompositeMapSceneManager(); + + if (mIsDummy) + { + // TODO - why is this completely black? + // TODO - store this default material somewhere instead of creating one for each empty cell + MaterialGenerator matGen(mTerrain->getShadersEnabled()); + std::vector layer; + layer.push_back("_land_default.dds"); + matGen.setLayerList(layer); + makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT(Ogre::MaterialPtr())); + return; + } + if (mSize > 1) + { + assert(hasChildren()); + + // 0,0 -------- 1,0 + // | | | + // |-----|------| + // | | | + // 0,1 -------- 1,1 + + float halfW = area.width()/2.f; + float halfH = area.height()/2.f; + mChildren[NW]->prepareForCompositeMap(Ogre::TRect(area.left, area.top, area.right-halfW, area.bottom-halfH)); + mChildren[NE]->prepareForCompositeMap(Ogre::TRect(area.left+halfW, area.top, area.right, area.bottom-halfH)); + mChildren[SW]->prepareForCompositeMap(Ogre::TRect(area.left, area.top+halfH, area.right-halfW, area.bottom)); + mChildren[SE]->prepareForCompositeMap(Ogre::TRect(area.left+halfW, area.top+halfH, area.right, area.bottom)); + } + else + { + ensureLayerInfo(); + + Ogre::MaterialPtr material = mMaterialGenerator->generateForCompositeMapRTT(Ogre::MaterialPtr()); + makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, material); + } +} + +void QuadTreeNode::ensureCompositeMap() +{ + if (!mCompositeMap.isNull()) + return; + + static int i=0; + std::stringstream name; + name << "terrain/comp" << i++; + + const int size = 128; + mCompositeMap = Ogre::TextureManager::getSingleton().createManual( + name.str(), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, size, size, Ogre::MIP_DEFAULT, Ogre::PF_A8B8G8R8); + + // Create quads for each cell + prepareForCompositeMap(Ogre::TRect(0,0,1,1)); + + mTerrain->renderCompositeMap(mCompositeMap); + + mTerrain->clearCompositeMapSceneManager(); + +} + +void QuadTreeNode::applyMaterials() +{ + if (mChunk) + { + mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); + mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); + if (mSize <= 1) + mChunk->setMaterial(mMaterialGenerator->generate(Ogre::MaterialPtr())); + else + mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(Ogre::MaterialPtr())); + } + if (hasChildren()) + for (int i=0; i<4; ++i) + mChildren[i]->applyMaterials(); +} + +void QuadTreeNode::setVisible(bool visible) +{ + if (!visible && mChunk) + mChunk->setVisible(false); + + if (hasChildren()) + for (int i=0; i<4; ++i) + mChildren[i]->setVisible(visible); +} diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp new file mode 100644 index 000000000..9f6fb5d24 --- /dev/null +++ b/components/terrain/quadtreenode.hpp @@ -0,0 +1,161 @@ +#ifndef COMPONENTS_TERRAIN_QUADTREENODE_H +#define COMPONENTS_TERRAIN_QUADTREENODE_H + +#include +#include +#include + +#include + +namespace Ogre +{ + class Rectangle2D; +} + +namespace Terrain +{ + class World; + class Chunk; + class MaterialGenerator; + + enum Direction + { + North = 0, + East = 1, + South = 2, + West = 3 + }; + + enum ChildDirection + { + NW = 0, + NE = 1, + SW = 2, + SE = 3, + Root + }; + + /** + * @brief A node in the quad tree for our terrain. Depending on LOD, + * a node can either choose to render itself in one batch (merging its children), + * or delegate the render process to its children, rendering each child in at least one batch. + */ + class QuadTreeNode + { + public: + /// @param terrain + /// @param dir relative to parent, or Root if we are the root node + /// @param size size (in *cell* units!) + /// @param center center (in *cell* units!) + /// @param parent parent node + QuadTreeNode (World* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent); + ~QuadTreeNode(); + + void setVisible(bool visible); + + /// Rebuild all materials + void applyMaterials(); + + /// Initialize neighbours - do this after the quadtree is built + void initNeighbours(); + /// Initialize bounding boxes of non-leafs by merging children bounding boxes. + /// Do this after the quadtree is built - note that leaf bounding boxes + /// need to be set first via setBoundingBox! + void initAabb(); + + /// @note takes ownership of \a child + void createChild (ChildDirection id, float size, const Ogre::Vector2& center); + + /// Mark this node as a dummy node. This can happen if the terrain size isn't a power of two. + /// For the QuadTree to work, we need to round the size up to a power of two, which means we'll + /// end up with empty nodes that don't actually render anything. + void markAsDummy() { mIsDummy = true; } + bool isDummy() { return mIsDummy; } + + QuadTreeNode* getParent() { return mParent; } + + Ogre::SceneNode* getSceneNode() { return mSceneNode; } + + int getSize() { return mSize; } + Ogre::Vector2 getCenter() { return mCenter; } + + bool hasChildren() { return mChildren[0] != 0; } + QuadTreeNode* getChild(ChildDirection dir) { return mChildren[dir]; } + + /// Get neighbour node in this direction + QuadTreeNode* getNeighbour (Direction dir); + + /// Returns our direction relative to the parent node, or Root if we are the root node. + ChildDirection getDirection() { return mDirection; } + + /// Set bounding box in local coordinates. Should be done at load time for leaf nodes. + /// Other nodes can merge AABB of child nodes. + void setBoundingBox (const Ogre::AxisAlignedBox& box); + + /// Get bounding box in local coordinates + const Ogre::AxisAlignedBox& getBoundingBox(); + + World* getTerrain() { return mTerrain; } + + /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. + void update (const Ogre::Vector3& cameraPos, Loading::Listener* loadingListener); + + /// Adjust index buffers of chunks to stitch together chunks of different LOD, so that cracks are avoided. + /// Call after QuadTreeNode::update! + void updateIndexBuffers(); + + /// Destroy chunks rendered by this node *and* its children (if param is true) + void destroyChunks(bool children); + + /// Get the effective LOD level if this node was rendered in one chunk + /// with ESM::Land::LAND_SIZE^2 vertices + size_t getNativeLodLevel() { return mLodLevel; } + + /// Get the effective current LOD level used by the chunk rendering this node + size_t getActualLodLevel(); + + /// Is this node currently configured to render itself? + bool hasChunk(); + + /// Add a textured quad to a specific 2d area in the composite map scenemanager. + /// Only nodes with size <= 1 can be rendered with alpha blending, so larger nodes will simply + /// call this method on their children. + /// @param area area in image space to put the quad + /// @param quads collect quads here so they can be deleted later + void prepareForCompositeMap(Ogre::TRect area); + + private: + // Stored here for convenience in case we need layer list again + MaterialGenerator* mMaterialGenerator; + + /// Is this node (or any of its child nodes) currently configured to render itself? + /// (only relevant when distant land is disabled, otherwise whole terrain is always rendered) + bool mIsActive; + + bool mIsDummy; + float mSize; + size_t mLodLevel; // LOD if we were to render this node in one chunk + Ogre::AxisAlignedBox mBounds; + Ogre::AxisAlignedBox mWorldBounds; + ChildDirection mDirection; + Ogre::Vector2 mCenter; + + Ogre::SceneNode* mSceneNode; + + QuadTreeNode* mParent; + QuadTreeNode* mChildren[4]; + QuadTreeNode* mNeighbours[4]; + + Chunk* mChunk; + + World* mTerrain; + + Ogre::TexturePtr mCompositeMap; + + void ensureLayerInfo(); + void ensureCompositeMap(); + }; + +} + +#endif diff --git a/components/terrain/storage.cpp b/components/terrain/storage.cpp new file mode 100644 index 000000000..f00677e97 --- /dev/null +++ b/components/terrain/storage.cpp @@ -0,0 +1,470 @@ +#include "storage.hpp" + +#include +#include +#include +#include +#include + +#include + +namespace Terrain +{ + + struct VertexElement + { + Ogre::Vector3 pos; + Ogre::Vector3 normal; + Ogre::ColourValue colour; + }; + + bool Storage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max) + { + assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); + + /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead + + Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); + + assert(origin.x == (int) origin.x); + assert(origin.y == (int) origin.y); + + int cellX = origin.x; + int cellY = origin.y; + + const ESM::Land* land = getLand(cellX, cellY); + if (!land) + return false; + + min = std::numeric_limits().max(); + max = -std::numeric_limits().max(); + for (int row=0; rowmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; + if (h > max) + max = h; + if (h < min) + min = h; + } + } + return true; + } + + void Storage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row) + { + while (col >= ESM::Land::LAND_SIZE-1) + { + ++cellY; + col -= ESM::Land::LAND_SIZE-1; + } + while (row >= ESM::Land::LAND_SIZE-1) + { + ++cellX; + row -= ESM::Land::LAND_SIZE-1; + } + while (col < 0) + { + --cellY; + col += ESM::Land::LAND_SIZE-1; + } + while (row < 0) + { + --cellX; + row += ESM::Land::LAND_SIZE-1; + } + ESM::Land* land = getLand(cellX, cellY); + if (land && land->mHasData) + { + normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalise(); + } + else + normal = Ogre::Vector3(0,0,1); + } + + void Storage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row) + { + Ogre::Vector3 n1,n2,n3,n4; + fixNormal(n1, cellX, cellY, col+1, row); + fixNormal(n2, cellX, cellY, col-1, row); + fixNormal(n3, cellX, cellY, col, row+1); + fixNormal(n4, cellX, cellY, col, row-1); + normal = (n1+n2+n3+n4); + normal.normalise(); + } + + void Storage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row) + { + if (col == ESM::Land::LAND_SIZE-1) + { + ++cellY; + col = 0; + } + if (row == ESM::Land::LAND_SIZE-1) + { + ++cellX; + row = 0; + } + ESM::Land* land = getLand(cellX, cellY); + if (land && land->mLandData->mUsingColours) + { + color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + } + else + { + color.r = 1; + color.g = 1; + color.b = 1; + } + + } + + void Storage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + Ogre::HardwareVertexBufferSharedPtr vertexBuffer, + Ogre::HardwareVertexBufferSharedPtr normalBuffer, + Ogre::HardwareVertexBufferSharedPtr colourBuffer) + { + // LOD level n means every 2^n-th vertex is kept + size_t increment = 1 << lodLevel; + + Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); + assert(origin.x == (int) origin.x); + assert(origin.y == (int) origin.y); + + int startX = origin.x; + int startY = origin.y; + + size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; + + std::vector colors; + colors.resize(numVerts*numVerts*4); + std::vector positions; + positions.resize(numVerts*numVerts*3); + std::vector normals; + normals.resize(numVerts*numVerts*3); + + Ogre::Vector3 normal; + Ogre::ColourValue color; + + float vertY; + float vertX; + + float vertY_ = 0; // of current cell corner + for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) + { + float vertX_ = 0; // of current cell corner + for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) + { + ESM::Land* land = getLand(cellX, cellY); + if (land && !land->mHasData) + land = NULL; + bool hasColors = land && land->mLandData->mUsingColours; + + int rowStart = 0; + int colStart = 0; + // Skip the first row / column unless we're at a chunk edge, + // since this row / column is already contained in a previous cell + if (colStart == 0 && vertY_ != 0) + colStart += increment; + if (rowStart == 0 && vertX_ != 0) + rowStart += increment; + + vertY = vertY_; + for (int col=colStart; colmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; + else + positions[vertX*numVerts*3 + vertY*3 + 2] = -2048; + + if (land) + { + normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalise(); + } + else + normal = Ogre::Vector3(0,0,1); + + // Normals apparently don't connect seamlessly between cells + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixNormal(normal, cellX, cellY, col, row); + + // some corner normals appear to be complete garbage (z < 0) + if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) + averageNormal(normal, cellX, cellY, col, row); + + assert(normal.z > 0); + + normals[vertX*numVerts*3 + vertY*3] = normal.x; + normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y; + normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z; + + if (hasColors) + { + color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + } + else + { + color.r = 1; + color.g = 1; + color.b = 1; + } + + // Unlike normals, colors mostly connect seamlessly between cells, but not always... + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixColour(color, cellX, cellY, col, row); + + color.a = 1; + Ogre::uint32 rsColor; + Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); + memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); + + ++vertX; + } + ++vertY; + } + vertX_ = vertX; + } + vertY_ = vertY; + + assert(vertX_ == numVerts); // Ensure we covered whole area + } + assert(vertY_ == numVerts); // Ensure we covered whole area + + vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); + normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); + colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true); + } + + Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, + int x, int y) + { + // For the first/last row/column, we need to get the texture from the neighbour cell + // to get consistent blending at the borders + --x; + if (x < 0) + { + --cellX; + x += ESM::Land::LAND_TEXTURE_SIZE; + } + if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? + { + ++cellY; + y -= ESM::Land::LAND_TEXTURE_SIZE; + } + + assert(xisDataLoaded(ESM::Land::DATA_VTEX)) + land->loadData(ESM::Land::DATA_VTEX); + + int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + if (tex == 0) + return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin + return std::make_pair(tex, land->mPlugin); + } + else + return std::make_pair(0,0); + } + + std::string Storage::getTextureName(UniqueTextureId id) + { + if (id.first == 0) + return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded? + + // NB: All vtex ids are +1 compared to the ltex ids + const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); + + std::string texture = ltex->mTexture; + //TODO this is needed due to MWs messed up texture handling + texture = texture.substr(0, texture.rfind(".")) + ".dds"; + + return texture; + } + + void Storage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, + bool pack, std::vector &blendmaps, std::vector &layerList) + { + // TODO - blending isn't completely right yet; the blending radius appears to be + // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap + // and interpolate the rest of the cell by hand? :/ + + Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f); + int cellX = origin.x; + int cellY = origin.y; + + // Save the used texture indices so we know the total number of textures + // and number of required blend maps + std::set textureIndices; + // Due to the way the blending works, the base layer will always shine through in between + // blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible). + // To get a consistent look, we need to make sure to use the same base layer in all cells. + // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. + textureIndices.insert(std::make_pair(0,0)); + + for (int y=0; y textureIndicesMap; + for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it) + { + int size = textureIndicesMap.size(); + textureIndicesMap[*it] = size; + layerList.push_back(getTextureName(*it)); + } + + int numTextures = textureIndices.size(); + // numTextures-1 since the base layer doesn't need blending + int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1); + + int channels = pack ? 4 : 1; + + // Second iteration - create and fill in the blend maps + const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1; + std::vector data; + data.resize(blendmapSize * blendmapSize * channels, 0); + + for (int i=0; isecond; + int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1); + int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; + + if (blendIndex == i) + data[y*blendmapSize*channels + x*channels + channel] = 255; + else + data[y*blendmapSize*channels + x*channels + channel] = 0; + } + } + + // All done, upload to GPU + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); + map->loadRawData(stream, blendmapSize, blendmapSize, format); + blendmaps.push_back(map); + } + } + + float Storage::getHeightAt(const Ogre::Vector3 &worldPos) + { + int cellX = std::floor(worldPos.x / 8192.f); + int cellY = std::floor(worldPos.y / 8192.f); + + ESM::Land* land = getLand(cellX, cellY); + if (!land) + return -2048; + + // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition + + // Normalized position in the cell + float nX = (worldPos.x - (cellX * 8192))/8192.f; + float nY = (worldPos.y - (cellY * 8192))/8192.f; + + // get left / bottom points (rounded down) + float factor = ESM::Land::LAND_SIZE - 1.0f; + float invFactor = 1.0f / factor; + + int startX = static_cast(nX * factor); + int startY = static_cast(nY * factor); + int endX = startX + 1; + int endY = startY + 1; + + assert(endX < ESM::Land::LAND_SIZE); + assert(endY < ESM::Land::LAND_SIZE); + + // now get points in terrain space (effectively rounding them to boundaries) + float startXTS = startX * invFactor; + float startYTS = startY * invFactor; + float endXTS = endX * invFactor; + float endYTS = endY * invFactor; + + // get parametric from start coord to next point + float xParam = (nX - startXTS) * factor; + float yParam = (nY - startYTS) * factor; + + /* For even / odd tri strip rows, triangles are this shape: + even odd + 3---2 3---2 + | / | | \ | + 0---1 0---1 + */ + + // Build all 4 positions in normalized cell space, using point-sampled height + Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); + Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); + Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); + Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); + // define this plane in terrain space + Ogre::Plane plane; + // (At the moment, all rows have the same triangle alignment) + if (true) + { + // odd row + bool secondTri = ((1.0 - yParam) > xParam); + if (secondTri) + plane.redefine(v0, v1, v3); + else + plane.redefine(v1, v2, v3); + } + else + { + // even row + bool secondTri = (yParam > xParam); + if (secondTri) + plane.redefine(v0, v2, v3); + else + plane.redefine(v0, v1, v2); + } + + // Solve plane equation for z + return (-plane.normal.x * nX + -plane.normal.y * nY + - plane.d) / plane.normal.z * 8192; + + } + + float Storage::getVertexHeight(const ESM::Land *land, int x, int y) + { + assert(x < ESM::Land::LAND_SIZE); + assert(y < ESM::Land::LAND_SIZE); + return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x]; + } + + +} diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp new file mode 100644 index 000000000..b82f6bbb6 --- /dev/null +++ b/components/terrain/storage.hpp @@ -0,0 +1,84 @@ +#ifndef COMPONENTS_TERRAIN_STORAGE_H +#define COMPONENTS_TERRAIN_STORAGE_H + +#include +#include + +#include + +#include + +namespace Terrain +{ + + /// We keep storage of terrain data abstract here since we need different implementations for game and editor + class Storage + { + public: + virtual ~Storage() {} + private: + virtual ESM::Land* getLand (int cellX, int cellY) = 0; + virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; + + public: + /// Get bounds of the whole terrain in cell units + virtual Ogre::AxisAlignedBox getBounds() = 0; + + /// Get the minimum and maximum heights of a terrain chunk. + /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. + /// Larger chunks can simply merge AABB of children. + /// @param size size of the chunk in cell units + /// @param center center of the chunk in cell units + /// @param min min height will be stored here + /// @param max max height will be stored here + /// @return true if there was data available for this terrain chunk + bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); + + /// Fill vertex buffers for a terrain chunk. + /// @param lodLevel LOD level, 0 = most detailed + /// @param size size of the terrain chunk in cell units + /// @param center center of the chunk in cell units + /// @param vertexBuffer buffer to write vertices + /// @param normalBuffer buffer to write vertex normals + /// @param colourBuffer buffer to write vertex colours + void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + Ogre::HardwareVertexBufferSharedPtr vertexBuffer, + Ogre::HardwareVertexBufferSharedPtr normalBuffer, + Ogre::HardwareVertexBufferSharedPtr colourBuffer); + + /// Create textures holding layer blend values for a terrain chunk. + /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might + /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @param chunkSize size of the terrain chunk in cell units + /// @param chunkCenter center of the chunk in cell units + /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - + /// otherwise, each texture contains blend values for one layer only. Shader-based rendering + /// can utilize packing, FFP can't. + /// @param blendmaps created blendmaps will be written here + /// @param layerList names of the layer textures used will be written here + void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, + std::vector& blendmaps, + std::vector& layerList); + + float getHeightAt (const Ogre::Vector3& worldPos); + + private: + void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row); + void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + + float getVertexHeight (const ESM::Land* land, int x, int y); + + // Since plugins can define new texture palettes, we need to know the plugin index too + // in order to retrieve the correct texture name. + // pair + typedef std::pair UniqueTextureId; + + UniqueTextureId getVtexIndexAt(int cellX, int cellY, + int x, int y); + std::string getTextureName (UniqueTextureId id); + }; + +} + +#endif diff --git a/components/terrain/tests/.gitignore b/components/terrain/tests/.gitignore deleted file mode 100644 index 814490404..000000000 --- a/components/terrain/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/components/terrain/tests/Makefile b/components/terrain/tests/Makefile deleted file mode 100644 index c886f392f..000000000 --- a/components/terrain/tests/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -GCC=g++ - -all: triangle_test esm_test - -LIB_INC=-I../../../libs/ - -triangle_test: triangle_test.cpp - $(GCC) $^ -o $@ - -esm_test: esm_test.cpp - $(GCC) $^ -o $@ $(LIB_INC) - -clean: - rm *_test diff --git a/components/terrain/tests/esm_test.cpp b/components/terrain/tests/esm_test.cpp deleted file mode 100644 index 509aa8aa9..000000000 --- a/components/terrain/tests/esm_test.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -using namespace std; - -#include "../esm_land_factory.hpp" - -int main() -{ - cout << "under development\n"; - return 0; -} diff --git a/components/terrain/tests/output/esm_test.out b/components/terrain/tests/output/esm_test.out deleted file mode 100644 index c6fec4b4d..000000000 --- a/components/terrain/tests/output/esm_test.out +++ /dev/null @@ -1 +0,0 @@ -under development diff --git a/components/terrain/tests/output/triangle_test.out b/components/terrain/tests/output/triangle_test.out deleted file mode 100644 index 001215043..000000000 --- a/components/terrain/tests/output/triangle_test.out +++ /dev/null @@ -1,55 +0,0 @@ -Cell types: -\ / \ / -/ \ / \ -\ / \ / -/ \ / \ - -Full index list: -0 -6 -5 -0 -1 -6 -1 -2 -6 -6 -2 -7 -2 -8 -7 -2 -3 -8 -3 -4 -8 -8 -4 -9 -5 -6 -10 -10 -6 -11 -6 -12 -11 -6 -7 -12 -7 -8 -12 -12 -8 -13 -8 -14 -13 -8 -9 -14 diff --git a/components/terrain/tests/test.sh b/components/terrain/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/components/terrain/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/components/terrain/tests/triangle_test.cpp b/components/terrain/tests/triangle_test.cpp deleted file mode 100644 index 464bc8709..000000000 --- a/components/terrain/tests/triangle_test.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -using namespace std; - -#include "../triangulator.hpp" - -const int X = 4; -const int Y = 4; - -typedef Terrain::Triangulator Triangles4x4; - -int main() -{ - Triangles4x4 t; - - cout << "Cell types:\n"; - for(int y=0;y= 0 && trinum < TriNum); - trinum *= 3; - - p1 = array[trinum++]; - p2 = array[trinum++]; - p3 = array[trinum]; - } - - /* - Get height interpolation weights for a given grid square. The - input is the grid square number (x,y) and the relative position - within that square (xrel,yrel = [0.0..1.0].) The weights are - returned as three vertex index + weight factor pairs. - - A more user-friendly version for HeightMap structs is given - below. - * / - void getWeights(int x, int y, float xrel, float yrel, - Index &p1, float w1, - Index &p2, float w2, - Index &p3, float w3) - { - // Find cell index - int index = y*SizeX + x; - - // First triangle in cell - index *= 2; - - // The rest depends on how the cell is triangulated - if(cellType(x,y)) - { - } - else - { - // Cell is divided as \ from 0,0 to 1,1 - if(xrel < yrel) - { - // Bottom left triangle. - - // Order is (0,0),(1,1),(0,1). - getTriangle(index, p1,p2,p3); - - - } - else - { - // Top right triangle - - // Order is (0,0),(1,0),(1,1). - getTriangle(index+1, p1,p2,p3); - } - } - } - - */ diff --git a/components/terrain/triangulator.hpp b/components/terrain/triangulator.hpp deleted file mode 100644 index c5c0e699b..000000000 --- a/components/terrain/triangulator.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef TERRAIN_TRIANGULATOR_H -#define TERRAIN_TRIANGULATOR_H - -/* - The triangulator is a simple math helper class, used for dividing a - regular square grid into alternating set of triangles. It divides a - grid like this: - - +----+----+ - | | | - | | | - +----+----+ - | | | - | | | - +----+----+ - - into this: - - +----+----+ - | \ 2|3 / | - |1 \ | / 4| - +----+----+ - |5 / | \ 8| - | / 6|7 \ | - +----+----+ - - Since the triangulation information is typically the same for all - terrains of the same size, once instance can usually be shared. -*/ - -#include - -namespace Terrain -{ - // Index number type, number of grid cells (not vertices) in X and Y - // directions. - template - class Triangulator - { - // Number of triangles - static const int TriNum = SizeX * SizeY * 2; - - // 3 indices per triangle - Index array[TriNum * 3]; - - public: - // Get raw triangle data pointer. Typically used for creating - // meshes. - const Index *getData() { return array; } - - // Return whether a given cell is divided as / (true) or \ - // (false). - static bool cellType(int x, int y) - { - assert(x >= 0 && x < SizeX); - assert(y >= 0 && y < SizeY); - - bool even = (x & 1) == 1; - if((y & 1) == 1) even = !even; - return even; - } - - // Constructor sets up the index buffer - Triangulator() - { - int index = 0; - for ( int y = 0; y < SizeX; y++ ) - for ( int x = 0; x < SizeY; x++ ) - { - // Get vertex indices - Index line1 = y*(SizeX+1) + x; - Index line2 = line1 + SizeX+1; - - if(cellType(x,y)) - { - // Top left - array[index++] = line1; - array[index++] = line1 + 1; - array[index++] = line2; - - // Bottom right - array[index++] = line2; - array[index++] = line1 + 1; - array[index++] = line2 + 1; - } - else - { - // Bottom left - array[index++] = line1; - array[index++] = line2 + 1; - array[index++] = line2; - - // Top right - array[index++] = line1; - array[index++] = line1 + 1; - array[index++] = line2 + 1; - } - } - assert(index == TriNum*3); - } - }; -} // Namespace - -#endif diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp new file mode 100644 index 000000000..711ebbc8f --- /dev/null +++ b/components/terrain/world.cpp @@ -0,0 +1,410 @@ +#include "world.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +#include "storage.hpp" +#include "quadtreenode.hpp" + +namespace +{ + + bool isPowerOfTwo(int x) + { + return ( (x > 0) && ((x & (x - 1)) == 0) ); + } + + int nextPowerOfTwo (int v) + { + if (isPowerOfTwo(v)) return v; + int depth=0; + while(v) + { + v >>= 1; + depth++; + } + return 1 << depth; + } + + Terrain::QuadTreeNode* findNode (const Ogre::Vector2& center, Terrain::QuadTreeNode* node) + { + if (center == node->getCenter()) + return node; + + if (center.x > node->getCenter().x && center.y > node->getCenter().y) + return findNode(center, node->getChild(Terrain::NE)); + else if (center.x > node->getCenter().x && center.y < node->getCenter().y) + return findNode(center, node->getChild(Terrain::SE)); + else if (center.x < node->getCenter().x && center.y > node->getCenter().y) + return findNode(center, node->getChild(Terrain::NW)); + else //if (center.x < node->getCenter().x && center.y < node->getCenter().y) + return findNode(center, node->getChild(Terrain::SW)); + } + +} + +namespace Terrain +{ + + World::World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, + Storage* storage, int visibilityFlags, bool distantLand, bool shaders) + : mStorage(storage) + , mMinBatchSize(1) + , mMaxBatchSize(64) + , mSceneMgr(sceneMgr) + , mVisibilityFlags(visibilityFlags) + , mDistantLand(distantLand) + , mShaders(shaders) + , mVisible(true) + , mLoadingListener(loadingListener) + { + loadingListener->setLabel("Creating terrain"); + loadingListener->indicateProgress(); + + mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + + Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a"); + mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual( + "terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, 128, 128, 0, Ogre::PF_A8B8G8R8, Ogre::TU_RENDERTARGET); + mCompositeMapRenderTarget = mCompositeMapRenderTexture->getBuffer()->getRenderTarget(); + mCompositeMapRenderTarget->setAutoUpdated(false); + mCompositeMapRenderTarget->addViewport(compositeMapCam); + + mBounds = storage->getBounds(); + + int origSizeX = mBounds.getSize().x; + int origSizeY = mBounds.getSize().y; + + // Dividing a quad tree only works well for powers of two, so round up to the nearest one + int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); + + // Adjust the center according to the new size + Ogre::Vector3 center = mBounds.getCenter() + Ogre::Vector3((size-origSizeX)/2.f, (size-origSizeY)/2.f, 0); + + mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + + mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL); + buildQuadTree(mRootNode); + loadingListener->indicateProgress(); + mRootNode->initAabb(); + loadingListener->indicateProgress(); + mRootNode->initNeighbours(); + loadingListener->indicateProgress(); + } + + World::~World() + { + delete mRootNode; + delete mStorage; + } + + void World::buildQuadTree(QuadTreeNode *node) + { + float halfSize = node->getSize()/2.f; + + if (node->getSize() <= mMinBatchSize) + { + // We arrived at a leaf + float minZ,maxZ; + Ogre::Vector2 center = node->getCenter(); + if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) + node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*8192, -halfSize*8192, minZ), + Ogre::Vector3(halfSize*8192, halfSize*8192, maxZ))); + else + node->markAsDummy(); // no data available for this node, skip it + return; + } + + if (node->getCenter().x - halfSize > mBounds.getMaximum().x + || node->getCenter().x + halfSize < mBounds.getMinimum().x + || node->getCenter().y - halfSize > mBounds.getMaximum().y + || node->getCenter().y + halfSize < mBounds.getMinimum().y ) + // Out of bounds of the actual terrain - this will happen because + // we rounded the size up to the next power of two + { + node->markAsDummy(); + return; + } + + // Not a leaf, create its children + node->createChild(SW, halfSize, node->getCenter() - halfSize/2.f); + node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f)); + node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f)); + node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f); + buildQuadTree(node->getChild(SW)); + buildQuadTree(node->getChild(SE)); + buildQuadTree(node->getChild(NW)); + buildQuadTree(node->getChild(NE)); + + // if all children are dummy, we are also dummy + for (int i=0; i<4; ++i) + { + if (!node->getChild((ChildDirection)i)->isDummy()) + return; + } + node->markAsDummy(); + } + + void World::update(const Ogre::Vector3& cameraPos) + { + if (!mVisible) + return; + mRootNode->update(cameraPos, mLoadingListener); + mRootNode->updateIndexBuffers(); + } + + Ogre::AxisAlignedBox World::getWorldBoundingBox (const Ogre::Vector2& center) + { + if (center.x > mBounds.getMaximum().x + || center.x < mBounds.getMinimum().x + || center.y > mBounds.getMaximum().y + || center.y < mBounds.getMinimum().y) + return Ogre::AxisAlignedBox::BOX_NULL; + QuadTreeNode* node = findNode(center, mRootNode); + Ogre::AxisAlignedBox box = node->getBoundingBox(); + box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * 8192, + box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * 8192); + return box; + } + + Ogre::HardwareVertexBufferSharedPtr World::getVertexBuffer(int numVertsOneSide) + { + if (mUvBufferMap.find(numVertsOneSide) != mUvBufferMap.end()) + { + return mUvBufferMap[numVertsOneSide]; + } + + int vertexCount = numVertsOneSide * numVertsOneSide; + + std::vector uvs; + uvs.reserve(vertexCount*2); + + for (int col = 0; col < numVertsOneSide; ++col) + { + for (int row = 0; row < numVertsOneSide; ++row) + { + uvs.push_back(col / static_cast(numVertsOneSide-1)); // U + uvs.push_back(row / static_cast(numVertsOneSide-1)); // V + } + } + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareVertexBufferSharedPtr buffer = mgr->createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), + vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + + buffer->writeData(0, buffer->getSizeInBytes(), &uvs[0], true); + + mUvBufferMap[numVertsOneSide] = buffer; + return buffer; + } + + Ogre::HardwareIndexBufferSharedPtr World::getIndexBuffer(int flags, size_t& numIndices) + { + if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) + { + numIndices = mIndexBufferMap[flags]->getNumIndexes(); + return mIndexBufferMap[flags]; + } + + // LOD level n means every 2^n-th vertex is kept + size_t lodLevel = (flags >> (4*4)); + + size_t lodDeltas[4]; + for (int i=0; i<4; ++i) + lodDeltas[i] = (flags >> (4*i)) & (0xf); + + bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); + + size_t increment = 1 << lodLevel; + assert((int)increment < ESM::Land::LAND_SIZE); + std::vector indices; + indices.reserve((ESM::Land::LAND_SIZE-1)*(ESM::Land::LAND_SIZE-1)*2*3 / increment); + + size_t rowStart = 0, colStart = 0, rowEnd = ESM::Land::LAND_SIZE-1, colEnd = ESM::Land::LAND_SIZE-1; + // If any edge needs stitching we'll skip all edges at this point, + // mainly because stitching one edge would have an effect on corners and on the adjacent edges + if (anyDeltas) + { + colStart += increment; + colEnd -= increment; + rowEnd -= increment; + rowStart += increment; + } + for (size_t row = rowStart; row < rowEnd; row += increment) + { + for (size_t col = colStart; col < colEnd; col += increment) + { + indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment); + indices.push_back(ESM::Land::LAND_SIZE*col+row+increment); + + indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row); + indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment); + } + } + + size_t innerStep = increment; + if (anyDeltas) + { + // Now configure LOD transitions at the edges - this is pretty tedious, + // and some very long and boring code, but it works great + + // South + size_t row = 0; + size_t outerStep = 1 << (lodDeltas[South] + lodLevel); + for (size_t col = 0; col < ESM::Land::LAND_SIZE-1; col += outerStep) + { + indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); + // Make sure not to touch the right edge + if (col+outerStep == ESM::Land::LAND_SIZE-1) + indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep-innerStep)+row+innerStep); + else + indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row+innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep) + continue; + indices.push_back(ESM::Land::LAND_SIZE*(col)+row); + indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row+innerStep); + indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row+innerStep); + } + } + + // North + row = ESM::Land::LAND_SIZE-1; + outerStep = 1 << (lodDeltas[North] + lodLevel); + for (size_t col = 0; col < ESM::Land::LAND_SIZE-1; col += outerStep) + { + indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); + indices.push_back(ESM::Land::LAND_SIZE*col+row); + // Make sure not to touch the left edge + if (col == 0) + indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row-innerStep); + else + indices.push_back(ESM::Land::LAND_SIZE*col+row-innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep) + continue; + indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row-innerStep); + indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row-innerStep); + indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); + } + } + + // West + size_t col = 0; + outerStep = 1 << (lodDeltas[West] + lodLevel); + for (size_t row = 0; row < ESM::Land::LAND_SIZE-1; row += outerStep) + { + indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); + indices.push_back(ESM::Land::LAND_SIZE*col+row); + // Make sure not to touch the top edge + if (row+outerStep == ESM::Land::LAND_SIZE-1) + indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep-innerStep); + else + indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep) + continue; + indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i); + indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i+innerStep); + } + } + + // East + col = ESM::Land::LAND_SIZE-1; + outerStep = 1 << (lodDeltas[East] + lodLevel); + for (size_t row = 0; row < ESM::Land::LAND_SIZE-1; row += outerStep) + { + indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); + // Make sure not to touch the bottom edge + if (row == 0) + indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+innerStep); + else + indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep) + continue; + indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); + indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i+innerStep); + indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i); + } + } + } + + + + numIndices = indices.size(); + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, + numIndices, Ogre::HardwareBuffer::HBU_STATIC); + buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); + mIndexBufferMap[flags] = buffer; + return buffer; + } + + void World::renderCompositeMap(Ogre::TexturePtr target) + { + mCompositeMapRenderTarget->update(); + target->getBuffer()->blit(mCompositeMapRenderTexture->getBuffer()); + } + + void World::clearCompositeMapSceneManager() + { + mCompositeMapSceneMgr->destroyAllManualObjects(); + mCompositeMapSceneMgr->clearScene(); + } + + float World::getHeightAt(const Ogre::Vector3 &worldPos) + { + return mStorage->getHeightAt(worldPos); + } + + void World::applyMaterials(bool shadows, bool splitShadows) + { + mShadows = shadows; + mSplitShadows = splitShadows; + mRootNode->applyMaterials(); + } + + void World::setVisible(bool visible) + { + if (visible && !mVisible) + mSceneMgr->getRootSceneNode()->addChild(mRootSceneNode); + else if (!visible && mVisible) + mSceneMgr->getRootSceneNode()->removeChild(mRootSceneNode); + + mVisible = visible; + } + + bool World::getVisible() + { + return mVisible; + } + + +} diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp new file mode 100644 index 000000000..b8c1b0a7d --- /dev/null +++ b/components/terrain/world.hpp @@ -0,0 +1,154 @@ +#ifndef COMPONENTS_TERRAIN_H +#define COMPONENTS_TERRAIN_H + +#include +#include +#include +#include + +namespace Loading +{ + class Listener; +} + +namespace Ogre +{ + class Camera; +} + +namespace Terrain +{ + + class QuadTreeNode; + class Storage; + + /** + * @brief A quadtree-based terrain implementation suitable for large data sets. \n + * Near cells are rendered with alpha splatting, distant cells are merged + * together in batches and have their layers pre-rendered onto a composite map. \n + * Cracks at LOD transitions are avoided using stitching. + * @note Multiple cameras are not supported yet + */ + class World + { + public: + /// @note takes ownership of \a storage + /// @param loadingListener Listener to update with progress + /// @param sceneMgr scene manager to use + /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) + /// @param visbilityFlags visibility flags for the created meshes + /// @param distantLand Whether to draw all of the terrain, or only a 3x3 grid around the camera. + /// This is a temporary option until it can be streamlined. + /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually + /// faster so this is just here for compatibility. + World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, + Storage* storage, int visiblityFlags, bool distantLand, bool shaders); + ~World(); + + void setLoadingListener(Loading::Listener* loadingListener) { mLoadingListener = loadingListener; } + + bool getDistantLandEnabled() { return mDistantLand; } + bool getShadersEnabled() { return mShaders; } + bool getShadowsEnabled() { return mShadows; } + bool getSplitShadowsEnabled() { return mSplitShadows; } + + float getHeightAt (const Ogre::Vector3& worldPos); + + /// Update chunk LODs according to this camera position + /// @note Calling this method might lead to composite textures being rendered, so it is best + /// not to call it when render commands are still queued, since that would cause a flush. + void update (const Ogre::Vector3& cameraPos); + + /// Get the world bounding box of a chunk of terrain centered at \a center + Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center); + + Ogre::SceneManager* getSceneManager() { return mSceneMgr; } + + Ogre::SceneNode* getRootSceneNode() { return mRootSceneNode; } + + Storage* getStorage() { return mStorage; } + + /// Show or hide the whole terrain + /// @note this setting will be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden + void setVisible(bool visible); + bool getVisible(); + + /// Recreate materials used by terrain chunks. This should be called whenever settings of + /// the material factory are changed. (Relying on the factory to update those materials is not + /// enough, since turning a feature on/off can change the number of texture units available for layer/blend + /// textures, and to properly respond to this we may need to change the structure of the material, such as + /// adding or removing passes. This can only be achieved by a full rebuild.) + void applyMaterials(bool shadows, bool splitShadows); + + int getVisiblityFlags() { return mVisibilityFlags; } + + int getMaxBatchSize() { return mMaxBatchSize; } + + void enableSplattingShader(bool enabled); + + private: + bool mDistantLand; + bool mShaders; + bool mShadows; + bool mSplitShadows; + bool mVisible; + + Loading::Listener* mLoadingListener; + + QuadTreeNode* mRootNode; + Ogre::SceneNode* mRootSceneNode; + Storage* mStorage; + + int mVisibilityFlags; + + Ogre::SceneManager* mSceneMgr; + Ogre::SceneManager* mCompositeMapSceneMgr; + + /// Bounds in cell units + Ogre::AxisAlignedBox mBounds; + + /// Minimum size of a terrain batch along one side (in cell units) + float mMinBatchSize; + /// Maximum size of a terrain batch along one side (in cell units) + float mMaxBatchSize; + + void buildQuadTree(QuadTreeNode* node); + + public: + // ----INTERNAL---- + + enum IndexBufferFlags + { + IBF_North = 1 << 0, + IBF_East = 1 << 1, + IBF_South = 1 << 2, + IBF_West = 1 << 3 + }; + + /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) + /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) + /// @param numIndices number of indices that were used will be written here + Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (int flags, size_t& numIndices); + + Ogre::HardwareVertexBufferSharedPtr getVertexBuffer (int numVertsOneSide); + + Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } + + // Delete all quads + void clearCompositeMapSceneManager(); + void renderCompositeMap (Ogre::TexturePtr target); + + private: + // Index buffers are shared across terrain batches where possible. There is one index buffer for each + // combination of LOD deltas and index buffer LOD we may need. + std::map mIndexBufferMap; + + std::map mUvBufferMap; + + Ogre::RenderTarget* mCompositeMapRenderTarget; + Ogre::TexturePtr mCompositeMapRenderTexture; + }; + +} + +#endif diff --git a/components/to_utf8/gen_iconv.cpp b/components/to_utf8/gen_iconv.cpp index dea68c1fa..8198b305d 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/to_utf8/gen_iconv.cpp @@ -46,7 +46,7 @@ void writeMissing(bool last) int write_table(const std::string &charset, const std::string &tableName) { // Write table header - cout << "static char " << tableName << "[] =\n{\n"; + cout << "static signed char " << tableName << "[] =\n{\n"; // Open conversion system iconv_t cd = iconv_open ("UTF-8", charset.c_str()); @@ -106,6 +106,8 @@ int main() "\n"; write_table("WINDOWS-1252", "windows_1252"); + write_table("CP437", "cp437"); + // Close namespace cout << "\n}\n\n"; diff --git a/components/to_utf8/tables_gen.hpp b/components/to_utf8/tables_gen.hpp index a1d4b6d80..14e66eac1 100644 --- a/components/to_utf8/tables_gen.hpp +++ b/components/to_utf8/tables_gen.hpp @@ -790,6 +790,265 @@ static signed char windows_1252[] = 2, -61, -66, 0, 0, 0, 2, -61, -65, 0, 0, 0 }; +static signed char cp437[] = +{ + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -61, -96, 0, 0, 0, + 2, -61, -91, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -61, -86, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -61, -88, 0, 0, 0, + 2, -61, -81, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -61, -84, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -61, -123, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -61, -90, 0, 0, 0, + 2, -61, -122, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -78, 0, 0, 0, + 2, -61, -69, 0, 0, 0, + 2, -61, -71, 0, 0, 0, + 2, -61, -65, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -62, -94, 0, 0, 0, + 2, -62, -93, 0, 0, 0, + 2, -62, -91, 0, 0, 0, + 3, -30, -126, -89, 0, 0, + 2, -58, -110, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -61, -79, 0, 0, 0, + 2, -61, -111, 0, 0, 0, + 2, -62, -86, 0, 0, 0, + 2, -62, -70, 0, 0, 0, + 2, -62, -65, 0, 0, 0, + 3, -30, -116, -112, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -67, 0, 0, 0, + 2, -62, -68, 0, 0, 0, + 2, -62, -95, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 3, -30, -106, -111, 0, 0, + 3, -30, -106, -110, 0, 0, + 3, -30, -106, -109, 0, 0, + 3, -30, -108, -126, 0, 0, + 3, -30, -108, -92, 0, 0, + 3, -30, -107, -95, 0, 0, + 3, -30, -107, -94, 0, 0, + 3, -30, -107, -106, 0, 0, + 3, -30, -107, -107, 0, 0, + 3, -30, -107, -93, 0, 0, + 3, -30, -107, -111, 0, 0, + 3, -30, -107, -105, 0, 0, + 3, -30, -107, -99, 0, 0, + 3, -30, -107, -100, 0, 0, + 3, -30, -107, -101, 0, 0, + 3, -30, -108, -112, 0, 0, + 3, -30, -108, -108, 0, 0, + 3, -30, -108, -76, 0, 0, + 3, -30, -108, -84, 0, 0, + 3, -30, -108, -100, 0, 0, + 3, -30, -108, -128, 0, 0, + 3, -30, -108, -68, 0, 0, + 3, -30, -107, -98, 0, 0, + 3, -30, -107, -97, 0, 0, + 3, -30, -107, -102, 0, 0, + 3, -30, -107, -108, 0, 0, + 3, -30, -107, -87, 0, 0, + 3, -30, -107, -90, 0, 0, + 3, -30, -107, -96, 0, 0, + 3, -30, -107, -112, 0, 0, + 3, -30, -107, -84, 0, 0, + 3, -30, -107, -89, 0, 0, + 3, -30, -107, -88, 0, 0, + 3, -30, -107, -92, 0, 0, + 3, -30, -107, -91, 0, 0, + 3, -30, -107, -103, 0, 0, + 3, -30, -107, -104, 0, 0, + 3, -30, -107, -110, 0, 0, + 3, -30, -107, -109, 0, 0, + 3, -30, -107, -85, 0, 0, + 3, -30, -107, -86, 0, 0, + 3, -30, -108, -104, 0, 0, + 3, -30, -108, -116, 0, 0, + 3, -30, -106, -120, 0, 0, + 3, -30, -106, -124, 0, 0, + 3, -30, -106, -116, 0, 0, + 3, -30, -106, -112, 0, 0, + 3, -30, -106, -128, 0, 0, + 2, -50, -79, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -50, -109, 0, 0, 0, + 2, -49, -128, 0, 0, 0, + 2, -50, -93, 0, 0, 0, + 2, -49, -125, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -49, -124, 0, 0, 0, + 2, -50, -90, 0, 0, 0, + 2, -50, -104, 0, 0, 0, + 2, -50, -87, 0, 0, 0, + 2, -50, -76, 0, 0, 0, + 3, -30, -120, -98, 0, 0, + 2, -49, -122, 0, 0, 0, + 2, -50, -75, 0, 0, 0, + 3, -30, -120, -87, 0, 0, + 3, -30, -119, -95, 0, 0, + 2, -62, -79, 0, 0, 0, + 3, -30, -119, -91, 0, 0, + 3, -30, -119, -92, 0, 0, + 3, -30, -116, -96, 0, 0, + 3, -30, -116, -95, 0, 0, + 2, -61, -73, 0, 0, 0, + 3, -30, -119, -120, 0, 0, + 2, -62, -80, 0, 0, 0, + 3, -30, -120, -103, 0, 0, + 2, -62, -73, 0, 0, 0, + 3, -30, -120, -102, 0, 0, + 3, -30, -127, -65, 0, 0, + 2, -62, -78, 0, 0, 0, + 3, -30, -106, -96, 0, 0, + 2, -62, -96, 0, 0, 0 +}; } diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 1de15d79c..59a9aff80 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -63,6 +63,12 @@ Utf8Encoder::Utf8Encoder(const FromType sourceEncoding): translationArray = ToUTF8::windows_1251; break; } + case ToUTF8::CP437: + { + translationArray = ToUTF8::cp437; + break; + } + default: { assert(0); diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index 839aa36aa..3f20a51f8 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -12,8 +12,9 @@ namespace ToUTF8 { WINDOWS_1250, // Central ane Eastern European languages WINDOWS_1251, // Cyrillic languages - WINDOWS_1252 // Used by English version of Morrowind (and + WINDOWS_1252, // Used by English version of Morrowind (and // probably others) + CP437 // Used for fonts (*.fnt) if data files encoding is 1252. Otherwise, uses the same encoding as the data files. }; FromType calculateEncoding(const std::string& encodingName); diff --git a/credits.txt b/credits.txt index f06377500..c766c34c3 100644 --- a/credits.txt +++ b/credits.txt @@ -12,14 +12,17 @@ Marc Zinnschlag (Zini) - Lead Programmer/Project Manager Adam Hogan (aurix) Aleksandar Jovanov +Alex McKibben (WeirdSexy) Alexander Nadeau (wareya) Alexander Olofsson (Ace) Artem Kotsynyak (greye) athile +Britt Mathis (galdor557) BrotherBrick Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) +darkf Douglas Diniz (Dgdiniz) Douglas Mencken (dougmencken) Edmondo Tommasina (edmondo) @@ -31,6 +34,7 @@ Jacob Essex (Yacoby) Jannik Heller (scrawl) Jason Hooks (jhooks) Joel Graff (graffy) +John Blomberg (fstp) Jordan Milne Julien Voisin (jvoisin/ap0) Karl-Felix Glatzer (k1ll) @@ -38,9 +42,12 @@ Lars Söderberg (Lazaroth) lazydev Leon Saunders (emoose) Lukasz Gromanowski (lgro) +Manuel Edelmann (vorenon) +Marc Bouvier (CramitDeFrog) Marcin Hulist (Gohan) Mark Siewert (mark76) -Manuel Edelmann (vorenon) +Mateusz Kołaczek (PL_kolek) +Michael Hogan (Xethik) Michael Mc Donnell Michael Papageorgiou (werdanith) Michał Bień (Glorf) @@ -51,12 +58,13 @@ Paul McElroy (Greendogo) Pieter van der Kloet (pvdk) Radu-Marius Popovici (rpopovici) Roman Melnik (Kromgart) +Roman Proskuryakov (humbug) Sandy Carter (bwrsandman) Sebastian Wick (swick) Sergey Shambir Sylvain Thesnieres (Garvek) Tom Mason (wheybags) - +Torben Leif Carrington (TorbenC) Packagers: Alexander Olofsson (Ace) - Windows @@ -75,7 +83,7 @@ Julien Voisin (jvoisin/ap0) - French News Writer Mickey Lyle (raevol) - Release Manager Pithorn - Chinese News Writer sir_herrbatka - English/Polish News Writer -WeirdSexy - Podcaster +Alex McKibben (WeirdSexy) - Podcaster Website: diff --git a/extern/oics/CMakeLists.txt b/extern/oics/CMakeLists.txt index 7c14387a4..5c1edbf62 100644 --- a/extern/oics/CMakeLists.txt +++ b/extern/oics/CMakeLists.txt @@ -9,12 +9,23 @@ set(OICS_SOURCE_FILES ICSInputControlSystem_keyboard.cpp ICSInputControlSystem_mouse.cpp ICSInputControlSystem_joystick.cpp +) + +set(TINYXML_SOURCE_FILES tinyxml.cpp tinyxmlparser.cpp tinyxmlerror.cpp - tinystr.cpp + tinystr.cpp ) -add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES}) +if(USE_SYSTEM_TINYXML) + add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES}) + target_link_libraries(${OICS_LIBRARY} ${TINYXML_LIBRARIES}) +else() + add_library(${OICS_LIBRARY} STATIC + ${OICS_SOURCE_FILES} + ${TINYXML_SOURCE_FILES}) +endif() +# Does this do anything? link_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/extern/oics/ICSControl.cpp b/extern/oics/ICSControl.cpp index d43733727..934c661c9 100644 --- a/extern/oics/ICSControl.cpp +++ b/extern/oics/ICSControl.cpp @@ -30,7 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace ICS { - Control::Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop, bool autoReverseToInitialValue + Control::Control(const std::string& name, bool autoChangeDirectionOnLimitsAfterStop, bool autoReverseToInitialValue , float initialValue, float stepSize, float stepsPerSeconds, bool axisBindable) : mName(name) , mValue(initialValue) diff --git a/extern/oics/ICSControl.h b/extern/oics/ICSControl.h index 73f1d5494..7939c86b9 100644 --- a/extern/oics/ICSControl.h +++ b/extern/oics/ICSControl.h @@ -35,7 +35,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace ICS { - class DllExport Control + class DllExport Control { public: @@ -44,7 +44,7 @@ namespace ICS DECREASE = -1, STOP = 0, INCREASE = 1 }; - Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop = false, bool autoReverseToInitialValue = false, float initialValue = 0.5, float stepSize = 0.1, float stepsPerSeconds = 2.0, bool axisBindable = true); + Control(const std::string& name, bool autoChangeDirectionOnLimitsAfterStop = false, bool autoReverseToInitialValue = false, float initialValue = 0.5, float stepSize = 0.1, float stepsPerSeconds = 2.0, bool axisBindable = true); ~Control(); void setChangingDirection(ControlChangingDirection direction); @@ -104,4 +104,4 @@ namespace ICS } -#endif \ No newline at end of file +#endif diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp index 1702c853e..cdf8fbfe2 100644 --- a/extern/oics/ICSInputControlSystem.cpp +++ b/extern/oics/ICSInputControlSystem.cpp @@ -41,7 +41,7 @@ namespace ICS this->mActive = active; - this->fillOISKeysMap(); + this->fillSDLKeysMap(); ICS_LOG("Channel count = " + ToString(channelCount) ); for(size_t i=0;i::iterator it = mKeys.begin() + mKeys["UNASSIGNED"]= SDLK_UNKNOWN; + mKeys["ESCAPE"]= SDLK_ESCAPE; + mKeys["1"]= SDLK_1; + mKeys["2"]= SDLK_2; + mKeys["3"]= SDLK_3; + mKeys["4"]= SDLK_4; + mKeys["5"]= SDLK_5; + mKeys["6"]= SDLK_6; + mKeys["7"]= SDLK_7; + mKeys["8"]= SDLK_8; + mKeys["9"]= SDLK_9; + mKeys["0"]= SDLK_0; + mKeys["MINUS"]= SDLK_MINUS; + mKeys["EQUALS"]= SDLK_EQUALS; + mKeys["BACK"]= SDLK_BACKSPACE; + mKeys["TAB"]= SDLK_TAB; + mKeys["Q"]= SDLK_q; + mKeys["W"]= SDLK_w; + mKeys["E"]= SDLK_e; + mKeys["R"]= SDLK_r; + mKeys["T"]= SDLK_t; + mKeys["Y"]= SDLK_y; + mKeys["U"]= SDLK_u; + mKeys["I"]= SDLK_i; + mKeys["O"]= SDLK_o; + mKeys["P"]= SDLK_p; + mKeys["LBRACKET"]= SDLK_LEFTBRACKET; + mKeys["RBRACKET"]= SDLK_RIGHTBRACKET; + mKeys["RETURN"]= SDLK_RETURN; + mKeys["LCONTROL"]= SDLK_LCTRL; + mKeys["A"]= SDLK_a; + mKeys["S"]= SDLK_s; + mKeys["D"]= SDLK_d; + mKeys["F"]= SDLK_f; + mKeys["G"]= SDLK_g; + mKeys["H"]= SDLK_h; + mKeys["J"]= SDLK_j; + mKeys["K"]= SDLK_k; + mKeys["L"]= SDLK_l; + mKeys["SEMICOLON"]= SDLK_SEMICOLON; + mKeys["APOSTROPHE"]= SDLK_QUOTE; + mKeys["GRAVE"]= SDLK_BACKQUOTE; + mKeys["LSHIFT"]= SDLK_LSHIFT; + mKeys["BACKSLASH"]= SDLK_BACKSLASH; + mKeys["Z"]= SDLK_z; + mKeys["X"]= SDLK_x; + mKeys["C"]= SDLK_c; + mKeys["V"]= SDLK_v; + mKeys["B"]= SDLK_b; + mKeys["N"]= SDLK_n; + mKeys["M"]= SDLK_m; + mKeys["COMMA"]= SDLK_COMMA; + mKeys["PERIOD"]= SDLK_PERIOD; + mKeys["SLASH"]= SDLK_SLASH; + mKeys["RSHIFT"]= SDLK_RSHIFT; + mKeys["MULTIPLY"]= SDLK_ASTERISK; + mKeys["LMENU"]= SDLK_LALT; + mKeys["SPACE"]= SDLK_SPACE; + mKeys["CAPITAL"]= SDLK_CAPSLOCK; + mKeys["F1"]= SDLK_F1; + mKeys["F2"]= SDLK_F2; + mKeys["F3"]= SDLK_F3; + mKeys["F4"]= SDLK_F4; + mKeys["F5"]= SDLK_F5; + mKeys["F6"]= SDLK_F6; + mKeys["F7"]= SDLK_F7; + mKeys["F8"]= SDLK_F8; + mKeys["F9"]= SDLK_F9; + mKeys["F10"]= SDLK_F10; + mKeys["F11"]= SDLK_F11; + mKeys["F12"]= SDLK_F12; + mKeys["NUMLOCK"]= SDLK_NUMLOCKCLEAR; + mKeys["SCROLL"]= SDLK_SCROLLLOCK; + mKeys["NUMPAD7"]= SDLK_KP_7; + mKeys["NUMPAD8"]= SDLK_KP_8; + mKeys["NUMPAD9"]= SDLK_KP_9; + mKeys["SUBTRACT"]= SDLK_KP_MINUS; + mKeys["NUMPAD4"]= SDLK_KP_4; + mKeys["NUMPAD5"]= SDLK_KP_5; + mKeys["NUMPAD6"]= SDLK_KP_6; + mKeys["ADD"]= SDLK_KP_PLUS; + mKeys["NUMPAD1"]= SDLK_KP_1; + mKeys["NUMPAD2"]= SDLK_KP_2; + mKeys["NUMPAD3"]= SDLK_KP_3; + mKeys["NUMPAD0"]= SDLK_KP_0; + mKeys["DECIMAL"]= SDLK_KP_DECIMAL; + mKeys["RCONTROL"]= SDLK_RCTRL; + mKeys["DIVIDE"]= SDLK_SLASH; + mKeys["SYSRQ"]= SDLK_SYSREQ; + mKeys["PRNTSCRN"] = SDLK_PRINTSCREEN; + mKeys["RMENU"]= SDLK_RALT; + mKeys["PAUSE"]= SDLK_PAUSE; + mKeys["HOME"]= SDLK_HOME; + mKeys["UP"]= SDLK_UP; + mKeys["PGUP"]= SDLK_PAGEUP; + mKeys["LEFT"]= SDLK_LEFT; + mKeys["RIGHT"]= SDLK_RIGHT; + mKeys["END"]= SDLK_END; + mKeys["DOWN"]= SDLK_DOWN; + mKeys["PGDOWN"]= SDLK_PAGEDOWN; + mKeys["INSERT"]= SDLK_INSERT; + mKeys["DELETE"]= SDLK_DELETE; + + mKeys["NUMPADENTER"]= SDLK_KP_ENTER; + + for(std::map::iterator it = mKeys.begin() ; it != mKeys.end() ; it++) { mKeyCodes[ it->second ] = it->first; } } - std::string InputControlSystem::keyCodeToString(OIS::KeyCode key) + std::string InputControlSystem::keyCodeToString(SDL_Keycode key) { return mKeyCodes[key]; } - OIS::KeyCode InputControlSystem::stringToKeyCode(std::string key) + SDL_Keycode InputControlSystem::stringToKeyCode(std::string key) { return mKeys[key]; } -} \ No newline at end of file + + void InputControlSystem::adjustMouseRegion(Uint16 width, Uint16 height) + { + mClientWidth = width; + mClientHeight = height; + } +} diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index f1c12d3b5..f42f9c0b5 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -32,6 +32,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "ICSControl.h" #include "ICSChannel.h" +#include "../sdl4ogre/events.h" + #define ICS_LOG(text) if(mLog) mLog->logMessage( ("ICS: " + std::string(text)).c_str() ); #define ICS_MAX_JOYSTICK_AXIS 16 #define ICS_MOUSE_BINDING_MARGIN 30 @@ -48,9 +50,9 @@ namespace ICS }; class DllExport InputControlSystem : - public OIS::MouseListener, - public OIS::KeyListener, - public OIS::JoyStickListener + public SFO::MouseListener, + public SFO::KeyListener, + public SFO::JoyListener { public: @@ -100,29 +102,30 @@ namespace ICS JoystickIDList& getJoystickIdList(){ return mJoystickIDList; }; // MouseListener - bool mouseMoved(const OIS::MouseEvent &evt); - bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID); - bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID); + bool mouseMoved(const SFO::MouseMotionEvent &evt); + bool mousePressed(const SDL_MouseButtonEvent &evt, Uint8); + bool mouseReleased(const SDL_MouseButtonEvent &evt, Uint8); // KeyListener - bool keyPressed(const OIS::KeyEvent &evt); - bool keyReleased(const OIS::KeyEvent &evt); + bool keyPressed(const SDL_KeyboardEvent &evt); + bool keyReleased(const SDL_KeyboardEvent &evt); // JoyStickListener - bool buttonPressed(const OIS::JoyStickEvent &evt, int button); - bool buttonReleased(const OIS::JoyStickEvent &evt, int button); - bool axisMoved(const OIS::JoyStickEvent &evt, int axis); - bool povMoved(const OIS::JoyStickEvent &evt, int index); - bool sliderMoved(const OIS::JoyStickEvent &evt, int index); - - void addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction); + bool buttonPressed(const SDL_JoyButtonEvent &evt, int button); + bool buttonReleased(const SDL_JoyButtonEvent &evt, int button); + bool axisMoved(const SDL_JoyAxisEvent &evt, int axis); + bool povMoved(const SDL_JoyHatEvent &evt, int index); + //TODO: does this have an SDL equivalent? + //bool sliderMoved(const OIS::JoyStickEvent &evt, int index); + + void addKeyBinding(Control* control, SDL_Keycode key, Control::ControlChangingDirection direction); void addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction); void addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction); void addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction); void addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction); void addJoystickPOVBinding(Control* control, int deviceId, int index, POVAxis axis, Control::ControlChangingDirection direction); void addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction); - void removeKeyBinding(OIS::KeyCode key); + void removeKeyBinding(SDL_Keycode key); void removeMouseAxisBinding(NamedAxis axis); void removeMouseButtonBinding(unsigned int button); void removeJoystickAxisBinding(int deviceId, int axis); @@ -130,7 +133,7 @@ namespace ICS void removeJoystickPOVBinding(int deviceId, int index, POVAxis axis); void removeJoystickSliderBinding(int deviceId, int index); - OIS::KeyCode getKeyBinding(Control* control, ICS::Control::ControlChangingDirection direction); + SDL_Keycode getKeyBinding(Control* control, ICS::Control::ControlChangingDirection direction); NamedAxis getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction); unsigned int getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction); int getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); @@ -138,14 +141,16 @@ namespace ICS POVBindingPair getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); int getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); - std::string keyCodeToString(OIS::KeyCode key); - OIS::KeyCode stringToKeyCode(std::string key); + std::string keyCodeToString(SDL_Keycode key); + SDL_Keycode stringToKeyCode(std::string key); void enableDetectingBindingState(Control* control, Control::ControlChangingDirection direction); void cancelDetectingBindingState(); bool save(std::string fileName = ""); + void adjustMouseRegion (Uint16 width, Uint16 height); + protected: void loadKeyBinders(TiXmlElement* xmlControlNode); @@ -180,7 +185,7 @@ namespace ICS std::string mFileName; - typedef std::map ControlsKeyBinderMapType; // + typedef std::map ControlsKeyBinderMapType; // typedef std::map ControlsAxisBinderMapType; // typedef std::map ControlsButtonBinderMapType; // typedef std::map ControlsPOVBinderMapType; // @@ -202,8 +207,8 @@ namespace ICS std::vector mChannels; ControlsKeyBinderMapType mControlsKeyBinderMap; - std::map mKeys; - std::map mKeyCodes; + std::map mKeys; + std::map mKeyCodes; bool mActive; InputControlSystemLog* mLog; @@ -221,14 +226,17 @@ namespace ICS private: - void fillOISKeysMap(); + void fillSDLKeysMap(); + + Uint16 mClientWidth; + Uint16 mClientHeight; }; class DllExport DetectingBindingListener { public: virtual void keyBindingDetected(InputControlSystem* ICS, Control* control - , OIS::KeyCode key, Control::ControlChangingDirection direction); + , SDL_Keycode key, Control::ControlChangingDirection direction); virtual void mouseAxisBindingDetected(InputControlSystem* ICS, Control* control , InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction); diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp index 1e66599ea..8e501d501 100644 --- a/extern/oics/ICSInputControlSystem_joystick.cpp +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -26,6 +26,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "ICSInputControlSystem.h" +#define SDL_JOY_AXIS_MIN -32768 +#define SDL_JOY_AXIS_MAX 32767 + namespace ICS { // load xml @@ -315,16 +318,16 @@ namespace ICS } // joyStick listeners - bool InputControlSystem::buttonPressed(const OIS::JoyStickEvent &evt, int button) + bool InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { if(!mDetectingBindingControl) { - if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + if(mControlsJoystickButtonBinderMap.find(evt.which) != mControlsJoystickButtonBinderMap.end()) { - ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); - if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.which].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.which].end()) { it->second.control->setIgnoreAutoReverse(false); if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) @@ -348,21 +351,21 @@ namespace ICS else if(mDetectingBindingListener) { mDetectingBindingListener->joystickButtonBindingDetected(this, - mDetectingBindingControl, evt.device->getID(), button, mDetectingBindingDirection); + mDetectingBindingControl, evt.which, button, mDetectingBindingDirection); } } return true; } - bool InputControlSystem::buttonReleased(const OIS::JoyStickEvent &evt, int button) + bool InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { - if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + if(mControlsJoystickButtonBinderMap.find(evt.which) != mControlsJoystickButtonBinderMap.end()) { - ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); - if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.which].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.which].end()) { it->second.control->setChangingDirection(Control::STOP); } @@ -371,31 +374,29 @@ namespace ICS return true; } - bool InputControlSystem::axisMoved(const OIS::JoyStickEvent &evt, int axis) - { + bool InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis) + { if(mActive) { if(!mDetectingBindingControl) { - if(mControlsJoystickAxisBinderMap.find(evt.device->getID()) != mControlsJoystickAxisBinderMap.end()) + if(mControlsJoystickAxisBinderMap.find(evt.which) != mControlsJoystickAxisBinderMap.end()) { - ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.which ][ axis ]; // joystic axis start at 0 index Control* ctrl = joystickBinderItem.control; if(ctrl) { ctrl->setIgnoreAutoReverse(true); + + float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MAX; + float valDisplaced = (float)(evt.value - SDL_JOY_AXIS_MIN); + if(joystickBinderItem.direction == Control::INCREASE) { - float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; - float valDisplaced = (float)( evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); - ctrl->setValue( valDisplaced / axisRange ); } else if(joystickBinderItem.direction == Control::DECREASE) { - float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; - float valDisplaced = (float)(evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); - ctrl->setValue( 1 - ( valDisplaced / axisRange ) ); } } @@ -403,15 +404,15 @@ namespace ICS } else if(mDetectingBindingListener) { - //ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + //ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.which ][ axis ]; // joystic axis start at 0 index //Control* ctrl = joystickBinderItem.control; //if(ctrl && ctrl->isAxisBindable()) if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) { - if( abs( evt.state.mAxes[axis].abs ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN) + if( abs( evt.value ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN) { mDetectingBindingListener->joystickAxisBindingDetected(this, - mDetectingBindingControl, evt.device->getID(), axis, mDetectingBindingDirection); + mDetectingBindingControl, evt.which, axis, mDetectingBindingDirection); } } } @@ -420,20 +421,21 @@ namespace ICS return true; } - bool InputControlSystem::povMoved(const OIS::JoyStickEvent &evt, int index) - { + //Here be dragons, apparently + bool InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index) + { if(mActive) { if(!mDetectingBindingControl) { - if(mControlsJoystickPOVBinderMap.find(evt.device->getID()) != mControlsJoystickPOVBinderMap.end()) + if(mControlsJoystickPOVBinderMap.find(evt.which) != mControlsJoystickPOVBinderMap.end()) { - std::map::const_iterator i = mControlsJoystickPOVBinderMap[ evt.device->getID() ].find(index); - if(i != mControlsJoystickPOVBinderMap[ evt.device->getID() ].end()) + std::map::const_iterator i = mControlsJoystickPOVBinderMap[ evt.which ].find(index); + if(i != mControlsJoystickPOVBinderMap[ evt.which ].end()) { - if(evt.state.mPOV[index].direction != OIS::Pov::West - && evt.state.mPOV[index].direction != OIS::Pov::East - && evt.state.mPOV[index].direction != OIS::Pov::Centered) + if(evt.value != SDL_HAT_LEFT + && evt.value != SDL_HAT_RIGHT + && evt.value != SDL_HAT_CENTERED) { ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); if(it != i->second.end()) @@ -441,9 +443,9 @@ namespace ICS it->second.control->setIgnoreAutoReverse(false); if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) { - if(evt.state.mPOV[index].direction == OIS::Pov::North - || evt.state.mPOV[index].direction == OIS::Pov::NorthWest - || evt.state.mPOV[index].direction == OIS::Pov::NorthEast) + if(evt.value == SDL_HAT_UP + || evt.value == SDL_HAT_LEFTUP + || evt.value == SDL_HAT_RIGHTUP) { it->second.control->setChangingDirection(it->second.direction); } @@ -453,7 +455,7 @@ namespace ICS } } else - { + { if(it->second.control->getValue() == 1) { it->second.control->setChangingDirection(Control::DECREASE); @@ -466,9 +468,9 @@ namespace ICS } } - if(evt.state.mPOV[index].direction != OIS::Pov::North - && evt.state.mPOV[index].direction != OIS::Pov::South - && evt.state.mPOV[index].direction != OIS::Pov::Centered) + if(evt.value != SDL_HAT_UP + && evt.value != SDL_HAT_DOWN + && evt.value != SDL_HAT_CENTERED) { ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/EastWest ); if(it != i->second.end()) @@ -476,9 +478,9 @@ namespace ICS it->second.control->setIgnoreAutoReverse(false); if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) { - if(evt.state.mPOV[index].direction == OIS::Pov::East - || evt.state.mPOV[index].direction == OIS::Pov::NorthEast - || evt.state.mPOV[index].direction == OIS::Pov::SouthEast) + if(evt.value == SDL_HAT_RIGHT + || evt.value == SDL_HAT_RIGHTUP + || evt.value == SDL_HAT_RIGHTDOWN) { it->second.control->setChangingDirection(it->second.direction); } @@ -488,7 +490,7 @@ namespace ICS } } else - { + { if(it->second.control->getValue() == 1) { it->second.control->setChangingDirection(Control::DECREASE); @@ -501,7 +503,7 @@ namespace ICS } } - if(evt.state.mPOV[index].direction == OIS::Pov::Centered) + if(evt.value == SDL_HAT_CENTERED) { ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); if(it != i->second.end()) @@ -522,28 +524,30 @@ namespace ICS { if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) { - if(evt.state.mPOV[index].direction == OIS::Pov::West - || evt.state.mPOV[index].direction == OIS::Pov::East - || evt.state.mPOV[index].direction == OIS::Pov::North - || evt.state.mPOV[index].direction == OIS::Pov::South) + if(evt.value == SDL_HAT_LEFT + || evt.value == SDL_HAT_RIGHT + || evt.value == SDL_HAT_UP + || evt.value == SDL_HAT_DOWN) { POVAxis povAxis = NorthSouth; - if(evt.state.mPOV[index].direction == OIS::Pov::West - || evt.state.mPOV[index].direction == OIS::Pov::East) + if(evt.value == SDL_HAT_LEFT + || evt.value == SDL_HAT_RIGHT) { povAxis = EastWest; } mDetectingBindingListener->joystickPOVBindingDetected(this, - mDetectingBindingControl, evt.device->getID(), index, povAxis, mDetectingBindingDirection); + mDetectingBindingControl, evt.which, index, povAxis, mDetectingBindingDirection); } } } } - + return true; } + //TODO: does this have an SDL equivalent? + /* bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) { if(mActive) @@ -552,7 +556,7 @@ namespace ICS { if(mControlsJoystickSliderBinderMap.find(evt.device->getID()) != mControlsJoystickSliderBinderMap.end()) { - ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; + ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; Control* ctrl = joystickBinderItem.control; if(ctrl) { @@ -576,10 +580,6 @@ namespace ICS } else if(mDetectingBindingListener) { - /*ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; - Control* ctrl = joystickBinderItem.control; - if(ctrl && ctrl->isAxisBindable()) - {*/ if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) { if( abs( evt.state.mSliders[index].abX ) > ICS_JOYSTICK_SLIDER_BINDING_MARGIN) @@ -593,6 +593,7 @@ namespace ICS return true; } + */ // joystick auto bindings void DetectingBindingListener::joystickAxisBindingDetected(InputControlSystem* ICS, Control* control @@ -662,4 +663,4 @@ namespace ICS ICS->addJoystickSliderBinding(control, deviceId, slider, direction); ICS->cancelDetectingBindingState(); } -} \ No newline at end of file +} diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp index 8ef81d979..01d68f784 100644 --- a/extern/oics/ICSInputControlSystem_keyboard.cpp +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -49,7 +49,7 @@ namespace ICS } } - void InputControlSystem::addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction) + void InputControlSystem::addKeyBinding(Control* control, SDL_Keycode key, Control::ControlChangingDirection direction) { ICS_LOG("\tAdding KeyBinder [key=" + keyCodeToString(key) + ", direction=" @@ -61,7 +61,7 @@ namespace ICS mControlsKeyBinderMap[ key ] = controlKeyBinderItem; } - void InputControlSystem::removeKeyBinding(OIS::KeyCode key) + void InputControlSystem::removeKeyBinding(SDL_Keycode key) { ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.find(key); if(it != mControlsKeyBinderMap.end()) @@ -70,7 +70,7 @@ namespace ICS } } - OIS::KeyCode InputControlSystem::getKeyBinding(Control* control + SDL_Keycode InputControlSystem::getKeyBinding(Control* control , ICS::Control::ControlChangingDirection direction) { ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.begin(); @@ -83,15 +83,15 @@ namespace ICS it++; } - return OIS::KC_UNASSIGNED; + return SDLK_UNKNOWN; } - bool InputControlSystem::keyPressed(const OIS::KeyEvent &evt) + bool InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt) { if(mActive) { if(!mDetectingBindingControl) { - ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.keysym.sym); if(it != mControlsKeyBinderMap.end()) { it->second.control->setIgnoreAutoReverse(false); @@ -115,18 +115,18 @@ namespace ICS else if(mDetectingBindingListener) { mDetectingBindingListener->keyBindingDetected(this, - mDetectingBindingControl, evt.key, mDetectingBindingDirection); + mDetectingBindingControl, evt.keysym.sym, mDetectingBindingDirection); } } return true; } - bool InputControlSystem::keyReleased(const OIS::KeyEvent &evt) + bool InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt) { if(mActive) { - ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.keysym.sym); if(it != mControlsKeyBinderMap.end()) { it->second.control->setChangingDirection(Control::STOP); @@ -137,14 +137,14 @@ namespace ICS } void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control - , OIS::KeyCode key, Control::ControlChangingDirection direction) + , SDL_Keycode key, Control::ControlChangingDirection direction) { // if the key is used by another control, remove it ICS->removeKeyBinding(key); // if the control has a key assigned, remove it - OIS::KeyCode oldKey = ICS->getKeyBinding(control, direction); - if(oldKey != OIS::KC_UNASSIGNED) + SDL_Keycode oldKey = ICS->getKeyBinding(control, direction); + if(oldKey != SDLK_UNKNOWN) { ICS->removeKeyBinding(oldKey); } @@ -153,4 +153,4 @@ namespace ICS ICS->cancelDetectingBindingState(); } -} \ No newline at end of file +} diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp index c62f1765e..52eb894ed 100644 --- a/extern/oics/ICSInputControlSystem_mouse.cpp +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -78,15 +78,15 @@ namespace ICS int button = 0; if(std::string(xmlMouseButtonBinder->Attribute("button")) == "LEFT") { - button = OIS::/*MouseButtonID::*/MB_Left; + button = SDL_BUTTON_LEFT; } else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "RIGHT") { - button = OIS::/*MouseButtonID::*/MB_Right; + button = SDL_BUTTON_RIGHT; } else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "MIDDLE") { - button = OIS::/*MouseButtonID::*/MB_Middle; + button = SDL_BUTTON_MIDDLE; } else { @@ -219,39 +219,39 @@ namespace ICS } // mouse Listeners - bool InputControlSystem::mouseMoved(const OIS::MouseEvent &evt) + bool InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt) { if(mActive) { if(!mDetectingBindingControl) { - if(mXmouseAxisBinded && evt.state.X.rel) + if(mXmouseAxisBinded && evt.xrel) { ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/X ]; Control* ctrl = mouseBinderItem.control; ctrl->setIgnoreAutoReverse(true); if(mouseBinderItem.direction == Control::INCREASE) { - ctrl->setValue( float( (evt.state.X.abs) / float(evt.state.width) ) ); + ctrl->setValue( float( (evt.x) / float(mClientWidth) ) ); } else if(mouseBinderItem.direction == Control::DECREASE) { - ctrl->setValue( 1 - float( evt.state.X.abs / float(evt.state.width) ) ); + ctrl->setValue( 1 - float( evt.x / float(mClientWidth) ) ); } } - if(mYmouseAxisBinded && evt.state.Y.rel) + if(mYmouseAxisBinded && evt.yrel) { ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/Y ]; Control* ctrl = mouseBinderItem.control; ctrl->setIgnoreAutoReverse(true); if(mouseBinderItem.direction == Control::INCREASE) { - ctrl->setValue( float( (evt.state.Y.abs) / float(evt.state.height) ) ); + ctrl->setValue( float( (evt.y) / float(mClientHeight) ) ); } else if(mouseBinderItem.direction == Control::DECREASE) { - ctrl->setValue( 1 - float( evt.state.Y.abs / float(evt.state.height) ) ); + ctrl->setValue( 1 - float( evt.y / float(mClientHeight) ) ); } } @@ -282,9 +282,9 @@ namespace ICS mMouseAxisBindingInitialValues[2] = 0; } - mMouseAxisBindingInitialValues[0] += evt.state.X.rel; - mMouseAxisBindingInitialValues[1] += evt.state.Y.rel; - mMouseAxisBindingInitialValues[2] += evt.state.Z.rel; + mMouseAxisBindingInitialValues[0] += evt.xrel; + mMouseAxisBindingInitialValues[1] += evt.yrel; + mMouseAxisBindingInitialValues[2] += evt.zrel; if( abs(mMouseAxisBindingInitialValues[0]) > ICS_MOUSE_BINDING_MARGIN ) { @@ -308,7 +308,7 @@ namespace ICS return true; } - bool InputControlSystem::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + bool InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -345,7 +345,7 @@ namespace ICS return true; } - bool InputControlSystem::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + bool InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -394,4 +394,4 @@ namespace ICS ICS->cancelDetectingBindingState(); } -} \ No newline at end of file +} diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h index 3b5d1935b..52daea3f4 100644 --- a/extern/oics/ICSPrerequisites.h +++ b/extern/oics/ICSPrerequisites.h @@ -39,11 +39,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "tinyxml.h" -#include -#include -#include -#include -#include +#include "SDL_keyboard.h" +#include "SDL_mouse.h" +#include "SDL_joystick.h" +#include "SDL_events.h" /// Define the dll export qualifier if compiling for Windows @@ -65,8 +64,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /// Version defines #define ICS_VERSION_MAJOR 0 -#define ICS_VERSION_MINOR 3 -#define ICS_VERSION_PATCH 1 +#define ICS_VERSION_MINOR 4 +#define ICS_VERSION_PATCH 0 #define ICS_MAX_DEVICE_BUTTONS 30 diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp index 841a41cd3..29a4768aa 100644 --- a/extern/oics/tinyxml.cpp +++ b/extern/oics/tinyxml.cpp @@ -706,9 +706,9 @@ void TiXmlElement::SetDoubleAttribute( const char * name, double val ) { char buf[256]; #if defined(TIXML_SNPRINTF) - TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); #else - sprintf( buf, "%f", val ); + sprintf( buf, "%f", val ); #endif SetAttribute( name, buf ); } @@ -1266,9 +1266,9 @@ void TiXmlAttribute::SetDoubleValue( double _value ) { char buf [256]; #if defined(TIXML_SNPRINTF) - TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + TIXML_SNPRINTF( buf, sizeof(buf), "%f", _value); #else - sprintf (buf, "%lf", _value); + sprintf (buf, "%f", _value); #endif SetValue (buf); } diff --git a/extern/sdl4ogre/CMakeLists.txt b/extern/sdl4ogre/CMakeLists.txt new file mode 100644 index 000000000..fb0832f71 --- /dev/null +++ b/extern/sdl4ogre/CMakeLists.txt @@ -0,0 +1,25 @@ +set(SDL4OGRE_LIBRARY "sdl4ogre") + +# Sources + +set(SDL4OGRE_SOURCE_FILES + sdlinputwrapper.cpp + sdlcursormanager.cpp + sdlwindowhelper.cpp +) + +if (APPLE) + set(SDL4OGRE_SOURCE_FILES ${SDL4OGRE_SOURCE_FILES} osx_utils.mm) +endif () + +set(SDL4OGRE_HEADER_FILES + OISCompat.h + cursormanager.hpp +) + +add_library(${SDL4OGRE_LIBRARY} STATIC ${SDL4OGRE_SOURCE_FILES} ${SDL4OGRE_HEADER_FILES}) + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) + + +target_link_libraries(${SDL4OGRE_LIBRARY} ${SDL2_LIBRARY}) diff --git a/extern/sdl4ogre/OISCompat.h b/extern/sdl4ogre/OISCompat.h new file mode 100644 index 000000000..3cffa143d --- /dev/null +++ b/extern/sdl4ogre/OISCompat.h @@ -0,0 +1,159 @@ +#ifndef _OIS_SDL_COMPAT_H +#define _OIS_SDL_COMPAT_H + +#include +#include + +namespace OIS +{ +//! Keyboard scan codes +enum KeyCode +{ + KC_UNASSIGNED = 0x00, + KC_ESCAPE = 0x01, + KC_1 = 0x02, + KC_2 = 0x03, + KC_3 = 0x04, + KC_4 = 0x05, + KC_5 = 0x06, + KC_6 = 0x07, + KC_7 = 0x08, + KC_8 = 0x09, + KC_9 = 0x0A, + KC_0 = 0x0B, + KC_MINUS = 0x0C, // - on main keyboard + KC_EQUALS = 0x0D, + KC_BACK = 0x0E, // backspace + KC_TAB = 0x0F, + KC_Q = 0x10, + KC_W = 0x11, + KC_E = 0x12, + KC_R = 0x13, + KC_T = 0x14, + KC_Y = 0x15, + KC_U = 0x16, + KC_I = 0x17, + KC_O = 0x18, + KC_P = 0x19, + KC_LBRACKET = 0x1A, + KC_RBRACKET = 0x1B, + KC_RETURN = 0x1C, // Enter on main keyboard + KC_LCONTROL = 0x1D, + KC_A = 0x1E, + KC_S = 0x1F, + KC_D = 0x20, + KC_F = 0x21, + KC_G = 0x22, + KC_H = 0x23, + KC_J = 0x24, + KC_K = 0x25, + KC_L = 0x26, + KC_SEMICOLON = 0x27, + KC_APOSTROPHE = 0x28, + KC_GRAVE = 0x29, // accent + KC_LSHIFT = 0x2A, + KC_BACKSLASH = 0x2B, + KC_Z = 0x2C, + KC_X = 0x2D, + KC_C = 0x2E, + KC_V = 0x2F, + KC_B = 0x30, + KC_N = 0x31, + KC_M = 0x32, + KC_COMMA = 0x33, + KC_PERIOD = 0x34, // . on main keyboard + KC_SLASH = 0x35, // / on main keyboard + KC_RSHIFT = 0x36, + KC_MULTIPLY = 0x37, // * on numeric keypad + KC_LMENU = 0x38, // left Alt + KC_SPACE = 0x39, + KC_CAPITAL = 0x3A, + KC_F1 = 0x3B, + KC_F2 = 0x3C, + KC_F3 = 0x3D, + KC_F4 = 0x3E, + KC_F5 = 0x3F, + KC_F6 = 0x40, + KC_F7 = 0x41, + KC_F8 = 0x42, + KC_F9 = 0x43, + KC_F10 = 0x44, + KC_NUMLOCK = 0x45, + KC_SCROLL = 0x46, // Scroll Lock + KC_NUMPAD7 = 0x47, + KC_NUMPAD8 = 0x48, + KC_NUMPAD9 = 0x49, + KC_SUBTRACT = 0x4A, // - on numeric keypad + KC_NUMPAD4 = 0x4B, + KC_NUMPAD5 = 0x4C, + KC_NUMPAD6 = 0x4D, + KC_ADD = 0x4E, // + on numeric keypad + KC_NUMPAD1 = 0x4F, + KC_NUMPAD2 = 0x50, + KC_NUMPAD3 = 0x51, + KC_NUMPAD0 = 0x52, + KC_DECIMAL = 0x53, // . on numeric keypad + KC_OEM_102 = 0x56, // < > | on UK/Germany keyboards + KC_F11 = 0x57, + KC_F12 = 0x58, + KC_F13 = 0x64, // (NEC PC98) + KC_F14 = 0x65, // (NEC PC98) + KC_F15 = 0x66, // (NEC PC98) + KC_KANA = 0x70, // (Japanese keyboard) + KC_ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards + KC_CONVERT = 0x79, // (Japanese keyboard) + KC_NOCONVERT = 0x7B, // (Japanese keyboard) + KC_YEN = 0x7D, // (Japanese keyboard) + KC_ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards + KC_NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98) + KC_PREVTRACK = 0x90, // Previous Track (KC_CIRCUMFLEX on Japanese keyboard) + KC_AT = 0x91, // (NEC PC98) + KC_COLON = 0x92, // (NEC PC98) + KC_UNDERLINE = 0x93, // (NEC PC98) + KC_KANJI = 0x94, // (Japanese keyboard) + KC_STOP = 0x95, // (NEC PC98) + KC_AX = 0x96, // (Japan AX) + KC_UNLABELED = 0x97, // (J3100) + KC_NEXTTRACK = 0x99, // Next Track + KC_NUMPADENTER = 0x9C, // Enter on numeric keypad + KC_RCONTROL = 0x9D, + KC_MUTE = 0xA0, // Mute + KC_CALCULATOR = 0xA1, // Calculator + KC_PLAYPAUSE = 0xA2, // Play / Pause + KC_MEDIASTOP = 0xA4, // Media Stop + KC_VOLUMEDOWN = 0xAE, // Volume - + KC_VOLUMEUP = 0xB0, // Volume + + KC_WEBHOME = 0xB2, // Web home + KC_NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98) + KC_DIVIDE = 0xB5, // / on numeric keypad + KC_SYSRQ = 0xB7, + KC_RMENU = 0xB8, // right Alt + KC_PAUSE = 0xC5, // Pause + KC_HOME = 0xC7, // Home on arrow keypad + KC_UP = 0xC8, // UpArrow on arrow keypad + KC_PGUP = 0xC9, // PgUp on arrow keypad + KC_LEFT = 0xCB, // LeftArrow on arrow keypad + KC_RIGHT = 0xCD, // RightArrow on arrow keypad + KC_END = 0xCF, // End on arrow keypad + KC_DOWN = 0xD0, // DownArrow on arrow keypad + KC_PGDOWN = 0xD1, // PgDn on arrow keypad + KC_INSERT = 0xD2, // Insert on arrow keypad + KC_DELETE = 0xD3, // Delete on arrow keypad + KC_LWIN = 0xDB, // Left Windows key + KC_RWIN = 0xDC, // Right Windows key + KC_APPS = 0xDD, // AppMenu key + KC_POWER = 0xDE, // System Power + KC_SLEEP = 0xDF, // System Sleep + KC_WAKE = 0xE3, // System Wake + KC_WEBSEARCH = 0xE5, // Web Search + KC_WEBFAVORITES= 0xE6, // Web Favorites + KC_WEBREFRESH = 0xE7, // Web Refresh + KC_WEBSTOP = 0xE8, // Web Stop + KC_WEBFORWARD = 0xE9, // Web Forward + KC_WEBBACK = 0xEA, // Web Back + KC_MYCOMPUTER = 0xEB, // My Computer + KC_MAIL = 0xEC, // Mail + KC_MEDIASELECT = 0xED // Media Select +}; +} +#endif diff --git a/extern/sdl4ogre/cursormanager.hpp b/extern/sdl4ogre/cursormanager.hpp new file mode 100644 index 000000000..f45c5cdc2 --- /dev/null +++ b/extern/sdl4ogre/cursormanager.hpp @@ -0,0 +1,33 @@ +#ifndef _SDL4OGRE_CURSOR_MANAGER_H +#define _SDL4OGRE_CURSOR_MANAGER_H + +#include +#include + +#include +#include + +namespace SFO +{ +class CursorManager +{ +public: + virtual ~CursorManager(){} + + /// \brief Tell the manager that the cursor has changed, giving the + /// name of the cursor we changed to ("arrow", "ibeam", etc) + /// \return Whether the manager is interested in more information about the cursor + virtual bool cursorChanged(const std::string &name) = 0; + + /// \brief Follow up a cursorChanged() call with enough info to create an cursor. + virtual void receiveCursorInfo(const std::string &name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) = 0; + + /// \brief Tell the manager when the cursor visibility changed + virtual void cursorVisibilityChange(bool visible) = 0; + + /// \brief sets whether to actively manage cursors or not + virtual void setEnabled(bool enabled) = 0; +}; +} + +#endif diff --git a/extern/sdl4ogre/events.h b/extern/sdl4ogre/events.h new file mode 100644 index 000000000..e6e8434cb --- /dev/null +++ b/extern/sdl4ogre/events.h @@ -0,0 +1,78 @@ +#ifndef _SFO_EVENTS_H +#define _SFO_EVENTS_H + +#include + + +//////////// +// Events // +//////////// + +namespace SFO { + +/** Extended mouse event struct where we treat the wheel like an axis, like everyone expects */ +struct MouseMotionEvent : SDL_MouseMotionEvent { + + Sint32 zrel; + Sint32 z; +}; + + +/////////////// +// Listeners // +/////////////// + +class MouseListener +{ +public: + virtual ~MouseListener() {} + virtual bool mouseMoved( const MouseMotionEvent &arg ) = 0; + virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; + virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; +}; + +class KeyListener +{ +public: + virtual ~KeyListener() {} + virtual void textInput (const SDL_TextInputEvent& arg) {} + virtual bool keyPressed(const SDL_KeyboardEvent &arg) = 0; + virtual bool keyReleased(const SDL_KeyboardEvent &arg) = 0; +}; + +class JoyListener +{ +public: + virtual ~JoyListener() {} + /** @remarks Joystick button down event */ + virtual bool buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0; + + /** @remarks Joystick button up event */ + virtual bool buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0; + + /** @remarks Joystick axis moved event */ + virtual bool axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0; + + //-- Not so common control events, so are not required --// + + //! Joystick Event, and povID + virtual bool povMoved( const SDL_JoyHatEvent &arg, int index) {return true;} +}; + +class WindowListener +{ +public: + virtual ~WindowListener() {} + + /** @remarks The window's visibility changed */ + virtual void windowVisibilityChange( bool visible ) {}; + + /** @remarks The window got / lost input focus */ + virtual void windowFocusChange( bool have_focus ) {} + + virtual void windowResized (int x, int y) {} +}; + +} + +#endif diff --git a/extern/sdl4ogre/osx_utils.h b/extern/sdl4ogre/osx_utils.h new file mode 100644 index 000000000..48149827a --- /dev/null +++ b/extern/sdl4ogre/osx_utils.h @@ -0,0 +1,12 @@ +#ifndef SDL4OGRE_OSX_UTILS_H +#define SDL4OGRE_OSX_UTILS_H + +#include + +namespace SFO { + +extern unsigned long WindowContentViewHandle(SDL_SysWMinfo &info); + +} + +#endif // SDL4OGRE_OSX_UTILS_H diff --git a/extern/sdl4ogre/osx_utils.mm b/extern/sdl4ogre/osx_utils.mm new file mode 100644 index 000000000..4069959cb --- /dev/null +++ b/extern/sdl4ogre/osx_utils.mm @@ -0,0 +1,15 @@ +#include "osx_utils.h" +#import + + +namespace SFO { + +unsigned long WindowContentViewHandle(SDL_SysWMinfo &info) +{ + NSWindow *window = info.info.cocoa.window; + NSView *view = [window contentView]; + + return (unsigned long)view; +} + +} diff --git a/extern/sdl4ogre/sdlcursormanager.cpp b/extern/sdl4ogre/sdlcursormanager.cpp new file mode 100644 index 000000000..d14a9ffa0 --- /dev/null +++ b/extern/sdl4ogre/sdlcursormanager.cpp @@ -0,0 +1,177 @@ +#include "sdlcursormanager.hpp" + +#include +#include + +#include + +namespace SFO +{ + + SDLCursorManager::SDLCursorManager() : + mEnabled(false), + mCursorVisible(false), + mInitialized(false) + { + } + + SDLCursorManager::~SDLCursorManager() + { + CursorMap::const_iterator curs_iter = mCursorMap.begin(); + + while(curs_iter != mCursorMap.end()) + { + SDL_FreeCursor(curs_iter->second); + ++curs_iter; + } + + mCursorMap.clear(); + } + + void SDLCursorManager::setEnabled(bool enabled) + { + if(mInitialized && enabled == mEnabled) + return; + + mInitialized = true; + mEnabled = enabled; + + //turn on hardware cursors + if(enabled) + { + _setGUICursor(mCurrentCursor); + } + //turn off hardware cursors + else + { + SDL_ShowCursor(SDL_FALSE); + } + } + + bool SDLCursorManager::cursorChanged(const std::string &name) + { + mCurrentCursor = name; + + CursorMap::const_iterator curs_iter = mCursorMap.find(name); + + //we have this cursor + if(curs_iter != mCursorMap.end()) + { + _setGUICursor(name); + + return false; + } + else + { + //they should get back to us with more info + return true; + } + } + + void SDLCursorManager::_setGUICursor(const std::string &name) + { + if(mEnabled && mCursorVisible) + { + SDL_SetCursor(mCursorMap.find(name)->second); + _setCursorVisible(mCursorVisible); + } + } + + void SDLCursorManager::_setCursorVisible(bool visible) + { + if(!mEnabled) + return; + + SDL_ShowCursor(visible ? SDL_TRUE : SDL_FALSE); + } + + void SDLCursorManager::cursorVisibilityChange(bool visible) + { + mCursorVisible = visible; + + _setGUICursor(mCurrentCursor); + _setCursorVisible(visible); + } + + void SDLCursorManager::receiveCursorInfo(const std::string& name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) + { + _createCursorFromResource(name, rotDegrees, tex, size_x, size_y, hotspot_x, hotspot_y); + } + + /// \brief creates an SDL cursor from an Ogre texture + void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) + { + if (mCursorMap.find(name) != mCursorMap.end()) + return; + + std::string tempName = tex->getName() + "_rotated"; + + // we use a render target to uncompress the DDS texture + // just blitting doesn't seem to work on D3D9 + OEngine::Render::ImageRotate::rotate(tex->getName(), tempName, -rotDegrees); + + Ogre::TexturePtr resultTexture = Ogre::TextureManager::getSingleton().getByName(tempName); + + // now blit to memory + Ogre::Image destImage; + resultTexture->convertToImage(destImage); + + SDL_Surface* surf = SDL_CreateRGBSurface(0,size_x,size_y,32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF); + + + //copy the Ogre texture to an SDL surface + for(size_t x = 0; x < size_x; ++x) + { + for(size_t y = 0; y < size_y; ++y) + { + Ogre::ColourValue clr = destImage.getColourAt(x, y, 0); + + //set the pixel on the SDL surface to the same value as the Ogre texture's + _putPixel(surf, x, y, SDL_MapRGBA(surf->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255)); + } + } + + //set the cursor and store it for later + SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y); + mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); + + //clean up + SDL_FreeSurface(surf); + Ogre::TextureManager::getSingleton().remove(tempName); + + _setGUICursor(name); + } + + void SDLCursorManager::_putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) + { + int bpp = surface->format->BytesPerPixel; + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) { + case 1: + *p = pixel; + break; + + case 2: + *(Uint16 *)p = pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } else { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } + } +} diff --git a/extern/sdl4ogre/sdlcursormanager.hpp b/extern/sdl4ogre/sdlcursormanager.hpp new file mode 100644 index 000000000..8940220d4 --- /dev/null +++ b/extern/sdl4ogre/sdlcursormanager.hpp @@ -0,0 +1,41 @@ +#ifndef _SDL4OGRE_CURSORMANAGER_H +#define _SDL4OGRE_CURSORMANAGER_H + +#include + +#include "cursormanager.hpp" +#include + +namespace SFO +{ + class SDLCursorManager : + public CursorManager + { + public: + SDLCursorManager(); + virtual ~SDLCursorManager(); + + virtual void setEnabled(bool enabled); + + virtual bool cursorChanged(const std::string &name); + virtual void receiveCursorInfo(const std::string &name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); + virtual void cursorVisibilityChange(bool visible); + + private: + void _createCursorFromResource(const std::string &name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); + void _putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel); + + void _setGUICursor(const std::string& name); + void _setCursorVisible(bool visible); + + typedef std::map CursorMap; + CursorMap mCursorMap; + + std::string mCurrentCursor; + bool mEnabled; + bool mInitialized; + bool mCursorVisible; + }; +} + +#endif diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp new file mode 100644 index 000000000..931d6aca3 --- /dev/null +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -0,0 +1,418 @@ +#include "sdlinputwrapper.hpp" +#include + +#include +#include + + +namespace SFO +{ + /// \brief General purpose wrapper for OGRE applications around SDL's event + /// queue, mostly used for handling input-related events. + InputWrapper::InputWrapper(SDL_Window* window, Ogre::RenderWindow* ogreWindow) : + mSDLWindow(window), + mOgreWindow(ogreWindow), + mWarpCompensate(false), + mMouseRelative(false), + mGrabPointer(false), + mWrapPointer(false), + mMouseZ(0), + mMouseY(0), + mMouseX(0), + mMouseInWindow(true), + mJoyListener(NULL), + mKeyboardListener(NULL), + mMouseListener(NULL), + mWindowListener(NULL) + { + _setupOISKeys(); + } + + InputWrapper::~InputWrapper() + { + if(mSDLWindow != NULL) + SDL_DestroyWindow(mSDLWindow); + mSDLWindow = NULL; + } + + void InputWrapper::capture(bool windowEventsOnly) + { + SDL_PumpEvents(); + + SDL_Event evt; + + if (windowEventsOnly) + { + // During loading, just handle window events, and keep others for later + while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT)) + handleWindowEvent(evt); + return; + } + + while(SDL_PollEvent(&evt)) + { + switch(evt.type) + { + case SDL_MOUSEMOTION: + //ignore this if it happened due to a warp + if(!_handleWarpMotion(evt.motion)) + { + mMouseListener->mouseMoved(_packageMouseMotion(evt)); + + //try to keep the mouse inside the window + _wrapMousePointer(evt.motion); + } + break; + case SDL_MOUSEWHEEL: + mMouseListener->mouseMoved(_packageMouseMotion(evt)); + break; + case SDL_MOUSEBUTTONDOWN: + mMouseListener->mousePressed(evt.button, evt.button.button); + break; + case SDL_MOUSEBUTTONUP: + mMouseListener->mouseReleased(evt.button, evt.button.button); + break; + case SDL_KEYDOWN: + if (!evt.key.repeat) + mKeyboardListener->keyPressed(evt.key); + break; + case SDL_KEYUP: + if (!evt.key.repeat) + mKeyboardListener->keyReleased(evt.key); + break; + case SDL_TEXTINPUT: + mKeyboardListener->textInput(evt.text); + break; + case SDL_JOYAXISMOTION: + if (mJoyListener) + mJoyListener->axisMoved(evt.jaxis, evt.jaxis.axis); + break; + case SDL_JOYBUTTONDOWN: + if (mJoyListener) + mJoyListener->buttonPressed(evt.jbutton, evt.jbutton.button); + break; + case SDL_JOYBUTTONUP: + if (mJoyListener) + mJoyListener->buttonReleased(evt.jbutton, evt.jbutton.button); + break; + case SDL_JOYDEVICEADDED: + //SDL_JoystickOpen(evt.jdevice.which); + //std::cout << "Detected a new joystick: " << SDL_JoystickNameForIndex(evt.jdevice.which) << std::endl; + break; + case SDL_JOYDEVICEREMOVED: + //std::cout << "A joystick has been removed" << std::endl; + break; + case SDL_WINDOWEVENT: + handleWindowEvent(evt); + break; + case SDL_QUIT: + Ogre::Root::getSingleton().queueEndRendering(); + break; + default: + std::cerr << "Unhandled SDL event of type " << evt.type << std::endl; + break; + } + } + } + + void InputWrapper::handleWindowEvent(const SDL_Event& evt) + { + switch (evt.window.event) { + case SDL_WINDOWEVENT_ENTER: + mMouseInWindow = true; + break; + case SDL_WINDOWEVENT_LEAVE: + mMouseInWindow = false; + SDL_SetWindowGrab(mSDLWindow, SDL_FALSE); + SDL_SetRelativeMouseMode(SDL_FALSE); + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + int w,h; + SDL_GetWindowSize(mSDLWindow, &w, &h); + // TODO: Fix Ogre to handle this more consistently +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + mOgreWindow->resize(w, h); +#else + mOgreWindow->windowMovedOrResized(); +#endif + if (mWindowListener) + mWindowListener->windowResized(w, h); + break; + + case SDL_WINDOWEVENT_RESIZED: + // TODO: Fix Ogre to handle this more consistently +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + mOgreWindow->resize(evt.window.data1, evt.window.data2); +#else + mOgreWindow->windowMovedOrResized(); +#endif + if (mWindowListener) + mWindowListener->windowResized(evt.window.data1, evt.window.data2); + break; + + case SDL_WINDOWEVENT_FOCUS_GAINED: + if (mWindowListener) + mWindowListener->windowFocusChange(true); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + if (mWindowListener) + mWindowListener->windowFocusChange(false); + break; + case SDL_WINDOWEVENT_CLOSE: + break; + case SDL_WINDOWEVENT_SHOWN: + mOgreWindow->setVisible(true); + if (mWindowListener) + mWindowListener->windowVisibilityChange(true); + break; + case SDL_WINDOWEVENT_HIDDEN: + mOgreWindow->setVisible(false); + if (mWindowListener) + mWindowListener->windowVisibilityChange(false); + break; + } + } + + bool InputWrapper::isModifierHeld(SDL_Keymod mod) + { + return SDL_GetModState() & mod; + } + + bool InputWrapper::isKeyDown(SDL_Scancode key) + { + return SDL_GetKeyboardState(NULL)[key]; + } + + /// \brief Moves the mouse to the specified point within the viewport + void InputWrapper::warpMouse(int x, int y) + { + SDL_WarpMouseInWindow(mSDLWindow, x, y); + mWarpCompensate = true; + mWarpX = x; + mWarpY = y; + } + + /// \brief Locks the pointer to the window + void InputWrapper::setGrabPointer(bool grab) + { + mGrabPointer = grab && mMouseInWindow; + SDL_SetWindowGrab(mSDLWindow, grab && mMouseInWindow ? SDL_TRUE : SDL_FALSE); + } + + /// \brief Set the mouse to relative positioning. Doesn't move the cursor + /// and disables mouse acceleration. + void InputWrapper::setMouseRelative(bool relative) + { + if(mMouseRelative == relative && mMouseInWindow) + return; + + mMouseRelative = relative && mMouseInWindow; + + mWrapPointer = false; + + //eep, wrap the pointer manually if the input driver doesn't support + //relative positioning natively + int success = SDL_SetRelativeMouseMode(relative && mMouseInWindow ? SDL_TRUE : SDL_FALSE); + if(relative && mMouseInWindow && success != 0) + mWrapPointer = true; + + //now remove all mouse events using the old setting from the queue + SDL_PumpEvents(); + SDL_FlushEvent(SDL_MOUSEMOTION); + } + + /// \brief Internal method for ignoring relative motions as a side effect + /// of warpMouse() + bool InputWrapper::_handleWarpMotion(const SDL_MouseMotionEvent& evt) + { + if(!mWarpCompensate) + return false; + + //this was a warp event, signal the caller to eat it. + if(evt.x == mWarpX && evt.y == mWarpY) + { + mWarpCompensate = false; + return true; + } + + return false; + } + + /// \brief Wrap the mouse to the viewport + void InputWrapper::_wrapMousePointer(const SDL_MouseMotionEvent& evt) + { + //don't wrap if we don't want relative movements, support relative + //movements natively, or aren't grabbing anyways + if(!mMouseRelative || !mWrapPointer || !mGrabPointer) + return; + + int width = 0; + int height = 0; + + SDL_GetWindowSize(mSDLWindow, &width, &height); + + const int FUDGE_FACTOR_X = width; + const int FUDGE_FACTOR_Y = height; + + //warp the mouse if it's about to go outside the window + if(evt.x - FUDGE_FACTOR_X < 0 || evt.x + FUDGE_FACTOR_X > width + || evt.y - FUDGE_FACTOR_Y < 0 || evt.y + FUDGE_FACTOR_Y > height) + { + warpMouse(width / 2, height / 2); + } + } + + /// \brief Package mouse and mousewheel motions into a single event + MouseMotionEvent InputWrapper::_packageMouseMotion(const SDL_Event &evt) + { + MouseMotionEvent pack_evt; + pack_evt.x = mMouseX; + pack_evt.xrel = 0; + pack_evt.y = mMouseY; + pack_evt.yrel = 0; + pack_evt.z = mMouseZ; + pack_evt.zrel = 0; + + if(evt.type == SDL_MOUSEMOTION) + { + pack_evt.x = mMouseX = evt.motion.x; + pack_evt.y = mMouseY = evt.motion.y; + pack_evt.xrel = evt.motion.xrel; + pack_evt.yrel = evt.motion.yrel; + } + else if(evt.type == SDL_MOUSEWHEEL) + { + mMouseZ += pack_evt.zrel = (evt.wheel.y * 120); + pack_evt.z = mMouseZ; + } + else + { + throw new std::runtime_error("Tried to package non-motion event!"); + } + + return pack_evt; + } + + OIS::KeyCode InputWrapper::sdl2OISKeyCode(SDL_Keycode code) + { + OIS::KeyCode kc = OIS::KC_UNASSIGNED; + + KeyMap::const_iterator ois_equiv = mKeyMap.find(code); + + if(ois_equiv != mKeyMap.end()) + kc = ois_equiv->second; + + return kc; + } + + void InputWrapper::_setupOISKeys() + { + //lifted from OIS's SDLKeyboard.cpp + + //TODO: Consider switching to scancodes so we + //can properly support international keyboards + //look at SDL_GetKeyFromScancode and SDL_GetKeyName + mKeyMap.insert( KeyMap::value_type(SDLK_UNKNOWN, OIS::KC_UNASSIGNED)); + mKeyMap.insert( KeyMap::value_type(SDLK_ESCAPE, OIS::KC_ESCAPE) ); + mKeyMap.insert( KeyMap::value_type(SDLK_1, OIS::KC_1) ); + mKeyMap.insert( KeyMap::value_type(SDLK_2, OIS::KC_2) ); + mKeyMap.insert( KeyMap::value_type(SDLK_3, OIS::KC_3) ); + mKeyMap.insert( KeyMap::value_type(SDLK_4, OIS::KC_4) ); + mKeyMap.insert( KeyMap::value_type(SDLK_5, OIS::KC_5) ); + mKeyMap.insert( KeyMap::value_type(SDLK_6, OIS::KC_6) ); + mKeyMap.insert( KeyMap::value_type(SDLK_7, OIS::KC_7) ); + mKeyMap.insert( KeyMap::value_type(SDLK_8, OIS::KC_8) ); + mKeyMap.insert( KeyMap::value_type(SDLK_9, OIS::KC_9) ); + mKeyMap.insert( KeyMap::value_type(SDLK_0, OIS::KC_0) ); + mKeyMap.insert( KeyMap::value_type(SDLK_MINUS, OIS::KC_MINUS) ); + mKeyMap.insert( KeyMap::value_type(SDLK_EQUALS, OIS::KC_EQUALS) ); + mKeyMap.insert( KeyMap::value_type(SDLK_BACKSPACE, OIS::KC_BACK) ); + mKeyMap.insert( KeyMap::value_type(SDLK_TAB, OIS::KC_TAB) ); + mKeyMap.insert( KeyMap::value_type(SDLK_q, OIS::KC_Q) ); + mKeyMap.insert( KeyMap::value_type(SDLK_w, OIS::KC_W) ); + mKeyMap.insert( KeyMap::value_type(SDLK_e, OIS::KC_E) ); + mKeyMap.insert( KeyMap::value_type(SDLK_r, OIS::KC_R) ); + mKeyMap.insert( KeyMap::value_type(SDLK_t, OIS::KC_T) ); + mKeyMap.insert( KeyMap::value_type(SDLK_y, OIS::KC_Y) ); + mKeyMap.insert( KeyMap::value_type(SDLK_u, OIS::KC_U) ); + mKeyMap.insert( KeyMap::value_type(SDLK_i, OIS::KC_I) ); + mKeyMap.insert( KeyMap::value_type(SDLK_o, OIS::KC_O) ); + mKeyMap.insert( KeyMap::value_type(SDLK_p, OIS::KC_P) ); + mKeyMap.insert( KeyMap::value_type(SDLK_RETURN, OIS::KC_RETURN) ); + mKeyMap.insert( KeyMap::value_type(SDLK_LCTRL, OIS::KC_LCONTROL)); + mKeyMap.insert( KeyMap::value_type(SDLK_a, OIS::KC_A) ); + mKeyMap.insert( KeyMap::value_type(SDLK_s, OIS::KC_S) ); + mKeyMap.insert( KeyMap::value_type(SDLK_d, OIS::KC_D) ); + mKeyMap.insert( KeyMap::value_type(SDLK_f, OIS::KC_F) ); + mKeyMap.insert( KeyMap::value_type(SDLK_g, OIS::KC_G) ); + mKeyMap.insert( KeyMap::value_type(SDLK_h, OIS::KC_H) ); + mKeyMap.insert( KeyMap::value_type(SDLK_j, OIS::KC_J) ); + mKeyMap.insert( KeyMap::value_type(SDLK_k, OIS::KC_K) ); + mKeyMap.insert( KeyMap::value_type(SDLK_l, OIS::KC_L) ); + mKeyMap.insert( KeyMap::value_type(SDLK_SEMICOLON, OIS::KC_SEMICOLON) ); + mKeyMap.insert( KeyMap::value_type(SDLK_COLON, OIS::KC_COLON) ); + mKeyMap.insert( KeyMap::value_type(SDLK_QUOTE, OIS::KC_APOSTROPHE) ); + mKeyMap.insert( KeyMap::value_type(SDLK_BACKQUOTE, OIS::KC_GRAVE) ); + mKeyMap.insert( KeyMap::value_type(SDLK_LSHIFT, OIS::KC_LSHIFT) ); + mKeyMap.insert( KeyMap::value_type(SDLK_BACKSLASH, OIS::KC_BACKSLASH) ); + mKeyMap.insert( KeyMap::value_type(SDLK_SLASH, OIS::KC_SLASH) ); + mKeyMap.insert( KeyMap::value_type(SDLK_z, OIS::KC_Z) ); + mKeyMap.insert( KeyMap::value_type(SDLK_x, OIS::KC_X) ); + mKeyMap.insert( KeyMap::value_type(SDLK_c, OIS::KC_C) ); + mKeyMap.insert( KeyMap::value_type(SDLK_v, OIS::KC_V) ); + mKeyMap.insert( KeyMap::value_type(SDLK_b, OIS::KC_B) ); + mKeyMap.insert( KeyMap::value_type(SDLK_n, OIS::KC_N) ); + mKeyMap.insert( KeyMap::value_type(SDLK_m, OIS::KC_M) ); + mKeyMap.insert( KeyMap::value_type(SDLK_COMMA, OIS::KC_COMMA) ); + mKeyMap.insert( KeyMap::value_type(SDLK_PERIOD, OIS::KC_PERIOD)); + mKeyMap.insert( KeyMap::value_type(SDLK_RSHIFT, OIS::KC_RSHIFT)); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_MULTIPLY, OIS::KC_MULTIPLY) ); + mKeyMap.insert( KeyMap::value_type(SDLK_LALT, OIS::KC_LMENU) ); + mKeyMap.insert( KeyMap::value_type(SDLK_SPACE, OIS::KC_SPACE)); + mKeyMap.insert( KeyMap::value_type(SDLK_CAPSLOCK, OIS::KC_CAPITAL) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F1, OIS::KC_F1) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F2, OIS::KC_F2) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F3, OIS::KC_F3) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F4, OIS::KC_F4) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F5, OIS::KC_F5) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F6, OIS::KC_F6) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F7, OIS::KC_F7) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F8, OIS::KC_F8) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F9, OIS::KC_F9) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F10, OIS::KC_F10) ); + mKeyMap.insert( KeyMap::value_type(SDLK_NUMLOCKCLEAR, OIS::KC_NUMLOCK) ); + mKeyMap.insert( KeyMap::value_type(SDLK_SCROLLLOCK, OIS::KC_SCROLL)); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_7, OIS::KC_NUMPAD7) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_8, OIS::KC_NUMPAD8) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_9, OIS::KC_NUMPAD9) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_MINUS, OIS::KC_SUBTRACT) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_4, OIS::KC_NUMPAD4) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_5, OIS::KC_NUMPAD5) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_6, OIS::KC_NUMPAD6) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_PLUS, OIS::KC_ADD) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_1, OIS::KC_NUMPAD1) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_2, OIS::KC_NUMPAD2) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_3, OIS::KC_NUMPAD3) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_0, OIS::KC_NUMPAD0) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_PERIOD, OIS::KC_DECIMAL) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F11, OIS::KC_F11) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F12, OIS::KC_F12) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F13, OIS::KC_F13) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F14, OIS::KC_F14) ); + mKeyMap.insert( KeyMap::value_type(SDLK_F15, OIS::KC_F15) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_EQUALS, OIS::KC_NUMPADEQUALS) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_DIVIDE, OIS::KC_DIVIDE) ); + mKeyMap.insert( KeyMap::value_type(SDLK_SYSREQ, OIS::KC_SYSRQ) ); + mKeyMap.insert( KeyMap::value_type(SDLK_RALT, OIS::KC_RMENU) ); + mKeyMap.insert( KeyMap::value_type(SDLK_HOME, OIS::KC_HOME) ); + mKeyMap.insert( KeyMap::value_type(SDLK_UP, OIS::KC_UP) ); + mKeyMap.insert( KeyMap::value_type(SDLK_PAGEUP, OIS::KC_PGUP) ); + mKeyMap.insert( KeyMap::value_type(SDLK_LEFT, OIS::KC_LEFT) ); + mKeyMap.insert( KeyMap::value_type(SDLK_RIGHT, OIS::KC_RIGHT) ); + mKeyMap.insert( KeyMap::value_type(SDLK_END, OIS::KC_END) ); + mKeyMap.insert( KeyMap::value_type(SDLK_DOWN, OIS::KC_DOWN) ); + mKeyMap.insert( KeyMap::value_type(SDLK_PAGEDOWN, OIS::KC_PGDOWN) ); + mKeyMap.insert( KeyMap::value_type(SDLK_INSERT, OIS::KC_INSERT) ); + mKeyMap.insert( KeyMap::value_type(SDLK_DELETE, OIS::KC_DELETE) ); + } +} diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp new file mode 100644 index 000000000..1bd8947a0 --- /dev/null +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -0,0 +1,76 @@ +#ifndef _SDL4OGRE_SDLINPUTWRAPPER_H +#define _SDL4OGRE_SDLINPUTWRAPPER_H + +#include + +#include +#include + +#include "OISCompat.h" +#include "events.h" + + + +namespace SFO +{ + class InputWrapper + { + public: + InputWrapper(SDL_Window *window, Ogre::RenderWindow* ogreWindow); + ~InputWrapper(); + + void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; } + void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; } + void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; } + void setJoyEventCallback(JoyListener* listen) { mJoyListener = listen; } + + void capture(bool windowEventsOnly); + bool isModifierHeld(SDL_Keymod mod); + bool isKeyDown(SDL_Scancode key); + + void setMouseRelative(bool relative); + bool getMouseRelative() { return mMouseRelative; } + void setGrabPointer(bool grab); + + OIS::KeyCode sdl2OISKeyCode(SDL_Keycode code); + + void warpMouse(int x, int y); + + private: + + void handleWindowEvent(const SDL_Event& evt); + + bool _handleWarpMotion(const SDL_MouseMotionEvent& evt); + void _wrapMousePointer(const SDL_MouseMotionEvent &evt); + MouseMotionEvent _packageMouseMotion(const SDL_Event& evt); + + void _setupOISKeys(); + + SFO::MouseListener* mMouseListener; + SFO::KeyListener* mKeyboardListener; + SFO::WindowListener* mWindowListener; + SFO::JoyListener* mJoyListener; + + typedef boost::unordered_map KeyMap; + KeyMap mKeyMap; + + Uint16 mWarpX; + Uint16 mWarpY; + bool mWarpCompensate; + bool mMouseRelative; + bool mWrapPointer; + bool mGrabPointer; + + Sint32 mMouseZ; + Sint32 mMouseX; + Sint32 mMouseY; + + bool mMouseInWindow; + + SDL_Window* mSDLWindow; + Ogre::RenderWindow* mOgreWindow; + }; + +} + +#endif diff --git a/extern/sdl4ogre/sdlwindowhelper.cpp b/extern/sdl4ogre/sdlwindowhelper.cpp new file mode 100644 index 000000000..f819043cf --- /dev/null +++ b/extern/sdl4ogre/sdlwindowhelper.cpp @@ -0,0 +1,119 @@ +#include "sdlwindowhelper.hpp" + +#include +#include + +#include +#include + +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE +#include "osx_utils.h" +#endif + +namespace SFO +{ + +SDLWindowHelper::SDLWindowHelper (SDL_Window* window, int w, int h, + const std::string& title, bool fullscreen, Ogre::NameValuePairList params) + : mSDLWindow(window) +{ + //get the native whnd + struct SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + + if (SDL_GetWindowWMInfo(mSDLWindow, &wmInfo) == SDL_FALSE) + throw std::runtime_error("Couldn't get WM Info!"); + + Ogre::String winHandle; + + switch (wmInfo.subsystem) + { +#ifdef WIN32 + case SDL_SYSWM_WINDOWS: + // Windows code + winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window); + break; +#elif __MACOSX__ + case SDL_SYSWM_COCOA: + //required to make OGRE play nice with our window + params.insert(std::make_pair("macAPI", "cocoa")); + params.insert(std::make_pair("macAPICocoaUseNSView", "true")); + + winHandle = Ogre::StringConverter::toString(WindowContentViewHandle(wmInfo)); + break; +#else + case SDL_SYSWM_X11: + winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.x11.window); + break; +#endif + default: + throw std::runtime_error("Unexpected WM!"); + break; + } + + /// \todo externalWindowHandle is deprecated according to the source code. Figure out a way to get parentWindowHandle + /// to work properly. On Linux/X11 it causes an occasional GLXBadDrawable error. + params.insert(std::make_pair("externalWindowHandle", winHandle)); + + mWindow = Ogre::Root::getSingleton().createRenderWindow(title, w, h, fullscreen, ¶ms); +} + +void SDLWindowHelper::setWindowIcon(const std::string &name) +{ + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().load(name, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); + if (texture.isNull()) + { + std::stringstream error; + error << "Window icon not found: " << name; + throw std::runtime_error(error.str()); + } + Ogre::Image image; + texture->convertToImage(image); + + SDL_Surface* surface = SDL_CreateRGBSurface(0,texture->getWidth(),texture->getHeight(),32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF); + + //copy the Ogre texture to an SDL surface + for(size_t x = 0; x < texture->getWidth(); ++x) + { + for(size_t y = 0; y < texture->getHeight(); ++y) + { + Ogre::ColourValue clr = image.getColourAt(x, y, 0); + + //set the pixel on the SDL surface to the same value as the Ogre texture's + int bpp = surface->format->BytesPerPixel; + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + Uint32 pixel = SDL_MapRGBA(surface->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255); + switch(bpp) { + case 1: + *p = pixel; + break; + + case 2: + *(Uint16 *)p = pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } else { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } + } + } + + SDL_SetWindowIcon(mSDLWindow, surface); + SDL_FreeSurface(surface); +} + +} diff --git a/extern/sdl4ogre/sdlwindowhelper.hpp b/extern/sdl4ogre/sdlwindowhelper.hpp new file mode 100644 index 000000000..834716b22 --- /dev/null +++ b/extern/sdl4ogre/sdlwindowhelper.hpp @@ -0,0 +1,31 @@ +#ifndef SDL4OGRE_SDLWINDOWHELPER_H +#define SDL4OGRE_SDLWINDOWHELPER_H + +#include + +namespace Ogre +{ + class RenderWindow; +} +struct SDL_Window; + +namespace SFO +{ + + /// @brief Creates an Ogre window from an SDL window and allows setting an Ogre texture as window icon + class SDLWindowHelper + { + public: + SDLWindowHelper (SDL_Window* window, int w, int h, const std::string& title, bool fullscreen, Ogre::NameValuePairList params); + void setWindowIcon(const std::string& name); + Ogre::RenderWindow* getWindow() { return mWindow; } + + private: + Ogre::RenderWindow* mWindow; + SDL_Window* mSDLWindow; + }; + +} + + +#endif diff --git a/extern/shiny/CMakeLists.txt b/extern/shiny/CMakeLists.txt index 6eadcc167..daf2a9df8 100644 --- a/extern/shiny/CMakeLists.txt +++ b/extern/shiny/CMakeLists.txt @@ -9,8 +9,6 @@ set(SHINY_LIBRARY "shiny") set(SHINY_OGREPLATFORM_LIBRARY "shiny.OgrePlatform") # Sources -file(GLOB SOURCE_FILES Main/*.cpp ) - set(SOURCE_FILES Main/Factory.cpp Main/MaterialInstance.cpp @@ -24,26 +22,6 @@ set(SOURCE_FILES Main/ShaderSet.cpp ) -if (DEFINED SHINY_USE_WAVE_SYSTEM_INSTALL) - # use system install -else() - list(APPEND SOURCE_FILES - Preprocessor/aq.cpp - Preprocessor/cpp_re.cpp - Preprocessor/instantiate_cpp_literalgrs.cpp - Preprocessor/instantiate_cpp_exprgrammar.cpp - Preprocessor/instantiate_cpp_grammar.cpp - Preprocessor/instantiate_defined_grammar.cpp - Preprocessor/instantiate_predef_macros.cpp - Preprocessor/instantiate_re2c_lexer.cpp - Preprocessor/instantiate_re2c_lexer_str.cpp - Preprocessor/token_ids.cpp - ) - - # Don't use thread-safe boost::wave. Results in a huge speed-up for the preprocessor. - add_definitions(-DBOOST_WAVE_SUPPORT_THREADING=0) -endif() - set(OGRE_PLATFORM_SOURCE_FILES Platforms/Ogre/OgreGpuProgram.cpp Platforms/Ogre/OgreMaterial.cpp @@ -57,12 +35,20 @@ file(GLOB OGRE_PLATFORM_SOURCE_FILES Platforms/Ogre/*.cpp) add_library(${SHINY_LIBRARY} STATIC ${SOURCE_FILES}) +set(SHINY_LIBRARIES ${SHINY_LIBRARY}) + if (SHINY_BUILD_OGRE_PLATFORM) add_library(${SHINY_OGREPLATFORM_LIBRARY} STATIC ${OGRE_PLATFORM_SOURCE_FILES}) + set(SHINY_LIBRARIES ${SHINY_LIBRARIES} ${SHINY_OGREPLATFORM_LIBRARY}) endif() +set(SHINY_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE) -link_directories(${CMAKE_CURRENT_BINARY_DIR}) +if (DEFINED SHINY_BUILD_MATERIAL_EDITOR) + add_subdirectory(Editor) -set(SHINY_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE) -set(SHINY_OGREPLATFORM_LIBRARY ${SHINY_OGREPLATFORM_LIBRARY} PARENT_SCOPE) + set(SHINY_BUILD_EDITOR_FLAG ${SHINY_BUILD_EDITOR_FLAG} PARENT_SCOPE) +endif() + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) +set(SHINY_LIBRARIES ${SHINY_LIBRARIES} PARENT_SCOPE) diff --git a/extern/shiny/Docs/Configurations.dox b/extern/shiny/Docs/Configurations.dox index affd91423..570e81d4a 100644 --- a/extern/shiny/Docs/Configurations.dox +++ b/extern/shiny/Docs/Configurations.dox @@ -21,7 +21,7 @@ } \endcode - \note You may also create configurations using sh::Factory::registerConfiguration. + \note You may also create configurations using sh::Factory::createConfiguration. The active Configuration is controlled by the active material scheme in Ogre. So, in order to use the configuration "reflection_targets" for your reflection renders, simply call \code diff --git a/extern/shiny/Docs/Macros.dox b/extern/shiny/Docs/Macros.dox index 0578c447f..c04ebd374 100644 --- a/extern/shiny/Docs/Macros.dox +++ b/extern/shiny/Docs/Macros.dox @@ -107,6 +107,23 @@ \section properties Property retrieval / binding + \subsection shPropertyHasValue shPropertyHasValue + + Usage: \@shPropertyHasValue(property) + + Gets replaced by 1 if the property's value is not empty, or 0 if it is empty. + Useful for checking whether an optional texture is present or not. + + Example: + \code + #if @shPropertyHasValue(specularMap) + // specular mapping code + #endif + #if @shPropertyHasValue(normalMap) + // normal mapping code + #endif + \endcode + \subsection shUniformProperty shUniformProperty Usage: \@shUniformProperty<4f|3f|2f|1f|int> (uniformName, property) @@ -130,15 +147,11 @@ Example: \code - #if @shPropertyBool(has_normal_map) + #if @shPropertyBool(has_vertex_colors) ... #endif \endcode - \subsection shPropertyNotBool shPropertyNotBool - - Same as shPropertyBool, but inverts the result (i.e. when shPropertyBool would return 0, this returns 1 and vice versa) - \subsection shPropertyString shPropertyString Retrieve a string property of the pass that this shader belongs to diff --git a/extern/shiny/Docs/Materials.dox b/extern/shiny/Docs/Materials.dox index 2dae60560..d08599a04 100644 --- a/extern/shiny/Docs/Materials.dox +++ b/extern/shiny/Docs/Materials.dox @@ -84,7 +84,10 @@ - There is no entry_point property because the entry point is always \a main. - Both profiles_cg and profiles_hlsl are a list of shader profiles. The first profile that is supported is automatically picked. GLSL does not have shader profiles. - Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader' + Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader'. + Make sure to also copy the 'core.h' file to the same location. It is included in shiny's source tree under 'Extra/'. + + Important: a newline at the end of the file is required. Many editors do this automatically or can be configured to do so. If there is no newline at the end of the file, and the last line is '#endif', you will get the rather cryptic error message " ill formed preprocessor directive: #endif" from boost::wave. \code #include "core.h" @@ -93,7 +96,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM { diff --git a/extern/shiny/Editor/Actions.cpp b/extern/shiny/Editor/Actions.cpp new file mode 100644 index 000000000..135e81987 --- /dev/null +++ b/extern/shiny/Editor/Actions.cpp @@ -0,0 +1,195 @@ +#include "Actions.hpp" + +#include "../Main/Factory.hpp" + +namespace sh +{ + + void ActionDeleteMaterial::execute() + { + sh::Factory::getInstance().destroyMaterialInstance(mName); + } + + void ActionCloneMaterial::execute() + { + sh::MaterialInstance* sourceMaterial = sh::Factory::getInstance().getMaterialInstance(mSourceName); + std::string sourceMaterialParent = static_cast(sourceMaterial->getParent())->getName(); + sh::MaterialInstance* material = sh::Factory::getInstance().createMaterialInstance( + mDestName, sourceMaterialParent); + sourceMaterial->copyAll(material, sourceMaterial, false); + + material->setSourceFile(sourceMaterial->getSourceFile()); + } + + void ActionSaveAll::execute() + { + sh::Factory::getInstance().saveAll(); + } + + void ActionChangeGlobalSetting::execute() + { + sh::Factory::getInstance().setGlobalSetting(mName, mNewValue); + } + + void ActionCreateConfiguration::execute() + { + sh::Configuration newConfiguration; + sh::Factory::getInstance().createConfiguration(mName); + } + + void ActionDeleteConfiguration::execute() + { + sh::Factory::getInstance().destroyConfiguration(mName); + } + + void ActionChangeConfiguration::execute() + { + sh::Configuration* c = sh::Factory::getInstance().getConfiguration(mName); + c->setProperty(mKey, sh::makeProperty(new sh::StringValue(mValue))); + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteConfigurationProperty::execute() + { + sh::Configuration* c = sh::Factory::getInstance().getConfiguration(mName); + c->deleteProperty(mKey); + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetMaterialProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->setProperty(mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteMaterialProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->deleteProperty(mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionCreatePass::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->createPass(); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeletePass::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + m->deletePass(mPassIndex); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetPassProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).setProperty (mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeletePassProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).deleteProperty(mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetShaderProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).mShaderProperties.setProperty (mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteShaderProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).mShaderProperties.deleteProperty (mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionSetTextureProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + m->getPasses()->at(mPassIndex).mTexUnits.at(mTextureIndex).setProperty(mKey, sh::makeProperty(mValue)); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteTextureProperty::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + m->getPasses()->at(mPassIndex).mTexUnits.at(mTextureIndex).deleteProperty(mKey); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionCreateTextureUnit::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + m->getPasses()->at(mPassIndex).createTextureUnit(mTexUnitName); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionDeleteTextureUnit::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + + m->getPasses()->at(mPassIndex).mTexUnits.erase(m->getPasses()->at(mPassIndex).mTexUnits.begin() + mTextureIndex); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionMoveTextureUnit::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + if (!mMoveUp) + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex+1); + + std::vector textures = m->getPasses()->at(mPassIndex).mTexUnits; + if (mMoveUp) + std::swap(textures[mTextureIndex-1], textures[mTextureIndex]); + else + std::swap(textures[mTextureIndex+1], textures[mTextureIndex]); + m->getPasses()->at(mPassIndex).mTexUnits = textures; + + sh::Factory::getInstance().notifyConfigurationChanged(); + } + + void ActionChangeTextureUnitName::execute() + { + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + assert (m->getPasses()->size() > mPassIndex); + assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex); + + m->getPasses()->at(mPassIndex).mTexUnits[mTextureIndex].setName(mTexUnitName); + + sh::Factory::getInstance().notifyConfigurationChanged(); + } +} diff --git a/extern/shiny/Editor/Actions.hpp b/extern/shiny/Editor/Actions.hpp new file mode 100644 index 000000000..1bbdbe5a9 --- /dev/null +++ b/extern/shiny/Editor/Actions.hpp @@ -0,0 +1,307 @@ +#ifndef SH_ACTIONS_H +#define SH_ACTIONS_H + +#include + +namespace sh +{ + + class Action + { + public: + virtual void execute() = 0; + virtual ~Action() {} + }; + + class ActionDeleteMaterial : public Action + { + public: + ActionDeleteMaterial(const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + }; + + class ActionCloneMaterial : public Action + { + public: + ActionCloneMaterial(const std::string& sourceName, const std::string& destName) + : mSourceName(sourceName), mDestName(destName) {} + + virtual void execute(); + private: + std::string mSourceName; + std::string mDestName; + }; + + class ActionSaveAll : public Action + { + public: + virtual void execute(); + }; + + class ActionChangeGlobalSetting : public Action + { + public: + ActionChangeGlobalSetting(const std::string& name, const std::string& newValue) + : mName(name), mNewValue(newValue) {} + + virtual void execute(); + private: + std::string mName; + std::string mNewValue; + }; + + // configuration + + class ActionCreateConfiguration : public Action + { + public: + ActionCreateConfiguration(const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + + }; + + class ActionDeleteConfiguration : public Action + { + public: + ActionDeleteConfiguration(const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + + }; + + class ActionChangeConfiguration : public Action + { + public: + ActionChangeConfiguration (const std::string& name, const std::string& key, const std::string& value) + : mName(name), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteConfigurationProperty : public Action + { + public: + ActionDeleteConfigurationProperty (const std::string& name, const std::string& key) + : mName(name), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + }; + + // material + + class ActionSetMaterialProperty : public Action + { + public: + ActionSetMaterialProperty (const std::string& name, const std::string& key, const std::string& value) + : mName(name), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteMaterialProperty : public Action + { + public: + ActionDeleteMaterialProperty (const std::string& name, const std::string& key) + : mName(name), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + std::string mKey; + }; + + // pass + + class ActionCreatePass : public Action + { + public: + ActionCreatePass (const std::string& name) + : mName(name) {} + + virtual void execute(); + private: + std::string mName; + }; + + class ActionDeletePass : public Action + { + public: + ActionDeletePass (const std::string& name, int passIndex) + : mName(name), mPassIndex(passIndex) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + }; + + class ActionSetPassProperty : public Action + { + public: + ActionSetPassProperty (const std::string& name, int passIndex, const std::string& key, const std::string& value) + : mName(name), mPassIndex(passIndex), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + std::string mValue; + }; + + class ActionDeletePassProperty : public Action + { + public: + ActionDeletePassProperty (const std::string& name, int passIndex, const std::string& key) + : mName(name), mPassIndex(passIndex), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + }; + + // shader + + class ActionSetShaderProperty : public Action + { + public: + ActionSetShaderProperty (const std::string& name, int passIndex, const std::string& key, const std::string& value) + : mName(name), mPassIndex(passIndex), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteShaderProperty : public Action + { + public: + ActionDeleteShaderProperty (const std::string& name, int passIndex, const std::string& key) + : mName(name), mPassIndex(passIndex), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + std::string mKey; + }; + + // texture unit + + class ActionChangeTextureUnitName : public Action + { + public: + ActionChangeTextureUnitName (const std::string& name, int passIndex, int textureIndex, const std::string& texUnitName) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mTexUnitName(texUnitName) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + std::string mTexUnitName; + }; + + class ActionCreateTextureUnit : public Action + { + public: + ActionCreateTextureUnit (const std::string& name, int passIndex, const std::string& texUnitName) + : mName(name), mPassIndex(passIndex), mTexUnitName(texUnitName) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + std::string mTexUnitName; + }; + + class ActionDeleteTextureUnit : public Action + { + public: + ActionDeleteTextureUnit (const std::string& name, int passIndex, int textureIndex) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + }; + + class ActionMoveTextureUnit : public Action + { + public: + ActionMoveTextureUnit (const std::string& name, int passIndex, int textureIndex, bool moveUp) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mMoveUp(moveUp) {} + + virtual void execute(); + + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + bool mMoveUp; + }; + + class ActionSetTextureProperty : public Action + { + public: + ActionSetTextureProperty (const std::string& name, int passIndex, int textureIndex, const std::string& key, const std::string& value) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mKey(key), mValue(value) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + std::string mKey; + std::string mValue; + }; + + class ActionDeleteTextureProperty : public Action + { + public: + ActionDeleteTextureProperty (const std::string& name, int passIndex, int textureIndex, const std::string& key) + : mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mKey(key) {} + + virtual void execute(); + private: + std::string mName; + int mPassIndex; + int mTextureIndex; + std::string mKey; + }; + +} + +#endif diff --git a/extern/shiny/Editor/AddPropertyDialog.cpp b/extern/shiny/Editor/AddPropertyDialog.cpp new file mode 100644 index 000000000..71b47feb1 --- /dev/null +++ b/extern/shiny/Editor/AddPropertyDialog.cpp @@ -0,0 +1,31 @@ +#include "AddPropertyDialog.hpp" +#include "ui_addpropertydialog.h" + +AddPropertyDialog::AddPropertyDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::AddPropertyDialog) + , mType(0) +{ + ui->setupUi(this); + + connect(ui->buttonBox, SIGNAL(accepted()), + this, SLOT(accepted())); + connect(ui->buttonBox, SIGNAL(rejected()), + this, SLOT(rejected())); +} + +void AddPropertyDialog::accepted() +{ + mName = ui->lineEdit->text(); + mType = ui->comboBox->currentIndex(); +} + +void AddPropertyDialog::rejected() +{ + mName = ""; +} + +AddPropertyDialog::~AddPropertyDialog() +{ + delete ui; +} diff --git a/extern/shiny/Editor/AddPropertyDialog.h b/extern/shiny/Editor/AddPropertyDialog.h new file mode 100644 index 000000000..c1d2c960b --- /dev/null +++ b/extern/shiny/Editor/AddPropertyDialog.h @@ -0,0 +1,22 @@ +#ifndef ADDPROPERTYDIALOG_H +#define ADDPROPERTYDIALOG_H + +#include + +namespace Ui { +class AddPropertyDialog; +} + +class AddPropertyDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddPropertyDialog(QWidget *parent = 0); + ~AddPropertyDialog(); + +private: + Ui::AddPropertyDialog *ui; +}; + +#endif // ADDPROPERTYDIALOG_H diff --git a/extern/shiny/Editor/AddPropertyDialog.hpp b/extern/shiny/Editor/AddPropertyDialog.hpp new file mode 100644 index 000000000..b4e19b087 --- /dev/null +++ b/extern/shiny/Editor/AddPropertyDialog.hpp @@ -0,0 +1,29 @@ +#ifndef ADDPROPERTYDIALOG_H +#define ADDPROPERTYDIALOG_H + +#include + +namespace Ui { +class AddPropertyDialog; +} + +class AddPropertyDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddPropertyDialog(QWidget *parent = 0); + ~AddPropertyDialog(); + + int mType; + QString mName; + +public slots: + void accepted(); + void rejected(); + +private: + Ui::AddPropertyDialog *ui; +}; + +#endif // ADDPROPERTYDIALOG_H diff --git a/extern/shiny/Editor/CMakeLists.txt b/extern/shiny/Editor/CMakeLists.txt new file mode 100644 index 000000000..eead159f0 --- /dev/null +++ b/extern/shiny/Editor/CMakeLists.txt @@ -0,0 +1,61 @@ +set(SHINY_EDITOR_LIBRARY "shiny.Editor") + +find_package(Qt4) + +if (QT_FOUND) + + add_definitions(-DSHINY_BUILD_MATERIAL_EDITOR=1) + set (SHINY_BUILD_EDITOR_FLAG -DSHINY_BUILD_MATERIAL_EDITOR=1 PARENT_SCOPE) + + set(QT_USE_QTGUI 1) + + # Headers that must be preprocessed + set(SHINY_EDITOR_HEADER_MOC + MainWindow.hpp + NewMaterialDialog.hpp + AddPropertyDialog.hpp + PropertySortModel.hpp + ) + + set(SHINY_EDITOR_UI + mainwindow.ui + newmaterialdialog.ui + addpropertydialog.ui + ) + + QT4_WRAP_CPP(MOC_SRCS ${SHINY_EDITOR_HEADER_MOC}) + QT4_WRAP_UI(UI_HDRS ${SHINY_EDITOR_UI}) + + set(SOURCE_FILES + NewMaterialDialog.cpp + AddPropertyDialog.cpp + ColoredTabWidget.hpp + MainWindow.cpp + Editor.cpp + Actions.cpp + Query.cpp + PropertySortModel.cpp + ${SHINY_EDITOR_UI} # Just to have them in the IDE's file explorer + ) + + include(${QT_USE_FILE}) + + set (CMAKE_INCLUDE_CURRENT_DIR "true") + + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + + add_library(${SHINY_EDITOR_LIBRARY} STATIC ${SOURCE_FILES} ${MOC_SRCS} ${UI_HDRS}) + + set(SHINY_LIBRARIES ${SHINY_LIBRARIES} + ${SHINY_EDITOR_LIBRARY} + ${QT_LIBRARIES} + ) + set(SHINY_LIBRARIES ${SHINY_LIBRARIES} PARENT_SCOPE) + +else (QT_FOUND) + + add_definitions(-DSHINY_BUILD_MATERIAL_EDITOR=0) + set (SHINY_BUILD_EDITOR_FLAG -DSHINY_BUILD_MATERIAL_EDITOR=0 PARENT_SCOPE) + message(STATUS "QT4 was not found. You will not be able to use the material editor.") + +endif(QT_FOUND) diff --git a/extern/shiny/Editor/ColoredTabWidget.hpp b/extern/shiny/Editor/ColoredTabWidget.hpp new file mode 100644 index 000000000..0bf30f6dd --- /dev/null +++ b/extern/shiny/Editor/ColoredTabWidget.hpp @@ -0,0 +1,24 @@ +#ifndef SHINY_EDITOR_COLOREDTABWIDGET_H +#define SHINY_EDITOR_COLOREDTABWIDGET_H + +#include + +namespace sh +{ + +/// Makes tabBar() public to allow changing tab title colors. +class ColoredTabWidget : public QTabWidget +{ +public: + ColoredTabWidget(QWidget* parent = 0) + : QTabWidget(parent) {} + + QTabBar* tabBar() + { + return QTabWidget::tabBar(); + } +}; + +} + +#endif diff --git a/extern/shiny/Editor/Editor.cpp b/extern/shiny/Editor/Editor.cpp new file mode 100644 index 000000000..8c58d0e66 --- /dev/null +++ b/extern/shiny/Editor/Editor.cpp @@ -0,0 +1,117 @@ +#include "Editor.hpp" + + +#include +#include + +#include + +#include "../Main/Factory.hpp" + +#include "MainWindow.hpp" + +namespace sh +{ + + Editor::Editor() + : mMainWindow(NULL) + , mApplication(NULL) + , mInitialized(false) + , mThread(NULL) + { + } + + Editor::~Editor() + { + if (mMainWindow) + mMainWindow->mRequestExit = true; + + if (mThread) + mThread->join(); + delete mThread; + } + + void Editor::show() + { + if (!mInitialized) + { + mInitialized = true; + + mThread = new boost::thread(boost::bind(&Editor::runThread, this)); + } + else + { + if (mMainWindow) + mMainWindow->mRequestShowWindow = true; + } + } + + void Editor::runThread() + { + int argc = 0; + char** argv = NULL; + mApplication = new QApplication(argc, argv); + mApplication->setQuitOnLastWindowClosed(false); + mMainWindow = new MainWindow(); + mMainWindow->mSync = &mSync; + mMainWindow->show(); + + mApplication->exec(); + + delete mApplication; + } + + void Editor::update() + { + sh::Factory::getInstance().doMonitorShaderFiles(); + + if (!mMainWindow) + return; + + + { + boost::mutex::scoped_lock lock(mSync.mActionMutex); + + // execute pending actions + while (mMainWindow->mActionQueue.size()) + { + Action* action = mMainWindow->mActionQueue.front(); + action->execute(); + delete action; + mMainWindow->mActionQueue.pop(); + } + } + { + boost::mutex::scoped_lock lock(mSync.mQueryMutex); + + // execute pending queries + for (std::vector::iterator it = mMainWindow->mQueries.begin(); it != mMainWindow->mQueries.end(); ++it) + { + Query* query = *it; + if (!query->mDone) + query->execute(); + } + } + + boost::mutex::scoped_lock lock2(mSync.mUpdateMutex); + + // update the list of materials + mMainWindow->mState.mMaterialList.clear(); + sh::Factory::getInstance().listMaterials(mMainWindow->mState.mMaterialList); + + // update global settings + mMainWindow->mState.mGlobalSettingsMap.clear(); + sh::Factory::getInstance().listGlobalSettings(mMainWindow->mState.mGlobalSettingsMap); + + // update configuration list + mMainWindow->mState.mConfigurationList.clear(); + sh::Factory::getInstance().listConfigurationNames(mMainWindow->mState.mConfigurationList); + + // update shader list + mMainWindow->mState.mShaderSets.clear(); + sh::Factory::getInstance().listShaderSets(mMainWindow->mState.mShaderSets); + + mMainWindow->mState.mErrors += sh::Factory::getInstance().getErrorLog(); + } + +} diff --git a/extern/shiny/Editor/Editor.hpp b/extern/shiny/Editor/Editor.hpp new file mode 100644 index 000000000..2b1e8040d --- /dev/null +++ b/extern/shiny/Editor/Editor.hpp @@ -0,0 +1,73 @@ +#ifndef SH_EDITOR_H +#define SH_EDITOR_H + +#if SHINY_BUILD_MATERIAL_EDITOR +class QApplication; + +#include +#include + +namespace boost +{ + class thread; +} + +namespace sh +{ + class MainWindow; + + struct SynchronizationState + { + boost::mutex mUpdateMutex; + boost::mutex mActionMutex; + boost::mutex mQueryMutex; + }; + + class Editor + { + public: + + Editor(); + ~Editor(); + + void show(); + void update(); + + + private: + bool mInitialized; + + MainWindow* mMainWindow; + QApplication* mApplication; + + SynchronizationState mSync; + + boost::thread* mThread; + + void runThread(); + + void processShowWindow(); + }; + +} + +#else + +// Dummy implementation, so that the user's code does not have to be polluted with #ifdefs +namespace sh +{ + + class Editor + { + public: + Editor() {} + ~Editor() {} + void show() {} + void update() {} + + }; +} + +#endif + +#endif diff --git a/extern/shiny/Editor/MainWindow.cpp b/extern/shiny/Editor/MainWindow.cpp new file mode 100644 index 000000000..a2c52dc2f --- /dev/null +++ b/extern/shiny/Editor/MainWindow.cpp @@ -0,0 +1,952 @@ +#include "MainWindow.hpp" +#include "ui_mainwindow.h" + +#include + +#include +#include + +#include +#include + +#include "Editor.hpp" +#include "ColoredTabWidget.hpp" +#include "AddPropertyDialog.hpp" + +sh::MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , mRequestShowWindow(false) + , mRequestExit(false) + , mIgnoreGlobalSettingChange(false) + , mIgnoreConfigurationChange(false) + , mIgnoreMaterialChange(false) + , mIgnoreMaterialPropertyChange(false) +{ + ui->setupUi(this); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(onIdle())); + timer->start(50); + + QList sizes; + sizes << 250; + sizes << 550; + ui->splitter->setSizes(sizes); + + mMaterialModel = new QStringListModel(this); + + mMaterialProxyModel = new QSortFilterProxyModel(this); + mMaterialProxyModel->setSourceModel(mMaterialModel); + mMaterialProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mMaterialProxyModel->setDynamicSortFilter(true); + mMaterialProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + ui->materialList->setModel(mMaterialProxyModel); + ui->materialList->setSelectionMode(QAbstractItemView::SingleSelection); + ui->materialList->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->materialList->setAlternatingRowColors(true); + + connect(ui->materialList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(onMaterialSelectionChanged(QModelIndex,QModelIndex))); + + mMaterialPropertyModel = new QStandardItemModel(0, 2, this); + mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + connect(mMaterialPropertyModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(onMaterialPropertyChanged(QStandardItem*))); + + mMaterialSortModel = new PropertySortModel(this); + mMaterialSortModel->setSourceModel(mMaterialPropertyModel); + mMaterialSortModel->setDynamicSortFilter(true); + mMaterialSortModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + ui->materialView->setModel(mMaterialSortModel); + ui->materialView->setContextMenuPolicy(Qt::CustomContextMenu); + ui->materialView->setAlternatingRowColors(true); + ui->materialView->setSortingEnabled(true); + connect(ui->materialView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(onContextMenuRequested(QPoint))); + + mGlobalSettingsModel = new QStandardItemModel(0, 2, this); + mGlobalSettingsModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mGlobalSettingsModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + connect(mGlobalSettingsModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(onGlobalSettingChanged(QStandardItem*))); + + ui->globalSettingsView->setModel(mGlobalSettingsModel); + ui->globalSettingsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + ui->globalSettingsView->verticalHeader()->hide(); + ui->globalSettingsView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + ui->globalSettingsView->setSelectionMode(QAbstractItemView::SingleSelection); + + ui->configurationList->setSelectionMode(QAbstractItemView::SingleSelection); + ui->configurationList->setEditTriggers(QAbstractItemView::NoEditTriggers); + connect(ui->configurationList, SIGNAL(currentTextChanged(QString)), + this, SLOT(onConfigurationSelectionChanged(QString))); + + mConfigurationModel = new QStandardItemModel(0, 2, this); + mConfigurationModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mConfigurationModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + connect(mConfigurationModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(onConfigurationChanged(QStandardItem*))); + + ui->configurationView->setModel(mConfigurationModel); + ui->configurationView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + ui->configurationView->verticalHeader()->hide(); + ui->configurationView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + ui->configurationView->setSelectionMode(QAbstractItemView::SingleSelection); +} + +sh::MainWindow::~MainWindow() +{ + delete ui; +} + +void sh::MainWindow::closeEvent(QCloseEvent *event) +{ + this->hide(); + event->ignore(); +} + +void sh::MainWindow::onIdle() +{ + if (mRequestShowWindow) + { + mRequestShowWindow = false; + show(); + } + + if (mRequestExit) + { + QApplication::exit(); + return; + } + + boost::mutex::scoped_lock lock(mSync->mUpdateMutex); + + + mIgnoreMaterialChange = true; + QString selected; + + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + if (selectedIndex.isValid()) + selected = mMaterialModel->data(selectedIndex, Qt::DisplayRole).toString(); + + QStringList list; + + for (std::vector::const_iterator it = mState.mMaterialList.begin(); it != mState.mMaterialList.end(); ++it) + { + list.push_back(QString::fromStdString(*it)); + } + + if (mMaterialModel->stringList() != list) + { + mMaterialModel->setStringList(list); + + // quick hack to keep our selection when the model has changed + if (!selected.isEmpty()) + for (int i=0; irowCount(); ++i) + { + const QModelIndex& index = mMaterialModel->index(i,0); + if (mMaterialModel->data(index, Qt::DisplayRole).toString() == selected) + { + ui->materialList->setCurrentIndex(index); + break; + } + } + } + mIgnoreMaterialChange = false; + + mIgnoreGlobalSettingChange = true; + for (std::map::const_iterator it = mState.mGlobalSettingsMap.begin(); + it != mState.mGlobalSettingsMap.end(); ++it) + { + QList list = mGlobalSettingsModel->findItems(QString::fromStdString(it->first)); + if (!list.empty()) // item was already there + { + // if it changed, set the value column + if (mGlobalSettingsModel->data(mGlobalSettingsModel->index(list.front()->row(), 1)).toString() + != QString::fromStdString(it->second)) + { + mGlobalSettingsModel->setItem(list.front()->row(), 1, new QStandardItem(QString::fromStdString(it->second))); + } + } + else // item wasn't there; insert new row + { + QList toAdd; + QStandardItem* name = new QStandardItem(QString::fromStdString(it->first)); + name->setFlags(name->flags() &= ~Qt::ItemIsEditable); + QStandardItem* value = new QStandardItem(QString::fromStdString(it->second)); + toAdd.push_back(name); + toAdd.push_back(value); + mGlobalSettingsModel->appendRow(toAdd); + } + } + mIgnoreGlobalSettingChange = false; + + + mIgnoreConfigurationChange = true; + QList selected_ = ui->configurationList->selectedItems(); + QString selectedStr; + if (selected_.size()) + selectedStr = selected_.front()->text(); + + ui->configurationList->clear(); + + for (std::vector::const_iterator it = mState.mConfigurationList.begin(); it != mState.mConfigurationList.end(); ++it) + ui->configurationList->addItem(QString::fromStdString(*it)); + + if (!selectedStr.isEmpty()) + for (int i=0; iconfigurationList->count(); ++i) + { + if (ui->configurationList->item(i)->text() == selectedStr) + { + ui->configurationList->setCurrentItem(ui->configurationList->item(i), QItemSelectionModel::ClearAndSelect); + } + } + + mIgnoreConfigurationChange = false; + + if (!mState.mErrors.empty()) + { + ui->errorLog->append(QString::fromStdString(mState.mErrors)); + mState.mErrors = ""; + QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::Link); + ui->tabWidget->tabBar()->setTabTextColor(3, color); + } + + + // process query results + boost::mutex::scoped_lock lock2(mSync->mQueryMutex); + for (std::vector::iterator it = mQueries.begin(); it != mQueries.end();) + { + if ((*it)->mDone) + { + if (typeid(**it) == typeid(ConfigurationQuery)) + buildConfigurationModel(static_cast(*it)); + else if (typeid(**it) == typeid(MaterialQuery)) + buildMaterialModel(static_cast(*it)); + else if (typeid(**it) == typeid(MaterialPropertyQuery)) + { + MaterialPropertyQuery* q = static_cast(*it); + mIgnoreMaterialPropertyChange = true; + if (getSelectedMaterial().toStdString() == q->mName) + { + for (int i=0; irowCount(); ++i) + { + if (mMaterialPropertyModel->item(i,0)->text() == QString::fromStdString(q->mPropertyName)) + { + mMaterialPropertyModel->item(i,1)->setText(QString::fromStdString(q->mValue)); + if (mMaterialPropertyModel->item(i,1)->isCheckable()) + mMaterialPropertyModel->item(i,1)->setCheckState ((q->mValue == "true") + ? Qt::Checked : Qt::Unchecked); + } + } + } + mIgnoreMaterialPropertyChange = false; + } + + delete *it; + it = mQueries.erase(it); + } + else + ++it; + } +} + +void sh::MainWindow::onMaterialSelectionChanged (const QModelIndex & current, const QModelIndex & previous) +{ + if (mIgnoreMaterialChange) + return; + + QString name = getSelectedMaterial(); + if (!name.isEmpty()) + requestQuery(new sh::MaterialQuery(name.toStdString())); +} + +QString sh::MainWindow::getSelectedMaterial() +{ + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + if (!selectedIndex.isValid()) + return QString(""); + + return mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString(); +} + +void sh::MainWindow::onConfigurationSelectionChanged (const QString& current) +{ + if (mIgnoreConfigurationChange) + return; + requestQuery(new sh::ConfigurationQuery(current.toStdString())); +} + +void sh::MainWindow::onGlobalSettingChanged(QStandardItem *item) +{ + if (mIgnoreGlobalSettingChange) + return; // we are only interested in changes by the user, not by the backend. + + std::string name = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 0)).toString().toStdString(); + std::string value = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 1)).toString().toStdString(); + + queueAction(new sh::ActionChangeGlobalSetting(name, value)); +} + +void sh::MainWindow::onConfigurationChanged (QStandardItem* item) +{ + QList items = ui->configurationList->selectedItems(); + if (items.size()) + { + std::string name = items.front()->text().toStdString(); + std::string key = mConfigurationModel->data(mConfigurationModel->index(item->row(), 0)).toString().toStdString(); + std::string value = mConfigurationModel->data(mConfigurationModel->index(item->row(), 1)).toString().toStdString(); + + queueAction(new sh::ActionChangeConfiguration(name, key, value)); + + requestQuery(new sh::ConfigurationQuery(name)); + } +} + +void sh::MainWindow::on_lineEdit_textEdited(const QString &arg1) +{ + mMaterialProxyModel->setFilterFixedString(arg1); +} + +void sh::MainWindow::on_actionSave_triggered() +{ + queueAction (new sh::ActionSaveAll()); +} + +void sh::MainWindow::on_actionNewMaterial_triggered() +{ + +} + +void sh::MainWindow::on_actionDeleteMaterial_triggered() +{ + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString(); + + queueAction (new sh::ActionDeleteMaterial(name.toStdString())); +} + +void sh::MainWindow::queueAction(Action* action) +{ + boost::mutex::scoped_lock lock(mSync->mActionMutex); + mActionQueue.push(action); +} + +void sh::MainWindow::requestQuery(Query *query) +{ + boost::mutex::scoped_lock lock(mSync->mActionMutex); + mQueries.push_back(query); +} + +void sh::MainWindow::on_actionQuit_triggered() +{ + hide(); +} + +void sh::MainWindow::on_actionNewConfiguration_triggered() +{ + QInputDialog dialog(this); + + QString text = QInputDialog::getText(this, tr("New Configuration"), + tr("Configuration name:")); + + if (!text.isEmpty()) + { + queueAction(new ActionCreateConfiguration(text.toStdString())); + } +} + +void sh::MainWindow::on_actionDeleteConfiguration_triggered() +{ + QList items = ui->configurationList->selectedItems(); + if (items.size()) + queueAction(new ActionDeleteConfiguration(items.front()->text().toStdString())); +} + +void sh::MainWindow::on_actionDeleteConfigurationProperty_triggered() +{ + QList items = ui->configurationList->selectedItems(); + if (items.empty()) + return; + std::string configurationName = items.front()->text().toStdString(); + + QModelIndex current = ui->configurationView->currentIndex(); + if (!current.isValid()) + return; + + std::string propertyName = mConfigurationModel->data(mConfigurationModel->index(current.row(), 0)).toString().toStdString(); + + queueAction(new sh::ActionDeleteConfigurationProperty(configurationName, propertyName)); + requestQuery(new sh::ConfigurationQuery(configurationName)); +} + +void sh::MainWindow::on_actionCloneMaterial_triggered() +{ + QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex(); + QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString(); + if (name.isEmpty()) + return; + + QInputDialog dialog(this); + + QString text = QInputDialog::getText(this, tr("Clone material"), + tr("Name:")); + + if (!text.isEmpty()) + { + queueAction(new ActionCloneMaterial(name.toStdString(), text.toStdString())); + } +} + +void sh::MainWindow::onContextMenuRequested(const QPoint &point) +{ + QPoint globalPos = ui->materialView->viewport()->mapToGlobal(point); + + QMenu menu; + + QList actions; + actions.push_back(ui->actionNewProperty); + actions.push_back(ui->actionDeleteProperty); + actions.push_back(ui->actionCreatePass); + actions.push_back(ui->actionCreateTextureUnit); + menu.addActions(actions); + + menu.exec(globalPos); +} + +void sh::MainWindow::getContext(QModelIndex index, int* passIndex, int* textureIndex, bool* isInPass, bool* isInTextureUnit) +{ + if (passIndex) + { + *passIndex = 0; + if (isInPass) + *isInPass = false; + QModelIndex passModelIndex = index; + // go up until we find the pass item. + while (getPropertyKey(passModelIndex) != "pass" && passModelIndex.isValid()) + passModelIndex = passModelIndex.parent(); + + if (passModelIndex.isValid()) + { + if (passModelIndex.column() != 0) + passModelIndex = passModelIndex.parent().child(passModelIndex.row(), 0); + for (int i=0; irowCount(); ++i) + { + if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass")) + { + if (mMaterialPropertyModel->index(i, 0) == passModelIndex) + { + if (isInPass) + *isInPass = true; + break; + } + ++(*passIndex); + } + } + } + } + if (textureIndex) + { + *textureIndex = 0; + if (isInTextureUnit) + *isInTextureUnit = false; + QModelIndex texModelIndex = index; + // go up until we find the texture_unit item. + while (getPropertyKey(texModelIndex) != "texture_unit" && texModelIndex.isValid()) + texModelIndex = texModelIndex.parent(); + if (texModelIndex.isValid()) + { + if (texModelIndex.column() != 0) + texModelIndex = texModelIndex.parent().child(texModelIndex.row(), 0); + for (int i=0; irowCount(texModelIndex.parent()); ++i) + { + if (texModelIndex.parent().child(i, 0).data().toString() == QString("texture_unit")) + { + if (texModelIndex.parent().child(i, 0) == texModelIndex) + { + if (isInTextureUnit) + *isInTextureUnit = true; + break; + } + ++(*textureIndex); + } + } + } + } +} + +std::string sh::MainWindow::getPropertyKey(QModelIndex index) +{ + if (!index.parent().isValid()) + return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 0)).toString().toStdString(); + else + return index.parent().child(index.row(), 0).data().toString().toStdString(); +} + +std::string sh::MainWindow::getPropertyValue(QModelIndex index) +{ + if (!index.parent().isValid()) + return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 1)).toString().toStdString(); + else + return index.parent().child(index.row(), 1).data().toString().toStdString(); +} + +void sh::MainWindow::onMaterialPropertyChanged(QStandardItem *item) +{ + if (mIgnoreMaterialPropertyChange) + return; + + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + // handle checkboxes being checked/unchecked + std::string value = getPropertyValue(item->index()); + if (item->data(Qt::UserRole).toInt() == MaterialProperty::Boolean) + { + if (item->checkState() == Qt::Checked && value != "true") + value = "true"; + else if (item->checkState() == Qt::Unchecked && value == "true") + value = "false"; + item->setText(QString::fromStdString(value)); + } + + // handle inherited properties being changed, i.e. overridden by the current (derived) material + if (item->data(Qt::UserRole+1).toInt() == MaterialProperty::Inherited_Unchanged) + { + QColor normalColor = ui->materialView->palette().color(QPalette::Normal, QPalette::WindowText); + mIgnoreMaterialPropertyChange = true; + mMaterialPropertyModel->item(item->index().row(), 0) + ->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1); + mMaterialPropertyModel->item(item->index().row(), 0) + ->setData(normalColor, Qt::ForegroundRole); + mMaterialPropertyModel->item(item->index().row(), 1) + ->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1); + mMaterialPropertyModel->item(item->index().row(), 1) + ->setData(normalColor, Qt::ForegroundRole); + mIgnoreMaterialPropertyChange = false; + + ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(item->index())); + } + + if (!item->index().parent().isValid()) + { + // top level material property + queueAction(new ActionSetMaterialProperty( + material.toStdString(), getPropertyKey(item->index()), value)); + } + else if (getPropertyKey(item->index()) == "texture_unit") + { + // texture unit name changed + int passIndex, textureIndex; + getContext(item->index(), &passIndex, &textureIndex); + std::cout << "passIndex " << passIndex << " " << textureIndex << std::endl; + + queueAction(new ActionChangeTextureUnitName( + material.toStdString(), passIndex, textureIndex, value)); + + } + else if (item->index().parent().data().toString() == "pass") + { + // pass property + int passIndex; + getContext(item->index(), &passIndex, NULL); + /// \todo if shaders are changed, check that the material provides all properties needed by the shader + queueAction(new ActionSetPassProperty( + material.toStdString(), passIndex, getPropertyKey(item->index()), value)); + } + else if (item->index().parent().data().toString() == "shader_properties") + { + // shader property + int passIndex; + getContext(item->index(), &passIndex, NULL); + queueAction(new ActionSetShaderProperty( + material.toStdString(), passIndex, getPropertyKey(item->index()), value)); + } + else if (item->index().parent().data().toString() == "texture_unit") + { + // texture property + int passIndex, textureIndex; + getContext(item->index(), &passIndex, &textureIndex); + queueAction(new ActionSetTextureProperty( + material.toStdString(), passIndex, textureIndex, getPropertyKey(item->index()), value)); + } +} + +void sh::MainWindow::buildMaterialModel(MaterialQuery *data) +{ + mMaterialPropertyModel->clear(); + + mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name"))); + mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value"))); + + for (std::map::const_iterator it = data->mProperties.begin(); + it != data->mProperties.end(); ++it) + { + addProperty(mMaterialPropertyModel->invisibleRootItem(), it->first, it->second); + } + + for (std::vector::iterator it = data->mPasses.begin(); + it != data->mPasses.end(); ++it) + { + QStandardItem* passItem = new QStandardItem (QString("pass")); + passItem->setFlags(passItem->flags() &= ~Qt::ItemIsEditable); + passItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + + if (it->mShaderProperties.size()) + { + QStandardItem* shaderPropertiesItem = new QStandardItem (QString("shader_properties")); + shaderPropertiesItem->setFlags(shaderPropertiesItem->flags() &= ~Qt::ItemIsEditable); + shaderPropertiesItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + + for (std::map::iterator pit = it->mShaderProperties.begin(); + pit != it->mShaderProperties.end(); ++pit) + { + addProperty(shaderPropertiesItem, pit->first, pit->second); + } + passItem->appendRow(shaderPropertiesItem); + } + + for (std::map::iterator pit = it->mProperties.begin(); + pit != it->mProperties.end(); ++pit) + { + addProperty(passItem, pit->first, pit->second); + } + + for (std::vector::iterator tIt = it->mTextureUnits.begin(); + tIt != it->mTextureUnits.end(); ++tIt) + { + QStandardItem* unitItem = new QStandardItem (QString("texture_unit")); + unitItem->setFlags(unitItem->flags() &= ~Qt::ItemIsEditable); + unitItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + QStandardItem* nameItem = new QStandardItem (QString::fromStdString(tIt->mName)); + nameItem->setData(QVariant(static_cast(MaterialProperty::Object)), Qt::UserRole); + + QList texUnit; + texUnit << unitItem << nameItem; + + for (std::map::iterator pit = tIt->mProperties.begin(); + pit != tIt->mProperties.end(); ++pit) + { + addProperty(unitItem, pit->first, pit->second); + } + + passItem->appendRow(texUnit); + } + + QList toAdd; + toAdd << passItem; + toAdd << new QStandardItem(QString("")); + mMaterialPropertyModel->appendRow(toAdd); + } + + ui->materialView->expandAll(); + ui->materialView->resizeColumnToContents(0); + ui->materialView->resizeColumnToContents(1); +} + +void sh::MainWindow::addProperty(QStandardItem *parent, const std::string &key, MaterialProperty value, bool scrollTo) +{ + QList toAdd; + QStandardItem* keyItem = new QStandardItem(QString::fromStdString(key)); + keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable); + keyItem->setData(QVariant(value.mType), Qt::UserRole); + keyItem->setData(QVariant(value.mSource), Qt::UserRole+1); + toAdd.push_back(keyItem); + + QStandardItem* valueItem = NULL; + if (value.mSource != MaterialProperty::None) + { + valueItem = new QStandardItem(QString::fromStdString(value.mValue)); + valueItem->setData(QVariant(value.mType), Qt::UserRole); + valueItem->setData(QVariant(value.mSource), Qt::UserRole+1); + toAdd.push_back(valueItem); + } + + + if (value.mSource == MaterialProperty::Inherited_Unchanged) + { + QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText); + keyItem->setData(color, Qt::ForegroundRole); + if (valueItem) + valueItem->setData(color, Qt::ForegroundRole); + } + if (value.mType == MaterialProperty::Boolean && valueItem) + { + valueItem->setCheckable(true); + valueItem->setCheckState((value.mValue == "true") ? Qt::Checked : Qt::Unchecked); + } + + parent->appendRow(toAdd); + + if (scrollTo) + ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(keyItem->index())); +} + +void sh::MainWindow::buildConfigurationModel(ConfigurationQuery *data) +{ + while (mConfigurationModel->rowCount()) + mConfigurationModel->removeRow(0); + for (std::map::iterator it = data->mProperties.begin(); + it != data->mProperties.end(); ++it) + { + QList toAdd; + QStandardItem* name = new QStandardItem(QString::fromStdString(it->first)); + name->setFlags(name->flags() &= ~Qt::ItemIsEditable); + QStandardItem* value = new QStandardItem(QString::fromStdString(it->second)); + toAdd.push_back(name); + toAdd.push_back(value); + mConfigurationModel->appendRow(toAdd); + } + + // add items that are in global settings, but not in this configuration (with a "inactive" color) + for (std::map::const_iterator it = mState.mGlobalSettingsMap.begin(); + it != mState.mGlobalSettingsMap.end(); ++it) + { + if (data->mProperties.find(it->first) == data->mProperties.end()) + { + QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText); + QList toAdd; + QStandardItem* name = new QStandardItem(QString::fromStdString(it->first)); + name->setFlags(name->flags() &= ~Qt::ItemIsEditable); + name->setData(color, Qt::ForegroundRole); + QStandardItem* value = new QStandardItem(QString::fromStdString(it->second)); + value->setData(color, Qt::ForegroundRole); + toAdd.push_back(name); + toAdd.push_back(value); + mConfigurationModel->appendRow(toAdd); + } + } +} + +void sh::MainWindow::on_actionCreatePass_triggered() +{ + QString material = getSelectedMaterial(); + if (!material.isEmpty()) + { + addProperty(mMaterialPropertyModel->invisibleRootItem(), + "pass", MaterialProperty("", MaterialProperty::Object, MaterialProperty::None), true); + + queueAction (new ActionCreatePass(material.toStdString())); + } +} + +void sh::MainWindow::on_actionDeleteProperty_triggered() +{ + QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex()); + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + mIgnoreMaterialPropertyChange = true; + + if (getPropertyKey(selectedIndex) == "pass") + { + // delete whole pass + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + if (passIndex == 0) + { + QMessageBox msgBox; + msgBox.setText("The first pass can not be deleted."); + msgBox.exec(); + } + else + { + queueAction(new ActionDeletePass(material.toStdString(), passIndex)); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + } + else if (getPropertyKey(selectedIndex) == "texture_unit") + { + // delete whole texture unit + int passIndex, textureIndex; + getContext(selectedIndex, &passIndex, &textureIndex); + queueAction(new ActionDeleteTextureUnit(material.toStdString(), passIndex, textureIndex)); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + else if (!selectedIndex.parent().isValid()) + { + // top level material property + MaterialProperty::Source source = static_cast( + mMaterialPropertyModel->itemFromIndex(selectedIndex)->data(Qt::UserRole+1).toInt()); + if (source == MaterialProperty::Inherited_Unchanged) + { + QMessageBox msgBox; + msgBox.setText("Inherited properties can not be deleted."); + msgBox.exec(); + } + else + { + queueAction(new ActionDeleteMaterialProperty( + material.toStdString(), getPropertyKey(selectedIndex))); + std::cout << "source is " << source << std::endl; + if (source == MaterialProperty::Inherited_Changed) + { + QColor inactiveColor = ui->materialView->palette().color(QPalette::Disabled, QPalette::WindowText); + mMaterialPropertyModel->item(selectedIndex.row(), 0) + ->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1); + mMaterialPropertyModel->item(selectedIndex.row(), 0) + ->setData(inactiveColor, Qt::ForegroundRole); + mMaterialPropertyModel->item(selectedIndex.row(), 1) + ->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1); + mMaterialPropertyModel->item(selectedIndex.row(), 1) + ->setData(inactiveColor, Qt::ForegroundRole); + + // make sure to update the property's value + requestQuery(new sh::MaterialPropertyQuery(material.toStdString(), getPropertyKey(selectedIndex))); + } + else + mMaterialPropertyModel->removeRow(selectedIndex.row()); + } + } + else if (selectedIndex.parent().data().toString() == "pass") + { + // pass property + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + queueAction(new ActionDeletePassProperty( + material.toStdString(), passIndex, getPropertyKey(selectedIndex))); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + else if (selectedIndex.parent().data().toString() == "shader_properties") + { + // shader property + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + queueAction(new ActionDeleteShaderProperty( + material.toStdString(), passIndex, getPropertyKey(selectedIndex))); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + else if (selectedIndex.parent().data().toString() == "texture_unit") + { + // texture property + int passIndex, textureIndex; + getContext(selectedIndex, &passIndex, &textureIndex); + queueAction(new ActionDeleteTextureProperty( + material.toStdString(), passIndex, textureIndex, getPropertyKey(selectedIndex))); + mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent()); + } + mIgnoreMaterialPropertyChange = false; +} + +void sh::MainWindow::on_actionNewProperty_triggered() +{ + QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex()); + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + AddPropertyDialog* dialog = new AddPropertyDialog(this); + dialog->exec(); + QString propertyName = dialog->mName; + QString defaultValue = ""; + + /// \todo check if this property name exists already + + if (!propertyName.isEmpty()) + { + int passIndex, textureIndex; + bool isInPass, isInTextureUnit; + getContext(selectedIndex, &passIndex, &textureIndex, &isInPass, &isInTextureUnit); + + QList items; + QStandardItem* keyItem = new QStandardItem(propertyName); + keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable); + items << keyItem; + items << new QStandardItem(defaultValue); + + // figure out which item the new property should be a child of + QModelIndex parentIndex = selectedIndex; + if (selectedIndex.data(Qt::UserRole) != MaterialProperty::Object) + parentIndex = selectedIndex.parent(); + QStandardItem* parentItem; + if (!parentIndex.isValid()) + parentItem = mMaterialPropertyModel->invisibleRootItem(); + else + parentItem = mMaterialPropertyModel->itemFromIndex(parentIndex); + + if (isInTextureUnit) + { + queueAction(new ActionSetTextureProperty( + material.toStdString(), passIndex, textureIndex, propertyName.toStdString(), defaultValue.toStdString())); + } + else if (isInPass) + { + if (selectedIndex.parent().child(selectedIndex.row(),0).data().toString() == "shader_properties" + || selectedIndex.parent().data().toString() == "shader_properties") + { + queueAction(new ActionSetShaderProperty( + material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString())); + } + else + { + queueAction(new ActionSetPassProperty( + material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString())); + } + } + else + { + queueAction(new ActionSetMaterialProperty( + material.toStdString(), propertyName.toStdString(), defaultValue.toStdString())); + } + + addProperty(parentItem, propertyName.toStdString(), + MaterialProperty (defaultValue.toStdString(), MaterialProperty::Misc, MaterialProperty::Normal), true); + + /// \todo scroll to newly added property + } +} + +void sh::MainWindow::on_actionCreateTextureUnit_triggered() +{ + QString material = getSelectedMaterial(); + if (material.isEmpty()) + return; + + QInputDialog dialog(this); + + QString text = QInputDialog::getText(this, tr("New texture unit"), + tr("Texture unit name (for referencing in shaders):")); + if (!text.isEmpty()) + { + QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex()); + int passIndex; + getContext(selectedIndex, &passIndex, NULL); + queueAction(new ActionCreateTextureUnit(material.toStdString(), passIndex, text.toStdString())); + + // add to model + int index = 0; + for (int i=0; irowCount(); ++i) + { + if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass")) + { + if (index == passIndex) + { + addProperty(mMaterialPropertyModel->itemFromIndex(mMaterialPropertyModel->index(i, 0)), + "texture_unit", MaterialProperty(text.toStdString(), MaterialProperty::Object), true); + break; + } + + ++index; + } + } + } +} + +void sh::MainWindow::on_clearButton_clicked() +{ + ui->errorLog->clear(); +} + +void sh::MainWindow::on_tabWidget_currentChanged(int index) +{ + QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::WindowText); + + if (index == 3) + ui->tabWidget->tabBar()->setTabTextColor(3, color); +} diff --git a/extern/shiny/Editor/MainWindow.hpp b/extern/shiny/Editor/MainWindow.hpp new file mode 100644 index 000000000..3f0dc295c --- /dev/null +++ b/extern/shiny/Editor/MainWindow.hpp @@ -0,0 +1,139 @@ +#ifndef SHINY_EDITOR_MAINWINDOW_HPP +#define SHINY_EDITOR_MAINWINDOW_HPP + +#include + +#include +#include +#include + +#include + +#include "Actions.hpp" +#include "Query.hpp" + +#include "PropertySortModel.hpp" + +namespace Ui { +class MainWindow; +} + +namespace sh +{ + +struct SynchronizationState; + + +/** + * @brief A snapshot of the material system's state. Lock the mUpdateMutex before accessing. + */ +struct MaterialSystemState +{ + std::vector mMaterialList; + + std::map mGlobalSettingsMap; + + std::vector mConfigurationList; + + std::vector mMaterialFiles; + std::vector mConfigurationFiles; + + std::vector mShaderSets; + + std::string mErrors; +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + // really should be an std::atomic + volatile bool mRequestShowWindow; + // dito + volatile bool mRequestExit; + + SynchronizationState* mSync; + + /// \todo Is there a better way to ignore manual model changes? + bool mIgnoreGlobalSettingChange; + bool mIgnoreConfigurationChange; + bool mIgnoreMaterialChange; + bool mIgnoreMaterialPropertyChange; + + std::queue mActionQueue; + std::vector mQueries; + + MaterialSystemState mState; + +private: + Ui::MainWindow *ui; + + // material tab + QStringListModel* mMaterialModel; + QSortFilterProxyModel* mMaterialProxyModel; + + QStandardItemModel* mMaterialPropertyModel; + PropertySortModel* mMaterialSortModel; + + // global settings tab + QStandardItemModel* mGlobalSettingsModel; + + // configuration tab + QStandardItemModel* mConfigurationModel; + + void queueAction(Action* action); + void requestQuery(Query* query); + + void buildMaterialModel (MaterialQuery* data); + void buildConfigurationModel (ConfigurationQuery* data); + + QString getSelectedMaterial(); + + /// get the context of an index in the material property model + void getContext(QModelIndex index, int* passIndex, int* textureIndex, bool* isInPass=NULL, bool* isInTextureUnit=NULL); + + std::string getPropertyKey(QModelIndex index); + std::string getPropertyValue(QModelIndex index); + + void addProperty (QStandardItem* parent, const std::string& key, MaterialProperty value, bool scrollTo=false); + +protected: + void closeEvent(QCloseEvent *event); + +public slots: + void onIdle(); + + void onMaterialSelectionChanged (const QModelIndex & current, const QModelIndex & previous); + void onConfigurationSelectionChanged (const QString& current); + + void onGlobalSettingChanged (QStandardItem* item); + void onConfigurationChanged (QStandardItem* item); + void onMaterialPropertyChanged (QStandardItem* item); + + void onContextMenuRequested(const QPoint& point); + +private slots: + void on_lineEdit_textEdited(const QString &arg1); + void on_actionSave_triggered(); + void on_actionNewMaterial_triggered(); + void on_actionDeleteMaterial_triggered(); + void on_actionQuit_triggered(); + void on_actionNewConfiguration_triggered(); + void on_actionDeleteConfiguration_triggered(); + void on_actionDeleteConfigurationProperty_triggered(); + void on_actionCloneMaterial_triggered(); + void on_actionCreatePass_triggered(); + void on_actionDeleteProperty_triggered(); + void on_actionNewProperty_triggered(); + void on_actionCreateTextureUnit_triggered(); + void on_clearButton_clicked(); + void on_tabWidget_currentChanged(int index); +}; + +} + +#endif // MAINWINDOW_HPP diff --git a/extern/shiny/Editor/NewMaterialDialog.cpp b/extern/shiny/Editor/NewMaterialDialog.cpp new file mode 100644 index 000000000..f1a716a9f --- /dev/null +++ b/extern/shiny/Editor/NewMaterialDialog.cpp @@ -0,0 +1,14 @@ +#include "NewMaterialDialog.hpp" +#include "ui_newmaterialdialog.h" + +NewMaterialDialog::NewMaterialDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::NewMaterialDialog) +{ + ui->setupUi(this); +} + +NewMaterialDialog::~NewMaterialDialog() +{ + delete ui; +} diff --git a/extern/shiny/Editor/NewMaterialDialog.hpp b/extern/shiny/Editor/NewMaterialDialog.hpp new file mode 100644 index 000000000..2a20bbb39 --- /dev/null +++ b/extern/shiny/Editor/NewMaterialDialog.hpp @@ -0,0 +1,22 @@ +#ifndef NEWMATERIALDIALOG_HPP +#define NEWMATERIALDIALOG_HPP + +#include + +namespace Ui { +class NewMaterialDialog; +} + +class NewMaterialDialog : public QDialog +{ + Q_OBJECT + +public: + explicit NewMaterialDialog(QWidget *parent = 0); + ~NewMaterialDialog(); + +private: + Ui::NewMaterialDialog *ui; +}; + +#endif // NEWMATERIALDIALOG_HPP diff --git a/extern/shiny/Editor/PropertySortModel.cpp b/extern/shiny/Editor/PropertySortModel.cpp new file mode 100644 index 000000000..637fe11b0 --- /dev/null +++ b/extern/shiny/Editor/PropertySortModel.cpp @@ -0,0 +1,35 @@ +#include "PropertySortModel.hpp" + +#include "Query.hpp" + +#include +sh::PropertySortModel::PropertySortModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +bool sh::PropertySortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + if (left.data(Qt::UserRole+1).toInt() != 0 && right.data(Qt::UserRole+1).toInt() != 0) + { + int sourceL = left.data(Qt::UserRole+1).toInt(); + int sourceR = right.data(Qt::UserRole+1).toInt(); + + if (sourceL > sourceR) + return true; + else if (sourceR > sourceL) + return false; + } + + int typeL = left.data(Qt::UserRole).toInt(); + int typeR = right.data(Qt::UserRole).toInt(); + + if (typeL > typeR) + return true; + else if (typeR > typeL) + return false; + + QString nameL = left.data().toString(); + QString nameR = right.data().toString(); + return nameL > nameR; +} diff --git a/extern/shiny/Editor/PropertySortModel.hpp b/extern/shiny/Editor/PropertySortModel.hpp new file mode 100644 index 000000000..f96927764 --- /dev/null +++ b/extern/shiny/Editor/PropertySortModel.hpp @@ -0,0 +1,21 @@ +#ifndef SHINY_EDITOR_PROPERTYSORTMODEL_H +#define SHINY_EDITOR_PROPERTYSORTMODEL_H + +#include + +namespace sh +{ + + class PropertySortModel : public QSortFilterProxyModel + { + Q_OBJECT + + public: + PropertySortModel(QObject* parent); + protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + }; + +} + +#endif diff --git a/extern/shiny/Editor/Query.cpp b/extern/shiny/Editor/Query.cpp new file mode 100644 index 000000000..ccf07b62b --- /dev/null +++ b/extern/shiny/Editor/Query.cpp @@ -0,0 +1,134 @@ +#include "Query.hpp" + +#include "../Main/Factory.hpp" + +namespace sh +{ + +void Query::execute() +{ + executeImpl(); + mDone = true; +} + +ConfigurationQuery::ConfigurationQuery(const std::string &name) + : mName(name) +{ +} + +void ConfigurationQuery::executeImpl() +{ + sh::Factory::getInstance().listConfigurationSettings(mName, mProperties); +} + +void MaterialQuery::executeImpl() +{ + sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(mName); + + if (instance->getParent()) + mParent = static_cast(instance->getParent())->getName(); + + // add the inherited properties + sh::PropertySetGet* parent = instance; + std::vector inheritedPropertiesVector; + while (parent->getParent()) + { + parent = parent->getParent(); + const sh::PropertyMap& parentProperties = parent->listProperties(); + + for (PropertyMap::const_iterator it = parentProperties.begin(); it != parentProperties.end(); ++it) + { + MaterialProperty::Source source = MaterialProperty::Inherited_Unchanged; + MaterialProperty::Type type = getType(it->first, parent->getProperty(it->first)); + mProperties[it->first] = MaterialProperty ( + retrieveValue(parent->getProperty(it->first), NULL).get(), + type, source); + inheritedPropertiesVector.push_back(it->first); + } + } + + // add our properties + const sh::PropertyMap& ourProperties = instance->listProperties(); + for (PropertyMap::const_iterator it = ourProperties.begin(); it != ourProperties.end(); ++it) + { + MaterialProperty::Source source = + (std::find(inheritedPropertiesVector.begin(), inheritedPropertiesVector.end(), it->first) + != inheritedPropertiesVector.end()) ? + MaterialProperty::Inherited_Changed : MaterialProperty::Normal; + MaterialProperty::Type type = getType(it->first, instance->getProperty(it->first)); + mProperties[it->first] = MaterialProperty ( + retrieveValue(instance->getProperty(it->first), NULL).get(), + type, source); + } + + std::vector* passes = instance->getPasses(); + for (std::vector::iterator it = passes->begin(); it != passes->end(); ++it) + { + mPasses.push_back(PassInfo()); + + const sh::PropertyMap& passProperties = it->listProperties(); + for (PropertyMap::const_iterator pit = passProperties.begin(); pit != passProperties.end(); ++pit) + { + PropertyValuePtr property = it->getProperty(pit->first); + MaterialProperty::Type type = getType(pit->first, property); + if (typeid(*property).name() == typeid(sh::LinkedValue).name()) + mPasses.back().mProperties[pit->first] = MaterialProperty("$" + property->_getStringValue(), type); + else + mPasses.back().mProperties[pit->first] = MaterialProperty( + retrieveValue(property, NULL).get(), type); + } + + const sh::PropertyMap& shaderProperties = it->mShaderProperties.listProperties(); + for (PropertyMap::const_iterator pit = shaderProperties.begin(); pit != shaderProperties.end(); ++pit) + { + PropertyValuePtr property = it->mShaderProperties.getProperty(pit->first); + MaterialProperty::Type type = getType(pit->first, property); + if (typeid(*property).name() == typeid(sh::LinkedValue).name()) + mPasses.back().mShaderProperties[pit->first] = MaterialProperty("$" + property->_getStringValue(), type); + else + mPasses.back().mShaderProperties[pit->first] = MaterialProperty( + retrieveValue(property, NULL).get(), type); + } + + std::vector* texUnits = &it->mTexUnits; + for (std::vector::iterator tIt = texUnits->begin(); tIt != texUnits->end(); ++tIt) + { + mPasses.back().mTextureUnits.push_back(TextureUnitInfo()); + mPasses.back().mTextureUnits.back().mName = tIt->getName(); + const sh::PropertyMap& unitProperties = tIt->listProperties(); + for (PropertyMap::const_iterator pit = unitProperties.begin(); pit != unitProperties.end(); ++pit) + { + PropertyValuePtr property = tIt->getProperty(pit->first); + MaterialProperty::Type type = getType(pit->first, property); + if (typeid(*property).name() == typeid(sh::LinkedValue).name()) + mPasses.back().mTextureUnits.back().mProperties[pit->first] = MaterialProperty( + "$" + property->_getStringValue(), MaterialProperty::Linked); + else + mPasses.back().mTextureUnits.back().mProperties[pit->first] = MaterialProperty( + retrieveValue(property, NULL).get(), type); + } + } + } +} + +MaterialProperty::Type MaterialQuery::getType(const std::string &key, PropertyValuePtr value) +{ + if (typeid(*value).name() == typeid(sh::LinkedValue).name()) + return MaterialProperty::Linked; + + if (key == "vertex_program" || key == "fragment_program") + return MaterialProperty::Shader; + + std::string valueStr = retrieveValue(value, NULL).get(); + + if (valueStr == "false" || valueStr == "true") + return MaterialProperty::Boolean; +} + +void MaterialPropertyQuery::executeImpl() +{ + sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName); + mValue = retrieveValue(m->getProperty(mPropertyName), m).get(); +} + +} diff --git a/extern/shiny/Editor/Query.hpp b/extern/shiny/Editor/Query.hpp new file mode 100644 index 000000000..d98c8c9b2 --- /dev/null +++ b/extern/shiny/Editor/Query.hpp @@ -0,0 +1,121 @@ +#ifndef SH_QUERY_H +#define SH_QUERY_H + +#include +#include +#include + +#include "../Main/PropertyBase.hpp" + +namespace sh +{ + +class Query +{ +public: + Query() + : mDone(false) {} + virtual ~Query() {} + + void execute(); + + bool mDone; + +protected: + virtual void executeImpl() = 0; +}; + +class ConfigurationQuery : public Query +{ +public: + ConfigurationQuery(const std::string& name); + + std::map mProperties; +protected: + std::string mName; + virtual void executeImpl(); +}; + + +struct MaterialProperty +{ + + enum Type + { + Texture, + Color, + Boolean, + Shader, + Misc, + Linked, + Object // child object, i.e. pass, texture unit, shader properties + }; + + enum Source + { + Normal, + Inherited_Changed, + Inherited_Unchanged, + None // there is no property source (e.g. a pass, which does not have a name) + }; + + MaterialProperty() {} + MaterialProperty (const std::string& value, Type type, Source source=Normal) + : mValue(value), mType(type), mSource(source) {} + + std::string mValue; + Type mType; + Source mSource; +}; + + +struct TextureUnitInfo +{ + std::string mName; + std::map mProperties; +}; + +struct PassInfo +{ + std::map mShaderProperties; + + std::map mProperties; + std::vector mTextureUnits; +}; + +class MaterialQuery : public Query +{ +public: + MaterialQuery(const std::string& name) + : mName(name) {} + + std::string mParent; + std::vector mPasses; + std::map mProperties; + +protected: + std::string mName; + virtual void executeImpl(); + + MaterialProperty::Type getType (const std::string& key, PropertyValuePtr value); +}; + +class MaterialPropertyQuery : public Query +{ +public: + MaterialPropertyQuery(const std::string& name, const std::string& propertyName) + : mName(name), mPropertyName(propertyName) + { + } + + std::string mValue; + + std::string mName; + std::string mPropertyName; +protected: + virtual void executeImpl(); +}; + +} + +#endif diff --git a/extern/shiny/Editor/addpropertydialog.ui b/extern/shiny/Editor/addpropertydialog.ui new file mode 100644 index 000000000..63de7d141 --- /dev/null +++ b/extern/shiny/Editor/addpropertydialog.ui @@ -0,0 +1,118 @@ + + + AddPropertyDialog + + + + 0 + 0 + 257 + 133 + + + + Dialog + + + + + + + + + + + + + Property name + + + + + + + Editing widget + + + + + + + + Checkbox + + + + + Shader + + + + + Color + + + + + Texture + + + + + Other + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + AddPropertyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddPropertyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/extern/shiny/Editor/mainwindow.ui b/extern/shiny/Editor/mainwindow.ui new file mode 100644 index 000000000..b27c8357d --- /dev/null +++ b/extern/shiny/Editor/mainwindow.ui @@ -0,0 +1,420 @@ + + + MainWindow + + + + 0 + 0 + 647 + 512 + + + + MainWindow + + + + + + + 0 + + + + + + + Materials + + + + + + Qt::Horizontal + + + + + + + + + + Search + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + + + + + + + + 1 + 0 + + + + + + + + + 1 + 0 + + + + + 16 + 16 + + + + Qt::ToolButtonIconOnly + + + + + + + + + + + + + + Global settings + + + + + + + + + + Configurations + + + + + + Qt::Horizontal + + + + + + + + + + + 16 + 16 + + + + + + + + + + + + + + + + + + 16 + 16 + + + + + + + + + + + + + + Errors + + + + + + + + true + + + + + + + + 0 + 0 + + + + Clear + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 647 + 25 + + + + false + + + + File + + + + + + + Material + + + + + + + + + History + + + + + + + + + + + + + + Quit + + + + + + + + + + Save + + + Save all + + + + + + + + + + Delete + + + Delete selected material + + + + + Change parent... + + + + + + + + + + New + + + Create a new material + + + + + + + + + + Clone + + + Clone selected material + + + + + + + + + + Delete + + + Delete selected configuration + + + Del + + + + + + + + + + New + + + Create a new configuration + + + + + + + + + + Delete + + + Delete property + + + + + + + + + + Delete + + + Delete item + + + + + + + + + + New property + + + + + + + + + + Create pass + + + + + + + + + + Create texture unit + + + + + + sh::ColoredTabWidget + QTabWidget +

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 40c695fd4..6254edbaf 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -51,8 +51,6 @@ namespace sh { assert(mCurrentLanguage != Language_None); - bool removeBinaryCache = false; - if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt")) { std::ifstream file; @@ -86,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) @@ -137,82 +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 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)); - } - } + bool removeBinaryCache = reloadShaders(); // load materials { @@ -315,6 +239,8 @@ namespace sh Factory::~Factory () { + mShaderSets.clear(); + if (mPlatform->supportsShaderSerialization () && mWriteMicrocodeCache) { std::string file = mPlatform->getCacheFolder () + "/" + mBinaryCacheName; @@ -367,15 +293,22 @@ namespace sh while (i>0) { --i; - m->createForConfiguration (configuration, i); - - if (mListener) + if (m->createForConfiguration (configuration, i)) + { + if (mListener) mListener->materialCreated (m, configuration, i); + } + else + return NULL; } - m->createForConfiguration (configuration, lodIndex); - if (mListener) - mListener->materialCreated (m, configuration, lodIndex); + if (m->createForConfiguration (configuration, lodIndex)) + { + if (mListener) + mListener->materialCreated (m, configuration, lodIndex); + } + else + return NULL; } return m; } @@ -439,6 +372,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; } @@ -466,6 +405,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); @@ -493,17 +440,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; @@ -571,17 +522,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) @@ -630,4 +657,147 @@ namespace sh } 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 846860b89..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,6 +236,7 @@ namespace sh bool mWriteMicrocodeCache; bool mReadSourceCache; bool mWriteSourceCache; + std::stringstream mErrorLog; MaterialMap mMaterials; ShaderSetMap mShaderSets; 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 1539128ab..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); diff --git a/extern/shiny/Main/ShaderSet.cpp b/extern/shiny/Main/ShaderSet.cpp index 413d7d1a2..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" @@ -26,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; @@ -52,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) @@ -135,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 a423b6779..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 @@ -53,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 4ec43fcae..f45e64155 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp @@ -2,6 +2,8 @@ #include +#include + namespace sh { void OgreMaterialSerializer::reset() @@ -44,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 3ed48b96f..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) @@ -105,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 9e65b516b..9b2325744 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -44,6 +44,9 @@ set(MATERIAL_FILES 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}") diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index eb05c3e18..f02c5766f 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -3,13 +3,24 @@ #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) shOutput(float, alphaFade) 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)); alphaFade = shInputPosition.z < 150.0 ? 0.0 : 1.0; } diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index e60026d3b..b76f2ded7 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -3,15 +3,26 @@ #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, alphaFade) 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, shInputPosition)); + UV = uv0; alphaFade = (shInputPosition.z <= 200.f) ? ((shInputPosition.z <= 100.f) ? 0.0 : 0.25) : 1.0; } diff --git a/files/materials/core.h b/files/materials/core.h index 3385e5fac..6f8179c08 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -58,11 +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 231f60ba0..a7d183d10 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -3,14 +3,26 @@ #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 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 5e18a666a..8f8734d62 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -6,12 +6,19 @@ material openmw_objects_base emissive 0.0 0.0 0.0 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 { @@ -21,7 +28,11 @@ material openmw_objects_base shader_properties { vertexcolor_mode $vertmode - is_transparent $is_transparent + normalMap $normalMap + emissiveMapUVSet $emissiveMapUVSet + detailMapUVSet $detailMapUVSet + emissiveMap $emissiveMap + detailMap $detailMap } diffuse $diffuse @@ -31,12 +42,38 @@ material openmw_objects_base scene_blend $scene_blend alpha_rejection $alpha_rejection depth_write $depth_write + 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 diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 46e3abeb3..36f92bfd9 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -14,13 +14,20 @@ #define NEED_DEPTH #endif +#define NORMAL_MAP @shPropertyHasValue(normalMap) +#define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) +#define DETAIL_MAP @shPropertyHasValue(detailMap) + +// 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 UNDERWATER @shGlobalSettingBool(render_refraction) #define VERTEXCOLOR_MODE @shPropertyString(vertexcolor_mode) -#define VERTEX_LIGHTING 1 - #define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) #ifdef SH_VERTEX_SHADER @@ -30,6 +37,8 @@ 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 @@ -40,8 +49,22 @@ #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 @@ -52,12 +75,16 @@ 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_object_space_array, @shGlobalSettingString(num_lights)) + 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 @@ -92,7 +119,21 @@ SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + + 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 @@ -131,23 +172,26 @@ #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 - (shInputPosition.xyz * lightPosition[@shIterator].w); + 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 - * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normalize(normal.xyz), normalize(lightDir)), 0); + * 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 - * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normalize(normal.xyz), normalize(lightDir)), 0); + * 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 @@ -184,15 +228,39 @@ #endif SH_BEGIN_PROGRAM - shSampler2D(diffuseMap) - shInput(float2, UV) + shSampler2D(diffuseMap) + +#if NORMAL_MAP + shSampler2D(normalMap) +#endif + +#if EMISSIVE_MAP + shSampler2D(emissiveMap) +#endif + +#if DETAIL_MAP + shSampler2D(detailMap) +#endif + + shInput(float4, UV) + +#if NORMAL_MAP + shInput(float3, tangentPassthrough) +#endif +#if !VERTEX_LIGHTING + shInput(float3, normalPassthrough) +#endif #ifdef NEED_DEPTH shInput(float, depthPassthrough) #endif shInput(float3, objSpacePositionPassthrough) - + +#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) @@ -218,23 +286,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(float, waterLevel) @shSharedParameter(waterLevel) shUniform(float, waterEnabled) @shSharedParameter(waterEnabled) #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) = 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; + float d; + 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 + 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); @@ -263,7 +414,7 @@ float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); #endif -#if SHADOWS +#if SHADOWS || SHADOWS_PSSM shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0)); #else shOutputColour(0) *= lightResult; @@ -271,7 +422,7 @@ #if FOG float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); - + #if UNDERWATER shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, UNDERWATER_COLOUR, shSaturate(length(waterEyePos-worldPos) / VISIBILITY)); @@ -279,6 +430,14 @@ shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); #endif +#endif + +#if EMISSIVE_MAP + #if @shPropertyString(emissiveMapUVSet) + shOutputColour(0).xyz += shSample(emissiveMap, UV.zw).xyz; + #else + shOutputColour(0).xyz += shSample(emissiveMap, UV.xy).xyz; + #endif #endif // prevent negative colour output (for example with negative lights) 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 e50aa51d8..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 @@ -92,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 fea007424..33f22bd14 100644 --- a/files/materials/stars.shader +++ b/files/materials/stars.shader @@ -3,15 +3,26 @@ #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; diff --git a/files/materials/sun.shader b/files/materials/sun.shader index 7954f417c..abe4c97f1 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -3,14 +3,26 @@ #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 diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index c73b582f8..861841a84 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -1,8 +1,8 @@ #include "core.h" -#define IS_FIRST_PASS 1 +#define IS_FIRST_PASS (@shPropertyString(pass_index) == 0) -#define FOG @shGlobalSettingBool(fog) +#define FOG (@shGlobalSettingBool(fog) && !@shPropertyBool(render_composite_map)) #define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm) #define SHADOWS @shGlobalSettingBool(shadows) @@ -11,8 +11,6 @@ #include "shadows.h" #endif -#define COLOUR_MAP @shPropertyBool(colour_map) - #define NUM_LAYERS @shPropertyString(num_layers) #if FOG || SHADOWS_PSSM @@ -23,6 +21,12 @@ #define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) +#define RENDERCMP @shPropertyBool(render_composite_map) + +#define LIGHTING !RENDERCMP + +#define COMPOSITE_MAP @shPropertyBool(display_composite_map) + #if NEED_DEPTH @shAllocatePassthrough(1, depth) @@ -32,6 +36,10 @@ @shAllocatePassthrough(3, worldPos) +#if LIGHTING +@shAllocatePassthrough(3, lightResult) +@shAllocatePassthrough(3, directionalResult) + #if SHADOWS @shAllocatePassthrough(4, lightSpacePos0) #endif @@ -40,6 +48,7 @@ @shAllocatePassthrough(4, lightSpacePos@shIterator) @shEndForeach #endif +#endif #ifdef SH_VERTEX_SHADER @@ -52,11 +61,19 @@ #if VIEWPROJ_FIX shUniform(float4, vpRow2Fix) @shSharedParameter(vpRow2Fix, vpRow2Fix) #endif - - shUniform(float2, lodMorph) @shAutoConstant(lodMorph, custom, 1001) shVertexInput(float2, uv0) shVertexInput(float2, uv1) // lodDelta, lodThreshold + +#if LIGHTING + shNormalInput(float4) + shColourInput(float4) + + shUniform(float, lightCount) @shAutoConstant(lightCount, light_count) + shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_object_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) #if SHADOWS shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) @@ -68,31 +85,15 @@ @shEndForeach #endif +#endif + @shPassthroughVertexOutputs SH_START_PROGRAM { - - float4 worldPos = shMatrixMult(worldMatrix, shInputPosition); - // determine whether to apply the LOD morph to this vertex - // we store the deltas against all vertices so we only want to apply - // the morph to the ones which would disappear. The target LOD which is - // being morphed to is stored in lodMorph.y, and the LOD at which - // the vertex should be morphed is stored in uv.w. If we subtract - // the former from the latter, and arrange to only morph if the - // result is negative (it will only be -1 in fact, since after that - // the vertex will never be indexed), we will achieve our aim. - // sign(vertexLOD - targetLOD) == -1 is to morph - float toMorph = -min(0, sign(uv1.y - lodMorph.y)); - - // morph - // this assumes XY terrain alignment - worldPos.z += uv1.x * toMorph * lodMorph.x; - - shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); #if NEED_DEPTH @@ -121,6 +122,8 @@ @shPassthroughAssign(worldPos, worldPos.xyz); +#if LIGHTING + #if SHADOWS float4 lightSpacePos = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); @shPassthroughAssign(lightSpacePos0, lightSpacePos); @@ -135,6 +138,34 @@ @shEndForeach #endif + + // Lighting + float3 lightDir; + float d; + float3 lightResult = float3(0,0,0); + float3 directionalResult = float3(0,0,0); + @shForeach(@shGlobalSettingString(num_lights)) + lightDir = lightPosition[@shIterator].xyz - (shInputPosition.xyz * lightPosition[@shIterator].w); + d = length(lightDir); + lightDir = normalize(lightDir); + + + lightResult.xyz += lightDiffuse[@shIterator].xyz + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * max(dot(normal.xyz, lightDir), 0); + +#if @shIterator == 0 + directionalResult = lightResult.xyz; +#endif + @shEndForeach + lightResult.xyz += lightAmbient.xyz; + lightResult.xyz *= colour.xyz; + directionalResult.xyz *= colour.xyz; + + @shPassthroughAssign(lightResult, lightResult); + @shPassthroughAssign(directionalResult, directionalResult); + +#endif } #else @@ -148,12 +179,9 @@ SH_BEGIN_PROGRAM -#if COLOUR_MAP - shSampler2D(colourMap) -#endif - - shSampler2D(normalMap) // global normal map - +#if COMPOSITE_MAP + shSampler2D(compositeMap) +#else @shForeach(@shPropertyString(num_blendmaps)) shSampler2D(blendMap@shIterator) @@ -162,6 +190,8 @@ @shForeach(@shPropertyString(num_layers)) shSampler2D(diffuseMap@shIterator) @shEndForeach + +#endif #if FOG shUniform(float3, fogColour) @shAutoConstant(fogColour, fog_colour) @@ -170,13 +200,7 @@ @shPassthroughFragmentInputs - shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - @shForeach(@shGlobalSettingString(terrain_num_lights)) - 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 - +#if LIGHTING #if SHADOWS shSampler2D(shadowMap0) shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, @shPropertyString(shadowtexture_offset)) @@ -192,6 +216,7 @@ #if SHADOWS || SHADOWS_PSSM shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) #endif +#endif #if (UNDERWATER) || (FOG) shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) @@ -202,7 +227,6 @@ shUniform(float, waterLevel) @shSharedParameter(waterLevel) #endif - SH_START_PROGRAM { @@ -213,48 +237,67 @@ float2 UV = @shPassthroughReceive(UV); float3 worldPos = @shPassthroughReceive(worldPos); - - float3 normal = shSample(normalMap, UV).rgb * 2 - 1; - normal = normalize(normal); #if UNDERWATER 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 + + +shOutputColour(0) = float4(1,1,1,1); + +#if COMPOSITE_MAP + shOutputColour(0).xyz = shSample(compositeMap, UV).xyz; +#else + // Layer calculations +// rescale UV to directly map edge vertices to texel centers - this is +// important to get correct blending at cell transitions +// TODO: parameterize texel size +float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; @shForeach(@shPropertyString(num_blendmaps)) - float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); + float4 blendValues@shIterator = shSaturate(shSample(blendMap@shIterator, blendUV)); @shEndForeach + float3 albedo = float3(0,0,0); + + float2 layerUV = UV * 16; + @shForeach(@shPropertyString(num_layers)) -#if IS_FIRST_PASS == 1 && @shIterator == 0 - // first layer of first pass doesn't need a blend map - albedo = 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, layerUV).rgb; + #else + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, layerUV).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + #endif #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); - + #if @shIterator == 0 + albedo = shSample(diffuseMap@shIterator, layerUV).rgb, blendValues@shPropertyString(blendmap_component_@shIterator); + #else + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, layerUV).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + #endif + previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @shEndForeach - shOutputColour(0) = float4(1,1,1,1); + shOutputColour(0).rgb *= albedo; -#if COLOUR_MAP - shOutputColour(0).rgb *= shSample(colourMap, UV).rgb; #endif - shOutputColour(0).rgb *= albedo; - - - - - - +#if LIGHTING // Lighting + float3 lightResult = @shPassthroughReceive(lightResult); + float3 directionalResult = @shPassthroughReceive(directionalResult); // shadows only for the first (directional) light #if SHADOWS @@ -279,40 +322,9 @@ float shadow = 1.0; #endif - - - float3 lightDir; - float3 diffuse = float3(0,0,0); - float d; - - @shForeach(@shGlobalSettingString(terrain_num_lights)) - - lightDir = lightPosObjSpace@shIterator.xyz - (worldPos.xyz * lightPosObjSpace@shIterator.w); - d = length(lightDir); - - - lightDir = normalize(lightDir); - -#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; - - #else - diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); - - #endif - -#else - diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); + shOutputColour(0).xyz *= (lightResult - directionalResult * (1.0-shadow)); #endif - @shEndForeach - - shOutputColour(0).xyz *= (lightAmbient.xyz + diffuse); - - - #if FOG float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); @@ -325,6 +337,12 @@ // prevent negative colour output (for example with negative lights) shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); + +#if IS_FIRST_PASS + shOutputColour(0).a = 1; +#else + shOutputColour(0).a = 1.f-previousAlpha; +#endif } #endif diff --git a/files/materials/water.mat b/files/materials/water.mat index 0ec71d2df..1e5f8c8e0 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -54,5 +54,24 @@ material Water scale 0.1 0.1 alpha_op_ex source1 src_manual src_current 0.7 } + + 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 793cdc95e..87e90a291 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -3,23 +3,28 @@ #define SIMPLE_WATER @shGlobalSettingBool(simple_water) - #if SIMPLE_WATER // --------------------------------------- SIMPLE WATER --------------------------------------------------- +#define FOG @shGlobalSettingBool(fog) + #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) - shOutput(float, depth) +#if FOG + shOutput(float, depth) +#endif SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); UV = uv0; +#if FOG depth = shOutputPosition.z; +#endif } #else @@ -38,8 +43,10 @@ shOutputColour(0).xyz = shSample(animatedTexture, UV * 15).xyz * float3(1.0, 1.0, 1.0); shOutputColour(0).w = 0.7; +#if FOG float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); +#endif } #endif @@ -50,6 +57,13 @@ // 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) @@ -64,6 +78,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); @@ -88,6 +119,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 @@ -186,10 +228,47 @@ 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; @@ -244,7 +323,7 @@ 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)); + 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 @@ -267,7 +346,7 @@ #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; diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index beace5b81..1ec1e08cb 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -78,9 +78,13 @@ 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 DejaVuLGCSansMono.ttf markers.png + ../launcher/images/openmw.png ) 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/core_layouteditor.xml b/files/mygui/core_layouteditor.xml index db917b6ef..007b5e638 100644 --- a/files/mygui/core_layouteditor.xml +++ b/files/mygui/core_layouteditor.xml @@ -1,25 +1,25 @@ - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index 8471f69df..4b15ac1bd 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -1,22 +1,28 @@ - + + - + + + - + - + + - + + + - - + + @@ -32,15 +38,19 @@ + - + + + + - - + + @@ -56,36 +66,44 @@ - - - - - - - - - + + + + + + + + + - - + + + + - + + + + + + + - + \ No newline at end of file diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 96d1153f0..9f30bf253 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -4,29 +4,31 @@ - - - - - - - - - + + + + + + + + + + - - - - - + + + + + + + - - + @@ -39,8 +41,9 @@ - - + + + diff --git a/files/mygui/openmw_box.skin.xml b/files/mygui/openmw_box.skin.xml index 2a54edd60..620f49e2b 100644 --- a/files/mygui/openmw_box.skin.xml +++ b/files/mygui/openmw_box.skin.xml @@ -42,35 +42,35 @@ as around the sections of the stats window, or around popup info windows --> - - + + - - + + - - + + - - + + - - - - - - - - + + + + + + + + diff --git a/files/mygui/openmw_button.skin.xml b/files/mygui/openmw_button.skin.xml index 2895d52cc..e152a9112 100644 --- a/files/mygui/openmw_button.skin.xml +++ b/files/mygui/openmw_button.skin.xml @@ -3,7 +3,7 @@ - + @@ -12,7 +12,7 @@ - + @@ -21,7 +21,7 @@ - + @@ -30,7 +30,7 @@ - + @@ -39,41 +39,41 @@ - - + + - - + + - - + + - - + + - - + + - - - - - - - - + + + + + + + + - + diff --git a/files/mygui/openmw_chargen_birth.layout b/files/mygui/openmw_chargen_birth.layout index f4b8c518d..b368a6407 100644 --- a/files/mygui/openmw_chargen_birth.layout +++ b/files/mygui/openmw_chargen_birth.layout @@ -1,20 +1,20 @@ - + + - + - - + + - - + - + @@ -25,5 +25,6 @@ + diff --git a/files/mygui/openmw_chargen_class.layout b/files/mygui/openmw_chargen_class.layout index 00e3793bc..3c0348b66 100644 --- a/files/mygui/openmw_chargen_class.layout +++ b/files/mygui/openmw_chargen_class.layout @@ -1,66 +1,71 @@ - + + - + - - + + - + - - - - - - - - - - + + + + + + + - - - - - - - - - - + + + - - - - - - - - - - + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -71,5 +76,6 @@ + diff --git a/files/mygui/openmw_chargen_class_description.layout b/files/mygui/openmw_chargen_class_description.layout index 8823e1e76..eaf754697 100644 --- a/files/mygui/openmw_chargen_class_description.layout +++ b/files/mygui/openmw_chargen_class_description.layout @@ -1,22 +1,22 @@ - + + - - - - - - + + + + - + + diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout index 54f73f221..92382640b 100644 --- a/files/mygui/openmw_chargen_create_class.layout +++ b/files/mygui/openmw_chargen_create_class.layout @@ -1,64 +1,71 @@ - + + - - - + + + + + + - - + - + - + - - + + + - + - + - - + + + - + - + - - - - - + + + + + + - + - + - - - - - + + + + + + - + @@ -72,5 +79,6 @@ + diff --git a/files/mygui/openmw_chargen_generate_class_result.layout b/files/mygui/openmw_chargen_generate_class_result.layout index aa4a89d28..f7178042f 100644 --- a/files/mygui/openmw_chargen_generate_class_result.layout +++ b/files/mygui/openmw_chargen_generate_class_result.layout @@ -1,26 +1,26 @@ - + - - + + - - + + - + - + - + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 4ef8da0f3..1290795ed 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -4,78 +4,78 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - - + + - + - + diff --git a/files/mygui/openmw_chargen_review.layout b/files/mygui/openmw_chargen_review.layout index 97e32cfe2..5d18f4bff 100644 --- a/files/mygui/openmw_chargen_review.layout +++ b/files/mygui/openmw_chargen_review.layout @@ -1,10 +1,10 @@ - + - - + + @@ -17,27 +17,27 @@ - - - - + + + + - - - + + + - + - + @@ -46,57 +46,57 @@ - - + + - + - + - + - + - + - + - + @@ -105,13 +105,13 @@ - - - + + + - - + + diff --git a/files/mygui/openmw_chargen_select_attribute.layout b/files/mygui/openmw_chargen_select_attribute.layout index 09c2a4aae..f0f72bb0f 100644 --- a/files/mygui/openmw_chargen_select_attribute.layout +++ b/files/mygui/openmw_chargen_select_attribute.layout @@ -1,23 +1,23 @@ - + - + - + - - - - - - - - + + + + + + + + diff --git a/files/mygui/openmw_chargen_select_skill.layout b/files/mygui/openmw_chargen_select_skill.layout index 2cf81e6c6..dc1798995 100644 --- a/files/mygui/openmw_chargen_select_skill.layout +++ b/files/mygui/openmw_chargen_select_skill.layout @@ -1,58 +1,58 @@ - + - + - + - + - + - - - - - - - - - + + + + + + + + + - + - + - - - - - - - - - + + + + + + + + + - + - + - - - - - - - - - + + + + + + + + + diff --git a/files/mygui/openmw_chargen_select_specialization.layout b/files/mygui/openmw_chargen_select_specialization.layout index e9cb76825..7f8b5e02b 100644 --- a/files/mygui/openmw_chargen_select_specialization.layout +++ b/files/mygui/openmw_chargen_select_specialization.layout @@ -2,23 +2,23 @@ - + - + - + - - + + - - + + - - + + diff --git a/files/mygui/openmw_companion_window.layout b/files/mygui/openmw_companion_window.layout new file mode 100644 index 000000000..1266da397 --- /dev/null +++ b/files/mygui/openmw_companion_window.layout @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index 46b477407..47e1fd2b8 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -1,19 +1,19 @@ - + - - - + + + - + - + diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index 732684ad1..bfda40c68 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -2,16 +2,17 @@ - + + - + - + diff --git a/files/mygui/openmw_console.skin.xml b/files/mygui/openmw_console.skin.xml index 1758c728d..219cce39a 100644 --- a/files/mygui/openmw_console.skin.xml +++ b/files/mygui/openmw_console.skin.xml @@ -1,32 +1,33 @@ - - - - - - - - - - + - + + + + + - - - + + - - - - - - + + + + + + + + + + + + + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 452196aae..06cc04ebe 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -1,14 +1,11 @@ - + + - - - - - + diff --git a/files/mygui/openmw_count_window.layout b/files/mygui/openmw_count_window.layout index 5812ec7fd..4e24277af 100644 --- a/files/mygui/openmw_count_window.layout +++ b/files/mygui/openmw_count_window.layout @@ -16,7 +16,7 @@ - + diff --git a/files/mygui/openmw_dialogue_window.layout b/files/mygui/openmw_dialogue_window.layout index 9a9da72d4..78daa0705 100644 --- a/files/mygui/openmw_dialogue_window.layout +++ b/files/mygui/openmw_dialogue_window.layout @@ -2,24 +2,24 @@ + - + - - - - - - - - + + + - + + + + + - - + + diff --git a/files/mygui/openmw_dialogue_window_skin.xml b/files/mygui/openmw_dialogue_window_skin.xml index 31ce626be..4f68a90fa 100644 --- a/files/mygui/openmw_dialogue_window_skin.xml +++ b/files/mygui/openmw_dialogue_window_skin.xml @@ -2,18 +2,18 @@ - - - + + + - - - - - - + + + + + + - + diff --git a/files/mygui/openmw_edit.skin.xml b/files/mygui/openmw_edit.skin.xml index da21385e2..b10854b19 100644 --- a/files/mygui/openmw_edit.skin.xml +++ b/files/mygui/openmw_edit.skin.xml @@ -4,15 +4,15 @@ - + - + - + - + @@ -20,32 +20,30 @@ - + + + - - - - - + - + - + - + - + - + - + diff --git a/files/mygui/openmw_edit_effect.layout b/files/mygui/openmw_edit_effect.layout index cad22c064..fa1e58b9d 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 b1446fae1..726bfb281 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -9,6 +9,7 @@ + @@ -22,34 +23,21 @@ - - - - - + + + + - + + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 382bc6dc9..e39777dd0 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -3,6 +3,11 @@ + + + + + @@ -28,6 +33,20 @@ + + + + + + + + + + + + + + @@ -60,6 +79,17 @@ + + + + + + + + + + + @@ -79,7 +109,7 @@ - + @@ -97,31 +127,50 @@ - + - + - + - + + + - - + + - + - - + + - + + + + + + + + + + + + + + + + + + + + - + diff --git a/files/mygui/openmw_hud_box.skin.xml b/files/mygui/openmw_hud_box.skin.xml index f847ca51a..dd8172661 100644 --- a/files/mygui/openmw_hud_box.skin.xml +++ b/files/mygui/openmw_hud_box.skin.xml @@ -8,27 +8,27 @@ - + - + - + - + - + diff --git a/files/mygui/openmw_hud_energybar.skin.xml b/files/mygui/openmw_hud_energybar.skin.xml index 03d2835d6..f10908d7b 100644 --- a/files/mygui/openmw_hud_energybar.skin.xml +++ b/files/mygui/openmw_hud_energybar.skin.xml @@ -3,70 +3,75 @@ - - + + - - + + - - + + - - - - - - - - - - - + + + + + + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + - - + + diff --git a/files/mygui/openmw_infobox.layout b/files/mygui/openmw_infobox.layout index 89031a616..4e70cd2c4 100644 --- a/files/mygui/openmw_infobox.layout +++ b/files/mygui/openmw_infobox.layout @@ -4,13 +4,13 @@ - - + + - + diff --git a/files/mygui/openmw_interactive_messagebox.layout b/files/mygui/openmw_interactive_messagebox.layout index b8a71c670..d3ac1f8b5 100644 --- a/files/mygui/openmw_interactive_messagebox.layout +++ b/files/mygui/openmw_interactive_messagebox.layout @@ -2,17 +2,17 @@ - - - - + + + + - - + + - - + + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 88cc5638d..ecccd995b 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -2,12 +2,12 @@ + - - + @@ -17,7 +17,6 @@ - @@ -27,11 +26,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..34421d431 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..ca6d309d7 100644 --- a/files/mygui/openmw_journal_skin.xml +++ b/files/mygui/openmw_journal_skin.xml @@ -1,17 +1,15 @@ - - - - - - + + + + + + - - - + 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 6631424cc..02c11c354 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -5,29 +5,29 @@ - - + + - + - - + + - - + + - - + + - + @@ -43,30 +43,30 @@ - - + + - + - - + + - - + + - - + + - + @@ -89,19 +89,19 @@ - - + + - + - - + + - + @@ -113,57 +113,66 @@ - - - - + + + + + + - + - + + + - + + + + + + - + - + - + - - - - + + + + - + - + - + - - - - - + + + + + - + - + - + - - + + diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index 1e4bba5ed..5fd3440f9 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -12,8 +12,7 @@ - - + diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index d4b87e60d..232f31b75 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -2,36 +2,37 @@ + - + - + - + - + - + - + - + diff --git a/files/mygui/openmw_map_window_skin.xml b/files/mygui/openmw_map_window_skin.xml index 13f18c6d3..887e33293 100644 --- a/files/mygui/openmw_map_window_skin.xml +++ b/files/mygui/openmw_map_window_skin.xml @@ -5,7 +5,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_messagebox.layout b/files/mygui/openmw_messagebox.layout index c5a7464ef..dfdb57648 100644 --- a/files/mygui/openmw_messagebox.layout +++ b/files/mygui/openmw_messagebox.layout @@ -2,17 +2,17 @@ - - - - + + + + - - + + diff --git a/files/mygui/openmw_pointer.xml b/files/mygui/openmw_pointer.xml index cf21037f8..a55a5453c 100644 --- a/files/mygui/openmw_pointer.xml +++ b/files/mygui/openmw_pointer.xml @@ -1,39 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_progress.skin.xml b/files/mygui/openmw_progress.skin.xml index 4666be221..35114ffeb 100644 --- a/files/mygui/openmw_progress.skin.xml +++ b/files/mygui/openmw_progress.skin.xml @@ -2,66 +2,79 @@ - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + + + + + + + + + + + + + + + + + + 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 e47ff6386..2c3908a1b 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -250,7 +250,7 @@ - + @@ -260,13 +260,25 @@ + + + + + + + + + + + + - + - + @@ -275,7 +287,7 @@ - + @@ -283,7 +295,7 @@ - + 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 1b94f0c29..b6ed9155f 100644 --- a/files/mygui/openmw_scroll_skin.xml +++ b/files/mygui/openmw_scroll_skin.xml @@ -4,12 +4,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 693c5a9cb..ebfaf678a 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,10 +1,10 @@ - + - - + + @@ -15,8 +15,9 @@ - - + + + @@ -30,8 +31,9 @@ - - + + + @@ -42,6 +44,12 @@ + + + + + + @@ -64,43 +72,48 @@ - - + + + - - + + + - - + + + - - + + + - - + + + - - + + @@ -114,33 +127,18 @@ - - - - - - - - - - - - - - - - - + - - + + + - + - + @@ -151,13 +149,13 @@ - + - + @@ -195,17 +193,18 @@ - + - - + + + - + - + @@ -223,16 +222,18 @@ - - + + + - - + + + @@ -334,12 +335,12 @@ - - + + - + - + @@ -349,7 +350,7 @@ - + diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout index 1e18fda23..1510372dd 100644 --- a/files/mygui/openmw_spell_buying_window.layout +++ b/files/mygui/openmw_spell_buying_window.layout @@ -5,18 +5,18 @@ - - + + - - - + + + - - + + diff --git a/files/mygui/openmw_spell_window.layout b/files/mygui/openmw_spell_window.layout index 18a5af352..ab924da6d 100644 --- a/files/mygui/openmw_spell_window.layout +++ b/files/mygui/openmw_spell_window.layout @@ -2,11 +2,11 @@ + - - + diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index 55ee7b9da..5ae3f96ca 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -2,6 +2,7 @@ + @@ -217,8 +218,8 @@ - - + + diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 13b22ff93..6a1dea60b 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -3,84 +3,84 @@ - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - + + - - - - + + + + @@ -92,10 +92,10 @@ - - - - + + + + @@ -107,10 +107,10 @@ - - - - + + + + @@ -122,10 +122,10 @@ - - - - + + + + @@ -137,42 +137,47 @@ - - + + - - - + + + - - - + + + - - - + + + - - - - + + + - - - - + + + + - - - - + + + + + + + + + + diff --git a/files/mygui/openmw_text_input.layout b/files/mygui/openmw_text_input.layout index a2ed94c67..012b0a82b 100644 --- a/files/mygui/openmw_text_input.layout +++ b/files/mygui/openmw_text_input.layout @@ -4,10 +4,10 @@ - - + + - + diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index 514d1a25b..624c133f2 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 ecc794c92..8a1be496f 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -3,6 +3,7 @@ + @@ -24,11 +25,7 @@ - - - - - + diff --git a/files/mygui/openmw_travel_window.layout b/files/mygui/openmw_travel_window.layout index 07a6daf48..db3fa24a0 100644 --- a/files/mygui/openmw_travel_window.layout +++ b/files/mygui/openmw_travel_window.layout @@ -15,8 +15,8 @@ - - + + diff --git a/files/mygui/openmw_wait_dialog.layout b/files/mygui/openmw_wait_dialog.layout index 66e0ec22f..eeb7012eb 100644 --- a/files/mygui/openmw_wait_dialog.layout +++ b/files/mygui/openmw_wait_dialog.layout @@ -16,7 +16,7 @@ - + diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index 73f68e80d..22586716c 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -2,128 +2,128 @@ - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + @@ -149,43 +149,43 @@ - - + + - - + + - - + + - - - + + + - - + + - - + + - + @@ -196,7 +196,7 @@ - + @@ -207,7 +207,7 @@ - + @@ -218,7 +218,7 @@ - + @@ -230,27 +230,84 @@ - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -286,69 +343,69 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - + + + + + + + + + - - - - + + + + - - + + - + - - - - - - + + + + + + - - - - - + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + - - + + + + + + + - - + + - - + + + + + + - - + + - - + + - - + + + + + + + + + + - - - + + + - - + + - - + + - - + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - + - - + + - - - - - + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + - - + + + + + + + - - + + - - + + + + + + - - + + - - + + - - + + + + + + + + + + - - - + + + - - + + - - + + - - + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - - - - + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + - - + + + + + + + - - + + - - + + + + + + - - + + - - + + - - + + + + + + + + + + - - - + + + - - + + - - + + - - + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - + - - + + - + - - - - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/files/mygui/smallbars.png b/files/mygui/smallbars.png index f938412c2..3c007a55c 100644 Binary files a/files/mygui/smallbars.png and b/files/mygui/smallbars.png differ diff --git a/files/opencs.cfg b/files/opencs.cfg new file mode 100644 index 000000000..3faac7c8e --- /dev/null +++ b/files/opencs.cfg @@ -0,0 +1,5 @@ +[Editor] +Record Status Display = Icon and Text +[Window Size] +Width = 640 +Height = 480 diff --git a/files/opencs/activator.png b/files/opencs/activator.png new file mode 100755 index 000000000..0446af22c Binary files /dev/null and b/files/opencs/activator.png differ diff --git a/files/opencs/added.png b/files/opencs/added.png new file mode 100644 index 000000000..aff7e25d4 Binary files /dev/null and b/files/opencs/added.png differ diff --git a/files/opencs/apparatus.png b/files/opencs/apparatus.png new file mode 100755 index 000000000..3cef537e1 Binary files /dev/null and b/files/opencs/apparatus.png differ diff --git a/files/opencs/armor.png b/files/opencs/armor.png new file mode 100755 index 000000000..fc534c7d1 Binary files /dev/null and b/files/opencs/armor.png differ diff --git a/files/opencs/base.png b/files/opencs/base.png new file mode 100644 index 000000000..4398e2d68 Binary files /dev/null and b/files/opencs/base.png differ diff --git a/files/opencs/book.png b/files/opencs/book.png new file mode 100755 index 000000000..3afa9e8aa Binary files /dev/null and b/files/opencs/book.png differ diff --git a/files/opencs/clothing.png b/files/opencs/clothing.png new file mode 100755 index 000000000..88c9b6ab8 Binary files /dev/null and b/files/opencs/clothing.png differ diff --git a/files/opencs/container.png b/files/opencs/container.png new file mode 100755 index 000000000..2a6ed01eb Binary files /dev/null and b/files/opencs/container.png differ diff --git a/files/opencs/creature.png b/files/opencs/creature.png new file mode 100755 index 000000000..99cf9c87c Binary files /dev/null and b/files/opencs/creature.png differ diff --git a/files/opencs/door.png b/files/opencs/door.png new file mode 100755 index 000000000..aa48858ef Binary files /dev/null and b/files/opencs/door.png differ diff --git a/files/opencs/ingredient.png b/files/opencs/ingredient.png new file mode 100755 index 000000000..6b36d008d Binary files /dev/null and b/files/opencs/ingredient.png differ diff --git a/files/opencs/leveled-creature.png b/files/opencs/leveled-creature.png new file mode 100755 index 000000000..ad4a7c6f8 Binary files /dev/null and b/files/opencs/leveled-creature.png differ diff --git a/files/opencs/leveled-item.png b/files/opencs/leveled-item.png new file mode 100755 index 000000000..7b8e68e60 Binary files /dev/null and b/files/opencs/leveled-item.png differ diff --git a/files/opencs/light.png b/files/opencs/light.png new file mode 100755 index 000000000..c606fcd98 Binary files /dev/null and b/files/opencs/light.png differ diff --git a/files/opencs/lockpick.png b/files/opencs/lockpick.png new file mode 100755 index 000000000..d9bd27f5e Binary files /dev/null and b/files/opencs/lockpick.png differ diff --git a/files/opencs/miscellaneous.png b/files/opencs/miscellaneous.png new file mode 100755 index 000000000..744bcd9db Binary files /dev/null and b/files/opencs/miscellaneous.png differ diff --git a/files/opencs/modified.png b/files/opencs/modified.png new file mode 100644 index 000000000..d15ad827c Binary files /dev/null and b/files/opencs/modified.png differ diff --git a/files/opencs/npc.png b/files/opencs/npc.png new file mode 100755 index 000000000..7a07f26df Binary files /dev/null and b/files/opencs/npc.png differ diff --git a/files/opencs/potion.png b/files/opencs/potion.png new file mode 100755 index 000000000..678f61fbf Binary files /dev/null and b/files/opencs/potion.png differ diff --git a/files/opencs/probe.png b/files/opencs/probe.png new file mode 100755 index 000000000..01536186d Binary files /dev/null and b/files/opencs/probe.png differ diff --git a/files/opencs/raster/GMST.png b/files/opencs/raster/GMST.png new file mode 100644 index 000000000..f24620288 Binary files /dev/null and b/files/opencs/raster/GMST.png differ diff --git a/files/opencs/raster/Info.png b/files/opencs/raster/Info.png new file mode 100644 index 000000000..d7bdad6cb Binary files /dev/null and b/files/opencs/raster/Info.png differ diff --git a/files/opencs/raster/LandTexture.png b/files/opencs/raster/LandTexture.png new file mode 100644 index 000000000..84f729098 Binary files /dev/null and b/files/opencs/raster/LandTexture.png differ diff --git a/files/opencs/raster/PathGrid.png b/files/opencs/raster/PathGrid.png new file mode 100644 index 000000000..23b6b84d7 Binary files /dev/null and b/files/opencs/raster/PathGrid.png differ diff --git a/files/opencs/raster/activator.png b/files/opencs/raster/activator.png new file mode 100644 index 000000000..32cc6f8a3 Binary files /dev/null and b/files/opencs/raster/activator.png differ diff --git a/files/opencs/raster/added.png b/files/opencs/raster/added.png new file mode 100644 index 000000000..ddd9c2108 Binary files /dev/null and b/files/opencs/raster/added.png differ diff --git a/files/opencs/raster/apparatus.png b/files/opencs/raster/apparatus.png new file mode 100644 index 000000000..3cef537e1 Binary files /dev/null and b/files/opencs/raster/apparatus.png differ diff --git a/files/opencs/raster/armor.png b/files/opencs/raster/armor.png new file mode 100644 index 000000000..fc534c7d1 Binary files /dev/null and b/files/opencs/raster/armor.png differ diff --git a/files/opencs/raster/attribute.png b/files/opencs/raster/attribute.png new file mode 100644 index 000000000..4aa5dc02e Binary files /dev/null and b/files/opencs/raster/attribute.png differ diff --git a/files/opencs/raster/base.png b/files/opencs/raster/base.png new file mode 100644 index 000000000..4398e2d68 Binary files /dev/null and b/files/opencs/raster/base.png differ diff --git a/files/opencs/raster/birthsign.png b/files/opencs/raster/birthsign.png new file mode 100644 index 000000000..8192d2ebf Binary files /dev/null and b/files/opencs/raster/birthsign.png differ diff --git a/files/opencs/raster/body-part.png b/files/opencs/raster/body-part.png new file mode 100644 index 000000000..823e43712 Binary files /dev/null and b/files/opencs/raster/body-part.png differ diff --git a/files/opencs/raster/book.png b/files/opencs/raster/book.png new file mode 100644 index 000000000..9d7669bd7 Binary files /dev/null and b/files/opencs/raster/book.png differ diff --git a/files/opencs/raster/cell.png b/files/opencs/raster/cell.png new file mode 100644 index 000000000..c4f00c1f0 Binary files /dev/null and b/files/opencs/raster/cell.png differ diff --git a/files/opencs/raster/class.png b/files/opencs/raster/class.png new file mode 100644 index 000000000..316380363 Binary files /dev/null and b/files/opencs/raster/class.png differ diff --git a/files/opencs/raster/clothing.png b/files/opencs/raster/clothing.png new file mode 100644 index 000000000..88c9b6ab8 Binary files /dev/null and b/files/opencs/raster/clothing.png differ diff --git a/files/opencs/raster/container.png b/files/opencs/raster/container.png new file mode 100644 index 000000000..2a6ed01eb Binary files /dev/null and b/files/opencs/raster/container.png differ diff --git a/files/opencs/raster/creature.png b/files/opencs/raster/creature.png new file mode 100644 index 000000000..99cf9c87c Binary files /dev/null and b/files/opencs/raster/creature.png differ diff --git a/files/opencs/raster/dialogoue-info.png b/files/opencs/raster/dialogoue-info.png new file mode 100644 index 000000000..f6743d43c Binary files /dev/null and b/files/opencs/raster/dialogoue-info.png differ diff --git a/files/opencs/raster/dialogoue-journal.png b/files/opencs/raster/dialogoue-journal.png new file mode 100644 index 000000000..b6a95c538 Binary files /dev/null and b/files/opencs/raster/dialogoue-journal.png differ diff --git a/files/opencs/raster/dialogoue-regular.png b/files/opencs/raster/dialogoue-regular.png new file mode 100644 index 000000000..f9b8d252d Binary files /dev/null and b/files/opencs/raster/dialogoue-regular.png differ diff --git a/files/opencs/raster/dialogue-greeting.png b/files/opencs/raster/dialogue-greeting.png new file mode 100644 index 000000000..a35e1fe6d Binary files /dev/null and b/files/opencs/raster/dialogue-greeting.png differ diff --git a/files/opencs/raster/dialogue-persuasion.png b/files/opencs/raster/dialogue-persuasion.png new file mode 100644 index 000000000..5bc5d6113 Binary files /dev/null and b/files/opencs/raster/dialogue-persuasion.png differ diff --git a/files/opencs/raster/dialogue-speech.png b/files/opencs/raster/dialogue-speech.png new file mode 100644 index 000000000..11eb9f1ca Binary files /dev/null and b/files/opencs/raster/dialogue-speech.png differ diff --git a/files/opencs/raster/door.png b/files/opencs/raster/door.png new file mode 100644 index 000000000..aa48858ef Binary files /dev/null and b/files/opencs/raster/door.png differ diff --git a/files/opencs/raster/enchantment.png b/files/opencs/raster/enchantment.png new file mode 100644 index 000000000..c90fb27ce Binary files /dev/null and b/files/opencs/raster/enchantment.png differ diff --git a/files/opencs/raster/faction.png b/files/opencs/raster/faction.png new file mode 100644 index 000000000..8ac1f5200 Binary files /dev/null and b/files/opencs/raster/faction.png differ diff --git a/files/opencs/raster/filter.png b/files/opencs/raster/filter.png new file mode 100644 index 000000000..94a57ecd9 Binary files /dev/null and b/files/opencs/raster/filter.png differ diff --git a/files/opencs/raster/globvar.png b/files/opencs/raster/globvar.png new file mode 100644 index 000000000..646145f0f Binary files /dev/null and b/files/opencs/raster/globvar.png differ diff --git a/files/opencs/raster/ingredient.png b/files/opencs/raster/ingredient.png new file mode 100644 index 000000000..564a93047 Binary files /dev/null and b/files/opencs/raster/ingredient.png differ diff --git a/files/opencs/raster/land.png b/files/opencs/raster/land.png new file mode 100644 index 000000000..20dd321dd Binary files /dev/null and b/files/opencs/raster/land.png differ diff --git a/files/opencs/raster/landpaint.png b/files/opencs/raster/landpaint.png new file mode 100644 index 000000000..711c0d8f5 Binary files /dev/null and b/files/opencs/raster/landpaint.png differ diff --git a/files/opencs/raster/leveled-creature.png b/files/opencs/raster/leveled-creature.png new file mode 100644 index 000000000..ad4a7c6f8 Binary files /dev/null and b/files/opencs/raster/leveled-creature.png differ diff --git a/files/opencs/raster/light.png b/files/opencs/raster/light.png new file mode 100644 index 000000000..2765ef1d3 Binary files /dev/null and b/files/opencs/raster/light.png differ diff --git a/files/opencs/raster/lockpick.png b/files/opencs/raster/lockpick.png new file mode 100644 index 000000000..d9bd27f5e Binary files /dev/null and b/files/opencs/raster/lockpick.png differ diff --git a/files/opencs/raster/magic-effect.png b/files/opencs/raster/magic-effect.png new file mode 100644 index 000000000..e672ffccb Binary files /dev/null and b/files/opencs/raster/magic-effect.png differ diff --git a/files/opencs/raster/magicrabbit.png b/files/opencs/raster/magicrabbit.png new file mode 100644 index 000000000..d1d7c8270 Binary files /dev/null and b/files/opencs/raster/magicrabbit.png differ diff --git a/files/opencs/raster/map.png b/files/opencs/raster/map.png new file mode 100644 index 000000000..3653797cc Binary files /dev/null and b/files/opencs/raster/map.png differ diff --git a/files/opencs/raster/miscellaneous.png b/files/opencs/raster/miscellaneous.png new file mode 100644 index 000000000..744bcd9db Binary files /dev/null and b/files/opencs/raster/miscellaneous.png differ diff --git a/files/opencs/raster/modified.png b/files/opencs/raster/modified.png new file mode 100644 index 000000000..39bd182ac Binary files /dev/null and b/files/opencs/raster/modified.png differ diff --git a/files/opencs/raster/npc.png b/files/opencs/raster/npc.png new file mode 100644 index 000000000..7a07f26df Binary files /dev/null and b/files/opencs/raster/npc.png differ diff --git a/files/opencs/raster/potion.png b/files/opencs/raster/potion.png new file mode 100644 index 000000000..678f61fbf Binary files /dev/null and b/files/opencs/raster/potion.png differ diff --git a/files/opencs/raster/probe.png b/files/opencs/raster/probe.png new file mode 100644 index 000000000..01536186d Binary files /dev/null and b/files/opencs/raster/probe.png differ diff --git a/files/opencs/raster/race.png b/files/opencs/raster/race.png new file mode 100644 index 000000000..94a2de696 Binary files /dev/null and b/files/opencs/raster/race.png differ diff --git a/files/opencs/raster/random-item.png b/files/opencs/raster/random-item.png new file mode 100644 index 000000000..7b8e68e60 Binary files /dev/null and b/files/opencs/raster/random-item.png differ diff --git a/files/opencs/raster/random.png b/files/opencs/raster/random.png new file mode 100644 index 000000000..2667630f5 Binary files /dev/null and b/files/opencs/raster/random.png differ diff --git a/files/opencs/raster/removed.png b/files/opencs/raster/removed.png new file mode 100644 index 000000000..2354bc743 Binary files /dev/null and b/files/opencs/raster/removed.png differ diff --git a/files/opencs/raster/repair.png b/files/opencs/raster/repair.png new file mode 100644 index 000000000..6cf1c0aac Binary files /dev/null and b/files/opencs/raster/repair.png differ diff --git a/files/opencs/raster/script.png b/files/opencs/raster/script.png new file mode 100644 index 000000000..297da4021 Binary files /dev/null and b/files/opencs/raster/script.png differ diff --git a/files/opencs/raster/skill.png b/files/opencs/raster/skill.png new file mode 100644 index 000000000..418f4f35c Binary files /dev/null and b/files/opencs/raster/skill.png differ diff --git a/files/opencs/raster/sound.png b/files/opencs/raster/sound.png new file mode 100644 index 000000000..b072acf76 Binary files /dev/null and b/files/opencs/raster/sound.png differ diff --git a/files/opencs/raster/soundgen.png b/files/opencs/raster/soundgen.png new file mode 100644 index 000000000..222fc4c7f Binary files /dev/null and b/files/opencs/raster/soundgen.png differ diff --git a/files/opencs/raster/spell.png b/files/opencs/raster/spell.png new file mode 100644 index 000000000..69c897180 Binary files /dev/null and b/files/opencs/raster/spell.png differ diff --git a/files/opencs/raster/startup/big/configure.png b/files/opencs/raster/startup/big/configure.png new file mode 100644 index 000000000..f0be888a1 Binary files /dev/null and b/files/opencs/raster/startup/big/configure.png differ diff --git a/files/opencs/raster/startup/big/create-addon.png b/files/opencs/raster/startup/big/create-addon.png new file mode 100644 index 000000000..fa059264e Binary files /dev/null and b/files/opencs/raster/startup/big/create-addon.png differ diff --git a/files/opencs/raster/startup/big/edit-content.png b/files/opencs/raster/startup/big/edit-content.png new file mode 100644 index 000000000..dbb2602e5 Binary files /dev/null and b/files/opencs/raster/startup/big/edit-content.png differ diff --git a/files/opencs/raster/startup/big/new-game.png b/files/opencs/raster/startup/big/new-game.png new file mode 100644 index 000000000..5cec44417 Binary files /dev/null and b/files/opencs/raster/startup/big/new-game.png differ diff --git a/files/opencs/raster/startup/small/configure.png b/files/opencs/raster/startup/small/configure.png new file mode 100644 index 000000000..e91b7f773 Binary files /dev/null and b/files/opencs/raster/startup/small/configure.png differ diff --git a/files/opencs/raster/startup/small/create-addon.png b/files/opencs/raster/startup/small/create-addon.png new file mode 100644 index 000000000..64fd138be Binary files /dev/null and b/files/opencs/raster/startup/small/create-addon.png differ diff --git a/files/opencs/raster/startup/small/edit-content.png b/files/opencs/raster/startup/small/edit-content.png new file mode 100644 index 000000000..6297f1169 Binary files /dev/null and b/files/opencs/raster/startup/small/edit-content.png differ diff --git a/files/opencs/raster/startup/small/new-game.png b/files/opencs/raster/startup/small/new-game.png new file mode 100644 index 000000000..0d7d14c55 Binary files /dev/null and b/files/opencs/raster/startup/small/new-game.png differ diff --git a/files/opencs/raster/static.png b/files/opencs/raster/static.png new file mode 100644 index 000000000..aedf2d30e Binary files /dev/null and b/files/opencs/raster/static.png differ diff --git a/files/opencs/raster/weapon.png b/files/opencs/raster/weapon.png new file mode 100644 index 000000000..3d4b53466 Binary files /dev/null and b/files/opencs/raster/weapon.png differ diff --git a/files/opencs/removed.png b/files/opencs/removed.png new file mode 100644 index 000000000..2ca9e094b Binary files /dev/null and b/files/opencs/removed.png differ diff --git a/files/opencs/repair.png b/files/opencs/repair.png new file mode 100755 index 000000000..6cf1c0aac Binary files /dev/null and b/files/opencs/repair.png differ diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index ecfab44a2..56e25b2c1 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -1,5 +1,33 @@ opencs.png + added.png + modified.png + removed.png + base.png + activator.png + apparatus.png + armor.png + book.png + clothing.png + container.png + creature.png + door.png + ingredient.png + leveled-creature.png + leveled-item.png + light.png + lockpick.png + miscellaneous.png + npc.png + potion.png + probe.png + repair.png + static.png + weapon.png + raster/startup/big/create-addon.png + raster/startup/big/new-game.png + raster/startup/big/edit-content.png + raster/startup/small/configure.png diff --git a/files/opencs/scalable/Palette.svg b/files/opencs/scalable/Palette.svg new file mode 100644 index 000000000..f42475330 --- /dev/null +++ b/files/opencs/scalable/Palette.svg @@ -0,0 +1,569 @@ + + + + + + + image/svg+xml + + + + + + Tango Palette + + + Tuomas Kuosmanen + + + + + Garrett Le Sage +Kenneth Wimer +Jakub Steiner + + +http://www.tango-project.org/files/Tango-Palette.svg + + + unify + global + theme + color + palette + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/files/opencs/scalable/referenceable-record/.directory b/files/opencs/scalable/referenceable-record/.directory new file mode 100644 index 000000000..98e1b7f92 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/.directory @@ -0,0 +1,7 @@ +[Dolphin] +GroupedSorting=true +SortFoldersFirst=false +Timestamp=2013,8,25,18,35,16 +Version=3 +ViewMode=2 +VisibleRoles=Compact_text,Compact_size diff --git a/files/opencs/scalable/referenceable-record/activator.svg b/files/opencs/scalable/referenceable-record/activator.svg new file mode 100644 index 000000000..e917d9332 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/activator.svg @@ -0,0 +1,1022 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/apparatus.svg b/files/opencs/scalable/referenceable-record/apparatus.svg new file mode 100644 index 000000000..37cef0e89 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/apparatus.svg @@ -0,0 +1,1058 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/book.svg b/files/opencs/scalable/referenceable-record/book.svg new file mode 100644 index 000000000..56c8e6c6f --- /dev/null +++ b/files/opencs/scalable/referenceable-record/book.svg @@ -0,0 +1,687 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/book2.svgz b/files/opencs/scalable/referenceable-record/book2.svgz new file mode 100644 index 000000000..4535a2fce Binary files /dev/null and b/files/opencs/scalable/referenceable-record/book2.svgz differ diff --git a/files/opencs/scalable/referenceable-record/container.svg b/files/opencs/scalable/referenceable-record/container.svg new file mode 100644 index 000000000..8c9465b66 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/container.svg @@ -0,0 +1,1899 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/ingredient.svg b/files/opencs/scalable/referenceable-record/ingredient.svg new file mode 100644 index 000000000..962a14ed1 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/ingredient.svg @@ -0,0 +1,1107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/light.svg b/files/opencs/scalable/referenceable-record/light.svg new file mode 100644 index 000000000..3bd5307f7 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/light.svg @@ -0,0 +1,1508 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/potion.svg b/files/opencs/scalable/referenceable-record/potion.svg new file mode 100644 index 000000000..552c14f19 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/potion.svg @@ -0,0 +1,1161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/random-item.svg b/files/opencs/scalable/referenceable-record/random-item.svg new file mode 100644 index 000000000..d051ce049 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/random-item.svg @@ -0,0 +1,156 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/repair.svg b/files/opencs/scalable/referenceable-record/repair.svg new file mode 100644 index 000000000..b1a23956e --- /dev/null +++ b/files/opencs/scalable/referenceable-record/repair.svg @@ -0,0 +1,1280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/static.svg b/files/opencs/scalable/referenceable-record/static.svg new file mode 100644 index 000000000..a6f29370f --- /dev/null +++ b/files/opencs/scalable/referenceable-record/static.svg @@ -0,0 +1,1141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/referenceable-record/weapon.svg b/files/opencs/scalable/referenceable-record/weapon.svg new file mode 100644 index 000000000..2e832fff7 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/weapon.svg @@ -0,0 +1,1206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/startup/configure.svgz b/files/opencs/scalable/startup/configure.svgz new file mode 100644 index 000000000..1275ec53f Binary files /dev/null and b/files/opencs/scalable/startup/configure.svgz differ diff --git a/files/opencs/scalable/startup/create-addon.svgz b/files/opencs/scalable/startup/create-addon.svgz new file mode 100644 index 000000000..75425667b Binary files /dev/null and b/files/opencs/scalable/startup/create-addon.svgz differ diff --git a/files/opencs/scalable/startup/edit-content.svgz b/files/opencs/scalable/startup/edit-content.svgz new file mode 100644 index 000000000..049f1e813 Binary files /dev/null and b/files/opencs/scalable/startup/edit-content.svgz differ diff --git a/files/opencs/scalable/startup/new-game.svgz b/files/opencs/scalable/startup/new-game.svgz new file mode 100644 index 000000000..e81571911 Binary files /dev/null and b/files/opencs/scalable/startup/new-game.svgz differ diff --git a/files/opencs/scalable/status/.directory b/files/opencs/scalable/status/.directory new file mode 100644 index 000000000..e2d80ed58 --- /dev/null +++ b/files/opencs/scalable/status/.directory @@ -0,0 +1,5 @@ +[Dolphin] +PreviewsShown=true +Timestamp=2013,3,21,10,19,49 +Version=3 +ViewMode=1 diff --git a/files/opencs/scalable/status/added.svg b/files/opencs/scalable/status/added.svg new file mode 100644 index 000000000..d83649f50 --- /dev/null +++ b/files/opencs/scalable/status/added.svg @@ -0,0 +1,932 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/files/opencs/scalable/status/base.svg b/files/opencs/scalable/status/base.svg new file mode 100644 index 000000000..90c9bc319 --- /dev/null +++ b/files/opencs/scalable/status/base.svg @@ -0,0 +1,942 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/files/opencs/scalable/status/modified.svg b/files/opencs/scalable/status/modified.svg new file mode 100644 index 000000000..8d2ec4710 --- /dev/null +++ b/files/opencs/scalable/status/modified.svg @@ -0,0 +1,1155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/files/opencs/scalable/status/removed.svg b/files/opencs/scalable/status/removed.svg new file mode 100644 index 000000000..c879af91f --- /dev/null +++ b/files/opencs/scalable/status/removed.svg @@ -0,0 +1,935 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/files/opencs/scalable/top-level/gmst.svg b/files/opencs/scalable/top-level/gmst.svg new file mode 100644 index 000000000..3b59a44fe --- /dev/null +++ b/files/opencs/scalable/top-level/gmst.svg @@ -0,0 +1,1047 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/opencs/scalable/top-level/topic-regular.svg b/files/opencs/scalable/top-level/topic-regular.svg new file mode 100644 index 000000000..c972dfa18 --- /dev/null +++ b/files/opencs/scalable/top-level/topic-regular.svg @@ -0,0 +1,1045 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/files/opencs/static.png b/files/opencs/static.png new file mode 100755 index 000000000..b53be12d9 Binary files /dev/null and b/files/opencs/static.png differ diff --git a/files/opencs/weapon.png b/files/opencs/weapon.png new file mode 100755 index 000000000..3d4b53466 Binary files /dev/null and b/files/opencs/weapon.png differ diff --git a/files/openmw.cfg b/files/openmw.cfg index b37e5a22b..15fde1bcb 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,4 +1,4 @@ data="?global?data" data="?mw?Data Files" -data-local="?local?data" +data-local="?user?data" resources=${MORROWIND_RESOURCE_FILES} diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index dd116e108..d6ca2d554 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -1,2 +1,5 @@ +data="?global?data" +data="?mw?Data Files" data=./data +data-local="?user?data" resources=./resources diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 69aa20883..f191430df 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -6,6 +6,7 @@ resolution x = 800 resolution y = 600 fullscreen = false +screen = 0 # Render system # blank means default @@ -79,6 +80,7 @@ texture size = 1024 actor shadows = true misc shadows = true statics shadows = true +terrain shadows = true # Fraction of the total shadow distance after which the shadow starts to fade out fade start = 0.8 @@ -124,8 +126,9 @@ fog start factor = 0.5 fog end factor = 1.0 [Terrain] -# Max. number of lights that affect the terrain. Setting to 1 will only reflect sunlight -num lights = 8 +distant land = false + +shader = true [Water] shader = true @@ -162,3 +165,7 @@ ui sensitivity = 1.0 camera y multiplier = 1.0 ui y multiplier = 1.0 + +[Game] +# Always use the most powerful attack when striking with a weapon (chop, slash or thrust) +best attack = false diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 5c498d4d5..041a9576d 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -6,8 +6,8 @@ 0 0 - 520 - 256 + 518 + 304 @@ -112,7 +112,9 @@ - + + + New Profile @@ -126,7 +128,9 @@ - + + + Delete Profile diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 5c330cebd..7e9fe00d9 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -58,6 +58,13 @@ + + + Screen: + + + + Resolution: @@ -71,6 +78,9 @@ + + + diff --git a/files/ui/playpage.ui b/files/ui/playpage.ui index c0320de1e..bf883b96e 100644 --- a/files/ui/playpage.ui +++ b/files/ui/playpage.ui @@ -2,9 +2,17 @@ PlayPage + + + 0 + 0 + 274 + 317 + + - + #Scroll { background-image: url(":/images/playpage-background.png"); @@ -13,15 +21,6 @@ } - - QFrame::StyledPanel - - - QFrame::Plain - - - 0 - 30 diff --git a/libs/openengine/bullet/BtOgre.cpp b/libs/openengine/bullet/BtOgre.cpp index f50465159..b0fa07fd6 100644 --- a/libs/openengine/bullet/BtOgre.cpp +++ b/libs/openengine/bullet/BtOgre.cpp @@ -293,7 +293,7 @@ namespace BtOgre { { const Ogre::Vector3 sz = getSize(); - assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && + assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.z > 0.0) && ("Size of box must be greater than zero on all axes")); btBoxShape* shape = new btBoxShape(Convert::toBullet(sz * 0.5)); @@ -308,7 +308,7 @@ namespace BtOgre { { const Ogre::Vector3 sz = getSize(); - assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && + assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.z > 0.0) && ("Size of Cylinder must be greater than zero on all axes")); btCylinderShape* shape = new btCylinderShapeX(Convert::toBullet(sz * 0.5)); diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index 8744c4e4d..5528924a9 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -19,8 +19,8 @@ Ogre::Resource(creator, name, handle, group, isManual, loader) */ mCollisionShape = NULL; mRaycastingShape = NULL; + mHasCollisionNode = false; mCollide = true; - mIgnore = false; createParamDictionary("BulletShape"); } @@ -104,6 +104,20 @@ BulletShapeManager::~BulletShapeManager() sThis = 0; } +#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0)) +BulletShapePtr BulletShapeManager::getByName(const Ogre::String& name, const Ogre::String& groupName) +{ + return getResourceByName(name, groupName).staticCast(); +} + +BulletShapePtr BulletShapeManager::create (const Ogre::String& name, const Ogre::String& group, + bool isManual, Ogre::ManualResourceLoader* loader, + const Ogre::NameValuePairList* createParams) +{ + return createResource(name,group,isManual,loader,createParams).staticCast(); +} +#endif + BulletShapePtr BulletShapeManager::load(const Ogre::String &name, const Ogre::String &group) { BulletShapePtr textf = getByName(name); diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index fa03b0276..98cda859d 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -35,17 +35,21 @@ public: 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; }; /** * */ + +#if (OGRE_VERSION < ((1 << 16) | (9 << 8) | 0)) class BulletShapePtr : public Ogre::SharedPtr { public: @@ -89,9 +93,9 @@ public: return *this; } }; - - - +#else +typedef Ogre::SharedPtr BulletShapePtr; +#endif /** *Hold any BulletShape that was created by the ManualBulletShapeLoader. @@ -135,6 +139,19 @@ public: BulletShapeManager(); virtual ~BulletShapeManager(); + +#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0)) + /// Get a resource by name + /// @see ResourceManager::getByName + BulletShapePtr getByName(const Ogre::String& name, const Ogre::String& groupName = Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); + + /// Create a new shape + /// @see ResourceManager::createResource + BulletShapePtr create (const Ogre::String& name, const Ogre::String& group, + bool isManual = false, Ogre::ManualResourceLoader* loader = 0, + const Ogre::NameValuePairList* createParams = 0); +#endif + virtual BulletShapePtr load(const Ogre::String &name, const Ogre::String &group); static BulletShapeManager &getSingleton(); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index c4947af1e..e33edda18 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -19,16 +19,14 @@ namespace Physic PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) : mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0) - , mBody(0), onGround(false), collisionMode(true), mBoxRotation(0,0,0,0), verticalForce(0.0f) + , mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0) + , mForce(0.0f) { - // FIXME: Force player to start in no-collision mode for now, until he spawns at a proper door marker. - if(name == "player") - collisionMode = false; mBody = mEngine->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, mRaycastingBody); //Add rigid body to dynamics world, but do not add to object map + mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } PhysicActor::~PhysicActor() @@ -47,14 +45,16 @@ namespace Physic void PhysicActor::enableCollisions(bool collision) { - if(collision && !collisionMode) mBody->translate(btVector3(0,0,-1000)); - if(!collision && collisionMode) mBody->translate(btVector3(0,0,1000)); - collisionMode = collision; + assert(mBody); + if(collision && !mCollisionMode) enableCollisionBody(); + if(!collision && mCollisionMode) disableCollisionBody(); + mCollisionMode = collision; } void PhysicActor::setPosition(const Ogre::Vector3 &pos) { + assert(mBody); if(pos != getPosition()) { mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); @@ -64,6 +64,7 @@ namespace Physic 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); @@ -74,6 +75,7 @@ namespace Physic 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()); @@ -84,23 +86,28 @@ 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::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 + 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 @@ -117,24 +124,24 @@ namespace Physic return Ogre::Vector3(0.0f); } - void PhysicActor::setVerticalForce(float force) + void PhysicActor::setInertialForce(const Ogre::Vector3 &force) { - verticalForce = force; + mForce = force; } - float PhysicActor::getVerticalForce() const + void PhysicActor::setOnGround(bool grounded) { - return verticalForce; + mOnGround = grounded; } - void PhysicActor::setOnGround(bool grounded) + void PhysicActor::disableCollisionBody() { - onGround = grounded; + mEngine->dynamicsWorld->removeRigidBody(mBody); } - bool PhysicActor::getOnGround() const + void PhysicActor::enableCollisionBody() { - return collisionMode && onGround; + mEngine->dynamicsWorld->addRigidBody(mBody,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -144,6 +151,7 @@ namespace Physic RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name) : btRigidBody(CI) , mName(name) + , mPlaceable(false) { } @@ -161,6 +169,7 @@ namespace Physic PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) : mDebugActive(0) + , mSceneMgr(NULL) { // Set up the collision configuration and dispatcher collisionConfiguration = new btDefaultCollisionConfiguration(); @@ -257,8 +266,8 @@ 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) { @@ -321,7 +330,8 @@ namespace Physic mHeightFieldMap [name] = hf; - dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal); + dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting, + CollisionType_World|CollisionType_Actor|CollisionType_Raycasting); } void PhysicEngine::removeHeightField(int x, int y) @@ -368,7 +378,7 @@ namespace Physic 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) + Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation, bool raycasting, bool placeable) { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; @@ -378,6 +388,9 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); + if (placeable && !raycasting && shape->mCollisionShape && !shape->mHasCollisionNode) + return NULL; + if (!shape->mCollisionShape && !raycasting) return NULL; if (!shape->mRaycastingShape && raycasting) @@ -395,6 +408,7 @@ namespace Physic btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo (0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); RigidBody* body = new RigidBody(CI,name); + body->mPlaceable = placeable; if(scaledBoxTranslation != 0) *scaledBoxTranslation = shape->mBoxTranslation * scale; @@ -407,15 +421,17 @@ namespace Physic } - void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody) + void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor) { if(!body && !raycastingBody) return; // nothing to do const std::string& name = (body ? body->mName : raycastingBody->mName); - if (body) - dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal); + 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); @@ -459,7 +475,7 @@ namespace Physic if (it != mCollisionObjectMap.end() ) { RigidBody* body = it->second; - + if(body != NULL) { delete body; @@ -494,10 +510,120 @@ namespace Physic } } + 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 && !(colObj0Wrap->m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup + & CollisionType_Raycasting)) + 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 && !(col0->getBroadphaseHandle()->m_collisionFilterGroup + & CollisionType_Raycasting)) + mResult.push_back(body->mName); + + return 0.f; + } +#endif + }; + + class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback + { + const std::string &mFilter; + // Store the real origin, since the shape's origin is its center + btVector3 mOrigin; + + public: + const RigidBody *mObject; + btVector3 mContactPoint; + btScalar mLeastDistSqr; + + DeepestNotMeContactTestResultCallback(const std::string &filter, const btVector3 &origin) + : mFilter(filter), mOrigin(origin), mObject(0), mContactPoint(0,0,0), + mLeastDistSqr(std::numeric_limits::max()) + { } + +#if defined(BT_COLLISION_OBJECT_WRAPPER_H) + virtual btScalar addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) + { + const RigidBody* body = dynamic_cast(col1Wrap->m_collisionObject); + if(body && body->mName != mFilter) + { + btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); + if(!mObject || distsqr < mLeastDistSqr) + { + mObject = body; + mLeastDistSqr = distsqr; + mContactPoint = cp.getPositionWorldOnA(); + } + } + + 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(col1); + if(body && body->mName != mFilter) + { + btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); + if(!mObject || distsqr < mLeastDistSqr) + { + mObject = body; + mLeastDistSqr = distsqr; + mContactPoint = cp.getPositionWorldOnA(); + } + } + + 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; + } + + + std::pair PhysicEngine::getFilteredContact(const std::string &filter, + const btVector3 &origin, + btCollisionObject *object) + { + DeepestNotMeContactTestResultCallback callback(filter, origin); + dynamicsWorld->contactTest(object, callback); + return std::make_pair(callback.mObject, callback.mContactPoint); + } + + void PhysicEngine::stepSimulation(double deltaT) { - // This isn't needed as there are no dynamic objects at this point - //dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); + // This seems to be needed for character controller objects + dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { mDebugDrawer->step(); @@ -512,33 +638,33 @@ namespace Physic removeCharacter(name); PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale); - + //dynamicsWorld->addAction( newActor->mCharacter ); - PhysicActorMap[name] = newActor; + mActorMap[name] = newActor; } void PhysicEngine::removeCharacter(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 = it->second; if(act != NULL) { - + delete act; } - PhysicActorMap.erase(it); + mActorMap.erase(it); } } 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 @@ -551,25 +677,64 @@ 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 = CollisionType_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()) { name = static_cast(*resultCallback1.m_collisionObject).mName; - d1 = resultCallback1.m_closestHitFraction; - d = d1; + d = resultCallback1.m_closestHitFraction;; } 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) + { + if (const RigidBody* body = dynamic_cast(convexResult.m_hitCollisionObject)) + if (body->mName == "player") + return 0; + return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); + } + }; + + 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; @@ -603,6 +768,32 @@ namespace Physic btTransform trans; trans.setIdentity(); - shape->mCollisionShape->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 367db261f..f28f95ccb 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -46,8 +46,8 @@ namespace Physic enum CollisionType { CollisionType_Nothing = 0, //= 1600 - void * operator new (size_t Size) { return _aligned_malloc (Size, 16); } - void operator delete (void * Data) { _aligned_free (Data); } + void * operator new (size_t Size) { return _aligned_malloc (Size, 16); } + void operator delete (void * Data) { _aligned_free (Data); } #endif private: - OEngine::Physic::RigidBody* mBody; OEngine::Physic::RigidBody* mRaycastingBody; + Ogre::Vector3 mBoxScaledTranslation; - btQuaternion mBoxRotationInverse; Ogre::Quaternion mBoxRotation; - float verticalForce; - bool onGround; - bool collisionMode; + btQuaternion mBoxRotationInverse; + + Ogre::Vector3 mForce; + bool mOnGround; + bool mCollisionMode; + std::string mMesh; - PhysicEngine* mEngine; std::string mName; + PhysicEngine *mEngine; }; - /** - *This class is just an extension of normal btRigidBody in order to add extra info. - *When bullet give back a btRigidBody, you can just do a static_cast to RigidBody, - *so one never should use btRigidBody directly! - */ - class RigidBody: public btRigidBody - { - public: - RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); - virtual ~RigidBody(); - std::string mName; - }; struct HeightField { @@ -197,7 +216,7 @@ namespace Physic */ RigidBody* createAndAdjustRigidBody(const std::string &mesh, const std::string &name, float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, - Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0, bool raycasting=false); + Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0, bool raycasting=false, bool placeable=false); /** * Adjusts a rigid body to the right position and rotation @@ -225,7 +244,7 @@ namespace Physic /** * Add a RigidBody to the simulation */ - void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL); + void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL,bool actor = false); /** * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. @@ -287,16 +306,29 @@ namespace Physic void setSceneManager(Ogre::SceneManager* sceneMgr); + bool isAnyActorStandingOn (const std::string& objectName); + /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). */ - std::pair 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); + + // Get the nearest object that's inside the given object, filtering out objects of the + // provided name + std::pair getFilteredContact(const std::string &filter, + const btVector3 &origin, + btCollisionObject *object); + //event list of non player object std::list NPEventList; @@ -323,7 +355,7 @@ namespace Physic RigidBodyContainer mRaycastingObjectMap; typedef std::map PhysicActorContainer; - PhysicActorContainer PhysicActorMap; + PhysicActorContainer mActorMap; Ogre::SceneManager* mSceneMgr; diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index cc443f267..afda52448 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -8,41 +8,123 @@ #include "physic.hpp" -enum traceWorldType + +namespace OEngine +{ +namespace Physic { - collisionWorldTrace = 1, - pickWorldTrace = 2, - bothWorldTrace = collisionWorldTrace | pickWorldTrace + +class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback +{ +public: + ClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot) + : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), + mMe(me), mUp(up), mMinSlopeDot(minSlopeDot) + { + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if(convexResult.m_hitCollisionObject == mMe) + return btScalar( 1 ); + + btVector3 hitNormalWorld; + if(normalInWorldSpace) + hitNormalWorld = convexResult.m_hitNormalLocal; + else + { + ///need to transform normal into worldspace + hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; + } + + // NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box... + + btScalar dotUp = mUp.dot(hitNormalWorld); + if(dotUp < mMinSlopeDot) + return btScalar(1); + + return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); + } + +protected: + btCollisionObject *mMe; + const btVector3 mUp; + const btScalar mMinSlopeDot; }; -void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine *enginePass) //Traceobj was a Aedra Object + +void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass) { - 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(0.0f, 0.0f, 0.0f); //y, x, z + const btVector3 btstart(start.x, start.y, start.z); + const btVector3 btend(end.x, end.y, end.z); - 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); + const btTransform &trans = actor->getWorldTransform(); + btTransform from(trans); + btTransform to(trans); + from.setOrigin(btstart); + to.setOrigin(btend); - btCollisionWorld::ClosestConvexResultCallback newTraceCallback(btstart, btend); - newTraceCallback.m_collisionFilterMask = OEngine::Physic::CollisionType_World; + ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | + CollisionType_Actor; - enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback); + btCollisionShape *shape = actor->getCollisionShape(); + assert(shape->isConvex()); + enginePass->dynamicsWorld->convexSweepTest(static_cast(shape), + 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; + mFraction = newTraceCallback.m_closestHitFraction; + mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + mEndPos = (end-start)*mFraction + start; } else { - results->endpos = end; - results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); - results->fraction = 1.0f; + mEndPos = end; + mPlaneNormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); + mFraction = 1.0f; } } + +void ActorTracer::findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass) +{ + const btVector3 btstart(start.x, start.y, start.z+1.0f); + const btVector3 btend(end.x, end.y, end.z+1.0f); + + const btTransform &trans = actor->getWorldTransform(); + btTransform from(trans.getBasis(), btstart); + btTransform to(trans.getBasis(), btend); + + ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | + CollisionType_Actor; + + const btBoxShape *shape = dynamic_cast(actor->getCollisionShape()); + assert(shape); + + btVector3 halfExtents = shape->getHalfExtentsWithMargin(); + halfExtents[2] = 1.0f; + btBoxShape box(halfExtents); + + enginePass->dynamicsWorld->convexSweepTest(&box, from, to, newTraceCallback); + if(newTraceCallback.hasHit()) + { + const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; + mFraction = newTraceCallback.m_closestHitFraction; + mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + mEndPos = (end-start)*mFraction + start; + mEndPos[2] -= 1.0f; + } + else + { + mEndPos = end; + mPlaneNormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); + mFraction = 1.0f; + } +} + +} +} diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index f484497d9..92795c87f 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -4,23 +4,28 @@ #include -namespace OEngine -{ - namespace Physic - { - class PhysicEngine; - } -} +class btCollisionObject; -struct traceResults +namespace OEngine { - Ogre::Vector3 endpos; - Ogre::Vector3 planenormal; +namespace Physic +{ + class PhysicEngine; + + struct ActorTracer + { + Ogre::Vector3 mEndPos; + Ogre::Vector3 mPlaneNormal; - float fraction; -}; + float mFraction; -void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); + void doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, + const PhysicEngine *enginePass); + void findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, + const PhysicEngine *enginePass); + }; +} +} #endif diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index c9b561400..91937d24b 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -2,11 +2,18 @@ #include #include +#include #include +#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. @@ -20,12 +27,544 @@ 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), + mVertexProgramNoTexture(NULL), + mFragmentProgramNoTexture(NULL), + mVertexProgramOneTexture(NULL), + mFragmentProgramOneTexture(NULL) + { + } + + 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; @@ -41,15 +580,21 @@ 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 @@ -59,7 +604,20 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd) { - mRenderManager->setRenderWindow (wnd); + if (mShaderRenderManager) + { + mShaderRenderManager->setRenderWindow (wnd); + mShaderRenderManager->setActiveViewport(0); + } + else + { + mRenderManager->setRenderWindow (wnd); + mRenderManager->setActiveViewport(0); + } +} + +void MyGUIManager::windowResized() +{ mRenderManager->setActiveViewport(0); } @@ -73,6 +631,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 16673ef98..cca70dfcf 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -9,6 +9,7 @@ namespace MyGUI class LogManager; class OgreDataManager; class OgreRenderManager; + class ShaderBasedRenderManager; } namespace Ogre @@ -26,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); @@ -42,6 +43,8 @@ namespace GUI void updateWindow (Ogre::RenderWindow* wnd); + void windowResized(); + void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")); void shutdown(); diff --git a/libs/openengine/input/dispatch_map.hpp b/libs/openengine/input/dispatch_map.hpp deleted file mode 100644 index be13e7f01..000000000 --- a/libs/openengine/input/dispatch_map.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef OENGINE_INPUT_DISPATCHMAP_H -#define OENGINE_INPUT_DISPATCHMAP_H - -#include -#include -#include - -namespace OEngine { -namespace Input { - -/** - DispatchMap is a simple connection system that connects incomming - signals with outgoing signals. - - The signals can be connected one-to-one, many-to-one, one-to-many - or many-to-many. - - The dispatch map is completely system agnostic. It is a pure data - structure and all signals are just integer indices. It does not - delegate any actions, but used together with Dispatcher it can be - used to build an event system. - */ -struct DispatchMap -{ - typedef std::set OutList; - typedef std::map InMap; - - typedef OutList::iterator Oit; - typedef InMap::iterator Iit; - - InMap map; - - void bind(int in, int out) - { - map[in].insert(out); - } - - void unbind(int in, int out) - { - Iit it = map.find(in); - if(it != map.end()) - { - it->second.erase(out); - - // If there are no more elements, then remove the entire list - if(it->second.empty()) - map.erase(it); - } - } - - /// Check if a given input is bound to anything - bool isBound(int in) const - { - return map.find(in) != map.end(); - } - - /** - Get the list of outputs bound to the given input. Only call this - on inputs that you know are bound to something. - - The returned set is only intended for immediate iteration. Do not - store references to it. - */ - const OutList &getList(int in) const - { - assert(isBound(in)); - InMap::const_iterator it = map.find(in); - assert(it != map.end()); - const OutList &out = it->second; - assert(!out.empty()); - return out; - } -}; -}} -#endif diff --git a/libs/openengine/input/dispatcher.hpp b/libs/openengine/input/dispatcher.hpp deleted file mode 100644 index a8d480d4b..000000000 --- a/libs/openengine/input/dispatcher.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef OENGINE_INPUT_DISPATCHER_H -#define OENGINE_INPUT_DISPATCHER_H - -#include -#include - -#include - -#include "dispatch_map.hpp" -#include "func_binder.hpp" - -namespace OEngine { -namespace Input { - -struct Dispatcher : Mangle::Input::Event -{ - DispatchMap map; - FuncBinder funcs; - - /** - Constructor. Takes the number of actions and passes it to - FuncBinder. - */ - Dispatcher(int actions) : funcs(actions) {} - - void bind(unsigned int action, int key) - { - assert(action < funcs.getSize()); - map.bind(key, action); - } - void unbind(unsigned int action, int key) - { - assert(action < funcs.getSize()); - map.unbind(key, action); - } - bool isBound(int key) const { return map.isBound(key); } - - /** - Instigate an event. It is translated through the dispatch map and - sent to the function bindings. - */ - typedef DispatchMap::OutList _O; - void event(Type type, int index, const void* p) - { - // No bindings, nothing happens - if(!isBound(index)) - return; - - // Get the mapped actions and execute them - const _O &list = map.getList(index); - _O::const_iterator it; - for(it = list.begin(); it != list.end(); it++) - { - //catch exceptions thrown in the input handlers so that pressing a key - //doesn't cause OpenMw to crash - try - { - funcs.call(*it, p); - } - catch(const std::exception& e) - { - std::cerr << "Exception in input handler: " << e.what() << std::endl; - } - catch(...) - { - std::cerr << "Unknown exception in input handler" << std::endl; - } - - } - } -}; - -// This helps us play nice with Mangle's EventPtr, but it should -// really be defined for all the classes in OEngine. - typedef boost::shared_ptr DispatcherPtr; - -}} -#endif diff --git a/libs/openengine/input/func_binder.hpp b/libs/openengine/input/func_binder.hpp deleted file mode 100644 index a815ba0ce..000000000 --- a/libs/openengine/input/func_binder.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef OENGINE_INPUT_FUNCBINDER_H -#define OENGINE_INPUT_FUNCBINDER_H - -#include -#include -#include -#include - -namespace OEngine { -namespace Input { - -/** - An Action defines the user defined action corresponding to a - binding. - - The first parameter is the action index that invoked this call. You - can assign the same function to multiple actions, and this can help - you keep track of which action was invoked. - - The second parameter is an optional user-defined parameter, - represented by a void pointer. In many cases it is practical to - point this to temporaries (stack values), so make sure not to store - permanent references to it unless you've planning for this on the - calling side as well. - */ -typedef boost::function Action; - -/** - The FuncBinder is a simple struct that binds user-defined indices - to functions. It is useful for binding eg. keyboard events to - specific actions in your program, but can potentially have many - other uses as well. - */ -class FuncBinder -{ - struct FuncBinding - { - std::string name; - Action action; - }; - - std::vector bindings; - -public: - /** - Constructor. Initialize the struct by telling it how many action - indices you intend to bind. - - The indices you use should be 0 <= i < number. - */ - FuncBinder(int number) : bindings(number) {} - - unsigned int getSize() { return bindings.size(); } - - /** - Bind an action to an index. - */ - void bind(int index, Action action, const std::string &name="") - { - assert(index >= 0 && index < (int)bindings.size()); - - FuncBinding &fb = bindings[index]; - fb.action = action; - fb.name = name; - } - - /** - Unbind an index, reverting a previous bind(). - */ - void unbind(int index) - { - assert(index >= 0 && index < (int)bindings.size()); - - bindings[index] = FuncBinding(); - } - - /** - Call a specific action. Takes an optional parameter that is - passed to the action. - */ - void call(int index, const void *p=NULL) const - { - assert(index >= 0 && index < (int)bindings.size()); - - const FuncBinding &fb = bindings[index]; - if(fb.action) fb.action(index, p); - } - - /// Check if a given index is bound to anything - bool isBound(int index) const - { - assert(index >= 0 && index < (int)bindings.size()); - - return !bindings[index].action.empty(); - } - - /// Return the name associated with an action (empty if not bound) - const std::string &getName(int index) const - { - assert(index >= 0 && index < (int)bindings.size()); - - return bindings[index].name; - } -}; -}} -#endif diff --git a/libs/openengine/input/poller.hpp b/libs/openengine/input/poller.hpp deleted file mode 100644 index c544aed52..000000000 --- a/libs/openengine/input/poller.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef OENGINE_INPUT_POLLER_H -#define OENGINE_INPUT_POLLER_H - -#include "dispatch_map.hpp" -#include - -namespace OEngine { -namespace Input { - -/** The poller is used to check (poll) for keys rather than waiting - for events. */ -struct Poller -{ - DispatchMap map; - Mangle::Input::Driver &input; - - Poller(Mangle::Input::Driver &drv) - : input(drv) {} - - /** Bind or unbind a given action with a key. The action is the first - parameter, the key is the second. - */ - void bind(int in, int out) { map.bind(in, out); } - void unbind(int in, int out) { map.unbind(in, out); } - bool isBound(int in) const { return map.isBound(in); } - - /// Check whether a given action button is currently pressed. - typedef DispatchMap::OutList _O; - bool isDown(int index) const - { - // No bindings, no action - if(!isBound(index)) - return false; - - // Get all the keys bound to this action, and check them. - const _O &list = map.getList(index); - _O::const_iterator it; - for(it = list.begin(); it != list.end(); it++) - // If there's any match, we're good to go. - if(input.isDown(*it)) return true; - - return false; - } -}; -}} -#endif diff --git a/libs/openengine/input/tests/Makefile b/libs/openengine/input/tests/Makefile deleted file mode 100644 index 91a0b2663..000000000 --- a/libs/openengine/input/tests/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -GCC=g++ - -all: funcbind_test dispatch_map_test sdl_driver_test sdl_binder_test - -funcbind_test: funcbind_test.cpp ../func_binder.hpp - $(GCC) $< -o $@ - -dispatch_map_test: dispatch_map_test.cpp ../dispatch_map.hpp - $(GCC) $< -o $@ - -sdl_driver_test: sdl_driver_test.cpp - $(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../ - -sdl_binder_test: sdl_binder_test.cpp - $(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../ - -clean: - rm *_test diff --git a/libs/openengine/input/tests/dispatch_map_test.cpp b/libs/openengine/input/tests/dispatch_map_test.cpp deleted file mode 100644 index 5f262494e..000000000 --- a/libs/openengine/input/tests/dispatch_map_test.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include -using namespace std; - -#include "../dispatch_map.hpp" - -using namespace OEngine::Input; - -typedef DispatchMap::OutList OutList; -typedef OutList::const_iterator Cit; - -void showList(const DispatchMap::OutList &out) -{ - for(Cit it = out.begin(); - it != out.end(); it++) - { - cout << " " << *it << endl; - } -} - -void showAll(DispatchMap &map) -{ - cout << "\nPrinting everything:\n"; - for(DispatchMap::Iit it = map.map.begin(); - it != map.map.end(); it++) - { - cout << it->first << ":\n"; - showList(map.getList(it->first)); - } -} - -int main() -{ - cout << "Testing the dispatch map\n"; - - DispatchMap dsp; - - dsp.bind(1,9); - dsp.bind(2,-5); - dsp.bind(2,9); - dsp.bind(3,10); - dsp.bind(3,12); - dsp.bind(3,10); - - showAll(dsp); - - dsp.unbind(1,9); - dsp.unbind(5,8); - dsp.unbind(3,11); - dsp.unbind(3,12); - dsp.unbind(3,12); - - showAll(dsp); - return 0; -} diff --git a/libs/openengine/input/tests/funcbind_test.cpp b/libs/openengine/input/tests/funcbind_test.cpp deleted file mode 100644 index bb4ad34e1..000000000 --- a/libs/openengine/input/tests/funcbind_test.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -using namespace std; - -#include "../func_binder.hpp" - -void f1(int i, const void *p) -{ - cout << " F1 i=" << i << endl; - - if(p) - cout << " Got a nice gift: " - << *((const float*)p) << endl; -} - -void f2(int i, const void *p) -{ - cout << " F2 i=" << i << endl; -} - -using namespace OEngine::Input; - -int main() -{ - cout << "This will test the function binding system\n"; - - FuncBinder bnd(5); - - bnd.bind(0, &f1, "This is action 1"); - bnd.bind(1, &f2); - bnd.bind(2, &f1, "This is action 3"); - bnd.bind(3, &f2, "This is action 4"); - - bnd.unbind(2); - - for(int i=0; i<5; i++) - { - cout << "Calling " << i << ": '" << bnd.getName(i) << "'\n"; - bnd.call(i); - if(!bnd.isBound(i)) cout << " (not bound)\n"; - } - - cout << "\nCalling with parameter:\n"; - float f = 3.1415; - bnd.call(0, &f); - - return 0; -} diff --git a/libs/openengine/input/tests/output/dispatch_map_test.out b/libs/openengine/input/tests/output/dispatch_map_test.out deleted file mode 100644 index 01aa9d9d9..000000000 --- a/libs/openengine/input/tests/output/dispatch_map_test.out +++ /dev/null @@ -1,18 +0,0 @@ -Testing the dispatch map - -Printing everything: -1: - 9 -2: - -5 - 9 -3: - 10 - 12 - -Printing everything: -2: - -5 - 9 -3: - 10 diff --git a/libs/openengine/input/tests/output/funcbind_test.out b/libs/openengine/input/tests/output/funcbind_test.out deleted file mode 100644 index 862c5c972..000000000 --- a/libs/openengine/input/tests/output/funcbind_test.out +++ /dev/null @@ -1,15 +0,0 @@ -This will test the function binding system -Calling 0: 'This is action 1' - F1 i=0 -Calling 1: '' - F2 i=1 -Calling 2: '' - (not bound) -Calling 3: 'This is action 4' - F2 i=3 -Calling 4: '' - (not bound) - -Calling with parameter: - F1 i=0 - Got a nice gift: 3.1415 diff --git a/libs/openengine/input/tests/output/sdl_binder_test.out b/libs/openengine/input/tests/output/sdl_binder_test.out deleted file mode 100644 index fd4eb90e3..000000000 --- a/libs/openengine/input/tests/output/sdl_binder_test.out +++ /dev/null @@ -1,4 +0,0 @@ -Hold the Q key to quit: -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/openengine/input/tests/output/sdl_driver_test.out b/libs/openengine/input/tests/output/sdl_driver_test.out deleted file mode 100644 index fd4eb90e3..000000000 --- a/libs/openengine/input/tests/output/sdl_driver_test.out +++ /dev/null @@ -1,4 +0,0 @@ -Hold the Q key to quit: -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/openengine/input/tests/sdl_binder_test.cpp b/libs/openengine/input/tests/sdl_binder_test.cpp deleted file mode 100644 index 7de5f5d4f..000000000 --- a/libs/openengine/input/tests/sdl_binder_test.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include -#include "../dispatcher.hpp" -#include "../poller.hpp" - -using namespace std; -using namespace Mangle::Input; -using namespace OEngine::Input; - -enum Actions - { - A_Quit, - A_Left, - A_Right, - - A_LAST - }; - -bool quit=false; - -void doExit(int,const void*) -{ - quit = true; -} - -void goLeft(int,const void*) -{ - cout << "Going left\n"; -} - -int main(int argc, char** argv) -{ - SDL_Init(SDL_INIT_VIDEO); - SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); - SDLDriver input; - Dispatcher *disp = new Dispatcher(A_LAST); - Poller poll(input); - - input.setEvent(EventPtr(disp)); - - disp->funcs.bind(A_Quit, &doExit); - disp->funcs.bind(A_Left, &goLeft); - - disp->bind(A_Quit, SDLK_q); - disp->bind(A_Left, SDLK_a); - disp->bind(A_Left, SDLK_LEFT); - - poll.bind(A_Right, SDLK_d); - poll.bind(A_Right, SDLK_RIGHT); - - cout << "Hold the Q key to quit:\n"; - //input->setEvent(&mycb); - while(!quit) - { - input.capture(); - if(poll.isDown(A_Right)) - cout << "We're going right!\n"; - SDL_Delay(20); - - if(argc == 1) - { - cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; - break; - } - } - cout << "\nBye bye!\n"; - - SDL_Quit(); - return 0; -} diff --git a/libs/openengine/input/tests/sdl_driver_test.cpp b/libs/openengine/input/tests/sdl_driver_test.cpp deleted file mode 100644 index 1771bcfe4..000000000 --- a/libs/openengine/input/tests/sdl_driver_test.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include - -using namespace std; -using namespace Mangle::Input; - -int main(int argc, char** argv) -{ - SDL_Init(SDL_INIT_VIDEO); - SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); - SDLDriver input; - - cout << "Hold the Q key to quit:\n"; - //input->setEvent(&mycb); - while(!input.isDown(SDLK_q)) - { - input.capture(); - SDL_Delay(20); - - if(argc == 1) - { - cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; - break; - } - } - cout << "\nBye bye!\n"; - - SDL_Quit(); - return 0; -} diff --git a/libs/openengine/input/tests/test.sh b/libs/openengine/input/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/libs/openengine/input/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done 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 new file mode 100644 index 000000000..9c32924f1 --- /dev/null +++ b/libs/openengine/ogre/imagerotate.cpp @@ -0,0 +1,88 @@ +#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_A8B8G8R8, + 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_A8B8G8R8, + 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 new file mode 100644 index 000000000..a3f6d662f --- /dev/null +++ b/libs/openengine/ogre/imagerotate.hpp @@ -0,0 +1,27 @@ +#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/lights.cpp b/libs/openengine/ogre/lights.cpp new file mode 100644 index 000000000..348057b84 --- /dev/null +++ b/libs/openengine/ogre/lights.cpp @@ -0,0 +1,102 @@ +#include "lights.hpp" + +#include + + +namespace OEngine { +namespace Render { + +Ogre::Real LightFunction::pulseAmplitude(Ogre::Real time) +{ + return std::sin(time); +} + +Ogre::Real LightFunction::flickerAmplitude(Ogre::Real time) +{ + static const float fb = 1.17024f; + static const float f[3] = { 1.5708f, 4.18774f, 5.19934f }; + static const float o[3] = { 0.804248f, 2.11115f, 3.46832f }; + static const float m[3] = { 1.0f, 0.785f, 0.876f }; + static const float s = 0.394f; + + float v = 0.0f; + for(int i = 0;i < 3;++i) + v += std::sin(fb*time*f[i] + o[1])*m[i]; + return v * s; +} + +Ogre::Real LightFunction::flickerFrequency(Ogre::Real phase) +{ + static const float fa = 0.785398f; + static const float tdo = 0.94f; + static const float tdm = 2.48f; + + return tdo + tdm*std::sin(fa * phase); +} + +Ogre::Real LightFunction::calculate(Ogre::Real value) +{ + Ogre::Real brightness = 1.0f; + float cycle_time; + float time_distortion; + + if(mType == LT_Pulse || mType == LT_PulseSlow) + { + cycle_time = 2.0f * Ogre::Math::PI; + time_distortion = 20.0f; + } + else + { + static const float fa = 0.785398f; + static const float phase_wavelength = 120.0f * 3.14159265359f / fa; + + cycle_time = 500.0f; + mPhase = std::fmod(mPhase + value, phase_wavelength); + time_distortion = flickerFrequency(mPhase); + } + + mDeltaCount += mDirection*value*time_distortion; + if(mDirection > 0 && mDeltaCount > +cycle_time) + { + mDirection = -1.0f; + mDeltaCount = 2.0f*cycle_time - mDeltaCount; + } + if(mDirection < 0 && mDeltaCount < -cycle_time) + { + mDirection = +1.0f; + mDeltaCount = -2.0f*cycle_time - mDeltaCount; + } + + static const float fast = 4.0f/1.0f; + static const float slow = 1.0f/1.0f; + + // These formulas are just guesswork, but they work pretty well + if(mType == LT_Normal) + { + // Less than 1/255 light modifier for a constant light: + brightness = 1.0 + flickerAmplitude(mDeltaCount*slow)/255.0f; + } + else if(mType == LT_Flicker) + brightness = 0.75 + flickerAmplitude(mDeltaCount*fast)*0.25; + else if(mType == LT_FlickerSlow) + brightness = 0.75 + flickerAmplitude(mDeltaCount*slow)*0.25; + else if(mType == LT_Pulse) + brightness = 1.0 + pulseAmplitude(mDeltaCount*fast)*0.25; + else if(mType == LT_PulseSlow) + brightness = 1.0 + pulseAmplitude(mDeltaCount*slow)*0.25; + + return brightness; +} + +Ogre::Real LightValue::getValue() const +{ + return 0.0f; +} + +void LightValue::setValue(Ogre::Real value) +{ + mTarget->setDiffuseColour(mColor * value); +} + +} +} diff --git a/libs/openengine/ogre/lights.hpp b/libs/openengine/ogre/lights.hpp new file mode 100644 index 000000000..61d09a0e6 --- /dev/null +++ b/libs/openengine/ogre/lights.hpp @@ -0,0 +1,64 @@ +#ifndef OENGINE_OGRE_LIGHTS_H +#define OENGINE_OGRE_LIGHTS_H + +#include +#include +#include + +/* + * Controller classes to handle pulsing and flicker lights + */ + +namespace OEngine { +namespace Render { + enum LightType { + LT_Normal, + LT_Flicker, + LT_FlickerSlow, + LT_Pulse, + LT_PulseSlow + }; + + class LightFunction : public Ogre::ControllerFunction + { + LightType mType; + Ogre::Real mPhase; + Ogre::Real mDirection; + + static Ogre::Real pulseAmplitude(Ogre::Real time); + + static Ogre::Real flickerAmplitude(Ogre::Real time); + static Ogre::Real flickerFrequency(Ogre::Real phase); + + public: + // MSVC needs the constructor for a class inheriting a template to be defined in header + LightFunction(LightType type) + : ControllerFunction(true) + , mType(type) + , mPhase(Ogre::Math::RangeRandom(-500.0f, +500.0f)) + , mDirection(1.0f) + { + } + virtual Ogre::Real calculate(Ogre::Real value); + }; + + class LightValue : public Ogre::ControllerValue + { + Ogre::Light *mTarget; + Ogre::ColourValue mColor; + + public: + // MSVC needs the constructor for a class inheriting a template to be defined in header + LightValue(Ogre::Light *light, const Ogre::ColourValue &color) + : mTarget(light) + , mColor(color) + { + } + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value); + }; +} +} + +#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 309ba8a06..912781240 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -1,5 +1,8 @@ #include "renderer.hpp" #include "fader.hpp" +#include "particles.hpp" + +#include #include "OgreRoot.h" #include "OgreRenderWindow.h" @@ -8,23 +11,24 @@ #include "OgreTextureManager.h" #include "OgreTexture.h" #include "OgreHardwarePixelBuffer.h" +#include +#include "OgreParticleAffectorFactory.h" #include #include +#include + #include #include #include -#if defined(__APPLE__) && !defined(__LP64__) -#include -#endif - using namespace Ogre; using namespace OEngine::Render; -#if defined(__APPLE__) && !defined(__LP64__) + +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE CustomRoot::CustomRoot(const Ogre::String& pluginFileName, const Ogre::String& configFileName, @@ -47,29 +51,22 @@ void OgreRenderer::cleanup() delete mRoot; mRoot = NULL; + // If we don't do this, the desktop resolution is not restored on exit + SDL_SetWindowFullscreen(mSDLWindow, 0); + + SDL_DestroyWindow(mSDLWindow); + mSDLWindow = NULL; + unloadPlugins(); } void OgreRenderer::start() { -#if defined(__APPLE__) && !defined(__LP64__) - // OSX Carbon Message Pump +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + // we need this custom main loop because otherwise Ogre's Carbon message pump will + // steal input events even from our Cocoa window + // There's no way to disable Ogre's message pump other that comment pump code in Ogre's source do { - EventRef event = NULL; - EventTargetRef targetWindow; - targetWindow = GetEventDispatcherTarget(); - - // If we are unable to get the target then we no longer care about events. - if (!targetWindow) return; - - // Grab the next event while possible - while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &event) == noErr) - { - // Dispatch the event - SendEventToEventTarget(event, targetWindow); - ReleaseEvent(event); - } - if (!mRoot->renderOneFrame()) { break; } @@ -106,6 +103,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; @@ -160,7 +167,7 @@ void OgreRenderer::configure(const std::string &logPath, // Disable logging log->setDebugOutputEnabled(false); -#if defined(__APPLE__) && !defined(__LP64__) +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE mRoot = new CustomRoot("", "", ""); #else mRoot = new Root("", "", ""); @@ -192,9 +199,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) @@ -205,37 +231,42 @@ void OgreRenderer::configure(const std::string &logPath, rs->setConfigOption ("RTT Preferred Mode", rttMode); } -void OgreRenderer::recreateWindow(const std::string &title, const WindowSettings &settings) +void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings) { - Ogre::ColourValue viewportBG = mView->getBackgroundColour(); + assert(mRoot); + mRoot->initialise(false); - 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); + int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(settings.screen), + pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(settings.screen); - // Create one viewport, entire window - mView = mWindow->addViewport(mCamera); - mView->setBackgroundColour(viewportBG); - - adjustViewport(); -} + if(settings.fullscreen) + { + pos_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(settings.screen); + pos_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(settings.screen); + } -void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings) -{ - assert(mRoot); - mRoot->initialise(false); - 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")); + // Create an application window with the following settings: + mSDLWindow = SDL_CreateWindow( + "OpenMW", // window title + pos_x, // initial x position + pos_y, // initial y position + settings.window_x, // width, in pixels + settings.window_y, // height, in pixels + SDL_WINDOW_SHOWN + | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) | SDL_WINDOW_RESIZABLE + ); + SFO::SDLWindowHelper helper(mSDLWindow, settings.window_x, settings.window_y, title, settings.fullscreen, params); + if (settings.icon != "") + helper.setWindowIcon(settings.icon); + mWindow = helper.getWindow(); - mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms); // create the semi-transparent black background texture used by the GUI. // has to be created in code with TU_DYNAMIC_WRITE_ONLY param @@ -248,46 +279,41 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& 0, Ogre::PF_A8R8G8B8, Ogre::TU_WRITE_ONLY); -} -void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip) -{ - assert(mRoot); - assert(mWindow); - // Get the SceneManager, in this case a generic one mScene = mRoot->createSceneManager(ST_GENERIC); - // Create the camera - mCamera = mScene->createCamera(camName); - mCamera->setNearClipDistance(nearClip); - mCamera->setFOVy(Degree(fov)); + mFader = new Fader(mScene); + + mCamera = mScene->createCamera("cam"); // Create one viewport, entire window mView = mWindow->addViewport(mCamera); - // Alter the camera aspect ratio to match the viewport mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight())); - - mFader = new Fader(mScene); } -void OgreRenderer::adjustViewport() +void OgreRenderer::adjustCamera(float fov, float nearClip) { - // Alter the camera aspect ratio to match the viewport - mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight())); + mCamera->setNearClipDistance(nearClip); + mCamera->setFOVy(Degree(fov)); } -void OgreRenderer::setWindowEventListener(Ogre::WindowEventListener* listener) +void OgreRenderer::adjustViewport() { - Ogre::WindowEventUtilities::addWindowEventListener(mWindow, listener); + // Alter the camera aspect ratio to match the viewport + if(mCamera != NULL) + { + mView->setDimensions(0, 0, 1, 1); + mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight())); + } } -void OgreRenderer::removeWindowEventListener(Ogre::WindowEventListener* listener) +void OgreRenderer::setFov(float fov) { - Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, listener); + mCamera->setFOVy(Degree(fov)); } -void OgreRenderer::setFov(float fov) +void OgreRenderer::windowResized(int x, int y) { - mCamera->setFOVy(Degree(fov)); + mWindowListener->windowResized(x,y); } diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 251dc9c54..89edc567d 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -27,19 +27,24 @@ #include "OgreTexture.h" #include -#if defined(__APPLE__) && !defined(__LP64__) +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE #include #endif +struct SDL_Window; +struct SDL_Surface; + namespace Ogre { -#if !defined(__APPLE__) || defined(__LP64__) +#if OGRE_PLATFORM != OGRE_PLATFORM_APPLE class Root; #endif class RenderWindow; class SceneManager; class Camera; class Viewport; + class ParticleEmitterFactory; + class ParticleAffectorFactory; } namespace OEngine @@ -51,10 +56,12 @@ namespace OEngine bool vsync; bool fullscreen; int window_x, window_y; + int screen; std::string fsaa; + std::string icon; }; -#if defined(__APPLE__) && !defined(__LP64__) +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE class CustomRoot : public Ogre::Root { public: bool isQueuedEnd() const; @@ -67,14 +74,21 @@ namespace OEngine class Fader; + class WindowSizeListener + { + public: + virtual void windowResized (int x, int y) = 0; + }; + class OgreRenderer { -#if defined(__APPLE__) && !defined(__LP64__) +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE CustomRoot *mRoot; #else Ogre::Root *mRoot; #endif Ogre::RenderWindow *mWindow; + SDL_Window *mSDLWindow; Ogre::SceneManager *mScene; Ogre::Camera *mCamera; Ogre::Viewport *mView; @@ -94,15 +108,21 @@ namespace OEngine Ogre::D3D9Plugin* mD3D9Plugin; #endif Fader* mFader; + std::vector mEmitterFactories; + std::vector mAffectorFactories; bool logging; + WindowSizeListener* mWindowListener; + public: OgreRenderer() : mRoot(NULL) , mWindow(NULL) + , mSDLWindow(NULL) , mScene(NULL) , mCamera(NULL) , mView(NULL) + , mWindowListener(NULL) #ifdef ENABLE_PLUGIN_CgProgramManager , mCgPlugin(NULL) #endif @@ -125,9 +145,6 @@ namespace OEngine ~OgreRenderer() { cleanup(); } - void setWindowEventListener(Ogre::WindowEventListener* listener); - void removeWindowEventListener(Ogre::WindowEventListener* listener); - /** Configure the renderer. This will load configuration files and set up the Root and logging classes. */ void configure( @@ -139,10 +156,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 + void adjustCamera( float fov=55, // Field of view angle float nearClip=5 // Near clip distance ); @@ -166,12 +181,17 @@ namespace OEngine float getFPS(); + void windowResized(int x, int y); + /// Get the Root Ogre::Root *getRoot() { return mRoot; } /// Get the rendering window Ogre::RenderWindow *getWindow() { return mWindow; } + /// Get the SDL Window + SDL_Window *getSDLWindow() { return mSDLWindow; } + /// Get the scene manager Ogre::SceneManager *getScene() { return mScene; } @@ -184,6 +204,8 @@ namespace OEngine /// Viewport Ogre::Viewport *getViewport() { return mView; } + void setWindowListener(WindowSizeListener* listener) { mWindowListener = listener; } + void adjustViewport(); }; } diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 30e7b9e1e..69375b74d 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -100,7 +101,16 @@ namespace Render return m->getTechnique(1); } else - throw std::runtime_error("selectionbuffer only works with entities"); + { + m = Ogre::MaterialManager::getSingleton().getByName("NullMaterial"); + if(m.isNull()) + { + m = Ogre::MaterialManager::getSingleton().create("NullMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + m->getTechnique(0)->getPass(0)->setDepthCheckEnabled(true); + m->getTechnique(0)->getPass(0)->setDepthFunction(Ogre::CMPF_ALWAYS_FAIL); + } + return m->getTechnique(0); + } } return NULL; } diff --git a/readme.txt b/readme.txt index 8edb0c4b3..7865f8dba 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.22.0 +Version: 0.26.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org @@ -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 @@ -55,8 +55,7 @@ Syntax: openmw Allowed options: --help print help message --version print version information and quit - --data arg (=data) set data directories (later directories have - higher priority) + --data arg (=data) set data directories (later directories have higher priority) --data-local arg set local data directory (highest priority) --resources arg (=resources) set resources directory --start arg (=Beshara) set initial cell @@ -66,34 +65,207 @@ Allowed options: --debug [=arg(=1)] (=0) debug mode --nosound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output - --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scri - pts) at startup + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of console - commands that is executed on startup - + --script-run arg select a file containing a list of console commands that is executed on startup --new-game [=arg(=1)] (=0) activate char gen/new game mechanics - --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding - ) - --encoding arg (=win1252) Character encoding used in OpenMW game messa - ges: - - win1250 - Central and Eastern European such - as Polish, Czech, Slovak, Hungarian, Slovene - , Bosnian, Croatian, Serbian (Latin script), - Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as Russian, - Bulgarian, Serbian Cyrillic and other langua - ges - - win1252 - Western European (Latin) alphabet, - used by default - --fallback arg fallback values + --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) + --encoding arg (=win1252) Character encoding used in OpenMW game messages: + + win1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages + + win1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages + win1252 - Western European (Latin) alphabet, used by default + + --fallback arg fallback values CHANGELOG +0.26.0 + +Bug #274: Inconsistencies in the terrain +Bug #557: Already-dead NPCs do not equip clothing/items. +Bug #592: Window resizing +Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) +Bug #664: Heart of lorkhan acts like a dead body (container) +Bug #767: Wonky ramp physics & water +Bug #780: Swimming out of water +Bug #792: Wrong ground alignment on actors when no clipping +Bug #796: Opening and closing door sound issue +Bug #797: No clipping hinders opening and closing of doors +Bug #799: sliders in enchanting window +Bug #838: Pressing key during startup procedure freezes the game +Bug #839: Combat/magic stances during character creation +Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment +Bug #844: Resting "until healed" option given even with full stats +Bug #846: Equipped torches are invisible. +Bug #847: Incorrect formula for autocalculated NPC initial health +Bug #850: Shealt weapon sound plays when leaving magic-ready stance +Bug #852: Some boots do not produce footstep sounds +Bug #860: FPS bar misalignment +Bug #861: Unable to print screen +Bug #863: No sneaking and jumping at the same time +Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. +Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. +Bug #868: Idle animations are repeated +Bug #874: Underwater swimming close to the ground is jerky +Bug #875: Animation problem while swimming on the surface and looking up +Bug #876: Always a starting upper case letter in the inventory +Bug #878: Active spell effects don't update the layout properly when ended +Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load +Bug #896: New game sound issue +Feature #49: Melee Combat +Feature #71: Lycanthropy +Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList +Feature #622: Multiple positions for inventory window +Feature #627: Drowning +Feature #786: Allow the 'Activate' key to close the countdialog window +Feature #798: Morrowind installation via Launcher (Linux/Max OS only) +Feature #851: First/Third person transitions with mouse wheel +Task #689: change PhysicActor::enableCollisions +Task #707: Reorganise Compiler + +0.25.0 + +Bug #411: Launcher crash on OS X < 10.8 +Bug #604: Terrible performance drop in the Census and Excise Office. +Bug #676: Start Scripts fail to load +Bug #677: OpenMW does not accept script names with - +Bug #766: Extra space in front of topic links +Bug #793: AIWander Isn't Being Passed The Repeat Parameter +Bug #795: Sound playing with drawn weapon and crossing cell-border +Bug #800: can't select weapon for enchantment +Bug #801: Player can move while over-encumbered +Bug #802: Dead Keys not working +Bug #808: mouse capture +Bug #809: ini Importer does not work without an existing cfg file +Bug #812: Launcher will run OpenMW with no ESM or ESP selected +Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected +Bug #817: Dead NPCs and Creatures still have collision boxes +Bug #820: Incorrect sorting of answers (Dialogue) +Bug #826: mwinimport dumps core when given an unknown parameter +Bug #833: getting stuck in door +Bug #835: Journals/books not showing up properly. +Feature #38: SoundGen +Feature #105: AI Package: Wander +Feature #230: 64-bit compatibility for OS X +Feature #263: Hardware mouse cursors +Feature #449: Allow mouse outside of window while paused +Feature #736: First person animations +Feature #750: Using mouse wheel in third person mode +Feature #822: Autorepeat for slider buttons + +0.24.0 + +Bug #284: Book's text misalignment +Bug #445: Camera able to get slightly below floor / terrain +Bug #582: Seam issue in Red Mountain +Bug #632: Journal Next Button shows white square +Bug #653: IndexedStore ignores index +Bug #694: Parser does not recognize float values starting with . +Bug #699: Resource handling broken with Ogre 1.9 trunk +Bug #718: components/esm/loadcell is using the mwworld subsystem +Bug #729: Levelled item list tries to add nonexistent item +Bug #730: Arrow buttons in the settings menu do not work. +Bug #732: Erroneous behavior when binding keys +Bug #733: Unclickable dialogue topic +Bug #734: Book empty line problem +Bug #738: OnDeath only works with implicit references +Bug #740: Script compiler fails on scripts with special names +Bug #742: Wait while no clipping +Bug #743: Problem with changeweather console command +Bug #744: No wait dialogue after starting a new game +Bug #748: Player is not able to unselect objects with the console +Bug #751: AddItem should only spawn a message box when called from dialogue +Bug #752: The enter button has several functions in trade and looting that is not impelemted. +Bug #753: Fargoth's Ring Quest Strange Behavior +Bug #755: Launcher writes duplicate lines into settings.cfg +Bug #759: Second quest in mages guild does not work +Bug #763: Enchantment cast cost is wrong +Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly +Bug #773: AIWander Isn't Being Passed The Correct idle Values +Bug #778: The journal can be opened at the start of a new game +Bug #779: Divayth Fyr starts as dead +Bug #787: "Batch count" on detailed FPS counter gets cut-off +Bug #788: chargen scroll layout does not match vanilla +Feature #60: Atlethics Skill +Feature #65: Security Skill +Feature #74: Interaction with non-load-doors +Feature #98: Render Weapon and Shield +Feature #102: AI Package: Escort, EscortCell +Feature #182: Advanced Journal GUI +Feature #288: Trading enhancements +Feature #405: Integrate "new game" into the menu +Feature #537: Highlight dialogue topic links +Feature #658: Rotate, RotateWorld script instructions and local rotations +Feature #690: Animation Layering +Feature #722: Night Eye/Blind magic effects +Feature #735: Move, MoveWorld script instructions. +Feature #760: Non-removable corpses + +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