diff --git a/.gitignore b/.gitignore index ada874bb2..b3bb8d82d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ Docs/mainpage.hpp CMakeFiles */CMakeFiles CMakeCache.txt +moc_*.cxx +cmake_install.cmake +*.[ao] +Makefile +makefile +data diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 1ce6250b3..000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "libs/mangle"] - path = libs/mangle - url = git://github.com/zinnschlag/mangle.git -[submodule "libs/openengine"] - path = libs/openengine - url = git://github.com/zinnschlag/OpenEngine diff --git a/CMakeLists.txt b/CMakeLists.txt index b3439d1a5..67fc2dbfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,13 @@ project(OpenMW) -IF (APPLE) - set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app") +if (APPLE) + set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") + + set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") # using 10.6 sdk set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk") -ENDIF (APPLE) +endif (APPLE) # Macros @@ -16,8 +18,8 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 11) -set (OPENMW_VERSION_RELEASE 1) +set (OPENMW_VERSION_MINOR 13) +set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -25,6 +27,8 @@ set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VE configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") +option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE) + # Sound source selection option(USE_AUDIERE "use Audiere for sound" OFF) option(USE_FFMPEG "use ffmpeg for sound" OFF) @@ -94,6 +98,7 @@ source_group(libs\\mangle FILES ${MANGLE_ALL}) set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/mouselook.cpp + ${LIBDIR}/openengine/ogre/fader.cpp ) set(OENGINE_GUI ${LIBDIR}/openengine/gui/events.cpp @@ -186,11 +191,18 @@ find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread) find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) +IF(OGRE_STATIC) +find_package(Cg REQUIRED) +IF(WIN32) +set(OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_Plugin_CgProgramManager_INCLUDE_DIRS} ${OGRE_Plugin_OctreeSceneManager_INCLUDE_DIRS} ${OGRE_Plugin_ParticleFX_INCLUDE_DIRS} ${OGRE_RenderSystem_Direct3D9_INCLUDE_DIRS} ${OGRE_RenderSystem_GL_INCLUDE_DIRS}) +ELSE(WIN32) +set(OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_Plugin_CgProgramManager_INCLUDE_DIRS} ${OGRE_Plugin_OctreeSceneManager_INCLUDE_DIRS} ${OGRE_Plugin_ParticleFX_INCLUDE_DIRS} ${OGRE_RenderSystem_GL_INCLUDE_DIRS}) +ENDIF(WIN32) +ENDIF(OGRE_STATIC) include_directories("." - ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE - ${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} + ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS} + ${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR} - ${CMAKE_HOME_DIRECTORY}/extern/caelum/include ${CMAKE_HOME_DIRECTORY}/extern/mygui_3.0.1/MyGUIEngine/include ${CMAKE_HOME_DIRECTORY}/extern/mygui_3.0.1/OgrePlatform/include ${OPENAL_INCLUDE_DIR} @@ -200,7 +212,14 @@ include_directories("." link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR}) -add_subdirectory( extern/caelum ) +if(APPLE) + # List used Ogre plugins + SET(USED_OGRE_PLUGINS "RenderSystem_GL" + "Plugin_OctreeSceneManager" + "Plugin_CgProgramManager" + "Plugin_ParticleFX") +endif(APPLE) + add_subdirectory( extern/mygui_3.0.1 ) add_subdirectory( files/) @@ -211,9 +230,6 @@ add_subdirectory( files/) # MyGUI: extern/mygui_3.0.0/ add_definitions(-DMYGUI_STATIC) -# Caelum: extern/caelum/ -add_definitions(-DCAELUM_STATIC) - # Specify build paths if (APPLE) @@ -244,61 +260,32 @@ if (APPLE) "${OpenMW_BINARY_DIR}/plugins.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist - "${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY) + "${APP_BUNDLE_DIR}/Contents/Info.plist") configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) - # prepare plugins - configure_file(${OGRE_PLUGIN_DIR}/RenderSystem_GL.dylib - "${APP_BUNDLE_DIR}/Contents/Plugins/RenderSystem_GL.dylib" COPYONLY) - - configure_file(${OGRE_PLUGIN_DIR}/Plugin_OctreeSceneManager.dylib - "${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_OctreeSceneManager.dylib" COPYONLY) - - configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib - "${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY) + if (${CMAKE_BUILD_TYPE} MATCHES "Release" OR + ${CMAKE_BUILD_TYPE} MATCHES "RelWithDebugInfo") + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + else() + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) + endif() + foreach(plugin ${USED_OGRE_PLUGINS}) + configure_file("${OGRE_PLUGIN_DIR}/${plugin}.dylib" + "${APP_BUNDLE_DIR}/Contents/Plugins/${plugin}.dylib" + COPYONLY) + endforeach() endif (APPLE) # Compiler settings if (CMAKE_COMPILER_IS_GNUCC) - #add_definitions (-Wall -Werror) - add_definitions (-Wall) + add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-reorder) endif (CMAKE_COMPILER_IS_GNUCC) -# Apple bundling -# TODO REWRITE! -if (APPLE) - set(MISC_FILES - ${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg - ${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg) - - set(OGRE_PLUGINS - ${APP_BUNDLE_DIR}/Contents/Plugins/*) - -install(FILES ${MISC_FILES} DESTINATION ../MacOS) -install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Plugins" DESTINATION ..) -install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Resources/resources" DESTINATION ../Resources) -set(CPACK_GENERATOR "Bundle") -set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/files/mac/Info.plist") -set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw.icns") -set(CPACK_BUNDLE_NAME "OpenMW") -set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) -set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) -set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) -set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) - -include(CPack) - -set(CMAKE_EXE_LINKER_FLAGS "-arch i386") -set(CMAKE_CXX_FLAGS "-arch i386") - -endif (APPLE) - - if(DPKG_PROGRAM) SET(CMAKE_INSTALL_PREFIX "/usr") @@ -327,7 +314,7 @@ if(DPKG_PROGRAM) SET(CPACK_GENERATOR "DEB") SET(CPACK_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.com") + 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 @@ -352,6 +339,57 @@ if(DPKG_PROGRAM) include(CPack) endif(DPKG_PROGRAM) +if(WIN32) + FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*") + INSTALL(FILES ${files} DESTINATION ".") + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") + + SET(CPACK_GENERATOR "NSIS") + SET(CPACK_PACKAGE_NAME "OpenMW") + SET(CPACK_PACKAGE_VENDOR "OpenMW.org") + SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) + SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) + SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) + SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;esmtool;Esmtool;omwlauncher;OpenMW Launcher") + SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt") + SET(CPACK_RESOURCE_FILE_LICENSE "${OpenMW_SOURCE_DIR}/GPL3.txt") + SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") + SET(CPACK_NSIS_DISPLAY_NAME "OpenMW") + SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") + SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") + SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe") + SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") + SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") + # SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") + + SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") + if(EXISTS ${VCREDIST32}) + INSTALL(FILES ${VCREDIST32} DESTINATION "redist") + SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" ) + endif(EXISTS ${VCREDIST32}) + + SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe") + if(EXISTS ${VCREDIST64}) + INSTALL(FILES ${VCREDIST64} DESTINATION "redist") + SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" ) + endif(EXISTS ${VCREDIST64}) + + SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe") + if(EXISTS ${OALREDIST}) + INSTALL(FILES ${OALREDIST} DESTINATION "redist") + SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} + ExecWait '\\\"$INSTDIR\\\\redist\\\\oalinst.exe\\\" /s'" ) + endif(EXISTS ${OALREDIST}) + + if(CMAKE_CL_64) + SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") + endif() + + include(CPack) +endif(WIN32) + # Components add_subdirectory (components) @@ -384,6 +422,45 @@ if (WIN32) set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") + + # Play a bit with the warning levels + + set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them + + set(WARNINGS_DISABLE + # Warnings that aren't enabled normally and don't need to be enabled + # They're unneeded and sometimes completely retarded warnings that /Wall enables + # Not going to bother commenting them as they tend to warn on every standard library files + 4061 4263 4264 4266 4350 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946 + + # Warnings that are thrown on standard libraries and not OpenMW + 4347 # Non-template function with same name and parameter count as template function + 4365 # Variable signed/unsigned mismatch + 4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base + 4706 # Assignment in conditional expression + 4738 # Storing 32-bit float result in memory, possible loss of performance + 4986 # Undocumented warning that occurs in the crtdbg.h file + 4996 # Function was declared deprecated + + # OpenMW specific warnings + 4099 # Type mismatch, declared class or struct is defined with other type + 4100 # Unreferenced formal parameter (-Wunused-parameter) + 4127 # Conditional expression is constant + 4242 # Storing value in a variable of a smaller type, possible loss of data + 4244 # Storing value of one type in variable of another (size_t in int, for example) + 4305 # Truncating value (double to float, for example) + 4309 # Variable overflow, trying to store 128 in a signed char for example + 4355 # Using 'this' in member initialization list + 4701 # Potentially uninitialized local variable used + ) + + foreach(d ${WARNINGS_DISABLE}) + set(WARNINGS "${WARNINGS} /wd${d}") + endforeach(d) + + set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif(MSVC) # Same for MinGW @@ -406,3 +483,82 @@ if (WIN32) #set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") #set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") endif() + +# Apple bundling +if (APPLE) + set(INSTALL_SUBDIR OpenMW) + + install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + + install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + + set(CPACK_GENERATOR "DragNDrop") + set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) + set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) + set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) + set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) + + set(APPS "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") + set(PLUGINS "") + + # Scan Plugins dir for *.dylibs + set(PLUGIN_SEARCH_ROOT "${APP_BUNDLE_DIR}/Contents/Plugins") + file(GLOB_RECURSE ALL_PLUGINS "${PLUGIN_SEARCH_ROOT}/*.dylib") + + set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins") + foreach(PLUGIN ${ALL_PLUGINS}) + string(REPLACE "${PLUGIN_SEARCH_ROOT}/" "" PLUGIN_RELATIVE "${PLUGIN}") + set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") + endforeach() + + #For now, search unresolved dependencies only in default system paths, so if you put unresolveable (i.e. with @executable_path in id name) lib or framework somewhere else, it would fail + set(DIRS "") + + # Overriding item resolving during installation, it needed if + # some library already has be "fixed up", i.e. its id name contains @executable_path, + # but library is not embedded in bundle. For example, it's Ogre.framework from Ogre SDK. + # Current implementation of GetPrerequsities/BundleUtilities doesn't handle that case. + # + # Current limitations: + # 1. Handles only frameworks, not simple libs + INSTALL(CODE " + set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_SYSTEM_FRAMEWORK_PATH ${CMAKE_SYSTEM_FRAMEWORK_PATH}) + + set(OPENMW_RESOLVED_ITEMS \"\") + + function(gp_resolve_item_override context item exepath dirs resolved_item_var resolved_var) + if(item MATCHES \"@executable_path\" AND NOT \${\${resolved_var}}) + if (item MATCHES \"Frameworks\") # if it is a framework + # get last segment of path + get_filename_component(fname \"\${item}\" NAME_WE) + find_library(ri NAMES \${fname} PATHS \${exepath} \${dirs} /Library/Frameworks) + if (ri) + message(STATUS \"found \${ri} for \${item}\") + string(REGEX REPLACE \"^.*/Frameworks/.*\\\\.framework\" \"\" item_part \${item}) + set(ri \"\${ri}\${item_part}\") + set(\${resolved_item_var} \${ri} PARENT_SCOPE) + set(\${resolved_var} 1 PARENT_SCOPE) + set(OPENMW_RESOLVED_ITEMS \${_OPENMW_RESOLVED_ITEMS} \${ri}) + endif() + else() + # code path for standard (non-framework) libs (ogre & qt pugins) + endif() + endif() + endfunction(gp_resolve_item_override) + + cmake_policy(SET CMP0009 OLD) + set(BU_CHMOD_BUNDLE_ITEMS ON) + include(BundleUtilities) + fixup_bundle(\"${APPS}\" \"${PLUGINS}\" \"${DIRS}\") + " COMPONENT Runtime) + +include(CPack) + +set(CMAKE_EXE_LINKER_FLAGS "-arch i386") +set(CMAKE_CXX_FLAGS "-arch i386") + +endif (APPLE) diff --git a/README_Mac.md b/README_Mac.md index 39382e0c9..dc3918368 100644 --- a/README_Mac.md +++ b/README_Mac.md @@ -1,80 +1,79 @@ -NOTE: This README is for ardekantur's Mac branch of OpenMW. A README -for the main branch has yet to be written. If you want to submit one, -please send me a message! +#Getting OpenMW Working on OS X -OpenMW -====== +## Initial setup +First of all, clone OpenMW repo. -From the [official website][]: + $ git clone github.com/zinnschlag/openmw -> OpenMW is an attempt to reimplement the popular role playing game - Morrowind. It aims to be a fully playable, open source - implementation of the game. You must own Morrowind to use OpenMW. +Or use your github url if you forked. +About dependencies: I prefer not to install them globally (i. e. in /usr/local/), so I'm installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs. -About This Project ------------------- +It's useful to create env var for lib install prefix: + + $ export OMW_LIB_PREFIX=$HOME/path/libs/root` -This specific repository is a branch of OpenMW intended to keep pace -with development of the project in order to provide a Mac build for -interested parties to contribute. This is not an official, sanctioned -branch of the OpenMW project. I will only be able to answer specific -questions about getting this project running on Mac OS X, **no other -platform**. I will not even be able to guarantee my changes maintain -backwards compatibility against builds in other operating systems. You -have been warned. +Most of libs can be installed from [Homebrew][homebrew]. Only mpg123 needs to be installed from source (due to lack of universal compilation support). I think that some of libs can be installed from MacPorts or Fink too. +As OpenMW currently only supports i386 architecture on OS X, denendencies also should support it. Set some env vars in current terminal: -Getting OpenMW Working ----------------------- - -1. Clone this repository. -2. Note about libs: I prefer not to install them globally (i. e. in /usr/local/), so I installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs. - It's useful to create env var for lib install prefix: - $ export OMW_LIB_PREFIX=$HOME/path/libs/root - -3. First of all, set for current terminal some env vars: $ export CFLAGS="-arch i386" $ export CXXFLAGS="-arch i386" $ export LDFLAGS="-arch i386" - All libs will build with correct architecture. - If you close your terminal, you should set env vars again before pcoceeding to next steps! -4. Download [boost][] (tested with 1.45) and install it with the following command: +If you close your terminal, you should set env vars again before pcoceeding to next steps! + +## Boost +Download [boost][boost] and install it with the following command: $ cd /path/to/boost/source $ ./bootstrap.sh --prefix=$OMW_LIB_PREFIX $ ./bjam --build-dir=build --layout=versioned \ --toolset=darwin architecture=x86 address-model=32 \ --link-shared,static --prefix=$OMW_LIB_PREFIX install + + +Alternatively you can install boost with homebrew: + $ brew install boost --universal -5. Download [Ogre][] SDK (tested with 1.7.2), unpack it and move -`lib/Release/Ogre.framework` into `Library/Frameworks`. +I think MacPorts also should support universal build for boost. -6. Download [OIS][] and use the XCode project provided in - `ois/Mac/XCode-2.2`. Be sure to set your build architecture to - `i386` and your SDK platform to either 10.5 or 10.6. Once it - builds, move `ois/Mac/XCode-2.2/build/Debug/OIS.framework` to - `/Library/Frameworks`. +## Ogre +Download [Ogre][] SDK (tested with 1.7.3), unpack it somewhere and move +`lib/Release/Ogre.framework` into `/Library/Frameworks`. + +## OIS +Download patched [OIS][] and use the XCode project provided. Be sure to set your build architecture to + `i386`. Once it built, locate built OIS.framework with Xcode and move it to `/Library/Frameworks`. + +## mpg123 +Download [MPG 123][mpg123] and build it: -7. Download [mpg123][] and build it: $ cd /path/to/mpg123/source $ ./configure --prefix=$OMW_LIB_PREFIX --disable-debug \ --disable-dependency-tracking \ --with-optimization=4 \ - --with-audio=coreaudio \ - --with-default-audio=coreaudio \ + --with-audio=dummy \ + --with-default-audio=dummy \ --with-cpu=sse_alone \ $ make install -8. Download [libsndfile][] and build it: +## libsndfile +Download [libsndfile][] and build it: + $ cd /path/to/libsndfile/source $ ./configure --prefix=$OMW_LIB_PREFIX \ --disable-dependency-tracking $ make install -9. Download [Bullet][] and build it: +or install with homebrew: + + $ brew install libsndfile --universal + +## Bullet +Download [Bullet][] and build it: + $ cd /path/to/bullet/source $ mkdir build $ cd build @@ -87,12 +86,25 @@ Getting OpenMW Working -G"Unix Makefiles" ../ $ make install -10. Generate the Makefile for OpenMW as follows and build OpenMW: +or install with homebrew: + + $ brew install bullet --HEAD --universal + +I prefer head because 2.79 has some issue which causes OpenMW to lag. Also you can edit formula and install 2.77, which is stable and haven't mentioned issue. + +## Qt +Install [Qt][qt]. Qt SDK distributed by Nokia is not an option because it's 64 bit only, and OpenMW currently doesn't build for 64 bit on OS X. I'm installing it from Homebrew: + + $ brew install qt --universal + +## Run CMake +Generate the Makefile for OpenMW as follows and build OpenMW: + $ mkdir /path/to/openmw/build/dir $ cd /path/to/open/build/dir $ cmake \ -D CMAKE_OSX_ARCHITECTURES=i386 \ - -D OGRESDK=/path/to/ogre/sdk \ + -D OGRE_SDK=/path/to/ogre/sdk \ -D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \ -D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \ -D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ @@ -106,27 +118,43 @@ Getting OpenMW Working -D BULLET_INCLUDE_DIR=$OMW_LIB_PREFIX/include/bullet/ \ -G "Unix Makefiles" /path/to/openmw/source/dir $ make - You can use -G"Xcode" if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles" - if you prefer Eclipse. You also can specify -D CMAKE_BUILD_TYPE=Debug for debug - build. + +You can use `-G"Xcode"` if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles" +if you prefer Eclipse. You also can specify `-D CMAKE_BUILD_TYPE=Debug` for debug +build. As for CMake 2.8.7 and Xcode 4.3, Xcode generator is broken. Sadly Eclipse CDT also cannot import generated project at least on my machine. -11. Copy your Morrowind `Data Files` directory into the OpenMW build dir - with the name `data` or create symlink: - $ ln -s /path/to/morrowind/data/files /path/to/openmw/build/dir/data +If all libs installed via homebrew (excluding mpg123), then command would be even simplier: + + $ cmake \ + -D CMAKE_OSX_ARCHITECTURES="i386" \ + -D OGRE_SDK=/path/to/ogre/sdk \ + -D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \ + -D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ + -G "Unix Makefiles" /path/to/openmw/source/dir + $ make + +Note for users with recent Xcode versions: you must explicitly specify what set of compilers do you use! If not, gcc will be used for C and Clang for C++. Just add this two -D's to command: `-D CMAKE_C_COMPILER=/usr/bin/clang` and `-D CMAKE_CXX_COMPILER=/usr/bin/clang` + +Note for Xcode 4.3 users: you should specify full path to used SDK, because current CMake (2.8.7) couldn't find SDKs inside Xcode app bundle: + + -D CMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk" + +# Run +From your build directory run: -12. From your build directory run: $ OpenMW.app/Contents/MacOS/openmw - or: +or: + $ open OpenMW.app - Enjoy! - - +Enjoy! +[homebrew]: https://github.com/mxcl/homebrew [boost]: http://www.boost.org [Ogre]: http://www.ogre3d.org [Bullet]: http://bulletphysics.org -[OIS]: http://wgois.sf.net +[OIS]: https://github.com/corristo/ois-fork [mpg123]: http://www.mpg123.de [libsndfile]: http://www.mega-nerd.com/libsndfile [official website]: http://openmw.com [Will Thimbleby's Ogre Framework]: http://www.thimbleby.net/ogre/ +[qt]: http://qt.nokia.com/ \ No newline at end of file diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index f2ab7bce7..af3dc090e 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -1,6 +1,4 @@ set(ESMTOOL - esmtool_cmd.c - esmtool_cmd.h esmtool.cpp ) source_group(apps\\esmtool FILES ${ESMTOOL}) diff --git a/apps/esmtool/Makefile b/apps/esmtool/Makefile deleted file mode 100644 index ee92cf4c8..000000000 --- a/apps/esmtool/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -esmtool_cmd.c: esmtool.ggo - gengetopt < esmtool.ggo - -clean: - rm esmtool_cmd.c esmtool_cmd.h diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index fe067d85d..f417d5c60 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -1,35 +1,138 @@ +#include + +#include + #include #include -#include "esmtool_cmd.h" - -#include +#define ESMTOOL_VERSION 1.1 using namespace std; using namespace ESM; +// Create a local alias for brevity +namespace bpo = boost::program_options; + void printRaw(ESMReader &esm); void loadCell(Cell &cell, ESMReader &esm, bool quiet); -int main(int argc, char**argv) +// Based on the legacy struct +struct Arguments { - gengetopt_args_info info; + unsigned int raw_given; + unsigned int quiet_given; + unsigned int loadcells_given; + std::string encoding; + std::string filename; +}; - if(cmdline_parser(argc, argv, &info) != 0) - return 1; +bool parseOptions (int argc, char** argv, Arguments &info) +{ + bpo::options_description desc("Inspect and extract from Morrowind ES files (ESM, ESP, ESS)\nSyntax: esmtool [options] file \nAllowed options"); - if(info.inputs_num != 1) + desc.add_options() + ("help,h", "print help message.") + ("version,v", "print version information and quit.") + ("raw,r", "Show an unformattet list of all records and subrecords.") + ("quiet,q", "Supress all record information. Useful for speed tests.") + ("loadcells,C", "Browse through contents of all cells.") + + ( "encoding,e", bpo::value(&(info.encoding))-> + default_value("win1252"), + "Character encoding used in ESMTool:\n" + "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" + "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" + "\n\twin1252 - Western European (Latin) alphabet, used by default") + ; + + std::string finalText = "\nIf no option is given, the default action is to parse all records in the archive\nand display diagnostic information."; + + // input-file is hidden and used as a positional argument + bpo::options_description hidden("Hidden Options"); + + hidden.add_options() + ( "input-file,i", bpo::value< vector >(), "input file") + ; + + bpo::positional_options_description p; + p.add("input-file", -1); + + // 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); + bpo::notify(variables); + + if (variables.count ("help")) { - if(info.inputs_num == 0) - cout << "ERROR: missing ES file\n\n"; - else - cout << "ERROR: more than one ES file specified\n\n"; - cmdline_parser_print_help(); - return 1; + std::cout << desc << finalText << std::endl; + return false; + } + if (variables.count ("version")) + { + std::cout << "ESMTool version " << ESMTOOL_VERSION << std::endl; + return false; } + if ( !variables.count("input-file") ) + { + std::cout << "\nERROR: missing ES file\n\n"; + std::cout << desc << finalText << std::endl; + return false; + } + + // handling gracefully the user adding multiple files + if (variables["input-file"].as< vector >().size() > 1) + { + std::cout << "\nERROR: more than one ES file specified\n\n"; + std::cout << desc << finalText << std::endl; + return false; + } + + info.filename = variables["input-file"].as< vector >()[0]; + + info.raw_given = variables.count ("raw"); + info.quiet_given = variables.count ("quiet"); + info.loadcells_given = variables.count ("loadcells"); + + // Font encoding settings + info.encoding = variables["encoding"].as(); + if (info.encoding == "win1250") + { + std::cout << "Using Central and Eastern European font encoding." << std::endl; + } + else if (info.encoding == "win1251") + { + std::cout << "Using Cyrillic font encoding." << std::endl; + } + else + { + if(info.encoding != "win1252") + { + std::cout << info.encoding << " is not a valid encoding option." << std::endl; + info.encoding = "win1252"; + } + std::cout << "Using default (English) font encoding." << std::endl; + } + + return true; +} + + +int main(int argc, char**argv) +{ + Arguments info; + if(!parseOptions (argc, argv, info)) + return 1; + ESMReader esm; - const char* filename = info.inputs[0]; + esm.setEncoding(info.encoding); + + string filename = info.filename; cout << "\nFile: " << filename << endl; try { diff --git a/apps/esmtool/esmtool.ggo b/apps/esmtool/esmtool.ggo deleted file mode 100644 index 9d0f3c189..000000000 --- a/apps/esmtool/esmtool.ggo +++ /dev/null @@ -1,10 +0,0 @@ -package "esmtool" -version "1.0" -purpose "Inspect and extract from Morrowind ES files (ESM, ESP, ESS)" -args "--unamed-opts=ES-FILE -F esmtool_cmd -G" - -option "raw" r "Show an unformattet list of all records and subrecords" optional -option "quiet" q "Supress all record information. Useful for speed tests." optional -option "loadcells" C "Browse through contents of all cells." optional - -text "\nIf no option is given, the default action is to parse all records in the archive and display diagnostic information." diff --git a/apps/esmtool/esmtool_cmd.c b/apps/esmtool/esmtool_cmd.c deleted file mode 100644 index d6556d9e7..000000000 --- a/apps/esmtool/esmtool_cmd.c +++ /dev/null @@ -1,1141 +0,0 @@ -/* - File autogenerated by gengetopt version 2.22.2 - generated with the following command: - gengetopt --unamed-opts=ES-FILE -F esmtool_cmd -G - - The developers of gengetopt consider the fixed text that goes in all - gengetopt output files to be in the public domain: - we make no copyright claims on it. -*/ - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#ifndef FIX_UNUSED -#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ -#endif - - -#include "esmtool_cmd.h" - -const char *gengetopt_args_info_purpose = "Inspect and extract from Morrowind ES files (ESM, ESP, ESS)"; - -const char *gengetopt_args_info_usage = "Usage: esmtool [OPTIONS]... [ES-FILE]..."; - -const char *gengetopt_args_info_description = ""; - -const char *gengetopt_args_info_help[] = { - " -h, --help Print help and exit", - " -V, --version Print version and exit", - " -r, --raw Show an unformattet list of all records and subrecords", - " -q, --quiet Supress all record information. Useful for speed tests.", - " -C, --loadcells Browse through contents of all cells.", - "\nIf no option is given, the default action is to parse all records in the \narchive and display diagnostic information.", - 0 -}; - -typedef enum {ARG_NO -} cmdline_parser_arg_type; - -static -void clear_given (struct gengetopt_args_info *args_info); -static -void clear_args (struct gengetopt_args_info *args_info); - -static int -cmdline_parser_internal (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error); - - -static char * -gengetopt_strdup (const char *s); - -static -void clear_given (struct gengetopt_args_info *args_info) -{ - args_info->help_given = 0 ; - args_info->version_given = 0 ; - args_info->raw_given = 0 ; - args_info->quiet_given = 0 ; - args_info->loadcells_given = 0 ; -} - -static -void clear_args (struct gengetopt_args_info *args_info) -{ - FIX_UNUSED (args_info); - -} - -static -void init_args_info(struct gengetopt_args_info *args_info) -{ - - - args_info->help_help = gengetopt_args_info_help[0] ; - args_info->version_help = gengetopt_args_info_help[1] ; - args_info->raw_help = gengetopt_args_info_help[2] ; - args_info->quiet_help = gengetopt_args_info_help[3] ; - args_info->loadcells_help = gengetopt_args_info_help[4] ; - -} - -void -cmdline_parser_print_version (void) -{ - printf ("%s %s\n", - (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), - CMDLINE_PARSER_VERSION); -} - -static void print_help_common(void) { - cmdline_parser_print_version (); - - if (strlen(gengetopt_args_info_purpose) > 0) - printf("\n%s\n", gengetopt_args_info_purpose); - - if (strlen(gengetopt_args_info_usage) > 0) - printf("\n%s\n", gengetopt_args_info_usage); - - printf("\n"); - - if (strlen(gengetopt_args_info_description) > 0) - printf("%s\n\n", gengetopt_args_info_description); -} - -void -cmdline_parser_print_help (void) -{ - int i = 0; - print_help_common(); - while (gengetopt_args_info_help[i]) - printf("%s\n", gengetopt_args_info_help[i++]); -} - -void -cmdline_parser_init (struct gengetopt_args_info *args_info) -{ - clear_given (args_info); - clear_args (args_info); - init_args_info (args_info); - - args_info->inputs = 0; - args_info->inputs_num = 0; -} - -void -cmdline_parser_params_init(struct cmdline_parser_params *params) -{ - if (params) - { - params->override = 0; - params->initialize = 1; - params->check_required = 1; - params->check_ambiguity = 0; - params->print_errors = 1; - } -} - -struct cmdline_parser_params * -cmdline_parser_params_create(void) -{ - struct cmdline_parser_params *params = - (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); - cmdline_parser_params_init(params); - return params; -} - - - -static void -cmdline_parser_release (struct gengetopt_args_info *args_info) -{ - unsigned int i; - - - for (i = 0; i < args_info->inputs_num; ++i) - free (args_info->inputs [i]); - - if (args_info->inputs_num) - free (args_info->inputs); - - clear_given (args_info); -} - - -static void -write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) -{ - FIX_UNUSED (values); - if (arg) { - fprintf(outfile, "%s=\"%s\"\n", opt, arg); - } else { - fprintf(outfile, "%s\n", opt); - } -} - - -int -cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) -{ - int i = 0; - - if (!outfile) - { - fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); - return EXIT_FAILURE; - } - - if (args_info->help_given) - write_into_file(outfile, "help", 0, 0 ); - if (args_info->version_given) - write_into_file(outfile, "version", 0, 0 ); - if (args_info->raw_given) - write_into_file(outfile, "raw", 0, 0 ); - if (args_info->quiet_given) - write_into_file(outfile, "quiet", 0, 0 ); - if (args_info->loadcells_given) - write_into_file(outfile, "loadcells", 0, 0 ); - - - i = EXIT_SUCCESS; - return i; -} - -int -cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) -{ - FILE *outfile; - int i = 0; - - outfile = fopen(filename, "w"); - - if (!outfile) - { - fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); - return EXIT_FAILURE; - } - - i = cmdline_parser_dump(outfile, args_info); - fclose (outfile); - - return i; -} - -void -cmdline_parser_free (struct gengetopt_args_info *args_info) -{ - cmdline_parser_release (args_info); -} - -/** @brief replacement of strdup, which is not standard */ -char * -gengetopt_strdup (const char *s) -{ - char *result = 0; - if (!s) - return result; - - result = (char*)malloc(strlen(s) + 1); - if (result == (char*)0) - return (char*)0; - strcpy(result, s); - return result; -} - -int -cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info) -{ - return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); -} - -int -cmdline_parser_ext (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params) -{ - int result; - result = cmdline_parser_internal (argc, argv, args_info, params, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser2 (int argc, char * const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) -{ - int result; - struct cmdline_parser_params params; - - params.override = override; - params.initialize = initialize; - params.check_required = check_required; - params.check_ambiguity = 0; - params.print_errors = 1; - - result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) -{ - FIX_UNUSED (args_info); - FIX_UNUSED (prog_name); - return EXIT_SUCCESS; -} - -/* - * Extracted from the glibc source tree, version 2.3.6 - * - * Licensed under the GPL as per the whole glibc source tree. - * - * This file was modified so that getopt_long can be called - * many times without risking previous memory to be spoiled. - * - * Modified by Andre Noll and Lorenzo Bettini for use in - * GNU gengetopt generated files. - * - */ - -/* - * we must include anything we need since this file is not thought to be - * inserted in a file already using getopt.h - * - * Lorenzo - */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. -*/ -/* - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `custom_optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -/* Names for the values of the `has_arg' field of `struct option'. */ -#ifndef no_argument -#define no_argument 0 -#endif - -#ifndef required_argument -#define required_argument 1 -#endif - -#ifndef optional_argument -#define optional_argument 2 -#endif - -struct custom_getopt_data { - /* - * These have exactly the same meaning as the corresponding global variables, - * except that they are used for the reentrant versions of getopt. - */ - int custom_optind; - int custom_opterr; - int custom_optopt; - char *custom_optarg; - - /* True if the internal members have been initialized. */ - int initialized; - - /* - * The next char to be scanned in the option-element in which the last option - * character we returned was found. This allows us to pick up the scan where - * we left off. If this is zero, or a null string, it means resume the scan by - * advancing to the next ARGV-element. - */ - char *nextchar; - - /* - * Describe the part of ARGV that contains non-options that have been skipped. - * `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is - * the index after the last of them. - */ - int first_nonopt; - int last_nonopt; -}; - -/* - * the variables optarg, optind, opterr and optopt are renamed with - * the custom_ prefix so that they don't interfere with getopt ones. - * - * Moreover they're static so they are visible only from within the - * file where this very file will be included. - */ - -/* - * For communication from `custom_getopt' to the caller. When `custom_getopt' finds an - * option that takes an argument, the argument value is returned here. - */ -static char *custom_optarg; - -/* - * Index in ARGV of the next element to be scanned. This is used for - * communication to and from the caller and for communication between - * successive calls to `custom_getopt'. - * - * On entry to `custom_getopt', 1 means this is the first call; initialize. - * - * When `custom_getopt' returns -1, this is the index of the first of the non-option - * elements that the caller should itself scan. - * - * Otherwise, `custom_optind' communicates from one call to the next how much of ARGV - * has been scanned so far. - * - * 1003.2 says this must be 1 before any call. - */ -static int custom_optind = 1; - -/* - * Callers store zero here to inhibit the error message for unrecognized - * options. - */ -static int custom_opterr = 1; - -/* - * Set to an option character which was unrecognized. This must be initialized - * on some systems to avoid linking in the system's own getopt implementation. - */ -static int custom_optopt = '?'; - -/* - * Exchange two adjacent subsequences of ARGV. One subsequence is elements - * [first_nonopt,last_nonopt) which contains all the non-options that have been - * skipped so far. The other is elements [last_nonopt,custom_optind), which contains - * all the options processed since those non-options were skipped. - * `first_nonopt' and `last_nonopt' are relocated so that they describe the new - * indices of the non-options in ARGV after they are moved. - */ -static void exchange(char **argv, struct custom_getopt_data *d) -{ - int bottom = d->first_nonopt; - int middle = d->last_nonopt; - int top = d->custom_optind; - char *tem; - - /* - * Exchange the shorter segment with the far end of the longer segment. - * That puts the shorter segment into the right place. It leaves the - * longer segment in the right place overall, but it consists of two - * parts that need to be swapped next. - */ - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { - /* Bottom segment is the short one. */ - int len = middle - bottom; - int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = - argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } else { - /* Top segment is the short one. */ - int len = top - middle; - int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - /* Update records for the slots the non-options now occupy. */ - d->first_nonopt += (d->custom_optind - d->last_nonopt); - d->last_nonopt = d->custom_optind; -} - -/* Initialize the internal data when the first call is made. */ -static void custom_getopt_initialize(struct custom_getopt_data *d) -{ - /* - * Start processing options with ARGV-element 1 (since ARGV-element 0 - * is the program name); the sequence of previously skipped non-option - * ARGV-elements is empty. - */ - d->first_nonopt = d->last_nonopt = d->custom_optind; - d->nextchar = NULL; - d->initialized = 1; -} - -#define NONOPTION_P (argv[d->custom_optind][0] != '-' || argv[d->custom_optind][1] == '\0') - -/* return: zero: continue, nonzero: return given value to user */ -static int shuffle_argv(int argc, char *const *argv,const struct option *longopts, - struct custom_getopt_data *d) -{ - /* - * Give FIRST_NONOPT & LAST_NONOPT rational values if CUSTOM_OPTIND has been - * moved back by the user (who may also have changed the arguments). - */ - if (d->last_nonopt > d->custom_optind) - d->last_nonopt = d->custom_optind; - if (d->first_nonopt > d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * If we have just processed some options following some - * non-options, exchange them so that the options come first. - */ - if (d->first_nonopt != d->last_nonopt && - d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->last_nonopt != d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * Skip any additional non-options and extend the range of - * non-options previously skipped. - */ - while (d->custom_optind < argc && NONOPTION_P) - d->custom_optind++; - d->last_nonopt = d->custom_optind; - /* - * The special ARGV-element `--' means premature end of options. Skip - * it like a null option, then exchange with previous non-options as if - * it were an option, then skip everything else like a non-option. - */ - if (d->custom_optind != argc && !strcmp(argv[d->custom_optind], "--")) { - d->custom_optind++; - if (d->first_nonopt != d->last_nonopt - && d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->first_nonopt == d->last_nonopt) - d->first_nonopt = d->custom_optind; - d->last_nonopt = argc; - d->custom_optind = argc; - } - /* - * If we have done all the ARGV-elements, stop the scan and back over - * any non-options that we skipped and permuted. - */ - if (d->custom_optind == argc) { - /* - * Set the next-arg-index to point at the non-options that we - * previously skipped, so the caller will digest them. - */ - if (d->first_nonopt != d->last_nonopt) - d->custom_optind = d->first_nonopt; - return -1; - } - /* - * If we have come to a non-option and did not permute it, either stop - * the scan or describe it to the caller and pass it by. - */ - if (NONOPTION_P) { - d->custom_optarg = argv[d->custom_optind++]; - return 1; - } - /* - * We have found another option-ARGV-element. Skip the initial - * punctuation. - */ - d->nextchar = (argv[d->custom_optind] + 1 + (longopts != NULL && argv[d->custom_optind][1] == '-')); - return 0; -} - -/* - * Check whether the ARGV-element is a long option. - * - * If there's a long option "fubar" and the ARGV-element is "-fu", consider - * that an abbreviation of the long option, just like "--fu", and not "-f" with - * arg "u". - * - * This distinction seems to be the most useful approach. - * - */ -static int check_long_opt(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - int print_errors, struct custom_getopt_data *d) -{ - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = d->nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match or abbreviated matches */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, d->nextchar, nameend - d->nextchar)) { - if ((unsigned int) (nameend - d->nextchar) - == (unsigned int) strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } else if (pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' is ambiguous\n", - argv[0], argv[d->custom_optind]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optind++; - d->custom_optopt = 0; - return '?'; - } - if (pfound) { - option_index = indfound; - d->custom_optind++; - if (*nameend) { - if (pfound->has_arg != no_argument) - d->custom_optarg = nameend + 1; - else { - if (print_errors) { - if (argv[d->custom_optind - 1][1] == '-') { - /* --option */ - fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", - argv[0], pfound->name); - } else { - /* +option or -option */ - fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[d->custom_optind - 1][0], pfound->name); - } - - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return '?'; - } - } else if (pfound->has_arg == required_argument) { - if (d->custom_optind < argc) - d->custom_optarg = argv[d->custom_optind++]; - else { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' requires an argument\n", - argv[0], - argv[d->custom_optind - 1]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - d->nextchar += strlen(d->nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* - * Can't find it as a long option. If this is not getopt_long_only, or - * the option starts with '--' or is not a valid short option, then - * it's an error. Otherwise interpret it as a short option. - */ - if (print_errors) { - if (argv[d->custom_optind][1] == '-') { - /* --option */ - fprintf(stderr, - "%s: unrecognized option `--%s'\n", - argv[0], d->nextchar); - } else { - /* +option or -option */ - fprintf(stderr, - "%s: unrecognized option `%c%s'\n", - argv[0], argv[d->custom_optind][0], - d->nextchar); - } - } - d->nextchar = (char *) ""; - d->custom_optind++; - d->custom_optopt = 0; - return '?'; -} - -static int check_short_opt(int argc, char *const *argv, const char *optstring, - int print_errors, struct custom_getopt_data *d) -{ - char c = *d->nextchar++; - const char *temp = strchr(optstring, c); - - /* Increment `custom_optind' when we start to process its last character. */ - if (*d->nextchar == '\0') - ++d->custom_optind; - if (!temp || c == ':') { - if (print_errors) - fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); - - d->custom_optopt = c; - return '?'; - } - if (temp[1] == ':') { - if (temp[2] == ':') { - /* This is an option that accepts an argument optionally. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - d->custom_optind++; - } else - d->custom_optarg = NULL; - d->nextchar = NULL; - } else { - /* This is an option that requires an argument. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - /* - * If we end this ARGV-element by taking the - * rest as an arg, we must advance to the next - * element now. - */ - d->custom_optind++; - } else if (d->custom_optind == argc) { - if (print_errors) { - fprintf(stderr, - "%s: option requires an argument -- %c\n", - argv[0], c); - } - d->custom_optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } else - /* - * We already incremented `custom_optind' once; - * increment it again when taking next ARGV-elt - * as argument. - */ - d->custom_optarg = argv[d->custom_optind++]; - d->nextchar = NULL; - } - } - return c; -} - -/* - * Scan elements of ARGV for option characters given in OPTSTRING. - * - * If an element of ARGV starts with '-', and is not exactly "-" or "--", - * then it is an option element. The characters of this element - * (aside from the initial '-') are option characters. If `getopt' - * is called repeatedly, it returns successively each of the option characters - * from each of the option elements. - * - * If `getopt' finds another option character, it returns that character, - * updating `custom_optind' and `nextchar' so that the next call to `getopt' can - * resume the scan with the following option character or ARGV-element. - * - * If there are no more option characters, `getopt' returns -1. - * Then `custom_optind' is the index in ARGV of the first ARGV-element - * that is not an option. (The ARGV-elements have been permuted - * so that those that are not options now come last.) - * - * OPTSTRING is a string containing the legitimate option characters. - * If an option character is seen that is not listed in OPTSTRING, - * return '?' after printing an error message. If you set `custom_opterr' to - * zero, the error message is suppressed but we still return '?'. - * - * If a char in OPTSTRING is followed by a colon, that means it wants an arg, - * so the following text in the same ARGV-element, or the text of the following - * ARGV-element, is returned in `custom_optarg'. Two colons mean an option that - * wants an optional arg; if there is text in the current ARGV-element, - * it is returned in `custom_optarg', otherwise `custom_optarg' is set to zero. - * - * If OPTSTRING starts with `-' or `+', it requests different methods of - * handling the non-option ARGV-elements. - * See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - * - * Long-named options begin with `--' instead of `-'. - * Their names may be abbreviated as long as the abbreviation is unique - * or is an exact match for some defined option. If they have an - * argument, it follows the option name in the same ARGV-element, separated - * from the option name by a `=', or else the in next ARGV-element. - * When `getopt' finds a long-named option, it returns 0 if that option's - * `flag' field is nonzero, the value of the option's `val' field - * if the `flag' field is zero. - * - * The elements of ARGV aren't really const, because we permute them. - * But we pretend they're const in the prototype to be compatible - * with other systems. - * - * LONGOPTS is a vector of `struct option' terminated by an - * element containing a name which is zero. - * - * LONGIND returns the index in LONGOPT of the long-named option found. - * It is only valid when a long-named option has been found by the most - * recent call. - * - * Return the option character from OPTS just read. Return -1 when there are - * no more options. For unrecognized options, or options missing arguments, - * `custom_optopt' is set to the option letter, and '?' is returned. - * - * The OPTS string is a list of characters which are recognized option letters, - * optionally followed by colons, specifying that that letter takes an - * argument, to be placed in `custom_optarg'. - * - * If a letter in OPTS is followed by two colons, its argument is optional. - * This behavior is specific to the GNU `getopt'. - * - * The argument `--' causes premature termination of argument scanning, - * explicitly telling `getopt' that there are no more options. If OPTS begins - * with `--', then non-option arguments are treated as arguments to the option - * '\0'. This behavior is specific to the GNU `getopt'. - */ - -static int getopt_internal_r(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - struct custom_getopt_data *d) -{ - int ret, print_errors = d->custom_opterr; - - if (optstring[0] == ':') - print_errors = 0; - if (argc < 1) - return -1; - d->custom_optarg = NULL; - - /* - * This is a big difference with GNU getopt, since optind == 0 - * means initialization while here 1 means first call. - */ - if (d->custom_optind == 0 || !d->initialized) { - if (d->custom_optind == 0) - d->custom_optind = 1; /* Don't scan ARGV[0], the program name. */ - custom_getopt_initialize(d); - } - if (d->nextchar == NULL || *d->nextchar == '\0') { - ret = shuffle_argv(argc, argv, longopts, d); - if (ret) - return ret; - } - if (longopts && (argv[d->custom_optind][1] == '-' )) - return check_long_opt(argc, argv, optstring, longopts, - longind, print_errors, d); - return check_short_opt(argc, argv, optstring, print_errors, d); -} - -static int custom_getopt_internal(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind) -{ - int result; - /* Keep a global copy of all internal members of d */ - static struct custom_getopt_data d; - - d.custom_optind = custom_optind; - d.custom_opterr = custom_opterr; - result = getopt_internal_r(argc, argv, optstring, longopts, - longind, &d); - custom_optind = d.custom_optind; - custom_optarg = d.custom_optarg; - custom_optopt = d.custom_optopt; - return result; -} - -static int custom_getopt_long (int argc, char *const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return custom_getopt_internal(argc, argv, options, long_options, - opt_index); -} - - -static char *package_name = 0; - -/** - * @brief updates an option - * @param field the generic pointer to the field to update - * @param orig_field the pointer to the orig field - * @param field_given the pointer to the number of occurrence of this option - * @param prev_given the pointer to the number of occurrence already seen - * @param value the argument for this option (if null no arg was specified) - * @param possible_values the possible values for this option (if specified) - * @param default_value the default value (in case the option only accepts fixed values) - * @param arg_type the type of this option - * @param check_ambiguity @see cmdline_parser_params.check_ambiguity - * @param override @see cmdline_parser_params.override - * @param no_free whether to free a possible previous value - * @param multiple_option whether this is a multiple option - * @param long_opt the corresponding long option - * @param short_opt the corresponding short option (or '-' if none) - * @param additional_error possible further error specification - */ -static -int update_arg(void *field, char **orig_field, - unsigned int *field_given, unsigned int *prev_given, - char *value, const char *possible_values[], - const char *default_value, - cmdline_parser_arg_type arg_type, - int check_ambiguity, int override, - int no_free, int multiple_option, - const char *long_opt, char short_opt, - const char *additional_error) -{ - char *stop_char = 0; - const char *val = value; - int found; - FIX_UNUSED (field); - - stop_char = 0; - found = 0; - - if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) - { - if (short_opt != '-') - fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", - package_name, long_opt, short_opt, - (additional_error ? additional_error : "")); - else - fprintf (stderr, "%s: `--%s' option given more than once%s\n", - package_name, long_opt, - (additional_error ? additional_error : "")); - return 1; /* failure */ - } - - FIX_UNUSED (default_value); - - if (field_given && *field_given && ! override) - return 0; - if (prev_given) - (*prev_given)++; - if (field_given) - (*field_given)++; - if (possible_values) - val = possible_values[found]; - - switch(arg_type) { - default: - break; - }; - - - /* store the original value */ - switch(arg_type) { - case ARG_NO: - break; - default: - if (value && orig_field) { - if (no_free) { - *orig_field = value; - } else { - if (*orig_field) - free (*orig_field); /* free previous string */ - *orig_field = gengetopt_strdup (value); - } - } - }; - - return 0; /* OK */ -} - - -int -cmdline_parser_internal ( - int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error) -{ - int c; /* Character of the parsed option. */ - - int error = 0; - struct gengetopt_args_info local_args_info; - - int override; - int initialize; - int check_required; - int check_ambiguity; - - char *optarg; - int optind; - int opterr; - int optopt; - - package_name = argv[0]; - - override = params->override; - initialize = params->initialize; - check_required = params->check_required; - check_ambiguity = params->check_ambiguity; - - if (initialize) - cmdline_parser_init (args_info); - - cmdline_parser_init (&local_args_info); - - optarg = 0; - optind = 0; - opterr = params->print_errors; - optopt = '?'; - - while (1) - { - int option_index = 0; - - static struct option long_options[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "raw", 0, NULL, 'r' }, - { "quiet", 0, NULL, 'q' }, - { "loadcells", 0, NULL, 'C' }, - { 0, 0, 0, 0 } - }; - - custom_optarg = optarg; - custom_optind = optind; - custom_opterr = opterr; - custom_optopt = optopt; - - c = custom_getopt_long (argc, argv, "hVrqC", long_options, &option_index); - - optarg = custom_optarg; - optind = custom_optind; - opterr = custom_opterr; - optopt = custom_optopt; - - if (c == -1) break; /* Exit from `while (1)' loop. */ - - switch (c) - { - case 'h': /* Print help and exit. */ - cmdline_parser_print_help (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'V': /* Print version and exit. */ - cmdline_parser_print_version (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'r': /* Show an unformattet list of all records and subrecords. */ - - - if (update_arg( 0 , - 0 , &(args_info->raw_given), - &(local_args_info.raw_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "raw", 'r', - additional_error)) - goto failure; - - break; - case 'q': /* Supress all record information. Useful for speed tests.. */ - - - if (update_arg( 0 , - 0 , &(args_info->quiet_given), - &(local_args_info.quiet_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "quiet", 'q', - additional_error)) - goto failure; - - break; - case 'C': /* Browse through contents of all cells.. */ - - - if (update_arg( 0 , - 0 , &(args_info->loadcells_given), - &(local_args_info.loadcells_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "loadcells", 'C', - additional_error)) - goto failure; - - break; - - case 0: /* Long option with no short option */ - case '?': /* Invalid option. */ - /* `getopt_long' already printed an error message. */ - goto failure; - - default: /* bug: option not considered. */ - fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); - abort (); - } /* switch */ - } /* while */ - - - - - cmdline_parser_release (&local_args_info); - - if ( error ) - return (EXIT_FAILURE); - - if (optind < argc) - { - int i = 0 ; - int found_prog_name = 0; - /* whether program name, i.e., argv[0], is in the remaining args - (this may happen with some implementations of getopt, - but surely not with the one included by gengetopt) */ - - - args_info->inputs_num = argc - optind - found_prog_name; - args_info->inputs = - (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ; - while (optind < argc) - args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind++]) ; - } - - return 0; - -failure: - - cmdline_parser_release (&local_args_info); - return (EXIT_FAILURE); -} diff --git a/apps/esmtool/esmtool_cmd.h b/apps/esmtool/esmtool_cmd.h deleted file mode 100644 index 8c420c189..000000000 --- a/apps/esmtool/esmtool_cmd.h +++ /dev/null @@ -1,179 +0,0 @@ -/** @file esmtool_cmd.h - * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.2 - * http://www.gnu.org/software/gengetopt. - * DO NOT modify this file, since it can be overwritten - * @author GNU Gengetopt by Lorenzo Bettini */ - -#ifndef ESMTOOL_CMD_H -#define ESMTOOL_CMD_H - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include /* for FILE */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef CMDLINE_PARSER_PACKAGE -/** @brief the program name (used for printing errors) */ -#define CMDLINE_PARSER_PACKAGE "esmtool" -#endif - -#ifndef CMDLINE_PARSER_PACKAGE_NAME -/** @brief the complete program name (used for help and version) */ -#define CMDLINE_PARSER_PACKAGE_NAME "esmtool" -#endif - -#ifndef CMDLINE_PARSER_VERSION -/** @brief the program version */ -#define CMDLINE_PARSER_VERSION "1.0" -#endif - -/** @brief Where the command line options are stored */ -struct gengetopt_args_info -{ - const char *help_help; /**< @brief Print help and exit help description. */ - const char *version_help; /**< @brief Print version and exit help description. */ - const char *raw_help; /**< @brief Show an unformattet list of all records and subrecords help description. */ - const char *quiet_help; /**< @brief Supress all record information. Useful for speed tests. help description. */ - const char *loadcells_help; /**< @brief Browse through contents of all cells. help description. */ - - unsigned int help_given ; /**< @brief Whether help was given. */ - unsigned int version_given ; /**< @brief Whether version was given. */ - unsigned int raw_given ; /**< @brief Whether raw was given. */ - unsigned int quiet_given ; /**< @brief Whether quiet was given. */ - unsigned int loadcells_given ; /**< @brief Whether loadcells was given. */ - - char **inputs ; /**< @brief unamed options (options without names) */ - unsigned inputs_num ; /**< @brief unamed options number */ -} ; - -/** @brief The additional parameters to pass to parser functions */ -struct cmdline_parser_params -{ - int override; /**< @brief whether to override possibly already present options (default 0) */ - int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ - int check_required; /**< @brief whether to check that all required options were provided (default 1) */ - int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ - int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ -} ; - -/** @brief the purpose string of the program */ -extern const char *gengetopt_args_info_purpose; -/** @brief the usage string of the program */ -extern const char *gengetopt_args_info_usage; -/** @brief all the lines making the help output */ -extern const char *gengetopt_args_info_help[]; - -/** - * The command line parser - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser (int argc, char * const *argv, - struct gengetopt_args_info *args_info); - -/** - * The command line parser (version with additional parameters - deprecated) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param override whether to override possibly already present options - * @param initialize whether to initialize the option structure my_args_info - * @param check_required whether to check that all required options were provided - * @return 0 if everything went fine, NON 0 if an error took place - * @deprecated use cmdline_parser_ext() instead - */ -int cmdline_parser2 (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required); - -/** - * The command line parser (version with additional parameters) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param params additional parameters for the parser - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_ext (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params); - -/** - * Save the contents of the option struct into an already open FILE stream. - * @param outfile the stream where to dump options - * @param args_info the option struct to dump - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_dump(FILE *outfile, - struct gengetopt_args_info *args_info); - -/** - * Save the contents of the option struct into a (text) file. - * This file can be read by the config file parser (if generated by gengetopt) - * @param filename the file where to save - * @param args_info the option struct to save - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_file_save(const char *filename, - struct gengetopt_args_info *args_info); - -/** - * Print the help - */ -void cmdline_parser_print_help(void); -/** - * Print the version - */ -void cmdline_parser_print_version(void); - -/** - * Initializes all the fields a cmdline_parser_params structure - * to their default values - * @param params the structure to initialize - */ -void cmdline_parser_params_init(struct cmdline_parser_params *params); - -/** - * Allocates dynamically a cmdline_parser_params structure and initializes - * all its fields to their default values - * @return the created and initialized cmdline_parser_params structure - */ -struct cmdline_parser_params *cmdline_parser_params_create(void); - -/** - * Initializes the passed gengetopt_args_info structure's fields - * (also set default values for options that have a default) - * @param args_info the structure to initialize - */ -void cmdline_parser_init (struct gengetopt_args_info *args_info); -/** - * Deallocates the string fields of the gengetopt_args_info structure - * (but does not deallocate the structure itself) - * @param args_info the structure to deallocate - */ -void cmdline_parser_free (struct gengetopt_args_info *args_info); - -/** - * Checks that all the required options were specified - * @param args_info the structure to check - * @param prog_name the name of the program that will be used to print - * possible errors - * @return - */ -int cmdline_parser_required (struct gengetopt_args_info *args_info, - const char *prog_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* ESMTOOL_CMD_H */ diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 2fc3189fc..fd736e011 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -41,8 +41,11 @@ source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC find_package(Qt4 REQUIRED) set(QT_USE_QTGUI 1) -find_package(PNG REQUIRED) -include_directories(${PNG_INCLUDE_DIR}) +# Set some platform specific settings +if(WIN32) + set(GUI_TYPE WIN32) + set(QT_USE_QTMAIN TRUE) +endif(WIN32) QT4_ADD_RESOURCES(RCC_SRCS resources.qrc) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) @@ -50,7 +53,17 @@ QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) include(${QT_USE_FILE}) # Main executable +IF(OGRE_STATIC) +IF(WIN32) +ADD_DEFINITIONS(-DENABLE_PLUGIN_Direct3D9 -DENABLE_PLUGIN_GL) +set(OGRE_STATIC_PLUGINS ${OGRE_RenderSystem_Direct3D9_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES}) +ELSE(WIN32) +ADD_DEFINITIONS(-DENABLE_PLUGIN_GL) +set(OGRE_STATIC_PLUGINS ${OGRE_RenderSystem_GL_LIBRARIES}) +ENDIF(WIN32) +ENDIF(OGRE_STATIC) add_executable(omwlauncher + ${GUI_TYPE} ${LAUNCHER} ${RCC_SRCS} ${MOC_SRCS} @@ -59,8 +72,8 @@ add_executable(omwlauncher target_link_libraries(omwlauncher ${Boost_LIBRARIES} ${OGRE_LIBRARIES} + ${OGRE_STATIC_PLUGINS} ${QT_LIBRARIES} - ${PNG_LIBRARY} components ) @@ -71,12 +84,16 @@ endif() if (APPLE) configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss "${APP_BUNDLE_DIR}/../launcher.qss") - configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg "${APP_BUNDLE_DIR}/../launcher.cfg") else() + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/launcher.qss") + + # Fallback in case getGlobalDataPath does not point to resources configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss") configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg") + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg") endif() diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index c8311846f..054cbf141 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include +#include #include "datafilespage.hpp" #include "lineedit.hpp" @@ -11,6 +9,23 @@ #include "pluginsmodel.hpp" #include "pluginsview.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) */ + using namespace ESM; using namespace std; @@ -26,7 +41,9 @@ bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2) return index1.row() <= index2.row(); } -DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) +DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent) + : QWidget(parent) + , mCfgMgr(cfg) { mDataFilesModel = new QStandardItemModel(); // Contains all plugins with masters mPluginsModel = new PluginsModel(); // Contains selectable plugins @@ -118,37 +135,139 @@ DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); connect(mPluginsTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); + connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); - - setupConfig(); createActions(); + setupConfig(); + setupDataFiles(); + } -void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) +void DataFilesPage::setupConfig() { - // Put the paths in a boost::filesystem vector to use with Files::Collections - Files::PathContainer dataDirs; + QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.cfg").string()); + QFile file(config); - foreach (const QString ¤tPath, paths) { - dataDirs.push_back(boost::filesystem::path(currentPath.toStdString())); + if (!file.exists()) { + config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string()); + } + + // Open our config file + mLauncherConfig = new QSettings(config, QSettings::IniFormat); + file.close(); + + // Make sure we have no groups open + while (!mLauncherConfig->group().isEmpty()) { + mLauncherConfig->endGroup(); + } + + mLauncherConfig->beginGroup("Profiles"); + QStringList profiles = mLauncherConfig->childGroups(); + + if (profiles.isEmpty()) { + // Add a default profile + profiles.append("Default"); + } + + mProfilesComboBox->addItems(profiles); + + QString currentProfile = mLauncherConfig->value("CurrentProfile").toString(); + + if (currentProfile.isEmpty()) { + // No current profile selected + currentProfile = "Default"; + } + + const int currentIndex = mProfilesComboBox->findText(currentProfile); + if (currentIndex != -1) { + // Profile is found + mProfilesComboBox->setCurrentIndex(currentIndex); + } + + mLauncherConfig->endGroup(); +} + + +void DataFilesPage::setupDataFiles() +{ + // We use the Configuration Manager to retrieve the configuration values + boost::program_options::variables_map variables; + boost::program_options::options_description desc; + + desc.add_options() + ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) +// ("data-local", boost::program_options::value()->default_value("")) + ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) + ("encoding", boost::program_options::value()->default_value("win1252")); + + mCfgMgr.readConfiguration(variables, desc); + + // Put the paths in a boost::filesystem vector to use with Files::Collections + Files::PathContainer dataDirs(variables["data"].as()); + mDataDirs = dataDirs; + +// std::string local = variables["data-local"].as(); +// if (!local.empty()) { +// mDataLocal.push_back(Files::PathContainer::value_type(local)); +// dataDirs.push_back(Files::PathContainer::value_type(local)); +// } + + if (dataDirs.size()>1) + dataDirs.resize (1); + + mCfgMgr.processPaths(dataDirs); + + while (dataDirs.empty()) { + // No valid data files directory found + + QMessageBox msgBox; + msgBox.setWindowTitle("Error detecting Morrowind installation"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("
Could not find the Data Files location

\ + The directory containing the Data Files was not found.

\ + Press \"Browse...\" to specify the location manually.
")); + + QAbstractButton *dirSelectButton = + msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == dirSelectButton) { + + QString dataDir = QFileDialog::getExistingDirectory( + this, tr("Select Data Files Directory"), + "/home", + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + dataDirs.push_back(Files::PathContainer::value_type(dataDir.toStdString())); + mDataDirs.push_back(Files::PathContainer::value_type(dataDir.toStdString())); + } else { + // Cancel + break; + } + } + + // Check if cancel was clicked because we can't exit from while loop + if (dataDirs.empty()) { + QApplication::exit(1); + return; } // Create a file collection for the dataDirs - Files::Collections mFileCollections(dataDirs, strict); + Files::Collections fileCollections(dataDirs, !variables["fs-strict"].as()); // First we add all the master files - const Files::MultiDirCollection &esm = mFileCollections.getCollection(".esm"); + const Files::MultiDirCollection &esm = fileCollections.getCollection(".esm"); unsigned int i = 0; // Row number - for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) - { + for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) { QString currentMaster = QString::fromStdString( boost::filesystem::path (iter->second.filename()).string()); const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); - if (itemList.isEmpty()) // Master is not yet in the widget - { + if (itemList.isEmpty()) { // Master is not yet in the widget mMastersWidget->insertRow(i); QTableWidgetItem *item = new QTableWidgetItem(currentMaster); mMastersWidget->setItem(i, 0, item); @@ -157,14 +276,13 @@ void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) } // Now on to the plugins - const Files::MultiDirCollection &esp = mFileCollections.getCollection(".esp"); + const Files::MultiDirCollection &esp = fileCollections.getCollection(".esp"); - for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) - { + for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) { ESMReader fileReader; QStringList availableMasters; // Will contain all found masters - fileReader.setEncoding("win1252"); // FIXME: This should be configurable! + fileReader.setEncoding(variables["encoding"].as()); fileReader.open(iter->second.string()); // First we fill the availableMasters and the mMastersWidget @@ -176,8 +294,7 @@ void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); - if (itemList.isEmpty()) // Master is not yet in the widget - { + if (itemList.isEmpty()) { // Master is not yet in the widget mMastersWidget->insertRow(i); QTableWidgetItem *item = new QTableWidgetItem(currentMaster); @@ -234,54 +351,6 @@ void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) readConfig(); } -void DataFilesPage::setupConfig() -{ - Cfg::ConfigurationManager cfg; - - QString config = (cfg.getRuntimeConfigPath() / "launcher.cfg").string().c_str(); - QFile file(config); - - if (!file.exists()) { - config = QString::fromStdString((cfg.getLocalConfigPath() / "launcher.cfg").string()); - } - - file.setFileName(config); // Just for displaying information - - // Open our config file - mLauncherConfig = new QSettings(config, QSettings::IniFormat); - mLauncherConfig->sync(); - - - // Make sure we have no groups open - while (!mLauncherConfig->group().isEmpty()) { - mLauncherConfig->endGroup(); - } - - mLauncherConfig->beginGroup("Profiles"); - QStringList profiles = mLauncherConfig->childGroups(); - - if (profiles.isEmpty()) { - // Add a default profile - profiles.append("Default"); - } - - mProfilesComboBox->addItems(profiles); - - QString currentProfile = mLauncherConfig->value("CurrentProfile").toString(); - - if (currentProfile.isEmpty()) { - // No current profile selected - currentProfile = "Default"; - } - mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile)); - - mLauncherConfig->endGroup(); - - // Now we connect the combobox to do something if the profile changes - // This prevents strange behaviour while reading and appending the profiles - connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); -} - void DataFilesPage::createActions() { // Refresh the plugins @@ -414,18 +483,18 @@ void DataFilesPage::deleteProfile() return; } - QMessageBox deleteMessageBox(this); - deleteMessageBox.setWindowTitle(tr("Delete Profile")); - deleteMessageBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); - deleteMessageBox.setIcon(QMessageBox::Warning); + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Delete Profile")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); + QAbstractButton *deleteButton = - deleteMessageBox.addButton(tr("Delete"), QMessageBox::ActionRole); + msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); - deleteMessageBox.addButton(QMessageBox::Cancel); + msgBox.exec(); - deleteMessageBox.exec(); - - if (deleteMessageBox.clickedButton() == deleteButton) { + if (msgBox.clickedButton() == deleteButton) { // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { mLauncherConfig->endGroup(); @@ -482,7 +551,6 @@ void DataFilesPage::moveUp() void DataFilesPage::moveDown() { // Shift the selected plugins down one row - if (!mPluginsTable->selectionModel()->hasSelection()) { return; } @@ -898,9 +966,18 @@ void DataFilesPage::filterChanged(const QString filter) void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) { + // Prevent the deletion of the default profile + if (current == "Default") { + mDeleteProfileAction->setEnabled(false); + } else { + mDeleteProfileAction->setEnabled(true); + } + if (!previous.isEmpty()) { writeConfig(previous); mLauncherConfig->sync(); + } else { + return; } uncheckPlugins(); @@ -968,11 +1045,105 @@ void DataFilesPage::readConfig() void DataFilesPage::writeConfig(QString profile) { - // Don't overwrite the config if no plugins are found - if (mPluginsModel->rowCount() < 1) { + // Don't overwrite the config if no masters are found + if (mMastersWidget->rowCount() < 1) { return; } + // Prepare the OpenMW config + QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "openmw.cfg").string()); + QFile file(config); + + if (!file.exists()) { + config = QString::fromStdString((mCfgMgr.getUserPath() / "openmw.cfg").string()); + } + + // Open the config as a QFile + file.setFileName(config); + + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle("Error writing OpenMW configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open or create %0

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

\ + Please make sure you have the right permissions and try again.
").arg(file.fileName())); + msgBox.exec(); + + qApp->exit(1); + return; + } + + if (!buffer.isEmpty()) { + file.write(buffer); + } + + QTextStream gameConfig(&file); + + // First write the list of data dirs + mCfgMgr.processPaths(mDataDirs); + mCfgMgr.processPaths(mDataLocal); + + QString path; + + // data= directories + for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) { + path = QString::fromStdString(it->string()); + path.remove(QChar('\"')); + + // Make sure the string is quoted when it contains spaces + if (path.contains(" ")) { + gameConfig << "data=\"" << path << "\"" << endl; + } else { + gameConfig << "data=" << path << endl; + } + } + + // data-local directory + if (!mDataLocal.empty()) { + path = QString::fromStdString(mDataLocal.front().string()); + path.remove(QChar('\"')); + + if (path.contains(" ")) { + gameConfig << "data-local=\"" << path << "\"" << endl; + } else { + gameConfig << "data-local=" << path << endl; + } + } + + if (profile.isEmpty()) { profile = mProfilesComboBox->currentText(); } @@ -993,24 +1164,29 @@ void DataFilesPage::writeConfig(QString profile) mLauncherConfig->beginGroup(profile); mLauncherConfig->remove(""); // Clear the subgroup - // First write the masters to the config - const QStringList masterList = selectedMasters(); + // Now write the masters to the configs + const QStringList masters = selectedMasters(); // We don't use foreach because we need i - for (int i = 0; i < masterList.size(); ++i) { - const QString master = masterList.at(i); - mLauncherConfig->setValue(QString("Master%0").arg(i), master); + for (int i = 0; i < masters.size(); ++i) { + const QString currentMaster = masters.at(i); + + mLauncherConfig->setValue(QString("Master%0").arg(i), currentMaster); + gameConfig << "master=" << currentMaster << endl; + } - // Now write all checked plugins + // And finally write all checked plugins const QStringList plugins = checkedPlugins(); - for (int i = 0; i < plugins.size(); ++i) - { - mLauncherConfig->setValue(QString("Plugin%1").arg(i), plugins.at(i)); + for (int i = 0; i < plugins.size(); ++i) { + const QString currentPlugin = plugins.at(i); + mLauncherConfig->setValue(QString("Plugin%1").arg(i), currentPlugin); + gameConfig << "plugin=" << currentPlugin << endl; } + file.close(); mLauncherConfig->endGroup(); mLauncherConfig->endGroup(); - + mLauncherConfig->sync(); } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 2d0a385a7..ad5e90511 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -3,6 +3,9 @@ #include #include + +#include + #include "combobox.hpp" class QTableWidget; @@ -19,24 +22,19 @@ class PluginsModel; class PluginsView; class ComboBox; +namespace Files { struct ConfigurationManager; } + class DataFilesPage : public QWidget { Q_OBJECT public: - DataFilesPage(QWidget *parent = 0); + DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent = 0); ComboBox *mProfilesComboBox; - QSettings *mLauncherConfig; - const QStringList checkedPlugins(); - const QStringList selectedMasters(); - void setupConfig(); - void readConfig(); void writeConfig(QString profile = QString()); - void setupDataFiles(const QStringList &paths, bool strict); - public slots: void masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void setCheckState(QModelIndex index); @@ -81,11 +79,22 @@ private: QAction *mCheckAction; QAction *mUncheckAction; + Files::ConfigurationManager &mCfgMgr; + Files::PathContainer mDataDirs; + Files::PathContainer mDataLocal; + + QSettings *mLauncherConfig; + + const QStringList checkedPlugins(); + const QStringList selectedMasters(); + void addPlugins(const QModelIndex &index); void removePlugins(const QModelIndex &index); void uncheckPlugins(); void createActions(); - + void setupDataFiles(); + void setupConfig(); + void readConfig(); void scrollToSelection(); }; diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 92fbf3350..95b38d53e 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -1,8 +1,11 @@ #include #include "graphicspage.hpp" +#include -GraphicsPage::GraphicsPage(QWidget *parent) : QWidget(parent) +GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent) + : QWidget(parent) + , mCfgMgr(cfg) { QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this); @@ -147,21 +150,21 @@ void GraphicsPage::createPages() void GraphicsPage::setupConfig() { - QString ogreCfg = mCfg.getOgreConfigPath().string().c_str(); + QString ogreCfg = mCfgMgr.getOgreConfigPath().string().c_str(); QFile file(ogreCfg); mOgreConfig = new QSettings(ogreCfg, QSettings::IniFormat); } void GraphicsPage::setupOgre() { - QString pluginCfg = mCfg.getPluginsConfigPath().string().c_str(); + QString pluginCfg = mCfgMgr.getPluginsConfigPath().string().c_str(); QFile file(pluginCfg); // Create a log manager so we can surpress debug text to stdout/stderr Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager; - logMgr->createLog((mCfg.getLogPath().string() + "/launcherOgre.log"), true, false, false); + logMgr->createLog((mCfgMgr.getLogPath().string() + "/launcherOgre.log"), true, false, false); - QString ogreCfg = QString::fromStdString(mCfg.getOgreConfigPath().string()); + QString ogreCfg = QString::fromStdString(mCfgMgr.getOgreConfigPath().string()); file.setFileName(ogreCfg); //we need to check that the path to the configuration file exists before we @@ -177,13 +180,17 @@ void GraphicsPage::setupOgre() Make sure you have write access to
%1

")).arg(configDir.path())); msgBox.exec(); - QApplication::exit(1); + qApp->exit(1); return; } try { + #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) + mOgre = new Ogre::Root("", file.fileName().toStdString(), "./launcherOgre.log"); + #else mOgre = new Ogre::Root(pluginCfg.toStdString(), file.fileName().toStdString(), "./launcherOgre.log"); + #endif } catch(Ogre::Exception &ex) { @@ -200,10 +207,19 @@ void GraphicsPage::setupOgre() qCritical("Error creating Ogre::Root, the error reported was:\n %s", qPrintable(ogreError)); - QApplication::exit(1); + qApp->exit(1); return; } + #ifdef ENABLE_PLUGIN_GL + mGLPlugin = new Ogre::GLPlugin(); + mOgre->installPlugin(mGLPlugin); + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + mD3D9Plugin = new Ogre::D3D9Plugin(); + mOgre->installPlugin(mD3D9Plugin); + #endif + // Get the available renderers and put them in the combobox const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers(); @@ -234,7 +250,7 @@ void GraphicsPage::setupOgre() Please make sure the plugins.cfg file exists and contains a valid rendering plugin.
")); msgBox.exec(); - QApplication::exit(1); + qApp->exit(1); return; } @@ -243,13 +259,7 @@ void GraphicsPage::setupOgre() if (mOpenGLRenderSystem) { mOGLRTTComboBox->addItems(getAvailableOptions(QString("RTT Preferred Mode"), mOpenGLRenderSystem)); mOGLAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mOpenGLRenderSystem)); - - QStringList videoModes = getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem); - // Remove extraneous spaces - videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" ")); - videoModes.replaceInStrings(QRegExp("^\\s"), QString()); - - mOGLResolutionComboBox->addItems(videoModes); + mOGLResolutionComboBox->addItems(getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem)); mOGLFrequencyComboBox->addItems(getAvailableOptions(QString("Display Frequency"), mOpenGLRenderSystem)); } @@ -258,12 +268,7 @@ void GraphicsPage::setupOgre() mD3DRenderDeviceComboBox->addItems(getAvailableOptions(QString("Rendering Device"), mDirect3DRenderSystem)); mD3DAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mDirect3DRenderSystem)); mD3DFloatingPointComboBox->addItems(getAvailableOptions(QString("Floating-point mode"), mDirect3DRenderSystem)); - - QStringList videoModes = getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem); - // Remove extraneous spaces - videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" ")); - videoModes.replaceInStrings(QRegExp("^\\s"), QString()); - mD3DResolutionComboBox->addItems(videoModes); + mD3DResolutionComboBox->addItems(getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem)); } } @@ -420,7 +425,7 @@ void GraphicsPage::writeConfig() qCritical("Error validating configuration"); - QApplication::exit(1); + qApp->exit(1); return; } @@ -446,7 +451,8 @@ void GraphicsPage::writeConfig() qCritical("Error saving Ogre configuration, the error reported was:\n %s", qPrintable(ogreError)); - QApplication::exit(1); + qApp->exit(1); + return; } } @@ -478,7 +484,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy { if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) - result << (*opt_it).c_str(); + result << QString::fromStdString((*opt_it).c_str()).simplified(); } } diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 5d50cfc61..6a91a0628 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -7,21 +7,28 @@ #include #include #include -#include + +// Static plugin headers +#ifdef ENABLE_PLUGIN_GL +# include "OgreGLPlugin.h" +#endif +#ifdef ENABLE_PLUGIN_Direct3D9 +# include "OgreD3D9Plugin.h" +#endif class QComboBox; class QCheckBox; class QStackedWidget; class QSettings; +namespace Files { struct ConfigurationManager; } + class GraphicsPage : public QWidget { Q_OBJECT public: - GraphicsPage(QWidget *parent = 0); - - QSettings *mOgreConfig; + GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent = 0); void writeConfig(); @@ -29,11 +36,16 @@ public slots: void rendererChanged(const QString &renderer); private: - Cfg::ConfigurationManager mCfg; Ogre::Root *mOgre; Ogre::RenderSystem *mSelectedRenderSystem; Ogre::RenderSystem *mOpenGLRenderSystem; Ogre::RenderSystem *mDirect3DRenderSystem; + #ifdef ENABLE_PLUGIN_GL + Ogre::GLPlugin* mGLPlugin; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + Ogre::D3D9Plugin* mD3D9Plugin; + #endif QComboBox *mRendererComboBox; @@ -59,6 +71,10 @@ private: QCheckBox *mD3DVSyncCheckBox; QCheckBox *mD3DFullScreenCheckBox; + QSettings *mOgreConfig; + + Files::ConfigurationManager &mCfgMgr; + QString getConfigValue(const QString &key, Ogre::RenderSystem *renderer); QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 4e438a4db..bd29e2bca 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "maindialog.hpp" @@ -17,17 +18,19 @@ int main(int argc, char *argv[]) dir.cdUp(); dir.cdUp(); } + + // force Qt to load only LOCAL plugins, don't touch system Qt installation + QDir pluginsPath(QCoreApplication::applicationDirPath()); + pluginsPath.cdUp(); + pluginsPath.cd("Plugins"); + + QStringList libraryPaths; + libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); + app.setLibraryPaths(libraryPaths); #endif QDir::setCurrent(dir.absolutePath()); - // Load the stylesheet - QFile file("./launcher.qss"); - - file.open(QFile::ReadOnly); - QString styleSheet = QLatin1String(file.readAll()); - app.setStyleSheet(styleSheet); - MainDialog dialog; return dialog.exec(); diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 4ec8b309c..49c0bd960 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -45,6 +45,20 @@ MainDialog::MainDialog() setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); setMinimumSize(QSize(575, 575)); + // Load the stylesheet + QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string()); + QFile file(config); + + if (!file.exists()) { + file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string())); + } + + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + qApp->setStyleSheet(styleSheet); + file.close(); + + connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(play())); @@ -85,116 +99,13 @@ void MainDialog::createIcons() } -QStringList MainDialog::readConfig(const QString &fileName) -{ - // We can't use QSettings directly because it - // does not support multiple keys with the same name - QFile file(fileName); - - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error opening OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open %0

\ - Please make sure you have the right permissions and try again.
").arg(file.fileName())); - msgBox.exec(); - - QApplication::exit(); // File cannot be opened or created - } - - QTextStream in(&file); - QStringList dataDirs; - QString dataLocal; - - // Read the config line by line - while (!in.atEnd()) { - QString line = in.readLine(); - - if (line.startsWith("data=")) { - dataDirs.append(line.remove("data=")); - } - - // Read the data-local key, if more than one are found only the last is used - if (line.startsWith("data-local=")) { - dataLocal = line.remove("data-local="); - } - - // Read fs-strict key - if (line.startsWith("fs-strict=")) { - QString value = line.remove("fs-strict="); - - (value.toLower() == QLatin1String("true")) - ? mStrict = true - : mStrict = false; - - } - - } - - // Add the data-local= key to the end of the dataDirs for priority reasons - if (!dataLocal.isEmpty()) { - dataDirs.append(dataLocal); - } - - if (!dataDirs.isEmpty()) - { - // Reset the global datadirs to the newly read entries - // Else return the previous dataDirs because nothing was found in this file; - mDataDirs = dataDirs; - } - - file.close(); - - return mDataDirs; -} - void MainDialog::createPages() { mPlayPage = new PlayPage(this); - mGraphicsPage = new GraphicsPage(this); - mDataFilesPage = new DataFilesPage(this); + mGraphicsPage = new GraphicsPage(mCfgMgr, this); + mDataFilesPage = new DataFilesPage(mCfgMgr, this); - // Retrieve all data entries from the configs - QStringList dataDirs; - - // Global location - QFile file(QString::fromStdString((mCfg.getGlobalConfigPath()/"openmw.cfg").string())); - if (file.exists()) { - dataDirs = readConfig(file.fileName()); - } - - // User location - file.setFileName(QString::fromStdString((mCfg.getLocalConfigPath()/"openmw.cfg").string())); - if (file.exists()) { - dataDirs = readConfig(file.fileName()); - } - - // Local location - file.setFileName("./openmw.cfg"); - - if (file.exists()) { - dataDirs = readConfig(file.fileName()); - } - - file.close(); - - if (!dataDirs.isEmpty()) { - // Now pass the datadirs on to the DataFilesPage - mDataFilesPage->setupDataFiles(dataDirs, mStrict); - } else { - QMessageBox msgBox; - msgBox.setWindowTitle("Error reading OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not read the location of the data files

\ - Please make sure OpenMW is correctly configured and try again.
")); - msgBox.exec(); - - QApplication::exit(); // No data or data-local entries in openmw.cfg - } - - // Set the combobox of the play page to imitate the comobox on the datafilespage + // Set the combobox of the play page to imitate the combobox on the datafilespage mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model()); mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex()); @@ -246,14 +157,16 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) void MainDialog::closeEvent(QCloseEvent *event) { // Now write all config files - writeConfig(); + mDataFilesPage->writeConfig(); + mGraphicsPage->writeConfig(); event->accept(); } void MainDialog::play() { // First do a write of all the configs, just to be sure - writeConfig(); + mDataFilesPage->writeConfig(); + mGraphicsPage->writeConfig(); #ifdef Q_WS_WIN QString game = "./openmw.exe"; @@ -313,75 +226,3 @@ void MainDialog::play() close(); } } - -void MainDialog::writeConfig() -{ - // Write the profiles - mDataFilesPage->writeConfig(); - mDataFilesPage->mLauncherConfig->sync(); - - // Write the graphics settings - mGraphicsPage->writeConfig(); - mGraphicsPage->mOgreConfig->sync(); - - QStringList dataFiles = mDataFilesPage->selectedMasters(); - dataFiles.append(mDataFilesPage->checkedPlugins()); - - // Open the config as a QFile - QFile file(QString::fromStdString((mCfg.getLocalConfigPath()/"openmw.cfg").string())); - - if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { - // File cannot be opened or created - QMessageBox msgBox; - msgBox.setWindowTitle("Error writing OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open or create %0

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

\ - Please make sure you have the right permissions and try again.
").arg(file.fileName())); - msgBox.exec(); - - QApplication::exit(1); - } - - file.write(buffer); - - QTextStream out(&file); - - // Write the list of game files to the config - foreach (const QString ¤tFile, dataFiles) { - - if (currentFile.endsWith(QString(".esm"), Qt::CaseInsensitive)) { - out << "master=" << currentFile << endl; - } else if (currentFile.endsWith(QString(".esp"), Qt::CaseInsensitive)) { - out << "plugin=" << currentFile << endl; - } - } - - file.close(); -} diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 047050902..d6d0e9974 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -3,7 +3,7 @@ #include -#include +#include class QListWidget; class QListWidgetItem; @@ -28,15 +28,11 @@ public slots: void play(); void profileChanged(int index); - private: void createIcons(); void createPages(); - void writeConfig(); void closeEvent(QCloseEvent *event); - QStringList readConfig(const QString &fileName); - QListWidget *mIconWidget; QStackedWidget *mPagesWidget; @@ -44,10 +40,7 @@ private: GraphicsPage *mGraphicsPage; DataFilesPage *mDataFilesPage; - QStringList mDataDirs; - bool mStrict; - - Cfg::ConfigurationManager mCfg; + Files::ConfigurationManager mCfgMgr; }; #endif diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index f1d940253..f416b996d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -1,22 +1,24 @@ -project(OpenMW) # config file - -configure_file ("${OpenMW_SOURCE_DIR}/config.hpp.cmake" "${OpenMW_SOURCE_DIR}/config.hpp") +configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/config.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/config.hpp") # local files - set(GAME main.cpp engine.cpp - ) +) set(GAME_HEADER engine.hpp - config.hpp) + config.hpp +) source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player npcs creatures objects renderinginterface water + + renderingmanager debugging sky player renderinginterface water + + animation npcanimation creatureanimation actors objects renderinginterface + ) add_openmw_dir (mwinput @@ -25,7 +27,7 @@ add_openmw_dir (mwinput add_openmw_dir (mwgui layouts text_input widgets race class birth review window_manager console dialogue - dialogue_history window_base stats_window messagebox + dialogue_history window_base stats_window messagebox journalwindow charactercreation ) add_openmw_dir (mwdialogue @@ -36,6 +38,7 @@ add_openmw_dir (mwscript locals scriptmanager compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions + animationextensions ) add_openmw_dir (mwsound @@ -44,8 +47,8 @@ add_openmw_dir (mwsound add_openmw_dir (mwworld refdata world physicssystem scene environment globals class action nullaction actionteleport - containerstore actiontalk actiontake containerstore manualref containerutil player cellfunctors - cells localscripts + containerstore actiontalk actiontake manualref player cellfunctors + cells localscripts customdata weather inventorystore ) add_openmw_dir (mwclass @@ -58,15 +61,22 @@ add_openmw_dir (mwmechanics ) # Main executable +IF(OGRE_STATIC) +IF(WIN32) +ADD_DEFINITIONS(-DENABLE_PLUGIN_CgProgramManager -DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_-DENABLE_PLUGIN_Direct3D9 -DENABLE_PLUGIN_GL) +set(OGRE_STATIC_PLUGINS ${OGRE_Plugin_CgProgramManager_LIBRARIES} ${OGRE_Plugin_OctreeSceneManager_LIBRARIES} ${OGRE_Plugin_ParticleFX_LIBRARIES} ${OGRE_RenderSystem_Direct3D9_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES}) +ELSE(WIN32) +ADD_DEFINITIONS(-DENABLE_PLUGIN_CgProgramManager -DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_GL) +set(OGRE_STATIC_PLUGINS ${OGRE_Plugin_CgProgramManager_LIBRARIES} ${Cg_LIBRARIES} ${OGRE_Plugin_OctreeSceneManager_LIBRARIES} ${OGRE_Plugin_ParticleFX_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES}) +ENDIF(WIN32) +ENDIF(OGRE_STATIC) add_executable(openmw ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER} - ${CONPONENT_FILES} + ${COMPONENT_FILES} ${OPENMW_FILES} ${GAME} ${GAME_HEADER} ${APPLE_BUNDLE_RESOURCES} - ) - -target_link_libraries (openmw components) +) # Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING # when we change the backend. @@ -74,26 +84,23 @@ include_directories(${SOUND_INPUT_INCLUDES} ${BULLET_INCLUDE_DIRS}) add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw - ${OGRE_LIBRARIES} - ${OIS_LIBRARIES} - ${Boost_LIBRARIES} - ${OPENAL_LIBRARY} - ${SOUND_INPUT_LIBRARY} - ${BULLET_LIBRARIES} - caelum - MyGUIEngine - MyGUIOgrePlatform + ${OGRE_LIBRARIES} + ${OGRE_STATIC_PLUGINS} + ${OIS_LIBRARIES} + ${Boost_LIBRARIES} + ${OPENAL_LIBRARY} + ${SOUND_INPUT_LIBRARY} + ${BULLET_LIBRARIES} + components + MyGUIEngine + MyGUIOgrePlatform ) -if (APPLE) +if(APPLE) find_library(CARBON_FRAMEWORK Carbon) target_link_libraries(openmw ${CARBON_FRAMEWORK}) - install(TARGETS openmw - BUNDLE DESTINATION . - RUNTIME DESTINATION ../MacOS - COMPONENT Runtime) -endif (APPLE) +endif(APPLE) if(DPKG_PROGRAM) INSTALL(TARGETS openmw RUNTIME DESTINATION games COMPONENT openmw) -endif() +endif(DPKG_PROGRAM) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8ca57feb5..7f6dccf1f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -7,6 +7,7 @@ #include #include +#include #include @@ -17,8 +18,11 @@ #include #include #include -#include +#include +#include + #include +#include #include "mwinput/inputmanager.hpp" @@ -33,8 +37,6 @@ #include "mwsound/soundmanager.hpp" #include "mwworld/world.hpp" -#include "mwworld/ptr.hpp" -#include "mwworld/environment.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" @@ -45,6 +47,7 @@ #include "mwmechanics/mechanicsmanager.hpp" + void OMW::Engine::executeLocalScripts() { MWWorld::LocalScripts& localScripts = mEnvironment.mWorld->getLocalScripts(); @@ -99,6 +102,13 @@ void OMW::Engine::updateFocusReport (float duration) } } +void OMW::Engine::setAnimationVerbose(bool animverbose){ + if(animverbose){ + NifOgre::NIFLoader::getSingletonPtr()->setOutputAnimFiles(true); + NifOgre::NIFLoader::getSingletonPtr()->setVerbosePath(mCfgMgr.getLogPath().string()); + } +} + bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) { try @@ -108,15 +118,16 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // sound if (mUseSound) { - if (!mEnvironment.mSoundManager->isMusicPlaying()) - mEnvironment.mSoundManager->startRandomTitle(); + mEnvironment.mSoundManager->playPlaylist(); mEnvironment.mSoundManager->update (evt.timeSinceLastFrame); } // update GUI - if(mShowFPS) - mEnvironment.mWindowManager->wmSetFPS(mOgre->getFPS()); + Ogre::RenderWindow* window = mOgre->getWindow(); + mEnvironment.mWindowManager->wmUpdateFps(window->getLastFPS(), + window->getTriangleCount(), + window->getBatchCount()); mEnvironment.mWindowManager->onFrame(mEnvironment.mFrameDuration); @@ -135,6 +146,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.mWorld->advanceTime ( mEnvironment.mFrameDuration*mEnvironment.mWorld->getTimeScaleFactor()/3600); + if (changed) // keep change flag for another frame, if cell changed happend in local script mEnvironment.mWorld->markCellAsUnchanged(); @@ -145,6 +157,9 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (mEnvironment.mWindowManager->getMode()==MWGui::GM_Game) mEnvironment.mWorld->doPhysics (movement, mEnvironment.mFrameDuration); + // update world + mEnvironment.mWorld->update (evt.timeSinceLastFrame); + // report focus object (for debugging) if (mReportFocus) updateFocusReport (mEnvironment.mFrameDuration); @@ -157,10 +172,9 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) return true; } -OMW::Engine::Engine(Cfg::ConfigurationManager& configurationManager) +OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mOgre (0) - , mPhysicEngine (0) - , mShowFPS (false) + , mFpsLevel(0) , mDebug (false) , mVerboseScripts (false) , mNewGame (false) @@ -170,7 +184,6 @@ OMW::Engine::Engine(Cfg::ConfigurationManager& configurationManager) , mFocusTDiff (0) , mScriptManager (0) , mScriptContext (0) - , mGuiManager (0) , mFSStrict (false) , mCfgMgr(configurationManager) { @@ -180,7 +193,6 @@ OMW::Engine::Engine(Cfg::ConfigurationManager& configurationManager) OMW::Engine::~Engine() { - delete mGuiManager; delete mEnvironment.mWorld; delete mEnvironment.mSoundManager; delete mEnvironment.mGlobalScripts; @@ -189,7 +201,6 @@ OMW::Engine::~Engine() delete mEnvironment.mJournal; delete mScriptManager; delete mScriptContext; - delete mPhysicEngine; delete mOgre; } @@ -198,15 +209,16 @@ OMW::Engine::~Engine() void OMW::Engine::loadBSA() { const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa"); - - for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter) + std::string dataDirectory; + for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter) { - std::cout << "Adding " << iter->second.string() << std::endl; - Bsa::addBSA (iter->second.string()); - } + std::cout << "Adding " << iter->second.string() << std::endl; + Bsa::addBSA(iter->second.string()); - std::cout << "Data dir " << mDataDir.string() << std::endl; - Bsa::addDir(mDataDir.string(), mFSStrict); + dataDirectory = iter->second.parent_path().string(); + std::cout << "Data dir " << dataDirectory << std::endl; + Bsa::addDir(dataDirectory, mFSStrict); + } } // add resources directory @@ -227,9 +239,7 @@ void OMW::Engine::enableFSStrict(bool fsStrict) void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs) { - /// \todo remove mDataDir, once resources system can handle multiple directories - assert (!dataDirs.empty()); - mDataDir = dataDirs.back(); + mDataDirs = dataDirs; mFileCollections = Files::Collections (dataDirs, !mFSStrict); } @@ -305,7 +315,7 @@ void OMW::Engine::go() } mOgre->configure(!boost::filesystem::is_regular_file(mCfgMgr.getOgreConfigPath()), mCfgMgr.getOgreConfigPath().string(), - mCfgMgr.getLogPath().string() + std::string("/"), + mCfgMgr.getLogPath().string(), mCfgMgr.getPluginsConfigPath().string(), false); // This has to be added BEFORE MyGUI is initialized, as it needs @@ -321,31 +331,20 @@ void OMW::Engine::go() loadBSA(); - /// \todo move this into the physics manager - // Create physics. shapeLoader is deleted by the physic engine - NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); - mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader); - // Create the world - mEnvironment.mWorld = new MWWorld::World (*mOgre, mPhysicEngine, mFileCollections, mMaster, + mEnvironment.mWorld = new MWWorld::World (*mOgre, mFileCollections, mMaster, mResDir, mNewGame, mEnvironment, mEncoding); - /// \todo move this into the GUI manager (a.k.a WindowManager) - // Set up the GUI system - mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, - mCfgMgr.getLogPath().string() + std::string("/")); - // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); - mEnvironment.mWindowManager = new MWGui::WindowManager(mGuiManager->getGui(), mEnvironment, - mExtensions, mShowFPS, mNewGame); + mEnvironment.mWindowManager = new MWGui::WindowManager(mEnvironment, + mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/")); // Create sound system mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(), mOgre->getCamera(), - mEnvironment.mWorld->getStore(), - (mDataDir), + mDataDirs, mUseSound, mFSStrict, mEnvironment); // Create script system @@ -393,7 +392,7 @@ void OMW::Engine::go() mOgre->getRoot()->addFrameListener (this); // Play some good 'ol tunes - mEnvironment.mSoundManager->startRandomTitle(); + mEnvironment.mSoundManager->playPlaylist(std::string("Explore")); // scripts if (mCompileAll) @@ -416,6 +415,9 @@ void OMW::Engine::go() void OMW::Engine::activate() { + if (mEnvironment.mWindowManager->getMode()!=MWGui::GM_Game) + return; + std::string handle = mEnvironment.mWorld->getFacedHandle(); if (handle.empty()) @@ -449,6 +451,28 @@ void OMW::Engine::activate() } } +void OMW::Engine::screenshot() +{ + // Count screenshots. + int shotCount = 0; + + const std::string screenshotPath = mCfgMgr.getUserPath().string(); + + // Find the first unused filename with a do-while + std::ostringstream stream; + do + { + // Reset the stream + stream.str(""); + stream.clear(); + + stream << screenshotPath << "screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << ".png"; + + } while (boost::filesystem::exists(stream.str())); + + mOgre->screenshot(stream.str()); +} + void OMW::Engine::setCompileAll (bool all) { mCompileAll = all; @@ -459,9 +483,9 @@ void OMW::Engine::setSoundUsage(bool soundUsage) mUseSound = soundUsage; } -void OMW::Engine::showFPS(bool showFps) +void OMW::Engine::showFPS(int level) { - mShowFPS = showFps; + mFpsLevel = level; } void OMW::Engine::setEncoding(const std::string& encoding) diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 443f790a4..5c5cdc018 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -7,11 +7,8 @@ #include -#include - #include #include -#include #include "mwworld/environment.hpp" #include "mwworld/ptr.hpp" @@ -54,19 +51,23 @@ namespace OEngine } } +namespace Files +{ + struct ConfigurationManager; +} + namespace OMW { /// \brief Main engine class, that brings together all the components of OpenMW class Engine : private Ogre::FrameListener { std::string mEncoding; - boost::filesystem::path mDataDir; + Files::PathContainer mDataDirs; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; - OEngine::Physic::PhysicEngine* mPhysicEngine; std::string mCellName; std::string mMaster; - bool mShowFPS; + int mFpsLevel; bool mDebug; bool mVerboseScripts; bool mNewGame; @@ -80,7 +81,7 @@ namespace OMW MWScript::ScriptManager *mScriptManager; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; - OEngine::GUI::MyGUIManager *mGuiManager; + Files::Collections mFileCollections; bool mFSStrict; @@ -104,7 +105,7 @@ namespace OMW virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt); public: - Engine(Cfg::ConfigurationManager& configurationManager); + Engine(Files::ConfigurationManager& configurationManager); virtual ~Engine(); /// Enable strict filesystem mode (do not fold case) @@ -128,7 +129,7 @@ namespace OMW void addMaster(const std::string& master); /// Enable fps counter - void showFPS(bool showFps); + void showFPS(int level); /// Enable debug mode: /// - non-exclusive input @@ -152,14 +153,19 @@ namespace OMW /// Activate the focussed object. void activate(); + /// Write screenshot to file. + void screenshot(); + /// Compile all scripts (excludign dialogue scripts) at startup? void setCompileAll (bool all); /// Font encoding void setEncoding(const std::string& encoding); + void setAnimationVerbose(bool animverbose); + private: - Cfg::ConfigurationManager& mCfgMgr; + Files::ConfigurationManager& mCfgMgr; }; } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 933d1c48a..cd1e0e26e 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -6,9 +6,9 @@ #include #include -#include +#include #include -#include +#include #include "engine.hpp" @@ -35,6 +35,23 @@ #include "config.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) */ + using namespace std; /** @@ -46,7 +63,7 @@ using namespace std; * \retval true - Everything goes OK * \retval false - Error */ -bool parseOptions (int argc, char** argv, OMW::Engine& engine, Cfg::ConfigurationManager& cfgMgr) +bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::ConfigurationManager& cfgMgr) { // Create a local alias for brevity namespace bpo = boost::program_options; @@ -75,8 +92,11 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Cfg::Configuratio ("plugin", bpo::value()->default_value(StringsVector(), "") ->multitoken(), "plugin file(s)") - ("fps", boost::program_options::value()->implicit_value(true) - ->default_value(false), "show fps counter") + ("fps", boost::program_options::value()->implicit_value(1) + ->default_value(0), "fps counter detail (0 = off, 1 = fps counter, 2 = full detail)") + + ("anim-verbose", boost::program_options::value()->implicit_value(true) + ->default_value(false), "output animation indices files") ("debug", boost::program_options::value()->implicit_value(true) ->default_value(false), "debug mode") @@ -161,14 +181,19 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Cfg::Configuratio std::string local(variables["data-local"].as()); if (!local.empty()) { - dataDirs.push_back(Files::PathContainer::value_type(local)); + std::cout << "Ignoring data-local (currently not supported)" << std::endl; +// dataDirs.push_back(Files::PathContainer::value_type(local)); } - if (dataDirs.empty()) + if (dataDirs.size()>1) { - dataDirs.push_back(cfgMgr.getLocalDataPath()); + dataDirs.resize (1); + std::cout << "Ignoring all but the first data path (multiple data paths currently not supported)" + << std::endl; } + cfgMgr.processPaths(dataDirs); + engine.setDataDirs(dataDirs); engine.setResourceDir(variables["resources"].as()); @@ -200,12 +225,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Cfg::Configuratio engine.setNewGame(variables["new-game"].as()); // other settings - engine.showFPS(variables["fps"].as()); + engine.showFPS(variables["fps"].as()); engine.setDebugMode(variables["debug"].as()); engine.setSoundUsage(!variables["nosound"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setCompileAll(variables["script-all"].as()); engine.setReportFocus(variables["report-focus"].as()); + engine.setAnimationVerbose(variables["anim-verbose"].as()); return true; } @@ -220,7 +246,7 @@ int main(int argc, char**argv) try { - Cfg::ConfigurationManager cfgMgr; + Files::ConfigurationManager cfgMgr; OMW::Engine engine(cfgMgr); if (parseOptions(argc, argv, engine, cfgMgr)) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 30b308e70..e95fb572f 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -7,9 +7,11 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwrender/objects.hpp" -#include "containerutil.hpp" +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -20,7 +22,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -54,16 +56,12 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Apparatus::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.appas); - } - std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -78,4 +76,14 @@ namespace MWClass registerClass (typeid (ESM::Apparatus).name(), instance); } + + std::string Apparatus::getUpSoundId (const MWWorld::Ptr& ptr) const + { + return std::string("Item Apparatus Up"); + } + + std::string Apparatus::getDownSoundId (const MWWorld::Ptr& ptr) const + { + return std::string("Item Apparatus Down"); + } } diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 4c8a2c0e2..c0849e1fe 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_APPARATUS_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,16 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index a8a431acf..e1c2734f0 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -2,14 +2,21 @@ #include "armor.hpp" #include +#include +#include #include #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" -#include "containerutil.hpp" +#include "../mwrender/objects.hpp" + +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -20,7 +27,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -53,6 +60,8 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -70,12 +79,6 @@ namespace MWClass return ref->base->data.health; } - void Armor::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.armors); - } - std::string Armor::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -84,10 +87,105 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + const int size = 11; + + static const int sMapping[size][2] = + { + { ESM::Armor::Helmet, MWWorld::InventoryStore::Slot_Helmet }, + { ESM::Armor::Cuirass, MWWorld::InventoryStore::Slot_Cuirass }, + { ESM::Armor::LPauldron, MWWorld::InventoryStore::Slot_LeftPauldron }, + { ESM::Armor::RPauldron, MWWorld::InventoryStore::Slot_RightPauldron }, + { ESM::Armor::Greaves, MWWorld::InventoryStore::Slot_Greaves }, + { ESM::Armor::Boots, MWWorld::InventoryStore::Slot_Boots }, + { ESM::Armor::LGauntlet, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Armor::RGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet }, + { ESM::Armor::Shield, MWWorld::InventoryStore::Slot_CarriedLeft }, + { ESM::Armor::LBracer, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Armor::RBracer, MWWorld::InventoryStore::Slot_RightGauntlet } + }; + + for (int i=0; ibase->data.type) + { + slots.push_back (int (sMapping[i][1])); + break; + } + + return std::make_pair (slots, false); + } + + int Armor::getEquipmentSkill (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::string typeGmst; + + switch (ref->base->data.type) + { + case ESM::Armor::Helmet: typeGmst = "iHelmWeight"; break; + case ESM::Armor::Cuirass: typeGmst = "iCuirassWeight"; break; + case ESM::Armor::LPauldron: + case ESM::Armor::RPauldron: typeGmst = "iPauldronWeight"; break; + case ESM::Armor::Greaves: typeGmst = "iGreavesWeight"; break; + case ESM::Armor::Boots: typeGmst = "iBootsWeight"; break; + case ESM::Armor::LGauntlet: + case ESM::Armor::RGauntlet: typeGmst = "iGauntletWeight"; break; +/// \todo how to determine if shield light, medium or heavy? +// case ESM::Armor::Shield: + case ESM::Armor::LBracer: + case ESM::Armor::RBracer: typeGmst = "iGauntletWeight"; break; + } + + if (typeGmst.empty()) + return -1; + + float iWeight = environment.mWorld->getStore().gameSettings.find (typeGmst)->i; + + if (iWeight * environment.mWorld->getStore().gameSettings.find ("fLightMaxMod")->f>= + ref->base->data.weight) + return ESM::Skill::LightArmor; + + if (iWeight * environment.mWorld->getStore().gameSettings.find ("fMedMaxMod")->f>= + ref->base->data.weight) + return ESM::Skill::MediumArmor; + + return ESM::Skill::HeavyArmor; + } + void Armor::registerSelf() { boost::shared_ptr instance (new Armor); registerClass (typeid (ESM::Armor).name(), instance); } + + std::string Armor::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + int es = getEquipmentSkill(ptr, environment); + if (es == ESM::Skill::LightArmor) + return std::string("Item Armor Light Up"); + else if (es == ESM::Skill::MediumArmor) + return std::string("Item Armor Medium Up"); + else + return std::string("Item Armor Heavy Up"); + } + + std::string Armor::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + int es = getEquipmentSkill(ptr, environment); + if (es == ESM::Skill::LightArmor) + return std::string("Item Armor Light Down"); + else if (es == ESM::Skill::MediumArmor) + return std::string("Item Armor Medium Down"); + else + return std::string("Item Armor Heavy Down"); + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index c5f9812b7..2b66ff828 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_ARMOR_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -29,14 +28,25 @@ namespace MWClass virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 011fd2c32..0a81ebafb 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -7,9 +7,11 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwrender/objects.hpp" -#include "containerutil.hpp" +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -20,7 +22,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -56,16 +58,12 @@ namespace MWClass { // TODO implement reading + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Book::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.books); - } - std::string Book::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -80,4 +78,14 @@ namespace MWClass registerClass (typeid (ESM::Book).name(), instance); } + + std::string Book::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Book Up"); + } + + std::string Book::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Book Down"); + } } diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index f0e38cceb..ccbbfb4b2 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_BOOK_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,16 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 0214c72ad..4fe19ada4 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwrender/objects.hpp" -#include "containerutil.hpp" +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -20,7 +23,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -54,16 +57,12 @@ namespace MWClass boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Clothing::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.clothes); - } - std::string Clothing::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -72,10 +71,86 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + if (ref->base->data.type==ESM::Clothing::Ring) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_LeftRing)); + slots.push_back (int (MWWorld::InventoryStore::Slot_RightRing)); + } + else + { + const int size = 9; + + static const int sMapping[size][2] = + { + { ESM::Clothing::Shirt, MWWorld::InventoryStore::Slot_Cuirass }, + { ESM::Clothing::Belt, MWWorld::InventoryStore::Slot_Belt }, + { ESM::Clothing::Robe, MWWorld::InventoryStore::Slot_Robe }, + { ESM::Clothing::Pants, MWWorld::InventoryStore::Slot_Pants }, + { ESM::Clothing::Shoes, MWWorld::InventoryStore::Slot_Boots }, + { ESM::Clothing::LGlove, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Clothing::RGlove, MWWorld::InventoryStore::Slot_RightGauntlet }, + { ESM::Clothing::Skirt, MWWorld::InventoryStore::Slot_Skirt }, + { ESM::Clothing::Amulet, MWWorld::InventoryStore::Slot_Amulet } + }; + + for (int i=0; ibase->data.type) + { + slots.push_back (int (sMapping[i][1])); + break; + } + } + + return std::make_pair (slots, false); + } + + int Clothing::getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->data.type==ESM::Clothing::Shoes) + return ESM::Skill::Unarmored; + + return -1; + } + void Clothing::registerSelf() { boost::shared_ptr instance (new Clothing); registerClass (typeid (ESM::Clothing).name(), instance); } + + std::string Clothing::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->data.type == 8) + { + return std::string("Item Ring Up"); + } + return std::string("Item Clothes Up"); + } + + std::string Clothing::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->data.type == 8) + { + return std::string("Item Ring Down"); + } + return std::string("Item Clothes Down"); + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 76c2c4a3e..171b06246 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_CLOTHING_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,25 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 4157ce17a..c58a25c03 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -6,9 +6,45 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwworld/nullaction.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/customdata.hpp" +#include "../mwworld/environment.hpp" + +#include "../mwrender/objects.hpp" + +#include "../mwsound/soundmanager.hpp" + +namespace +{ + struct CustomData : public MWWorld::CustomData + { + MWWorld::ContainerStore mContainerStore; + + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } +} namespace MWClass { + void Container::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + // \todo add initial container content + + // store + ptr.getRefData().setCustomData (data.release()); + } + } + void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { ESMS::LiveCellRef *ref = @@ -16,7 +52,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -39,6 +75,38 @@ namespace MWClass } + boost::shared_ptr Container::activate (const MWWorld::Ptr& ptr, + const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const + { + const std::string lockedSound = "LockedChest"; + const std::string trapActivationSound = "Disarm Trap Fail"; + + if (ptr.getCellRef().lockLevel>0) + { + // TODO check for key + std::cout << "Locked container" << std::endl; + environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false); + return boost::shared_ptr (new MWWorld::NullAction); + } + else + { + std::cout << "Unlocked container" << std::endl; + if(ptr.getCellRef().trap.empty()) + { + // Not trapped, Inventory GUI goes here + return boost::shared_ptr (new MWWorld::NullAction); + } + else + { + // Trap activation goes here + std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; + environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0, false); + ptr.getCellRef().trap = ""; + return boost::shared_ptr (new MWWorld::NullAction); + } + } + } + std::string Container::getName (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -47,20 +115,12 @@ namespace MWClass return ref->base->name; } - MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) + MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getContainerStore().get()) - { - boost::shared_ptr > store ( - new MWWorld::ContainerStore); + ensureCustomData (ptr); - // TODO add initial content - - ptr.getRefData().getContainerStore() = store; - } - - return *ptr.getRefData().getContainerStore(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; } std::string Container::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 01763870a..387714176 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -2,12 +2,13 @@ #define GAME_MWCLASS_CONTAINER_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { class Container : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -19,8 +20,11 @@ namespace MWClass ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. - virtual MWWorld::ContainerStore& getContainerStore ( - const MWWorld::Ptr& ptr) const; + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, + const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; + ///< Generate action for activation + + virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store virtual std::string getScript (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/containerutil.hpp b/apps/openmw/mwclass/containerutil.hpp deleted file mode 100644 index 76bdf0236..000000000 --- a/apps/openmw/mwclass/containerutil.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef GAME_MWCLASS_CONTAINERUTIL_H -#define GAME_MWCLASS_CONTAINERUTIL_H - -#include - -#include "../mwworld/ptr.hpp" -#include "../mwworld/containerstore.hpp" - -namespace MWClass -{ - template - void insertIntoContainerStore (const MWWorld::Ptr& ptr, - ESMS::CellRefList& containerStore) - { - if (!ptr.isEmpty()) - { - // TODO check stacking - - ESMS::LiveCellRef cellRef(ptr.getCellRef(), ptr.get()->base); - cellRef.mData = ptr.getRefData(); - - containerStore.list.push_back (cellRef); - - } - } -} - -#endif diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 852701cce..7270fd22b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -4,16 +4,62 @@ #include #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/environment.hpp" +#include "../mwworld/customdata.hpp" +#include "../mwworld/containerstore.hpp" +namespace +{ + struct CustomData : public MWWorld::CustomData + { + MWMechanics::CreatureStats mCreatureStats; + MWWorld::ContainerStore mContainerStore; -#include "../mwmechanics/mechanicsmanager.hpp" + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } +} namespace MWClass { + void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + ESMS::LiveCellRef *ref = ptr.get(); + + // creature stats + data->mCreatureStats.mAttributes[0].set (ref->base->data.strength); + data->mCreatureStats.mAttributes[1].set (ref->base->data.intelligence); + data->mCreatureStats.mAttributes[2].set (ref->base->data.willpower); + data->mCreatureStats.mAttributes[3].set (ref->base->data.agility); + data->mCreatureStats.mAttributes[4].set (ref->base->data.speed); + data->mCreatureStats.mAttributes[5].set (ref->base->data.endurance); + data->mCreatureStats.mAttributes[6].set (ref->base->data.personality); + data->mCreatureStats.mAttributes[7].set (ref->base->data.luck); + data->mCreatureStats.mDynamic[0].set (ref->base->data.health); + data->mCreatureStats.mDynamic[1].set (ref->base->data.mana); + data->mCreatureStats.mDynamic[2].set (ref->base->data.fatigue); + + data->mCreatureStats.mLevel = ref->base->data.level; + + // \todo add initial container content + + // store + ptr.getRefData().setCustomData (data.release()); + } + } + std::string Creature::getId (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -24,24 +70,12 @@ namespace MWClass void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - /* - ESMS::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { - MWRender::Creatures& creatures = renderingInterface.getCreatures(); - //creatures.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - //creatures.insertMesh(ptr, "meshes\\" + model); - }*/ + MWRender::Actors& actors = renderingInterface.getActors(); + actors.insertCreature(ptr); } void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const { - /* ESMS::LiveCellRef *ref = ptr.get(); @@ -50,8 +84,7 @@ namespace MWClass assert (ref->base != NULL); if(!model.empty()){ physics.insertActorPhysics(ptr, "meshes\\" + model); - }*/ - + } } void Creature::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const @@ -74,31 +107,9 @@ namespace MWClass MWMechanics::CreatureStats& Creature::getCreatureStats (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getCreatureStats().get()) - { - boost::shared_ptr stats ( - new MWMechanics::CreatureStats); + ensureCustomData (ptr); - ESMS::LiveCellRef *ref = ptr.get(); - - stats->mAttributes[0].set (ref->base->data.strength); - stats->mAttributes[1].set (ref->base->data.intelligence); - stats->mAttributes[2].set (ref->base->data.willpower); - stats->mAttributes[3].set (ref->base->data.agility); - stats->mAttributes[4].set (ref->base->data.speed); - stats->mAttributes[5].set (ref->base->data.endurance); - stats->mAttributes[6].set (ref->base->data.personality); - stats->mAttributes[7].set (ref->base->data.luck); - stats->mDynamic[0].set (ref->base->data.health); - stats->mDynamic[1].set (ref->base->data.mana); - stats->mDynamic[2].set (ref->base->data.fatigue); - - stats->mLevel = ref->base->data.level; - - ptr.getRefData().getCreatureStats() = stats; - } - - return *ptr.getRefData().getCreatureStats(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; } boost::shared_ptr Creature::activate (const MWWorld::Ptr& ptr, @@ -107,20 +118,12 @@ namespace MWClass return boost::shared_ptr (new MWWorld::ActionTalk (ptr)); } - MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) + MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getContainerStore().get()) - { - boost::shared_ptr > store ( - new MWWorld::ContainerStore); + ensureCustomData (ptr); - // TODO add initial content - - ptr.getRefData().getContainerStore() = store; - } - - return *ptr.getRefData().getContainerStore(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; } std::string Creature::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index f74cdf991..8eb45e838 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -2,12 +2,16 @@ #define GAME_MWCLASS_CREATURE_H #include "../mwworld/class.hpp" -#include "../mwrender/creatures.hpp" +#include "../mwrender/renderinginterface.hpp" +#include "../mwrender/actors.hpp" + namespace MWClass { class Creature : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -35,7 +39,7 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual MWWorld::ContainerStore& getContainerStore ( + virtual MWWorld::ContainerStore& getContainerStore ( const MWWorld::Ptr& ptr) const; ///< Return container store diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 26436a012..5654dff69 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -14,7 +14,7 @@ #include "../mwrender/objects.hpp" -#include +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -25,7 +25,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -39,13 +39,11 @@ namespace MWClass ESMS::LiveCellRef *ref = ptr.get(); - const std::string &model = ref->base->model; assert (ref->base != NULL); if(!model.empty()){ physics.insertObjectPhysics(ptr, "meshes\\" + model); } - } std::string Door::getName (const MWWorld::Ptr& ptr) const @@ -65,15 +63,28 @@ namespace MWClass ESMS::LiveCellRef *ref = ptr.get(); + const std::string &openSound = ref->base->openSound; + //const std::string &closeSound = ref->base->closeSound; + const std::string lockedSound = "LockedDoor"; + const std::string trapActivationSound = "Disarm Trap Fail"; + if (ptr.getCellRef().lockLevel>0) { // TODO check for key // TODO report failure to player (message, sound?). Look up behaviour of original MW. std::cout << "Locked!" << std::endl; + environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false); return boost::shared_ptr (new MWWorld::NullAction); } - // TODO check trap + if(!ptr.getCellRef().trap.empty()) + { + // Trap activation + std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; + environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0, false); + ptr.getCellRef().trap = ""; + return boost::shared_ptr (new MWWorld::NullAction); + } if (ref->ref.teleport) { @@ -81,12 +92,14 @@ namespace MWClass if (environment.mWorld->getPlayer().getPlayer()==actor) { // the player is using the door + // The reason this is not 3D is that it would get interrupted when you teleport + environment.mSoundManager->playSound(openSound, 1.0, 1.0); return boost::shared_ptr ( new MWWorld::ActionTeleportPlayer (ref->ref.destCell, ref->ref.doorDest)); } else { - // another NPC or a create is using the door + // another NPC or a creature is using the door // TODO return action for teleporting other NPC/creature return boost::shared_ptr (new MWWorld::NullAction); } @@ -95,6 +108,9 @@ namespace MWClass { // animated door // TODO return action for rotating the door + + // This is a little pointless, but helps with testing + environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0, false); return boost::shared_ptr (new MWWorld::NullAction); } } diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index c230cf357..aecb4224c 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_DOOR_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 5e55010eb..1a7edf632 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -7,9 +7,11 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwrender/objects.hpp" -#include "containerutil.hpp" +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -20,7 +22,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -34,14 +36,11 @@ namespace MWClass ESMS::LiveCellRef *ref = ptr.get(); - - const std::string &model = ref->base->model; assert (ref->base != NULL); if(!model.empty()){ physics.insertObjectPhysics(ptr, "meshes\\" + model); } - } std::string Ingredient::getName (const MWWorld::Ptr& ptr) const @@ -55,16 +54,12 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Ingredient::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.ingreds); - } - std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -79,4 +74,14 @@ namespace MWClass registerClass (typeid (ESM::Ingredient).name(), instance); } + + std::string Ingredient::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Ingredient Up"); + } + + std::string Ingredient::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Ingredient Down"); + } } diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 47bd1a9e5..9463dcf8d 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_INGREDIENT_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,16 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 3890899b0..e2e63a89b 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -9,10 +9,11 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwsound/soundmanager.hpp" -#include "containerutil.hpp" +#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,19 +24,19 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + + MWRender::Objects& objects = renderingInterface.getObjects(); + objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + if (!model.empty()) - { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); objects.insertMesh(ptr, "meshes\\" + model); - const int color = ref->base->data.color; - 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->base->data.radius); - objects.insertLight (ptr, r, g, b, radius); - } + + const int color = ref->base->data.color; + 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->base->data.radius); + objects.insertLight (ptr, r, g, b, radius); } void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const @@ -43,13 +44,12 @@ namespace MWClass ESMS::LiveCellRef *ref = ptr.get(); - - const std::string &model = ref->base->model; assert (ref->base != NULL); + const std::string &model = ref->base->model; + if(!model.empty()){ physics.insertObjectPhysics(ptr, "meshes\\" + model); } - } void Light::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const @@ -83,16 +83,12 @@ namespace MWClass if (!(ref->base->data.flags & ESM::Light::Carry)) return boost::shared_ptr (new MWWorld::NullAction); + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Light::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.lights); - } - std::string Light::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -101,10 +97,33 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + if (ref->base->data.flags & ESM::Light::Carry) + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedLeft)); + + return std::make_pair (slots, false); + } + void Light::registerSelf() { boost::shared_ptr instance (new Light); registerClass (typeid (ESM::Light).name(), instance); } + + std::string Light::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Misc Up"); + } + + std::string Light::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Misc Down"); + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 34421ff51..46a4d60ba 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_LIGHT_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -28,14 +27,20 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 636a8f0be..3dda2f4af 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -7,8 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" -#include "containerutil.hpp" +#include "../mwrender/objects.hpp" + +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -19,7 +23,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -54,16 +58,12 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Lockpick::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.lockpicks); - } - std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -72,10 +72,29 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Lockpick::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + std::vector slots; + + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, false); + } + void Lockpick::registerSelf() { boost::shared_ptr instance (new Lockpick); registerClass (typeid (ESM::Tool).name(), instance); } + + std::string Lockpick::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Lockpick Up"); + } + + std::string Lockpick::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Lockpick Down"); + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index c5f1539b4..0c9189c54 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_LOCKPICK_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,20 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index a2642d8d5..864fc1e38 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -7,8 +7,11 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" -#include "containerutil.hpp" +#include "../mwrender/objects.hpp" + +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -19,7 +22,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -53,16 +56,12 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Miscellaneous::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.miscItems); - } - std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -77,4 +76,28 @@ namespace MWClass registerClass (typeid (ESM::Miscellaneous).name(), instance); } + + std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->name =="Gold") + { + return std::string("Item Gold Up"); + } + return std::string("Item Misc Up"); + } + + std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->name =="Gold") + { + return std::string("Item Gold Down"); + } + return std::string("Item Misc Down"); + } } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 36ee2c1b2..b07964f99 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_MISC_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,16 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index cce23407f..83a94d27d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1,27 +1,88 @@ #include "npc.hpp" +#include + +#include + #include #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/movement.hpp" +#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" - -#include "../mwmechanics/mechanicsmanager.hpp" -#include +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/customdata.hpp" namespace { const Ogre::Radian kOgrePi (Ogre::Math::PI); const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0)); + + struct CustomData : public MWWorld::CustomData + { + MWMechanics::NpcStats mNpcStats; + MWMechanics::CreatureStats mCreatureStats; + MWMechanics::Movement mMovement; + MWWorld::InventoryStore mInventoryStore; + + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } } namespace MWClass { + void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + ESMS::LiveCellRef *ref = ptr.get(); + + // NPC stats + if (!ref->base->faction.empty()) + { + // TODO research how initial rank is stored. The information in loadnpc.hpp are at + // best very unclear. + data->mNpcStats.mFactionRank[ref->base->faction] = 0; + } + + for (int i=0; i<27; ++i) + data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]); + + // creature stats + data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength); + data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence); + data->mCreatureStats.mAttributes[2].set (ref->base->npdt52.willpower); + data->mCreatureStats.mAttributes[3].set (ref->base->npdt52.agility); + data->mCreatureStats.mAttributes[4].set (ref->base->npdt52.speed); + data->mCreatureStats.mAttributes[5].set (ref->base->npdt52.endurance); + data->mCreatureStats.mAttributes[6].set (ref->base->npdt52.personality); + data->mCreatureStats.mAttributes[7].set (ref->base->npdt52.luck); + data->mCreatureStats.mDynamic[0].set (ref->base->npdt52.health); + data->mCreatureStats.mDynamic[1].set (ref->base->npdt52.mana); + data->mCreatureStats.mDynamic[2].set (ref->base->npdt52.fatigue); + + data->mCreatureStats.mLevel = ref->base->npdt52.level; + + // \todo add initial container content + + // store + ptr.getRefData().setCustomData (data.release()); + } + } + std::string Npc::getId (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -32,34 +93,28 @@ namespace MWClass void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - /* - ESMS::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { - MWRender::Npcs& npcs = renderingInterface.getNPCs(); - //npcs.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - //npcs.insertMesh(ptr, "meshes\\" + model); - }*/ + renderingInterface.getActors().insertNPC(ptr); } void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const { - /* + ESMS::LiveCellRef *ref = ptr.get(); - const std::string &model = ref->base->model; assert (ref->base != NULL); - if(!model.empty()){ - physics.insertActorPhysics(ptr, "meshes\\" + model); - }*/ + std::string headID = ref->base->head; + std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); + bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; + + + std::string smodel = "meshes\\base_anim.nif"; + if(beast) + smodel = "meshes\\base_animkna.nif"; + physics.insertActorPhysics(ptr, smodel); + } @@ -83,56 +138,16 @@ namespace MWClass MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getCreatureStats().get()) - { - boost::shared_ptr stats ( - new MWMechanics::CreatureStats); + ensureCustomData (ptr); - ESMS::LiveCellRef *ref = ptr.get(); - - stats->mAttributes[0].set (ref->base->npdt52.strength); - stats->mAttributes[1].set (ref->base->npdt52.intelligence); - stats->mAttributes[2].set (ref->base->npdt52.willpower); - stats->mAttributes[3].set (ref->base->npdt52.agility); - stats->mAttributes[4].set (ref->base->npdt52.speed); - stats->mAttributes[5].set (ref->base->npdt52.endurance); - stats->mAttributes[6].set (ref->base->npdt52.personality); - stats->mAttributes[7].set (ref->base->npdt52.luck); - stats->mDynamic[0].set (ref->base->npdt52.health); - stats->mDynamic[1].set (ref->base->npdt52.mana); - stats->mDynamic[2].set (ref->base->npdt52.fatigue); - - stats->mLevel = ref->base->npdt52.level; - - ptr.getRefData().getCreatureStats() = stats; - } - - return *ptr.getRefData().getCreatureStats(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; } MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getNpcStats().get()) - { - boost::shared_ptr stats ( - new MWMechanics::NpcStats); + ensureCustomData (ptr); - ESMS::LiveCellRef *ref = ptr.get(); - - if (!ref->base->faction.empty()) - { - // TODO research how initial rank is stored. The information in loadnpc.hpp are at - // best very unclear. - stats->mFactionRank[ref->base->faction] = 0; - } - - for (int i=0; i<27; ++i) - stats->mSkill[i].setBase (ref->base->npdt52.skills[i]); - - ptr.getRefData().getNpcStats() = stats; - } - - return *ptr.getRefData().getNpcStats(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; } boost::shared_ptr Npc::activate (const MWWorld::Ptr& ptr, @@ -141,20 +156,20 @@ namespace MWClass return boost::shared_ptr (new MWWorld::ActionTalk (ptr)); } - MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) + MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getContainerStore().get()) - { - boost::shared_ptr > store ( - new MWWorld::ContainerStore); + ensureCustomData (ptr); - // TODO add initial content + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; + } - ptr.getRefData().getContainerStore() = store; - } + MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr) + const + { + ensureCustomData (ptr); - return *ptr.getRefData().getContainerStore(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; } std::string Npc::getScript (const MWWorld::Ptr& ptr) const @@ -245,29 +260,20 @@ namespace MWClass MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getMovement().get()) - { - boost::shared_ptr movement ( - new MWMechanics::Movement); + ensureCustomData (ptr); - ptr.getRefData().getMovement() = movement; - } - - return *ptr.getRefData().getMovement(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; } Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const { Ogre::Vector3 vector (0, 0, 0); - if (ptr.getRefData().getMovement().get()) - { - vector.x = - ptr.getRefData().getMovement()->mLeftRight * 200; - vector.y = ptr.getRefData().getMovement()->mForwardBackward * 200; + vector.x = - getMovementSettings (ptr).mLeftRight * 200; + vector.y = getMovementSettings (ptr).mForwardBackward * 200; - if (getStance (ptr, Run, false)) - vector *= 2; - } + if (getStance (ptr, Run, false)) + vector *= 2; return vector; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 271d66392..f210eda5f 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -2,12 +2,13 @@ #define GAME_MWCLASS_NPC_H #include "../mwworld/class.hpp" -#include "../mwrender/npcs.hpp" namespace MWClass { class Npc : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -34,10 +35,12 @@ namespace MWClass virtual MWMechanics::NpcStats& getNpcStats (const MWWorld::Ptr& ptr) const; ///< Return NPC stats - virtual MWWorld::ContainerStore& getContainerStore ( - const MWWorld::Ptr& ptr) const; + virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store + virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; + ///< Return inventory store + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 86d1e2a98..4ab374590 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -7,9 +7,11 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwrender/objects.hpp" -#include "containerutil.hpp" +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -20,7 +22,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -54,16 +56,12 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Potion::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.potions); - } - std::string Potion::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -78,4 +76,14 @@ namespace MWClass registerClass (typeid (ESM::Potion).name(), instance); } + + std::string Potion::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Potion Up"); + } + + std::string Potion::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Potion Down"); + } } diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 85678121f..be9e713fb 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_POTION_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,16 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index a09a39e66..4b4d79a73 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -7,10 +7,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" -#include "containerutil.hpp" #include "../mwrender/objects.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -20,7 +23,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -54,16 +57,12 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Probe::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.probes); - } - std::string Probe::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -72,10 +71,29 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Probe::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + std::vector slots; + + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, false); + } + void Probe::registerSelf() { boost::shared_ptr instance (new Probe); registerClass (typeid (ESM::Probe).name(), instance); } + + std::string Probe::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Probe Up"); + } + + std::string Probe::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Probe Down"); + } } diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index d7b9df738..1507d65aa 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -3,7 +3,6 @@ #include "../mwworld/class.hpp" - namespace MWClass { class Probe : public MWWorld::Class @@ -23,14 +22,20 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index f8755b2eb..758bf4079 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -7,8 +7,11 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" -#include "containerutil.hpp" +#include "../mwrender/objects.hpp" + +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -19,7 +22,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -53,16 +56,12 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Repair::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.repairs); - } - std::string Repair::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -77,4 +76,14 @@ namespace MWClass registerClass (typeid (ESM::Repair).name(), instance); } + + std::string Repair::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Repair Up"); + } + + std::string Repair::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + return std::string("Item Repair Down"); + } } diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 1e0ea5178..17b606f4c 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_REPAIR_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -23,14 +22,16 @@ namespace MWClass const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 946da311d..48750dd01 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -5,6 +5,7 @@ #include "../mwworld/ptr.hpp" +#include "../mwrender/objects.hpp" namespace MWClass { @@ -15,11 +16,11 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr, ptr.getRefData().isEnabled(), true); objects.insertMesh(ptr, "meshes\\" + model); } } @@ -29,13 +30,12 @@ namespace MWClass ESMS::LiveCellRef *ref = ptr.get(); - - const std::string &model = ref->base->model; assert (ref->base != NULL); + const std::string &model = ref->base->model; + if(!model.empty()){ physics.insertObjectPhysics(ptr, "meshes\\" + model); } - } std::string Static::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index be3fdb180..a4b1d8c54 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_STATIC_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 1fbd21f7c..20db0cf38 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -7,9 +7,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwrender/objects.hpp" -#include "containerutil.hpp" +#include "../mwsound/soundmanager.hpp" namespace MWClass { @@ -20,7 +23,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -54,6 +57,8 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { + environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true); + return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } @@ -71,12 +76,6 @@ namespace MWClass return ref->base->data.health; } - void Weapon::insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const - { - insertIntoContainerStore (ptr, containerStore.weapons); - } - std::string Weapon::getScript (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -85,10 +84,157 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + bool stack = false; + + if (ref->base->data.type==ESM::Weapon::Arrow || ref->base->data.type==ESM::Weapon::Bolt) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_Ammunition)); + stack = true; + } + else if (ref->base->data.type==ESM::Weapon::MarksmanThrown) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + stack = true; + } + else + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, stack); + } + + int Weapon::getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + const int size = 12; + + static const int sMapping[size][2] = + { + { ESM::Weapon::ShortBladeOneHand, ESM::Skill::ShortBlade }, + { ESM::Weapon::LongBladeOneHand, ESM::Skill::LongBlade }, + { ESM::Weapon::LongBladeTwoHand, ESM::Skill::LongBlade }, + { ESM::Weapon::BluntOneHand, ESM::Skill::BluntWeapon }, + { ESM::Weapon::BluntTwoClose, ESM::Skill::BluntWeapon }, + { ESM::Weapon::BluntTwoWide, ESM::Skill::BluntWeapon }, + { ESM::Weapon::SpearTwoWide, ESM::Skill::Spear }, + { ESM::Weapon::AxeOneHand, ESM::Skill::Axe }, + { ESM::Weapon::AxeTwoHand, ESM::Skill::Axe }, + { ESM::Weapon::MarksmanBow, ESM::Skill::Marksman }, + { ESM::Weapon::MarksmanCrossbow, ESM::Skill::Marksman }, + { ESM::Weapon::MarksmanThrown, ESM::Skill::Marksman } + }; + + for (int i=0; ibase->data.type) + return sMapping[i][1]; + + return -1; + } + void Weapon::registerSelf() { boost::shared_ptr instance (new Weapon); registerClass (typeid (ESM::Weapon).name(), instance); } + + std::string Weapon::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + int type = ref->base->data.type; + // Ammo + if (type == 12 || type == 13) + { + return std::string("Item Ammo Up"); + } + // Bow + if (type == 9) + { + return std::string("Item Weapon Bow Up"); + } + // Crossbow + if (type == 10) + { + return std::string("Item Weapon Crossbow Up"); + } + // Longblades, One hand and Two + if (type == 1 || type == 2) + { + return std::string("Item Weapon Longblade Up"); + } + // Shortblade and thrown weapons + // thrown weapons may not be entirely correct + if (type == 0 || type == 11) + { + return std::string("Item Weapon Shortblade Up"); + } + // Spear + if (type == 6) + { + return std::string("Item Weapon Spear Up"); + } + // Blunts and Axes + if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8) + { + return std::string("Item Weapon Blunt Up"); + } + + return std::string("Item Misc Up"); + } + + std::string Weapon::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + int type = ref->base->data.type; + // Ammo + if (type == 12 || type == 13) + { + return std::string("Item Ammo Down"); + } + // Bow + if (type == 9) + { + return std::string("Item Weapon Bow Down"); + } + // Crossbow + if (type == 10) + { + return std::string("Item Weapon Crossbow Down"); + } + // Longblades, One hand and Two + if (type == 1 || type == 2) + { + return std::string("Item Weapon Longblade Down"); + } + // Shortblade and thrown weapons + // thrown weapons may not be entirely correct + if (type == 0 || type == 11) + { + return std::string("Item Weapon Shortblade Down"); + } + // Spear + if (type == 6) + { + return std::string("Item Weapon Spear Down"); + } + // Blunts and Axes + if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8) + { + return std::string("Item Weapon Blunt Down"); + } + + return std::string("Item Misc Down"); + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 79bc4d4de..f863c0bfe 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_WEAPON_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { @@ -29,14 +28,25 @@ namespace MWClass virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health - virtual void insertIntoContainer (const MWWorld::Ptr& ptr, - MWWorld::ContainerStore& containerStore) const; - ///< Insert into a containe - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEquipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); + + virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the pick up sound Id + + virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the put down sound Id }; } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 5e9dfa674..4eb6b8001 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -42,7 +42,7 @@ namespace MWDialogue iter!=dialogue->mInfo.end(); ++iter) if (iter->data.disposition==index) /// \todo cleanup info structure { - iter->id; + return iter->id; } throw std::runtime_error ("unknown journal index for topic " + topic); diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp new file mode 100644 index 000000000..1cb0593e7 --- /dev/null +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -0,0 +1,629 @@ +#include "charactercreation.hpp" + +#include "text_input.hpp" +#include "race.hpp" +#include "class.hpp" +#include "birth.hpp" +#include "review.hpp" +#include "dialogue.hpp" +#include "mode.hpp" + +namespace +{ + struct Step + { + const char* mText; + const char* mButtons[3]; + ESM::Class::Specialization mSpecializations[3]; // The specialization for each answer + }; + + static boost::array sGenerateClassSteps = { { + // Question 1 + {"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.", + {"Draw your dagger, mercifully endings its life with a single thrust.", + "Use herbs from your pack to put it to sleep.", + "Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 2 + {"One Summer afternoon your father gives you a choice of chores.", + {"Work in the forge with him casting iron for a new plow.", + "Gather herbs for your mother who is preparing dinner.", + "Go catch fish at the stream using a net and line."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 3 + {"Your cousin has given you a very embarrassing nickname and, even worse, likes to call you it in front of your friends. You asked him to stop, but he finds it very amusing to watch you blush.", + {"Beat up your cousin, then tell him that if he ever calls you that nickname again, you will bloody him worse than this time.", + "Make up a story that makes your nickname a badge of honor instead of something humiliating.", + "Make up an even more embarrassing nickname for him and use it constantly until he learns his lesson."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 4 + {"There is a lot of heated discussion at the local tavern over a grouped of people called 'Telepaths'. They have been hired by certain City-State kings. Rumor has it these Telepaths read a person's mind and tell their lord whether a follower is telling the truth or not.", + {"This is a terrible practice. A person's thoughts are his own and no one, not even a king, has the right to make such an invasion into another human's mind.", + "Loyal followers to the king have nothing to fear from a Telepath. It is important to have a method of finding assassins and spies before it is too late.", + "In these times, it is a necessary evil. Although you do not necessarily like the idea, a Telepath could have certain advantages during a time of war or in finding someone innocent of a crime."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 5 + {"Your mother sends you to the market with a list of goods to buy. After you finish you find that by mistake a shopkeeper has given you too much money back in exchange for one of the items.", + {"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?", + "Decide to put the extra money to good use and purchase items that would help your family?", + "Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?"}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 6 + {"While in the market place you witness a thief cut a purse from a noble. Even as he does so, the noble notices and calls for the city guards. In his haste to get away, the thief drops the purse near you. Surprisingly no one seems to notice the bag of coins at your feet.", + {"Pick up the bag and signal to the guard, knowing that the only honorable thing to do is return the money to its rightful owner.", + "Leave the bag there, knowing that it is better not to get involved.", + "Pick up the bag and pocket it, knowing that the extra windfall will help your family in times of trouble."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 7 + {"Your father sends you on a task which you loathe, cleaning the stables. On the way there, pitchfork in hand, you run into your friend from the homestead near your own. He offers to do it for you, in return for a future favor of his choosing.", + {"Decline his offer, knowing that your father expects you to do the work, and it is better not to be in debt.", + "Ask him to help you, knowing that two people can do the job faster than one, and agree to help him with one task of his choosing in the future.", + "Accept his offer, reasoning that as long as the stables are cleaned, it matters not who does the cleaning."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 8 + {"Your mother asks you to help fix the stove. While you are working, a very hot pipe slips its mooring and falls towards her.", + {"Position yourself between the pipe and your mother.", + "Grab the hot pipe and try to push it away.", + "Push your mother out of the way."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 9 + {"While in town the baker gives you a sweetroll. Delighted, you take it into an alley to enjoy only to be intercepted by a gang of three other kids your age. The leader demands the sweetroll, or else he and his friends will beat you and take it.", + {"Drop the sweetroll and step on it, then get ready for the fight.", + "Give him the sweetroll now without argument, knowing that later this afternoon you will have all your friends with you and can come and take whatever he owes you.", + "Act like you're going to give him the sweetroll, but at the last minute throw it in the air, hoping that they'll pay attention to it long enough for you to get a shot in on the leader."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 10 + {"Entering town you find that you are witness to a very well-dressed man running from a crowd. He screams to you for help. The crowd behind him seem very angry.", + {"Rush to the town's aid immediately, despite your lack of knowledge of the circumstances.", + "Stand aside and allow the man and the mob to pass, realizing it is probably best not to get involved.", + "Rush to the man's aid immediately, despite your lack of knowledge of the circumstances."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + } + } }; +} + +using namespace MWGui; + +CharacterCreation::CharacterCreation(WindowManager* _wm, MWWorld::Environment* _environment) + : mNameDialog(0) + , mRaceDialog(0) + , mDialogueWindow(0) + , mClassChoiceDialog(0) + , mGenerateClassQuestionDialog(0) + , mGenerateClassResultDialog(0) + , mPickClassDialog(0) + , mCreateClassDialog(0) + , mBirthSignDialog(0) + , mReviewDialog(0) + , mWM(_wm) + , mEnvironment(_environment) +{ + mCreationStage = CSE_NotStarted; +} + +void CharacterCreation::spawnDialog(const char id) +{ + switch (id) + { + case GM_Name: + if(mNameDialog) + mWM->removeDialog(mNameDialog); + 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->open(); + break; + + case GM_Race: + if (mRaceDialog) + mWM->removeDialog(mRaceDialog); + 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->open(); + break; + + case GM_Class: + if (mClassChoiceDialog) + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = new ClassChoiceDialog(*mWM); + mClassChoiceDialog->eventButtonSelected = MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); + mClassChoiceDialog->open(); + break; + + case GM_ClassPick: + if (mPickClassDialog) + mWM->removeDialog(mPickClassDialog); + mPickClassDialog = new PickClassDialog(*mWM); + mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mPickClassDialog->setClassId(mPlayerClass.name); + mPickClassDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); + mPickClassDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); + mPickClassDialog->open(); + break; + + case GM_Birth: + if (mBirthSignDialog) + mWM->removeDialog(mBirthSignDialog); + 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->open(); + break; + + case GM_ClassCreate: + if (mCreateClassDialog) + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = new CreateClassDialog(*mWM); + mCreateClassDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); + mCreateClassDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); + mCreateClassDialog->open(); + break; + case GM_ClassGenerate: + mGenerateClassStep = 0; + mGenerateClass = ""; + mGenerateClassSpecializations[0] = 0; + mGenerateClassSpecializations[1] = 0; + mGenerateClassSpecializations[2] = 0; + showClassQuestionDialog(); + break; + case GM_Review: + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + 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); + + { + std::map >::iterator end = mPlayerAttributes.end(); + for (std::map >::iterator it = mPlayerAttributes.begin(); it != end; ++it) + { + mReviewDialog->setAttribute(it->first, it->second); + } + } + + { + std::map >::iterator end = mPlayerSkillValues.end(); + for (std::map >::iterator it = mPlayerSkillValues.begin(); it != end; ++it) + { + mReviewDialog->setSkillValue(it->first, it->second); + } + mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills); + } + + mReviewDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); + mReviewDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); + mReviewDialog->eventActivateDialog = MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); + mReviewDialog->open(); + break; + } +} + +void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) +{ + mPlayerHealth = value; +} + +void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) +{ + mPlayerMagicka = value; +} + +void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) +{ + mPlayerFatigue = value; +} + +void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) +{ + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + + mWM->setGuiMode(GM_Game); +} + +void CharacterCreation::onReviewDialogBack() +{ + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + + mWM->setGuiMode(GM_Birth); +} + +void CharacterCreation::onReviewActivateDialog(int parDialog) +{ + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + mCreationStage = CSE_ReviewNext; + + switch(parDialog) + { + case ReviewDialog::NAME_DIALOG: + mWM->setGuiMode(GM_Name); + break; + case ReviewDialog::RACE_DIALOG: + mWM->setGuiMode(GM_Race); + break; + case ReviewDialog::CLASS_DIALOG: + mWM->setGuiMode(GM_Class); + break; + case ReviewDialog::BIRTHSIGN_DIALOG: + mWM->setGuiMode(GM_Birth); + }; +} + +void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) +{ + if (mPickClassDialog) + { + const std::string &classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + mEnvironment->mMechanicsManager->setPlayerClass(classId); + const ESM::Class *klass = mEnvironment->mWorld->getStore().classes.find(classId); + if (klass) + { + mPlayerClass = *klass; + mWM->setPlayerClass(mPlayerClass); + } + mWM->removeDialog(mPickClassDialog); + } + + //TODO This bit gets repeated a few times; wrap it in a function + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_ClassChosen) + mWM->setGuiMode(GM_Birth); + else + { + mCreationStage = CSE_ClassChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onPickClassDialogBack() +{ + if (mPickClassDialog) + { + const std::string classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + mEnvironment->mMechanicsManager->setPlayerClass(classId); + mWM->removeDialog(mPickClassDialog); + } + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onClassChoice(int _index) +{ + if (mClassChoiceDialog) + { + mWM->removeDialog(mClassChoiceDialog); + } + + switch(_index) + { + case ClassChoiceDialog::Class_Generate: + mWM->setGuiMode(GM_ClassGenerate); + break; + case ClassChoiceDialog::Class_Pick: + mWM->setGuiMode(GM_ClassPick); + break; + case ClassChoiceDialog::Class_Create: + mWM->setGuiMode(GM_ClassCreate); + break; + case ClassChoiceDialog::Class_Back: + mWM->setGuiMode(GM_Race); + break; + + }; +} + +void CharacterCreation::onNameDialogDone(WindowBase* parWindow) +{ + if (mNameDialog) + { + mPlayerName = mNameDialog->getTextInput(); + mWM->setValue("name", mPlayerName); + mEnvironment->mMechanicsManager->setPlayerName(mPlayerName); + mWM->removeDialog(mNameDialog); + } + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_NameChosen) + mWM->setGuiMode(GM_Race); + else + { + mCreationStage = CSE_NameChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onRaceDialogBack() +{ + if (mRaceDialog) + { + mPlayerRaceId = mRaceDialog->getRaceId(); + if (!mPlayerRaceId.empty()) + mEnvironment->mMechanicsManager->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); + mWM->removeDialog(mRaceDialog); + } + + mWM->setGuiMode(GM_Name); +} + +void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) +{ + if (mRaceDialog) + { + mPlayerRaceId = mRaceDialog->getRaceId(); + mWM->setValue("race", mPlayerRaceId); + if (!mPlayerRaceId.empty()) + mEnvironment->mMechanicsManager->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); + mWM->removeDialog(mRaceDialog); + } + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if(mCreationStage >= CSE_RaceChosen) + mWM->setGuiMode(GM_Class); + else + { + mCreationStage = CSE_RaceChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) +{ + if (mBirthSignDialog) + { + mPlayerBirthSignId = mBirthSignDialog->getBirthId(); + mWM->setBirthSign(mPlayerBirthSignId); + if (!mPlayerBirthSignId.empty()) + mEnvironment->mMechanicsManager->setPlayerBirthsign(mPlayerBirthSignId); + mWM->removeDialog(mBirthSignDialog); + } + + if (mCreationStage >= CSE_BirthSignChosen) + mWM->setGuiMode(GM_Review); + else + { + mCreationStage = CSE_BirthSignChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onBirthSignDialogBack() +{ + if (mBirthSignDialog) + { + mEnvironment->mMechanicsManager->setPlayerBirthsign(mBirthSignDialog->getBirthId()); + mWM->removeDialog(mBirthSignDialog); + } + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) +{ + if (mCreateClassDialog) + { + ESM::Class klass; + klass.name = mCreateClassDialog->getName(); + klass.description = mCreateClassDialog->getDescription(); + klass.data.specialization = mCreateClassDialog->getSpecializationId(); + klass.data.isPlayable = 0x1; + + std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); + assert(attributes.size() == 2); + klass.data.attribute[0] = attributes[0]; + klass.data.attribute[1] = attributes[1]; + + std::vector majorSkills = mCreateClassDialog->getMajorSkills(); + std::vector minorSkills = mCreateClassDialog->getMinorSkills(); + assert(majorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); + assert(minorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); + for (size_t i = 0; i < sizeof(klass.data.skills)/sizeof(klass.data.skills[0]); ++i) + { + klass.data.skills[i][1] = majorSkills[i]; + klass.data.skills[i][0] = minorSkills[i]; + } + mEnvironment->mMechanicsManager->setPlayerClass(klass); + mPlayerClass = klass; + mWM->setPlayerClass(klass); + + mWM->removeDialog(mCreateClassDialog); + } + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_ClassChosen) + mWM->setGuiMode(GM_Birth); + else + { + mCreationStage = CSE_ClassChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onCreateClassDialogBack() +{ + if (mCreateClassDialog) + mWM->removeDialog(mCreateClassDialog); + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onClassQuestionChosen(int _index) +{ + if (mGenerateClassQuestionDialog) + mWM->removeDialog(mGenerateClassQuestionDialog); + if (_index < 0 || _index >= 3) + { + mWM->setGuiMode(GM_Class); + return; + } + + ESM::Class::Specialization specialization = sGenerateClassSteps[mGenerateClassStep].mSpecializations[_index]; + if (specialization == ESM::Class::Stealth) + ++mGenerateClassSpecializations[0]; + else if (specialization == ESM::Class::Combat) + ++mGenerateClassSpecializations[1]; + else if (specialization == ESM::Class::Magic) + ++mGenerateClassSpecializations[2]; + ++mGenerateClassStep; + showClassQuestionDialog(); +} + +void CharacterCreation::showClassQuestionDialog() +{ + if (mGenerateClassStep == sGenerateClassSteps.size()) + { + 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]) + { + 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 + { + std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; + mGenerateClass = "Thief"; + } + } + + if (mGenerateClassResultDialog) + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); + mGenerateClassResultDialog->setClassId(mGenerateClass); + mGenerateClassResultDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); + mGenerateClassResultDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); + mGenerateClassResultDialog->open(); + return; + } + + if (mGenerateClassStep > sGenerateClassSteps.size()) + { + mWM->setGuiMode(GM_Class); + return; + } + + if (mGenerateClassQuestionDialog) + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); + + 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->open(); +} + +void CharacterCreation::onGenerateClassBack() +{ + if(mCreationStage < CSE_ClassChosen) + mCreationStage = CSE_ClassChosen; + + if (mGenerateClassResultDialog) + mWM->removeDialog(mGenerateClassResultDialog); + mEnvironment->mMechanicsManager->setPlayerClass(mGenerateClass); + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) +{ + if (mGenerateClassResultDialog) + mWM->removeDialog(mGenerateClassResultDialog); + mEnvironment->mMechanicsManager->setPlayerClass(mGenerateClass); + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_ClassChosen) + mWM->setGuiMode(GM_Birth); + else + { + mCreationStage = CSE_ClassChosen; + mWM->setGuiMode(GM_Game); + } +} + +CharacterCreation::~CharacterCreation() +{ + delete mNameDialog; + delete mRaceDialog; + delete mDialogueWindow; + 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 new file mode 100644 index 000000000..b01e754d9 --- /dev/null +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -0,0 +1,120 @@ +#ifndef CHARACTER_CREATION_HPP +#define CHARACTER_CREATION_HPP + +#include "window_manager.hpp" + +#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwmechanics/stat.hpp" +#include "../mwworld/world.hpp" +#include + +namespace MWGui +{ + class WindowManager; + class WindowBase; + + class TextInputDialog; + class InfoBoxDialog; + class RaceDialog; + class DialogueWindow; + class ClassChoiceDialog; + class GenerateClassResultDialog; + class PickClassDialog; + class CreateClassDialog; + class BirthDialog; + class ReviewDialog; + class MessageBoxManager; + + class CharacterCreation + { + public: + typedef std::vector SkillList; + + CharacterCreation(WindowManager* _wm, MWWorld::Environment* _environment); + ~CharacterCreation(); + + //Show a dialog + void spawnDialog(const char id); + + void setPlayerHealth (const MWMechanics::DynamicStat& value); + + void setPlayerMagicka (const MWMechanics::DynamicStat& value); + + void setPlayerFatigue (const MWMechanics::DynamicStat& value); + + private: + //Dialogs + TextInputDialog* mNameDialog; + RaceDialog* mRaceDialog; + DialogueWindow* mDialogueWindow; + ClassChoiceDialog* mClassChoiceDialog; + InfoBoxDialog* mGenerateClassQuestionDialog; + GenerateClassResultDialog* mGenerateClassResultDialog; + PickClassDialog* mPickClassDialog; + CreateClassDialog* mCreateClassDialog; + BirthDialog* mBirthSignDialog; + ReviewDialog* mReviewDialog; + + WindowManager* mWM; + MWWorld::Environment* mEnvironment; + + //Player data + std::string mPlayerName; + std::string mPlayerRaceId; + std::string mPlayerBirthSignId; + ESM::Class mPlayerClass; + std::map > mPlayerAttributes; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map > mPlayerSkillValues; + MWMechanics::DynamicStat mPlayerHealth; + MWMechanics::DynamicStat mPlayerMagicka; + MWMechanics::DynamicStat mPlayerFatigue; + + //Class generation vars + unsigned mGenerateClassStep; // Keeps track of current step in Generate Class dialog + unsigned mGenerateClassSpecializations[3]; // A counter for each specialization which is increased when an answer is chosen + std::string mGenerateClass; // In order: Stealth, Combat, Magic + + ////Dialog events + //Name dialog + void onNameDialogDone(WindowBase* parWindow); + + //Race dialog + void onRaceDialogDone(WindowBase* parWindow); + void onRaceDialogBack(); + + //Class dialogs + void onClassChoice(int _index); + void onPickClassDialogDone(WindowBase* parWindow); + void onPickClassDialogBack(); + void onCreateClassDialogDone(WindowBase* parWindow); + void onCreateClassDialogBack(); + void showClassQuestionDialog(); + void onClassQuestionChosen(int _index); + void onGenerateClassBack(); + void onGenerateClassDone(WindowBase* parWindow); + + //Birthsign dialog + void onBirthSignDialogDone(WindowBase* parWindow); + void onBirthSignDialogBack(); + + //Review dialog + void onReviewDialogDone(WindowBase* parWindow); + void onReviewDialogBack(); + void onReviewActivateDialog(int parDialog); + + enum CSE //Creation Stage Enum + { + CSE_NotStarted, + CSE_NameChosen, + CSE_RaceChosen, + CSE_ClassChosen, + CSE_BirthSignChosen, + CSE_ReviewNext + }; + + CSE mCreationStage; // Which state the character creating is in, controls back/next/ok buttons + }; +} + +#endif diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 0e2e692d3..3fd6e7892 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -50,7 +50,7 @@ namespace MWGui return isGood(); } - catch (const Compiler::SourceException& error) + catch (const Compiler::SourceException&) { // error has already been reported via error handler } @@ -342,7 +342,7 @@ namespace MWGui if( ( matches.front().find(' ') != string::npos ) ) { if( !has_front_quote ) output.append(string("\"")); - return output.append(matches.front() + string("\" ")); + return output.append(matches.front() + string("\" ")); } else if( has_front_quote ) { return output.append(matches.front() + string("\" ")); @@ -361,7 +361,7 @@ namespace MWGui /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); return output; - } + } } } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp new file mode 100644 index 000000000..5c9ef1f9b --- /dev/null +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -0,0 +1,201 @@ +#include "journalwindow.hpp" +#include "window_manager.hpp" +#include "../mwdialogue/journal.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" + +#include "../mwsound/soundmanager.hpp" + +namespace +{ + struct book + { + int endLine; + std::list pages; + }; +} + +book formatText(std::string text,book mBook,int maxLine, int lineSize) +{ + //stringList.push_back(""); + + int cLineSize = 0; + int cLine = mBook.endLine +1; + std::string cString; + + if(mBook.pages.empty()) + { + cString = ""; + cLine = 0; + } + else + { + cString = mBook.pages.back() + std::string("\n"); + mBook.pages.pop_back(); + } + + //std::string::iterator wordBegin = text.begin(); + //std::string::iterator wordEnd; + + std::string cText = text; + + while(cText.length() != 0) + { + size_t firstSpace = cText.find_first_of(' '); + if(firstSpace == std::string::npos) + { + cString = cString + cText; + mBook.pages.push_back(cString); + //TODO:finnish this + break; + } + if(static_cast (firstSpace) + cLineSize <= lineSize) + { + cLineSize = firstSpace + cLineSize; + cString = cString + cText.substr(0,firstSpace +1); + } + else + { + cLineSize = firstSpace; + if(cLine +1 <= maxLine) + { + cLine = cLine + 1; + cString = cString + std::string("\n") + cText.substr(0,firstSpace +1); + } + else + { + cLine = 0; + mBook.pages.push_back(cString); + cString = cText.substr(0,firstSpace +1); + } + } + //std::cout << cText << "\n"; + //std::cout << cText.length(); + cText = cText.substr(firstSpace +1,cText.length() - firstSpace -1); + } + mBook.endLine = cLine; + return mBook; + //std::string +} + + +MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) + : WindowBase("openmw_journal_layout.xml", parWindowManager) + , lastPos(0) +{ + //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()); + + MyGUI::WindowPtr t = static_cast(mMainWidget); + t->eventWindowChangeCoord = MyGUI::newDelegate(this, &JournalWindow::onWindowResize); +} + +void MWGui::JournalWindow::open() +{ + mPageNumber = 0; + std::string journalOpenSound = "book open"; + mWindowManager.getEnvironment().mSoundManager->playSound (journalOpenSound, 1.0, 1.0); + if(mWindowManager.getEnvironment().mJournal->begin()!=mWindowManager.getEnvironment().mJournal->end()) + { + book journal; + journal.endLine = 0; + + for(std::deque::const_iterator it = mWindowManager.getEnvironment().mJournal->begin();it!=mWindowManager.getEnvironment().mJournal->end();it++) + { + std::string a = it->getText(mWindowManager.getEnvironment().mWorld->getStore()); + journal = formatText(a,journal,10,17); + journal.endLine = journal.endLine +1; + journal.pages.back() = journal.pages.back() + std::string("\n"); + } + //std::string a = mWindowManager.getEnvironment().mJournal->begin()->getText(mWindowManager.getEnvironment().mWorld->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++) + { + if(left) + { + leftPages.push_back(*it); + } + else + { + rightPages.push_back(*it); + } + left = !left; + } + if(!left) rightPages.push_back(""); + + mPageNumber = leftPages.size()-1; + displayLeftText(leftPages[mPageNumber]); + displayRightText(rightPages[mPageNumber]); + + } + else + { + //std::cout << mWindowManager.getEnvironment().mJournal->begin()->getText(mWindowManager.getEnvironment().mWorld->getStore()); + } +} + +void MWGui::JournalWindow::onWindowResize(MyGUI::Window* window) +{ +} + +void MWGui::JournalWindow::displayLeftText(std::string text) +{ + mLeftTextWidget->eraseText(0,mLeftTextWidget->getTextLength()); + mLeftTextWidget->addText(text); +} + +void MWGui::JournalWindow::displayRightText(std::string text) +{ + mRightTextWidget->eraseText(0,mRightTextWidget->getTextLength()); + mRightTextWidget->addText(text); +} + + +void MWGui::JournalWindow::notifyNextPage(MyGUI::WidgetPtr _sender) +{ + if(mPageNumber < int(leftPages.size())-1) + { + std::string nextSound = "book page2"; + mWindowManager.getEnvironment().mSoundManager->playSound (nextSound, 1.0, 1.0); + mPageNumber = mPageNumber + 1; + displayLeftText(leftPages[mPageNumber]); + displayRightText(rightPages[mPageNumber]); + } +} + +void MWGui::JournalWindow::notifyPrevPage(MyGUI::WidgetPtr _sender) +{ + if(mPageNumber > 0) + { + std::string prevSound = "book page"; + mWindowManager.getEnvironment().mSoundManager->playSound (prevSound, 1.0, 1.0); + mPageNumber = mPageNumber - 1; + displayLeftText(leftPages[mPageNumber]); + displayRightText(rightPages[mPageNumber]); + } +} diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp new file mode 100644 index 000000000..e66448763 --- /dev/null +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -0,0 +1,57 @@ +#ifndef MWGUI_JOURNAL_H +#define MWGUI_JOURNAL_H + +#include +#include +#include +#include + +#include "window_base.hpp" + +namespace MWGui +{ + class WindowManager; + + class JournalWindow : public WindowBase + { + public: + JournalWindow(WindowManager& parWindowManager); + void open(); + + private: + enum ColorStyle + { + CS_Sub, + CS_Normal, + CS_Super + }; + + void onWindowResize(MyGUI::Window* window); + + void displayLeftText(std::string text); + void displayRightText(std::string text); + + + /** + *Called when next/prev button is used. + */ + void notifyNextPage(MyGUI::WidgetPtr _sender); + void notifyPrevPage(MyGUI::WidgetPtr _sender); + + static const int lineHeight; + + MyGUI::WidgetPtr skillAreaWidget, skillClientWidget; + MyGUI::VScrollPtr skillScrollerWidget; + int lastPos, clientHeight; + MyGUI::EditPtr mLeftTextWidget; + MyGUI::EditPtr mRightTextWidget; + MyGUI::ButtonPtr mPrevBtn; + MyGUI::ButtonPtr mNextBtn; + std::vector leftPages; + std::vector rightPages; + int mPageNumber; //store the number of the current left page + }; + +} + +#endif \ No newline at end of file diff --git a/apps/openmw/mwgui/layouts.cpp b/apps/openmw/mwgui/layouts.cpp index 10740e224..ebabc6faf 100644 --- a/apps/openmw/mwgui/layouts.cpp +++ b/apps/openmw/mwgui/layouts.cpp @@ -13,7 +13,7 @@ using namespace MWGui; -HUD::HUD(int width, int height, bool fpsSwitch) +HUD::HUD(int width, int height, int fpsLevel) : Layout("openmw_hud_layout.xml") { setCoord(0,0, width, height); @@ -37,10 +37,19 @@ HUD::HUD(int width, int height, bool fpsSwitch) getWidget(crosshair, "Crosshair"); - getWidget(fpsbox, "FPSBox"); - getWidget(fpscounter, "FPSCounter"); - - fpsbox->setVisible(fpsSwitch); + if ( fpsLevel == 2 ){ + getWidget(fpsbox, "FPSBoxAdv"); + fpsbox->setVisible(true); + getWidget(fpscounter, "FPSCounterAdv"); + }else if ( fpsLevel == 1 ){ + getWidget(fpsbox, "FPSBox"); + fpsbox->setVisible(true); + getWidget(fpscounter, "FPSCounter"); + }else{ + getWidget(fpscounter, "FPSCounter"); + } + getWidget(trianglecounter, "TriangleCounter"); + getWidget(batchcounter, "BatchCounter"); compass->setImageTexture("textures\\compass.dds"); crosshair->setImageTexture("textures\\target.dds"); @@ -59,6 +68,15 @@ void HUD::setFPS(float fps) fpscounter->setCaption(boost::lexical_cast((int)fps)); } +void HUD::setTriangleCount(size_t count) +{ + trianglecounter->setCaption(boost::lexical_cast(count)); +} + +void HUD::setBatchCount(size_t count) +{ + batchcounter->setCaption(boost::lexical_cast(count)); +} void HUD::setStats(int h, int hmax, int m, int mmax, int s, int smax) { diff --git a/apps/openmw/mwgui/layouts.hpp b/apps/openmw/mwgui/layouts.hpp index ab91f4217..9917dcdcc 100644 --- a/apps/openmw/mwgui/layouts.hpp +++ b/apps/openmw/mwgui/layouts.hpp @@ -32,7 +32,7 @@ namespace MWGui class HUD : public OEngine::GUI::Layout { public: - HUD(int width, int height, bool fpsSwitch); + HUD(int width, int height, int fpsLevel); void setStats(int h, int hmax, int m, int mmax, int s, int smax); void setWeapIcon(const char *str); void setSpellIcon(const char *str); @@ -41,6 +41,8 @@ namespace MWGui void setEffect(const char *img); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setFPS(float fps); + void setTriangleCount(size_t count); + void setBatchCount(size_t count); MyGUI::ProgressPtr health, magicka, stamina; MyGUI::StaticImagePtr weapImage, spellImage; @@ -53,6 +55,8 @@ namespace MWGui MyGUI::WidgetPtr fpsbox; MyGUI::StaticTextPtr fpscounter; + MyGUI::StaticTextPtr trianglecounter; + MyGUI::StaticTextPtr batchcounter; }; class MapWindow : public OEngine::GUI::Layout diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index b0dc14029..55f0952ce 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -10,6 +10,7 @@ namespace MWGui GM_MainMenu, // Main menu mode GM_Console, // Console mode + GM_Journal, // Journal mode // None of the following are implemented yet diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 095d347e7..aca9fbd9a 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -1,9 +1,6 @@ #include "window_manager.hpp" #include "layouts.hpp" #include "text_input.hpp" -#include "race.hpp" -#include "class.hpp" -#include "birth.hpp" #include "review.hpp" #include "dialogue.hpp" #include "dialogue_history.hpp" @@ -14,6 +11,8 @@ #include "../mwinput/inputmanager.hpp" #include "console.hpp" +#include "journalwindow.hpp" +#include "charactercreation.hpp" #include #include @@ -21,29 +20,21 @@ using namespace MWGui; -WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment, - const Compiler::Extensions& extensions, bool fpsSwitch, bool newGame) +WindowManager::WindowManager(MWWorld::Environment& environment, + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string logpath) : environment(environment) - , nameDialog(nullptr) - , raceDialog(nullptr) , dialogueWindow(nullptr) - , classChoiceDialog(nullptr) - , generateClassQuestionDialog(nullptr) - , generateClassResultDialog(nullptr) - , pickClassDialog(nullptr) - , createClassDialog(nullptr) - , birthSignDialog(nullptr) - , reviewDialog(nullptr) - , gui(_gui) , mode(GM_Game) , nextMode(GM_Game) , needModeChange(false) , shown(GW_ALL) , allowed(newGame ? GW_None : GW_ALL) { - showFPSCounter = fpsSwitch; + showFPSLevel = fpsLevel; - creationStage = NotStarted; + // Set up the GUI system + mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath); + gui = mGuiManager->getGui(); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -53,19 +44,19 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment int w = gui->getViewSize().width; int h = gui->getViewSize().height; - hud = new HUD(w,h, showFPSCounter); + hud = new HUD(w,h, showFPSLevel); menu = new MainMenu(w,h); map = new MapWindow(); stats = new StatsWindow(*this); -#if 0 - inventory = new InventoryWindow (); -#endif console = new Console(w,h, environment, extensions); + mJournal = new JournalWindow(*this); mMessageBoxManager = new MessageBoxManager(this); // The HUD is always on hud->setVisible(true); + mCharGen = new CharacterCreation(this, &environment); + // Setup player stats for (int i = 0; i < ESM::Attribute::Length; ++i) { @@ -89,26 +80,17 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment WindowManager::~WindowManager() { + delete mGuiManager; delete console; delete mMessageBoxManager; delete hud; delete map; delete menu; delete stats; -#if 0 - delete inventory; -#endif - - delete nameDialog; - delete raceDialog; + delete mJournal; delete dialogueWindow; - delete classChoiceDialog; - delete generateClassQuestionDialog; - delete generateClassResultDialog; - delete pickClassDialog; - delete createClassDialog; - delete birthSignDialog; - delete reviewDialog; + + delete mCharGen; cleanupGarbage(); } @@ -135,12 +117,19 @@ void WindowManager::update() environment.mInputManager->setGuiMode(nextMode); nextMode = GM_Game; } - if (showFPSCounter) + if (showFPSLevel > 0) { hud->setFPS(mFPS); + hud->setTriangleCount(mTriangleCount); + hud->setBatchCount(mBatchCount); } } +MWWorld::Environment& WindowManager::getEnvironment() +{ + return environment; +} + void WindowManager::setNextMode(GuiMode newMode) { nextMode = newMode; @@ -158,16 +147,14 @@ void WindowManager::updateVisible() map->setVisible(false); menu->setVisible(false); stats->setVisible(false); -#if 0 - inventory->setVisible(false); -#endif console->disable(); + mJournal->setVisible(false); // Mouse is visible whenever we're not in game mode gui->setVisiblePointer(isGuiMode()); // If in game mode, don't show anything. - if(mode == GM_Game) + if(mode == GM_Game) //Use a switch/case structure { return; } @@ -185,126 +172,10 @@ void WindowManager::updateVisible() return; } - if (mode == GM_Name) + //There must be a more elegant solution + if (mode == GM_Name || mode == GM_Race || mode == GM_Class || mode == GM_ClassPick || mode == GM_ClassCreate || mode == GM_Birth || mode == GM_ClassGenerate || mode == GM_Review) { - if (nameDialog) - removeDialog(nameDialog); - nameDialog = new TextInputDialog(*this); - std::string sName = getGameSettingString("sName", "Name"); - nameDialog->setTextLabel(sName); - nameDialog->setTextInput(playerName); - nameDialog->setNextButtonShow(creationStage >= NameChosen); - nameDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onNameDialogDone); - nameDialog->open(); - return; - } - - if (mode == GM_Race) - { - if (raceDialog) - removeDialog(raceDialog); - raceDialog = new RaceDialog(*this); - raceDialog->setNextButtonShow(creationStage >= RaceChosen); - raceDialog->setRaceId(playerRaceId); - raceDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onRaceDialogDone); - raceDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onRaceDialogBack); - raceDialog->open(); - return; - } - - if (mode == GM_Class) - { - if (classChoiceDialog) - removeDialog(classChoiceDialog); - classChoiceDialog = new ClassChoiceDialog(*this); - classChoiceDialog->eventButtonSelected = MyGUI::newDelegate(this, &WindowManager::onClassChoice); - classChoiceDialog->open(); - return; - } - - if (mode == GM_ClassGenerate) - { - generateClassStep = 0; - generateClass = ""; - generateClassSpecializations[0] = 0; - generateClassSpecializations[1] = 0; - generateClassSpecializations[2] = 0; - showClassQuestionDialog(); - return; - } - - if (mode == GM_ClassPick) - { - if (pickClassDialog) - removeDialog(pickClassDialog); - pickClassDialog = new PickClassDialog(*this); - pickClassDialog->setNextButtonShow(creationStage >= ClassChosen); - pickClassDialog->setClassId(playerClass.name); - pickClassDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onPickClassDialogDone); - pickClassDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onPickClassDialogBack); - pickClassDialog->open(); - return; - } - - if (mode == GM_ClassCreate) - { - if (createClassDialog) - removeDialog(createClassDialog); - createClassDialog = new CreateClassDialog(*this); - createClassDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onCreateClassDialogDone); - createClassDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onCreateClassDialogBack); - createClassDialog->open(); - return; - } - - if (mode == GM_Birth) - { - if (birthSignDialog) - removeDialog(birthSignDialog); - birthSignDialog = new BirthDialog(*this); - birthSignDialog->setNextButtonShow(creationStage >= BirthSignChosen); - birthSignDialog->setBirthId(playerBirthSignId); - birthSignDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onBirthSignDialogDone); - birthSignDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onBirthSignDialogBack); - birthSignDialog->open(); - return; - } - - if (mode == GM_Review) - { - if (reviewDialog) - removeDialog(reviewDialog); - reviewDialog = new ReviewDialog(*this); - reviewDialog->setPlayerName(playerName); - reviewDialog->setRace(playerRaceId); - reviewDialog->setClass(playerClass); - reviewDialog->setBirthSign(playerBirthSignId); - - reviewDialog->setHealth(playerHealth); - reviewDialog->setMagicka(playerMagicka); - reviewDialog->setFatigue(playerFatigue); - - { - std::map >::iterator end = playerAttributes.end(); - for (std::map >::iterator it = playerAttributes.begin(); it != end; ++it) - { - reviewDialog->setAttribute(it->first, it->second); - } - } - - { - std::map >::iterator end = playerSkillValues.end(); - for (std::map >::iterator it = playerSkillValues.begin(); it != end; ++it) - { - reviewDialog->setSkillValue(it->first, it->second); - } - reviewDialog->configureSkills(playerMajorSkills, playerMinorSkills); - } - - reviewDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onReviewDialogDone); - reviewDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onReviewDialogBack); - reviewDialog->eventActivateDialog = MyGUI::newDelegate(this, &WindowManager::onReviewActivateDialog); - reviewDialog->open(); + mCharGen->spawnDialog(mode); return; } @@ -319,9 +190,6 @@ void WindowManager::updateVisible() // Show the windows we want map -> setVisible( (eff & GW_Map) != 0 ); stats -> setVisible( (eff & GW_Stats) != 0 ); -#if 0 - // inventory -> setVisible( eff & GW_Inventory ); -#endif return; } @@ -344,6 +212,12 @@ void WindowManager::updateVisible() return; } + if(mode == GM_Journal) + { + mJournal->setVisible(true); + mJournal->open(); + return; + } // Unsupported mode, switch back to game // Note: The call will eventually end up this method again but @@ -392,13 +266,34 @@ void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicS stats->setValue (id, value); hud->setValue (id, value); if (id == "HBar") + { playerHealth = value; + mCharGen->setPlayerHealth (value); + } else if (id == "MBar") + { playerMagicka = value; + mCharGen->setPlayerMagicka (value); + } else if (id == "FBar") + { playerFatigue = value; + mCharGen->setPlayerFatigue (value); + } } +#if 0 +MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) +{ + if(id == "HBar") + return playerHealth; + else if (id == "MBar") + return playerMagicka; + else if (id == "FBar") + return playerFatigue; +} +#endif + void WindowManager::setValue (const std::string& id, const std::string& value) { stats->setValue (id, value); @@ -487,49 +382,6 @@ const std::string &WindowManager::getGameSettingString(const std::string &id, co return default_; } -void WindowManager::onNameDialogDone(WindowBase* parWindow) -{ - if (nameDialog) - { - playerName = nameDialog->getTextInput(); - environment.mMechanicsManager->setPlayerName(playerName); - removeDialog(nameDialog); - } - - // Go to next dialog if name was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= NameChosen) - setGuiMode(GM_Race); - else - { - creationStage = NameChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onRaceDialogDone(WindowBase* parWindow) -{ - if (raceDialog) - { - playerRaceId = raceDialog->getRaceId(); - if (!playerRaceId.empty()) - environment.mMechanicsManager->setPlayerRace(playerRaceId, raceDialog->getGender() == RaceDialog::GM_Male); - removeDialog(raceDialog); - } - - // Go to next dialog if race was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if(creationStage >= RaceChosen) - setGuiMode(GM_Class); - else - { - creationStage = RaceChosen; - setGuiMode(GM_Game); - } -} - void WindowManager::onDialogueWindowBye() { if (dialogueWindow) @@ -540,434 +392,11 @@ void WindowManager::onDialogueWindowBye() setGuiMode(GM_Game); } -void WindowManager::onRaceDialogBack() -{ - if (raceDialog) - { - playerRaceId = raceDialog->getRaceId(); - if (!playerRaceId.empty()) - environment.mMechanicsManager->setPlayerRace(playerRaceId, raceDialog->getGender() == RaceDialog::GM_Male); - removeDialog(raceDialog); - } - - setGuiMode(GM_Name); -} - -void WindowManager::onClassChoice(int _index) -{ - if (classChoiceDialog) - { - removeDialog(classChoiceDialog); - } - - switch(_index) - { - case ClassChoiceDialog::Class_Generate: - setGuiMode(GM_ClassGenerate); - break; - case ClassChoiceDialog::Class_Pick: - setGuiMode(GM_ClassPick); - break; - case ClassChoiceDialog::Class_Create: - setGuiMode(GM_ClassCreate); - break; - case ClassChoiceDialog::Class_Back: - setGuiMode(GM_Race); - break; - - }; -} - void WindowManager::onFrame (float frameDuration) { mMessageBoxManager->onFrame(frameDuration); } -namespace MWGui -{ - - struct Step - { - const char* text; - const char* buttons[3]; - // The specialization for each answer - ESM::Class::Specialization specializations[3]; - }; - - static boost::array generateClassSteps = { { - // Question 1 - {"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.", - {"Draw your dagger, mercifully endings its life with a single thrust.", - "Use herbs from your pack to put it to sleep.", - "Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 2 - {"One Summer afternoon your father gives you a choice of chores.", - {"Work in the forge with him casting iron for a new plow.", - "Gather herbs for your mother who is preparing dinner.", - "Go catch fish at the stream using a net and line."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 3 - {"Your cousin has given you a very embarrassing nickname and, even worse, likes to call you it in front of your friends. You asked him to stop, but he finds it very amusing to watch you blush.", - {"Beat up your cousin, then tell him that if he ever calls you that nickname again, you will bloody him worse than this time.", - "Make up a story that makes your nickname a badge of honor instead of something humiliating.", - "Make up an even more embarrassing nickname for him and use it constantly until he learns his lesson."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 4 - {"There is a lot of heated discussion at the local tavern over a grouped of people called 'Telepaths'. They have been hired by certain City-State kings. Rumor has it these Telepaths read a person's mind and tell their lord whether a follower is telling the truth or not.", - {"This is a terrible practice. A person's thoughts are his own and no one, not even a king, has the right to make such an invasion into another human's mind.", - "Loyal followers to the king have nothing to fear from a Telepath. It is important to have a method of finding assassins and spies before it is too late.", - "In these times, it is a necessary evil. Although you do not necessarily like the idea, a Telepath could have certain advantages during a time of war or in finding someone innocent of a crime."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 5 - {"Your mother sends you to the market with a list of goods to buy. After you finish you find that by mistake a shopkeeper has given you too much money back in exchange for one of the items.", - {"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?", - "Decide to put the extra money to good use and purchase items that would help your family?", - "Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?"}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 6 - {"While in the market place you witness a thief cut a purse from a noble. Even as he does so, the noble notices and calls for the city guards. In his haste to get away, the thief drops the purse near you. Surprisingly no one seems to notice the bag of coins at your feet.", - {"Pick up the bag and signal to the guard, knowing that the only honorable thing to do is return the money to its rightful owner.", - "Leave the bag there, knowing that it is better not to get involved.", - "Pick up the bag and pocket it, knowing that the extra windfall will help your family in times of trouble."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 7 - {"Your father sends you on a task which you loathe, cleaning the stables. On the way there, pitchfork in hand, you run into your friend from the homestead near your own. He offers to do it for you, in return for a future favor of his choosing.", - {"Decline his offer, knowing that your father expects you to do the work, and it is better not to be in debt.", - "Ask him to help you, knowing that two people can do the job faster than one, and agree to help him with one task of his choosing in the future.", - "Accept his offer, reasoning that as long as the stables are cleaned, it matters not who does the cleaning."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 8 - {"Your mother asks you to help fix the stove. While you are working, a very hot pipe slips its mooring and falls towards her.", - {"Position yourself between the pipe and your mother.", - "Grab the hot pipe and try to push it away.", - "Push your mother out of the way."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 9 - {"While in town the baker gives you a sweetroll. Delighted, you take it into an alley to enjoy only to be intercepted by a gang of three other kids your age. The leader demands the sweetroll, or else he and his friends will beat you and take it.", - {"Drop the sweetroll and step on it, then get ready for the fight.", - "Give him the sweetroll now without argument, knowing that later this afternoon you will have all your friends with you and can come and take whatever he owes you.", - "Act like you're going to give him the sweetroll, but at the last minute throw it in the air, hoping that they'll pay attention to it long enough for you to get a shot in on the leader."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 10 - {"Entering town you find that you are witness to a very well-dressed man running from a crowd. He screams to you for help. The crowd behind him seem very angry.", - {"Rush to the town's aid immediately, despite your lack of knowledge of the circumstances.", - "Stand aside and allow the man and the mob to pass, realizing it is probably best not to get involved.", - "Rush to the man's aid immediately, despite your lack of knowledge of the circumstances."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - } - } }; -} - -void WindowManager::showClassQuestionDialog() -{ - if (generateClassStep == generateClassSteps.size()) - { - - 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 (generateClassSpecializations[0] == classes[i].points[0] && - generateClassSpecializations[1] == classes[i].points[1] && - generateClassSpecializations[2] == classes[i].points[2]) - { - match = i; - generateClass = classes[i].id; - break; - } - } - - if (match == -1) - { - if (generateClassSpecializations[0] >= 7) - generateClass = "Thief"; - else if (generateClassSpecializations[1] >= 7) - generateClass = "Warrior"; - else if (generateClassSpecializations[2] >= 7) - generateClass = "Mage"; - else - { - std::cerr - << "Failed to deduce class from chosen answers in generate class dialog" - << std::endl; - generateClass = "Thief"; - } - } - - if (generateClassResultDialog) - removeDialog(generateClassResultDialog); - generateClassResultDialog = new GenerateClassResultDialog(*this); - generateClassResultDialog->setClassId(generateClass); - generateClassResultDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onGenerateClassBack); - generateClassResultDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onGenerateClassDone); - generateClassResultDialog->open(); - return; - } - - if (generateClassStep > generateClassSteps.size()) - { - setGuiMode(GM_Class); - return; - } - - if (generateClassQuestionDialog) - removeDialog(generateClassQuestionDialog); - generateClassQuestionDialog = new InfoBoxDialog(*this); - - InfoBoxDialog::ButtonList buttons; - generateClassQuestionDialog->setText(generateClassSteps[generateClassStep].text); - buttons.push_back(generateClassSteps[generateClassStep].buttons[0]); - buttons.push_back(generateClassSteps[generateClassStep].buttons[1]); - buttons.push_back(generateClassSteps[generateClassStep].buttons[2]); - generateClassQuestionDialog->setButtons(buttons); - generateClassQuestionDialog->eventButtonSelected = MyGUI::newDelegate(this, &WindowManager::onClassQuestionChosen); - generateClassQuestionDialog->open(); -} - -void WindowManager::onClassQuestionChosen(int _index) -{ - if (generateClassQuestionDialog) - removeDialog(generateClassQuestionDialog); - if (_index < 0 || _index >= 3) - { - setGuiMode(GM_Class); - return; - } - - ESM::Class::Specialization specialization = generateClassSteps[generateClassStep].specializations[_index]; - if (specialization == ESM::Class::Stealth) - ++generateClassSpecializations[0]; - else if (specialization == ESM::Class::Combat) - ++generateClassSpecializations[1]; - else if (specialization == ESM::Class::Magic) - ++generateClassSpecializations[2]; - ++generateClassStep; - showClassQuestionDialog(); -} - -void WindowManager::onGenerateClassBack() -{ - if(creationStage < ClassChosen) - creationStage = ClassChosen; - - if (generateClassResultDialog) - removeDialog(generateClassResultDialog); - environment.mMechanicsManager->setPlayerClass(generateClass); - - setGuiMode(GM_Class); -} - -void WindowManager::onGenerateClassDone(WindowBase* parWindow) -{ - if (generateClassResultDialog) - removeDialog(generateClassResultDialog); - environment.mMechanicsManager->setPlayerClass(generateClass); - - // Go to next dialog if class was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= ClassChosen) - setGuiMode(GM_Birth); - else - { - creationStage = ClassChosen; - setGuiMode(GM_Game); - } -} - - -void WindowManager::onPickClassDialogDone(WindowBase* parWindow) -{ - if (pickClassDialog) - { - const std::string &classId = pickClassDialog->getClassId(); - if (!classId.empty()) - environment.mMechanicsManager->setPlayerClass(classId); - const ESM::Class *klass = environment.mWorld->getStore().classes.find(classId); - if (klass) - playerClass = *klass; - removeDialog(pickClassDialog); - } - - // Go to next dialog if class was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= ClassChosen) - setGuiMode(GM_Birth); - else - { - creationStage = ClassChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onPickClassDialogBack() -{ - if (pickClassDialog) - { - const std::string classId = pickClassDialog->getClassId(); - if (!classId.empty()) - environment.mMechanicsManager->setPlayerClass(classId); - removeDialog(pickClassDialog); - } - - setGuiMode(GM_Class); -} - -void WindowManager::onCreateClassDialogDone(WindowBase* parWindow) -{ - if (createClassDialog) - { - ESM::Class klass; - klass.name = createClassDialog->getName(); - klass.description = createClassDialog->getDescription(); - klass.data.specialization = createClassDialog->getSpecializationId(); - klass.data.isPlayable = 0x1; - - std::vector attributes = createClassDialog->getFavoriteAttributes(); - assert(attributes.size() == 2); - klass.data.attribute[0] = attributes[0]; - klass.data.attribute[1] = attributes[1]; - - std::vector majorSkills = createClassDialog->getMajorSkills(); - std::vector minorSkills = createClassDialog->getMinorSkills(); - assert(majorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); - assert(minorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); - for (size_t i = 0; i < sizeof(klass.data.skills)/sizeof(klass.data.skills[0]); ++i) - { - klass.data.skills[i][1] = majorSkills[i]; - klass.data.skills[i][0] = minorSkills[i]; - } - environment.mMechanicsManager->setPlayerClass(klass); - playerClass = klass; - - removeDialog(createClassDialog); - } - - // Go to next dialog if class was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= ClassChosen) - setGuiMode(GM_Birth); - else - { - creationStage = ClassChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onCreateClassDialogBack() -{ - if (createClassDialog) - removeDialog(createClassDialog); - - setGuiMode(GM_Class); -} - -void WindowManager::onBirthSignDialogDone(WindowBase* parWindow) -{ - if (birthSignDialog) - { - playerBirthSignId = birthSignDialog->getBirthId(); - if (!playerBirthSignId.empty()) - environment.mMechanicsManager->setPlayerBirthsign(playerBirthSignId); - removeDialog(birthSignDialog); - } - - // Go to next dialog if birth sign was previously chosen - if (creationStage >= BirthSignChosen) - setGuiMode(GM_Review); - else - { - creationStage = BirthSignChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onBirthSignDialogBack() -{ - if (birthSignDialog) - { - environment.mMechanicsManager->setPlayerBirthsign(birthSignDialog->getBirthId()); - removeDialog(birthSignDialog); - } - - setGuiMode(GM_Class); -} - -void WindowManager::onReviewDialogDone(WindowBase* parWindow) -{ - if (reviewDialog) - removeDialog(reviewDialog); - - setGuiMode(GM_Game); -} - -void WindowManager::onReviewDialogBack() -{ - if (reviewDialog) - removeDialog(reviewDialog); - - setGuiMode(GM_Birth); -} - -void WindowManager::onReviewActivateDialog(int parDialog) -{ - if (reviewDialog) - removeDialog(reviewDialog); - creationStage = ReviewNext; - - switch(parDialog) - { - case ReviewDialog::NAME_DIALOG: - setGuiMode(GM_Name); - break; - case ReviewDialog::RACE_DIALOG: - setGuiMode(GM_Race); - break; - case ReviewDialog::CLASS_DIALOG: - setGuiMode(GM_Class); - break; - case ReviewDialog::BIRTHSIGN_DIALOG: - setGuiMode(GM_Birth); - }; -} - const ESMS::ESMStore& WindowManager::getStore() const { return environment.mWorld->getStore(); diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 293bac601..89ff4b9bb 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -8,13 +8,15 @@ MyGUI should be initialized separately before creating instances of this class. - */ +**/ #include #include #include #include +#include +#include #include "../mwmechanics/stat.hpp" #include "mode.hpp" @@ -32,6 +34,12 @@ namespace Compiler namespace MWWorld { class Environment; + class World; +} + +namespace MWMechanics +{ + class MechanicsManager; } namespace OEngine @@ -51,17 +59,12 @@ namespace MWGui class StatsWindow; class InventoryWindow; class Console; + class JournalWindow; + class CharacterCreation; class TextInputDialog; class InfoBoxDialog; - class RaceDialog; class DialogueWindow; - class ClassChoiceDialog; - class GenerateClassResultDialog; - class PickClassDialog; - class CreateClassDialog; - class BirthDialog; - class ReviewDialog; class MessageBoxManager; struct ClassPoint @@ -79,92 +82,11 @@ namespace MWGui typedef std::vector FactionList; typedef std::vector SkillList; - private: - MWWorld::Environment& environment; - HUD *hud; - MapWindow *map; - MainMenu *menu; - StatsWindow *stats; - MessageBoxManager *mMessageBoxManager; -#if 0 - InventoryWindow *inventory; -#endif - Console *console; - - // Character creation - TextInputDialog *nameDialog; - RaceDialog *raceDialog; - DialogueWindow *dialogueWindow; - ClassChoiceDialog *classChoiceDialog; - InfoBoxDialog *generateClassQuestionDialog; - GenerateClassResultDialog *generateClassResultDialog; - PickClassDialog *pickClassDialog; - CreateClassDialog *createClassDialog; - BirthDialog *birthSignDialog; - ReviewDialog *reviewDialog; - - // Keeps track of current step in Generate Class dialogs - unsigned generateClassStep; - // A counter for each specialization which is increased when an answer is chosen, in order: Stealth, Combat, Magic - unsigned generateClassSpecializations[3]; - std::string generateClass; - - // Various stats about player as needed by window manager - std::string playerName; - ESM::Class playerClass; - std::string playerRaceId, playerBirthSignId; - std::map > playerAttributes; - SkillList playerMajorSkills, playerMinorSkills; - std::map > playerSkillValues; - MWMechanics::DynamicStat playerHealth, playerMagicka, playerFatigue; - - // Gui - MyGUI::Gui *gui; - - // Current gui mode - GuiMode mode; - - /** - * Next mode to activate in update(). - */ - GuiMode nextMode; - /** - * Whether a mode change is needed in update(). - * Will use @a nextMode as the new mode. - */ - bool needModeChange; - - std::vector garbageDialogs; - void cleanupGarbage(); - - // Currently shown windows in inventory mode - GuiWindow shown; - - /* Currently ALLOWED windows in inventory mode. This is used at - the start of the game, when windows are enabled one by one - through script commands. You can manipulate this through using - allow() and disableAll(). - - The setting should also affect visibility of certain HUD - elements, but this is not done yet. - */ - GuiWindow allowed; - - // Update visibility of all windows based on mode, shown and - // allowed settings. - void updateVisible(); + WindowManager(MWWorld::Environment& environment, const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string logpath); + virtual ~WindowManager(); void setGuiMode(GuiMode newMode); - bool showFPSCounter; - float mFPS; - - public: - /// The constructor needs the main Gui object - WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment, - const Compiler::Extensions& extensions, bool fpsSwitch, bool newGame); - virtual ~WindowManager(); - /** * Should be called each frame to update windows/gui elements. * This could mean updating sizes of gui elements or opening @@ -172,6 +94,8 @@ namespace MWGui */ void update(); + MWWorld::Environment& getEnvironment(); + void setMode(GuiMode newMode) { if (newMode==GM_Inventory && allowed==GW_None) @@ -184,8 +108,7 @@ namespace MWGui GuiMode getMode() const { return mode; } - // Everything that is not game mode is considered "gui mode" - bool isGuiMode() const { return getMode() != GM_Game; } + bool isGuiMode() const { return getMode() != GM_Game; } // Everything that is not game mode is considered "gui mode" // Disallow all inventory mode windows void disallowAll() @@ -203,56 +126,38 @@ namespace MWGui MyGUI::Gui* getGui() const { return gui; } - void wmSetFPS(float fps) { mFPS = fps; } + void wmUpdateFps(float fps, size_t triangleCount, size_t batchCount) + { + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; + } +// MWMechanics::DynamicStat getValue(const std::string& id); + + ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); - ///< Set value for the given ID. - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); - ///< Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - ///< Set value for the given ID. - void setValue (const std::string& id, const std::string& value); - ///< set value for the given ID. - void setValue (const std::string& id, int value); - ///< set value for the given ID. - void setPlayerClass (const ESM::Class &class_); - ///< set current class of player + void setPlayerClass (const ESM::Class &class_); ///< set current class of player + void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. + void setFactions (const FactionList& factions); ///< set faction and rank to display on stat window, use an empty vector to disable + void setBirthSign (const std::string &signId); ///< set birth sign to display on stat window, use an empty string to disable. + void setReputation (int reputation); ///< set the current reputation value + void setBounty (int bounty); ///< set the current bounty value + void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty - void configureSkills (const SkillList& major, const SkillList& minor); - ///< configure skill groups, each set contains the skill ID for that group. - - void setFactions (const FactionList& factions); - ///< set faction and rank to display on stat window, use an empty vector to disable - - void setBirthSign (const std::string &signId); - ///< set birth sign to display on stat window, use an empty string to disable. - - void setReputation (int reputation); - ///< set the current reputation value - - void setBounty (int bounty); - ///< set the current bounty value - - void updateSkillArea(); - ///< update display of skills, factions, birth sign, reputation and bounty template - void removeDialog(T*& dialog); - ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. + void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. + void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - void removeDialog(OEngine::GUI::Layout* dialog); - ///< Hides dialog and schedules dialog to be deleted. - void messageBox (const std::string& message, const std::vector& buttons); - - int readPressedButton (); - ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) - + int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + void onFrame (float frameDuration); /** @@ -267,54 +172,58 @@ namespace MWGui const ESMS::ESMStore& getStore() const; private: + OEngine::GUI::MyGUIManager *mGuiManager; + MWWorld::Environment& environment; + HUD *hud; + MapWindow *map; + MainMenu *menu; + StatsWindow *stats; + MessageBoxManager *mMessageBoxManager; + Console *console; + JournalWindow* mJournal; + DialogueWindow *dialogueWindow; + + CharacterCreation* mCharGen; + + // Various stats about player as needed by window manager + ESM::Class playerClass; + std::string playerName; + std::string playerRaceId; + std::string playerBirthSignId; + std::map > playerAttributes; + SkillList playerMajorSkills, playerMinorSkills; + std::map > playerSkillValues; + MWMechanics::DynamicStat playerHealth, playerMagicka, playerFatigue; + + + MyGUI::Gui *gui; // Gui + GuiMode mode; // Current gui mode + GuiMode nextMode; // Next mode to activate in update() + bool needModeChange; //Whether a mode change is needed in update() [will use nextMode] + + std::vector garbageDialogs; + void cleanupGarbage(); + + GuiWindow shown; // Currently shown windows in inventory mode + + /* Currently ALLOWED windows in inventory mode. This is used at + the start of the game, when windows are enabled one by one + through script commands. You can manipulate this through using + allow() and disableAll(). + + The setting should also affect visibility of certain HUD + elements, but this is not done yet. + */ + GuiWindow allowed; + + void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings + + int showFPSLevel; + float mFPS; + size_t mTriangleCount; + size_t mBatchCount; void onDialogueWindowBye(); - - // Character generation: Name dialog - void onNameDialogDone(WindowBase* parWindow); - - // Character generation: Race dialog - void onRaceDialogDone(WindowBase* parWindow); - void onRaceDialogBack(); - - // Character generation: Choose class process - void onClassChoice(int _index); - - // Character generation: Generate Class - void showClassQuestionDialog(); - void onClassQuestionChosen(int _index); - void onGenerateClassBack(); - void onGenerateClassDone(WindowBase* parWindow); - - // Character generation: Pick Class dialog - void onPickClassDialogDone(WindowBase* parWindow); - void onPickClassDialogBack(); - - // Character generation: Create Class dialog - void onCreateClassDialogDone(WindowBase* parWindow); - void onCreateClassDialogBack(); - - // Character generation: Birth sign dialog - void onBirthSignDialogDone(WindowBase* parWindow); - void onBirthSignDialogBack(); - - // Character generation: Review dialog - void onReviewDialogDone(WindowBase* parWindow); - void onReviewDialogBack(); - void onReviewActivateDialog(int parDialog); - - enum CreationStageEnum - { - NotStarted, - NameChosen, - RaceChosen, - ClassChosen, - BirthSignChosen, - ReviewNext - }; - - // Which state the character creating is in, controls back/next/ok buttons - CreationStageEnum creationStage; }; template diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index eb6c59963..88534ddda 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -83,27 +83,14 @@ namespace MWInput MWGui::WindowManager &windows; OMW::Engine& mEngine; - // Count screenshots. - int shotCount; - /* InputImpl Methods */ - // Write screenshot to file. void screenshot() { - - // Find the first unused filename with a do-while - char buf[50]; - do - { - snprintf(buf, 50, "screenshot%03d.png", shotCount++); - } while (boost::filesystem::exists(buf)); - - ogre.screenshot(buf); + mEngine.screenshot(); } - /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ void toggleInventory() { @@ -134,6 +121,21 @@ namespace MWInput else setGuiMode(GM_Console); } + void toggleJournal() + { + using namespace MWGui; + + GuiMode mode = windows.getMode(); + + // Toggle between game mode and journal mode + if(mode == GM_Game) + setGuiMode(GM_Journal); + else if(mode == GM_Journal) + setGuiMode(GM_Game); + + // .. but don't touch any other mode. + } + void activate() { mEngine.activate(); @@ -168,8 +170,7 @@ namespace MWInput poller(input), player(_player), windows(_windows), - mEngine (engine), - shotCount(0) + mEngine (engine) { using namespace OEngine::Input; using namespace OEngine::Render; @@ -188,6 +189,8 @@ namespace MWInput "Toggle inventory screen"); disp->funcs.bind(A_Console, boost::bind(&InputImpl::toggleConsole, this), "Toggle console"); + disp->funcs.bind(A_Journal, boost::bind(&InputImpl::toggleJournal, this), + "Toggle journal"); disp->funcs.bind(A_Activate, boost::bind(&InputImpl::activate, this), "Activate"); disp->funcs.bind(A_AutoMove, boost::bind(&InputImpl::toggleAutoMove, this), @@ -236,6 +239,7 @@ namespace MWInput disp->bind(A_Screenshot, KC_SYSRQ); disp->bind(A_Inventory, KC_I); disp->bind(A_Console, KC_F1); + disp->bind(A_Journal, KC_J); disp->bind(A_Activate, KC_SPACE); disp->bind(A_AutoMove, KC_Z); disp->bind(A_ToggleSneak, KC_X); diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanager.cpp index 33093c24d..7ed81f785 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.cpp @@ -250,6 +250,7 @@ namespace MWMechanics while (iter!=mActors.end()) if (iter->getCell()==cellStore) { + //std::cout << "Erasing an actor"; mActors.erase (iter++); } else diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp new file mode 100644 index 000000000..d8ca78e3a --- /dev/null +++ b/apps/openmw/mwrender/actors.cpp @@ -0,0 +1,126 @@ +#include "actors.hpp" +#include + + + + +using namespace Ogre; +using namespace MWRender; +using namespace NifOgre; + +void Actors::setMwRoot(Ogre::SceneNode* root){ + mMwRoot = root; +} +void Actors::insertNPC(const MWWorld::Ptr& ptr){ + + insertBegin(ptr, true, true); + NpcAnimation* anim = new MWRender::NpcAnimation(ptr, mEnvironment, mRend); + + mAllActors[ptr] = anim; +} +void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ + Ogre::SceneNode* cellnode; + if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) + { + //Create the scenenode and put it in the map + cellnode = mMwRoot->createChildSceneNode(); + mCellSceneNodes[ptr.getCell()] = cellnode; + } + else + { + cellnode = mCellSceneNodes[ptr.getCell()]; + } + + Ogre::SceneNode* insert = cellnode->createChildSceneNode(); + const float *f = ptr.getRefData().getPosition().pos; + insert->setPosition(f[0], f[1], f[2]); + insert->setScale(ptr.getCellRef().scale, ptr.getCellRef().scale, ptr.getCellRef().scale); + + // Convert MW rotation to a quaternion: + f = ptr.getCellRef().pos.rot; + + // Rotate around X axis + Quaternion xr(Radian(-f[0]), Vector3::UNIT_X); + + // Rotate around Y axis + Quaternion yr(Radian(-f[1]), Vector3::UNIT_Y); + + // Rotate around Z axis + Quaternion zr(Radian(-f[2]), Vector3::UNIT_Z); + + // Rotates first around z, then y, then x + insert->setOrientation(xr*yr*zr); + if (!enabled) + insert->setVisible (false); + ptr.getRefData().setBaseNode(insert); + + +} +void Actors::insertCreature (const MWWorld::Ptr& ptr){ + + insertBegin(ptr, true, true); + CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr, mEnvironment, mRend); + //mAllActors.insert(std::pair(ptr,anim)); + mAllActors[ptr] = anim; + //mAllActors.push_back(&anim);*/ +} + +bool Actors::deleteObject (const MWWorld::Ptr& ptr) +{ + delete mAllActors[ptr]; + mAllActors.erase(ptr); + if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode()) + { + + Ogre::SceneNode *parent = base->getParentSceneNode(); + + for (std::map::const_iterator iter ( + mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter) + if (iter->second==parent) + { + base->removeAndDestroyAllChildren(); + mRend.getScene()->destroySceneNode (base); + ptr.getRefData().setBaseNode (0); + return true; + } + + return false; + } + + return true; +} + +void Actors::removeCell(MWWorld::Ptr::CellStore* store){ + if(mCellSceneNodes.find(store) != mCellSceneNodes.end()) + { + Ogre::SceneNode* base = mCellSceneNodes[store]; + base->removeAndDestroyAllChildren(); + mCellSceneNodes.erase(store); + mRend.getScene()->destroySceneNode(base); + base = 0; + } + for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); ) + { + if(iter->first.getCell() == store){ + delete iter->second; + mAllActors.erase(iter++); + } + else + ++iter; + } +} + +void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number){ + if(mAllActors.find(ptr) != mAllActors.end()) + mAllActors[ptr]->startScript(groupName, mode, number); +} +void Actors::skipAnimation (const MWWorld::Ptr& ptr){ + if(mAllActors.find(ptr) != mAllActors.end()) + mAllActors[ptr]->stopScript(); +} +void Actors::update (float duration){ + for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) + { + (iter->second)->runAnimation(duration); + } +} diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp new file mode 100644 index 000000000..7179c08fb --- /dev/null +++ b/apps/openmw/mwrender/actors.hpp @@ -0,0 +1,59 @@ +#ifndef _GAME_RENDER_ACTORS_H +#define _GAME_RENDER_ACTORS_H + +#include "components/esm_store/cell_store.hpp" +#include +#include + + + +#include +#include "components/nifogre/ogre_nif_loader.hpp" + +#include "../mwworld/refdata.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/actiontalk.hpp" +#include "../mwworld/environment.hpp" +#include "npcanimation.hpp" +#include "creatureanimation.hpp" +#include + +namespace MWRender{ + class Actors{ + OEngine::Render::OgreRenderer &mRend; + std::map mCellSceneNodes; + Ogre::SceneNode* mMwRoot; + MWWorld::Environment& mEnvironment; + std::map mAllActors; + + + + public: + Actors(OEngine::Render::OgreRenderer& _rend, MWWorld::Environment& _env): mRend(_rend), mEnvironment(_env){} + ~Actors(){} + void setMwRoot(Ogre::SceneNode* root); + void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); + void insertCreature (const MWWorld::Ptr& ptr); + void insertNPC(const MWWorld::Ptr& ptr); + bool deleteObject (const MWWorld::Ptr& ptr); + ///< \return found? + + void removeCell(MWWorld::Ptr::CellStore* store); + + void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, + int number = 1); + ///< Run animation for a MW-reference. Calls to this function for references that are currently not + /// in the rendered scene should be ignored. + /// + /// \param mode: 0 normal, 1 immediate start, 2 immediate loop + /// \param number How offen the animation should be run + + void skipAnimation (const MWWorld::Ptr& ptr); + ///< Skip the animation for the given MW-reference for one frame. Calls to this function for + /// references that are currently not in the rendered scene should be ignored. + + void update (float duration); + + }; +} +#endif diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp new file mode 100644 index 000000000..7b0d7015c --- /dev/null +++ b/apps/openmw/mwrender/animation.cpp @@ -0,0 +1,480 @@ +#include "animation.hpp" + + +namespace MWRender{ + std::map Animation::mUniqueIDs; + + Animation::~Animation(){ + } + + std::string Animation::getUniqueID(std::string mesh){ + int counter; + std::string copy = mesh; + std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower); + if(mUniqueIDs.find(copy) == mUniqueIDs.end()){ + counter = mUniqueIDs[copy] = 0; + } + else{ + mUniqueIDs[copy] = mUniqueIDs[copy] + 1; + counter = mUniqueIDs[copy]; + } + + std::stringstream out; + if(counter > 99 && counter < 1000) + out << "0"; + else if(counter > 9) + out << "00"; + else + out << "000"; + out << counter; + return out.str(); +} + void Animation::startScript(std::string groupname, int mode, int loops){ + //If groupname is recognized set animate to true + //Set the start time and stop time + //How many times to loop + if(groupname == "all"){ + animate = loops; + time = startTime; + } + else if(textmappings){ + + std::string startName = groupname + ": loop start"; + std::string stopName = groupname + ": loop stop"; + + bool first = false; + + if(loops > 1){ + startName = groupname + ": loop start"; + stopName = groupname + ": loop stop"; + + for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++){ + + std::string current = iter->first.substr(0, startName.size()); + std::transform(current.begin(), current.end(), current.begin(), ::tolower); + std::string current2 = iter->first.substr(0, stopName.size()); + std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); + + if(current == startName){ + startTime = iter->second; + animate = loops; + time = startTime; + first = true; + } + if(current2 == stopName){ + stopTime = iter->second; + if(first) + break; + } + } + } + if(!first){ + startName = groupname + ": start"; + stopName = groupname + ": stop"; + + for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++){ + + std::string current = iter->first.substr(0, startName.size()); + std::transform(current.begin(), current.end(), current.begin(), ::tolower); + std::string current2 = iter->first.substr(0, stopName.size()); + std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); + + if(current == startName){ + startTime = iter->second; + animate = loops; + time = startTime; + first = true; + } + if(current2 == stopName){ + stopTime = iter->second; + if(first) + break; + } + } + } + + } + + } + void Animation::stopScript(){ + animate = 0; + } + + void Animation::handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){ + shapeNumber = 0; + + std::vector::iterator allshapesiter; + for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++) + + { + //std::map vecPosRot; + + Nif::NiTriShapeCopy& copy = *allshapesiter; + std::vector* allvertices = ©.vertices; + + + + //std::set vertices; + //std::set normals; + //std::vector boneinfovector = copy.boneinfo; + std::map >* verticesToChange = ©.vertsToWeights; + + //std::cout << "Name " << copy.sname << "\n"; + Ogre::HardwareVertexBufferSharedPtr vbuf = creaturemodel->getMesh()->getSubMesh(copy.sname)->vertexData->vertexBufferBinding->getBuffer(0); + Ogre::Real* pReal = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL)); + + + std::vector initialVertices = copy.morph.getInitialVertices(); + //Each shape has multiple indices + if(initialVertices.size() ) + { + + if(copy.vertices.size() == initialVertices.size()) + { + //Create if it doesn't already exist + if(shapeIndexI.size() == static_cast (shapeNumber)) + { + std::vector vec; + shapeIndexI.push_back(vec); + } + if(time >= copy.morph.getStartTime() && time <= copy.morph.getStopTime()){ + float x; + for (unsigned int i = 0; i < copy.morph.getAdditionalVertices().size(); i++){ + int j = 0; + if(shapeIndexI[shapeNumber].size() <= i) + shapeIndexI[shapeNumber].push_back(0); + + + if(timeIndex(time,copy.morph.getRelevantTimes()[i],(shapeIndexI[shapeNumber])[i], j, x)){ + int indexI = (shapeIndexI[shapeNumber])[i]; + std::vector relevantData = (copy.morph.getRelevantData()[i]); + float v1 = relevantData[indexI].x; + float v2 = relevantData[j].x; + float t = v1 + (v2 - v1) * x; + if ( t < 0 ) t = 0; + if ( t > 1 ) t = 1; + if( t != 0 && initialVertices.size() == copy.morph.getAdditionalVertices()[i].size()) + { + for (unsigned int v = 0; v < initialVertices.size(); v++){ + initialVertices[v] += ((copy.morph.getAdditionalVertices()[i])[v]) * t; + } + } + + } + + + + } + + allvertices = &initialVertices; + } + shapeNumber++; + } + } + + + if(verticesToChange->size() > 0){ + + for(std::map >::iterator iter = verticesToChange->begin(); + iter != verticesToChange->end(); iter++) + { + std::vector inds = iter->second; + int verIndex = iter->first; + Ogre::Vector3 currentVertex = (*allvertices)[verIndex]; + Nif::NiSkinData::BoneInfoCopy* boneinfocopy = &(allshapesiter->boneinfo[inds[0].boneinfocopyindex]); + Ogre::Bone *bonePtr = 0; + + + + Ogre::Vector3 vecPos; + Ogre::Quaternion vecRot; + std::map::iterator result = vecRotPos.find(boneinfocopy); + + if(result == vecRotPos.end()){ + bonePtr = skel->getBone(boneinfocopy->bonename); + + vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; + vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; + + + PosAndRot both; + both.vecPos = vecPos; + both.vecRot = vecRot; + vecRotPos[boneinfocopy] = both; + + } + else{ + PosAndRot both = result->second; + vecPos = both.vecPos; + vecRot = both.vecRot; + } + + Ogre::Vector3 absVertPos = (vecPos + vecRot * currentVertex) * inds[0].weight; + + + + for(std::size_t i = 1; i < inds.size(); i++){ + boneinfocopy = &(allshapesiter->boneinfo[inds[i].boneinfocopyindex]); + result = vecRotPos.find(boneinfocopy); + + + if(result == vecRotPos.end()){ + bonePtr = skel->getBone(boneinfocopy->bonename); + vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; + vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; + + PosAndRot both; + both.vecPos = vecPos; + both.vecRot = vecRot; + vecRotPos[boneinfocopy] = both; + + } + else{ + PosAndRot both = result->second; + vecPos = both.vecPos; + vecRot = both.vecRot; + } + + + absVertPos += (vecPos + vecRot * currentVertex) * inds[i].weight; + + + } + Ogre::Real* addr = (pReal + 3 * verIndex); + *addr = absVertPos.x; + *(addr+1) = absVertPos.y; + *(addr+2) = absVertPos.z; + + } + + + + + } + else + { + //Ogre::Bone *bonePtr = creaturemodel->getSkeleton()->getBone(copy.bonename); + Ogre::Quaternion shaperot = copy.trafo.rotation; + Ogre::Vector3 shapetrans = copy.trafo.trans; + float shapescale = copy.trafo.scale; + std::vector boneSequence = copy.boneSequence; + + Ogre::Vector3 transmult; + Ogre::Quaternion rotmult; + float scale; + if(boneSequence.size() > 0){ + std::vector::iterator boneSequenceIter = boneSequence.begin(); + if(skel->hasBone(*boneSequenceIter)){ + Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); + + + + + transmult = bonePtr->getPosition(); + rotmult = bonePtr->getOrientation(); + scale = bonePtr->getScale().x; + boneSequenceIter++; + + for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) + { + if(creaturemodel->getSkeleton()->hasBone(*boneSequenceIter)){ + Ogre::Bone *bonePtr = creaturemodel->getSkeleton()->getBone(*boneSequenceIter); + // Computes C = B + AxC*scale + transmult = transmult + rotmult * bonePtr->getPosition(); + rotmult = rotmult * bonePtr->getOrientation(); + scale = scale * bonePtr->getScale().x; + } + //std::cout << "Bone:" << *boneSequenceIter << " "; + } + transmult = transmult + rotmult * shapetrans; + rotmult = rotmult * shaperot; + scale = shapescale * scale; + + //std::cout << "Position: " << transmult << "Rotation: " << rotmult << "\n"; + } + } + else + { + transmult = shapetrans; + rotmult = shaperot; + scale = shapescale; + } + + + + + // Computes C = B + AxC*scale + // final_vector = old_vector + old_rotation*new_vector*old_scale/ + + for(unsigned int i = 0; i < allvertices->size(); i++){ + Ogre::Vector3 current = transmult + rotmult * (*allvertices)[i]; + Ogre::Real* addr = pReal + i * 3; + *addr = current.x; + *(addr+1) = current.y; + *(addr + 2) = current.z; + + }/* + for(int i = 0; i < allnormals.size(); i++){ + Ogre::Vector3 current =rotmult * allnormals[i]; + Ogre::Real* addr = pRealNormal + i * 3; + *addr = current.x; + *(addr+1) = current.y; + *(addr + 2) = current.z; + + }*/ + + } + vbuf->unlock(); + + } + + } + bool Animation::timeIndex( float time, const std::vector & times, int & i, int & j, float & x ){ + int count; + if ( (count = times.size()) > 0 ) + { + if ( time <= times[0] ) + { + i = j = 0; + x = 0.0; + return true; + } + if ( time >= times[count - 1] ) + { + i = j = count - 1; + x = 0.0; + return true; + } + + if ( i < 0 || i >= count ) + i = 0; + + float tI = times[i]; + if ( time > tI ) + { + j = i + 1; + float tJ; + while ( time >= ( tJ = times[j]) ) + { + i = j++; + tI = tJ; + } + x = ( time - tI ) / ( tJ - tI ); + return true; + } + else if ( time < tI ) + { + j = i - 1; + float tJ; + while ( time <= ( tJ = times[j] ) ) + { + i = j--; + tI = tJ; + } + x = ( time - tI ) / ( tJ - tI ); + return true; + } + else + { + j = i; + x = 0.0; + return true; + } + } + else + return false; + +} + + void Animation::handleAnimationTransforms(){ + + + Ogre::SkeletonInstance* skel = base->getSkeleton(); + + + Ogre::Bone* b = skel->getRootBone(); + b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3)); //This is a trick + + skel->_updateTransforms(); + //skel->_notifyManualBonesDirty(); + + base->getAllAnimationStates()->_notifyDirty(); + //base->_updateAnimation(); + //base->_notifyMoved(); + + for(unsigned int i = 0; i < entityparts.size(); i++){ + //Ogre::SkeletonInstance* skel = entityparts[i]->getSkeleton(); + + //Ogre::Bone* b = skel->getRootBone(); + //b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3));//This is a trick + + //entityparts[i]->getAllAnimationStates()->_notifyDirty(); + } + + + std::vector::iterator iter; + int slot = 0; + if(transformations){ + for(iter = transformations->begin(); iter != transformations->end(); iter++){ + if(time < iter->getStartTime() || time < startTime || time > iter->getStopTime()) + { + slot++; + continue; + } + + float x; + float x2; + + const std::vector & quats = iter->getQuat(); + + const std::vector & ttime = iter->gettTime(); + + + const std::vector & rtime = iter->getrTime(); + int rindexJ = rindexI[slot]; + + timeIndex(time, rtime, rindexI[slot], rindexJ, x2); + int tindexJ = tindexI[slot]; + + + const std::vector & translist1 = iter->getTranslist1(); + + timeIndex(time, ttime, tindexI[slot], tindexJ, x); + + Ogre::Vector3 t; + Ogre::Quaternion r; + + bool bTrans = translist1.size() > 0; + + + bool bQuats = quats.size() > 0; + + if(skel->hasBone(iter->getBonename())){ + Ogre::Bone* bone = skel->getBone(iter->getBonename()); + if(bTrans){ + Ogre::Vector3 v1 = translist1[tindexI[slot]]; + Ogre::Vector3 v2 = translist1[tindexJ]; + t = (v1 + (v2 - v1) * x); + bone->setPosition(t); + + } + if(bQuats){ + r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); + bone->setOrientation(r); + } + + + + + + } + + + slot++; + } + skel->_updateTransforms(); + base->getAllAnimationStates()->_notifyDirty(); +} +} + +} diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp new file mode 100644 index 000000000..d1e8071f0 --- /dev/null +++ b/apps/openmw/mwrender/animation.hpp @@ -0,0 +1,73 @@ +#ifndef _GAME_RENDER_ANIMATION_H +#define _GAME_RENDER_ANIMATION_H +#include +#include +#include "../mwworld/refdata.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/actiontalk.hpp" +#include "../mwworld/environment.hpp" +#include +#include +#include + + + + +namespace MWRender{ + +struct PosAndRot{ + Ogre::Quaternion vecRot; + Ogre::Vector3 vecPos; +}; + +class Animation{ + + protected: + Ogre::SceneNode* insert; + OEngine::Render::OgreRenderer &mRend; + MWWorld::Environment& mEnvironment; + std::map vecRotPos; + static std::map mUniqueIDs; + + + + std::vector* > shapeparts; //All the NiTriShape data that we need for animating an npc + + float time; + float startTime; + float stopTime; + int animate; + //Represents a rotation index for each bone + std::vectorrindexI; + //Represents a translation index for each bone + std::vectortindexI; + + //Only shapes with morphing data will use a shape number + int shapeNumber; + std::vector > shapeIndexI; + + //Ogre::SkeletonInstance* skel; + std::vector* shapes; //All the NiTriShapeData for a creature + std::vector entityparts; + + + std::vector* transformations; + std::map* textmappings; + Ogre::Entity* base; + void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); + void handleAnimationTransforms(); + bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); + std::string getUniqueID(std::string mesh); + + public: + Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend): mRend(_rend), mEnvironment(_env), animate(0){}; + virtual void runAnimation(float timepassed) = 0; + void startScript(std::string groupname, int mode, int loops); + void stopScript(); + + + virtual ~Animation(); + +}; +} +#endif \ No newline at end of file diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp new file mode 100644 index 000000000..4de6453aa --- /dev/null +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -0,0 +1,63 @@ +#include "creatureanimation.hpp" + +#include "../mwworld/world.hpp" + +using namespace Ogre; +using namespace NifOgre; +namespace MWRender{ + +CreatureAnimation::~CreatureAnimation(){ + +} +CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env,OEngine::Render::OgreRenderer& _rend): Animation(_env,_rend){ + insert = ptr.getRefData().getBaseNode(); + ESMS::LiveCellRef *ref = + ptr.get(); + + assert (ref->base != NULL); + if(!ref->base->model.empty()){ + const std::string &mesh = "meshes\\" + ref->base->model; + std::string meshNumbered = mesh + getUniqueID(mesh) + ">|"; + NifOgre::NIFLoader::load(meshNumbered); + base = mRend.getScene()->createEntity(meshNumbered); + std::string meshZero = mesh + "0000>|"; + + if((transformations = (NIFLoader::getSingletonPtr())->getAnim(meshZero))){ + + for(std::size_t init = 0; init < transformations->size(); init++){ + rindexI.push_back(0); + tindexI.push_back(0); + } + stopTime = transformations->begin()->getStopTime(); + startTime = transformations->begin()->getStartTime(); + shapes = (NIFLoader::getSingletonPtr())->getShapes(meshZero); + } + textmappings = NIFLoader::getSingletonPtr()->getTextIndices(meshZero); + insert->attachObject(base); + } +} + +void CreatureAnimation::runAnimation(float timepassed){ + vecRotPos.clear(); + if(animate > 0){ + //Add the amount of time passed to time + + //Handle the animation transforms dependent on time + + //Handle the shapes dependent on animation transforms + time += timepassed; + if(time >= stopTime){ + animate--; + //std::cout << "Stopping the animation\n"; + if(animate == 0) + time = stopTime; + else + time = startTime + (time - stopTime); + } + + handleAnimationTransforms(); + handleShapes(shapes, base, base->getSkeleton()); + + } +} +} diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp new file mode 100644 index 000000000..2229eeec9 --- /dev/null +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -0,0 +1,26 @@ +#ifndef _GAME_RENDER_CREATUREANIMATION_H +#define _GAME_RENDER_CREATUREANIMATION_H + +#include "animation.hpp" +#include + + +#include "../mwworld/refdata.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/environment.hpp" +#include "components/nifogre/ogre_nif_loader.hpp" + + +namespace MWRender{ + +class CreatureAnimation: public Animation{ + + public: + virtual ~CreatureAnimation(); + CreatureAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend); + virtual void runAnimation(float timepassed); + + +}; +} +#endif \ No newline at end of file diff --git a/apps/openmw/mwrender/creatures.cpp b/apps/openmw/mwrender/creatures.cpp deleted file mode 100644 index 998951249..000000000 --- a/apps/openmw/mwrender/creatures.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "creatures.hpp" -using namespace MWRender; \ No newline at end of file diff --git a/apps/openmw/mwrender/creatures.hpp b/apps/openmw/mwrender/creatures.hpp deleted file mode 100644 index 9aabb175b..000000000 --- a/apps/openmw/mwrender/creatures.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _GAME_RENDER_CREATURES_H -#define _GAME_RENDER_CREATURES_H -#include - -namespace MWRender{ -class Creatures{ - -}; -} -#endif \ No newline at end of file diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp new file mode 100644 index 000000000..c6fe023d6 --- /dev/null +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -0,0 +1,284 @@ +#include "npcanimation.hpp" +#include "../mwworld/world.hpp" + + +using namespace Ogre; +using namespace NifOgre; +namespace MWRender{ +NpcAnimation::~NpcAnimation(){ + +} + + +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env,OEngine::Render::OgreRenderer& _rend): Animation(_env,_rend){ + ESMS::LiveCellRef *ref = + ptr.get(); + + //Part selection on last character of the file string + // " Tri Chest + // * Tri Tail + // : Tri Left Foot + // < Tri Right Foot + // > Tri Left Hand + // ? Tri Right Hand + // | Normal + + //Mirroring Parts on second to last character + //suffix == '*' + // vector = Ogre::Vector3(-1,1,1); + // suffix == '?' + // vector = Ogre::Vector3(1,-1,1); + // suffix == '<' + // vector = Ogre::Vector3(1,1,-1); + + + std::string hairID = ref->base->hair; + std::string headID = ref->base->head; + std::string npcName = ref->base->name; + //ESMStore::Races r = + const ESM::Race* race = mEnvironment.mWorld->getStore().races.find(ref->base->race); + + + std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); + char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2); + bool female = tolower(secondtolast) == 'f'; + std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); + bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; + + /*std::cout << "Race: " << ref->base->race ; + if(female){ + std::cout << " Sex: Female" << " Height: " << race->data.height.female << "\n"; + } + else{ + std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; + }*/ + + + + std::string smodel = "meshes\\base_anim.nif"; + if(beast) + smodel = "meshes\\base_animkna.nif"; + + insert = ptr.getRefData().getBaseNode(); + assert(insert); + + NifOgre::NIFLoader::load(smodel); + + base = mRend.getScene()->createEntity(smodel); + base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones + //stay in the same place when we skipanim, or open a gui window + + + + if((transformations = (NIFLoader::getSingletonPtr())->getAnim(smodel))){ + + for(unsigned int init = 0; init < transformations->size(); init++){ + rindexI.push_back(0); + tindexI.push_back(0); + } + + stopTime = transformations->begin()->getStopTime(); + startTime = transformations->begin()->getStartTime(); + } + textmappings = NIFLoader::getSingletonPtr()->getTextIndices(smodel); + insert->attachObject(base); + + if(female) + insert->scale(race->data.height.female, race->data.height.female, race->data.height.female); + else + insert->scale(race->data.height.male, race->data.height.male, race->data.height.male); + std::string headModel = "meshes\\" + + mEnvironment.mWorld->getStore().bodyParts.find(headID)->model; + + std::string hairModel = "meshes\\" + + mEnvironment.mWorld->getStore().bodyParts.find(hairID)->model; + const ESM::BodyPart *chest = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "chest"); + const ESM::BodyPart *upperleg = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "upper leg"); + const ESM::BodyPart *groin = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "groin"); + const ESM::BodyPart *arml = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "upper arm"); //We need two + const ESM::BodyPart *neck = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "neck"); + const ESM::BodyPart *knee = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "knee"); + const ESM::BodyPart *ankle = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "ankle"); + const ESM::BodyPart *foot = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "foot"); + const ESM::BodyPart *feet = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "feet"); + const ESM::BodyPart *tail = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "tail"); + const ESM::BodyPart *wristl = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "wrist"); //We need two + const ESM::BodyPart *forearml = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "forearm"); //We need two + const ESM::BodyPart *handl = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "hand"); //We need two + const ESM::BodyPart *hair = mEnvironment.mWorld->getStore().bodyParts.search(hairID); + const ESM::BodyPart *head = mEnvironment.mWorld->getStore().bodyParts.search(headID); + if(bodyRaceID == "b_n_argonian_f_") + forearml = mEnvironment.mWorld->getStore().bodyParts.search ("b_n_argonian_m_forearm"); //We need two + if(!handl) + handl = mEnvironment.mWorld->getStore().bodyParts.search (bodyRaceID + "hands"); + //const ESM::BodyPart* claviclel = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "clavicle"); + //const ESM::BodyPart* clavicler = claviclel; + const ESM::BodyPart* handr = handl; + const ESM::BodyPart* forearmr = forearml; + const ESM::BodyPart* wristr = wristl; + const ESM::BodyPart* armr = arml; + + + if(upperleg){ + insertBoundedPart("meshes\\" + upperleg->model + "*|", "Left Upper Leg"); + insertBoundedPart("meshes\\" + upperleg->model, "Right Upper Leg"); + + } + if(foot){ + if(bodyRaceID.compare("b_n_khajiit_m_") == 0) + { + feet = foot; + } + else + { + insertBoundedPart("meshes\\" + foot->model, "Right Foot"); + insertBoundedPart("meshes\\" + foot->model + "*|", "Left Foot"); + } + } + if(groin){ + insertBoundedPart("meshes\\" + groin->model, "Groin"); + } + if(knee) + { + insertBoundedPart("meshes\\" + knee->model + "*|", "Left Knee"); //e + insertBoundedPart("meshes\\" + knee->model, "Right Knee"); //e + + } + if(ankle){ + + insertBoundedPart("meshes\\" + ankle->model + "*|", "Left Ankle"); //Ogre::Quaternion(Ogre::Radian(3.14 / 4), Ogre::Vector3(1, 0, 0)),blank); //1,0,0, blank); + insertBoundedPart("meshes\\" + ankle->model, "Right Ankle"); + } + if (armr){ + insertBoundedPart("meshes\\" + armr->model, "Right Upper Arm"); + } + if(arml){ + insertBoundedPart("meshes\\" + arml->model + "*|", "Left Upper Arm"); + } + + if (forearmr) + { + insertBoundedPart("meshes\\" + forearmr->model, "Right Forearm"); + } + if(forearml) + insertBoundedPart("meshes\\" + forearml->model + "*|", "Left Forearm"); + + if (wristr) + { + insertBoundedPart("meshes\\" + wristr->model, "Right Wrist"); + } + + if(wristl) + insertBoundedPart("meshes\\" + wristl->model + "*|", "Left Wrist"); + + + + + + /*if(claviclel) + insertBoundedPart("meshes\\" + claviclel->model + "*|", "Left Clavicle", base); + if(clavicler) + insertBoundedPart("meshes\\" + clavicler->model , "Right Clavicle", base);*/ + + if(neck) + { + insertBoundedPart("meshes\\" + neck->model, "Neck"); + } + if(head) + insertBoundedPart("meshes\\" + head->model, "Head"); + if(hair) + insertBoundedPart("meshes\\" + hair->model, "Head"); + + if (chest){ + insertFreePart("meshes\\" + chest->model, ">\"", insert); + + + } + if (handr){ + insertFreePart("meshes\\" + handr->model , ">?", insert); + + } + if (handl){ + insertFreePart("meshes\\" + handl->model, ">>", insert); + + } + if(tail){ + insertFreePart("meshes\\" + tail->model, ">*", insert); + } + if(feet){ + std::string num = getUniqueID(feet->model); + insertFreePart("meshes\\" + feet->model,"><", insert); + insertFreePart("meshes\\" + feet->model,">:", insert); + } + //originalpos = insert->_getWorldAABB().getCenter(); + //originalscenenode = insert->getPosition(); +} + +Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, std::string bonename){ + + NIFLoader::load(mesh); + Entity* ent = mRend.getScene()->createEntity(mesh); + + base->attachObjectToBone(bonename, ent); + return ent; +} +void NpcAnimation::insertFreePart(const std::string &mesh, const std::string suffix, Ogre::SceneNode* insert){ + std::string meshNumbered = mesh + getUniqueID(mesh + suffix) + suffix; + NIFLoader::load(meshNumbered); + + Ogre::Entity* ent = mRend.getScene()->createEntity(meshNumbered); + + + + + insert->attachObject(ent); + entityparts.push_back(ent); + shapes = ((NIFLoader::getSingletonPtr())->getShapes(mesh + "0000" + suffix)); + if(shapes){ + shapeparts.push_back(shapes); + handleShapes(shapes, ent, base->getSkeleton()); + } + + +} + + +void NpcAnimation::runAnimation(float timepassed){ + + //1. Add the amount of time passed to time + + //2. Handle the animation transforms dependent on time + + //3. Handle the shapes dependent on animation transforms + if(animate > 0){ + time += timepassed; + + if(time > stopTime){ + animate--; + + if(animate == 0) + time = stopTime; + else + time = startTime + (time - stopTime); + } + + handleAnimationTransforms(); + + std::vector*>::iterator shapepartsiter = shapeparts.begin(); + std::vector::iterator entitypartsiter = entityparts.begin(); + while(shapepartsiter != shapeparts.end()) + { + vecRotPos.clear(); + std::vector* shapes = *shapepartsiter; + Ogre::Entity* theentity = *entitypartsiter; + + + handleShapes(shapes, theentity, base->getSkeleton()); + shapepartsiter++; + entitypartsiter++; + } + + } + +} +} diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp new file mode 100644 index 000000000..e2071957c --- /dev/null +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -0,0 +1,30 @@ +#ifndef _GAME_RENDER_NPCANIMATION_H +#define _GAME_RENDER_NPCANIMATION_H +#include "animation.hpp" +#include +#include +#include +#include +#include + +#include "../mwworld/refdata.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/environment.hpp" +#include "components/nifogre/ogre_nif_loader.hpp" + +namespace MWRender{ + +class NpcAnimation: public Animation{ + + + + public: + NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend); + virtual ~NpcAnimation(); + Ogre::Entity* insertBoundedPart(const std::string &mesh, std::string bonename); + void insertFreePart(const std::string &mesh, const std::string suffix, Ogre::SceneNode* insert); + virtual void runAnimation(float timepassed); + +}; +} +#endif \ No newline at end of file diff --git a/apps/openmw/mwrender/npcs.cpp b/apps/openmw/mwrender/npcs.cpp deleted file mode 100644 index 7012ccb18..000000000 --- a/apps/openmw/mwrender/npcs.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "npcs.hpp" -using namespace MWRender; diff --git a/apps/openmw/mwrender/npcs.hpp b/apps/openmw/mwrender/npcs.hpp deleted file mode 100644 index 88ee5ca30..000000000 --- a/apps/openmw/mwrender/npcs.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _GAME_RENDER_NPCS_H -#define _GAME_RENDER_NPCS_H -#include -namespace MWRender{ -class Npcs{ - -}; -} -#endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 757d86f3b..717064ada 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -1,11 +1,11 @@ #include "objects.hpp" + #include + #include -using namespace Ogre; using namespace MWRender; - bool Objects::lightConst = false; float Objects::lightConstValue = 0.0f; @@ -23,11 +23,25 @@ bool Objects::lightOutQuadInLin = false; int Objects::uniqueID = 0; -void Objects::setMwRoot(Ogre::SceneNode* root){ - mwRoot = root; +void Objects::clearSceneNode (Ogre::SceneNode *node) +{ + /// \todo This should probably be moved into OpenEngine at some point. + for (int i=node->numAttachedObjects()-1; i>=0; --i) + { + Ogre::MovableObject *object = node->getAttachedObject (i); + node->detachObject (object); + mRenderer.getScene()->destroyMovableObject (object); + } } -void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ - Ogre::SceneNode* root = mwRoot; + +void Objects::setMwRoot(Ogre::SceneNode* root) +{ + mMwRoot = root; +} + +void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) +{ + Ogre::SceneNode* root = mMwRoot; Ogre::SceneNode* cellnode; if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) { @@ -49,90 +63,98 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ f = ptr.getCellRef().pos.rot; // Rotate around X axis - Quaternion xr(Radian(-f[0]), Vector3::UNIT_X); + Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); // Rotate around Y axis - Quaternion yr(Radian(-f[1]), Vector3::UNIT_Y); + Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y); // Rotate around Z axis - Quaternion zr(Radian(-f[2]), Vector3::UNIT_Z); + Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); - // Rotates first around z, then y, then x + // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); + if (!enabled) insert->setVisible (false); ptr.getRefData().setBaseNode(insert); - isStatic = static_; - - + mIsStatic = static_; } -void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh){ + +void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) +{ Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); NifOgre::NIFLoader::load(mesh); - Entity *ent = mRend.getScene()->createEntity(mesh); + Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh); - if(!isStatic) + if(!mIsStatic) { insert->attachObject(ent); } else { Ogre::StaticGeometry* sg = 0; - if(mSG.find(ptr.getCell()) == mSG.end()) + if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end()) { uniqueID = uniqueID +1; - sg = mRend.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); + sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); //Create the scenenode and put it in the map - mSG[ptr.getCell()] = sg; + mStaticGeometry[ptr.getCell()] = sg; + + // This specifies the size of a single batch region. + // If it is set too high: + // - there will be problems choosing the correct lights + // - 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)); } else { - sg = mSG[ptr.getCell()]; + sg = mStaticGeometry[ptr.getCell()]; } sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); - sg->setRegionDimensions(Ogre::Vector3(100000,10000,100000)); - - mRend.getScene()->destroyEntity(ent); + mRenderer.getScene()->destroyEntity(ent); } - - } -void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius){ - Ogre::SceneNode* insert = mRend.getScene()->getSceneNode(ptr.getRefData().getHandle()); + +void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius) +{ + Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle()); assert(insert); - Ogre::Light *light = mRend.getScene()->createLight(); + Ogre::Light *light = mRenderer.getScene()->createLight(); light->setDiffuseColour (r, g, b); float cval=0.0f, lval=0.0f, qval=0.0f; if(lightConst) cval = lightConstValue; - if(!lightOutQuadInLin) - { - if(lightLinear) - radius *= lightLinearRadiusMult; - if(lightQuadratic) - radius *= lightQuadraticRadiusMult; - if(lightLinear) - lval = lightLinearValue / pow(radius, lightLinearMethod); - if(lightQuadratic) - qval = lightQuadraticValue / pow(radius, lightQuadraticMethod); - } - else - { - // FIXME: - // Do quadratic or linear, depending if we're in an exterior or interior - // cell, respectively. Ignore lightLinear and lightQuadratic. - } + if(!lightOutQuadInLin) + { + if(lightLinear) + radius *= lightLinearRadiusMult; + if(lightQuadratic) + radius *= lightQuadraticRadiusMult; - light->setAttenuation(10*radius, cval, lval, qval); + if(lightLinear) + lval = lightLinearValue / pow(radius, lightLinearMethod); + if(lightQuadratic) + qval = lightQuadraticValue / pow(radius, lightQuadraticMethod); + } + else + { + // FIXME: + // Do quadratic or linear, depending if we're in an exterior or interior + // cell, respectively. Ignore lightLinear and lightQuadratic. + } - insert->attachObject(light); + light->setAttenuation(10*radius, cval, lval, qval); + + insert->attachObject(light); } bool Objects::deleteObject (const MWWorld::Ptr& ptr) @@ -145,8 +167,9 @@ bool Objects::deleteObject (const MWWorld::Ptr& ptr) mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter) if (iter->second==parent) { + clearSceneNode (base); base->removeAndDestroyAllChildren(); - mRend.getScene()->destroySceneNode (base); + mRenderer.getScene()->destroySceneNode (base); ptr.getRefData().setBaseNode (0); return true; } @@ -157,29 +180,35 @@ bool Objects::deleteObject (const MWWorld::Ptr& ptr) return true; } -void Objects::removeCell(MWWorld::Ptr::CellStore* store){ +void Objects::removeCell(MWWorld::Ptr::CellStore* store) +{ if(mCellSceneNodes.find(store) != mCellSceneNodes.end()) { Ogre::SceneNode* base = mCellSceneNodes[store]; + + for (int i=0; inumChildren(); ++i) + clearSceneNode (static_cast (base->getChild (i))); + base->removeAndDestroyAllChildren(); mCellSceneNodes.erase(store); - mRend.getScene()->destroySceneNode(base); + mRenderer.getScene()->destroySceneNode(base); base = 0; } - - if(mSG.find(store) != mSG.end()) + if(mStaticGeometry.find(store) != mStaticGeometry.end()) { - Ogre::StaticGeometry* sg = mSG[store]; - mSG.erase(store); - mRend.getScene()->destroyStaticGeometry (sg); + Ogre::StaticGeometry* sg = mStaticGeometry[store]; + mStaticGeometry.erase(store); + mRenderer.getScene()->destroyStaticGeometry (sg); sg = 0; } } -void Objects::buildStaticGeometry(ESMS::CellStore& cell){ - if(mSG.find(&cell) != mSG.end()) + +void Objects::buildStaticGeometry(ESMS::CellStore& cell) +{ + if(mStaticGeometry.find(&cell) != mStaticGeometry.end()) { - Ogre::StaticGeometry* sg = mSG[&cell]; + Ogre::StaticGeometry* sg = mStaticGeometry[&cell]; sg->build(); } } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 6f36e4849..d58455b9f 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -1,20 +1,21 @@ #ifndef _GAME_RENDER_OBJECTS_H #define _GAME_RENDER_OBJECTS_H -#include "components/esm_store/cell_store.hpp" +#include + +#include #include "../mwworld/refdata.hpp" #include "../mwworld/ptr.hpp" -#include namespace MWRender{ class Objects{ - OEngine::Render::OgreRenderer &mRend; + OEngine::Render::OgreRenderer &mRenderer; std::map mCellSceneNodes; - std::map mSG; - Ogre::SceneNode* mwRoot; - bool isStatic; + std::map mStaticGeometry; + Ogre::SceneNode* mMwRoot; + bool mIsStatic; static int uniqueID; static bool lightConst; static float lightConstValue; @@ -30,8 +31,12 @@ class Objects{ static float lightQuadraticRadiusMult; static bool lightOutQuadInLin; + + void clearSceneNode (Ogre::SceneNode *node); + ///< Remove all movable objects from \a node. + public: - Objects(OEngine::Render::OgreRenderer& _rend): mRend(_rend){} + Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer){} ~Objects(){} void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh); diff --git a/apps/openmw/mwrender/renderinginterface.hpp b/apps/openmw/mwrender/renderinginterface.hpp index ebd4af2bd..03935bef6 100644 --- a/apps/openmw/mwrender/renderinginterface.hpp +++ b/apps/openmw/mwrender/renderinginterface.hpp @@ -1,16 +1,15 @@ #ifndef _GAME_RENDERING_INTERFACE_H #define _GAME_RENDERING_INTERFACE_H namespace MWRender{ - class Npcs; - class Creatures; class Objects; + class Actors; class Player; + class RenderingInterface{ public: - virtual MWRender::Npcs& getNPCs() = 0; - virtual MWRender::Creatures& getCreatures() = 0; virtual MWRender::Objects& getObjects() = 0; virtual MWRender::Player& getPlayer() = 0; + virtual MWRender::Actors& getActors() = 0; virtual ~RenderingInterface(){}; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3adfa6929..b4a6625dc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -19,13 +19,10 @@ using namespace Ogre; namespace MWRender { - - -RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine) -:rend(_rend), objects(rend), mDebugging(engine) +RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment) +:mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mDebugging(engine) { - rend.createScene("PlayerCam", 55, 5); - mSkyManager = MWRender::SkyManager::create(rend.getWindow(), rend.getCamera(), resDir); + mRendering.createScene("PlayerCam", 55, 5); // Set default mipmap level (NB some APIs ignore this) TextureManager::getSingleton().setDefaultNumMipmaps(5); @@ -38,48 +35,64 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const // the screen (when x is to the right.) This is the orientation that // Morrowind uses, and it automagically makes everything work as it // should. - SceneNode *rt = rend.getScene()->getRootSceneNode(); - mwRoot = rt->createChildSceneNode(); - mwRoot->pitch(Degree(-90)); - objects.setMwRoot(mwRoot); - + SceneNode *rt = mRendering.getScene()->getRootSceneNode(); + mMwRoot = rt->createChildSceneNode(); + mMwRoot->pitch(Degree(-90)); + mObjects.setMwRoot(mMwRoot); + mActors.setMwRoot(mMwRoot); + //used to obtain ingame information of ogre objects (which are faced or selected) - mRaySceneQuery = rend.getScene()->createRayQuery(Ray()); + mRaySceneQuery = mRendering.getScene()->createRayQuery(Ray()); - Ogre::SceneNode *playerNode = mwRoot->createChildSceneNode ("player"); + Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); playerNode->pitch(Degree(90)); Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode(); Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode(); - cameraPitchNode->attachObject(rend.getCamera()); + cameraPitchNode->attachObject(mRendering.getCamera()); + + //mSkyManager = 0; + mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment); - mPlayer = new MWRender::Player (rend.getCamera(), playerNode); mWater = 0; - //std::cout << "Three"; + + mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); + mSun = 0; } RenderingManager::~RenderingManager () { + //TODO: destroy mSun? delete mPlayer; delete mSkyManager; } -MWRender::Npcs& RenderingManager::getNPCs(){ - return npcs; +MWRender::SkyManager* RenderingManager::getSkyManager() +{ + return mSkyManager; } + MWRender::Objects& RenderingManager::getObjects(){ - return objects; + return mObjects; } -MWRender::Creatures& RenderingManager::getCreatures(){ - return creatures; +MWRender::Actors& RenderingManager::getActors(){ + return mActors; } + MWRender::Player& RenderingManager::getPlayer(){ return (*mPlayer); } +OEngine::Render::Fader* RenderingManager::getFader() +{ + return mRendering.getFader(); +} + void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){ - objects.removeCell(store); + + mObjects.removeCell(store); + mActors.removeCell(store); } void RenderingManager::removeWater (){ @@ -91,7 +104,7 @@ void RenderingManager::removeWater (){ void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) { - objects.buildStaticGeometry (*store); + mObjects.buildStaticGeometry (*store); } void RenderingManager::addObject (const MWWorld::Ptr& ptr){ @@ -102,7 +115,11 @@ void RenderingManager::addObject (const MWWorld::Ptr& ptr){ } void RenderingManager::removeObject (const MWWorld::Ptr& ptr) { - if (!objects.deleteObject (ptr)) + if (!mObjects.deleteObject (ptr)) + { + /// \todo delete non-object MW-references + } + if (!mActors.deleteObject (ptr)) { /// \todo delete non-object MW-references } @@ -111,7 +128,7 @@ void RenderingManager::removeObject (const MWWorld::Ptr& ptr) void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position) { /// \todo move this to the rendering-subsystems - rend.getScene()->getSceneNode (ptr.getRefData().getHandle())-> + mRendering.getScene()->getSceneNode (ptr.getRefData().getHandle())-> setPosition (position); } @@ -127,12 +144,16 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve void RenderingManager::update (float duration){ - + mActors.update (duration); + + mSkyManager->update(duration); + + mRendering.update(duration); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ if(store->cell->data.flags & store->cell->HasWater){ if(mWater == 0) - mWater = new MWRender::Water(rend.getCamera(), store->cell); + mWater = new MWRender::Water(mRendering.getCamera(), store->cell); else mWater->changeCell(store->cell); //else @@ -145,27 +166,32 @@ void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ void RenderingManager::skyEnable () { + if(mSkyManager) mSkyManager->enable(); } void RenderingManager::skyDisable () { - mSkyManager->disable(); + if(mSkyManager) + mSkyManager->disable(); } void RenderingManager::skySetHour (double hour) { - mSkyManager->setHour(hour); + if(mSkyManager) + mSkyManager->setHour(hour); } void RenderingManager::skySetDate (int day, int month) { - mSkyManager->setDate(day, month); + if(mSkyManager) + mSkyManager->setDate(day, month); } int RenderingManager::skyGetMasserPhase() const { + return mSkyManager->getMasserPhase(); } @@ -174,12 +200,28 @@ int RenderingManager::skyGetSecundaPhase() const return mSkyManager->getSecundaPhase(); } -void RenderingManager::skySetMoonColour (bool red) -{ - mSkyManager->setMoonColour(red); +void RenderingManager::skySetMoonColour (bool red){ + if(mSkyManager) + mSkyManager->setMoonColour(red); } -bool RenderingManager::toggleRenderMode(int mode){ - return mDebugging.toggleRenderMode(mode); + +bool RenderingManager::toggleRenderMode(int mode) +{ + if (mode == MWWorld::World::Render_CollisionDebug) + return mDebugging.toggleRenderMode(mode); + else // if (mode == MWWorld::World::Render_Wireframe) + { + if (mRendering.getCamera()->getPolygonMode() == PM_SOLID) + { + mRendering.getCamera()->setPolygonMode(PM_WIREFRAME); + return true; + } + else + { + mRendering.getCamera()->setPolygonMode(PM_SOLID); + return false; + } + } } void RenderingManager::configureFog(ESMS::CellStore &mCell) @@ -187,31 +229,44 @@ void RenderingManager::configureFog(ESMS::CellStore &mCell) Ogre::ColourValue color; color.setAsABGR (mCell.cell->ambi.fog); - float high = 4500 + 9000 * (1-mCell.cell->ambi.fogDensity); - float low = 200; - - rend.getScene()->setFog (FOG_LINEAR, color, 0, low, high); - rend.getCamera()->setFarClipDistance (high + 10); - rend.getViewport()->setBackgroundColour (color); + configureFog(mCell.cell->ambi.fogDensity, color); } +void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) +{ + /// \todo make the viewing distance and fog start/end configurable + + // right now we load 3x3 cells, so the maximum viewing distance we + // can allow (to prevent objects suddenly popping up) equals: + // 8192 * 0.69 + // ^ cell size ^ minimum density value used (clear weather) + float low = 5652.48 / density / 2.f; + float high = 5652.48 / density; + + mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high); + + mRendering.getCamera()->setFarClipDistance ( high ); + mRendering.getViewport()->setBackgroundColour (colour); +} + + void RenderingManager::setAmbientMode() { switch (mAmbientMode) { case 0: - rend.getScene()->setAmbientLight(mAmbientColor); + mRendering.getScene()->setAmbientLight(mAmbientColor); break; case 1: - rend.getScene()->setAmbientLight(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); + mRendering.getScene()->setAmbientLight(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); break; case 2: - rend.getScene()->setAmbientLight(ColourValue(1,1,1)); + mRendering.getScene()->setAmbientLight(ColourValue(1,1,1)); break; } } @@ -223,12 +278,15 @@ void RenderingManager::configureAmbient(ESMS::CellStore &mCell // Create a "sun" that shines light downwards. It doesn't look // completely right, but leave it for now. - Ogre::Light *light = rend.getScene()->createLight(); + if(!mSun) + { + mSun = mRendering.getScene()->createLight(); + } Ogre::ColourValue colour; colour.setAsABGR (mCell.cell->ambi.sunlight); - light->setDiffuseColour (colour); - light->setType(Ogre::Light::LT_DIRECTIONAL); - light->setDirection(0,-1,0); + mSun->setDiffuseColour (colour); + mSun->setType(Ogre::Light::LT_DIRECTIONAL); + mSun->setDirection(0,-1,0); } // Switch through lighting modes. @@ -254,6 +312,49 @@ void RenderingManager::checkUnderwater(float y){ } } - - +void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, + int mode, int number) +{ + mActors.playAnimationGroup(ptr, groupName, mode, number); } + +void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr) +{ + mActors.skipAnimation(ptr); +} + +void RenderingManager::setSunColour(const Ogre::ColourValue& colour) +{ + mSun->setDiffuseColour(colour); +} + +void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) +{ + mRendering.getScene()->setAmbientLight(colour); +} + +void RenderingManager::sunEnable() +{ + if (mSun) mSun->setVisible(true); +} + +void RenderingManager::sunDisable() +{ + if (mSun) mSun->setVisible(false); +} + +void RenderingManager::setSunDirection(const Ogre::Vector3& direction) +{ + // direction * -1 (because 'direction' is camera to sun vector and not sun to camera), + // then convert from MW to ogre coordinates (swap y,z and make y negative) + if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.z, direction.y)); + + mSkyManager->setSunDirection(direction); +} + +void RenderingManager::setGlare(bool glare) +{ + mSkyManager->setGlare(glare); +} + +} // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 217b3df66..e06c64cfa 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -19,9 +20,9 @@ #include #include "renderinginterface.hpp" -#include "npcs.hpp" -#include "creatures.hpp" + #include "objects.hpp" +#include "actors.hpp" #include "player.hpp" #include "water.hpp" @@ -50,12 +51,12 @@ class RenderingManager: private RenderingInterface { private: - virtual MWRender::Npcs& getNPCs(); - virtual MWRender::Creatures& getCreatures(); + virtual MWRender::Objects& getObjects(); + virtual MWRender::Actors& getActors(); public: - RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine); + RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment); virtual ~RenderingManager(); @@ -64,8 +65,12 @@ class RenderingManager: private RenderingInterface { /// MWWorld::Player has been rewritten to not need access /// to internal details of the rendering system anymore + SkyManager* getSkyManager(); + void toggleLight(); bool toggleRenderMode(int mode); + + OEngine::Render::Fader* getFader(); void removeCell (MWWorld::Ptr::CellStore *store); @@ -89,7 +94,14 @@ class RenderingManager: private RenderingInterface { void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store); void update (float duration); - + + void setAmbientColour(const Ogre::ColourValue& colour); + void setSunColour(const Ogre::ColourValue& colour); + void setSunDirection(const Ogre::Vector3& direction); + void sunEnable(); + void sunDisable(); + + void setGlare(bool glare); void skyEnable (); void skyDisable (); void skySetHour (double hour); @@ -98,34 +110,55 @@ class RenderingManager: private RenderingInterface { int skyGetSecundaPhase() const; void skySetMoonColour (bool red); void configureAmbient(ESMS::CellStore &mCell); + /// configure fog according to cell void configureFog(ESMS::CellStore &mCell); + + /// configure fog manually + void configureFog(const float density, const Ogre::ColourValue& colour); + + void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, + int number = 1); + ///< Run animation for a MW-reference. Calls to this function for references that are currently not + /// in the rendered scene should be ignored. + /// + /// \param mode: 0 normal, 1 immediate start, 2 immediate loop + /// \param number How offen the animation should be run + + void skipAnimation (const MWWorld::Ptr& ptr); + ///< Skip the animation for the given MW-reference for one frame. Calls to this function for + /// references that are currently not in the rendered scene should be ignored. private: void setAmbientMode(); + SkyManager* mSkyManager; - OEngine::Render::OgreRenderer &rend; - Ogre::Camera* camera; - MWRender::Npcs npcs; - MWRender::Creatures creatures; - MWRender::Water *mWater; - MWRender::Objects objects; + Ogre::Camera* camera; + MWRender::Water *mWater; + + + + OEngine::Render::OgreRenderer &mRendering; + + MWRender::Objects mObjects; + MWRender::Actors mActors; // 0 normal, 1 more bright, 2 max int mAmbientMode; Ogre::ColourValue mAmbientColor; + Ogre::Light* mSun; /// Root node for all objects added to the scene. This is rotated so /// that the OGRE coordinate system matches that used internally in /// Morrowind. - Ogre::SceneNode *mwRoot; + Ogre::SceneNode *mMwRoot; Ogre::RaySceneQuery *mRaySceneQuery; - OEngine::Physic::PhysicEngine* eng; + OEngine::Physic::PhysicEngine* mPhysicsEngine; MWRender::Player *mPlayer; MWRender::Debugging mDebugging; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 95601d043..a747b9be0 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1,116 +1,783 @@ #include "sky.hpp" -#include "Caelum.h" -namespace MWRender +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" + +using namespace MWRender; +using namespace Ogre; + +BillboardObject::BillboardObject( const String& textureName, + const float initialSize, + const Vector3& position, + SceneNode* rootNode) { - // - // Implements a Caelum sky with default settings. - // - // Note: this is intended as a temporary solution to provide some form of - // sky rendering. This code will obviously need significant tailoring to - // support fidelity with Morrowind's rendering. Before doing major work - // on this class, more research should be done to determine whether - // Caelum or another plug-in such as SkyX would be best for the long-term. - // - class CaelumManager : public SkyManager + init(textureName, initialSize, position, rootNode); +} + +BillboardObject::BillboardObject() +{ +} + +void BillboardObject::setVisible(const bool visible) +{ + mNode->setVisible(visible); +} + +void BillboardObject::setSize(const float size) +{ + mNode->setScale(size, size, size); +} + +void BillboardObject::setVisibility(const float visibility) +{ + mMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, visibility); +} + +void BillboardObject::setPosition(const Vector3& pPosition) +{ + Vector3 normalised = pPosition.normalisedCopy(); + Vector3 finalPosition = normalised * 1000.f; + + mBBSet->setCommonDirection( -normalised ); + + mNode->setPosition(finalPosition); +} + +Vector3 BillboardObject::getPosition() const +{ + Vector3 p = mNode->_getDerivedPosition() - mNode->getParentSceneNode()->_getDerivedPosition(); + return Vector3(p.x, -p.z, p.y); +} + +void BillboardObject::setColour(const ColourValue& pColour) +{ + mMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(pColour); +} + +void BillboardObject::setRenderQueue(unsigned int id) +{ + mBBSet->setRenderQueueGroup(id); +} + +SceneNode* BillboardObject::getNode() +{ + return mNode; +} + +void BillboardObject::init(const String& textureName, + const float initialSize, + const Vector3& position, + SceneNode* rootNode) +{ + SceneManager* sceneMgr = rootNode->getCreator(); + + Vector3 finalPosition = position.normalisedCopy() * 1000.f; + + 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->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+2); + mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON); + mBBSet->setCommonDirection( -position.normalisedCopy() ); + mNode = rootNode->createChildSceneNode(); + mNode->setPosition(finalPosition); + mNode->attachObject(mBBSet); + mBBSet->createBillboard(0,0,0); + + mMaterial = MaterialManager::getSingleton().create("BillboardMaterial"+StringConverter::toString(bodyCount), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + mMaterial->removeAllTechniques(); + Pass* p = mMaterial->createTechnique()->createPass(); + p->setSceneBlending(SBT_TRANSPARENT_ALPHA); + p->setDepthCheckEnabled(false); + p->setDepthWriteEnabled(false); + p->setSelfIllumination(1.0,1.0,1.0); + p->setDiffuse(0.0,0.0,0.0,1.0); + p->setAmbient(0.0,0.0,0.0); + p->createTextureUnitState(textureName); + mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); + + bodyCount++; +} + +Moon::Moon( const String& textureName, + const float initialSize, + const Vector3& position, + SceneNode* rootNode) +{ + init(textureName, initialSize, position, rootNode); + + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + HighLevelGpuProgramPtr vshader; + if (mgr.resourceExists("Moon_VP")) + vshader = mgr.getByName("Moon_VP"); + else + vshader = mgr.createProgram("Moon_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); + vshader->setParameter("profiles", "vs_2_x arbvp1"); + vshader->setParameter("entry_point", "main_vp"); + StringUtil::StrStreamType outStream; + outStream << + "void main_vp( \n" + " float4 position : POSITION, \n" + " in float2 uv : TEXCOORD0, \n" + " out float2 oUV : TEXCOORD0, \n" + " out float4 oPosition : POSITION, \n" + " uniform float4x4 worldViewProj \n" + ") \n" + "{ \n" + " oUV = uv; \n" + " oPosition = mul( worldViewProj, position ); \n" + "}"; + vshader->setSource(outStream.str()); + vshader->load(); + vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); + mMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); + + HighLevelGpuProgramPtr fshader; + if (mgr.resourceExists("Moon_FP")) + fshader = mgr.getByName("Moon_FP"); + else + fshader = mgr.createProgram("Moon_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); + + fshader->setParameter("profiles", "ps_2_x arbfp1"); + fshader->setParameter("entry_point", "main_fp"); + StringUtil::StrStreamType outStream2; + outStream2 << + "void main_fp( \n" + " in float2 uv : TEXCOORD0, \n" + " out float4 oColor : COLOR, \n" + " uniform sampler2D texture : TEXUNIT0, \n" + " uniform float4 skyColour, \n" + " uniform float4 diffuse, \n" + " uniform float4 emissive \n" + ") \n" + "{ \n" + " float4 tex = tex2D(texture, uv); \n" + " oColor = float4(emissive.xyz,1) * tex; \n" + // use a circle for the alpha (compute UV distance to center) + // looks a bit bad because its not filtered on the edges, + // but it's cheaper than a seperate alpha texture. + " float sqrUVdist = pow(uv.x-0.5,2) + pow(uv.y-0.5, 2); \n" + " oColor.a = diffuse.a * (sqrUVdist >= 0.24 ? 0 : 1); \n" + " oColor.rgb += (1-tex.a) * oColor.a * skyColour.rgb; \n"//fill dark side of moon with skycolour + " oColor.rgb += (1-diffuse.a) * skyColour.rgb; \n"//fade bump + "}"; + fshader->setSource(outStream2.str()); + fshader->load(); + fshader->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); + fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); + mMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); + + setVisibility(1.0); + + mPhase = Moon::Phase_Full; +} + +void Moon::setType(const Moon::Type& type) +{ + mType = type; +} + +void Moon::setSkyColour(const Ogre::ColourValue& colour) +{ + mMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("skyColour", colour); +} + +void Moon::setPhase(const Moon::Phase& phase) +{ + // Colour texture + Ogre::String textureName = "textures\\tx_"; + + if (mType == Moon::Type_Secunda) textureName += "secunda_"; + else textureName += "masser_"; + + if (phase == Moon::Phase_New) textureName += "new"; + else if (phase == Moon::Phase_WaxingCrescent) textureName += "one_wax"; + else if (phase == Moon::Phase_WaxingHalf) textureName += "half_wax"; + else if (phase == Moon::Phase_WaxingGibbous) textureName += "three_wax"; + else if (phase == Moon::Phase_WaningCrescent) textureName += "one_wan"; + else if (phase == Moon::Phase_WaningHalf) textureName += "half_wan"; + else if (phase == Moon::Phase_WaningGibbous) textureName += "three_wan"; + else if (phase == Moon::Phase_Full) textureName += "full"; + + textureName += ".dds"; + + mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(textureName); + + mPhase = phase; +} + +Moon::Phase Moon::getPhase() const +{ + return mPhase; +} + +unsigned int Moon::getPhaseInt() const +{ + if (mPhase == Moon::Phase_New) return 0; + else if (mPhase == Moon::Phase_WaxingCrescent) return 1; + else if (mPhase == Moon::Phase_WaningCrescent) return 1; + else if (mPhase == Moon::Phase_WaxingHalf) return 2; + else if (mPhase == Moon::Phase_WaningHalf) return 2; + else if (mPhase == Moon::Phase_WaxingGibbous) return 3; + else if (mPhase == Moon::Phase_WaningGibbous) return 3; + else if (mPhase == Moon::Phase_Full) return 4; + + return 0; +} + +void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType) +{ + // Get the vertex colour buffer of this mesh + const Ogre::VertexElement* ves_diffuse = ent->getMesh()->getSubMesh(0)->vertexData->vertexDeclaration->findElementBySemantic( Ogre::VES_DIFFUSE ); + HardwareVertexBufferSharedPtr colourBuffer = ent->getMesh()->getSubMesh(0)->vertexData->vertexBufferBinding->getBuffer(ves_diffuse->getSource()); + + // Lock + void* pData = colourBuffer->lock(HardwareBuffer::HBL_NORMAL); + + // Iterate over all vertices + int vertex_size = colourBuffer->getVertexSize(); + float * currentVertex = NULL; + for (unsigned int i=0; igetNumVertices(); ++i) { - protected: - Caelum::CaelumSystem* mpCaelumSystem; + // Get a pointer to the vertex colour + ves_diffuse->baseVertexPointerToElement( pData, ¤tVertex ); - public: - CaelumManager (Ogre::RenderWindow* pRenderWindow, - Ogre::Camera* pCamera, - const boost::filesystem::path& resDir); - virtual ~CaelumManager (); - - virtual void enable() {} - - virtual void disable() {} - - virtual void setHour (double hour) {} - ///< will be called even when sky is disabled. - - virtual void setDate (int day, int month) {} - ///< will be called even when sky is disabled. - - virtual int getMasserPhase() const { return 0; } - ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, - /// 3 waxing or waning gibbous, 4 full moon - - virtual int getSecundaPhase() const { return 0; } - ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, - /// 3 waxing or waning gibbous, 4 full moon - - virtual void setMoonColour (bool red) {} - }; - - CaelumManager::CaelumManager (Ogre::RenderWindow* pRenderWindow, - Ogre::Camera* pCamera, - const boost::filesystem::path& resDir) - : mpCaelumSystem (NULL) - { - using namespace Ogre; - using namespace Caelum; - - assert(pCamera); - assert(pRenderWindow); - - // Load the Caelum resources - // - ResourceGroupManager::getSingleton().addResourceLocation((resDir / "caelum").string(), "FileSystem", "Caelum"); - ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); - - // Load the Caelum resources - // - Ogre::SceneManager* pScene = pCamera->getSceneManager(); - Caelum::CaelumSystem::CaelumComponent componentMask = CaelumSystem::CAELUM_COMPONENTS_DEFAULT; - mpCaelumSystem = new Caelum::CaelumSystem (Root::getSingletonPtr(), pScene, componentMask); - - // Set time acceleration. - mpCaelumSystem->getUniversalClock()->setTimeScale(128); - - // Disable fog since OpenMW is handling OGRE fog elsewhere - mpCaelumSystem->setManageSceneFog(false); - - // Change the camera far distance to make sure the sky is not clipped - pCamera->setFarClipDistance(50000); - - // Register Caelum as an OGRE listener - pRenderWindow->addListener(mpCaelumSystem); - Root::getSingletonPtr()->addFrameListener(mpCaelumSystem); - } - - CaelumManager::~CaelumManager() - { - if (mpCaelumSystem) - mpCaelumSystem->shutdown (false); - } - - /// Creates and connects the sky rendering component to OGRE. - /// - /// \return NULL on failure. - /// - SkyManager* SkyManager::create (Ogre::RenderWindow* pRenderWindow, - Ogre::Camera* pCamera, - const boost::filesystem::path& resDir) - { - SkyManager* pSkyManager = NULL; - - try + unsigned char alpha; + if (meshType == 0) alpha = i%2 ? 0 : 255; // this is a cylinder, so every second vertex belongs to the bottom-most row + else if (meshType == 1) { - pSkyManager = new CaelumManager(pRenderWindow, pCamera, resDir); - } - catch (Ogre::Exception& e) - { - std::cout << "\nOGRE Exception when attempting to add sky: " - << e.getFullDescription().c_str() << std::endl; - } - catch (std::exception& e) - { - std::cout << "\nException when attempting to add sky: " - << e.what() << std::endl; + if (i>= 49 && i <= 64) alpha = 0; // bottom-most row + else if (i>= 33 && i <= 48) alpha = 64; // second bottom-most row + else alpha = 255; } - return pSkyManager; + uint8 tmpR = static_cast(255); + uint8 tmpG = static_cast(255); + uint8 tmpB = static_cast(255); + uint8 tmpA = static_cast(alpha); + + // This does not matter since R and B are always 1. + /*VertexElementType format = Root::getSingleton().getRenderSystem()->getColourVertexElementType(); + switch (format) + { + case VET_COLOUR_ARGB: + std::swap(tmpR, tmpB); + break; + case VET_COLOUR_ABGR: + break; + default: + break; + }*/ + + // Modify + *((uint32*)currentVertex) = tmpR | (tmpG << 8) | (tmpB << 16) | (tmpA << 24); + + // Move to the next vertex + pData = static_cast (pData) + vertex_size; } -} + + // Unlock + ent->getMesh()->getSubMesh(0)->vertexData->vertexBufferBinding->getBuffer(ves_diffuse->getSource())->unlock(); +} + +SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env) : + mGlareFade(0), mGlareEnabled(false) +{ + mEnvironment = env; + mViewport = pCamera->getViewport(); + mSceneMgr = pMwRoot->getCreator(); + mRootNode = pCamera->getParentSceneNode()->createChildSceneNode(); + mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates + mRootNode->setInheritOrientation(false); + + /// \todo preload all the textures and meshes that are used for sky rendering + + // Create overlay used for thunderstorm + MaterialPtr material = MaterialManager::getSingleton().create( "ThunderMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); + Pass* pass = material->getTechnique(0)->getPass(0); + pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mThunderTextureUnit = pass->createTextureUnitState(); + mThunderTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(1.f, 1.f, 1.f)); + mThunderTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 0.5f); + OverlayManager& ovm = OverlayManager::getSingleton(); + mThunderOverlay = ovm.create( "ThunderOverlay" ); + OverlayContainer* overlay_panel; + overlay_panel = (OverlayContainer*)ovm.createOverlayElement("Panel", "ThunderPanel"); + overlay_panel->_setPosition(0, 0); + overlay_panel->_setDimensions(1, 1); + overlay_panel->setMaterialName( "ThunderMaterial" ); + overlay_panel->show(); + mThunderOverlay->add2D(overlay_panel); + mThunderOverlay->hide(); + + mSecunda = new Moon("textures\\tx_secunda_full.dds", 0.5, Vector3(-0.4, 0.4, 0.5), mRootNode); + mSecunda->setType(Moon::Type_Secunda); + mSecunda->setRenderQueue(RENDER_QUEUE_SKIES_EARLY+4); + + mMasser = new Moon("textures\\tx_masser_full.dds", 0.75, Vector3(-0.4, 0.4, 0.5), mRootNode); + mMasser->setRenderQueue(RENDER_QUEUE_SKIES_EARLY+3); + mMasser->setType(Moon::Type_Masser); + + mSun = new BillboardObject("textures\\tx_sun_05.dds", 1, Vector3(0.4, 0.4, 0.4), mRootNode); + mSunGlare = new BillboardObject("textures\\tx_sun_flash_grey_05.dds", 3, Vector3(0.4, 0.4, 0.4), mRootNode); + mSunGlare->setRenderQueue(RENDER_QUEUE_SKIES_LATE); + + + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + + // Stars + /// \todo sky_night_02.nif (available in Bloodmoon) + MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); + Entity* night1_ent = mSceneMgr->createEntity("meshes\\sky_night_01.nif"); + night1_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+1); + + mAtmosphereNight = mRootNode->createChildSceneNode(); + mAtmosphereNight->attachObject(night1_ent); + + // Stars vertex shader + HighLevelGpuProgramPtr stars_vp = mgr.createProgram("Stars_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_VERTEX_PROGRAM); + stars_vp->setParameter("profiles", "vs_2_x arbvp1"); + stars_vp->setParameter("entry_point", "main_vp"); + StringUtil::StrStreamType outStream4; + outStream4 << + "void main_vp( \n" + " float4 position : POSITION, \n" + " in float2 uv : TEXCOORD0, \n" + " out float2 oUV : TEXCOORD0, \n" + " out float oFade : TEXCOORD1, \n" + " out float4 oPosition : POSITION, \n" + " uniform float4x4 worldViewProj \n" + ") \n" + "{ \n" + " oUV = uv; \n" + " oFade = (position.z > 50) ? 1.f : 0.f; \n" + " oPosition = mul( worldViewProj, position ); \n" + "}"; + stars_vp->setSource(outStream4.str()); + stars_vp->load(); + stars_vp->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); + + // Stars fragment shader + HighLevelGpuProgramPtr stars_fp = mgr.createProgram("Stars_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_FRAGMENT_PROGRAM); + stars_fp->setParameter("profiles", "ps_2_x arbfp1"); + stars_fp->setParameter("entry_point", "main_fp"); + StringUtil::StrStreamType outStream5; + outStream5 << + "void main_fp( \n" + " in float2 uv : TEXCOORD0, \n" + " out float4 oColor : COLOR, \n" + " in float fade : TEXCOORD1, \n" + " uniform sampler2D texture : TEXUNIT0, \n" + " uniform float opacity, \n" + " uniform float4 diffuse, \n" + " uniform float4 emissive \n" + ") \n" + "{ \n" + " oColor = tex2D(texture, uv) * float4(emissive.xyz, 1) * float4(1,1,1,fade*diffuse.a); \n" + "}"; + stars_fp->setSource(outStream5.str()); + stars_fp->load(); + stars_fp->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); + stars_fp->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); + + for (unsigned int i=0; igetNumSubEntities(); ++i) + { + MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); + mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); + mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); + mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0); + mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mp->getTechnique(0)->getPass(0)->setVertexProgram(stars_vp->getName()); + mp->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName()); + mStarsMaterials[i] = mp; + } + + // Atmosphere (day) + mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); + Entity* atmosphere_ent = mSceneMgr->createEntity("meshes\\sky_atmosphere.nif"); + + ModVertexAlpha(atmosphere_ent, 0); + + atmosphere_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY); + mAtmosphereDay = mRootNode->createChildSceneNode(); + mAtmosphereDay->attachObject(atmosphere_ent); + mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); + + // Atmosphere shader + HighLevelGpuProgramPtr vshader = mgr.createProgram("Atmosphere_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_VERTEX_PROGRAM); + + vshader->setParameter("profiles", "vs_2_x arbvp1"); + vshader->setParameter("entry_point", "main_vp"); + + StringUtil::StrStreamType outStream; + outStream << + "void main_vp( \n" + " float4 position : POSITION, \n" + " in float4 color : COLOR, \n" + " out float4 oPosition : POSITION, \n" + " out float4 oColor : COLOR, \n" + " uniform float4 emissive, \n" + " uniform float4x4 worldViewProj \n" + ") \n" + "{ \n" + " oPosition = mul( worldViewProj, position ); \n" + " oColor = color * emissive; \n" + "}"; + vshader->setSource(outStream.str()); + vshader->load(); + + vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); + vshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); + + // Clouds + NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); + Entity* clouds_ent = mSceneMgr->createEntity("meshes\\sky_clouds_01.nif"); + clouds_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+5); + SceneNode* clouds_node = mRootNode->createChildSceneNode(); + clouds_node->attachObject(clouds_ent); + mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); + + // Clouds vertex shader + HighLevelGpuProgramPtr vshader2 = mgr.createProgram("Clouds_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_VERTEX_PROGRAM); + vshader2->setParameter("profiles", "vs_2_x arbvp1"); + vshader2->setParameter("entry_point", "main_vp"); + StringUtil::StrStreamType outStream3; + outStream3 << + "void main_vp( \n" + " float4 position : POSITION, \n" + " in float4 color : COLOR, \n" + " out float4 oColor : TEXCOORD1, \n" + " in float2 uv : TEXCOORD0, \n" + " out float2 oUV : TEXCOORD0, \n" + " out float4 oPosition : POSITION, \n" + " uniform float4x4 worldViewProj \n" + ") \n" + "{ \n" + " oUV = uv; \n" + " oColor = color; \n" + " oPosition = mul( worldViewProj, position ); \n" + "}"; + vshader2->setSource(outStream3.str()); + vshader2->load(); + vshader2->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); + mCloudMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader2->getName()); + + // Clouds fragment shader + mCloudFragmentShader = mgr.createProgram("Clouds_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_FRAGMENT_PROGRAM); + mCloudFragmentShader->setParameter("profiles", "ps_2_x arbfp1"); + mCloudFragmentShader->setParameter("entry_point", "main_fp"); + StringUtil::StrStreamType outStream2; + outStream2 << + "void main_fp( \n" + " in float2 uv : TEXCOORD0, \n" + " out float4 oColor : COLOR, \n" + " in float4 color : TEXCOORD1, \n" + " uniform sampler2D texture : TEXUNIT0, \n" + " uniform sampler2D secondTexture : TEXUNIT1, \n" + " uniform float transitionFactor, \n" + " uniform float time, \n" + " uniform float speed, \n" + " uniform float opacity, \n" + " uniform float4 emissive \n" + ") \n" + "{ \n" + " uv += float2(1,0) * time * speed * 0.003; \n" // Scroll in x direction + " float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n" + " oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n" + "}"; + mCloudFragmentShader->setSource(outStream2.str()); + mCloudFragmentShader->load(); + mCloudFragmentShader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); + mCloudMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(mCloudFragmentShader->getName()); + setCloudsOpacity(0.75); + + ModVertexAlpha(clouds_ent, 1); + + // I'm not sure if the materials are being used by any other objects + // Make a unique "modifiable" copy of the materials to be sure + mCloudMaterial = mCloudMaterial->clone("Clouds"); + clouds_ent->getSubEntity(0)->setMaterial(mCloudMaterial); + mAtmosphereMaterial = mAtmosphereMaterial->clone("Atmosphere"); + atmosphere_ent->getSubEntity(0)->setMaterial(mAtmosphereMaterial); + + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 0.0); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); + mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); + mCloudMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + + mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); +} + +SkyManager::~SkyManager() +{ + delete mSun; + delete mSunGlare; + delete mMasser; + delete mSecunda; +} + +int SkyManager::getMasserPhase() const +{ + return mMasser->getPhaseInt(); +} + +int SkyManager::getSecundaPhase() const +{ + return mSecunda->getPhaseInt(); +} + +void SkyManager::update(float duration) +{ + if (!mEnabled) return; + + // UV Scroll the clouds + mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstantFromTime("time", mEnvironment->mWorld->getTimeScaleFactor()/30.f); + + /// \todo improve this + mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); + mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); + + // increase the strength of the sun glare effect depending + // on how directly the player is looking at the sun + if (mSunEnabled) + { + Vector3 sun = mSunGlare->getPosition(); + sun = Vector3(sun.x, sun.z, -sun.y); + Vector3 cam = mViewport->getCamera()->getRealDirection(); + const Degree angle = sun.angleBetween( cam ); + float val = 1- (angle.valueDegrees() / 180.f); + val = (val*val*val*val)*2; + + if (mGlareEnabled) + { + mGlareFade += duration*3; + if (mGlareFade > 1) mGlareFade = 1; + } + else + { + mGlareFade -= duration*3; + if (mGlareFade < 0.3) mGlareFade = 0; + } + + mSunGlare->setSize(val * (mGlareFade)); + } + + mSunGlare->setVisible(mGlareFade>0 && mSunEnabled); + mSun->setVisible(mSunEnabled); + mMasser->setVisible(mMasserEnabled); + mSecunda->setVisible(mSecundaEnabled); + + // rotate the stars by 360 degrees every 4 days + mAtmosphereNight->roll(Degree(mEnvironment->mWorld->getTimeScaleFactor()*duration*360 / (3600*96.f))); +} + +void SkyManager::enable() +{ + mRootNode->setVisible(true); + mEnabled = true; +} + +void SkyManager::disable() +{ + mRootNode->setVisible(false); + mEnabled = false; +} + +void SkyManager::setMoonColour (bool red) +{ + mSecunda->setColour( red ? ColourValue(1.0, 0.0784, 0.0784) + : ColourValue(1.0, 1.0, 1.0)); +} + +void SkyManager::setCloudsOpacity(float opacity) +{ + mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(opacity)); +} + +void SkyManager::setWeather(const MWWorld::WeatherResult& weather) +{ + if (mClouds != weather.mCloudTexture) + { + mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("textures\\"+weather.mCloudTexture); + mClouds = weather.mCloudTexture; + } + + if (mNextClouds != weather.mNextCloudTexture) + { + mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName("textures\\"+weather.mNextCloudTexture); + mNextClouds = weather.mNextCloudTexture; + } + + if (mCloudBlendFactor != weather.mCloudBlendFactor) + { + mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("transitionFactor", Real(weather.mCloudBlendFactor)); + mCloudBlendFactor = weather.mCloudBlendFactor; + } + + if (mCloudOpacity != weather.mCloudOpacity) + { + mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(weather.mCloudOpacity)); + mCloudOpacity = weather.mCloudOpacity; + } + + if (mCloudColour != weather.mSunColor) + { + ColourValue clr( weather.mSunColor.r*0.7 + weather.mAmbientColor.r*0.7, + weather.mSunColor.g*0.7 + weather.mAmbientColor.g*0.7, + weather.mSunColor.b*0.7 + weather.mAmbientColor.b*0.7); + + mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(clr); + mCloudColour = weather.mSunColor; + } + + if (mSkyColour != weather.mSkyColor) + { + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(weather.mSkyColor); + mMasser->setSkyColour(weather.mSkyColor); + mSecunda->setSkyColour(weather.mSkyColor); + mSkyColour = weather.mSkyColor; + } + + if (mCloudSpeed != weather.mCloudSpeed) + { + mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("speed", Real(weather.mCloudSpeed)); + mCloudSpeed = weather.mCloudSpeed; + } + + if (weather.mNight && mStarsOpacity != weather.mNightFade) + { + if (weather.mNightFade == 0) + mAtmosphereNight->setVisible(false); + else + { + mAtmosphereNight->setVisible(true); + for (int i=0; i<7; ++i) + mStarsMaterials[i]->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, weather.mNightFade); + mStarsOpacity = weather.mNightFade; + } + } + + float strength; + float timeofday_angle = std::abs(mSunGlare->getPosition().z/mSunGlare->getPosition().length()); + if (timeofday_angle <= 0.44) + strength = timeofday_angle/0.44f; + else + strength = 1.f; + + mSunGlare->setVisibility(weather.mGlareView * strength); + mSun->setVisibility(strength); + + mAtmosphereNight->setVisible(weather.mNight && mEnabled); +} + +void SkyManager::setGlare(bool glare) +{ + mGlareEnabled = glare; +} + +Vector3 SkyManager::getRealSunPos() +{ + return mSun->getNode()->_getDerivedPosition(); +} + +void SkyManager::sunEnable() +{ + mSunEnabled = true; +} + +void SkyManager::sunDisable() +{ + mSunEnabled = false; +} + +void SkyManager::setSunDirection(const Vector3& direction) +{ + mSun->setPosition(direction); + mSunGlare->setPosition(direction); +} + +void SkyManager::setMasserDirection(const Vector3& direction) +{ + mMasser->setPosition(direction); +} + +void SkyManager::setSecundaDirection(const Vector3& direction) +{ + mSecunda->setPosition(direction); +} + +void SkyManager::masserEnable() +{ + mMasserEnabled = true; +} + +void SkyManager::secundaEnable() +{ + mSecundaEnabled = true; +} + +void SkyManager::masserDisable() +{ + mMasserEnabled = false; +} + +void SkyManager::secundaDisable() +{ + mSecundaEnabled = false; +} + +void SkyManager::setThunder(const float factor) +{ + if (factor > 0.f) + { + mThunderOverlay->show(); + mThunderTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, factor*0.6); + } + else + mThunderOverlay->hide(); +} + +void SkyManager::setMasserFade(const float fade) +{ + mMasser->setVisibility(fade); +} + +void SkyManager::setSecundaFade(const float fade) +{ + mSecunda->setVisibility(fade); +} + +void SkyManager::setHour(double hour) +{ + mHour = hour; +} + +void SkyManager::setDate(int day, int month) +{ + mDay = day; + mMonth = month; +} diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 446ed3f1c..bf52afd8d 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -1,40 +1,217 @@ #ifndef _GAME_RENDER_SKY_H #define _GAME_RENDER_SKY_H -#include +#include +#include +#include +#include +#include + +#include "sky.hpp" +#include "../mwworld/weather.hpp" namespace Ogre { class RenderWindow; + class SceneNode; class Camera; + class Viewport; + class SceneManager; + class Entity; + class BillboardSet; + class TextureUnitState; + class Overlay; } namespace MWRender { - /// - /// Interface for the sky rendering system - /// + class BillboardObject + { + public: + BillboardObject( const Ogre::String& textureName, + const float size, + const Ogre::Vector3& position, + Ogre::SceneNode* rootNode + ); + BillboardObject(); + + virtual ~BillboardObject() {} + + void setColour(const Ogre::ColourValue& pColour); + void setPosition(const Ogre::Vector3& pPosition); + void setVisible(const bool visible); + void setRenderQueue(unsigned int id); + void setSize(const float size); + Ogre::Vector3 getPosition() const; + + void setVisibility(const float visibility); + + Ogre::SceneNode* getNode(); + + protected: + virtual void init(const Ogre::String& textureName, + const float size, + const Ogre::Vector3& position, + Ogre::SceneNode* rootNode); + + Ogre::SceneNode* mNode; + Ogre::MaterialPtr mMaterial; + Ogre::BillboardSet* mBBSet; + }; + + + /* + * The moons need a seperate class because of their shader (which allows them to be partially transparent) + */ + class Moon : public BillboardObject + { + public: + Moon( const Ogre::String& textureName, + const float size, + const Ogre::Vector3& position, + Ogre::SceneNode* rootNode + ); + + virtual ~Moon() {} + + enum Phase + { + Phase_New = 0, + Phase_WaxingCrescent, + Phase_WaxingHalf, + Phase_WaxingGibbous, + Phase_Full, + Phase_WaningGibbous, + Phase_WaningHalf, + Phase_WaningCrescent + }; + + enum Type + { + Type_Masser = 0, + Type_Secunda + }; + + void setPhase(const Phase& phase); + void setType(const Type& type); + void setSkyColour(const Ogre::ColourValue& colour); + + Phase getPhase() const; + unsigned int getPhaseInt() const; + + private: + Type mType; + Phase mPhase; + }; + class SkyManager { public: - static SkyManager* create (Ogre::RenderWindow* pRenderWindow, - Ogre::Camera* pCamera, - const boost::filesystem::path& resDir); - virtual ~SkyManager() {} + SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env); + ~SkyManager(); - virtual void enable() = 0; + void update(float duration); - virtual void disable() = 0; + void enable(); - virtual void setHour (double hour) = 0; + void disable(); - virtual void setDate (int day, int month) = 0; + void setHour (double hour); + ///< will be called even when sky is disabled. - virtual int getMasserPhase() const = 0; + void setDate (int day, int month); + ///< will be called even when sky is disabled. - virtual int getSecundaPhase() const = 0; + int getMasserPhase() const; + ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, + /// 3 waxing or waning gibbous, 4 full moon - virtual void setMoonColour (bool red) = 0; + int getSecundaPhase() const; + ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, + /// 3 waxing or waning gibbous, 4 full moon + + void setMoonColour (bool red); + ///< change Secunda colour to red + + void setCloudsOpacity(float opacity); + ///< change opacity of the clouds + + void setWeather(const MWWorld::WeatherResult& weather); + + void sunEnable(); + + void sunDisable(); + + void setSunDirection(const Ogre::Vector3& direction); + + void setMasserDirection(const Ogre::Vector3& direction); + + void setSecundaDirection(const Ogre::Vector3& direction); + + void setMasserFade(const float fade); + + void setSecundaFade(const float fade); + + void masserEnable(); + void masserDisable(); + + void secundaEnable(); + void secundaDisable(); + + void setThunder(const float factor); + + void setGlare(bool glare); + Ogre::Vector3 getRealSunPos(); + + private: + MWWorld::Environment* mEnvironment; + float mHour; + int mDay; + int mMonth; + + BillboardObject* mSun; + BillboardObject* mSunGlare; + Moon* mMasser; + Moon* mSecunda; + + Ogre::Viewport* mViewport; + Ogre::SceneNode* mRootNode; + Ogre::SceneManager* mSceneMgr; + + Ogre::SceneNode* mAtmosphereDay; + Ogre::SceneNode* mAtmosphereNight; + + Ogre::MaterialPtr mCloudMaterial; + Ogre::MaterialPtr mAtmosphereMaterial; + + Ogre::MaterialPtr mStarsMaterials[7]; + + 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; + float mCloudBlendFactor; + float mCloudOpacity; + float mCloudSpeed; + float mStarsOpacity; + Ogre::ColourValue mCloudColour; + Ogre::ColourValue mSkyColour; + + Ogre::Overlay* mThunderOverlay; + Ogre::TextureUnitState* mThunderTextureUnit; + + float mRemainingTransitionTime; + + float mGlareFade; + + void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType); + + bool mEnabled; + bool mGlareEnabled; + bool mSunEnabled; + bool mMasserEnabled; + bool mSecundaEnabled; }; } diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 6d74ae45a..aa541e55d 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -25,13 +25,13 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Float x = runtime[0].mInteger; + Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); - Interpreter::Type_Float y = runtime[0].mInteger; + Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); - Interpreter::Type_Float z = runtime[0].mInteger; + Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. @@ -53,16 +53,16 @@ namespace MWScript std::string actor = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - Interpreter::Type_Float duration = runtime[0].mInteger; + Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); - Interpreter::Type_Float x = runtime[0].mInteger; + Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); - Interpreter::Type_Float y = runtime[0].mInteger; + Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); - Interpreter::Type_Float z = runtime[0].mInteger; + Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. @@ -99,9 +99,9 @@ namespace MWScript void registerExtensions (Compiler::Extensions& extensions) { - extensions.registerInstruction ("aitravel", "lll/l", opcodeAiTravel, + extensions.registerInstruction ("aitravel", "fff/l", opcodeAiTravel, opcodeAiTravelExplicit); - extensions.registerInstruction ("aiescort", "cllll/l", opcodeAiEscort, + extensions.registerInstruction ("aiescort", "cffff/l", opcodeAiEscort, opcodeAiEscortExplicit); extensions.registerFunction ("getaipackagedone", 'l', "", opcodeGetAiPackageDone, diff --git a/apps/openmw/mwscript/animationextensions.cpp b/apps/openmw/mwscript/animationextensions.cpp new file mode 100644 index 000000000..184be83db --- /dev/null +++ b/apps/openmw/mwscript/animationextensions.cpp @@ -0,0 +1,127 @@ + +#include "animationextensions.hpp" + +#include + +#include + +#include +#include +#include + +#include "../mwworld/world.hpp" + +#include "interpretercontext.hpp" +#include "ref.hpp" + +namespace MWScript +{ + namespace Animation + { + template + class OpSkipAnim : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + InterpreterContext& context = + static_cast (runtime.getContext()); + + context.getWorld().skipAnimation (ptr); + } + }; + + template + class OpPlayAnim : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + InterpreterContext& context = + static_cast (runtime.getContext()); + + std::string group = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer mode = 0; + + if (arg0==1) + { + mode = runtime[0].mInteger; + runtime.pop(); + + if (mode<0 || mode>2) + throw std::runtime_error ("animation mode out of range"); + } + + context.getWorld().playAnimationGroup (ptr, group, mode, 1); + } + }; + + template + class OpLoopAnim : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + InterpreterContext& context = + static_cast (runtime.getContext()); + + std::string group = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer loops = runtime[0].mInteger; + runtime.pop(); + + if (loops<0) + throw std::runtime_error ("number of animation loops must be non-negative"); + + Interpreter::Type_Integer mode = 0; + + if (arg0==1) + { + mode = runtime[0].mInteger; + runtime.pop(); + + if (mode<0 || mode>2) + throw std::runtime_error ("animation mode out of range"); + } + + context.getWorld().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); + } + } +} diff --git a/apps/openmw/mwscript/animationextensions.hpp b/apps/openmw/mwscript/animationextensions.hpp new file mode 100644 index 000000000..ff619ab73 --- /dev/null +++ b/apps/openmw/mwscript/animationextensions.hpp @@ -0,0 +1,24 @@ +#ifndef GAME_SCRIPT_ANIMATIONEXTENSIONS_H +#define GAME_SCRIPT_ANIMATIONEXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWScript +{ + namespace Animation + { + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); + } +} + +#endif diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index aef48ddec..a0aba5db9 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -11,7 +11,7 @@ #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/containerutil.hpp" +#include "../mwworld/containerstore.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -45,8 +45,7 @@ namespace MWScript ref.getPtr().getRefData().setCount (count); - MWWorld::Class::get (ref.getPtr()).insertIntoContainer (ref.getPtr(), - MWWorld::Class::get (ptr).getContainerStore (ptr)); + MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr()); } }; @@ -59,25 +58,16 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWScript::InterpreterContext& context - = static_cast (runtime.getContext()); - std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - std::vector list; - - MWWorld::listItemsInContainer (item, - MWWorld::Class::get (ptr).getContainerStore (ptr), - context.getWorld().getStore(), list); + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); Interpreter::Type_Integer sum = 0; - for (std::vector::iterator iter (list.begin()); iter!=list.end(); - ++iter) - { - sum += iter->getRefData().getCount(); - } + for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) + if (iter->getCellRef().refID==item) + sum += iter->getRefData().getCount(); runtime.push (sum); } @@ -92,9 +82,6 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWScript::InterpreterContext& context - = static_cast (runtime.getContext()); - std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -104,25 +91,23 @@ namespace MWScript if (count<0) throw std::runtime_error ("second argument for RemoveItem must be non-negative"); - std::vector list; + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); - MWWorld::listItemsInContainer (item, - MWWorld::Class::get (ptr).getContainerStore (ptr), - context.getWorld().getStore(), list); - - for (std::vector::iterator iter (list.begin()); - iter!=list.end() && count; + for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; ++iter) { - if (iter->getRefData().getCount()<=count) + if (iter->getCellRef().refID==item) { - count -= iter->getRefData().getCount(); - iter->getRefData().setCount (0); - } - else - { - iter->getRefData().setCount (iter->getRefData().getCount()-count); - count = 0; + if (iter->getRefData().getCount()<=count) + { + count -= iter->getRefData().getCount(); + iter->getRefData().setCount (0); + } + else + { + iter->getRefData().setCount (iter->getRefData().getCount()-count); + count = 0; + } } } diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 893af259f..384e13e45 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -1,5 +1,5 @@ -#include "statsextensions.hpp" +#include "controlextensions.hpp" #include @@ -48,7 +48,7 @@ namespace MWScript bool enabled = context.getWorld().toggleCollisionMode(); - context.report (enabled ? "Collsion -> On" : "Collision -> Off"); + context.report (enabled ? "Collision -> On" : "Collision -> Off"); } }; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 33ce38cdd..09b0c0482 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -19,7 +19,11 @@ op 0x20002: AiEscort op 0x20003: AiEscort, explicit reference op 0x20004: Lock op 0x20005: Lock, explicit reference -opcodes 0x20006-0x3ffff unused +op 0x20006: PlayAnim +op 0x20007: PlayAnim, explicit reference +op 0x20008: LoopAnim +op 0x20009: LoopAnim, explicit reference +opcodes 0x2000a-0x3ffff unused Segment 4: (not implemented yet) @@ -109,4 +113,12 @@ op 0x2000134: SetJournalIndex op 0x2000135: GetJournalIndex op 0x2000136: GetPCCell op 0x2000137: GetButtonPressed -opcodes 0x2000138-0x3ffffff unused +op 0x2000138: SkipAnim +op 0x2000139: SkipAnim, expplicit reference +op 0x200013b: twf +op 0x200013c: FadeIn +op 0x200013d: FadeOut +op 0x200013e: FadeTo +op 0x200013f: GetCurrentWeather +op 0x2000140: ChangeWeather +opcodes 0x2000141-0x3ffffff unused diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp index 86161d2b1..197494146 100644 --- a/apps/openmw/mwscript/extensions.cpp +++ b/apps/openmw/mwscript/extensions.cpp @@ -14,6 +14,7 @@ #include "aiextensions.hpp" #include "controlextensions.hpp" #include "dialogueextensions.hpp" +#include "animationextensions.hpp" namespace MWScript { @@ -29,6 +30,7 @@ namespace MWScript Ai::registerExtensions (extensions); Control::registerExtensions (extensions); Dialogue::registerExtensions (extensions); + Animation::registerExtensions (extensions); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -44,5 +46,6 @@ namespace MWScript Ai::installOpcodes (interpreter); Control::installOpcodes (interpreter); Dialogue::installOpcodes (interpreter); + Animation::installOpcodes (interpreter); } } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d8dfbdde4..5bfffd3a2 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -103,7 +103,75 @@ namespace MWScript context.getWorld().toggleRenderMode (MWWorld::World::Render_CollisionDebug); context.report (enabled ? - "Collsion Mesh Rendering -> On" : "Collision Mesh Rendering -> Off"); + "Collision Mesh Rendering -> On" : "Collision Mesh Rendering -> Off"); + } + }; + + class OpToggleWireframe : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + bool enabled = + context.getWorld().toggleRenderMode (MWWorld::World::Render_Wireframe); + + context.report (enabled ? + "Wireframe Rendering -> On" : "Wireframe Rendering -> Off"); + } + }; + + class OpFadeIn : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + Interpreter::Type_Float time = runtime[0].mFloat; + runtime.pop(); + + context.getWorld().getFader()->fadeIn(time); + } + }; + + class OpFadeOut : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + Interpreter::Type_Float time = runtime[0].mFloat; + runtime.pop(); + + context.getWorld().getFader()->fadeOut(time); + } + }; + + class OpFadeTo : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + Interpreter::Type_Float alpha = runtime[0].mFloat; + runtime.pop(); + + Interpreter::Type_Float time = runtime[0].mFloat; + runtime.pop(); + + context.getWorld().getFader()->fadeTo(alpha, time); } }; @@ -115,6 +183,10 @@ namespace MWScript const int opcodeUnlock = 0x200008c; const int opcodeUnlockExplicit = 0x200008d; const int opcodeToggleCollisionDebug = 0x2000132; + const int opcodeToggleWireframe = 0x200013b; + const int opcodeFadeIn = 0x200013c; + const int opcodeFadeOut = 0x200013d; + const int opcodeFadeTo = 0x200013e; void registerExtensions (Compiler::Extensions& extensions) { @@ -127,6 +199,11 @@ namespace MWScript extensions.registerInstruction ("togglecollisiongrid", "", opcodeToggleCollisionDebug); extensions.registerInstruction ("tcb", "", opcodeToggleCollisionDebug); 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); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -139,6 +216,10 @@ namespace MWScript interpreter.installSegment5 (opcodeUnlock, new OpUnlock); interpreter.installSegment5 (opcodeUnlockExplicit, new OpUnlock); interpreter.installSegment5 (opcodeToggleCollisionDebug, new OpToggleCollisionDebug); + interpreter.installSegment5 (opcodeToggleWireframe, new OpToggleWireframe); + interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn); + interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut); + interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo); } } } diff --git a/apps/openmw/mwscript/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index caa07c095..a5cc9e213 100644 --- a/apps/openmw/mwscript/skyextensions.cpp +++ b/apps/openmw/mwscript/skyextensions.cpp @@ -79,12 +79,46 @@ namespace MWScript runtime.push (context.getWorld().getSecundaPhase()); } }; + + class OpGetCurrentWeather : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + runtime.push (context.getWorld().getCurrentWeather()); + } + }; + + class OpChangeWeather : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + std::string region = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer id = runtime[0].mInteger; + runtime.pop(); + + context.getWorld().changeWeather(region, id); + } + }; 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) { @@ -92,8 +126,10 @@ namespace MWScript 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); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -103,6 +139,8 @@ namespace MWScript 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); } } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index ca82830d9..0e97a39cf 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -10,6 +10,7 @@ #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" #include "ref.hpp" diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 7390e4c5c..226796603 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -4,15 +4,12 @@ #include #include -using namespace std; - #include #include #include #include -#include #include #include "../mwworld/environment.hpp" @@ -46,7 +43,6 @@ using namespace std; using namespace Mangle::Sound; typedef OEngine::Sound::SoundManager OEManager; -typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; // Set the position on a sound based on a Ptr. static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) @@ -61,158 +57,69 @@ static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) namespace MWSound { - struct SoundManager::SoundImpl - { - /* This is the sound manager. It loades, stores and deletes - sounds based on the sound factory it is given. - */ - OEManagerPtr mgr; - SoundPtr music; - /* This class calls update() on the sound manager each frame - using and Ogre::FrameListener - */ - Mangle::Sound::OgreOutputUpdater updater; - - /* This class tracks the movement of an Ogre::Camera and moves - a sound listener automatically to follow it. - */ - Mangle::Sound::OgreListenerMover cameraTracker; - - const ESMS::ESMStore &store; - - typedef std::map IDMap; - typedef std::map PtrMap; - PtrMap sounds; - - // This is used for case insensitive and slash-type agnostic file - // finding. It takes DOS paths (any case, \\ slashes or / slashes) - // relative to the sound dir, and translates them into full paths - // of existing files in the filesystem, if they exist. - bool FSstrict; - FileFinder::FileFinder files; - FileFinder::FileFinderStrict strict; - FileFinder::FileFinder musicpath; - FileFinder::FileFinderStrict musicpathStrict; - - SoundImpl(Ogre::Root *root, Ogre::Camera *camera, - const ESMS::ESMStore &str, - const std::string &soundDir, const std::string &musicDir, bool fsstrict) - : mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) - , updater(mgr) - , cameraTracker(mgr) - , store(str) - , files(soundDir), strict(soundDir) - ,musicpath(musicDir), musicpathStrict(musicDir) + SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, + const Files::PathContainer& dataDirs, + bool useSound, bool fsstrict, MWWorld::Environment& environment) + : mFSStrict(fsstrict) + , mEnvironment(environment) + , mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) + , updater(mgr) + , cameraTracker(mgr) + , mCurrentPlaylist(NULL) + , mUsingSound(useSound) { - FSstrict = fsstrict; - cout << "Sound output: " << SOUND_OUT << endl; - cout << "Sound decoder: " << SOUND_IN << endl; - // Attach the camera to the camera tracker - cameraTracker.followCamera(camera); - - // Tell Ogre to update the sound system each frame - root->addFrameListener(&updater); - } - - ~SoundImpl() - { - Ogre::Root::getSingleton().removeFrameListener(&updater); - cameraTracker.unfollowCamera(); - } - - - - static std::string toMp3(std::string str) - { - std::string::size_type i = str.rfind('.'); - if(str.find('/', i) == std::string::npos && - str.find('\\', i) == std::string::npos) - str = str.substr(0, i) + ".mp3"; - else - str += ".mp3"; - return str; - } - - bool hasFile(const std::string &str, bool music = false) - { - if(FSstrict == false) + if(useSound) { - if(music) - { - if(musicpath.has(str)) return true; + // The music library will accept these filetypes + // If none is given then it will accept all filetypes + std::vector acceptableExtensions; + acceptableExtensions.push_back(".mp3"); + acceptableExtensions.push_back(".wav"); + acceptableExtensions.push_back(".ogg"); + acceptableExtensions.push_back(".flac"); - // Not found? Try with .mp3 - return musicpath.has(toMp3(str)); - } - else + // Makes a list of all sound files, searches in reverse for priority reasons + for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it) { - if(files.has(str)) return true; - return files.has(toMp3(str)); + Files::FileLister(*it / std::string("Sound"), mSoundFiles, true); } + + // Makes a FileLibrary of all music files, searches in reverse for priority reasons + for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it) + { + mMusicLibrary.add(*it / std::string("Music"), true, mFSStrict, acceptableExtensions); + } + + std::string anything = "anything"; // anything is better that a segfault + mCurrentPlaylist = mMusicLibrary.section(anything, mFSStrict); // now points to an empty path + + std::cout << "Sound output: " << SOUND_OUT << std::endl; + std::cout << "Sound decoder: " << SOUND_IN << std::endl; + // Attach the camera to the camera tracker + cameraTracker.followCamera(camera); + + // Tell Ogre to update the sound system each frame + root->addFrameListener(&updater); } - else - { - if(music) - { - if(musicpathStrict.has(str)) return true; + } - // Not found? Try with .mp3 - return musicpathStrict.has(toMp3(str)); - } - else - { - if(strict.has(str)) return true; - return strict.has(toMp3(str)); - } - } - } - - // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path - // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) - std::string convertPath(const std::string &str, bool music = false) + SoundManager::~SoundManager() { - if(FSstrict == false) + if(mUsingSound) { - // Search and return - if(music && musicpath.has(str)) - return musicpath.lookup(str); - else if(files.has(str)) - return files.lookup(str); - - // Try mp3 if the wav wasn't found - std::string mp3 = toMp3(str); - if(music && musicpath.has(mp3)) - return musicpath.lookup(mp3); - else if(files.has(mp3)) - return files.lookup(mp3); + Ogre::Root::getSingleton().removeFrameListener(&updater); + cameraTracker.unfollowCamera(); } - else - { - if(music && musicpathStrict.has(str)) - return musicpathStrict.lookup(str); - else if(strict.has(str)) - return strict.lookup(str); - - // Try mp3 if the wav wasn't found - std::string mp3 = toMp3(str); - if(music && musicpathStrict.has(mp3)) - return musicpathStrict.lookup(mp3); - else if(strict.has(str)) - return strict.lookup(mp3); - } - - // Give up - return ""; } // Convert a soundId to file name, and modify the volume // according to the sounds local volume setting, minRange and // maxRange. - std::string lookup(const std::string &soundId, + std::string SoundManager::lookup(const std::string &soundId, float &volume, float &min, float &max) { - const ESM::Sound *snd = store.sounds.search(soundId); + const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId); if(snd == NULL) return ""; if(snd->data.volume == 0) @@ -233,16 +140,16 @@ namespace MWSound max = std::max(min, max); } - return convertPath(snd->sound); + return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict, false); } // Add a sound to the list and play it - void add(const std::string &file, + void SoundManager::add(const std::string &file, MWWorld::Ptr ptr, const std::string &id, float volume, float pitch, float min, float max, - bool loop) + bool loop, bool untracked) { try { @@ -254,17 +161,20 @@ namespace MWSound setPos(snd, ptr); snd->play(); - sounds[ptr][id] = WSoundPtr(snd); + if (!untracked) + { + sounds[ptr][id] = WSoundPtr(snd); + } } catch(...) { - cout << "Error loading " << file << ", skipping.\n"; + std::cout << "Error loading " << file << ", skipping.\n"; } } // Clears all the sub-elements of a given iterator, and then // removes it from 'sounds'. - void clearAll(PtrMap::iterator it) + void SoundManager::clearAll(PtrMap::iterator& it) { IDMap::iterator sit = it->second.begin(); @@ -285,7 +195,7 @@ namespace MWSound // Stop a sound and remove it from the list. If id="" then // remove the entire object and stop all its sounds. - void remove(MWWorld::Ptr ptr, const std::string &id = "") + void SoundManager::remove(MWWorld::Ptr ptr, const std::string &id) { PtrMap::iterator it = sounds.find(ptr); if(it != sounds.end()) @@ -308,7 +218,7 @@ namespace MWSound } } - bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const + bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const { PtrMap::const_iterator it = sounds.find(ptr); if(it != sounds.end()) @@ -332,7 +242,7 @@ namespace MWSound } // Remove all references to objects belonging to a given cell - void removeCell(const MWWorld::Ptr::CellStore *cell) + void SoundManager::removeCell(const MWWorld::Ptr::CellStore *cell) { PtrMap::iterator it2, it = sounds.begin(); while(it != sounds.end()) @@ -344,7 +254,7 @@ namespace MWSound } } - void updatePositions(MWWorld::Ptr ptr) + void SoundManager::updatePositions(MWWorld::Ptr ptr) { // Find the reference (if any) PtrMap::iterator it = sounds.find(ptr); @@ -362,213 +272,234 @@ namespace MWSound } } } - }; - void SoundManager::streamMusicFull (const std::string& filename) + void SoundManager::stopMusic() + { + if (music) + music->stop(); + setPlaylist(); + } + + + void SoundManager::streamMusicFull(const std::string& filename) { - if(!mData) return; - // Play the sound and tell it to stream, if possible. TODO: // Store the reference, the jukebox will need to check status, // control volume etc. - if (mData->music) - mData->music->stop(); - mData->music = mData->mgr->load(filename); - mData->music->setStreaming(true); - mData->music->setVolume(0.4); - mData->music->play(); + if (music) + music->stop(); + music = mgr->load(filename); + music->setStreaming(true); + music->setVolume(0.4); + music->play(); } - SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, - const ESMS::ESMStore &store, - boost::filesystem::path dataDir, - bool useSound, bool fsstrict, MWWorld::Environment& environment) - : mData(NULL), fsStrict (fsstrict), mEnvironment (environment) - { - MP3Lookup(dataDir / "Music/Explore/"); - if(useSound) - mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string(), (dataDir / "Music").string(), fsstrict); - - - test.name = ""; - total = 0; - - - } - - SoundManager::~SoundManager() - { - if(mData) - delete mData; - } - void SoundManager::streamMusic(const std::string& filename) { - if(mData->hasFile(filename, true)) + std::string filePath = mMusicLibrary.locate(filename, mFSStrict, true).string(); + if(!filePath.empty()) { - std::string fullpath = mData->convertPath(filename, true); - streamMusicFull(fullpath); + streamMusicFull(filePath); } } - - void SoundManager::MP3Lookup(boost::filesystem::path dir) -{ - boost::filesystem::directory_iterator dir_iter(dir), dir_end; - - std::string mp3extension = ".mp3"; - for(;dir_iter != dir_end; dir_iter++) - { - if(boost::filesystem::extension(*dir_iter) == mp3extension) - { - files.push_back(*dir_iter); - } - } -} - void SoundManager::startRandomTitle() -{ - std::vector::iterator fileIter; - - if(files.size() > 0) + { + if(mCurrentPlaylist && !mCurrentPlaylist->empty()) { - fileIter = files.begin(); - srand ( time(NULL) ); - int r = rand() % files.size() + 1; //old random code + Files::PathContainer::const_iterator fileIter = mCurrentPlaylist->begin(); + srand( time(NULL) ); + int r = rand() % mCurrentPlaylist->size() + 1; //old random code - for(int i = 1; i < r; i++) - { - fileIter++; - } + std::advance(fileIter, r - 1); std::string music = fileIter->string(); + std::cout << "Playing " << music << "\n"; + try { - std::cout << "Playing " << music << "\n"; streamMusicFull(music); } - catch(std::exception &e) + catch (std::exception &e) { std::cout << " Music Error: " << e.what() << "\n"; } } -} - + } bool SoundManager::isMusicPlaying() { bool test = false; - if(mData && mData->music) + if(music) { - test = mData->music->isPlaying(); + test = music->isPlaying(); } return test; } - SoundManager::SoundImpl SoundManager::getMData() - { - // bool test = mData->music->isPlaying(); - return *mData; - } + bool SoundManager::setPlaylist(std::string playlist) + { + const Files::PathContainer* previousPlaylist; + previousPlaylist = mCurrentPlaylist; + if (playlist == "") + { + mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict); + } + else if(mMusicLibrary.containsSection(playlist, mFSStrict)) + { + mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict); + } + else + { + std::cout << "Warning: playlist named " << playlist << " does not exist.\n"; + } + return previousPlaylist == mCurrentPlaylist; + } + void SoundManager::playPlaylist(std::string playlist) + { + if (!mUsingSound) + return; + if (playlist == "") + { + if(!isMusicPlaying()) + { + startRandomTitle(); + } + return; + } + + if(!setPlaylist(playlist)) + { + startRandomTitle(); + } + else if (!isMusicPlaying()) + { + startRandomTitle(); + } + } void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename) { + if (!mUsingSound) + return; + // The range values are not tested - if(!mData) return; - if(mData->hasFile(filename)) - mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 20000, false); + std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict, true); + if(!filePath.empty()) + add(filePath, ptr, "_say_sound", 1, 1, 100, 20000, false); else - cout << "Sound file " << filename << " not found, skipping.\n"; + std::cout << "Sound file " << filename << " not found, skipping.\n"; } bool SoundManager::sayDone (MWWorld::Ptr ptr) const { - if(!mData) return false; - return !mData->isPlaying(ptr, "_say_sound"); + return !isPlaying(ptr, "_say_sound"); } - void SoundManager::playSound (const std::string& soundId, float volume, float pitch) + void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop) { - if(!mData) return; - // Play and forget float min, max; - const std::string &file = mData->lookup(soundId, volume, min, max); - if(file != "") - { - SoundPtr snd = mData->mgr->load(file); + const std::string &file = lookup(soundId, volume, min, max); + if (file != "") + { + SoundPtr snd = mgr->load(file); + snd->setRepeat(loop); snd->setVolume(volume); snd->setRange(min,max); snd->setPitch(pitch); + snd->setRelative(true); snd->play(); - } + + if (loop) + { + // Only add the looping sound once + IDMap::iterator it = mLoopedSounds.find(soundId); + if(it == mLoopedSounds.end()) + { + mLoopedSounds[soundId] = WSoundPtr(snd); + } + } + } } void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId, - float volume, float pitch, bool loop) + float volume, float pitch, bool loop, bool untracked) { - if(!mData) return; - // Look up the sound in the ESM data float min, max; - const std::string &file = mData->lookup(soundId, volume, min, max); - if(file != "") - mData->add(file, ptr, soundId, volume, pitch, min, max, loop); + const std::string &file = lookup(soundId, volume, min, max); + if (file != "") + add(file, ptr, soundId, volume, pitch, min, max, loop, untracked); } void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId) { - if(!mData) return; - mData->remove(ptr, soundId); + remove(ptr, soundId); } void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell) { - if(!mData) return; - mData->removeCell(cell); + removeCell(cell); } + void SoundManager::stopSound(const std::string& soundId) + { + IDMap::iterator it = mLoopedSounds.find(soundId); + if(it != mLoopedSounds.end()) + { + SoundPtr snd = it->second.lock(); + if(snd) snd->stop(); + mLoopedSounds.erase(it); + } + } + bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const { // Mark all sounds as playing, otherwise the scripts will just // keep trying to play them every frame. - if(!mData) return true; - return mData->isPlaying(ptr, soundId); + return isPlaying(ptr, soundId); } void SoundManager::updateObject(MWWorld::Ptr ptr) { - if(!mData) return; - mData->updatePositions(ptr); + updatePositions(ptr); } void SoundManager::update (float duration) { - std::string effect; - MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell(); + static int total = 0; + static std::string regionName = ""; + static float timePassed = 0.0; + timePassed += duration; //If the region has changed - if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){ - timer.restart(); - if (test.name != current->cell->region) + if(!(current->cell->data.flags & current->cell->Interior) && timePassed >= 10) + { + + ESM::Region test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region)); + + timePassed = 0; + if (regionName != current->cell->region) { + regionName = current->cell->region; total = 0; - test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region)); } if(test.soundList.size() > 0) { std::vector::iterator soundIter = test.soundList.begin(); //mEnvironment.mSoundManager - if(total == 0){ - while (!(soundIter == test.soundList.end())) + if(total == 0) + { + while (soundIter != test.soundList.end()) { - ESM::NAME32 go = soundIter->sound; int chance = (int) soundIter->chance; + //ESM::NAME32 go = soundIter->sound; //std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; soundIter++; total += chance; @@ -578,21 +509,19 @@ namespace MWSound int r = rand() % total; //old random code int pos = 0; soundIter = test.soundList.begin(); - while (!(soundIter == test.soundList.end())) + while (soundIter != test.soundList.end()) { - const ESM::NAME32 go = soundIter->sound; + const std::string go = soundIter->sound.toString(); int chance = (int) soundIter->chance; //std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; soundIter++; if( r - pos < chance) { - effect = go.name; //play sound - std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; - mEnvironment.mSoundManager->playSound(effect, 20.0, 1.0); + std::cout << "Sound: " << go <<" Chance:" << chance << "\n"; + mEnvironment.mSoundManager->playSound(go, 20.0, 1.0); break; - } pos += chance; } @@ -600,7 +529,7 @@ namespace MWSound } else if(current->cell->data.flags & current->cell->Interior) { - test.name = ""; + regionName = ""; } } diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 7dff16c76..dcf64b90c 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -2,14 +2,16 @@ #define GAME_SOUND_SOUNDMANAGER_H #include -#include -#include -#include "../mwworld/ptr.hpp" +#include +#include + #include +#include + +#include "../mwworld/ptr.hpp" -#include namespace Ogre { @@ -17,11 +19,16 @@ namespace Ogre class Camera; } -namespace ESMS +namespace Mangle { - struct ESMStore; + namespace Sound + { + typedef boost::shared_ptr SoundPtr; + } } +typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; + namespace MWWorld { struct Environment; @@ -29,43 +36,96 @@ namespace MWWorld namespace MWSound { - //SoundPtr *music; class SoundManager { - // Hide implementation details - engine.cpp is compiling - // enough as it is. - struct SoundImpl; - SoundImpl *mData; - std::vector files; - bool fsStrict; + // This is used for case insensitive and slash-type agnostic file + // finding. It takes DOS paths (any case, \\ slashes or / slashes) + // relative to the sound dir, and translates them into full paths + // of existing files in the filesystem, if they exist. + bool mFSStrict; + MWWorld::Environment& mEnvironment; - int total; - ESM::Region test; - boost::timer timer; - void streamMusicFull (const std::string& filename); ///< Play a soundifle /// \param absolute filename + /* This is the sound manager. It loades, stores and deletes + sounds based on the sound factory it is given. + */ + OEManagerPtr mgr; + Mangle::Sound::SoundPtr music; + + /* This class calls update() on the sound manager each frame + using and Ogre::FrameListener + */ + Mangle::Sound::OgreOutputUpdater updater; + + /* This class tracks the movement of an Ogre::Camera and moves + a sound listener automatically to follow it. + */ + Mangle::Sound::OgreListenerMover cameraTracker; + + typedef std::map IDMap; + typedef std::map PtrMap; + PtrMap sounds; + + // A list of all sound files used to lookup paths + Files::PathContainer mSoundFiles; + + // A library of all Music file paths stored by the folder they are contained in + Files::FileLibrary mMusicLibrary; + + // Points to the current playlist of music files stored in the music library + const Files::PathContainer* mCurrentPlaylist; + + IDMap mLoopedSounds; + + bool mUsingSound; + + std::string lookup(const std::string &soundId, + float &volume, float &min, float &max); + void add(const std::string &file, + MWWorld::Ptr ptr, const std::string &id, + float volume, float pitch, float min, float max, + bool loop, bool untracked=false); + void clearAll(PtrMap::iterator& it); + void remove(MWWorld::Ptr ptr, const std::string &id = ""); + bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const; + void removeCell(const MWWorld::Ptr::CellStore *cell); + void updatePositions(MWWorld::Ptr ptr); + public: - SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, - boost::filesystem::path dataDir, bool useSound, bool fsstrict, + SoundManager(Ogre::Root*, Ogre::Camera*, + const Files::PathContainer& dataDir, bool useSound, bool fsstrict, MWWorld::Environment& environment); ~SoundManager(); + void stopMusic(); + ///< Stops music if it's playing + void streamMusic(const std::string& filename); ///< Play a soundifle /// \param filename name of a sound file in "Music/" in the data directory. void startRandomTitle(); - void MP3Lookup(boost::filesystem::path dir); + ///< Starts a random track from the current playlist bool isMusicPlaying(); + ///< Returns true if music is playing - SoundImpl getMData(); + bool setPlaylist(std::string playlist=""); + ///< Set the playlist to an existing folder + /// \param name of the folder that contains the playlist + /// if none is set then it is set to an empty playlist + /// \return Return true if the previous playlist was the same + + void playPlaylist(std::string playlist=""); + ///< Start playing music from the selected folder + /// \param name of the folder that contains the playlist + /// if none is set then it plays from the current playlist void say (MWWorld::Ptr reference, const std::string& filename); ///< Make an actor say some text. @@ -74,11 +134,11 @@ namespace MWSound bool sayDone (MWWorld::Ptr reference) const; ///< Is actor not speaking? - void playSound (const std::string& soundId, float volume, float pitch); + void playSound (const std::string& soundId, float volume, float pitch, bool loop=false); ///< Play a sound, independently of 3D-position void playSound3D (MWWorld::Ptr reference, const std::string& soundId, - float volume, float pitch, bool loop); + float volume, float pitch, bool loop, bool untracked=false); ///< Play a sound from an object void stopSound3D (MWWorld::Ptr reference, const std::string& soundId = ""); @@ -88,6 +148,9 @@ namespace MWSound void stopSound (MWWorld::Ptr::CellStore *cell); ///< Stop all sounds for the given cell. + void stopSound(const std::string& soundId); + ///< Stop a non-3d looping sound + bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index b318f0796..bbe954049 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -4,6 +4,7 @@ #include "class.hpp" #include "environment.hpp" #include "world.hpp" +#include "containerstore.hpp" namespace MWWorld { @@ -14,8 +15,7 @@ namespace MWWorld // insert into player's inventory MWWorld::Ptr player = environment.mWorld->getPtr ("player", true); - MWWorld::Class::get (mObject).insertIntoContainer (mObject, - MWWorld::Class::get (player).getContainerStore (player)); + MWWorld::Class::get (player).getContainerStore (player).add (mObject); // remove from world environment.mWorld->deleteObject (mObject); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 143ce557b..8c657a5c8 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -5,6 +5,8 @@ #include #include "world.hpp" +#include "class.hpp" +#include "containerstore.hpp" MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { @@ -35,6 +37,39 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) } } +void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) +{ + for (ESMS::CellRefList::List::iterator iter ( + cellStore.containers.list.begin()); + iter!=cellStore.containers.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } + + for (ESMS::CellRefList::List::iterator iter ( + cellStore.creatures.list.begin()); + iter!=cellStore.creatures.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } + + for (ESMS::CellRefList::List::iterator iter ( + cellStore.npcs.list.begin()); + iter!=cellStore.npcs.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } +} + MWWorld::Cells::Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world) : mStore (store), mReader (reader), mWorld (world) {} @@ -43,6 +78,8 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) std::map, Ptr::CellStore>::iterator result = mExteriors.find (std::make_pair (x, y)); + bool fill = false; + if (result==mExteriors.end()) { const ESM::Cell *cell = mStore.cells.searchExt (x, y); @@ -64,9 +101,15 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) result = mExteriors.insert (std::make_pair ( std::make_pair (x, y), Ptr::CellStore (cell))).first; - result->second.load (mStore, mReader); + fill = true; } + if (result->second.mState!=Ptr::CellStore::State_Loaded) + result->second.load (mStore, mReader); + + if (fill) + fillContainers (result->second); + return &result->second; } @@ -74,15 +117,23 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) { std::map::iterator result = mInteriors.find (name); + bool fill = false; + if (result==mInteriors.end()) { const ESM::Cell *cell = mStore.cells.findInt (name); result = mInteriors.insert (std::make_pair (name, Ptr::CellStore (cell))).first; - result->second.load (mStore, mReader); + fill = true; } + if (result->second.mState!=Ptr::CellStore::State_Loaded) + result->second.load (mStore, mReader); + + if (fill) + fillContainers (result->second); + return &result->second; } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0ecbd02d3..42aa1050c 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -34,6 +34,8 @@ namespace MWWorld Ptr::CellStore *getCellStore (const ESM::Cell *cell); + void fillContainers (Ptr::CellStore& cellStore); + public: Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index b4f93576a..9d766909f 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -7,6 +7,7 @@ #include "ptr.hpp" #include "nullaction.hpp" +#include "containerstore.hpp" namespace MWWorld { @@ -71,14 +72,14 @@ namespace MWWorld return boost::shared_ptr (new NullAction); } - ContainerStore& Class::getContainerStore (const Ptr& ptr) const + ContainerStore& Class::getContainerStore (const Ptr& ptr) const { throw std::runtime_error ("class does not have a container store"); } - void Class::insertIntoContainer (const Ptr& ptr, ContainerStore& containerStore) const + InventoryStore& Class::getInventoryStore (const Ptr& ptr) const { - throw std::runtime_error ("class does not support inserting into a container"); + throw std::runtime_error ("class does not have an inventory store"); } void Class::lock (const Ptr& ptr, int lockLevel) const @@ -126,6 +127,16 @@ namespace MWWorld return Ogre::Vector3 (0, 0, 0); } + std::pair, bool> Class::getEquipmentSlots (const Ptr& ptr) const + { + return std::make_pair (std::vector(), false); + } + + int Class::getEquipmentSkill (const Ptr& ptr, const Environment& environment) const + { + return -1; + } + const Class& Class::get (const std::string& key) { std::map >::const_iterator iter = sClasses.find (key); @@ -145,4 +156,14 @@ namespace MWWorld { sClasses.insert (std::make_pair (key, instance)); } + + std::string Class::getUpSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const + { + throw std::runtime_error ("class does not have an up sound"); + } + + std::string Class::getDownSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const + { + throw std::runtime_error ("class does not have an down sound"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 706b11aaa..67320b3e0 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -3,15 +3,16 @@ #include #include +#include #include #include "action.hpp" -#include "containerstore.hpp" #include "refdata.hpp" -#include "../mwrender/renderinginterface.hpp" #include "physicssystem.hpp" +#include "../mwrender/renderinginterface.hpp" + namespace Ogre { class Vector3; @@ -33,6 +34,8 @@ namespace MWWorld { class Ptr; class Environment; + class ContainerStore; + class InventoryStore; /// \brief Base class for referenceable esm records class Class @@ -61,8 +64,6 @@ namespace MWWorld ///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) - - virtual void insertObjectRendering (const Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObject(const Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const; ///< Add reference into a cell for rendering (default implementation: don't render anything). @@ -105,14 +106,13 @@ namespace MWWorld ///< Generate action for using via inventory menu (default implementation: return a /// null action). - virtual ContainerStore& getContainerStore (const Ptr& ptr) const; + virtual ContainerStore& getContainerStore (const Ptr& ptr) const; ///< Return container store or throw an exception, if class does not have a /// container store (default implementation: throw an exceoption) - virtual void insertIntoContainer (const Ptr& ptr, ContainerStore& containerStore) - const; - ///< Insert into a container or throw an exception, if class does not support inserting into - /// a container. + virtual InventoryStore& getInventoryStore (const Ptr& ptr) const; + ///< Return inventory store or throw an exception, if class does not have a + /// inventory store (default implementation: throw an exceoption) virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) @@ -143,6 +143,18 @@ namespace MWWorld ///< Return desired movement vector (determined based on movement settings, /// stance and stats). + 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? + /// + /// Default implementation: return (empty vector, false). + + virtual int getEquipmentSkill (const Ptr& ptr, const Environment& environment) + const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + /// (default implementation: return -1) + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. @@ -150,6 +162,14 @@ namespace MWWorld ///< 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 MWWorld::Environment& environment) 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) + + virtual std::string getDownSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const; + ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval + /// (default implementation: throw an exception) }; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp new file mode 100644 index 000000000..c498cabce --- /dev/null +++ b/apps/openmw/mwworld/containerstore.cpp @@ -0,0 +1,387 @@ + +#include "containerstore.hpp" + +#include +#include +#include + +#include + +#include "manualref.hpp" + +MWWorld::ContainerStore::~ContainerStore() {} + +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask) +{ + return ContainerStoreIterator (mask, this); +} + +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() +{ + return ContainerStoreIterator (this); +} + +void MWWorld::ContainerStore::add (const Ptr& ptr) +{ + /// \todo implement item stacking + + switch (getType (ptr)) + { + case Type_Potion: potions.list.push_back (*ptr.get()); break; + case Type_Apparatus: appas.list.push_back (*ptr.get()); break; + case Type_Armor: armors.list.push_back (*ptr.get()); break; + case Type_Book: books.list.push_back (*ptr.get()); break; + case Type_Clothing: clothes.list.push_back (*ptr.get()); break; + case Type_Ingredient: ingreds.list.push_back (*ptr.get()); break; + case Type_Light: lights.list.push_back (*ptr.get()); break; + case Type_Lockpick: lockpicks.list.push_back (*ptr.get()); break; + case Type_Miscellaneous: miscItems.list.push_back (*ptr.get()); break; + case Type_Probe: probes.list.push_back (*ptr.get()); break; + case Type_Repair: repairs.list.push_back (*ptr.get()); break; + case Type_Weapon: weapons.list.push_back (*ptr.get()); break; + } +} + +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const ESMS::ESMStore& store) +{ + for (std::vector::const_iterator iter (items.list.begin()); iter!=items.list.end(); + ++iter) + { + ManualRef ref (store, iter->item.toString()); + + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + { + /// \todo implement leveled item lists + continue; + } + + ref.getPtr().getRefData().setCount (iter->count); + add (ref.getPtr()); + } +} + +void MWWorld::ContainerStore::clear() +{ + potions.list.clear(); + appas.list.clear(); + armors.list.clear(); + books.list.clear(); + clothes.list.clear(); + ingreds.list.clear(); + lights.list.clear(); + lockpicks.list.clear(); + miscItems.list.clear(); + probes.list.clear(); + repairs.list.clear(); + weapons.list.clear(); +} + +int MWWorld::ContainerStore::getType (const Ptr& ptr) +{ + if (ptr.isEmpty()) + throw std::runtime_error ("can't put a non-existent object into a container"); + + if (ptr.getTypeName()==typeid (ESM::Potion).name()) + return Type_Potion; + + if (ptr.getTypeName()==typeid (ESM::Apparatus).name()) + return Type_Apparatus; + + if (ptr.getTypeName()==typeid (ESM::Armor).name()) + return Type_Armor; + + if (ptr.getTypeName()==typeid (ESM::Book).name()) + return Type_Book; + + if (ptr.getTypeName()==typeid (ESM::Clothing).name()) + return Type_Clothing; + + if (ptr.getTypeName()==typeid (ESM::Ingredient).name()) + return Type_Ingredient; + + if (ptr.getTypeName()==typeid (ESM::Light).name()) + return Type_Light; + + if (ptr.getTypeName()==typeid (ESM::Tool).name()) + return Type_Lockpick; + + if (ptr.getTypeName()==typeid (ESM::Miscellaneous).name()) + return Type_Miscellaneous; + + if (ptr.getTypeName()==typeid (ESM::Probe).name()) + return Type_Probe; + + if (ptr.getTypeName()==typeid (ESM::Repair).name()) + return Type_Repair; + + if (ptr.getTypeName()==typeid (ESM::Weapon).name()) + return Type_Weapon; + + throw std::runtime_error ( + "Object of type " + ptr.getTypeName() + " can not be placed into a container"); +} + + +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container) +: mType (-1), mMask (0), mContainer (container) +{} + +MWWorld::ContainerStoreIterator::ContainerStoreIterator (int mask, ContainerStore *container) +: mType (0), mMask (mask), mContainer (container) +{ + nextType(); +} + +void MWWorld::ContainerStoreIterator::incType() +{ + if (mType==0) + mType = 1; + else if (mType!=-1) + { + mType <<= 1; + + if (mType>ContainerStore::Type_Last) + mType = -1; + } +} + +void MWWorld::ContainerStoreIterator::nextType() +{ + while (mType!=-1) + { + incType(); + + if (mType & mMask) + if (resetIterator()) + break; + } +} + +bool MWWorld::ContainerStoreIterator::resetIterator() +{ + switch (mType) + { + case ContainerStore::Type_Potion: + + mPotion = mContainer->potions.list.begin(); + return mPotion!=mContainer->potions.list.end(); + + case ContainerStore::Type_Apparatus: + + mApparatus = mContainer->appas.list.begin(); + return mApparatus!=mContainer->appas.list.end(); + + case ContainerStore::Type_Armor: + + mArmor = mContainer->armors.list.begin(); + return mArmor!=mContainer->armors.list.end(); + + case ContainerStore::Type_Book: + + mBook = mContainer->books.list.begin(); + return mBook!=mContainer->books.list.end(); + + case ContainerStore::Type_Clothing: + + mClothing = mContainer->clothes.list.begin(); + return mClothing!=mContainer->clothes.list.end(); + + case ContainerStore::Type_Ingredient: + + mIngredient = mContainer->ingreds.list.begin(); + return mIngredient!=mContainer->ingreds.list.end(); + + case ContainerStore::Type_Light: + + mLight = mContainer->lights.list.begin(); + return mLight!=mContainer->lights.list.end(); + + case ContainerStore::Type_Lockpick: + + mLockpick = mContainer->lockpicks.list.begin(); + return mLockpick!=mContainer->lockpicks.list.end(); + + case ContainerStore::Type_Miscellaneous: + + mMiscellaneous = mContainer->miscItems.list.begin(); + return mMiscellaneous!=mContainer->miscItems.list.end(); + + case ContainerStore::Type_Probe: + + mProbe = mContainer->probes.list.begin(); + return mProbe!=mContainer->probes.list.end(); + + case ContainerStore::Type_Repair: + + mRepair = mContainer->repairs.list.begin(); + return mRepair!=mContainer->repairs.list.end(); + + case ContainerStore::Type_Weapon: + + mWeapon = mContainer->weapons.list.begin(); + return mWeapon!=mContainer->weapons.list.end(); + } + + return false; +} + +bool MWWorld::ContainerStoreIterator::incIterator() +{ + switch (mType) + { + case ContainerStore::Type_Potion: + + ++mPotion; + return mPotion==mContainer->potions.list.end(); + + case ContainerStore::Type_Apparatus: + + ++mApparatus; + return mApparatus==mContainer->appas.list.end(); + + case ContainerStore::Type_Armor: + + ++mArmor; + return mArmor==mContainer->armors.list.end(); + + case ContainerStore::Type_Book: + + ++mBook; + return mBook==mContainer->books.list.end(); + + case ContainerStore::Type_Clothing: + + ++mClothing; + return mClothing==mContainer->clothes.list.end(); + + case ContainerStore::Type_Ingredient: + + ++mIngredient; + return mIngredient==mContainer->ingreds.list.end(); + + case ContainerStore::Type_Light: + + ++mLight; + return mLight==mContainer->lights.list.end(); + + case ContainerStore::Type_Lockpick: + + ++mLockpick; + return mLockpick==mContainer->lockpicks.list.end(); + + case ContainerStore::Type_Miscellaneous: + + ++mMiscellaneous; + return mMiscellaneous==mContainer->miscItems.list.end(); + + case ContainerStore::Type_Probe: + + ++mProbe; + return mProbe==mContainer->probes.list.end(); + + case ContainerStore::Type_Repair: + + ++mRepair; + return mRepair==mContainer->repairs.list.end(); + + case ContainerStore::Type_Weapon: + + ++mWeapon; + return mWeapon==mContainer->weapons.list.end(); + } + + return true; +} + +MWWorld::Ptr *MWWorld::ContainerStoreIterator::operator->() const +{ + mPtr = **this; + return &mPtr; +} + +MWWorld::Ptr MWWorld::ContainerStoreIterator::operator*() const +{ + switch (mType) + { + case ContainerStore::Type_Potion: return MWWorld::Ptr (&*mPotion, 0); + case ContainerStore::Type_Apparatus: return MWWorld::Ptr (&*mApparatus, 0); + case ContainerStore::Type_Armor: return MWWorld::Ptr (&*mArmor, 0); + case ContainerStore::Type_Book: return MWWorld::Ptr (&*mBook, 0); + case ContainerStore::Type_Clothing: return MWWorld::Ptr (&*mClothing, 0); + case ContainerStore::Type_Ingredient: return MWWorld::Ptr (&*mIngredient, 0); + case ContainerStore::Type_Light: return MWWorld::Ptr (&*mLight, 0); + case ContainerStore::Type_Lockpick: return MWWorld::Ptr (&*mLockpick, 0); + case ContainerStore::Type_Miscellaneous: return MWWorld::Ptr (&*mMiscellaneous, 0); + case ContainerStore::Type_Probe: return MWWorld::Ptr (&*mProbe, 0); + case ContainerStore::Type_Repair: return MWWorld::Ptr (&*mRepair, 0); + case ContainerStore::Type_Weapon: return MWWorld::Ptr (&*mWeapon, 0); + } + + throw std::runtime_error ("invalid pointer"); +} + +MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator++() +{ + do + { + if (incIterator()) + nextType(); + } + while (mType!=-1 && !(**this).getRefData().getCount()); + + return *this; +} + +MWWorld::ContainerStoreIterator MWWorld::ContainerStoreIterator::operator++ (int) +{ + ContainerStoreIterator iter (*this); + ++*this; + return iter; +} + +bool MWWorld::ContainerStoreIterator::isEqual (const ContainerStoreIterator& iter) const +{ + assert (mContainer==iter.mContainer); + + if (mType!=iter.mType) + return false; + + switch (mType) + { + case ContainerStore::Type_Potion: return mPotion==iter.mPotion; + case ContainerStore::Type_Apparatus: return mApparatus==iter.mApparatus; + case ContainerStore::Type_Armor: return mArmor==iter.mArmor; + case ContainerStore::Type_Book: return mBook==iter.mBook; + case ContainerStore::Type_Clothing: return mClothing==iter.mClothing; + case ContainerStore::Type_Ingredient: return mIngredient==iter.mIngredient; + case ContainerStore::Type_Light: return mLight==iter.mLight; + case ContainerStore::Type_Lockpick: return mLockpick==iter.mLockpick; + case ContainerStore::Type_Miscellaneous: return mMiscellaneous==iter.mMiscellaneous; + case ContainerStore::Type_Probe: return mProbe==iter.mProbe; + case ContainerStore::Type_Repair: return mRepair==iter.mRepair; + case ContainerStore::Type_Weapon: return mWeapon==iter.mWeapon; + case -1: return true; + } + + return false; +} + +int MWWorld::ContainerStoreIterator::getType() const +{ + return mType; +} + +const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStore() const +{ + return mContainer; +} + +bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) +{ + return left.isEqual (right); +} + +bool MWWorld::operator!= (const ContainerStoreIterator& left, const ContainerStoreIterator& right) +{ + return !(left==right); +} diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 971a79c15..69e5ba446 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -1,26 +1,154 @@ #ifndef GAME_MWWORLD_CONTAINERSTORE_H #define GAME_MWWORLD_CONTAINERSTORE_H +#include + #include +#include "refdata.hpp" +#include "ptr.hpp" + +namespace ESM +{ + struct InventoryList; +} + namespace MWWorld { - template - struct ContainerStore + class ContainerStoreIterator; + + class ContainerStore { - ESMS::CellRefList potions; - ESMS::CellRefList appas; - ESMS::CellRefList armors; - ESMS::CellRefList books; - ESMS::CellRefList clothes; - ESMS::CellRefList ingreds; - ESMS::CellRefList lights; - ESMS::CellRefList lockpicks; - ESMS::CellRefList miscItems; - ESMS::CellRefList probes; - ESMS::CellRefList repairs; - ESMS::CellRefList weapons; + public: + + static const int Type_Potion = 0x0001; + static const int Type_Apparatus = 0x0002; + static const int Type_Armor = 0x0004; + static const int Type_Book = 0x0008; + static const int Type_Clothing = 0x0010; + static const int Type_Ingredient = 0x0020; + static const int Type_Light = 0x0040; + static const int Type_Lockpick = 0x0080; + static const int Type_Miscellaneous = 0x0100; + static const int Type_Probe = 0x0200; + static const int Type_Repair = 0x0400; + static const int Type_Weapon = 0x0800; + + static const int Type_Last = Type_Weapon; + + static const int Type_All = 0xffff; + + private: + + ESMS::CellRefList potions; + ESMS::CellRefList appas; + ESMS::CellRefList armors; + ESMS::CellRefList books; + ESMS::CellRefList clothes; + ESMS::CellRefList ingreds; + ESMS::CellRefList lights; + ESMS::CellRefList lockpicks; + ESMS::CellRefList miscItems; + ESMS::CellRefList probes; + ESMS::CellRefList repairs; + ESMS::CellRefList weapons; + + public: + + virtual ~ContainerStore(); + + ContainerStoreIterator begin (int mask = Type_All); + + ContainerStoreIterator end(); + + void add (const Ptr& ptr); + ///< Add the item pointed to by \a ptr to this container. + /// + /// \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! + + void fill (const ESM::InventoryList& items, const ESMS::ESMStore& store); + ///< Insert items into *this. + + void clear(); + ///< Empty container. + + static int getType (const Ptr& ptr); + ///< This function throws an exception, if ptr does not point to an object, that can be + /// put into a container. + + friend class ContainerStoreIterator; }; + + /// \brief Iteration over a subset of objects in a ContainerStore + /// + /// \note The iterator will automatically skip over deleted objects. + class ContainerStoreIterator + : public std::iterator + { + int mType; + int mMask; + ContainerStore *mContainer; + mutable Ptr mPtr; + + ESMS::CellRefList::List::iterator mPotion; + ESMS::CellRefList::List::iterator mApparatus; + ESMS::CellRefList::List::iterator mArmor; + ESMS::CellRefList::List::iterator mBook; + ESMS::CellRefList::List::iterator mClothing; + ESMS::CellRefList::List::iterator mIngredient; + ESMS::CellRefList::List::iterator mLight; + ESMS::CellRefList::List::iterator mLockpick; + ESMS::CellRefList::List::iterator mMiscellaneous; + ESMS::CellRefList::List::iterator mProbe; + ESMS::CellRefList::List::iterator mRepair; + ESMS::CellRefList::List::iterator mWeapon; + + private: + + ContainerStoreIterator (ContainerStore *container); + ///< End-iterator + + ContainerStoreIterator (int mask, ContainerStore *container); + ///< Begin-iterator + + void incType(); + + void nextType(); + + bool resetIterator(); + ///< Reset iterator for selected type. + /// + /// \return Type not empty? + + bool incIterator(); + ///< Increment iterator for selected type. + /// + /// \return reached the end? + + public: + + Ptr *operator->() const; + + Ptr operator*() const; + + ContainerStoreIterator& operator++(); + + ContainerStoreIterator operator++ (int); + + bool isEqual (const ContainerStoreIterator& iter) const; + + int getType() const; + + const ContainerStore *getContainerStore() const; + + friend class ContainerStore; + }; + + bool operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right); + bool operator!= (const ContainerStoreIterator& left, const ContainerStoreIterator& right); } #endif diff --git a/apps/openmw/mwworld/containerutil.cpp b/apps/openmw/mwworld/containerutil.cpp deleted file mode 100644 index 7c7e5e5e8..000000000 --- a/apps/openmw/mwworld/containerutil.cpp +++ /dev/null @@ -1,43 +0,0 @@ - -#include "containerutil.hpp" - -namespace -{ - template - void listItemsInContainerImp (const std::string& id, - ESMS::CellRefList& containerStore, - const ESMS::RecListT& store, std::vector& list) - { - if (const T *record = store.search (id)) - { - for (typename ESMS::CellRefList::List::iterator iter - (containerStore.list.begin()); - iter!=containerStore.list.end(); ++iter) - { - if (iter->base==record) - list.push_back (MWWorld::Ptr (&*iter, 0)); - } - } - } -} - -namespace MWWorld -{ - void listItemsInContainer (const std::string& id, - ContainerStore& containerStore, - const ESMS::ESMStore& store, std::vector& list) - { - listItemsInContainerImp (id, containerStore.potions, store.potions, list); - listItemsInContainerImp (id, containerStore.appas, store.appas, list); - listItemsInContainerImp (id, containerStore.armors, store.armors, list); - listItemsInContainerImp (id, containerStore.books, store.books, list); - listItemsInContainerImp (id, containerStore.clothes, store.clothes, list); - listItemsInContainerImp (id, containerStore.ingreds, store.ingreds, list); - listItemsInContainerImp (id, containerStore.lights, store.lights, list); - listItemsInContainerImp (id, containerStore.lockpicks, store.lockpicks, list); - listItemsInContainerImp (id, containerStore.miscItems, store.miscItems, list); - listItemsInContainerImp (id, containerStore.probes, store.probes, list); - listItemsInContainerImp (id, containerStore.repairs, store.repairs, list); - listItemsInContainerImp (id, containerStore.weapons, store.weapons, list); - } -} diff --git a/apps/openmw/mwworld/containerutil.hpp b/apps/openmw/mwworld/containerutil.hpp deleted file mode 100644 index 21e770404..000000000 --- a/apps/openmw/mwworld/containerutil.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef GAME_MWWORLD_CONTAINERUTIL_H -#define GAME_MWWORLD_CONTAINERUTIL_H - -#include -#include - -#include - -#include "containerstore.hpp" -#include "ptr.hpp" -#include "refdata.hpp" - -namespace MWWorld -{ - void listItemsInContainer (const std::string& id, ContainerStore& containerStore, - const ESMS::ESMStore& store, std::vector& list); - ///< append all references with the given id to list. -} - -#endif diff --git a/apps/openmw/mwworld/customdata.hpp b/apps/openmw/mwworld/customdata.hpp new file mode 100644 index 000000000..588991fe4 --- /dev/null +++ b/apps/openmw/mwworld/customdata.hpp @@ -0,0 +1,17 @@ +#ifndef GAME_MWWORLD_CUSTOMDATA_H +#define GAME_MWWORLD_CUSTOMDATA_H + +namespace MWWorld +{ + /// \brief Base class for the MW-class-specific part of RefData + class CustomData + { + public: + + virtual ~CustomData() {} + + virtual CustomData *clone() const = 0; + }; +} + +#endif diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp new file mode 100644 index 000000000..264be7bb3 --- /dev/null +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -0,0 +1,86 @@ + +#include "inventorystore.hpp" + +#include +#include + +#include "class.hpp" + +void MWWorld::InventoryStore::copySlots (const InventoryStore& store) +{ + // some const-trickery, required because of a flaw in the handling of MW-references and the + // resulting workarounds + for (std::vector::const_iterator iter ( + const_cast (store).mSlots.begin()); + iter!=const_cast (store).mSlots.end(); ++iter) + { + std::size_t distance = std::distance (const_cast (store).begin(), *iter); + + ContainerStoreIterator slot = begin(); + + std::advance (slot, distance); + + mSlots.push_back (slot); + } +} + +MWWorld::InventoryStore::InventoryStore() +{ + for (int i=0; i=static_cast (mSlots.size())) + throw std::runtime_error ("slot number out of range"); + + if (iterator.getContainerStore()!=this) + throw std::runtime_error ("attempt to equip an item that is not in the inventory"); + + if (iterator!=end()) + { + std::pair, bool> slots = Class::get (*iterator).getEquipmentSlots (*iterator); + + if (std::find (slots.first.begin(), slots.first.end(), slot)==slots.first.end()) + throw std::runtime_error ("invalid slot"); + } + + /// \todo restack item previously in this slot (if required) + + /// \todo unstack item pointed to by iterator if required) + + mSlots[slot] = iterator; +} + +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) +{ + if (slot<0 || slot>=static_cast (mSlots.size())) + throw std::runtime_error ("slot number out of range"); + + if (mSlots[slot]==end()) + return end(); + + if (mSlots[slot]->getRefData().getCount()<1) + { + // object has been deleted + mSlots[slot] = end(); + return end(); + } + + return mSlots[slot]; +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp new file mode 100644 index 000000000..c41f9e8e3 --- /dev/null +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -0,0 +1,58 @@ +#ifndef GAME_MWWORLD_INVENTORYSTORE_H +#define GAME_MWWORLD_INVENTORYSTORE_H + +#include "containerstore.hpp" + +namespace MWWorld +{ + ///< \brief Variant of the ContainerStore for NPCs + class InventoryStore : public ContainerStore + { + public: + + static const int Slot_Helmet = 0; + static const int Slot_Cuirass = 1; + static const int Slot_Greaves = 2; + static const int Slot_LeftPauldron = 3; + static const int Slot_RightPauldron = 4; + static const int Slot_LeftGauntlet = 5; + static const int Slot_RightGauntlet = 6; + static const int Slot_Boots = 7; + static const int Slot_Shirt = 8; + static const int Slot_Pants = 9; + static const int Slot_Skirt = 10; + static const int Slot_Robe = 11; + static const int Slot_LeftRing = 12; + static const int Slot_RightRing = 13; + static const int Slot_Amulet = 14; + static const int Slot_Belt = 15; + static const int Slot_CarriedRight = 16; + static const int Slot_CarriedLeft = 17; + static const int Slot_Ammunition = 18; + + static const int Slots = 19; + + static const int Slot_NoSlot = -1; + + private: + + mutable std::vector mSlots; + + void copySlots (const InventoryStore& store); + + public: + + InventoryStore(); + + InventoryStore (const InventoryStore& store); + + InventoryStore& operator= (const InventoryStore& store); + + void equip (int slot, const ContainerStoreIterator& iterator); + ///< \note \a iteartor can be an end-iterator + + ContainerStoreIterator getSlot (int slot); + }; +} + +#endif diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index b4d3f7007..f8bc7d983 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -82,6 +82,7 @@ namespace MWWorld // initialise ESM::CellRef& cellRef = mPtr.getCellRef(); + cellRef.refID = name; cellRef.refnum = -1; cellRef.scale = 1; cellRef.factIndex = 0; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index eb5c3c91c..bb2f9f8a9 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -3,6 +3,7 @@ #include "physicssystem.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/world.hpp" // FIXME +#include #include "OgreRoot.h" #include "OgreRenderWindow.h" @@ -16,16 +17,24 @@ using namespace Ogre; namespace MWWorld { - PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng) : - mRender(_rend), mEngine(physEng), mFreeFly (true) + PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : + mRender(_rend), mEngine(0), mFreeFly (true) { - + // Create physics. shapeLoader is deleted by the physic engine + NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); + mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); } PhysicsSystem::~PhysicsSystem() { - + delete mEngine; + } + OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine() + { + return mEngine; + } + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) { std::string handle = ""; @@ -41,6 +50,17 @@ namespace MWWorld return mEngine->rayTest(from,to); } + + bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) + { + 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); + + return !(result.first == ""); + } std::vector< std::pair > PhysicsSystem::doPhysics (float duration, @@ -100,7 +120,7 @@ namespace MWWorld void PhysicsSystem::addObject (const std::string& handle, const std::string& mesh, const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position) { - OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle); + OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle,scale); mEngine->addRigidBody(body); btTransform tr; tr.setOrigin(btVector3(position.x,position.y,position.z)); @@ -145,6 +165,12 @@ namespace MWWorld void PhysicsSystem::rotateObject (const std::string& handle, const Ogre::Quaternion& rotation) { + if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + { + // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow + // start positions others than 0, 0, 0 + act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); + } } void PhysicsSystem::scaleObject (const std::string& handle, float scale) @@ -191,6 +217,7 @@ namespace MWWorld void PhysicsSystem::insertActorPhysics(const MWWorld::Ptr& ptr, const std::string model){ Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + // std::cout << "Adding node with name" << node->getName(); addActor (node->getName(), model, node->getPosition()); } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index a447d7bc1..78cbde083 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -12,7 +12,7 @@ namespace MWWorld class PhysicsSystem { public: - PhysicsSystem (OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng); + PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); std::vector< std::pair > doPhysics (float duration, @@ -33,11 +33,17 @@ namespace MWWorld void scaleObject (const std::string& handle, float scale); bool toggleCollisionMode(); - std::pair getFacedHandle (MWWorld::World& world); + + std::pair getFacedHandle (MWWorld::World& world); + + // cast ray, return true if it hit something + bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); void insertObjectPhysics(const MWWorld::Ptr& ptr, std::string model); - void insertActorPhysics(const MWWorld::Ptr&, std::string model); + void insertActorPhysics(const MWWorld::Ptr&, std::string model); + + OEngine::Physic::PhysicEngine* getEngine(); private: OEngine::Render::OgreRenderer &mRender; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 4eb41ebf5..5bfb82138 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -3,6 +3,8 @@ #include "../mwrender/player.hpp" +#include "../mwmechanics/movement.hpp" + #include "world.hpp" #include "class.hpp" diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 8bf75aa3c..389c9349d 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -5,6 +5,8 @@ #include +#include + #include #include "refdata.hpp" diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp new file mode 100644 index 000000000..528f49c33 --- /dev/null +++ b/apps/openmw/mwworld/refdata.cpp @@ -0,0 +1,144 @@ + +#include "refdata.hpp" + +#include + +#include "customdata.hpp" + +namespace MWWorld +{ + void RefData::copy (const RefData& refData) + { + mBaseNode = refData.mBaseNode; + mLocals = refData.mLocals; + mHasLocals = refData.mHasLocals; + mEnabled = refData.mEnabled; + mCount = refData.mCount; + mPosition = refData.mPosition; + + mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; + } + + void RefData::cleanup() + { + mBaseNode = 0; + + delete mCustomData; + mCustomData = 0; + } + + RefData::RefData (const ESM::CellRef& cellRef) + : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.pos), + mCustomData (0) + {} + + RefData::RefData (const RefData& refData) + : mBaseNode(0), mCustomData (0) + { + try + { + copy (refData); + } + catch (...) + { + cleanup(); + throw; + } + } + + RefData& RefData::operator= (const RefData& refData) + { + try + { + cleanup(); + copy (refData); + } + catch (...) + { + cleanup(); + throw; + } + + return *this; + } + + RefData::~RefData() + { + try + { + cleanup(); + } + catch (...) + {} + } + + std::string RefData::getHandle() + { + return mBaseNode->getName(); + } + + Ogre::SceneNode* RefData::getBaseNode() + { + return mBaseNode; + } + + void RefData::setBaseNode(Ogre::SceneNode* base) + { + mBaseNode = base; + } + + int RefData::getCount() const + { + return mCount; + } + + void RefData::setLocals (const ESM::Script& script) + { + if (!mHasLocals) + { + mLocals.configure (script); + mHasLocals = true; + } + } + + void RefData::setCount (int count) + { + mCount = count; + } + + MWScript::Locals& RefData::getLocals() + { + return mLocals; + } + + bool RefData::isEnabled() const + { + return mEnabled; + } + + void RefData::enable() + { + mEnabled = true; + } + + void RefData::disable() + { + mEnabled = true; + } + + ESM::Position& RefData::getPosition() + { + return mPosition; + } + + void RefData::setCustomData (CustomData *data) + { + delete mCustomData; + mCustomData = data; + } + + CustomData *RefData::getCustomData() + { + return mCustomData; + } +} diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 9a91a27b5..30d676f13 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -3,24 +3,22 @@ #include -#include +#include + +#include #include "../mwscript/locals.hpp" -#include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/movement.hpp" - -#include "containerstore.hpp" -#include - namespace ESM { class Script; + class CellRef; } namespace MWWorld { + class CustomData; + class RefData { Ogre::SceneNode* mBaseNode; @@ -33,102 +31,58 @@ namespace MWWorld bool mEnabled; int mCount; // 0: deleted - // we are using shared pointer here to avoid having to create custom copy-constructor, - // assignment operator and destructor. As a consequence though copying a RefData object - // manually will probably give unexcepted results. This is not a problem since RefData - // are never copied outside of container operations. - boost::shared_ptr mCreatureStats; - boost::shared_ptr mNpcStats; - boost::shared_ptr mMovement; - - boost::shared_ptr > mContainerStore; - ESM::Position mPosition; + CustomData *mCustomData; + + void copy (const RefData& refData); + + void cleanup(); public: - /// @param cr Used to copy constant data such as position into this class where it can - /// be altered without effecting the original data. This makes it possible - /// to reset the position as the orignal data is still held in the CellRef - RefData(const ESMS::CellRef& cr) : mBaseNode(0), mHasLocals (false), mEnabled (true), - mCount (1), mPosition(cr.pos) {} + /// @param cellRef Used to copy constant data such as position into this class where it can + /// be altered without effecting the original data. This makes it possible + /// to reset the position as the orignal data is still held in the CellRef + RefData (const ESM::CellRef& cellRef); - std::string getHandle() - { - return mBaseNode->getName(); - } - Ogre::SceneNode* getBaseNode(){ - return mBaseNode; - } - void setBaseNode(Ogre::SceneNode* base){ - mBaseNode = base; - } + RefData (const RefData& refData); - int getCount() const - { - return mCount; - } + ~RefData(); - void setLocals (const ESM::Script& script) - { - if (!mHasLocals) - { - mLocals.configure (script); - mHasLocals = true; - } - } + RefData& operator= (const RefData& refData); + /// Return OGRE handle (may be empty). + std::string getHandle(); - void setCount (int count) - { - mCount = count; - } + /// Return OGRE base node (can be a null pointer). + Ogre::SceneNode* getBaseNode(); - MWScript::Locals& getLocals() - { - return mLocals; - } + /// Set OGRE base node (can be a null pointer). + void setBaseNode (Ogre::SceneNode* base); - bool isEnabled() const - { - return mEnabled; - } + int getCount() const; - void enable() - { - mEnabled = true; - } + void setLocals (const ESM::Script& script); - void disable() - { - mEnabled = true; - } + void setCount (int count); - boost::shared_ptr& getCreatureStats() - { - return mCreatureStats; - } + MWScript::Locals& getLocals(); - boost::shared_ptr& getNpcStats() - { - return mNpcStats; - } + bool isEnabled() const; - boost::shared_ptr& getMovement() - { - return mMovement; - } + void enable(); - boost::shared_ptr >& getContainerStore() - { - return mContainerStore; - } + void disable(); - ESM::Position& getPosition() - { - return mPosition; - } + ESM::Position& getPosition(); + + void setCustomData (CustomData *data); + ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is + /// transferred to this. + + CustomData *getCustomData(); + ///< May return a 0-pointer. The ownership of the return data object is not transferred. }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0b02a867e..b72cb91b3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -52,20 +52,22 @@ void insertCellRefList(MWRender::RenderingManager& rendering, MWWorld::Environme namespace MWWorld { - + + void Scene::update (float duration){ + mRendering.update (duration); + } + void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; ListHandles functor; - MWWorld::Ptr::CellStore* active = *iter; - - - - mRendering.removeCell(active); - active->forEach(functor); + + + + (*iter)->forEach(functor); { @@ -77,12 +79,16 @@ namespace MWWorld mPhysics->removeObject (node->getName()); } } - + + mRendering.removeCell(*iter); //mPhysics->removeObject("Unnamed_43"); - mWorld->getLocalScripts().clearCell (active); - mEnvironment.mMechanicsManager->dropActors (active); - mEnvironment.mSoundManager->stopSound (active); - mActiveCells.erase(active); + + mWorld->getLocalScripts().clearCell (*iter); + mEnvironment.mMechanicsManager->dropActors (*iter); + mEnvironment.mSoundManager->stopSound (*iter); + mActiveCells.erase(*iter); + + } @@ -236,6 +242,9 @@ namespace MWWorld // adjust player mCurrentCell = cell; playerCellChange (cell, position); + + // adjust fog + mRendering.configureFog(*cell); // Sky system mWorld->adjustSky(); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 1ddabb57b..4d1bd6fb5 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -101,6 +101,8 @@ namespace MWWorld void markCellAsUnchanged(); void insertCell(ESMS::CellStore &cell, MWWorld::Environment& environment); + + void update (float duration); }; } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp new file mode 100644 index 000000000..ccbca52db --- /dev/null +++ b/apps/openmw/mwworld/weather.cpp @@ -0,0 +1,818 @@ +#include "weather.hpp" +#include "world.hpp" +#include "player.hpp" + +#include "../mwrender/renderingmanager.hpp" +#include "../mwsound/soundmanager.hpp" + +#include +#include +#include + +#include + +using namespace Ogre; +using namespace MWWorld; +using namespace MWSound; + +#define lerp(x, y) (x * (1-factor) + y * factor) + +const std::string WeatherGlobals::mThunderSoundID0 = "Thunder0"; +const std::string WeatherGlobals::mThunderSoundID1 = "Thunder1"; +const std::string WeatherGlobals::mThunderSoundID2 = "Thunder2"; +const std::string WeatherGlobals::mThunderSoundID3 = "Thunder3"; + +const float WeatherGlobals::mWeatherUpdateTime = 20.f; + +// morrowind sets these per-weather, but since they are only used by 'thunderstorm' +// weather setting anyway, we can just as well set them globally +const float WeatherGlobals::mThunderFrequency = .4; +const float WeatherGlobals::mThunderThreshold = 0.6; +const float WeatherGlobals::mThunderSoundDelay = 0.25; + +WeatherManager::WeatherManager(MWRender::RenderingManager* rendering, Environment* env) : + mHour(14), mCurrentWeather("clear"), mFirstUpdate(true), mWeatherUpdateTime(0), + mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0) +{ + mRendering = rendering; + mEnvironment = env; + + #define clr(r,g,b) ColourValue(r/255.f, g/255.f, b/255.f) + + /// \todo read these from Morrowind.ini + Weather clear; + clear.mCloudTexture = "tx_sky_clear.dds"; + clear.mCloudsMaximumPercent = 1.0; + clear.mTransitionDelta = 0.015; + clear.mSkySunriseColor = clr(118, 141, 164); + clear.mSkyDayColor = clr(95, 135, 203); + clear.mSkySunsetColor = clr(56, 89, 129); + clear.mSkyNightColor = clr(9, 10, 11); + clear.mFogSunriseColor = clr(255, 189, 157); + clear.mFogDayColor = clr(206, 227, 255); + clear.mFogSunsetColor = clr(255, 189, 157); + clear.mFogNightColor = clr(9, 10, 11); + clear.mAmbientSunriseColor = clr(47, 66, 96); + clear.mAmbientDayColor = clr(137, 140, 160); + clear.mAmbientSunsetColor = clr(68, 75, 96); + clear.mAmbientNightColor = clr(32, 35, 42); + clear.mSunSunriseColor = clr(242, 159, 99); + clear.mSunDayColor = clr(255, 252, 238); + clear.mSunSunsetColor = clr(255, 115, 79); + clear.mSunNightColor = clr(59, 97, 176); + clear.mSunDiscSunsetColor = clr(255, 189, 157); + clear.mLandFogDayDepth = 0.69; + clear.mLandFogNightDepth = 0.69; + clear.mWindSpeed = 0.1; + clear.mCloudSpeed = 1.25; + clear.mGlareView = 1.0; + mWeatherSettings["clear"] = clear; + + Weather cloudy; + cloudy.mCloudTexture = "tx_sky_cloudy.dds"; + cloudy.mCloudsMaximumPercent = 1.0; + cloudy.mTransitionDelta = 0.015; + cloudy.mSkySunriseColor = clr(126, 158, 173); + cloudy.mSkyDayColor = clr(117, 160, 215); + cloudy.mSkySunsetColor = clr(111, 114, 159); + cloudy.mSkyNightColor = clr(9, 10, 11); + cloudy.mFogSunriseColor = clr(255, 207, 149); + cloudy.mFogDayColor = clr(245, 235, 224); + cloudy.mFogSunsetColor = clr(255, 155, 106); + cloudy.mFogNightColor = clr(9, 10, 11); + cloudy.mAmbientSunriseColor = clr(66, 74, 87); + cloudy.mAmbientDayColor = clr(137, 145, 160); + cloudy.mAmbientSunsetColor = clr(71, 80, 92); + cloudy.mAmbientNightColor = clr(32, 39, 54); + cloudy.mSunSunriseColor = clr(241, 177, 99); + cloudy.mSunDayColor = clr(255, 236, 221); + cloudy.mSunSunsetColor = clr(255, 89, 00); + cloudy.mSunNightColor = clr(77, 91, 124); + cloudy.mSunDiscSunsetColor = clr(255, 202, 179); + cloudy.mLandFogDayDepth = 0.72; + cloudy.mLandFogNightDepth = 0.72; + cloudy.mWindSpeed = 0.2; + cloudy.mCloudSpeed = 2; + cloudy.mGlareView = 1.0; + mWeatherSettings["cloudy"] = cloudy; + + Weather foggy; + foggy.mCloudTexture = "tx_sky_foggy.dds"; + foggy.mCloudsMaximumPercent = 1.0; + foggy.mTransitionDelta = 0.015; + foggy.mSkySunriseColor = clr(197, 190, 180); + foggy.mSkyDayColor = clr(184, 211, 228); + foggy.mSkySunsetColor = clr(142, 159, 176); + foggy.mSkyNightColor = clr(18, 23, 28); + foggy.mFogSunriseColor = clr(173, 164, 148); + foggy.mFogDayColor = clr(150, 187, 209); + foggy.mFogSunsetColor = clr(113, 135, 157); + foggy.mFogNightColor = clr(19, 24, 29); + foggy.mAmbientSunriseColor = clr(48, 43, 37); + foggy.mAmbientDayColor = clr(92, 109, 120); + foggy.mAmbientSunsetColor = clr(28, 33, 39); + foggy.mAmbientNightColor = clr(28, 33, 39); + foggy.mSunSunriseColor = clr(177, 162, 137); + foggy.mSunDayColor = clr(111, 131, 151); + foggy.mSunSunsetColor = clr(125, 157, 189); + foggy.mSunNightColor = clr(81, 100, 119); + foggy.mSunDiscSunsetColor = clr(223, 223, 223); + foggy.mLandFogDayDepth = 1.0; + foggy.mLandFogNightDepth = 1.9; + foggy.mWindSpeed = 0; + foggy.mCloudSpeed = 1.25; + foggy.mGlareView = 0.25; + mWeatherSettings["foggy"] = foggy; + + Weather thunderstorm; + thunderstorm.mCloudTexture = "tx_sky_thunder.dds"; + thunderstorm.mCloudsMaximumPercent = 0.66; + thunderstorm.mTransitionDelta = 0.03; + thunderstorm.mSkySunriseColor = clr(35, 36, 39); + thunderstorm.mSkyDayColor = clr(97, 104, 115); + thunderstorm.mSkySunsetColor = clr(35, 36, 39); + thunderstorm.mSkyNightColor = clr(19, 20, 22); + thunderstorm.mFogSunriseColor = clr(70, 74, 85); + thunderstorm.mFogDayColor = clr(97, 104, 115); + thunderstorm.mFogSunsetColor = clr(70, 74, 85); + thunderstorm.mFogNightColor = clr(19, 20, 22); + thunderstorm.mAmbientSunriseColor = clr(54, 54, 54); + thunderstorm.mAmbientDayColor = clr(90, 90, 90); + thunderstorm.mAmbientSunsetColor = clr(54, 54, 54); + thunderstorm.mAmbientNightColor = clr(49, 51, 54); + thunderstorm.mSunSunriseColor = clr(91, 99, 122); + thunderstorm.mSunDayColor = clr(138, 144, 155); + thunderstorm.mSunSunsetColor = clr(96, 101, 117); + thunderstorm.mSunNightColor = clr(55, 76, 110); + thunderstorm.mSunDiscSunsetColor = clr(128, 128, 128); + thunderstorm.mLandFogDayDepth = 1; + thunderstorm.mLandFogNightDepth = 1.15; + thunderstorm.mWindSpeed = 0.5; + thunderstorm.mCloudSpeed = 3; + thunderstorm.mGlareView = 0; + thunderstorm.mRainLoopSoundID = "rain heavy"; + mWeatherSettings["thunderstorm"] = thunderstorm; + + Weather rain; + rain.mCloudTexture = "tx_sky_rainy.dds"; + rain.mCloudsMaximumPercent = 0.66; + rain.mTransitionDelta = 0.015; + rain.mSkySunriseColor = clr(71, 74, 75); + rain.mSkyDayColor = clr(116, 120, 122); + rain.mSkySunsetColor = clr(73, 73, 73); + rain.mSkyNightColor = clr(24, 25, 26); + rain.mFogSunriseColor = clr(71, 74, 75); + rain.mFogDayColor = clr(116, 120, 122); + rain.mFogSunsetColor = clr(73, 73, 73); + rain.mFogNightColor = clr(24, 25, 26); + rain.mAmbientSunriseColor = clr(97, 90, 88); + rain.mAmbientDayColor = clr(105, 110, 113); + rain.mAmbientSunsetColor = clr(88, 97, 97); + rain.mAmbientNightColor = clr(50, 55, 67); + rain.mSunSunriseColor = clr(131, 122, 120); + rain.mSunDayColor = clr(149, 157, 170); + rain.mSunSunsetColor = clr(120, 126, 131); + rain.mSunNightColor = clr(50, 62, 101); + rain.mSunDiscSunsetColor = clr(128, 128, 128); + rain.mLandFogDayDepth = 0.8; + rain.mLandFogNightDepth = 0.8; + rain.mWindSpeed = 0.3; + rain.mCloudSpeed = 2; + rain.mGlareView = 0; + rain.mRainLoopSoundID = "rain"; + mWeatherSettings["rain"] = rain; + + Weather overcast; + overcast.mCloudTexture = "tx_sky_overcast.dds"; + overcast.mCloudsMaximumPercent = 1.0; + overcast.mTransitionDelta = 0.015; + overcast.mSkySunriseColor = clr(91, 99, 106); + overcast.mSkyDayColor = clr(143, 146, 149); + overcast.mSkySunsetColor = clr(108, 115, 121); + overcast.mSkyNightColor = clr(19, 22, 25); + overcast.mFogSunriseColor = clr(91, 99, 106); + overcast.mFogDayColor = clr(143, 146, 149); + overcast.mFogSunsetColor = clr(108, 115, 121); + overcast.mFogNightColor = clr(19, 22, 25); + overcast.mAmbientSunriseColor = clr(84, 88, 92); + overcast.mAmbientDayColor = clr(93, 96, 105); + overcast.mAmbientSunsetColor = clr(83, 77, 75); + overcast.mAmbientNightColor = clr(57, 60, 66); + overcast.mSunSunriseColor = clr(87, 125, 163); + overcast.mSunDayColor = clr(163, 169, 183); + overcast.mSunSunsetColor = clr(85, 103, 157); + overcast.mSunNightColor = clr(32, 54, 100); + overcast.mSunDiscSunsetColor = clr(128, 128, 128); + overcast.mLandFogDayDepth = 0.7; + overcast.mLandFogNightDepth = 0.7; + overcast.mWindSpeed = 0.2; + overcast.mCloudSpeed = 1.5; + overcast.mGlareView = 0; + mWeatherSettings["overcast"] = overcast; + + Weather ashstorm; + ashstorm.mCloudTexture = "tx_sky_ashstorm.dds"; + ashstorm.mCloudsMaximumPercent = 1.0; + ashstorm.mTransitionDelta = 0.035; + ashstorm.mSkySunriseColor = clr(91, 56, 51); + ashstorm.mSkyDayColor = clr(124, 73, 58); + ashstorm.mSkySunsetColor = clr(106, 55, 40); + ashstorm.mSkyNightColor = clr(20, 21, 22); + ashstorm.mFogSunriseColor = clr(91, 56, 51); + ashstorm.mFogDayColor = clr(124, 73, 58); + ashstorm.mFogSunsetColor = clr(106, 55, 40); + ashstorm.mFogNightColor = clr(20, 21, 22); + ashstorm.mAmbientSunriseColor = clr(52, 42, 37); + ashstorm.mAmbientDayColor = clr(75, 49, 41); + ashstorm.mAmbientSunsetColor = clr(48, 39, 35); + ashstorm.mAmbientNightColor = clr(36, 42, 49); + ashstorm.mSunSunriseColor = clr(184, 91, 71); + ashstorm.mSunDayColor = clr(228, 139, 114); + ashstorm.mSunSunsetColor = clr(185, 86, 57); + ashstorm.mSunNightColor = clr(54, 66, 74); + ashstorm.mSunDiscSunsetColor = clr(128, 128, 128); + ashstorm.mLandFogDayDepth = 1.1; + ashstorm.mLandFogNightDepth = 1.2; + ashstorm.mWindSpeed = 0.8; + ashstorm.mCloudSpeed = 7; + ashstorm.mGlareView = 0; + ashstorm.mAmbientLoopSoundID = "ashstorm"; + mWeatherSettings["ashstorm"] = ashstorm; + + Weather blight; + blight.mCloudTexture = "tx_sky_blight.dds"; + blight.mCloudsMaximumPercent = 1.0; + blight.mTransitionDelta = 0.04; + blight.mSkySunriseColor = clr(90, 35, 35); + blight.mSkyDayColor = clr(90, 35, 35); + blight.mSkySunsetColor = clr(92, 33, 33); + blight.mSkyNightColor = clr(44, 14, 14); + blight.mFogSunriseColor = clr(90, 35, 35); + blight.mFogDayColor = clr(128, 19, 19); + blight.mFogSunsetColor = clr(92, 33, 33); + blight.mFogNightColor = clr(44, 14, 14); + blight.mAmbientSunriseColor = clr(61, 40, 40); + blight.mAmbientDayColor = clr(79, 54, 54); + blight.mAmbientSunsetColor = clr(61, 40, 40); + blight.mAmbientNightColor = clr(56, 58, 62); + blight.mSunSunriseColor = clr(180, 78, 78); + blight.mSunDayColor = clr(224, 84, 84); + blight.mSunSunsetColor = clr(180, 78, 78); + blight.mSunNightColor = clr(61, 91, 143); + blight.mSunDiscSunsetColor = clr(128, 128, 128); + blight.mLandFogDayDepth = 1.1; + blight.mLandFogNightDepth = 1.2; + blight.mWindSpeed = 0.9; + blight.mCloudSpeed = 9; + blight.mGlareView = 0; + blight.mAmbientLoopSoundID = "blight"; + mWeatherSettings["blight"] = blight; + + 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; + + 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; +} + +void WeatherManager::setWeather(const String& weather, bool instant) +{ + if (weather == mCurrentWeather && mNextWeather == "") + return; + + if (instant || mFirstUpdate) + { + mNextWeather = ""; + mCurrentWeather = weather; + mFirstUpdate = false; + } + else + { + if (mNextWeather != "") + { + // transition more than 50% finished? + if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600) <= 0.5) + mCurrentWeather = mNextWeather; + } + + mNextWeather = weather; + mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600; + } +} + +WeatherResult WeatherManager::getResult(const String& weather) +{ + const Weather& current = mWeatherSettings[weather]; + WeatherResult result; + + 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; + + result.mNight = (mHour < 6 || mHour > 19); + + result.mFogDepth = result.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; + + // night + if (mHour <= 5.5f || mHour >= 21) + { + result.mFogColor = current.mFogNightColor; + result.mAmbientColor = current.mAmbientNightColor; + result.mSunColor = current.mSunNightColor; + result.mSkyColor = current.mSkyNightColor; + result.mNightFade = 1.f; + } + + // sunrise + else if (mHour >= 5.5f && mHour <= 9) + { + if (mHour <= 6) + { + // fade in + float advance = 6-mHour; + float factor = advance / 0.5f; + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor); + result.mNightFade = factor; + } + else //if (mHour >= 6) + { + // fade out + float advance = mHour-6; + float factor = advance / 3.f; + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor); + } + } + + // day + else if (mHour >= 9 && mHour <= 17) + { + result.mFogColor = current.mFogDayColor; + result.mAmbientColor = current.mAmbientDayColor; + result.mSunColor = current.mSunDayColor; + result.mSkyColor = current.mSkyDayColor; + } + + // sunset + else if (mHour >= 17 && mHour <= 21) + { + if (mHour <= 19) + { + // fade in + float advance = 19-mHour; + float factor = (advance / 2); + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor); + } + else //if (mHour >= 19) + { + // fade out + float advance = mHour-19; + float factor = advance / 2.f; + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor); + result.mNightFade = factor; + } + } + + return result; +} + +WeatherResult 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); + result.mFogColor = lerp(current.mFogColor, other.mFogColor); + result.mSunColor = lerp(current.mSunColor, other.mSunColor); + result.mSkyColor = lerp(current.mSkyColor, other.mSkyColor); + + result.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor); + result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor); + result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth); + result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed); + //result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed); + result.mCloudSpeed = current.mCloudSpeed; + result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); + result.mGlareView = lerp(current.mGlareView, other.mGlareView); + + result.mNight = current.mNight; + + return result; +} + +void WeatherManager::update(float duration) +{ + mWeatherUpdateTime -= duration * mEnvironment->mWorld->getTimeScaleFactor(); + + bool exterior = (mEnvironment->mWorld->isCellExterior() || mEnvironment->mWorld->isCellQuasiExterior()); + + if (exterior) + { + std::string regionstr = mEnvironment->mWorld->getPlayer().getPlayer().getCell()->cell->region; + boost::algorithm::to_lower(regionstr); + + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) + { + mCurrentRegion = regionstr; + mWeatherUpdateTime = WeatherGlobals::mWeatherUpdateTime*3600; + + std::string weather; + + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + weather = mRegionOverrides[regionstr]; + else + { + // get weather probabilities for the current region + const ESM::Region *region = mEnvironment->mWorld->getStore().regions.find (regionstr); + + float clear = region->data.clear/255.f; + float cloudy = region->data.cloudy/255.f; + float foggy = region->data.foggy/255.f; + float overcast = region->data.overcast/255.f; + float rain = region->data.rain/255.f; + float thunder = region->data.thunder/255.f; + float ash = region->data.ash/255.f; + float blight = region->data.blight/255.f; + float snow = region->data.a/255.f; + float blizzard = region->data.b/255.f; + + // re-scale to 100 percent + const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight+snow+blizzard; + + srand(time(NULL)); + 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"; + } + + setWeather(weather, false); + } + + WeatherResult result; + + if (mNextWeather != "") + { + mRemainingTransitionTime -= duration * mEnvironment->mWorld->getTimeScaleFactor(); + if (mRemainingTransitionTime < 0) + { + mCurrentWeather = mNextWeather; + mNextWeather = ""; + } + } + + if (mNextWeather != "") + result = transition(1-(mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600))); + else + result = getResult(mCurrentWeather); + + mRendering->configureFog(result.mFogDepth, result.mFogColor); + + // disable sun during night + if (mHour >= 20 || mHour <= 6.f) + mRendering->getSkyManager()->sunDisable(); + else + mRendering->getSkyManager()->sunEnable(); + + // sun angle + float height; + + // 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); + + int facing = (mHour > 13.f) ? 1 : -1; + + 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; + + night /= 20.f; + + 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 secunda( + (1-moonHeight)*facing*0.8, + (1-moonHeight)*facing*1.25, + moonHeight); + + mRendering->getSkyManager()->setMasserDirection(masser); + mRendering->getSkyManager()->setSecundaDirection(secunda); + mRendering->getSkyManager()->masserEnable(); + mRendering->getSkyManager()->secundaEnable(); + + float hour_fade; + if (mHour >= 7.f && mHour <= 14.f) + hour_fade = 1-(mHour-7)/3.f; + else if (mHour >= 14 && mHour <= 15.f) + hour_fade = mHour-14; + else + hour_fade = 1; + + float secunda_angle_fade; + float masser_angle_fade; + float angle = moonHeight*90.f; + + if (angle >= 30 && angle <= 50) + secunda_angle_fade = (angle-30)/20.f; + else if (angle <30) + secunda_angle_fade = 0.f; + else + secunda_angle_fade = 1.f; + + if (angle >= 40 && angle <= 50) + masser_angle_fade = (angle-40)/10.f; + else if (angle <40) + masser_angle_fade = 0.f; + else + masser_angle_fade = 1.f; + + masser_angle_fade *= hour_fade; + secunda_angle_fade *= hour_fade; + + mRendering->getSkyManager()->setMasserFade(masser_angle_fade); + mRendering->getSkyManager()->setSecundaFade(secunda_angle_fade); + } + else + { + mRendering->getSkyManager()->masserDisable(); + mRendering->getSkyManager()->secundaDisable(); + } + + if (mCurrentWeather == "thunderstorm" && mNextWeather == "" && exterior) + { + if (mThunderFlash > 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 = WeatherGlobals::mThunderSoundID0; + else if (sound == 1) soundname = WeatherGlobals::mThunderSoundID1; + else if (sound == 2) soundname = WeatherGlobals::mThunderSoundID2; + else if (sound == 3) soundname = WeatherGlobals::mThunderSoundID3; + mEnvironment->mSoundManager->playSound(soundname, 1.0, 1.0); + mThunderSoundDelay = 1000; + } + + mThunderFlash -= duration; + if (mThunderFlash > 0) + mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold ); + else + { + srand(time(NULL)); + mThunderChanceNeeded = rand() % 100; + mThunderChance = 0; + mRendering->getSkyManager()->setThunder( 0.f ); + } + } + else + { + // no thunder active + mThunderChance += duration*4; // chance increases by 4 percent every second + if (mThunderChance >= mThunderChanceNeeded) + { + mThunderFlash = WeatherGlobals::mThunderThreshold; + + mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold ); + + mThunderSoundDelay = WeatherGlobals::mThunderSoundDelay; + } + } + } + else + mRendering->getSkyManager()->setThunder(0.f); + + mRendering->setAmbientColour(result.mAmbientColor); + mRendering->sunEnable(); + mRendering->setSunColour(result.mSunColor); + + mRendering->getSkyManager()->setWeather(result); + } + else + { + mRendering->sunDisable(); + mRendering->skyDisable(); + mRendering->getSkyManager()->setThunder(0.f); + } + + // play sounds + std::string ambientSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID : ""); + if (!exterior) ambientSnd = ""; + if (ambientSnd != "") + { + if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) + { + mSoundsPlaying.push_back(ambientSnd); + mEnvironment->mSoundManager->playSound(ambientSnd, 1.0, 1.0, true); + } + } + + std::string rainSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mRainLoopSoundID : ""); + if (!exterior) rainSnd = ""; + if (rainSnd != "") + { + if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) + { + mSoundsPlaying.push_back(rainSnd); + mEnvironment->mSoundManager->playSound(rainSnd, 1.0, 1.0, true); + } + } + + // stop sounds + std::vector::iterator it=mSoundsPlaying.begin(); + while (it!=mSoundsPlaying.end()) + { + if ( *it != ambientSnd && *it != rainSnd) + { + mEnvironment->mSoundManager->stopSound(*it); + it = mSoundsPlaying.erase(it); + } + else + ++it; + } +} + +void WeatherManager::setHour(const float hour) +{ + mHour = hour; +} + +void WeatherManager::setDate(const int day, const int month) +{ + mDay = day; + mMonth = month; +} + +unsigned int WeatherManager::getWeatherID() const +{ + // Source: http://www.uesp.net/wiki/Tes3Mod:GetCurrentWeather + + if (mCurrentWeather == "clear") + return 0; + else if (mCurrentWeather == "cloudy") + return 1; + else if (mCurrentWeather == "foggy") + return 2; + else if (mCurrentWeather == "overcast") + return 3; + else if (mCurrentWeather == "rain") + return 4; + else if (mCurrentWeather == "thunderstorm") + return 5; + else if (mCurrentWeather == "ashstorm") + return 6; + else if (mCurrentWeather == "blight") + return 7; + else if (mCurrentWeather == "snow") + return 8; + else if (mCurrentWeather == "blizzard") + return 9; + + else + return 0; +} + +void WeatherManager::changeWeather(const std::string& region, const unsigned int id) +{ + std::string weather; + if (id==0) + weather = "clear"; + else if (id==1) + weather = "cloudy"; + else if (id==2) + weather = "foggy"; + else if (id==3) + weather = "overcast"; + else if (id==4) + weather = "rain"; + else if (id==5) + weather = "thunderstorm"; + else if (id==6) + weather = "ashstorm"; + else if (id==7) + weather = "blight"; + else if (id==8) + weather = "snow"; + else if (id==9) + weather = "blizzard"; + else + weather = "clear"; + + mRegionOverrides[region] = weather; +} diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp new file mode 100644 index 000000000..b9b40e6fa --- /dev/null +++ b/apps/openmw/mwworld/weather.hpp @@ -0,0 +1,274 @@ +#ifndef GAME_MWWORLD_WEATHER_H +#define GAME_MWWORLD_WEATHER_H + +#include +#include + +namespace MWRender +{ + class RenderingManager; +} + +namespace MWWorld +{ + class Environment; + + /// Global weather manager properties (according to INI) + struct WeatherGlobals + { + /* + [Weather] + EnvReduceColor=255,255,255,255 + LerpCloseColor=037,046,048,255 + BumpFadeColor=230,239,255,255 + AlphaReduce=0.35 + Minimum Time Between Environmental Sounds=1.0 + Maximum Time Between Environmental Sounds=5.0 + Sun Glare Fader Max=0.5 + Sun Glare Fader Angle Max=30.0 + Sun Glare Fader Color=222,095,039 + Timescale Clouds=0 + Precip Gravity=575 + Hours Between Weather Changes=20 + Rain Ripples=1 + Rain Ripple Radius=1024 + Rain Ripples Per Drop=1 + Rain Ripple Scale=0.3 + Rain Ripple Speed=1.0 + Fog Depth Change Speed=3 + Sunrise Time=6 + Sunset Time=18 + Sunrise Duration=2 + Sunset Duration=2 + Sky Pre-Sunrise Time=.5 + Sky Post-Sunrise Time=1 + Sky Pre-Sunset Time=1.5 + Sky Post-Sunset Time=.5 + Ambient Pre-Sunrise Time=.5 + Ambient Post-Sunrise Time=2 + Ambient Pre-Sunset Time=1 + Ambient Post-Sunset Time=1.25 + Fog Pre-Sunrise Time=.5 + Fog Post-Sunrise Time=1 + Fog Pre-Sunset Time=2 + Fog Post-Sunset Time=1 + Sun Pre-Sunrise Time=0 + Sun Post-Sunrise Time=0 + Sun Pre-Sunset Time=1 + Sun Post-Sunset Time=1.25 + Stars Post-Sunset Start=1 + Stars Pre-Sunrise Finish=2 + Stars Fading Duration=2 + Snow Ripples=0 + Snow Ripple Radius=1024 + Snow Ripples Per Flake=1 + Snow Ripple Scale=0.3 + Snow Ripple Speed=1.0 + Snow Gravity Scale=0.1 + Snow High Kill=700 + Snow Low Kill=150 + + + [Moons] + Masser Size=94 + Masser Fade In Start=14 + Masser Fade In Finish=15 + Masser Fade Out Start=7 + Masser Fade Out Finish=10 + Masser Axis Offset=35 + Masser Speed=.5 + Masser Daily Increment=1 + Masser Fade Start Angle=50 + Masser Fade End Angle=40 + Masser Moon Shadow Early Fade Angle=0.5 + Secunda Size=40 + Secunda Fade In Start=14 + Secunda Fade In Finish=15 + Secunda Fade Out Start=7 + Secunda Fade Out Finish=10 + Secunda Axis Offset=50 + Secunda Speed=.6 + Secunda Daily Increment=1.2 + Secunda Fade Start Angle=50 + Secunda Fade End Angle=30 + Secunda Moon Shadow Early Fade Angle=0.5 + Script Color=255,20,20 + */ + + static const float mSunriseTime; + static const float mSunsetTime; + static const float mSunriseDuration; + static const float mSunsetDuration; + + static const float mWeatherUpdateTime; + + // morrowind sets these per-weather, but since they are only used by 'thunderstorm' + // weather setting anyway, we can just as well set them globally + static const float mThunderFrequency; + static const float mThunderThreshold; + static const float mThunderSoundDelay; + static const std::string mThunderSoundID0; + static const std::string mThunderSoundID1; + static const std::string mThunderSoundID2; + static const std::string mThunderSoundID3; + }; + + /// Defines the actual weather that results from weather setting (see below), time of day and weather transition + struct WeatherResult + { + Ogre::String mCloudTexture; + Ogre::String mNextCloudTexture; + float mCloudBlendFactor; + + Ogre::ColourValue mFogColor; + + Ogre::ColourValue mAmbientColor; + + Ogre::ColourValue mSkyColor; + + Ogre::ColourValue mSunColor; + + Ogre::ColourValue mSunDiscColor; + + float mFogDepth; + + float mWindSpeed; + + float mCloudSpeed; + + float mCloudOpacity; + + float mGlareView; + + bool mNight; // use night skybox + float mNightFade; // fading factor for night skybox + + Ogre::String mAmbientLoopSoundID; + }; + + + /// Defines a single weather setting (according to INI) + struct Weather + { + Ogre::String mCloudTexture; + + // Sky (atmosphere) colors + Ogre::ColourValue mSkySunriseColor, + mSkyDayColor, + mSkySunsetColor, + mSkyNightColor; + + // Fog colors + Ogre::ColourValue mFogSunriseColor, + mFogDayColor, + mFogSunsetColor, + mFogNightColor; + + // Ambient lighting colors + Ogre::ColourValue mAmbientSunriseColor, + mAmbientDayColor, + mAmbientSunsetColor, + mAmbientNightColor; + + // Sun (directional) lighting colors + Ogre::ColourValue mSunSunriseColor, + mSunDayColor, + mSunSunsetColor, + mSunNightColor; + + // Fog depth/density + float mLandFogDayDepth, + mLandFogNightDepth; + + // Color modulation for the sun itself during sunset (not completely sure) + Ogre::ColourValue mSunDiscSunsetColor; + + // Duration of weather transition (in days) + float mTransitionDelta; + + // No idea what this one is used for? + float mWindSpeed; + + // Cloud animation speed multiplier + float mCloudSpeed; + + // Multiplier for clouds transparency + float mCloudsMaximumPercent; + + // Value between 0 and 1, defines the strength of the sun glare effect + float mGlareView; + + // Sound effect + // This is used for Blight, Ashstorm and Blizzard (Bloodmoon) + Ogre::String mAmbientLoopSoundID; + + // Rain sound effect + Ogre::String mRainLoopSoundID; + + /// \todo disease chance + }; + + /// + /// Interface for weather settings + /// + class WeatherManager + { + public: + WeatherManager(MWRender::RenderingManager*, MWWorld::Environment*); + + /** + * Change the weather in the specified region + * @param region that should be changed + * @param ID of the weather setting to shift to + */ + void changeWeather(const std::string& region, const unsigned int id); + + /** + * Per-frame update + * @param duration + */ + void update(float duration); + + void setHour(const float hour); + + void setDate(const int day, const int month); + + unsigned int getWeatherID() const; + + private: + float mHour; + int mDay, mMonth; + + MWRender::RenderingManager* mRendering; + MWWorld::Environment* mEnvironment; + + std::map mWeatherSettings; + + std::map mRegionOverrides; + + std::vector mSoundsPlaying; + + Ogre::String mCurrentWeather; + Ogre::String mNextWeather; + + std::string mCurrentRegion; + + bool mFirstUpdate; + + float mWeatherUpdateTime; + + float mRemainingTransitionTime; + + float mThunderFlash; + float mThunderChance; + float mThunderChanceNeeded; + float mThunderSoundDelay; + + WeatherResult transition(const float factor); + WeatherResult getResult(const Ogre::String& weather); + + void setWeather(const Ogre::String& weather, bool instant=false); + }; +} + +#endif // GAME_MWWORLD_WEATHER_H diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index b694d45cc..f0b846c5a 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -13,15 +13,20 @@ #include "../mwsound/soundmanager.hpp" + #include "ptr.hpp" #include "environment.hpp" #include "class.hpp" #include "player.hpp" +#include "weather.hpp" #include "refdata.hpp" #include "globals.hpp" #include "cellfunctors.hpp" +#include +using namespace Ogre; + namespace { template @@ -135,24 +140,31 @@ namespace MWWorld void World::adjustSky() { - if (mSky) + if (mSky && (isCellExterior() || isCellQuasiExterior())) { - toggleSky(); - // TODO set weather - toggleSky(); + mRendering->skySetHour (mGlobalVariables->getFloat ("gamehour")); + mRendering->skySetDate (mGlobalVariables->getInt ("day"), + mGlobalVariables->getInt ("month")); + + mRendering->getSkyManager()->enable(); } + else + mRendering->getSkyManager()->disable(); } - World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, + World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment, const std::string& encoding) - : mRendering (renderer,resDir, physEng),mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (false), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this) + : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), + mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this) { - mPhysEngine = physEng; + mPhysics = new PhysicsSystem(renderer); + mPhysEngine = mPhysics->getEngine(); - mPhysics = new PhysicsSystem(renderer, physEng); + mRendering = new MWRender::RenderingManager(renderer, resDir, mPhysEngine, environment); + + mWeatherManager = new MWWorld::WeatherManager(mRendering, &environment); boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master)); @@ -163,7 +175,7 @@ namespace MWWorld mEsm.open (masterPath.string()); mStore.load (mEsm); - MWRender::Player* play = &(mRendering.getPlayer()); + MWRender::Player* play = &(mRendering->getPlayer()); mPlayer = new MWWorld::Player (play, mStore.npcs.find ("player"), *this); mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); @@ -176,17 +188,17 @@ namespace MWWorld mGlobalVariables->setInt ("chargenstate", 1); } - mPhysEngine = physEng; - - mWorldScene = new Scene(environment, this, mRendering, mPhysics); + mWorldScene = new Scene(environment, this, *mRendering, mPhysics); } + World::~World() { + delete mWeatherManager; delete mWorldScene; delete mGlobalVariables; - + delete mRendering; delete mPhysics; delete mPlayer; @@ -368,7 +380,9 @@ namespace MWWorld mGlobalVariables->setFloat ("gamehour", hour); - mRendering.skySetHour (hour); + mRendering->skySetHour (hour); + + mWeatherManager->setHour (hour); if (days>0) setDay (days + mGlobalVariables->getInt ("day")); @@ -403,7 +417,11 @@ namespace MWWorld mGlobalVariables->setInt ("day", day); mGlobalVariables->setInt ("month", month); - mRendering.skySetDate (day, month); + mRendering->skySetDate (day, month); + + mWeatherManager->setDate (day, month); + + } void World::setMonth (int month) @@ -424,7 +442,7 @@ namespace MWWorld if (years>0) mGlobalVariables->setInt ("year", years+mGlobalVariables->getInt ("year")); - mRendering.skySetDate (mGlobalVariables->getInt ("day"), month); + mRendering->skySetDate (mGlobalVariables->getInt ("day"), month); } bool World::toggleSky() @@ -432,39 +450,35 @@ namespace MWWorld if (mSky) { mSky = false; - mRendering.skyDisable(); + mRendering->skyDisable(); return false; } else { mSky = true; - // TODO check for extorior or interior with sky. - mRendering.skySetHour (mGlobalVariables->getFloat ("gamehour")); - mRendering.skySetDate (mGlobalVariables->getInt ("day"), - mGlobalVariables->getInt ("month")); - mRendering.skyEnable(); + mRendering->skyEnable(); return true; } } int World::getMasserPhase() const { - return mRendering.skyGetMasserPhase(); + return mRendering->skyGetMasserPhase(); } int World::getSecundaPhase() const { - return mRendering.skyGetSecundaPhase(); + return mRendering->skyGetSecundaPhase(); } void World::setMoonColour (bool red) { - mRendering.skySetMoonColour (red); + mRendering->skySetMoonColour (red); } float World::getTimeScaleFactor() const { - return mGlobalVariables->getInt ("timescale"); + return mGlobalVariables->getFloat ("timescale"); } void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) @@ -505,7 +519,7 @@ namespace MWWorld mEnvironment.mSoundManager->stopSound3D (ptr); mPhysics->removeObject (ptr.getRefData().getHandle()); - mRendering.removeObject(ptr); + mRendering->removeObject(ptr); mLocalScripts.remove (ptr); } @@ -521,7 +535,7 @@ namespace MWWorld { //std::cout << "X:" << ptr.getRefData().getPosition().pos[0] << " Z: " << ptr.getRefData().getPosition().pos[1] << "\n"; - mRendering.checkUnderwater( ptr.getRefData().getPosition().pos[2]); + mRendering->checkUnderwater( ptr.getRefData().getPosition().pos[2]); Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { @@ -544,7 +558,7 @@ namespace MWWorld /// \todo cell change for non-player ref - mRendering.moveObject (ptr, Ogre::Vector3 (x, y, z)); + mRendering->moveObject (ptr, Ogre::Vector3 (x, y, z)); } void World::moveObject (Ptr ptr, float x, float y, float z) @@ -613,12 +627,12 @@ namespace MWWorld bool World::toggleCollisionMode() { - return mPhysics->toggleCollisionMode(); + return mPhysics->toggleCollisionMode();; } bool World::toggleRenderMode (RenderMode mode) { - return mRendering.toggleRenderMode (mode); + return mRendering->toggleRenderMode (mode); } std::pair World::createRecord (const ESM::Potion& record) @@ -675,4 +689,71 @@ namespace MWWorld return cell; } } + + void World::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, + int number) + { + mRendering->playAnimationGroup (ptr, groupName, mode, number); + } + + void World::skipAnimation (const MWWorld::Ptr& ptr) + { + mRendering->skipAnimation (ptr); + } + + void World::update (float duration) + { + mWorldScene->update (duration); + + mWeatherManager->update (duration); + + // cast a ray from player to sun to detect if the sun is visible + // this is temporary until we find a better place to put this code + // currently its here because we need to access the physics system + float* p = mPlayer->getPlayer().getRefData().getPosition().pos; + Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); + sun = Vector3(sun.x, -sun.z, sun.y); + mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); + } + + bool World::isCellExterior() const + { + Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + if (currentCell) + { + if (!(currentCell->cell->data.flags & ESM::Cell::Interior)) + return true; + else + return false; + } + return false; + } + + bool World::isCellQuasiExterior() const + { + Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + if (currentCell) + { + if (!(currentCell->cell->data.flags & ESM::Cell::QuasiEx)) + return false; + else + return true; + } + return false; + } + + int World::getCurrentWeather() const + { + return mWeatherManager->getWeatherID(); + } + + void World::changeWeather(const std::string& region, const unsigned int id) + { + mWeatherManager->changeWeather(region, id); + } + + OEngine::Render::Fader* World::getFader() + { + return mRendering->getFader(); + } } diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index ea775453a..71cca3545 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -20,6 +20,7 @@ #include "localscripts.hpp" #include +#include namespace Ogre { @@ -49,6 +50,7 @@ namespace MWRender namespace MWWorld { + class WeatherManager; class Environment; class Player; @@ -60,13 +62,16 @@ namespace MWWorld enum RenderMode { - Render_CollisionDebug + Render_CollisionDebug, + Render_Wireframe }; private: - MWRender::RenderingManager mRendering; + MWRender::RenderingManager* mRendering; + MWWorld::WeatherManager* mWeatherManager; + MWWorld::Scene *mWorldScene; MWWorld::Player *mPlayer; ESM::ESMReader mEsm; @@ -95,17 +100,19 @@ namespace MWWorld public: - World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, + World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment, const std::string& encoding); ~World(); + + OEngine::Render::Fader* getFader(); Ptr::CellStore *getExterior (int x, int y); Ptr::CellStore *getInterior (const std::string& name); - + void adjustSky(); MWWorld::Player& getPlayer(); @@ -118,6 +125,9 @@ namespace MWWorld bool hasCellChanged() const; ///< Has the player moved to a different cell, since the last frame? + + bool isCellExterior() const; + bool isCellQuasiExterior() const; Globals::Data& getGlobalVariable (const std::string& name); @@ -132,23 +142,31 @@ namespace MWWorld Ptr getPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle. - + /// \todo enable reference in the OGRE scene void enable (Ptr reference); - + /// \todo 5disable reference in the OGRE scene void disable (Ptr reference); void advanceTime (double hours); + ///< Advance in-game time. void setHour (double hour); + ///< Set in-game time hour. void setMonth (int month); + ///< Set in-game time month. void setDay (int day); + ///< Set in-game time day. bool toggleSky(); ///< \return Resulting mode + + void changeWeather(const std::string& region, const unsigned int id); + + int getCurrentWeather() const; int getMasserPhase() const; @@ -206,6 +224,21 @@ namespace MWWorld const ESM::Cell *createRecord (const ESM::Cell& record); ///< Create a new recrod (of type cell) in the ESM store. /// \return ID, pointer to created record + + void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, + int number = 1); + ///< Run animation for a MW-reference. Calls to this function for references that are + /// currently not in the rendered scene should be ignored. + /// + /// \param mode: 0 normal, 1 immediate start, 2 immediate loop + /// \param number How offen the animation should be run + + void skipAnimation (const MWWorld::Ptr& ptr); + ///< Skip the animation for the given MW-reference for one frame. Calls to this function for + /// references that are currently not in the rendered scene should be ignored. + + void update (float duration); + }; } diff --git a/cmake/FindCg.cmake b/cmake/FindCg.cmake new file mode 100644 index 000000000..4bd348c46 --- /dev/null +++ b/cmake/FindCg.cmake @@ -0,0 +1,53 @@ +#------------------------------------------------------------------- +# 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 Cg +# Once done, this will define +# +# Cg_FOUND - system has Cg +# Cg_INCLUDE_DIRS - the Cg include directories +# Cg_LIBRARIES - link these to use Cg + +include(FindPkgMacros) +findpkg_begin(Cg) + +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(Cg_HOME) +getenv_path(OGRE_SOURCE) +getenv_path(OGRE_HOME) + +# construct search paths +set(Cg_PREFIX_PATH ${Cg_HOME} ${ENV_Cg_HOME} + ${OGRE_SOURCE}/Dependencies + ${ENV_OGRE_SOURCE}/Dependencies + ${OGRE_HOME} ${ENV_OGRE_HOME} + /opt/nvidia-cg-toolkit) +create_search_paths(Cg) +# redo search if prefix path changed +clear_if_changed(Cg_PREFIX_PATH + Cg_LIBRARY_FWK + Cg_LIBRARY_REL + Cg_LIBRARY_DBG + Cg_INCLUDE_DIR +) + +set(Cg_LIBRARY_NAMES Cg) +get_debug_names(Cg_LIBRARY_NAMES) + +use_pkgconfig(Cg_PKGC Cg) + +findpkg_framework(Cg) + +find_path(Cg_INCLUDE_DIR NAMES cg.h HINTS ${Cg_FRAMEWORK_INCLUDES} ${Cg_INC_SEARCH_PATH} ${Cg_PKGC_INCLUDE_DIRS} PATH_SUFFIXES Cg) +find_library(Cg_LIBRARY_REL NAMES ${Cg_LIBRARY_NAMES} HINTS ${Cg_LIB_SEARCH_PATH} ${Cg_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel) +find_library(Cg_LIBRARY_DBG NAMES ${Cg_LIBRARY_NAMES_DBG} HINTS ${Cg_LIB_SEARCH_PATH} ${Cg_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug) +make_library_set(Cg_LIBRARY) + +findpkg_finish(Cg) +add_parent_dir(Cg_INCLUDE_DIRS Cg_INCLUDE_DIR) diff --git a/cmake/FindFreeImage.cmake b/cmake/FindFreeImage.cmake new file mode 100644 index 000000000..3b21a17d6 --- /dev/null +++ b/cmake/FindFreeImage.cmake @@ -0,0 +1,47 @@ +#------------------------------------------------------------------- +# 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 FreeImage +# Once done, this will define +# +# FreeImage_FOUND - system has FreeImage +# FreeImage_INCLUDE_DIRS - the FreeImage include directories +# FreeImage_LIBRARIES - link these to use FreeImage + +include(FindPkgMacros) +findpkg_begin(FreeImage) + +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(FREEIMAGE_HOME) + +# construct search paths +set(FreeImage_PREFIX_PATH ${FREEIMAGE_HOME} ${ENV_FREEIMAGE_HOME}) +create_search_paths(FreeImage) +# redo search if prefix path changed +clear_if_changed(FreeImage_PREFIX_PATH + FreeImage_LIBRARY_FWK + FreeImage_LIBRARY_REL + FreeImage_LIBRARY_DBG + FreeImage_INCLUDE_DIR +) + +set(FreeImage_LIBRARY_NAMES freeimage) +get_debug_names(FreeImage_LIBRARY_NAMES) + +use_pkgconfig(FreeImage_PKGC freeimage) + +findpkg_framework(FreeImage) + +find_path(FreeImage_INCLUDE_DIR NAMES FreeImage.h HINTS ${FreeImage_INC_SEARCH_PATH} ${FreeImage_PKGC_INCLUDE_DIRS}) +find_library(FreeImage_LIBRARY_REL NAMES ${FreeImage_LIBRARY_NAMES} HINTS ${FreeImage_LIB_SEARCH_PATH} ${FreeImage_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel) +find_library(FreeImage_LIBRARY_DBG NAMES ${FreeImage_LIBRARY_NAMES_DBG} HINTS ${FreeImage_LIB_SEARCH_PATH} ${FreeImage_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug) +make_library_set(FreeImage_LIBRARY) + +findpkg_finish(FreeImage) + diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake index ce3993805..78d0ef24e 100644 --- a/cmake/FindOGRE.cmake +++ b/cmake/FindOGRE.cmake @@ -1,110 +1,538 @@ -# Find OGRE includes and library +#------------------------------------------------------------------- +# 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/ # -# This module defines -# OGRE_INCLUDE_DIR -# OGRE_LIBRARIES, the libraries to link against to use OGRE. -# OGRE_LIB_DIR, the location of the libraries -# OGRE_FOUND, If false, do not try to use OGRE +# 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 OGRE +# If you have multiple versions of Ogre installed, use the CMake or +# the environment variable OGRE_HOME to point to the path where the +# desired Ogre version can be found. +# By default this script will look for a dynamic Ogre build. If you +# need to link against static Ogre libraries, set the CMake variable +# OGRE_STATIC to TRUE. # -# Copyright © 2007, Matt Williams +# Once done, this will define # -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -CMAKE_POLICY(PUSH) +# OGRE_FOUND - system has OGRE +# OGRE_INCLUDE_DIRS - the OGRE include directories +# OGRE_LIBRARIES - link these to use the OGRE core +# OGRE_BINARY_REL - location of the main Ogre binary (win32 non-static only, release) +# OGRE_BINARY_DBG - location of the main Ogre binaries (win32 non-static only, debug) +# +# Additionally this script searches for the following optional +# parts of the Ogre package: +# Plugin_BSPSceneManager, Plugin_CgProgramManager, +# Plugin_OctreeSceneManager, Plugin_OctreeZone, +# Plugin_ParticleFX, Plugin_PCZSceneManager, +# RenderSystem_GL, RenderSystem_Direct3D9, RenderSystem_Direct3D10, +# Paging, Terrain +# +# For each of these components, the following variables are defined: +# +# OGRE_${COMPONENT}_FOUND - ${COMPONENT} is available +# OGRE_${COMPONENT}_INCLUDE_DIRS - additional include directories for ${COMPONENT} +# OGRE_${COMPONENT}_LIBRARIES - link these to use ${COMPONENT} +# OGRE_${COMPONENT}_BINARY_REL - location of the component binary (win32 non-static only, release) +# OGRE_${COMPONENT}_BINARY_DBG - location of the component binary (win32 non-static only, debug) +# +# Finally, the following variables are defined: +# +# OGRE_PLUGIN_DIR_REL - The directory where the release versions of +# the OGRE plugins are located +# OGRE_PLUGIN_DIR_DBG - The directory where the debug versions of +# the OGRE plugins are located +# OGRE_MEDIA_DIR - The directory where the OGRE sample media is +# located, if available -IF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR) - SET(OGRE_FIND_QUIETLY TRUE) # Already in cache, be silent -ENDIF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR) +include(FindPkgMacros) +include(PreprocessorUtils) +findpkg_begin(OGRE) -IF (WIN32) #Windows - MESSAGE(STATUS "Looking for OGRE") - SET(OGRESDK $ENV{OGRE_HOME}) - SET(OGRESOURCE $ENV{OGRE_SRC}) - IF (OGRESDK) - MESSAGE(STATUS "Using OGRE SDK") - STRING(REGEX REPLACE "[\\]" "/" OGRESDK "${OGRESDK}") - SET(OGRE_INCLUDE_DIR ${OGRESDK}/include) - SET(OGRE_LIB_DIR ${OGRESDK}/lib) - SET(OGRE_LIBRARIES debug OgreMain_d optimized OgreMain) - ENDIF (OGRESDK) - IF (OGRESOURCE) - MESSAGE(STATUS "Using OGRE built from source") - SET(OGRE_INCLUDE_DIR $ENV{OGRE_SRC}/OgreMain/include) - SET(OGRE_LIB_DIR $ENV{OGRE_SRC}/lib) - SET(OGRE_LIBRARIES debug OgreMain_d optimized OgreMain) - ENDIF (OGRESOURCE) -ENDIF (WIN32) -IF (UNIX AND NOT APPLE) - CMAKE_MINIMUM_REQUIRED(VERSION 2.4.7 FATAL_ERROR) - FIND_PACKAGE(PkgConfig REQUIRED) - # Don't mark REQUIRED, but use PKG_CHECK_MODULES below (otherwise PkgConfig - # complains even if OGRE_* are set by hand). - PKG_SEARCH_MODULE(OGRE OGRE) - SET(OGRE_INCLUDE_DIR ${OGRE_INCLUDE_DIRS}) - SET(OGRE_LIB_DIR ${OGRE_LIBDIR}) - SET(OGRE_LIBRARIES ${OGRE_LIBRARIES} CACHE STRING "") - PKG_CHECK_MODULES(OGRE OGRE) -ENDIF (UNIX AND NOT APPLE) +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(OGRE_HOME) +getenv_path(OGRE_SDK) +getenv_path(OGRE_SOURCE) +getenv_path(OGRE_BUILD) +getenv_path(OGRE_DEPENDENCIES_DIR) +getenv_path(PROGRAMFILES) -# on OS X we need Ogre SDK because framework doesn't include all libs, just Ogre Main lib -IF (APPLE) - IF (OGRESDK) - MESSAGE(STATUS "Using Ogre SDK") - SET(OGRE_LIB_DIR ${OGRESDK}/lib) - ELSE (OGRESDK) - MESSAGE(FATAL_ERROR "Path to Ogre SDK not specified. Specify OGRESDK.") - ENDIF (OGRESDK) - +# Determine whether to search for a dynamic or static build +if (OGRE_STATIC) + set(OGRE_LIB_SUFFIX "Static") +else () + set(OGRE_LIB_SUFFIX "") +endif () - FIND_PATH(OGRE_INCLUDE_DIR Ogre.h - PATHS - /Library/Frameworks - /opt/local - ) - FIND_LIBRARY(OGRE_LIBRARIES - NAMES Ogre - PATHS - /Library/Frameworks - /opt/local - ) -ENDIF (APPLE) -#Do some preparation -SEPARATE_ARGUMENTS(OGRE_INCLUDE_DIR) -SEPARATE_ARGUMENTS(OGRE_LIBRARIES) +set(OGRE_LIBRARY_NAMES "OgreMain${OGRE_LIB_SUFFIX}") +get_debug_names(OGRE_LIBRARY_NAMES) -SET(OGRE_INCLUDE_DIR ${OGRE_INCLUDE_DIR} CACHE PATH "") -SET(OGRE_LIBRARIES ${OGRE_LIBRARIES} CACHE STRING "") -SET(OGRE_LIB_DIR ${OGRE_LIB_DIR} CACHE PATH "") +# construct search paths from environmental hints and +# OS specific guesses +if (WIN32) + set(OGRE_PREFIX_GUESSES + ${ENV_PROGRAMFILES}/OGRE + C:/OgreSDK + ) +elseif (UNIX) + set(OGRE_PREFIX_GUESSES + /opt/ogre + /opt/OGRE + /usr/lib${LIB_SUFFIX}/ogre + /usr/lib${LIB_SUFFIX}/OGRE + /usr/local/lib${LIB_SUFFIX}/ogre + /usr/local/lib${LIB_SUFFIX}/OGRE + $ENV{HOME}/ogre + $ENV{HOME}/OGRE + ) +endif () +set(OGRE_PREFIX_PATH + ${OGRE_HOME} ${OGRE_SDK} ${ENV_OGRE_HOME} ${ENV_OGRE_SDK} + ${OGRE_PREFIX_GUESSES} +) +create_search_paths(OGRE) +# If both OGRE_BUILD and OGRE_SOURCE are set, prepare to find Ogre in a build dir +set(OGRE_PREFIX_SOURCE ${OGRE_SOURCE} ${ENV_OGRE_SOURCE}) +set(OGRE_PREFIX_BUILD ${OGRE_BUILD} ${ENV_OGRE_BUILD}) +set(OGRE_PREFIX_DEPENDENCIES_DIR ${OGRE_DEPENDENCIES_DIR} ${ENV_OGRE_DEPENDENCIES_DIR}) +if (OGRE_PREFIX_SOURCE AND OGRE_PREFIX_BUILD) + foreach(dir ${OGRE_PREFIX_SOURCE}) + set(OGRE_INC_SEARCH_PATH ${dir}/OgreMain/include ${dir}/Dependencies/include ${dir}/iPhoneDependencies/include ${OGRE_INC_SEARCH_PATH}) + set(OGRE_LIB_SEARCH_PATH ${dir}/lib ${dir}/Dependencies/lib ${dir}/iPhoneDependencies/lib ${OGRE_LIB_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${dir}/Samples/Common/bin ${OGRE_BIN_SEARCH_PATH}) + endforeach(dir) + foreach(dir ${OGRE_PREFIX_BUILD}) + set(OGRE_INC_SEARCH_PATH ${dir}/include ${OGRE_INC_SEARCH_PATH}) + set(OGRE_LIB_SEARCH_PATH ${dir}/lib ${OGRE_LIB_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${dir}/bin ${OGRE_BIN_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${dir}/Samples/Common/bin ${OGRE_BIN_SEARCH_PATH}) + endforeach(dir) + + if (OGRE_PREFIX_DEPENDENCIES_DIR) + set(OGRE_INC_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/include ${OGRE_INC_SEARCH_PATH}) + set(OGRE_LIB_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/lib ${OGRE_LIB_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/bin ${OGRE_BIN_SEARCH_PATH}) + endif() +else() + set(OGRE_PREFIX_SOURCE "NOTFOUND") + set(OGRE_PREFIX_BUILD "NOTFOUND") +endif () -if(OGRE_LIB_DIR) - CMAKE_POLICY(SET CMP0009 NEW) - IF (NOT APPLE) - FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.so") - ENDIF (NOT APPLE) - IF (APPLE) - FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.dylib") - ENDIF (APPLE) - FOREACH (OGRE_PLUGINS_FILE ${OGRE_PLUGINS}) - STRING(REGEX REPLACE "/[^/]*$" "" OGRE_PLUGIN_DIR ${OGRE_PLUGINS_FILE}) - ENDFOREACH(OGRE_PLUGINS_FILE) +# redo search if any of the environmental hints changed +set(OGRE_COMPONENTS Paging Terrain + Plugin_BSPSceneManager Plugin_CgProgramManager Plugin_OctreeSceneManager + Plugin_OctreeZone Plugin_PCZSceneManager Plugin_ParticleFX + RenderSystem_Direct3D10 RenderSystem_Direct3D9 RenderSystem_GL RenderSystem_GLES) +set(OGRE_RESET_VARS + OGRE_CONFIG_INCLUDE_DIR OGRE_INCLUDE_DIR + OGRE_LIBRARY_FWK OGRE_LIBRARY_REL OGRE_LIBRARY_DBG + OGRE_PLUGIN_DIR_DBG OGRE_PLUGIN_DIR_REL OGRE_MEDIA_DIR) +foreach (comp ${OGRE_COMPONENTS}) + set(OGRE_RESET_VARS ${OGRE_RESET_VARS} + OGRE_${comp}_INCLUDE_DIR OGRE_${comp}_LIBRARY_FWK + OGRE_${comp}_LIBRARY_DBG OGRE_${comp}_LIBRARY_REL + ) +endforeach (comp) +set(OGRE_PREFIX_WATCH ${OGRE_PREFIX_PATH} ${OGRE_PREFIX_SOURCE} ${OGRE_PREFIX_BUILD}) +clear_if_changed(OGRE_PREFIX_WATCH ${OGRE_RESET_VARS}) + +# try to locate Ogre via pkg-config +use_pkgconfig(OGRE_PKGC "OGRE${OGRE_LIB_SUFFIX}") + +if(NOT OGRE_BUILD_PLATFORM_IPHONE AND APPLE) + # try to find framework on OSX + findpkg_framework(OGRE) +else() + set(OGRE_LIBRARY_FWK "") endif() -IF (OGRE_INCLUDE_DIR AND OGRE_LIBRARIES) - SET(OGRE_FOUND TRUE) -ENDIF (OGRE_INCLUDE_DIR AND OGRE_LIBRARIES) +# locate Ogre include files +find_path(OGRE_CONFIG_INCLUDE_DIR NAMES OgreBuildSettings.h HINTS ${OGRE_INC_SEARCH_PATH} ${OGRE_FRAMEWORK_INCLUDES} ${OGRE_PKGC_INCLUDE_DIRS} PATH_SUFFIXES "OGRE") +find_path(OGRE_INCLUDE_DIR NAMES OgreRoot.h HINTS ${OGRE_CONFIG_INCLUDE_DIR} ${OGRE_INC_SEARCH_PATH} ${OGRE_FRAMEWORK_INCLUDES} ${OGRE_PKGC_INCLUDE_DIRS} PATH_SUFFIXES "OGRE") +set(OGRE_INCOMPATIBLE FALSE) -IF (OGRE_FOUND) - IF (NOT OGRE_FIND_QUIETLY) - MESSAGE(STATUS " libraries : ${OGRE_LIBRARIES} from ${OGRE_LIB_DIR}") - MESSAGE(STATUS " includes : ${OGRE_INCLUDE_DIR}") - MESSAGE(STATUS " plugins : ${OGRE_PLUGIN_DIR}") - ENDIF (NOT OGRE_FIND_QUIETLY) -ELSE (OGRE_FOUND) - IF (OGRE_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find OGRE") - ENDIF (OGRE_FIND_REQUIRED) -ENDIF (OGRE_FOUND) +if (OGRE_INCLUDE_DIR) + if (NOT OGRE_CONFIG_INCLUDE_DIR) + set(OGRE_CONFIG_INCLUDE_DIR ${OGRE_INCLUDE_DIR}) + endif () + # determine Ogre version + file(READ ${OGRE_INCLUDE_DIR}/OgrePrerequisites.h OGRE_TEMP_VERSION_CONTENT) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_MAJOR OGRE_VERSION_MAJOR) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_MINOR OGRE_VERSION_MINOR) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_PATCH OGRE_VERSION_PATCH) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_NAME OGRE_VERSION_NAME) + set(OGRE_VERSION "${OGRE_VERSION_MAJOR}.${OGRE_VERSION_MINOR}.${OGRE_VERSION_PATCH}") + pkg_message(OGRE "Found Ogre ${OGRE_VERSION_NAME} (${OGRE_VERSION})") + + # determine configuration settings + set(OGRE_CONFIG_HEADERS + ${OGRE_CONFIG_INCLUDE_DIR}/OgreBuildSettings.h + ${OGRE_CONFIG_INCLUDE_DIR}/OgreConfig.h + ) + foreach(CFG_FILE ${OGRE_CONFIG_HEADERS}) + if (EXISTS ${CFG_FILE}) + set(OGRE_CONFIG_HEADER ${CFG_FILE}) + break() + endif() + endforeach() + if (OGRE_CONFIG_HEADER) + file(READ ${OGRE_CONFIG_HEADER} OGRE_TEMP_CONFIG_CONTENT) + has_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_STATIC_LIB OGRE_CONFIG_STATIC) + get_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_THREAD_SUPPORT OGRE_CONFIG_THREADS) + get_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_THREAD_PROVIDER OGRE_CONFIG_THREAD_PROVIDER) + get_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_NO_FREEIMAGE OGRE_CONFIG_FREEIMAGE) + if (OGRE_CONFIG_STATIC AND OGRE_STATIC) + elseif (OGRE_CONFIG_STATIC OR OGRE_STATIC) + pkg_message(OGRE "Build type (static, dynamic) does not match the requested one.") + set(OGRE_INCOMPATIBLE TRUE) + endif () + else () + pkg_message(OGRE "Could not determine Ogre build configuration.") + set(OGRE_INCOMPATIBLE TRUE) + endif () +else () + set(OGRE_INCOMPATIBLE FALSE) +endif () + +find_library(OGRE_LIBRARY_REL NAMES ${OGRE_LIBRARY_NAMES} HINTS ${OGRE_LIB_SEARCH_PATH} ${OGRE_PKGC_LIBRARY_DIRS} ${OGRE_FRAMEWORK_SEARCH_PATH} PATH_SUFFIXES "" "release" "relwithdebinfo" "minsizerel") +find_library(OGRE_LIBRARY_DBG NAMES ${OGRE_LIBRARY_NAMES_DBG} HINTS ${OGRE_LIB_SEARCH_PATH} ${OGRE_PKGC_LIBRARY_DIRS} ${OGRE_FRAMEWORK_SEARCH_PATH} PATH_SUFFIXES "" "debug") +make_library_set(OGRE_LIBRARY) + +if(APPLE) + set(OGRE_LIBRARY_DBG ${OGRE_LIB_SEARCH_PATH}) +endif() +if (OGRE_INCOMPATIBLE) + set(OGRE_LIBRARY "NOTFOUND") +endif () + +set(OGRE_INCLUDE_DIR ${OGRE_CONFIG_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}) +list(REMOVE_DUPLICATES OGRE_INCLUDE_DIR) +findpkg_finish(OGRE) +add_parent_dir(OGRE_INCLUDE_DIRS OGRE_INCLUDE_DIR) +if (OGRE_SOURCE) + # If working from source rather than SDK, add samples include + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} "${OGRE_SOURCE}/Samples/Common/include") +endif() + +mark_as_advanced(OGRE_CONFIG_INCLUDE_DIR OGRE_MEDIA_DIR OGRE_PLUGIN_DIR_REL OGRE_PLUGIN_DIR_DBG) + +if (NOT OGRE_FOUND) + return() +endif () + + +# look for required Ogre dependencies in case of static build and/or threading +if (OGRE_STATIC) + set(OGRE_DEPS_FOUND TRUE) + find_package(Cg QUIET) + find_package(DirectX QUIET) + find_package(FreeImage QUIET) + find_package(Freetype QUIET) + find_package(OpenGL QUIET) + find_package(OpenGLES QUIET) + find_package(ZLIB QUIET) + find_package(ZZip QUIET) + if (UNIX AND NOT APPLE) + find_package(X11 QUIET) + find_library(XAW_LIBRARY NAMES Xaw Xaw7 PATHS ${DEP_LIB_SEARCH_DIR} ${X11_LIB_SEARCH_PATH}) + if (NOT XAW_LIBRARY OR NOT X11_Xt_FOUND) + set(X11_FOUND FALSE) + endif () + endif () + if (APPLE AND NOT OGRE_BUILD_PLATFORM_IPHONE) + find_package(Cocoa QUIET) + find_package(Carbon QUIET) + if (NOT Cocoa_FOUND OR NOT Carbon_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + endif () + if (APPLE AND OGRE_BUILD_PLATFORM_IPHONE) + find_package(iPhoneSDK QUIET) + if (NOT iPhoneSDK_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + endif () + + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${OGRE_LIBRARY_FWK} ${ZZip_LIBRARIES} ${ZLIB_LIBRARIES} + ${FreeImage_LIBRARIES} ${FREETYPE_LIBRARIES} + ${X11_LIBRARIES} ${X11_Xt_LIBRARIES} ${XAW_LIBRARY} ${X11_Xrandr_LIB} + ${Cocoa_LIBRARIES} ${Carbon_LIBRARIES}) + + if (NOT ZLIB_FOUND OR NOT ZZip_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + if (NOT FreeImage_FOUND AND NOT OGRE_CONFIG_FREEIMAGE) + set(OGRE_DEPS_FOUND FALSE) + endif () + if (NOT FREETYPE_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + if (UNIX AND NOT APPLE) + if (NOT X11_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + endif () + + if (OGRE_CONFIG_THREADS) + if (OGRE_CONFIG_THREAD_PROVIDER EQUAL 1) + find_package(Boost COMPONENTS thread QUIET) + if (NOT Boost_THREAD_FOUND) + set(OGRE_DEPS_FOUND FALSE) + else () + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${Boost_LIBRARIES}) + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + endif () + elseif (OGRE_CONFIG_THREAD_PROVIDER EQUAL 2) + find_package(POCO QUIET) + if (NOT POCO_FOUND) + set(OGRE_DEPS_FOUND FALSE) + else () + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${POCO_LIBRARIES}) + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} ${POCO_INCLUDE_DIRS}) + endif () + elseif (OGRE_CONFIG_THREAD_PROVIDER EQUAL 3) + find_package(TBB QUIET) + if (NOT TBB_FOUND) + set(OGRE_DEPS_FOUND FALSE) + else () + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${TBB_LIBRARIES}) + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} ${TBB_INCLUDE_DIRS}) + endif () + endif () + endif () + + if (NOT OGRE_DEPS_FOUND) + pkg_message(OGRE "Could not find all required dependencies for the Ogre package.") + set(OGRE_FOUND FALSE) + endif () +endif () + +if (NOT OGRE_FOUND) + return() +endif () + + +get_filename_component(OGRE_LIBRARY_DIR_REL "${OGRE_LIBRARY_REL}" PATH) +get_filename_component(OGRE_LIBRARY_DIR_DBG "${OGRE_LIBRARY_DBG}" PATH) +set(OGRE_LIBRARY_DIRS ${OGRE_LIBRARY_DIR_REL} ${OGRE_LIBRARY_DIR_DBG}) + +# find binaries +if (NOT OGRE_STATIC) + if (WIN32) + find_file(OGRE_BINARY_REL NAMES "OgreMain.dll" HINTS ${OGRE_BIN_SEARCH_PATH} + PATH_SUFFIXES "" release relwithdebinfo minsizerel) + find_file(OGRE_BINARY_DBG NAMES "OgreMain_d.dll" HINTS ${OGRE_BIN_SEARCH_PATH} + PATH_SUFFIXES "" debug ) + endif() + mark_as_advanced(OGRE_BINARY_REL OGRE_BINARY_DBG) +endif() + + +######################################################### +# Find Ogre components +######################################################### + +set(OGRE_COMPONENT_SEARCH_PATH_REL + ${OGRE_LIBRARY_DIR_REL}/.. + ${OGRE_LIBRARY_DIR_REL}/../.. + ${OGRE_BIN_SEARCH_PATH} +) +set(OGRE_COMPONENT_SEARCH_PATH_DBG + ${OGRE_LIBRARY_DIR_DBG}/.. + ${OGRE_LIBRARY_DIR_DBG}/../.. + ${OGRE_BIN_SEARCH_PATH} +) + +macro(ogre_find_component COMPONENT HEADER) + findpkg_begin(OGRE_${COMPONENT}) + find_path(OGRE_${COMPONENT}_INCLUDE_DIR NAMES ${HEADER} HINTS ${OGRE_INCLUDE_DIRS} ${OGRE_PREFIX_SOURCE} PATH_SUFFIXES ${COMPONENT} OGRE/${COMPONENT} Components/${COMPONENT}/include) + set(OGRE_${COMPONENT}_LIBRARY_NAMES "Ogre${COMPONENT}${OGRE_LIB_SUFFIX}") + get_debug_names(OGRE_${COMPONENT}_LIBRARY_NAMES) + find_library(OGRE_${COMPONENT}_LIBRARY_REL NAMES ${OGRE_${COMPONENT}_LIBRARY_NAMES} HINTS ${OGRE_LIBRARY_DIR_REL} PATH_SUFFIXES "" "release" "relwithdebinfo" "minsizerel") + find_library(OGRE_${COMPONENT}_LIBRARY_DBG NAMES ${OGRE_${COMPONENT}_LIBRARY_NAMES_DBG} HINTS ${OGRE_LIBRARY_DIR_DBG} PATH_SUFFIXES "" "debug") + make_library_set(OGRE_${COMPONENT}_LIBRARY) + findpkg_finish(OGRE_${COMPONENT}) + if (OGRE_${COMPONENT}_FOUND) + # find binaries + if (NOT OGRE_STATIC) + if (WIN32) + find_file(OGRE_${COMPONENT}_BINARY_REL NAMES "Ogre${COMPONENT}.dll" HINTS ${OGRE_COMPONENT_SEARCH_PATH_REL} PATH_SUFFIXES "" bin bin/release bin/relwithdebinfo bin/minsizerel release) + find_file(OGRE_${COMPONENT}_BINARY_DBG NAMES "Ogre${COMPONENT}_d.dll" HINTS ${OGRE_COMPONENT_SEARCH_PATH_DBG} PATH_SUFFIXES "" bin bin/debug debug) + endif() + mark_as_advanced(OGRE_${COMPONENT}_BINARY_REL OGRE_${COMPONENT}_BINARY_DBG) + endif() + endif() +endmacro() + +# look for Paging component +ogre_find_component(Paging OgrePaging.h) +# look for Terrain component +ogre_find_component(Terrain OgreTerrain.h) +# look for Property component +ogre_find_component(Property OgreProperty.h) +# look for RTShaderSystem component +ogre_find_component(RTShaderSystem OgreRTShaderSystem.h) + + +######################################################### +# Find Ogre plugins +######################################################### + +macro(ogre_find_plugin PLUGIN HEADER) + # On Unix, the plugins might have no prefix + if (CMAKE_FIND_LIBRARY_PREFIXES) + set(TMP_CMAKE_LIB_PREFIX ${CMAKE_FIND_LIBRARY_PREFIXES}) + set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "") + endif() + + # strip RenderSystem_ or Plugin_ prefix from plugin name + string(REPLACE "RenderSystem_" "" PLUGIN_TEMP ${PLUGIN}) + string(REPLACE "Plugin_" "" PLUGIN_NAME ${PLUGIN_TEMP}) + + # header files for plugins are not usually needed, but find them anyway if they are present + set(OGRE_PLUGIN_PATH_SUFFIXES + PlugIns PlugIns/${PLUGIN_NAME} Plugins Plugins/${PLUGIN_NAME} ${PLUGIN} + RenderSystems RenderSystems/${PLUGIN_NAME} ${ARGN}) + find_path(OGRE_${PLUGIN}_INCLUDE_DIR NAMES ${HEADER} + HINTS ${OGRE_INCLUDE_DIRS} ${OGRE_PREFIX_SOURCE} + PATH_SUFFIXES ${OGRE_PLUGIN_PATH_SUFFIXES}) + # find link libraries for plugins + set(OGRE_${PLUGIN}_LIBRARY_NAMES "${PLUGIN}${OGRE_LIB_SUFFIX}") + get_debug_names(OGRE_${PLUGIN}_LIBRARY_NAMES) + set(OGRE_${PLUGIN}_LIBRARY_FWK ${OGRE_LIBRARY_FWK}) + find_library(OGRE_${PLUGIN}_LIBRARY_REL NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES} + HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt release release/opt relwithdebinfo relwithdebinfo/opt minsizerel minsizerel/opt) + find_library(OGRE_${PLUGIN}_LIBRARY_DBG NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES_DBG} + HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt debug debug/opt) + make_library_set(OGRE_${PLUGIN}_LIBRARY) + + if (OGRE_${PLUGIN}_LIBRARY OR OGRE_${PLUGIN}_INCLUDE_DIR) + set(OGRE_${PLUGIN}_FOUND TRUE) + if (OGRE_${PLUGIN}_INCLUDE_DIR) + set(OGRE_${PLUGIN}_INCLUDE_DIRS ${OGRE_${PLUGIN}_INCLUDE_DIR}) + endif() + set(OGRE_${PLUGIN}_LIBRARIES ${OGRE_${PLUGIN}_LIBRARY}) + endif () + + mark_as_advanced(OGRE_${PLUGIN}_INCLUDE_DIR OGRE_${PLUGIN}_LIBRARY_REL OGRE_${PLUGIN}_LIBRARY_DBG OGRE_${PLUGIN}_LIBRARY_FWK) + + # look for plugin dirs + if (OGRE_${PLUGIN}_FOUND) + if (NOT OGRE_PLUGIN_DIR_REL OR NOT OGRE_PLUGIN_DIR_DBG) + if (WIN32) + set(OGRE_PLUGIN_SEARCH_PATH_REL + ${OGRE_LIBRARY_DIR_REL}/.. + ${OGRE_LIBRARY_DIR_REL}/../.. + ${OGRE_BIN_SEARCH_PATH} + ) + set(OGRE_PLUGIN_SEARCH_PATH_DBG + ${OGRE_LIBRARY_DIR_DBG}/.. + ${OGRE_LIBRARY_DIR_DBG}/../.. + ${OGRE_BIN_SEARCH_PATH} + ) + find_path(OGRE_PLUGIN_DIR_REL NAMES "${PLUGIN}.dll" HINTS ${OGRE_PLUGIN_SEARCH_PATH_REL} + PATH_SUFFIXES "" bin bin/release bin/relwithdebinfo bin/minsizerel release) + find_path(OGRE_PLUGIN_DIR_DBG NAMES "${PLUGIN}_d.dll" HINTS ${OGRE_PLUGIN_SEARCH_PATH_DBG} + PATH_SUFFIXES "" bin bin/debug debug) + elseif (UNIX) + get_filename_component(OGRE_PLUGIN_DIR_TMP ${OGRE_${PLUGIN}_LIBRARY_REL} PATH) + set(OGRE_PLUGIN_DIR_REL ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (release)") + get_filename_component(OGRE_PLUGIN_DIR_TMP ${OGRE_${PLUGIN}_LIBRARY_DBG} PATH) + set(OGRE_PLUGIN_DIR_DBG ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (debug)") + endif () + endif () + + # find binaries + if (NOT OGRE_STATIC) + if (WIN32) + find_file(OGRE_${PLUGIN}_REL NAMES "${PLUGIN}.dll" HINTS ${OGRE_PLUGIN_DIR_REL}) + find_file(OGRE_${PLUGIN}_DBG NAMES "${PLUGIN}_d.dll" HINTS ${OGRE_PLUGIN_DIR_DBG}) + endif() + mark_as_advanced(OGRE_${PLUGIN}_REL OGRE_${PLUGIN}_DBG) + endif() + + endif () + + if (TMP_CMAKE_LIB_PREFIX) + set(CMAKE_FIND_LIBRARY_PREFIXES ${TMP_CMAKE_LIB_PREFIX}) + endif () +endmacro(ogre_find_plugin) + +ogre_find_plugin(Plugin_PCZSceneManager OgrePCZSceneManager.h PCZ PlugIns/PCZSceneManager/include) +ogre_find_plugin(Plugin_OctreeZone OgreOctreeZone.h PCZ PlugIns/OctreeZone/include) +ogre_find_plugin(Plugin_BSPSceneManager OgreBspSceneManager.h PlugIns/BSPSceneManager/include) +ogre_find_plugin(Plugin_CgProgramManager OgreCgProgram.h PlugIns/CgProgramManager/include) +ogre_find_plugin(Plugin_OctreeSceneManager OgreOctreeSceneManager.h PlugIns/OctreeSceneManager/include) +ogre_find_plugin(Plugin_ParticleFX OgreParticleFXPrerequisites.h PlugIns/ParticleFX/include) +ogre_find_plugin(RenderSystem_GL OgreGLRenderSystem.h RenderSystems/GL/include) +ogre_find_plugin(RenderSystem_GLES OgreGLESRenderSystem.h RenderSystems/GLES/include) +ogre_find_plugin(RenderSystem_Direct3D9 OgreD3D9RenderSystem.h RenderSystems/Direct3D9/include) +ogre_find_plugin(RenderSystem_Direct3D10 OgreD3D10RenderSystem.h RenderSystems/Direct3D10/include) +ogre_find_plugin(RenderSystem_Direct3D11 OgreD3D11RenderSystem.h RenderSystems/Direct3D11/include) + +if (OGRE_STATIC) + # check if dependencies for plugins are met + if (NOT DirectX_FOUND) + set(OGRE_RenderSystem_Direct3D9_FOUND FALSE) + endif () + if (NOT DirectX_D3D10_FOUND) + set(OGRE_RenderSystem_Direct3D10_FOUND FALSE) + endif () + if (NOT DirectX_D3D11_FOUND) + set(OGRE_RenderSystem_Direct3D11_FOUND FALSE) + endif () + if (NOT OPENGL_FOUND) + set(OGRE_RenderSystem_GL_FOUND FALSE) + endif () + if (NOT OPENGLES_FOUND AND NOT OPENGLES2_FOUND) + set(OGRE_RenderSystem_GLES_FOUND FALSE) + endif () + if (NOT Cg_FOUND) + set(OGRE_Plugin_CgProgramManager_FOUND FALSE) + endif () + + set(OGRE_RenderSystem_Direct3D9_LIBRARIES ${OGRE_RenderSystem_Direct3D9_LIBRARIES} + ${DirectX_LIBRARIES} + ) + set(OGRE_RenderSystem_Direct3D10_LIBRARIES ${OGRE_RenderSystem_Direct3D10_LIBRARIES} + ${DirectX_D3D10_LIBRARIES} + ) + set(OGRE_RenderSystem_Direct3D11_LIBRARIES ${OGRE_RenderSystem_Direct3D11_LIBRARIES} + ${DirectX_D3D11_LIBRARIES} + ) + set(OGRE_RenderSystem_GL_LIBRARIES ${OGRE_RenderSystem_GL_LIBRARIES} + ${OPENGL_LIBRARIES} + ) + set(OGRE_RenderSystem_GLES_LIBRARIES ${OGRE_RenderSystem_GLES_LIBRARIES} + ${OPENGLES_LIBRARIES} + ) + set(OGRE_Plugin_CgProgramManager_LIBRARIES ${OGRE_Plugin_CgProgramManager_LIBRARIES} + ${Cg_LIBRARIES} + ) +endif () + +# look for the media directory +set(OGRE_MEDIA_SEARCH_PATH + ${OGRE_SOURCE} + ${OGRE_LIBRARY_DIR_REL}/.. + ${OGRE_LIBRARY_DIR_DBG}/.. + ${OGRE_LIBRARY_DIR_REL}/../.. + ${OGRE_LIBRARY_DIR_DBG}/../.. + ${OGRE_PREFIX_SOURCE} +) +set(OGRE_MEDIA_SEARCH_SUFFIX + Samples/Media + Media + media + share/OGRE/media +) + +clear_if_changed(OGRE_PREFIX_WATCH OGRE_MEDIA_DIR) +find_path(OGRE_MEDIA_DIR NAMES packs/cubemapsJS.zip HINTS ${OGRE_MEDIA_SEARCH_PATH} + PATHS ${OGRE_PREFIX_PATH} PATH_SUFFIXES ${OGRE_MEDIA_SEARCH_SUFFIX}) -CMAKE_POLICY(POP) diff --git a/cmake/FindPkgMacros.cmake b/cmake/FindPkgMacros.cmake index da3303ac5..473b27b2a 100644 --- a/cmake/FindPkgMacros.cmake +++ b/cmake/FindPkgMacros.cmake @@ -50,6 +50,11 @@ endmacro(create_search_paths) # clear cache variables if a certain variable changed macro(clear_if_changed TESTVAR) # test against internal check variable + # HACK: Apparently, adding a variable to the cache cleans up the list + # a bit. We need to also remove any empty strings from the list, but + # at the same time ensure that we are actually dealing with a list. + list(APPEND ${TESTVAR} "") + list(REMOVE_ITEM ${TESTVAR} "") if (NOT "${${TESTVAR}}" STREQUAL "${${TESTVAR}_INT_CHECK}") message(STATUS "${TESTVAR} changed.") foreach(var ${ARGN}) @@ -129,9 +134,18 @@ MACRO(findpkg_framework fwk) /System/Library/Frameworks /Network/Library/Frameworks /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/System/Library/Frameworks/ - ${CMAKE_CURRENT_SOURCE_DIR}/../lib/Release - ${CMAKE_CURRENT_SOURCE_DIR}/../lib/Debug + ${CMAKE_CURRENT_SOURCE_DIR}/lib/Release + ${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug ) + # These could be arrays of paths, add each individually to the search paths + foreach(i ${OGRE_PREFIX_PATH}) + set(${fwk}_FRAMEWORK_PATH ${${fwk}_FRAMEWORK_PATH} ${i}/lib/Release ${i}/lib/Debug) + endforeach(i) + + foreach(i ${OGRE_PREFIX_BUILD}) + set(${fwk}_FRAMEWORK_PATH ${${fwk}_FRAMEWORK_PATH} ${i}/lib/Release ${i}/lib/Debug) + endforeach(i) + FOREACH(dir ${${fwk}_FRAMEWORK_PATH}) SET(fwkpath ${dir}/${fwk}.framework) IF(EXISTS ${fwkpath}) diff --git a/cmake/FindZZip.cmake b/cmake/FindZZip.cmake new file mode 100644 index 000000000..68fe043f3 --- /dev/null +++ b/cmake/FindZZip.cmake @@ -0,0 +1,48 @@ +#------------------------------------------------------------------- +# 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 zziplib +# Once done, this will define +# +# ZZip_FOUND - system has ZZip +# ZZip_INCLUDE_DIRS - the ZZip include directories +# ZZip_LIBRARIES - link these to use ZZip + +include(FindPkgMacros) +findpkg_begin(ZZip) + +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(ZZIP_HOME) + + +# construct search paths +set(ZZip_PREFIX_PATH ${ZZIP_HOME} ${ENV_ZZIP_HOME}) +create_search_paths(ZZip) +# redo search if prefix path changed +clear_if_changed(ZZip_PREFIX_PATH + ZZip_LIBRARY_FWK + ZZip_LIBRARY_REL + ZZip_LIBRARY_DBG + ZZip_INCLUDE_DIR +) + +set(ZZip_LIBRARY_NAMES zzip zziplib) +get_debug_names(ZZip_LIBRARY_NAMES) + +use_pkgconfig(ZZip_PKGC zziplib) + +findpkg_framework(ZZip) + +find_path(ZZip_INCLUDE_DIR NAMES zzip/zzip.h HINTS ${ZZip_INC_SEARCH_PATH} ${ZZip_PKGC_INCLUDE_DIRS}) +find_library(ZZip_LIBRARY_REL NAMES ${ZZip_LIBRARY_NAMES} HINTS ${ZZip_LIB_SEARCH_PATH} ${ZZip_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel) +find_library(ZZip_LIBRARY_DBG NAMES ${ZZip_LIBRARY_NAMES_DBG} HINTS ${ZZip_LIB_SEARCH_PATH} ${ZZip_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug) +make_library_set(ZZip_LIBRARY) + +findpkg_finish(ZZip) + diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index c40936691..024338d3a 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -2,7 +2,7 @@ macro (add_openmw_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.*") +file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND OPENMW_FILES "${f}") @@ -14,7 +14,7 @@ endmacro (add_openmw_dir) macro (add_component_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.*") +file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND COMPONENT_FILES "${f}") diff --git a/cmake/PreprocessorUtils.cmake b/cmake/PreprocessorUtils.cmake new file mode 100644 index 000000000..38462a98d --- /dev/null +++ b/cmake/PreprocessorUtils.cmake @@ -0,0 +1,60 @@ +#------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------- + +macro(get_preprocessor_entry CONTENTS KEYWORD VARIABLE) + string(REGEX MATCH + "# *define +${KEYWORD} +((\"([^\n]*)\")|([^ \n]*))" + PREPROC_TEMP_VAR + ${${CONTENTS}} + ) + if (CMAKE_MATCH_3) + set(${VARIABLE} ${CMAKE_MATCH_3}) + else () + set(${VARIABLE} ${CMAKE_MATCH_4}) + endif () +endmacro() + +macro(has_preprocessor_entry CONTENTS KEYWORD VARIABLE) + string(REGEX MATCH + "\n *# *define +(${KEYWORD})" + PREPROC_TEMP_VAR + ${${CONTENTS}} + ) + if (CMAKE_MATCH_1) + set(${VARIABLE} TRUE) + else () + set(${VARIABLE} FALSE) + endif () +endmacro() + +macro(replace_preprocessor_entry VARIABLE KEYWORD NEW_VALUE) + string(REGEX REPLACE + "(// *)?# *define +${KEYWORD} +[^ \n]*" + "#define ${KEYWORD} ${NEW_VALUE}" + ${VARIABLE}_TEMP + ${${VARIABLE}} + ) + set(${VARIABLE} ${${VARIABLE}_TEMP}) +endmacro() + +macro(set_preprocessor_entry VARIABLE KEYWORD ENABLE) + if (${ENABLE}) + set(TMP_REPLACE_STR "#define ${KEYWORD}") + else () + set(TMP_REPLACE_STR "// #define ${KEYWORD}") + endif () + string(REGEX REPLACE + "(// *)?# *define +${KEYWORD} *\n" + ${TMP_REPLACE_STR} + ${VARIABLE}_TEMP + ${${VARIABLE}} + ) + set(${VARIABLE} ${${VARIABLE}_TEMP}) +endmacro() + diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 76e68dd89..c95efb37d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -6,10 +6,6 @@ add_component_dir (bsa bsa_archive bsa_file ) -add_component_dir (cfg - configurationmanager - ) - add_component_dir (nif controlled effect nif_types record controller extra node record_ptr data nif_file property ) @@ -47,7 +43,8 @@ add_component_dir (misc ) add_component_dir (files - linuxpath windowspath macospath path multidircollection collections fileops + linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager + filelibrary ) add_component_dir (compiler @@ -63,4 +60,10 @@ add_component_dir (interpreter include_directories(${BULLET_INCLUDE_DIRS}) -add_library (components STATIC ${COMPONENT_FILES}) +add_library(components STATIC ${COMPONENT_FILES}) + +target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) + +# Make the variable accessible for other subdirectories +set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) + diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 2178be318..72d15944d 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -240,15 +240,36 @@ public: // should not have been declared const in the first place. BSAFile *narc = (BSAFile*)&arc; + String passed = filename; + if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' + || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' + || filename.at(filename.length() - 1) == '|') + { + passed = filename.substr(0, filename.length() - 2); + } + if(filename.at(filename.length() - 2) == '>') + passed = filename.substr(0, filename.length() - 6); // Open the file - StreamPtr strm = narc->getFile(filename.c_str()); + StreamPtr strm = narc->getFile(passed.c_str()); // Wrap it into an Ogre::DataStream. return DataStreamPtr(new Mangle2OgreStream(strm)); } // Check if the file exists. - bool exists(const String& filename) { return arc.exists(filename.c_str()); } + bool exists(const String& filename) { + String passed = filename; + if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' + || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' + || filename.at(filename.length() - 1) == '|') + { + passed = filename.substr(0, filename.length() - 2); + } + if(filename.at(filename.length() - 2) == '>') + passed = filename.substr(0, filename.length() - 6); + +return arc.exists(passed.c_str()); +} time_t getModifiedTime(const String&) { return 0; } // This is never called as far as I can see. diff --git a/components/cfg/configurationmanager.cpp b/components/cfg/configurationmanager.cpp deleted file mode 100644 index 0998debee..000000000 --- a/components/cfg/configurationmanager.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "configurationmanager.hpp" - -#include -#include -#include - -namespace Cfg -{ - -static const char* const openmwCfgFile = "openmw.cfg"; -static const char* const ogreCfgFile = "ogre.cfg"; -static const char* const pluginsCfgFile = "plugins.cfg"; - - -ConfigurationManager::ConfigurationManager() - : mPath("openmw") -{ - /** - * According to task #168 plugins.cfg file shall be located in global - * configuration path or in runtime configuration path. - */ - mPluginsCfgPath = mPath.getGlobalConfigPath() / pluginsCfgFile; - if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) - { - mPluginsCfgPath = mPath.getRuntimeConfigPath() / pluginsCfgFile; - if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) - { - std::cerr << "Failed to find " << pluginsCfgFile << " file!" << std::endl; - mPluginsCfgPath.clear(); - } - } - - /** - * According to task #168 ogre.cfg file shall be located only - * in user configuration path. - */ - mOgreCfgPath = mPath.getLocalConfigPath() / ogreCfgFile; - - mLogPath = mPath.getLocalConfigPath(); -} - -ConfigurationManager::~ConfigurationManager() -{ -} - -void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, - boost::program_options::options_description& description) -{ - loadConfig(mPath.getLocalConfigPath(), variables, description); - boost::program_options::notify(variables); - loadConfig(mPath.getRuntimeConfigPath(), variables, description); - boost::program_options::notify(variables); - loadConfig(mPath.getGlobalConfigPath(), variables, description); - boost::program_options::notify(variables); -} - -void ConfigurationManager::loadConfig(const boost::filesystem::path& path, - boost::program_options::variables_map& variables, - boost::program_options::options_description& description) -{ - boost::filesystem::path cfgFile(path); - cfgFile /= std::string(openmwCfgFile); - if (boost::filesystem::is_regular_file(cfgFile)) - { - std::cout << "Loading config file: " << cfgFile.string() << "... "; - - std::ifstream configFileStream(cfgFile.string().c_str()); - if (configFileStream.is_open()) - { - boost::program_options::store(boost::program_options::parse_config_file( - configFileStream, description), variables); - - std::cout << "done." << std::endl; - } - else - { - std::cout << "failed." << std::endl; - } - } -} - -const boost::filesystem::path& ConfigurationManager::getGlobalConfigPath() const -{ - return mPath.getGlobalConfigPath(); -} - -void ConfigurationManager::setGlobalConfigPath(const boost::filesystem::path& newPath) -{ - mPath.setGlobalConfigPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getLocalConfigPath() const -{ - return mPath.getLocalConfigPath(); -} - -void ConfigurationManager::setLocalConfigPath(const boost::filesystem::path& newPath) -{ - mPath.setLocalConfigPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getRuntimeConfigPath() const -{ - return mPath.getRuntimeConfigPath(); -} - -void ConfigurationManager::setRuntimeConfigPath(const boost::filesystem::path& newPath) -{ - mPath.setRuntimeConfigPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const -{ - return mPath.getGlobalDataPath(); -} - -void ConfigurationManager::setGlobalDataPath(const boost::filesystem::path& newPath) -{ - mPath.setGlobalDataPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getLocalDataPath() const -{ - return mPath.getLocalDataPath(); -} - -void ConfigurationManager::setLocalDataPath(const boost::filesystem::path& newPath) -{ - mPath.setLocalDataPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getRuntimeDataPath() const -{ - return mPath.getRuntimeDataPath(); -} - -void ConfigurationManager::setRuntimeDataPath(const boost::filesystem::path& newPath) -{ - mPath.setRuntimeDataPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getOgreConfigPath() const -{ - return mOgreCfgPath; -} - -const boost::filesystem::path& ConfigurationManager::getPluginsConfigPath() const -{ - return mPluginsCfgPath; -} - -const boost::filesystem::path& ConfigurationManager::getLogPath() const -{ - return mLogPath; -} - -} /* namespace Cfg */ diff --git a/components/cfg/configurationmanager.hpp b/components/cfg/configurationmanager.hpp deleted file mode 100644 index 7f13d0914..000000000 --- a/components/cfg/configurationmanager.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef COMPONENTS_CFG_CONFIGURATIONMANAGER_HPP -#define COMPONENTS_CFG_CONFIGURATIONMANAGER_HPP - -#include -#include - -#include - -/** - * \namespace Cfg - */ -namespace Cfg -{ - -/** - * \struct ConfigurationManager - */ -struct ConfigurationManager -{ - ConfigurationManager(); - virtual ~ConfigurationManager(); - - void readConfiguration(boost::program_options::variables_map& variables, - boost::program_options::options_description& description); - - const boost::filesystem::path& getGlobalConfigPath() const; - void setGlobalConfigPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getLocalConfigPath() const; - void setLocalConfigPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getRuntimeConfigPath() const; - void setRuntimeConfigPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getGlobalDataPath() const; - void setGlobalDataPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getLocalDataPath() const; - void setLocalDataPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getRuntimeDataPath() const; - void setRuntimeDataPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getOgreConfigPath() const; - const boost::filesystem::path& getPluginsConfigPath() const; - const boost::filesystem::path& getLogPath() const; - - private: - void loadConfig(const boost::filesystem::path& path, - boost::program_options::variables_map& variables, - boost::program_options::options_description& description); - - Files::Path<> mPath; - - boost::filesystem::path mOgreCfgPath; - boost::filesystem::path mPluginsCfgPath; - boost::filesystem::path mLogPath; -}; - -} /* namespace Cfg */ - -#endif /* COMPONENTS_CFG_CONFIGURATIONMANAGER_HPP */ diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index c255154b5..5d74ee9d4 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -95,8 +95,6 @@ namespace Compiler return true; } - - return false; } bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner) @@ -108,7 +106,7 @@ namespace Compiler Codes expr; mExprParser.append (expr); - Generator::jump (loop, -mCodeBlock.size()-expr.size()); + Generator::jump (loop, -static_cast (mCodeBlock.size()-expr.size())); std::copy (expr.begin(), expr.end(), std::back_inserter (mCode)); @@ -122,7 +120,7 @@ namespace Compiler Codes loop2; - Generator::jump (loop2, -mCodeBlock.size()-expr.size()-skip.size()); + Generator::jump (loop2, -static_cast (mCodeBlock.size()-expr.size()-skip.size())); if (loop.size()!=loop2.size()) throw std::logic_error ( @@ -153,8 +151,6 @@ namespace Compiler return true; } - - return false; } ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index e5b230748..0420f37cd 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -153,7 +153,7 @@ public: *************************************************************************/ int getVer() { return mCtx.header.version; } - float getFVer() { return *((float*)&mCtx.header.version); } + float getFVer() { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } int getSpecial() { return mSpf; } const std::string getAuthor() { return mCtx.header.author.toString(); } const std::string getDesc() { return mCtx.header.desc.toString(); } diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index b0727b0c0..dc63ce335 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -3,32 +3,68 @@ namespace ESM { -void PathGrid::load(ESMReader &esm) +void Pathgrid::load(ESMReader &esm) { esm.getHNT(data, "DATA", 12); cell = esm.getHNString("NAME"); - // Remember this file position - context = esm.getContext(); + // keep track of total connections so we can reserve edge vector size + int edgeCount = 0; - // Check that the sizes match up. Size = 16 * s2 (path points?) if (esm.isNextSub("PGRP")) { - esm.skipHSub(); + esm.getSubHeader(); int size = esm.getSubSize(); - if (size != 16 * data.s2) - esm.fail("Path grid table size mismatch"); + // Check that the sizes match up. Size = 16 * s2 (path points) + if (size != static_cast (sizeof(Point) * data.s2)) + esm.fail("Path point subrecord size mismatch"); + else + { + int pointCount = data.s2; + points.reserve(pointCount); + for (int i = 0; i < pointCount; ++i) + { + Point p; + esm.getExact(&p, sizeof(Point)); + points.push_back(p); + edgeCount += p.connectionNum; + } + } } - // Size varies. Path grid chances? Connections? Multiples of 4 - // suggest either int or two shorts, or perhaps a float. Study - // it later. if (esm.isNextSub("PGRC")) { - esm.skipHSub(); + esm.getSubHeader(); int size = esm.getSubSize(); - if (size % 4 != 0) + if (size % sizeof(int) != 0) esm.fail("PGRC size not a multiple of 4"); + else + { + int rawConnNum = size / sizeof(int); + std::vector rawConnections; + rawConnections.reserve(rawConnNum); + for (int i = 0; i < rawConnNum; ++i) + { + int currentValue; + esm.getT(currentValue); + rawConnections.push_back(currentValue); + } + + std::vector::const_iterator rawIt = rawConnections.begin(); + int pointIndex = 0; + edges.reserve(edgeCount); + for(PointList::const_iterator it = points.begin(); it != points.end(); it++, pointIndex++) + { + unsigned char connectionNum = (*it).connectionNum; + for (int i = 0; i < connectionNum; ++i) { + Edge edge; + edge.v0 = pointIndex; + edge.v1 = *rawIt; + rawIt++; + edges.push_back(edge); + } + } + } } } diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index 8c030d314..6e2c6e134 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -9,20 +9,37 @@ namespace ESM /* * Path grid. */ -struct PathGrid +struct Pathgrid { struct DATAstruct { int x, y; // Grid location, matches cell for exterior cells short s1; // ?? Usually but not always a power of 2. Doesn't seem // to have any relation to the size of PGRC. - short s2; // Number of path points? Size of PGRP block is always 16 * s2; + short s2; // Number of path points. }; // 12 bytes + struct Point // path grid point + { + int x, y, z; // Location of point + unsigned char autogenerated; // autogenerated vs. user coloring flag? + unsigned char connectionNum; // number of connections for this point + short unknown; + }; // 16 bytes + + struct Edge // path grid edge + { + int v0, v1; // index of points connected with this edge + }; // 8 bytes + std::string cell; // Cell name DATAstruct data; - ESM_Context context; // Context so we can return here later and - // finish the job + + typedef std::vector PointList; + PointList points; + + typedef std::vector EdgeList; + EdgeList edges; void load(ESMReader &esm); }; diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index d064312f1..c4bcf84d8 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -12,7 +12,6 @@ #include "store.hpp" #include "components/esm/records.hpp" -#include "components/esm/loadcell.hpp" #include #include @@ -36,7 +35,7 @@ namespace ESMS { LiveCellRef(const CellRef& cref, const X* b = NULL) : base(b), ref(cref), mData(ref) {} - + LiveCellRef(const X* b = NULL) : base(b), mData(ref) {} @@ -187,7 +186,7 @@ namespace ESMS ++iter) if (!functor (iter->ref, iter->mData)) return false; - + return true; } diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 20a2e8ff9..678f794c8 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -22,6 +22,8 @@ namespace ESMS struct RecList { + virtual ~RecList() {} + virtual void load(ESMReader &esm, const std::string &id) = 0; virtual int getSize() = 0; virtual void listIdentifier (std::vector& identifier) const = 0; @@ -42,6 +44,8 @@ namespace ESMS template struct RecListT : RecList { + virtual ~RecListT() {} + typedef std::map MapType; MapType list; @@ -90,6 +94,8 @@ namespace ESMS template struct RecListWithIDT : RecList { + virtual ~RecListWithIDT() {} + typedef std::map MapType; MapType list; @@ -139,6 +145,8 @@ namespace ESMS template struct RecIDListT : RecList { + virtual ~RecIDListT() {} + typedef std::map MapType; MapType list; @@ -189,6 +197,8 @@ namespace ESMS */ struct LTexList : RecList { + virtual ~LTexList() {} + // TODO: For multiple ESM/ESP files we need one list per file. std::vector ltex; int count; @@ -223,6 +233,8 @@ namespace ESMS */ struct LandList : RecList { + virtual ~LandList() {} + // Map containing all landscapes typedef std::map LandsCol; typedef std::map Lands; @@ -296,7 +308,7 @@ namespace ESMS identifier.push_back (iter->first); } - ~CellList() + virtual ~CellList() { for (IntCells::iterator it = intCells.begin(); it!=intCells.end(); ++it) delete it->second; @@ -390,9 +402,100 @@ namespace ESMS } }; + struct PathgridList : RecList + { + int count; + + // List of grids for interior cells. Indexed by cell name. + typedef std::map IntGrids; + IntGrids intGrids; + + // List of grids for exterior cells. Indexed as extCells[gridX][gridY]. + typedef std::map, ESM::Pathgrid*> ExtGrids; + ExtGrids extGrids; + + PathgridList() : count(0) {} + + virtual ~PathgridList() + { + for (IntGrids::iterator it = intGrids.begin(); it!=intGrids.end(); ++it) + delete it->second; + + for (ExtGrids::iterator it = extGrids.begin(); it!=extGrids.end(); ++it) + delete it->second; + } + + int getSize() { return count; } + + virtual void listIdentifier (std::vector& identifier) const + { + // do nothing + } + + void load(ESMReader &esm, const std::string &id) + { + count++; + ESM::Pathgrid *grid = new ESM::Pathgrid; + grid->load(esm); + if (grid->data.x == 0 && grid->data.y == 0) + { + intGrids[grid->cell] = grid; + } + else + { + extGrids[std::make_pair(grid->data.x, grid->data.y)] = grid; + } + } + + Pathgrid *find(int cellX, int cellY, std::string cellName) const + { + Pathgrid *result = search(cellX, cellY, cellName); + if (!result) + { + throw std::runtime_error("no pathgrid found for cell " + cellName); + } + return result; + } + + Pathgrid *search(int cellX, int cellY, std::string cellName) const + { + Pathgrid *result = NULL; + if (cellX == 0 && cellY == 0) // possibly interior + { + IntGrids::const_iterator it = intGrids.find(cellName); + if (it != intGrids.end()) + result = it->second; + } + else + { + ExtGrids::const_iterator it = extGrids.find(std::make_pair(cellX, cellY)); + if (it != extGrids.end()) + result = it->second; + } + return result; + } + + Pathgrid *search(const ESM::Cell &cell) const + { + int cellX, cellY; + if (cell.data.flags & ESM::Cell::Interior) + { + cellX = cellY = 0; + } + else + { + cellX = cell.data.gridX; + cellY = cell.data.gridY; + } + return search(cellX, cellY, cell.name); + } + }; + template struct ScriptListT : RecList { + virtual ~ScriptListT() {} + typedef std::map MapType; MapType list; @@ -444,6 +547,8 @@ namespace ESMS template struct IndexListT { + virtual ~IndexListT() {} + typedef std::map MapType; MapType list; diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp index e3bbf9e82..fab04d3e9 100644 --- a/components/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -74,7 +74,8 @@ namespace ESMS ScriptListT