actorid
Jason Hooks 13 years ago
commit 51fb9e67cd

6
.gitignore vendored

@ -7,3 +7,9 @@ Docs/mainpage.hpp
CMakeFiles
*/CMakeFiles
CMakeCache.txt
moc_*.cxx
cmake_install.cmake
*.[ao]
Makefile
makefile
data

6
.gitmodules vendored

@ -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

@ -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)

@ -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.
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"
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
# Run
From your build directory run:
12. From your build directory run:
$ OpenMW.app/Contents/MacOS/openmw
or:
$ open OpenMW.app
Enjoy!
or:
$ open OpenMW.app
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/

@ -1,6 +1,4 @@
set(ESMTOOL
esmtool_cmd.c
esmtool_cmd.h
esmtool.cpp
)
source_group(apps\\esmtool FILES ${ESMTOOL})

@ -1,5 +0,0 @@
esmtool_cmd.c: esmtool.ggo
gengetopt < esmtool.ggo
clean:
rm esmtool_cmd.c esmtool_cmd.h

@ -1,35 +1,138 @@
#include <iostream>
#include <boost/program_options.hpp>
#include <components/esm/esm_reader.hpp>
#include <components/esm/records.hpp>
#include "esmtool_cmd.h"
#include <iostream>
#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");
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<std::string>(&(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.";
if(info.inputs_num != 1)
// 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<std::string> >(), "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<std::string> >().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<std::string> >()[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<std::string>();
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 {

@ -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."

File diff suppressed because it is too large Load Diff

@ -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 <stdio.h> /* 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 */

@ -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()

@ -1,9 +1,7 @@
#include <QtGui>
#include <components/esm/esm_reader.hpp>
#include <components/files/collections.hpp>
#include <components/files/multidircollection.hpp>
#include <components/cfg/configurationmanager.hpp>
#include <components/files/configurationmanager.hpp>
#include "datafilespage.hpp"
#include "lineedit.hpp"
@ -11,6 +9,23 @@
#include "pluginsmodel.hpp"
#include "pluginsview.hpp"
#include <boost/version.hpp>
/**
* 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<boost::filesystem::path, std::string>(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()
{
QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.cfg").string());
QFile file(config);
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<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
// ("data-local", boost::program_options::value<std::string>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
mCfgMgr.readConfiguration(variables, desc);
// Put the paths in a boost::filesystem vector to use with Files::Collections
Files::PathContainer dataDirs;
Files::PathContainer dataDirs(variables["data"].as<Files::PathContainer>());
mDataDirs = dataDirs;
// std::string local = variables["data-local"].as<std::string>();
// 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("<br><b>Could not find the Data Files location</b><br><br> \
The directory containing the Data Files was not found.<br><br> \
Press \"Browse...\" to specify the location manually.<br>"));
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);
foreach (const QString &currentPath, paths) {
dataDirs.push_back(boost::filesystem::path(currentPath.toStdString()));
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<bool>());
// 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<QTableWidgetItem*> 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<std::string>());
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<QTableWidgetItem*> 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 <b>%0</b>?").arg(profile));
deleteMessageBox.setIcon(QMessageBox::Warning);
QAbstractButton *deleteButton =
deleteMessageBox.addButton(tr("Delete"), QMessageBox::ActionRole);
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 <b>%0</b>?").arg(profile));
deleteMessageBox.addButton(QMessageBox::Cancel);
QAbstractButton *deleteButton =
msgBox.addButton(tr("Delete"), QMessageBox::ActionRole);
deleteMessageBox.exec();
msgBox.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 &current)
{
// 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("<br><b>Could not open or create %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").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("<br><b>Could not write to %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").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();
}

@ -3,6 +3,9 @@
#include <QWidget>
#include <QModelIndex>
#include <components/files/collections.hpp>
#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();
};

@ -1,8 +1,11 @@
#include <QtGui>
#include "graphicspage.hpp"
#include <components/files/configurationmanager.hpp>
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<br>%1<br><br>")).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.<br>"));
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();
}
}

@ -7,21 +7,28 @@
#include <OgreRenderSystem.h>
#include <OgreConfigFile.h>
#include <OgreConfigDialog.h>
#include <components/cfg/configurationmanager.hpp>
// 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);

@ -1,6 +1,7 @@
#include <QApplication>
#include <QDir>
#include <QFile>
#include <QtDebug>
#include "maindialog.hpp"
@ -17,16 +18,18 @@ int main(int argc, char *argv[])
dir.cdUp();
dir.cdUp();
}
#endif
QDir::setCurrent(dir.absolutePath());
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
// Load the stylesheet
QFile file("./launcher.qss");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
app.setLibraryPaths(libraryPaths);
#endif
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
app.setStyleSheet(styleSheet);
QDir::setCurrent(dir.absolutePath());
MainDialog dialog;
return dialog.exec();

@ -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("<br><b>Could not open %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").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);
// Retrieve all data entries from the configs
QStringList dataDirs;
mGraphicsPage = new GraphicsPage(mCfgMgr, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, this);
// 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("<br><b>Could not read the location of the data files</b><br><br> \
Please make sure OpenMW is correctly configured and try again.<br>"));
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("<br><b>Could not open or create %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").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("<br><b>Could not write to %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").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 &currentFile, 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();
}

@ -3,7 +3,7 @@
#include <QDialog>
#include <components/cfg/configurationmanager.hpp>
#include <components/files/configurationmanager.hpp>
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

@ -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)

@ -7,6 +7,7 @@
#include <utility>
#include <OgreRoot.h>
#include <OgreRenderWindow.h>
#include <MyGUI_WidgetManager.h>
@ -17,8 +18,11 @@
#include <components/esm_store/cell_store.hpp>
#include <components/bsa/bsa_archive.hpp>
#include <components/esm/esm_reader.hpp>
#include <components/files/path.hpp>
#include <components/files/fixedpath.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/nifbullet/bullet_nif_loader.hpp>
#include <components/nifogre/ogre_nif_loader.hpp>
#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)

@ -7,11 +7,8 @@
#include <OgreFrameListener.h>
#include <openengine/bullet/physic.hpp>
#include <components/compiler/extensions.hpp>
#include <components/files/collections.hpp>
#include <components/cfg/configurationmanager.hpp>
#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;
};
}

@ -6,9 +6,9 @@
#include <boost/program_options.hpp>
#include <components/files/fileops.hpp>
#include <components/files/path.hpp>
#include <components/files/fixedpath.hpp>
#include <components/files/collections.hpp>
#include <components/cfg/configurationmanager.hpp>
#include <components/files/configurationmanager.hpp>
#include "engine.hpp"
@ -35,6 +35,23 @@
#include "config.hpp"
#include <boost/version.hpp>
/**
* 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<boost::filesystem::path, std::string>(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<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "plugin file(s)")
("fps", boost::program_options::value<bool>()->implicit_value(true)
->default_value(false), "show fps counter")
("fps", boost::program_options::value<int>()->implicit_value(1)
->default_value(0), "fps counter detail (0 = off, 1 = fps counter, 2 = full detail)")
("anim-verbose", boost::program_options::value<bool>()->implicit_value(true)
->default_value(false), "output animation indices files")
("debug", boost::program_options::value<bool>()->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<std::string>());
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<std::string>());
@ -200,12 +225,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Cfg::Configuratio
engine.setNewGame(variables["new-game"].as<bool>());
// other settings
engine.showFPS(variables["fps"].as<bool>());
engine.showFPS(variables["fps"].as<int>());
engine.setDebugMode(variables["debug"].as<bool>());
engine.setSoundUsage(!variables["nosound"].as<bool>());
engine.setScriptsVerbosity(variables["script-verbose"].as<bool>());
engine.setCompileAll(variables["script-all"].as<bool>());
engine.setReportFocus(variables["report-focus"].as<bool>());
engine.setAnimationVerbose(variables["anim-verbose"].as<bool>());
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))

@ -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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Apparatus::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.appas);
}
std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Apparatus, MWWorld::RefData> *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");
}
}

@ -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<MWWorld::RefData>& 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
};
}

@ -2,14 +2,21 @@
#include "armor.hpp"
#include <components/esm/loadarmo.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm_store/cell_store.hpp>
#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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -70,18 +79,85 @@ namespace MWClass
return ref->base->data.health;
}
void Armor::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
std::string Armor::getScript (const MWWorld::Ptr& ptr) const
{
insertIntoContainerStore (ptr, containerStore.armors);
ESMS::LiveCellRef<ESM::Armor, MWWorld::RefData> *ref =
ptr.get<ESM::Armor>();
return ref->base->script;
}
std::string Armor::getScript (const MWWorld::Ptr& ptr) const
std::pair<std::vector<int>, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Armor, MWWorld::RefData> *ref =
ptr.get<ESM::Armor>();
return ref->base->script;
std::vector<int> 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; i<size; ++i)
if (sMapping[i][0]==ref->base->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<ESM::Armor, MWWorld::RefData> *ref =
ptr.get<ESM::Armor>();
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()
@ -90,4 +166,26 @@ namespace MWClass
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");
}
}

@ -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<MWWorld::RefData>& 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<std::vector<int>, 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
};
}

@ -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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Book::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.books);
}
std::string Book::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Book, MWWorld::RefData> *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");
}
}

@ -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<MWWorld::RefData>& 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
};
}

@ -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,22 +57,70 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Clothing::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
std::string Clothing::getScript (const MWWorld::Ptr& ptr) const
{
insertIntoContainerStore (ptr, containerStore.clothes);
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
return ref->base->script;
}
std::string Clothing::getScript (const MWWorld::Ptr& ptr) const
std::pair<std::vector<int>, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
return ref->base->script;
std::vector<int> 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; i<size; ++i)
if (sMapping[i][0]==ref->base->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<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
if (ref->base->data.type==ESM::Clothing::Shoes)
return ESM::Skill::Unarmored;
return -1;
}
void Clothing::registerSelf()
@ -78,4 +129,28 @@ namespace MWClass
registerClass (typeid (ESM::Clothing).name(), instance);
}
std::string Clothing::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
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<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
if (ref->base->data.type == 8)
{
return std::string("Item Ring Down");
}
return std::string("Item Clothes Down");
}
}

@ -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<MWWorld::RefData>& 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<std::vector<int>, 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
};
}

@ -6,9 +6,45 @@
#include <components/esm_store/cell_store.hpp>
#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<CustomData> 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<ESM::Container, MWWorld::RefData> *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<MWWorld::Action> 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<MWWorld::Action> (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<MWWorld::Action> (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<MWWorld::Action> (new MWWorld::NullAction);
}
}
}
std::string Container::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Container, MWWorld::RefData> *ref =
@ -47,20 +115,12 @@ namespace MWClass
return ref->base->name;
}
MWWorld::ContainerStore<MWWorld::RefData>& Container::getContainerStore (const MWWorld::Ptr& ptr)
MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr)
const
{
if (!ptr.getRefData().getContainerStore().get())
{
boost::shared_ptr<MWWorld::ContainerStore<MWWorld::RefData> > store (
new MWWorld::ContainerStore<MWWorld::RefData>);
// TODO add initial content
ptr.getRefData().getContainerStore() = store;
}
ensureCustomData (ptr);
return *ptr.getRefData().getContainerStore();
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
}
std::string Container::getScript (const MWWorld::Ptr& ptr) const

@ -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<MWWorld::RefData>& getContainerStore (
const MWWorld::Ptr& ptr) const;
virtual boost::shared_ptr<MWWorld::Action> 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;

@ -1,28 +0,0 @@
#ifndef GAME_MWCLASS_CONTAINERUTIL_H
#define GAME_MWCLASS_CONTAINERUTIL_H
#include <components/esm_store/cell_store.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/containerstore.hpp"
namespace MWClass
{
template<typename T>
void insertIntoContainerStore (const MWWorld::Ptr& ptr,
ESMS::CellRefList<T, MWWorld::RefData>& containerStore)
{
if (!ptr.isEmpty())
{
// TODO check stacking
ESMS::LiveCellRef<T, MWWorld::RefData> cellRef(ptr.getCellRef(), ptr.get<T>()->base);
cellRef.mData = ptr.getRefData();
containerStore.list.push_back (cellRef);
}
}
}
#endif

@ -4,16 +4,62 @@
#include <components/esm/loadcrea.hpp>
#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<CustomData> data (new CustomData);
ESMS::LiveCellRef<ESM::Creature, MWWorld::RefData> *ref = ptr.get<ESM::Creature>();
// 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<ESM::Creature, MWWorld::RefData> *ref =
@ -24,24 +70,12 @@ namespace MWClass
void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
/*
ESMS::LiveCellRef<ESM::Creature, MWWorld::RefData> *ref =
ptr.get<ESM::Creature>();
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<ESM::Creature, MWWorld::RefData> *ref =
ptr.get<ESM::Creature>();
@ -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<MWMechanics::CreatureStats> stats (
new MWMechanics::CreatureStats);
ensureCustomData (ptr);
ESMS::LiveCellRef<ESM::Creature, MWWorld::RefData> *ref = ptr.get<ESM::Creature>();
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<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
}
boost::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
@ -107,20 +118,12 @@ namespace MWClass
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
}
MWWorld::ContainerStore<MWWorld::RefData>& Creature::getContainerStore (const MWWorld::Ptr& ptr)
MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr)
const
{
if (!ptr.getRefData().getContainerStore().get())
{
boost::shared_ptr<MWWorld::ContainerStore<MWWorld::RefData> > store (
new MWWorld::ContainerStore<MWWorld::RefData>);
// TODO add initial content
ptr.getRefData().getContainerStore() = store;
}
ensureCustomData (ptr);
return *ptr.getRefData().getContainerStore();
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
}
std::string Creature::getScript (const MWWorld::Ptr& ptr) const

@ -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<MWWorld::RefData>& getContainerStore (
virtual MWWorld::ContainerStore& getContainerStore (
const MWWorld::Ptr& ptr) const;
///< Return container store

@ -14,7 +14,7 @@
#include "../mwrender/objects.hpp"
#include <iostream>
#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<ESM::Door, MWWorld::RefData> *ref =
ptr.get<ESM::Door>();
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<ESM::Door, MWWorld::RefData> *ref =
ptr.get<ESM::Door>();
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<MWWorld::Action> (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<MWWorld::Action> (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<MWWorld::Action> (
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<MWWorld::Action> (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<MWWorld::Action> (new MWWorld::NullAction);
}
}

@ -2,7 +2,6 @@
#define GAME_MWCLASS_DOOR_H
#include "../mwworld/class.hpp"
#include "../mwrender/objects.hpp"
namespace MWClass
{

@ -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<ESM::Ingredient, MWWorld::RefData> *ref =
ptr.get<ESM::Ingredient>();
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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Ingredient::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.ingreds);
}
std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Ingredient, MWWorld::RefData> *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");
}
}

@ -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<MWWorld::RefData>& 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
};
}

@ -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<ESM::Light, MWWorld::RefData> *ref =
ptr.get<ESM::Light>();
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,22 +83,31 @@ namespace MWClass
if (!(ref->base->data.flags & ESM::Light::Carry))
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Light::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
std::string Light::getScript (const MWWorld::Ptr& ptr) const
{
insertIntoContainerStore (ptr, containerStore.lights);
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =
ptr.get<ESM::Light>();
return ref->base->script;
}
std::string Light::getScript (const MWWorld::Ptr& ptr) const
std::pair<std::vector<int>, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =
ptr.get<ESM::Light>();
return ref->base->script;
std::vector<int> 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()
@ -107,4 +116,14 @@ namespace MWClass
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");
}
}

@ -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<MWWorld::RefData>& 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<std::vector<int>, 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
};
}

@ -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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Lockpick::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.lockpicks);
}
std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Tool, MWWorld::RefData> *ref =
@ -72,10 +72,29 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Lockpick::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
std::vector<int> slots;
slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));
return std::make_pair (slots, false);
}
void Lockpick::registerSelf()
{
boost::shared_ptr<Class> 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");
}
}

@ -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<MWWorld::RefData>& 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<std::vector<int>, 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
};
}

@ -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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Miscellaneous::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.miscItems);
}
std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *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<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Miscellaneous>();
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<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Miscellaneous>();
if (ref->base->name =="Gold")
{
return std::string("Item Gold Down");
}
return std::string("Item Misc Down");
}
}

@ -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<MWWorld::RefData>& 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
};
}

@ -1,27 +1,88 @@
#include "npc.hpp"
#include <memory>
#include <OgreSceneNode.h>
#include <components/esm/loadnpc.hpp>
#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 <OgreSceneNode.h>
#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<CustomData> data (new CustomData);
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref = ptr.get<ESM::NPC>();
// 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<ESM::NPC, MWWorld::RefData> *ref =
@ -32,34 +93,28 @@ namespace MWClass
void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
/*
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
ptr.get<ESM::NPC>();
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<ESM::NPC, MWWorld::RefData> *ref =
ptr.get<ESM::NPC>();
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<MWMechanics::CreatureStats> stats (
new MWMechanics::CreatureStats);
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref = ptr.get<ESM::NPC>();
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;
}
ensureCustomData (ptr);
return *ptr.getRefData().getCreatureStats();
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
}
MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const
{
if (!ptr.getRefData().getNpcStats().get())
{
boost::shared_ptr<MWMechanics::NpcStats> stats (
new MWMechanics::NpcStats);
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref = ptr.get<ESM::NPC>();
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]);
ensureCustomData (ptr);
ptr.getRefData().getNpcStats() = stats;
}
return *ptr.getRefData().getNpcStats();
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
}
boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
@ -141,20 +156,20 @@ namespace MWClass
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
}
MWWorld::ContainerStore<MWWorld::RefData>& Npc::getContainerStore (const MWWorld::Ptr& ptr)
MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr)
const
{
if (!ptr.getRefData().getContainerStore().get())
{
boost::shared_ptr<MWWorld::ContainerStore<MWWorld::RefData> > store (
new MWWorld::ContainerStore<MWWorld::RefData>);
ensureCustomData (ptr);
// TODO add initial content
return dynamic_cast<CustomData&> (*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<CustomData&> (*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<MWMechanics::Movement> movement (
new MWMechanics::Movement);
ensureCustomData (ptr);
ptr.getRefData().getMovement() = movement;
}
return *ptr.getRefData().getMovement();
return dynamic_cast<CustomData&> (*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;
}

@ -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<MWWorld::RefData>& 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<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const;
///< Generate action for activation

@ -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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Potion::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.potions);
}
std::string Potion::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Potion, MWWorld::RefData> *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");
}
}

@ -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<MWWorld::RefData>& 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
};
}

@ -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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Probe::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.probes);
}
std::string Probe::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Probe, MWWorld::RefData> *ref =
@ -72,10 +71,29 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Probe::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
std::vector<int> slots;
slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));
return std::make_pair (slots, false);
}
void Probe::registerSelf()
{
boost::shared_ptr<Class> 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");
}
}

@ -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<MWWorld::RefData>& 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<std::vector<int>, 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
};
}

@ -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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Repair::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
insertIntoContainerStore (ptr, containerStore.repairs);
}
std::string Repair::getScript (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Repair, MWWorld::RefData> *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");
}
}

@ -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<MWWorld::RefData>& 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
};
}

@ -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<ESM::Static, MWWorld::RefData> *ref =
ptr.get<ESM::Static>();
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

@ -2,7 +2,6 @@
#define GAME_MWCLASS_STATIC_H
#include "../mwworld/class.hpp"
#include "../mwrender/objects.hpp"
namespace MWClass
{

@ -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<MWWorld::Action> 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<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -71,18 +76,67 @@ namespace MWClass
return ref->base->data.health;
}
void Weapon::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
std::string Weapon::getScript (const MWWorld::Ptr& ptr) const
{
insertIntoContainerStore (ptr, containerStore.weapons);
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
return ref->base->script;
}
std::string Weapon::getScript (const MWWorld::Ptr& ptr) const
std::pair<std::vector<int>, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
return ref->base->script;
std::vector<int> 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<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
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; i<size; ++i)
if (sMapping[i][0]==ref->base->data.type)
return sMapping[i][1];
return -1;
}
void Weapon::registerSelf()
@ -91,4 +145,96 @@ namespace MWClass
registerClass (typeid (ESM::Weapon).name(), instance);
}
std::string Weapon::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
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<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
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");
}
}

@ -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<MWWorld::RefData>& 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<std::vector<int>, 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
};
}

@ -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);

@ -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<Step, 10> 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<ESM::Attribute::AttributeID, MWMechanics::Stat<int> >::iterator end = mPlayerAttributes.end();
for (std::map<ESM::Attribute::AttributeID, MWMechanics::Stat<int> >::iterator it = mPlayerAttributes.begin(); it != end; ++it)
{
mReviewDialog->setAttribute(it->first, it->second);
}
}
{
std::map<ESM::Skill::SkillEnum, MWMechanics::Stat<float> >::iterator end = mPlayerSkillValues.end();
for (std::map<ESM::Skill::SkillEnum, MWMechanics::Stat<float> >::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<int>& value)
{
mPlayerHealth = value;
}
void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat<int>& value)
{
mPlayerMagicka = value;
}
void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat<int>& 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<int> attributes = mCreateClassDialog->getFavoriteAttributes();
assert(attributes.size() == 2);
klass.data.attribute[0] = attributes[0];
klass.data.attribute[1] = attributes[1];
std::vector<ESM::Skill::SkillEnum> majorSkills = mCreateClassDialog->getMajorSkills();
std::vector<ESM::Skill::SkillEnum> 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<ClassPoint, 23> 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;
}

@ -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 <components/esm_store/store.hpp>
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<int> SkillList;
CharacterCreation(WindowManager* _wm, MWWorld::Environment* _environment);
~CharacterCreation();
//Show a dialog
void spawnDialog(const char id);
void setPlayerHealth (const MWMechanics::DynamicStat<int>& value);
void setPlayerMagicka (const MWMechanics::DynamicStat<int>& value);
void setPlayerFatigue (const MWMechanics::DynamicStat<int>& 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<ESM::Attribute::AttributeID, MWMechanics::Stat<int> > mPlayerAttributes;
SkillList mPlayerMajorSkills, mPlayerMinorSkills;
std::map<ESM::Skill::SkillEnum, MWMechanics::Stat<float> > mPlayerSkillValues;
MWMechanics::DynamicStat<int> mPlayerHealth;
MWMechanics::DynamicStat<int> mPlayerMagicka;
MWMechanics::DynamicStat<int> 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

@ -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;
}
}
}
}

@ -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<std::string> 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<int> (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 lbsdflkbdSLKJGBLskdhbvl<kbvlqksbgkqsjhdvb");
//mEditWidget->show();
//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<std::string> 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<std::string> list = formatText();
//displayLeftText(list.front());
MyGUI::WindowPtr t = static_cast<MyGUI::WindowPtr>(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<MWDialogue::StampedJournalEntry>::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<std::string> journal = formatText(a,10,20,1);
bool left = true;
for(std::list<std::string>::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]);
}
}

@ -0,0 +1,57 @@
#ifndef MWGUI_JOURNAL_H
#define MWGUI_JOURNAL_H
#include <sstream>
#include <set>
#include <string>
#include <utility>
#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<std::string> leftPages;
std::vector<std::string> rightPages;
int mPageNumber; //store the number of the current left page
};
}
#endif

@ -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<std::string>((int)fps));
}
void HUD::setTriangleCount(size_t count)
{
trianglecounter->setCaption(boost::lexical_cast<std::string>(count));
}
void HUD::setBatchCount(size_t count)
{
batchcounter->setCaption(boost::lexical_cast<std::string>(count));
}
void HUD::setStats(int h, int hmax, int m, int mmax, int s, int smax)
{

@ -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<int>& 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

@ -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

@ -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 <assert.h>
#include <iostream>
@ -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<DialogeHistory>("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)
{
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)
//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 (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<ESM::Attribute::AttributeID, MWMechanics::Stat<int> >::iterator end = playerAttributes.end();
for (std::map<ESM::Attribute::AttributeID, MWMechanics::Stat<int> >::iterator it = playerAttributes.begin(); it != end; ++it)
{
reviewDialog->setAttribute(it->first, it->second);
}
}
{
std::map<ESM::Skill::SkillEnum, MWMechanics::Stat<float> >::iterator end = playerSkillValues.end();
for (std::map<ESM::Skill::SkillEnum, MWMechanics::Stat<float> >::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,12 +266,33 @@ 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<int> 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)
{
@ -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<Step, 10> 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<ClassPoint, 23> 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<int> attributes = createClassDialog->getFavoriteAttributes();
assert(attributes.size() == 2);
klass.data.attribute[0] = attributes[0];
klass.data.attribute[1] = attributes[1];
std::vector<ESM::Skill::SkillEnum> majorSkills = createClassDialog->getMajorSkills();
std::vector<ESM::Skill::SkillEnum> 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();

@ -8,13 +8,15 @@
MyGUI should be initialized separately before creating instances of
this class.
*/
**/
#include <string>
#include <vector>
#include <set>
#include <components/esm_store/store.hpp>
#include <openengine/ogre/renderer.hpp>
#include <openengine/gui/manager.hpp>
#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<Faction> FactionList;
typedef std::vector<int> 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<ESM::Attribute::AttributeID, MWMechanics::Stat<int> > playerAttributes;
SkillList playerMajorSkills, playerMinorSkills;
std::map<ESM::Skill::SkillEnum, MWMechanics::Stat<float> > playerSkillValues;
MWMechanics::DynamicStat<int> 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<OEngine::GUI::Layout*> 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;
}
void setValue (const std::string& id, const MWMechanics::Stat<int>& value);
///< Set value for the given ID.
// MWMechanics::DynamicStat<int> getValue(const std::string& id);
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat<float>& value);
///< Set value for the given ID.
void setValue (const std::string& id, const MWMechanics::Stat<int>& value);
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat<float>& value);
void setValue (const std::string& id, const MWMechanics::DynamicStat<int>& 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<typename T>
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<std::string>& 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;
void onDialogueWindowBye();
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<ESM::Attribute::AttributeID, MWMechanics::Stat<int> > playerAttributes;
SkillList playerMajorSkills, playerMinorSkills;
std::map<ESM::Skill::SkillEnum, MWMechanics::Stat<float> > playerSkillValues;
MWMechanics::DynamicStat<int> playerHealth, playerMagicka, playerFatigue;
// Character generation: Name dialog
void onNameDialogDone(WindowBase* parWindow);
// Character generation: Race dialog
void onRaceDialogDone(WindowBase* parWindow);
void onRaceDialogBack();
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]
// Character generation: Choose class process
void onClassChoice(int _index);
std::vector<OEngine::GUI::Layout*> garbageDialogs;
void cleanupGarbage();
// Character generation: Generate Class
void showClassQuestionDialog();
void onClassQuestionChosen(int _index);
void onGenerateClassBack();
void onGenerateClassDone(WindowBase* parWindow);
GuiWindow shown; // Currently shown windows in inventory mode
// Character generation: Pick Class dialog
void onPickClassDialogDone(WindowBase* parWindow);
void onPickClassDialogBack();
/* 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().
// Character generation: Create Class dialog
void onCreateClassDialogDone(WindowBase* parWindow);
void onCreateClassDialogBack();
The setting should also affect visibility of certain HUD
elements, but this is not done yet.
*/
GuiWindow allowed;
// Character generation: Birth sign dialog
void onBirthSignDialogDone(WindowBase* parWindow);
void onBirthSignDialogBack();
void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings
// Character generation: Review dialog
void onReviewDialogDone(WindowBase* parWindow);
void onReviewDialogBack();
void onReviewActivateDialog(int parDialog);
int showFPSLevel;
float mFPS;
size_t mTriangleCount;
size_t mBatchCount;
enum CreationStageEnum
{
NotStarted,
NameChosen,
RaceChosen,
ClassChosen,
BirthSignChosen,
ReviewNext
};
// Which state the character creating is in, controls back/next/ok buttons
CreationStageEnum creationStage;
void onDialogueWindowBye();
};
template<typename T>

@ -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);

@ -250,6 +250,7 @@ namespace MWMechanics
while (iter!=mActors.end())
if (iter->getCell()==cellStore)
{
//std::cout << "Erasing an actor";
mActors.erase (iter++);
}
else

@ -0,0 +1,126 @@
#include "actors.hpp"
#include <OgreSceneNode.h>
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<MWWorld::Ptr, Animation*>(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<MWWorld::Ptr::CellStore *, Ogre::SceneNode *>::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<MWWorld::Ptr, Animation*>::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<MWWorld::Ptr, Animation*>::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++)
{
(iter->second)->runAnimation(duration);
}
}

@ -0,0 +1,59 @@
#ifndef _GAME_RENDER_ACTORS_H
#define _GAME_RENDER_ACTORS_H
#include "components/esm_store/cell_store.hpp"
#include <map>
#include <list>
#include <openengine/ogre/renderer.hpp>
#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 <openengine/bullet/physic.hpp>
namespace MWRender{
class Actors{
OEngine::Render::OgreRenderer &mRend;
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
Ogre::SceneNode* mMwRoot;
MWWorld::Environment& mEnvironment;
std::map<MWWorld::Ptr, Animation*> 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

@ -0,0 +1,480 @@
#include "animation.hpp"
namespace MWRender{
std::map<std::string, int> 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<std::string, float>::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<std::string, float>::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<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){
shapeNumber = 0;
std::vector<Nif::NiTriShapeCopy>::iterator allshapesiter;
for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++)
{
//std::map<unsigned short, PosAndRot> vecPosRot;
Nif::NiTriShapeCopy& copy = *allshapesiter;
std::vector<Ogre::Vector3>* allvertices = &copy.vertices;
//std::set<unsigned int> vertices;
//std::set<unsigned int> normals;
//std::vector<Nif::NiSkinData::BoneInfoCopy> boneinfovector = copy.boneinfo;
std::map<int, std::vector<Nif::NiSkinData::IndividualWeight> >* verticesToChange = &copy.vertsToWeights;
//std::cout << "Name " << copy.sname << "\n";
Ogre::HardwareVertexBufferSharedPtr vbuf = creaturemodel->getMesh()->getSubMesh(copy.sname)->vertexData->vertexBufferBinding->getBuffer(0);
Ogre::Real* pReal = static_cast<Ogre::Real*>(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL));
std::vector<Ogre::Vector3> 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<std::size_t> (shapeNumber))
{
std::vector<int> 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<Ogre::Vector3> 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<int, std::vector<Nif::NiSkinData::IndividualWeight> >::iterator iter = verticesToChange->begin();
iter != verticesToChange->end(); iter++)
{
std::vector<Nif::NiSkinData::IndividualWeight> 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<Nif::NiSkinData::BoneInfoCopy*, PosAndRot>::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<std::string> boneSequence = copy.boneSequence;
Ogre::Vector3 transmult;
Ogre::Quaternion rotmult;
float scale;
if(boneSequence.size() > 0){
std::vector<std::string>::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<float> & 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<Nif::NiKeyframeData>::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<Ogre::Quaternion> & quats = iter->getQuat();
const std::vector<float> & ttime = iter->gettTime();
const std::vector<float> & rtime = iter->getrTime();
int rindexJ = rindexI[slot];
timeIndex(time, rtime, rindexI[slot], rindexJ, x2);
int tindexJ = tindexI[slot];
const std::vector<Ogre::Vector3> & 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();
}
}
}

@ -0,0 +1,73 @@
#ifndef _GAME_RENDER_ANIMATION_H
#define _GAME_RENDER_ANIMATION_H
#include <components/nif/data.hpp>
#include <openengine/ogre/renderer.hpp>
#include "../mwworld/refdata.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp"
#include "../mwworld/environment.hpp"
#include <components/nif/node.hpp>
#include <map>
#include <openengine/bullet/physic.hpp>
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<Nif::NiSkinData::BoneInfoCopy*, PosAndRot> vecRotPos;
static std::map<std::string, int> mUniqueIDs;
std::vector<std::vector<Nif::NiTriShapeCopy>* > 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::vector<int>rindexI;
//Represents a translation index for each bone
std::vector<int>tindexI;
//Only shapes with morphing data will use a shape number
int shapeNumber;
std::vector<std::vector<int> > shapeIndexI;
//Ogre::SkeletonInstance* skel;
std::vector<Nif::NiTriShapeCopy>* shapes; //All the NiTriShapeData for a creature
std::vector<Ogre::Entity*> entityparts;
std::vector<Nif::NiKeyframeData>* transformations;
std::map<std::string,float>* textmappings;
Ogre::Entity* base;
void handleShapes(std::vector<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel);
void handleAnimationTransforms();
bool timeIndex( float time, const std::vector<float> & 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

@ -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<ESM::Creature, MWWorld::RefData> *ref =
ptr.get<ESM::Creature>();
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());
}
}
}

@ -0,0 +1,26 @@
#ifndef _GAME_RENDER_CREATUREANIMATION_H
#define _GAME_RENDER_CREATUREANIMATION_H
#include "animation.hpp"
#include <components/nif/node.hpp>
#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

@ -1,2 +0,0 @@
#include "creatures.hpp"
using namespace MWRender;

@ -1,10 +0,0 @@
#ifndef _GAME_RENDER_CREATURES_H
#define _GAME_RENDER_CREATURES_H
#include <openengine/ogre/renderer.hpp>
namespace MWRender{
class Creatures{
};
}
#endif

@ -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<ESM::NPC, MWWorld::RefData> *ref =
ptr.get<ESM::NPC>();
//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<std::vector<Nif::NiTriShapeCopy>*>::iterator shapepartsiter = shapeparts.begin();
std::vector<Ogre::Entity*>::iterator entitypartsiter = entityparts.begin();
while(shapepartsiter != shapeparts.end())
{
vecRotPos.clear();
std::vector<Nif::NiTriShapeCopy>* shapes = *shapepartsiter;
Ogre::Entity* theentity = *entitypartsiter;
handleShapes(shapes, theentity, base->getSkeleton());
shapepartsiter++;
entitypartsiter++;
}
}
}
}

@ -0,0 +1,30 @@
#ifndef _GAME_RENDER_NPCANIMATION_H
#define _GAME_RENDER_NPCANIMATION_H
#include "animation.hpp"
#include <components/nif/data.hpp>
#include <components/nif/node.hpp>
#include <components/nif/property.hpp>
#include <components/nif/controller.hpp>
#include <components/nif/extra.hpp>
#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

@ -1,2 +0,0 @@
#include "npcs.hpp"
using namespace MWRender;

@ -1,9 +0,0 @@
#ifndef _GAME_RENDER_NPCS_H
#define _GAME_RENDER_NPCS_H
#include <openengine/ogre/renderer.hpp>
namespace MWRender{
class Npcs{
};
}
#endif

@ -1,11 +1,11 @@
#include "objects.hpp"
#include <OgreSceneNode.h>
#include <components/nifogre/ogre_nif_loader.hpp>
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::setMwRoot(Ogre::SceneNode* root)
{
mMwRoot = root;
}
void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){
Ogre::SceneNode* root = mwRoot;
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.
}
light->setAttenuation(10*radius, cval, lval, qval);
insert->attachObject(light);
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.
}
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; i<base->numChildren(); ++i)
clearSceneNode (static_cast<Ogre::SceneNode *> (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<MWWorld::RefData>& cell){
if(mSG.find(&cell) != mSG.end())
void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
{
if(mStaticGeometry.find(&cell) != mStaticGeometry.end())
{
Ogre::StaticGeometry* sg = mSG[&cell];
Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
sg->build();
}
}

@ -1,20 +1,21 @@
#ifndef _GAME_RENDER_OBJECTS_H
#define _GAME_RENDER_OBJECTS_H
#include "components/esm_store/cell_store.hpp"
#include <openengine/ogre/renderer.hpp>
#include <components/esm_store/cell_store.hpp>
#include "../mwworld/refdata.hpp"
#include "../mwworld/ptr.hpp"
#include <openengine/ogre/renderer.hpp>
namespace MWRender{
class Objects{
OEngine::Render::OgreRenderer &mRend;
OEngine::Render::OgreRenderer &mRenderer;
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mSG;
Ogre::SceneNode* mwRoot;
bool isStatic;
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> 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);

@ -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(){};
};
}

@ -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<MWWorld::RefData> &mCell)
@ -187,31 +229,44 @@ void RenderingManager::configureFog(ESMS::CellStore<MWWorld::RefData> &mCell)
Ogre::ColourValue color;
color.setAsABGR (mCell.cell->ambi.fog);
float high = 4500 + 9000 * (1-mCell.cell->ambi.fogDensity);
float low = 200;
configureFog(mCell.cell->ambi.fogDensity, color);
}
rend.getScene()->setFog (FOG_LINEAR, color, 0, low, high);
rend.getCamera()->setFarClipDistance (high + 10);
rend.getViewport()->setBackgroundColour (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<MWWorld::RefData> &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

@ -9,6 +9,7 @@
#include <utility>
#include <openengine/ogre/renderer.hpp>
#include <openengine/ogre/fader.hpp>
#include <openengine/bullet/physic.hpp>
#include <vector>
@ -19,9 +20,9 @@
#include <boost/filesystem.hpp>
#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<MWWorld::RefData> &mCell);
/// configure fog according to cell
void configureFog(ESMS::CellStore<MWWorld::RefData> &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;
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;

@ -1,116 +1,783 @@
#include "sky.hpp"
#include "Caelum.h"
namespace MWRender
{
//
// 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
{
protected:
Caelum::CaelumSystem* mpCaelumSystem;
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)
#include <OgreCamera.h>
#include <OgreRenderWindow.h>
#include <OgreSceneNode.h>
#include <OgreMesh.h>
#include <OgreSceneManager.h>
#include <OgreHardwareVertexBuffer.h>
#include <OgreHighLevelGpuProgramManager.h>
#include <components/nifogre/ogre_nif_loader.hpp>
#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)
{
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; i<colourBuffer->getNumVertices(); ++i)
{
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);
// Get a pointer to the vertex colour
ves_diffuse->baseVertexPointerToElement( pData, &currentVertex );
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)
{
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;
}
uint8 tmpR = static_cast<uint8>(255);
uint8 tmpG = static_cast<uint8>(255);
uint8 tmpB = static_cast<uint8>(255);
uint8 tmpA = static_cast<uint8>(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<unsigned char *> (pData) + vertex_size;
}
CaelumManager::~CaelumManager()
// 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; i<night1_ent->getNumSubEntities(); ++i)
{
if (mpCaelumSystem)
mpCaelumSystem->shutdown (false);
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;
}
/// 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)
// 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<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
mSecunda->setPhase ( static_cast<Moon::Phase>( (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)
{
SkyManager* pSkyManager = NULL;
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;
try
if (mGlareEnabled)
{
pSkyManager = new CaelumManager(pRenderWindow, pCamera, resDir);
mGlareFade += duration*3;
if (mGlareFade > 1) mGlareFade = 1;
}
catch (Ogre::Exception& e)
else
{
std::cout << "\nOGRE Exception when attempting to add sky: "
<< e.getFullDescription().c_str() << std::endl;
mGlareFade -= duration*3;
if (mGlareFade < 0.3) mGlareFade = 0;
}
catch (std::exception& e)
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
{
std::cout << "\nException when attempting to add sky: "
<< e.what() << std::endl;
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);
}
return pSkyManager;
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;
}

@ -1,40 +1,217 @@
#ifndef _GAME_RENDER_SKY_H
#define _GAME_RENDER_SKY_H
#include <boost/filesystem.hpp>
#include <OgreVector3.h>
#include <OgreString.h>
#include <OgreMaterial.h>
#include <OgreColourValue.h>
#include <OgreHighLevelGpuProgram.h>
#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();
void update(float duration);
void enable();
void disable();
void setHour (double hour);
///< will be called even when sky is disabled.
void setDate (int day, int month);
///< will be called even when sky is disabled.
int getMasserPhase() const;
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
/// 3 waxing or waning gibbous, 4 full moon
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;
virtual void enable() = 0;
Ogre::MaterialPtr mStarsMaterials[7];
virtual void disable() = 0;
Ogre::HighLevelGpuProgramPtr mCloudFragmentShader;
virtual void setHour (double hour) = 0;
// 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;
virtual void setDate (int day, int month) = 0;
Ogre::Overlay* mThunderOverlay;
Ogre::TextureUnitState* mThunderTextureUnit;
float mRemainingTransitionTime;
virtual int getMasserPhase() const = 0;
float mGlareFade;
virtual int getSecundaPhase() const = 0;
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
virtual void setMoonColour (bool red) = 0;
bool mEnabled;
bool mGlareEnabled;
bool mSunEnabled;
bool mMasserEnabled;
bool mSecundaEnabled;
};
}

@ -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,

@ -0,0 +1,127 @@
#include "animationextensions.hpp"
#include <stdexcept>
#include <components/compiler/extensions.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp>
#include "../mwworld/world.hpp"
#include "interpretercontext.hpp"
#include "ref.hpp"
namespace MWScript
{
namespace Animation
{
template<class R>
class OpSkipAnim : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().skipAnimation (ptr);
}
};
template<class R>
class OpPlayAnim : public Interpreter::Opcode1
{
public:
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime);
InterpreterContext& context =
static_cast<InterpreterContext&> (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 R>
class OpLoopAnim : public Interpreter::Opcode1
{
public:
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
MWWorld::Ptr ptr = R()(runtime);
InterpreterContext& context =
static_cast<InterpreterContext&> (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<ImplicitRef>);
interpreter.installSegment5 (opcodeSkipAnimExplicit, new OpSkipAnim<ExplicitRef>);
interpreter.installSegment3 (opcodePlayAnim, new OpPlayAnim<ImplicitRef>);
interpreter.installSegment3 (opcodePlayAnimExplicit, new OpPlayAnim<ExplicitRef>);
interpreter.installSegment3 (opcodeLoopAnim, new OpLoopAnim<ImplicitRef>);
interpreter.installSegment3 (opcodeLoopAnimExplicit, new OpLoopAnim<ExplicitRef>);
}
}
}

@ -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

@ -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<MWScript::InterpreterContext&> (runtime.getContext());
std::string item = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
std::vector<MWWorld::Ptr> 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<MWWorld::Ptr>::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<MWScript::InterpreterContext&> (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<MWWorld::Ptr> 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<MWWorld::Ptr>::iterator iter (list.begin());
iter!=list.end() && count;
for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count;
++iter)
{
if (iter->getRefData().getCount()<=count)
{
count -= iter->getRefData().getCount();
iter->getRefData().setCount (0);
}
else
if (iter->getCellRef().refID==item)
{
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;
}
}
}

@ -1,5 +1,5 @@
#include "statsextensions.hpp"
#include "controlextensions.hpp"
#include <components/compiler/extensions.hpp>
@ -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");
}
};

@ -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

@ -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);
}
}

@ -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<InterpreterContext&> (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<InterpreterContext&> (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<InterpreterContext&> (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<InterpreterContext&> (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<ImplicitRef>);
interpreter.installSegment5 (opcodeUnlockExplicit, new OpUnlock<ExplicitRef>);
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);
}
}
}

@ -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<InterpreterContext&> (runtime.getContext());
runtime.push (context.getWorld().getCurrentWeather());
}
};
class OpChangeWeather : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (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);
}
}
}

@ -10,6 +10,7 @@
#include "../mwworld/class.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "interpretercontext.hpp"
#include "ref.hpp"

@ -4,15 +4,12 @@
#include <algorithm>
#include <map>
using namespace std;
#include <OgreRoot.h>
#include <openengine/sound/sndmanager.hpp>
#include <mangle/sound/clients/ogre_listener_mover.hpp>
#include <mangle/sound/clients/ogre_output_updater.hpp>
#include <components/file_finder/file_finder.hpp>
#include <components/esm_store/store.hpp>
#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<std::string,Mangle::Sound::WSoundPtr> IDMap;
typedef std::map<MWWorld::Ptr,IDMap> 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)
{
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)
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)
{
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)
// The music library will accept these filetypes
// If none is given then it will accept all filetypes
std::vector<std::string> acceptableExtensions;
acceptableExtensions.push_back(".mp3");
acceptableExtensions.push_back(".wav");
acceptableExtensions.push_back(".ogg");
acceptableExtensions.push_back(".flac");
// 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(musicpath.has(str)) return true;
// Not found? Try with .mp3
return musicpath.has(toMp3(str));
Files::FileLister(*it / std::string("Sound"), mSoundFiles, true);
}
else
{
if(files.has(str)) return true;
return files.has(toMp3(str));
}
}
else
{
if(music)
{
if(musicpathStrict.has(str)) return true;
// Not found? Try with .mp3
return musicpathStrict.has(toMp3(str));
}
else
// 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)
{
if(strict.has(str)) return true;
return strict.has(toMp3(str));
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);
}
}
}
// 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)
{
// 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);
}
else
if(mUsingSound)
{
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);
Ogre::Root::getSingleton().removeFrameListener(&updater);
cameraTracker.unfollowCamera();
}
// 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)
{
if(!mData) return;
void SoundManager::stopMusic()
{
if (music)
music->stop();
setPlaylist();
}
void SoundManager::streamMusicFull(const std::string& filename)
{
// 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<boost::filesystem::path>::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<ESM::Region::SoundRef>::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 = "";
}
}

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

Loading…
Cancel
Save