From ae5d25ac58ad7aeeea5fc1b02ea170904cd758a1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 14:23:27 +0100 Subject: [PATCH 01/36] FindSDL.cmake is unused --- cmake/FindSDL.cmake | 177 -------------------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 cmake/FindSDL.cmake diff --git a/cmake/FindSDL.cmake b/cmake/FindSDL.cmake deleted file mode 100644 index 0dc02f5b6..000000000 --- a/cmake/FindSDL.cmake +++ /dev/null @@ -1,177 +0,0 @@ -# Locate SDL library -# This module defines -# SDL_LIBRARY, the name of the library to link against -# SDL_FOUND, if false, do not try to link to SDL -# SDL_INCLUDE_DIR, where to find SDL.h -# -# This module responds to the the flag: -# SDL_BUILDING_LIBRARY -# If this is defined, then no SDL_main will be linked in because -# only applications need main(). -# Otherwise, it is assumed you are building an application and this -# module will attempt to locate and set the the proper link flags -# as part of the returned SDL_LIBRARY variable. -# -# Don't forget to include SDLmain.h and SDLmain.m your project for the -# OS X framework based version. (Other versions link to -lSDLmain which -# this module will try to find on your behalf.) Also for OS X, this -# module will automatically add the -framework Cocoa on your behalf. -# -# -# Additional Note: If you see an empty SDL_LIBRARY_TEMP in your configuration -# and no SDL_LIBRARY, it means CMake did not find your SDL library -# (SDL.dll, libsdl.so, SDL.framework, etc). -# Set SDL_LIBRARY_TEMP to point to your SDL library, and configure again. -# Similarly, if you see an empty SDLMAIN_LIBRARY, you should set this value -# as appropriate. These values are used to generate the final SDL_LIBRARY -# variable, but when these values are unset, SDL_LIBRARY does not get created. -# -# -# $SDLDIR is an environment variable that would -# correspond to the ./configure --prefix=$SDLDIR -# used in building SDL. -# l.e.galup 9-20-02 -# -# Modified by Eric Wing. -# Added code to assist with automated building by using environmental variables -# and providing a more controlled/consistent search behavior. -# Added new modifications to recognize OS X frameworks and -# additional Unix paths (FreeBSD, etc). -# Also corrected the header search path to follow "proper" SDL guidelines. -# Added a search for SDLmain which is needed by some platforms. -# Added a search for threads which is needed by some platforms. -# Added needed compile switches for MinGW. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. -# -# Note that the header path has changed from SDL/SDL.h to just SDL.h -# This needed to change because "proper" SDL convention -# is #include "SDL.h", not . This is done for portability -# reasons because not all systems place things in SDL/ (see FreeBSD). - -#============================================================================= -# Copyright 2003-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -FIND_PATH(SDL_INCLUDE_DIR SDL.h - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES include/SDL include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local/include/SDL12 - /usr/local/include/SDL11 # FreeBSD ports - /usr/include/SDL12 - /usr/include/SDL11 - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) -#MESSAGE("SDL_INCLUDE_DIR is ${SDL_INCLUDE_DIR}") - -# SDL-1.1 is the name used by FreeBSD ports... -# don't confuse it for the version number. -FIND_LIBRARY(SDL_LIBRARY_TEMP - NAMES SDL SDL-1.1 - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt -) - -#MESSAGE("SDL_LIBRARY_TEMP is ${SDL_LIBRARY_TEMP}") - -IF(NOT SDL_BUILDING_LIBRARY) - IF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") - # Non-OS X framework versions expect you to also dynamically link to - # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms - # seem to provide SDLmain for compatibility even though they don't - # necessarily need it. - FIND_LIBRARY(SDLMAIN_LIBRARY - NAMES SDLmain SDLmain-1.1 - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt - ) - ENDIF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") -ENDIF(NOT SDL_BUILDING_LIBRARY) - -# SDL may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -IF(NOT APPLE) - FIND_PACKAGE(Threads) -ENDIF(NOT APPLE) - -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) -IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") -ENDIF(MINGW) - -SET(SDL_FOUND "NO") -IF(SDL_LIBRARY_TEMP) - # For SDLmain - IF(NOT SDL_BUILDING_LIBRARY) - IF(SDLMAIN_LIBRARY) - SET(SDL_LIBRARY_TEMP ${SDLMAIN_LIBRARY} ${SDL_LIBRARY_TEMP}) - ENDIF(SDLMAIN_LIBRARY) - ENDIF(NOT SDL_BUILDING_LIBRARY) - - # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - IF(APPLE) - SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa") - ENDIF(APPLE) - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - IF(NOT APPLE) - SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF(NOT APPLE) - - # For MinGW library - IF(MINGW) - SET(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP}) - ENDIF(MINGW) - - # Set the final string here so the GUI reflects the final state. - SET(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found") - # Set the temp variable to INTERNAL so it is not seen in the CMake GUI - SET(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "") - - SET(SDL_FOUND "YES") -ENDIF(SDL_LIBRARY_TEMP) - -#MESSAGE("SDL_LIBRARY is ${SDL_LIBRARY}") - From 90f6cda4cc4781c014352f79230a97a35a5d4a4f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 14:33:30 +0100 Subject: [PATCH 02/36] Moved includes to appropriate place --- apps/openmw/mwrender/terrainstorage.hpp | 3 +++ components/terrain/storage.hpp | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 5c2035952..f2c4ecae3 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -1,6 +1,9 @@ #ifndef MWRENDER_TERRAINSTORAGE_H #define MWRENDER_TERRAINSTORAGE_H +#include +#include + #include namespace MWRender diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 021e01c7e..9864e9825 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -1,9 +1,6 @@ #ifndef COMPONENTS_TERRAIN_STORAGE_H #define COMPONENTS_TERRAIN_STORAGE_H -#include -#include - #include #include From 439018e7067015975fcca25772838d65730521d5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 15:08:40 +0100 Subject: [PATCH 03/36] Get rid of useless return values --- apps/openmw/mwinput/inputmanagerimp.cpp | 21 ++++++------------- apps/openmw/mwinput/inputmanagerimp.hpp | 10 ++++----- extern/oics/ICSInputControlSystem.h | 18 ++++++++-------- .../oics/ICSInputControlSystem_joystick.cpp | 19 +++++------------ .../oics/ICSInputControlSystem_keyboard.cpp | 10 +++------ extern/oics/ICSInputControlSystem_mouse.cpp | 12 +++-------- extern/sdl4ogre/events.h | 18 ++++++++-------- 7 files changed, 40 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 66d93469c..8bc6facea 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -452,7 +452,7 @@ namespace MWInput mInputBinder->adjustMouseRegion(width, height); } - bool InputManager::keyPressed( const SDL_KeyboardEvent &arg ) + void InputManager::keyPressed( const SDL_KeyboardEvent &arg ) { // Cut, copy & paste MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); @@ -498,7 +498,6 @@ namespace MWInput if (kc != OIS::KC_UNASSIGNED) MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); - return true; } void InputManager::textInput(const SDL_TextInputEvent &arg) @@ -509,23 +508,21 @@ namespace MWInput MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it); } - bool InputManager::keyReleased(const SDL_KeyboardEvent &arg ) + void InputManager::keyReleased(const SDL_KeyboardEvent &arg ) { mInputBinder->keyReleased (arg); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)); - - return true; } - bool InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) + void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) { mInputBinder->mousePressed (arg, id); if (id != SDL_BUTTON_LEFT && id != SDL_BUTTON_RIGHT) - return true; // MyGUI has no use for these events + return; // MyGUI has no use for these events MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)); if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) @@ -536,20 +533,16 @@ namespace MWInput MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); } } - - return true; } - bool InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) + void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) { mInputBinder->mouseReleased (arg, id); MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)); - - return true; } - bool InputManager::mouseMoved(const SFO::MouseMotionEvent &arg ) + void InputManager::mouseMoved(const SFO::MouseMotionEvent &arg ) { mInputBinder->mouseMoved (arg); @@ -597,8 +590,6 @@ namespace MWInput MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true); } } - - return true; } void InputManager::windowFocusChange(bool have_focus) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d41b4c3f3..bd3f4954b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -86,13 +86,13 @@ namespace MWInput virtual void resetToDefaultBindings(); public: - virtual bool keyPressed(const SDL_KeyboardEvent &arg ); - virtual bool keyReleased( const SDL_KeyboardEvent &arg ); + virtual void keyPressed(const SDL_KeyboardEvent &arg ); + virtual void keyReleased( const SDL_KeyboardEvent &arg ); virtual void textInput (const SDL_TextInputEvent &arg); - virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual bool mouseMoved( const SFO::MouseMotionEvent &arg ); + virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual void mouseMoved( const SFO::MouseMotionEvent &arg ); virtual void windowVisibilityChange( bool visible ); virtual void windowFocusChange( bool have_focus ); diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index 907cba5fc..a83ae539e 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -102,19 +102,19 @@ namespace ICS JoystickIDList& getJoystickIdList(){ return mJoystickIDList; }; // MouseListener - bool mouseMoved(const SFO::MouseMotionEvent &evt); - bool mousePressed(const SDL_MouseButtonEvent &evt, Uint8); - bool mouseReleased(const SDL_MouseButtonEvent &evt, Uint8); + void mouseMoved(const SFO::MouseMotionEvent &evt); + void mousePressed(const SDL_MouseButtonEvent &evt, Uint8); + void mouseReleased(const SDL_MouseButtonEvent &evt, Uint8); // KeyListener - bool keyPressed(const SDL_KeyboardEvent &evt); - bool keyReleased(const SDL_KeyboardEvent &evt); + void keyPressed(const SDL_KeyboardEvent &evt); + void keyReleased(const SDL_KeyboardEvent &evt); // JoyStickListener - bool buttonPressed(const SDL_JoyButtonEvent &evt, int button); - bool buttonReleased(const SDL_JoyButtonEvent &evt, int button); - bool axisMoved(const SDL_JoyAxisEvent &evt, int axis); - bool povMoved(const SDL_JoyHatEvent &evt, int index); + void buttonPressed(const SDL_JoyButtonEvent &evt, int button); + void buttonReleased(const SDL_JoyButtonEvent &evt, int button); + void axisMoved(const SDL_JoyAxisEvent &evt, int axis); + void povMoved(const SDL_JoyHatEvent &evt, int index); //TODO: does this have an SDL equivalent? //bool sliderMoved(const OIS::JoyStickEvent &evt, int index); diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp index 8e501d501..21adc9f74 100644 --- a/extern/oics/ICSInputControlSystem_joystick.cpp +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -318,7 +318,7 @@ namespace ICS } // joyStick listeners - bool InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button) + void InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { @@ -354,11 +354,9 @@ namespace ICS mDetectingBindingControl, evt.which, button, mDetectingBindingDirection); } } - - return true; } - bool InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button) + void InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { @@ -371,10 +369,9 @@ namespace ICS } } } - return true; } - bool InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis) + void InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis) { if(mActive) { @@ -417,12 +414,10 @@ namespace ICS } } } - - return true; } //Here be dragons, apparently - bool InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index) + void InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index) { if(mActive) { @@ -542,13 +537,11 @@ namespace ICS } } } - - return true; } //TODO: does this have an SDL equivalent? /* - bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) + void InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) { if(mActive) { @@ -590,8 +583,6 @@ namespace ICS } } } - - return true; } */ diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp index 01d68f784..0a9a34d63 100644 --- a/extern/oics/ICSInputControlSystem_keyboard.cpp +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -85,7 +85,7 @@ namespace ICS return SDLK_UNKNOWN; } - bool InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt) + void InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt) { if(mActive) { @@ -118,11 +118,9 @@ namespace ICS mDetectingBindingControl, evt.keysym.sym, mDetectingBindingDirection); } } - - return true; - } + } - bool InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt) + void InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt) { if(mActive) { @@ -132,8 +130,6 @@ namespace ICS it->second.control->setChangingDirection(Control::STOP); } } - - return true; } void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp index 52eb894ed..be18ebbc0 100644 --- a/extern/oics/ICSInputControlSystem_mouse.cpp +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -219,7 +219,7 @@ namespace ICS } // mouse Listeners - bool InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt) + void InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt) { if(mActive) { @@ -304,11 +304,9 @@ namespace ICS } } } - - return true; } - bool InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn) + void InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -341,11 +339,9 @@ namespace ICS mDetectingBindingControl, btn, mDetectingBindingDirection); } } - - return true; } - bool InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn) + void InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -355,8 +351,6 @@ namespace ICS it->second.control->setChangingDirection(Control::STOP); } } - - return true; } // mouse auto bindings diff --git a/extern/sdl4ogre/events.h b/extern/sdl4ogre/events.h index 48adb4545..0fb4d6f06 100644 --- a/extern/sdl4ogre/events.h +++ b/extern/sdl4ogre/events.h @@ -26,9 +26,9 @@ class MouseListener { public: virtual ~MouseListener() {} - virtual bool mouseMoved( const MouseMotionEvent &arg ) = 0; - virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; - virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; + virtual void mouseMoved( const MouseMotionEvent &arg ) = 0; + virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; + virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; }; class KeyListener @@ -36,8 +36,8 @@ class KeyListener public: virtual ~KeyListener() {} virtual void textInput (const SDL_TextInputEvent& arg) {} - virtual bool keyPressed(const SDL_KeyboardEvent &arg) = 0; - virtual bool keyReleased(const SDL_KeyboardEvent &arg) = 0; + virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0; + virtual void keyReleased(const SDL_KeyboardEvent &arg) = 0; }; class JoyListener @@ -45,18 +45,18 @@ class JoyListener public: virtual ~JoyListener() {} /** @remarks Joystick button down event */ - virtual bool buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0; + virtual void buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0; /** @remarks Joystick button up event */ - virtual bool buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0; + virtual void buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0; /** @remarks Joystick axis moved event */ - virtual bool axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0; + virtual void axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0; //-- Not so common control events, so are not required --// //! Joystick Event, and povID - virtual bool povMoved( const SDL_JoyHatEvent &arg, int index) {return true;} + virtual void povMoved( const SDL_JoyHatEvent &arg, int index) {} }; class WindowListener From bc376e6649b9d0f6e9f1da01b3410c2b5b9127db Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 17:41:24 +0100 Subject: [PATCH 04/36] Closes #888: Treat "Bip 01" as animation root if existing --- apps/openmw/mwrender/animation.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 44bba90d0..b3aa0cd85 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -285,6 +285,17 @@ void Animation::addAnimSource(const std::string &model) } } + if (grp == 0 && dstval->getNode()->getName() == "Bip01") + { + mNonAccumRoot = dstval->getNode(); + mAccumRoot = mNonAccumRoot->getParent(); + if(!mAccumRoot) + { + std::cerr<< "Non-Accum root for "< Date: Sun, 16 Feb 2014 13:07:32 +0100 Subject: [PATCH 05/36] Terrain: change world bounds from AABB to 4 floats --- apps/openmw/mwrender/terrainstorage.cpp | 6 ++--- apps/openmw/mwrender/terrainstorage.hpp | 2 +- components/terrain/storage.hpp | 2 +- components/terrain/world.cpp | 31 ++++++++++++++----------- components/terrain/world.hpp | 2 +- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 197572db9..750441f6a 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -16,9 +16,9 @@ namespace MWRender { - Ogre::AxisAlignedBox TerrainStorage::getBounds() + void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) { - int minX = 0, minY = 0, maxX = 0, maxY = 0; + minX = 0, minY = 0, maxX = 0, maxY = 0; const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -39,8 +39,6 @@ namespace MWRender // since grid coords are at cell origin, we need to add 1 cell maxX += 1; maxY += 1; - - return Ogre::AxisAlignedBox(minX, minY, 0, maxX, maxY, 0); } ESM::Land* TerrainStorage::getLand(int cellX, int cellY) diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index f2c4ecae3..2ef014aaf 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -17,7 +17,7 @@ namespace MWRender public: /// Get bounds of the whole terrain in cell units - virtual Ogre::AxisAlignedBox getBounds(); + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); /// Get the minimum and maximum heights of a terrain chunk. /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 9864e9825..d8cdab9ec 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -24,7 +24,7 @@ namespace Terrain public: /// Get bounds of the whole terrain in cell units - virtual Ogre::AxisAlignedBox getBounds() = 0; + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; /// Get the minimum and maximum heights of a terrain chunk. /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index f4070393d..4273f227d 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -62,6 +62,10 @@ namespace Terrain , mShaders(shaders) , mVisible(true) , mLoadingListener(loadingListener) + , mMaxX(0) + , mMinX(0) + , mMaxY(0) + , mMinY(0) { loadingListener->setLabel("Creating terrain"); loadingListener->indicateProgress(); @@ -76,20 +80,21 @@ namespace Terrain mCompositeMapRenderTarget->setAutoUpdated(false); mCompositeMapRenderTarget->addViewport(compositeMapCam); - mBounds = storage->getBounds(); + storage->getBounds(mMinX, mMaxX, mMinY, mMaxY); - int origSizeX = mBounds.getSize().x; - int origSizeY = mBounds.getSize().y; + int origSizeX = mMaxX-mMinX; + int origSizeY = mMaxY-mMinY; // Dividing a quad tree only works well for powers of two, so round up to the nearest one int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); // Adjust the center according to the new size - Ogre::Vector3 center = mBounds.getCenter() + Ogre::Vector3((size-origSizeX)/2.f, (size-origSizeY)/2.f, 0); + float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f; + float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL); + mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); buildQuadTree(mRootNode); loadingListener->indicateProgress(); mRootNode->initAabb(); @@ -122,10 +127,10 @@ namespace Terrain return; } - if (node->getCenter().x - halfSize > mBounds.getMaximum().x - || node->getCenter().x + halfSize < mBounds.getMinimum().x - || node->getCenter().y - halfSize > mBounds.getMaximum().y - || node->getCenter().y + halfSize < mBounds.getMinimum().y ) + if (node->getCenter().x - halfSize > mMaxX + || node->getCenter().x + halfSize < mMinX + || node->getCenter().y - halfSize > mMaxY + || node->getCenter().y + halfSize < mMinY ) // Out of bounds of the actual terrain - this will happen because // we rounded the size up to the next power of two { @@ -162,10 +167,10 @@ namespace Terrain Ogre::AxisAlignedBox World::getWorldBoundingBox (const Ogre::Vector2& center) { - if (center.x > mBounds.getMaximum().x - || center.x < mBounds.getMinimum().x - || center.y > mBounds.getMaximum().y - || center.y < mBounds.getMinimum().y) + if (center.x > mMaxX + || center.x < mMinX + || center.y > mMaxY + || center.y < mMinY) return Ogre::AxisAlignedBox::BOX_NULL; QuadTreeNode* node = findNode(center, mRootNode); Ogre::AxisAlignedBox box = node->getBoundingBox(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index b8c1b0a7d..bf733b889 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -105,7 +105,7 @@ namespace Terrain Ogre::SceneManager* mCompositeMapSceneMgr; /// Bounds in cell units - Ogre::AxisAlignedBox mBounds; + float mMinX, mMaxX, mMinY, mMaxY; /// Minimum size of a terrain batch along one side (in cell units) float mMinBatchSize; From ebc67a82cf7fda16943353ac834cfeff6ed4e6ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Feb 2014 13:12:31 +0100 Subject: [PATCH 06/36] Don't list unnamed quests in the quest book --- apps/openmw/mwgui/journalviewmodel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index a0d67b025..c6bd6d15d 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -206,9 +206,9 @@ struct JournalViewModelImpl : JournalViewModel const MWDialogue::Quest& quest = i->second; // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. - if (quest.getName().empty()) - visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); - else + // Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed + // to appear in the quest book. + if (!quest.getName().empty()) visitor (reinterpret_cast (&i->second), toUtf8Span (quest.getName())); } } From d25b3ad9cb969967cb7e87b37651668815871130 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Feb 2014 18:48:03 +0100 Subject: [PATCH 07/36] Fix AiCombat for creatures with weapons --- apps/openmw/mwmechanics/aicombat.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 3653587f8..6a49f2f5e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -13,7 +13,7 @@ #include "../mwbase/dialoguemanager.hpp" -#include "npcstats.hpp" +#include "creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" #include "character.hpp" // fixme: for getActiveWeapon @@ -138,11 +138,11 @@ namespace MWMechanics { MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); //Get weapon speed and range MWWorld::ContainerStoreIterator weaponSlot = - MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype); + MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype); if (weaptype == WeapType_HandToHand) { const MWWorld::Store &gmst = From 85b72409481622c1f29e779f61f61f2f0a9bc64a Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 19 Feb 2014 13:43:14 +0100 Subject: [PATCH 08/36] Made the git version retrieval more reliable --- CMakeLists.txt | 54 ++++++++++++++++----------- cmake/GetGitRevisionDescription.cmake | 21 ++++++++--- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9d1f2017..5451fd131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,27 +13,39 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) include(OpenMWMacros) # Version - -include(GetGitRevisionDescription) - -get_git_tag_revision(TAGHASH --tags --max-count=1 "HEAD...") -get_git_head_revision(REFSPEC COMMITHASH) -git_describe(VERSION --tags ${TAGHASH}) - -string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") -if (MATCH) - string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") - - set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") - set(OPENMW_VERSION_TAGHASH "${TAGHASH}") - - message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") -else (MATCH) - message(FATAL_ERROR "Failed to get valid version information from Git") -endif (MATCH) +set(OPENMW_VERSION_MAJOR 0) +set(OPENMW_VERSION_MINOR 29) +set(OPENMW_VERSION_RELEASE 0) + +set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") + +if(EXISTS ${PROJECT_SOURCE_DIR}/.git) + find_package(Git) + + if(GIT_FOUND) + include(GetGitRevisionDescription) + get_git_tag_revision(TAGHASH --tags --max-count=1) + get_git_head_revision(REFSPEC COMMITHASH) + git_describe(VERSION --tags ${TAGHASH}) + + string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") + if(MATCH) + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") + + set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") + set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") + set(OPENMW_VERSION_TAGHASH "${TAGHASH}") + + message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") + else(MATCH) + message(WARNING "Failed to get valid version information from Git") + endif(MATCH) + else(GIT_FOUND) + message(WARNING "Git executable not found") + endif(GIT_FOUND) +endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) # doxygen main page diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index fecd1654d..216eeb9fb 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -85,10 +85,6 @@ function(get_git_head_revision _refspecvar _hashvar) endfunction() function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - #get_git_head_revision(refspec hash) if(NOT GIT_FOUND) @@ -124,6 +120,20 @@ function(git_describe _var) out OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + "--always" + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() + if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() @@ -133,7 +143,8 @@ endfunction() function(get_git_tag_revision _var) if(NOT GIT_FOUND) - find_package(Git QUIET) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() endif() execute_process(COMMAND From b43325119a72009868901f96bea19bca5359fc5e Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 19 Feb 2014 14:19:08 +0100 Subject: [PATCH 09/36] Some changes to the version retrieval: ignore shallow clones --- CMakeLists.txt | 66 +++++++++++++++------------ apps/launcher/maindialog.cpp | 23 ++++++---- cmake/GetGitRevisionDescription.cmake | 14 ------ 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5451fd131..788616811 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,47 +6,55 @@ if (APPLE) set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") endif (APPLE) -# Macros - set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) -include(OpenMWMacros) - # Version +message(STATUS "Configuring OpenMW...") + set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 29) set(OPENMW_VERSION_RELEASE 0) +set(OPENMW_VERSION_COMMITHASH "") +set(OPENMW_VERSION_TAGHASH "") + set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") if(EXISTS ${PROJECT_SOURCE_DIR}/.git) - find_package(Git) - - if(GIT_FOUND) - include(GetGitRevisionDescription) - get_git_tag_revision(TAGHASH --tags --max-count=1) - get_git_head_revision(REFSPEC COMMITHASH) - git_describe(VERSION --tags ${TAGHASH}) - - string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") - if(MATCH) - string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") - - set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") - set(OPENMW_VERSION_TAGHASH "${TAGHASH}") - - message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") - else(MATCH) - message(WARNING "Failed to get valid version information from Git") - endif(MATCH) - else(GIT_FOUND) - message(WARNING "Git executable not found") - endif(GIT_FOUND) + if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) + find_package(Git) + + if(GIT_FOUND) + include(GetGitRevisionDescription) + get_git_tag_revision(TAGHASH --tags --max-count=1) + get_git_head_revision(REFSPEC COMMITHASH) + git_describe(VERSION --tags ${TAGHASH}) + + string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") + if(MATCH) + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") + + set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") + set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") + set(OPENMW_VERSION_TAGHASH "${TAGHASH}") + + message(STATUS "OpenMW version ${OPENMW_VERSION}") + else(MATCH) + message(WARNING "Failed to get valid version information from Git") + endif(MATCH) + else(GIT_FOUND) + message(WARNING "Git executable not found") + endif(GIT_FOUND) + else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) + message(STATUS "Shallow Git clone detected, not attempting to retrieve version info") + endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) +# Macros +include(OpenMWMacros) + # doxygen main page configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 56b3186ff..5cf8f8a89 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -76,17 +76,20 @@ Launcher::MainDialog::MainDialog(QWidget *parent) QString revision(OPENMW_VERSION_COMMITHASH); QString tag(OPENMW_VERSION_TAGHASH); - if (revision == tag) { - versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); - } else { - versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); - } + if (!revision.isEmpty() && !tag.isEmpty()) + { + if (revision == tag) { + versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); + } else { + versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); + } - // Add the compile date and time - versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), - QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), - QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), - QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); + // Add the compile date and time + versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), + QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), + QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), + QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); + } createIcons(); } diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index 216eeb9fb..56ff1d545 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -120,20 +120,6 @@ function(git_describe _var) out OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - execute_process(COMMAND - "${GIT_EXECUTABLE}" - describe - "--always" - WORKING_DIRECTORY - "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - OUTPUT_STRIP_TRAILING_WHITESPACE) - endif() - if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() From 56d537e23ddd48eca6b9d7c44472f94ab6e616be Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 19 Feb 2014 14:37:27 +0100 Subject: [PATCH 10/36] Added some sanity checking: compare git version with manual version --- CMakeLists.txt | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 788616811..ae9ec8ac0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 29) +set(OPENMW_VERSION_MINOR 28) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") @@ -32,13 +32,22 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git) string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") if(MATCH) - string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") - - set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") - set(OPENMW_VERSION_TAGHASH "${TAGHASH}") + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" GIT_VERSION_MAJOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_MINOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_RELEASE "${VERSION}") + + set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}") + + if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) + message(FATAL_ERROR "Silly Zini forgot to update the version again...") + else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) + set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR}) + set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR}) + set(OPENMW_VERSION_RELEASE ${GIT_VERSION_RELEASE}) + + set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") + set(OPENMW_VERSION_TAGHASH "${TAGHASH}") + endif(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) message(STATUS "OpenMW version ${OPENMW_VERSION}") else(MATCH) From c0631187c6836b490cbe6268ed37da50ff11de63 Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 19 Feb 2014 14:48:33 +0100 Subject: [PATCH 11/36] Removed git tag retrieval for Travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 39a02de63..5d0326a07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ branches: - /openmw-.*$/ before_install: - pwd - - git fetch --tags - echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" - echo "yes" | sudo apt-add-repository ppa:openmw/openmw - sudo apt-get update -qq From 736644de05555d9e5b9771a63f35ba28e0b228ff Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 20 Feb 2014 12:31:40 +0100 Subject: [PATCH 12/36] Revert "#1041 in progress: decode first sample batch right in OpenAL_SoundStream::play()" This reverts commit 51fb9f65ea5086433fa0d1db12197b133a9b27fd. --- apps/openmw/mwsound/openal_output.cpp | 45 ++++++++++++++------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 58566225b..7563ad015 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -172,6 +172,7 @@ class OpenAL_SoundStream : public Sound DecoderPtr mDecoder; volatile bool mIsFinished; + volatile bool mIsInitialBatchEnqueued; void updateAll(bool local); @@ -264,7 +265,7 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) - , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) + , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true), mIsInitialBatchEnqueued(false) { throwALerror(); @@ -315,26 +316,8 @@ void OpenAL_SoundStream::play() alSourcei(mSource, AL_BUFFER, 0); throwALerror(); mSamplesQueued = 0; - - std::vector data(mBufferSize); - - bool finished = false; - for(ALuint i = 0;i < sNumBuffers && !finished;i++) - { - size_t got = mDecoder->read(&data[0], data.size()); - finished = (got < data.size()); - if(got > 0) - { - ALuint bufid = mBuffers[i]; - alBufferData(bufid, mFormat, &data[0], got, mSampleRate); - alSourceQueueBuffers(mSource, 1, &bufid); - throwALerror(); - mSamplesQueued += getBufferSampleCount(bufid); - } - } - - mIsFinished = finished; - alSourcePlay(mSource); + mIsFinished = false; + mIsInitialBatchEnqueued = false; mOutput.mStreamThread->add(this); } @@ -342,6 +325,7 @@ void OpenAL_SoundStream::stop() { mOutput.mStreamThread->remove(this); mIsFinished = true; + mIsInitialBatchEnqueued = false; alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); @@ -454,6 +438,24 @@ bool OpenAL_SoundStream::process() } while(processed > 0); throwALerror(); } + else if (!mIsInitialBatchEnqueued) { // nothing enqueued yet + std::vector data(mBufferSize); + + for(ALuint i = 0;i < sNumBuffers && !finished;i++) + { + size_t got = mDecoder->read(&data[0], data.size()); + finished = (got < data.size()); + if(got > 0) + { + ALuint bufid = mBuffers[i]; + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + throwALerror(); + mSamplesQueued += getBufferSampleCount(bufid); + } + } + mIsInitialBatchEnqueued = true; + } if(state != AL_PLAYING && state != AL_PAUSED) { @@ -471,6 +473,7 @@ bool OpenAL_SoundStream::process() std::cout<< "Error updating stream \""<getName()<<"\"" < Date: Thu, 20 Feb 2014 12:35:57 +0100 Subject: [PATCH 13/36] fix broken esm writer header code --- components/esm/esmwriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index f38591b7b..a29b876e7 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -80,8 +80,8 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? + writeT(0); // Size goes here + writeT(0); // Unused header? writeT(flags); mRecords.push_back(rec); From ee7364a8a32d70bb1d0e16df02debf742fd9720f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 20 Feb 2014 12:43:49 +0100 Subject: [PATCH 14/36] updated changelog --- readme.txt | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/readme.txt b/readme.txt index 045b18b8b..07e89affa 100644 --- a/readme.txt +++ b/readme.txt @@ -97,6 +97,72 @@ Allowed options: CHANGELOG +0.29.0 + +Bug #556: Video soundtrack not played when music volume is set to zero +Bug #848: Wrong amount of footsteps playing in 1st person +Bug #888: Ascended Sleepers have movement issues +Bug #892: Explicit references are allowed on all script functions +Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly +Bug #1009: Lake Fjalding AI related slowdown. +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window +Bug #1060: Some message boxes are cut off at the bottom +Bug #1062: Bittercup script does not work ('end' variable) +Bug #1074: Inventory paperdoll obscures armour rating +Bug #1077: Message after killing an essential NPC disappears too fast +Bug #1078: "Clutterbane" shows empty charge bar +Bug #1083: UndoWerewolf fails +Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered +Bug #1090: Start scripts fail when going to a non-predefined cell +Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. +Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior +Bug #1105: Magicka is depleted when using uncastable spells +Bug #1106: Creatures should be able to run +Bug #1107: TR cliffs have way too huge collision boxes in OpenMW +Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. +Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) +Bug #1115: Memory leak when spying on Fargoth +Bug #1137: Script execution fails (drenSlaveOwners script) +Bug #1143: Mehra Milo quest (vivec informants) is broken +Bug #1145: Issues with moving gold between inventory and containers +Bug #1146: Issues with picking up stacks of gold +Bug #1147: Dwemer Crossbows are held incorrectly +Bug #1158: Armor rating should always stay below inventory mannequin +Bug #1159: Quick keys can be set during character generation +Bug #1160: Crash on equip lockpick when +Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Feature #30: Loading/Saving (still missing a few parts) +Feature #101: AI Package: Activate +Feature #103: AI Package: Follow, FollowCell +Feature #138: Editor: Drag & Drop +Feature #428: Player death +Feature #505: Editor: Record Cloning +Feature #701: Levelled creatures +Feature #708: Improved Local Variable handling +Feature #709: Editor: Script verifier +Feature #764: Missing journal backend features +Feature #777: Creature weapons/shields +Feature #789: Editor: Referenceable record verifier +Feature #924: Load/Save GUI (still missing loading screen and progress bars) +Feature #946: Knockdown +Feature #947: Decrease fatigue when running, swimming and attacking +Feature #956: Melee Combat: Blocking +Feature #957: Area magic +Feature #960: Combat/AI combat for creatures +Feature #962: Combat-Related AI instructions +Feature #1075: Damage/Restore skill/attribute magic effects +Feature #1076: Soultrap magic effect +Feature #1081: Disease contraction +Feature #1086: Blood particles +Feature #1092: Interrupt resting +Feature #1101: Inventory equip scripts +Feature #1116: Version/Build number in Launcher window +Feature #1119: Resistance/weakness to normal weapons magic effect +Feature #1123: Slow Fall magic effect +Feature #1130: Auto-calculate spells +Feature #1164: Editor: Case-insensitive sorting in tables + 0.28.0 Bug #399: Inventory changes are not visible immediately From c5b2c154f8b3dc69afbefcd2b40cc53577c4d913 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 20 Feb 2014 12:49:56 +0100 Subject: [PATCH 15/36] another esm writer fix --- components/esm/esmwriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index a29b876e7..ff7bd765d 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -105,7 +105,7 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here + writeT(0); // Size goes here mRecords.push_back(rec); assert(mRecords.back().size == 0); From c3f350e3fb1b7936ad1d78bc79c04054d5489dac Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 22 Feb 2014 12:06:50 +1100 Subject: [PATCH 16/36] Allow MinGW64 compilation in Windows/msys --- .gitignore | 1 + apps/openmw/mwrender/videoplayer.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- components/files/configurationmanager.hpp | 2 +- extern/sdl4ogre/sdlwindowhelper.cpp | 4 ++++ libs/platform/string.h | 2 +- 6 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 3975c4521..ca58fc006 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ Doxygen ## ides/editors *~ +*.bak *.kdev4 *.swp *.swo diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index adf20dc63..4209fb978 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -385,7 +385,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder } void open(const std::string&) -#ifdef _WIN32 +#ifdef _MSC_VER { fail(std::string("Invalid call to ")+__FUNCSIG__); } #else { fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index be6c0b338..21780fd5c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,5 +1,5 @@ #include "worldimp.hpp" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 35144fe04..9f31a7f2d 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -1,7 +1,7 @@ #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include diff --git a/extern/sdl4ogre/sdlwindowhelper.cpp b/extern/sdl4ogre/sdlwindowhelper.cpp index f819043cf..2a54a8636 100644 --- a/extern/sdl4ogre/sdlwindowhelper.cpp +++ b/extern/sdl4ogre/sdlwindowhelper.cpp @@ -31,7 +31,11 @@ SDLWindowHelper::SDLWindowHelper (SDL_Window* window, int w, int h, #ifdef WIN32 case SDL_SYSWM_WINDOWS: // Windows code +#ifdef __MINGW64__ + winHandle = Ogre::StringConverter::toString((DWORD_PTR)wmInfo.info.win.window); +#else winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window); +#endif /* __MINGW64__ */ break; #elif __MACOSX__ case SDL_SYSWM_COCOA: diff --git a/libs/platform/string.h b/libs/platform/string.h index 5368d757c..7f0876587 100644 --- a/libs/platform/string.h +++ b/libs/platform/string.h @@ -9,7 +9,7 @@ #include #if (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) || defined(__MINGW32__) // need our own implementation of strnlen -#ifdef __MINGW32__ +#ifdef __MINGW32__ && !__MINGW64__ static size_t strnlen(const char *s, size_t n) { const char *p = (const char *)memchr(s, 0, n); From c241405d91bfa7dff7cd7149c7b81e1989b345be Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 22 Feb 2014 12:15:20 +1100 Subject: [PATCH 17/36] Fix Windows save & load. --- apps/openmw/engine.cpp | 5 ++++- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- components/esm/esmwriter.cpp | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e80bd954e..09757786b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -339,8 +340,10 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { + boost::filesystem::path saves(mCfgMgr.getUserDataPath() / "saves"); + mEnvironment.setStateManager ( - new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0))); + new MWState::StateManager (saves.make_preferred(), mContentFiles.at (0))); Nif::NIFFile::CacheLock cachelock; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index ba0e1d056..265069dc4 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -176,7 +176,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot else slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); - std::ofstream stream (slot->mPath.string().c_str()); + std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); ESM::ESMWriter writer; diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index f38591b7b..91f123eb7 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -80,8 +80,8 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? + writeT(0); // Size goes here + writeT(0); // Unused header? writeT(flags); mRecords.push_back(rec); @@ -105,7 +105,7 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here + writeT(0); // Size goes here mRecords.push_back(rec); assert(mRecords.back().size == 0); @@ -120,7 +120,7 @@ namespace ESM mStream->seekp(rec.position); mCounting = false; - write (reinterpret_cast (&rec.size), sizeof(int)); + write (reinterpret_cast (&rec.size), sizeof(uint32_t)); mCounting = true; mStream->seekp(0, std::ios::end); From 29a33364cc4f72fc86b3cbb3b6617bf13983388c Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 22 Feb 2014 17:21:19 +1100 Subject: [PATCH 18/36] Another attempt at fixing MinGW64 --- libs/platform/string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/platform/string.h b/libs/platform/string.h index 7f0876587..be2e976b2 100644 --- a/libs/platform/string.h +++ b/libs/platform/string.h @@ -9,7 +9,7 @@ #include #if (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) || defined(__MINGW32__) // need our own implementation of strnlen -#ifdef __MINGW32__ && !__MINGW64__ +#if defined(__MINGW32__) && !defined(__MINGW64__) static size_t strnlen(const char *s, size_t n) { const char *p = (const char *)memchr(s, 0, n); From 2ca28f40bef740aa489d31e6bcada2ab8f4682ac Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 22 Feb 2014 12:55:14 +0100 Subject: [PATCH 19/36] minor addition to change log --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 07e89affa..a23cd1077 100644 --- a/readme.txt +++ b/readme.txt @@ -100,6 +100,7 @@ CHANGELOG 0.29.0 Bug #556: Video soundtrack not played when music volume is set to zero +Bug #829: OpenMW uses up all available vram, when playing for extended time Bug #848: Wrong amount of footsteps playing in 1st person Bug #888: Ascended Sleepers have movement issues Bug #892: Explicit references are allowed on all script functions From ac606a865c5fa2ffbf1dfe521ae0bf5f1384a786 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 22 Feb 2014 23:22:23 +1100 Subject: [PATCH 20/36] Back out unnecessary change. --- apps/openmw/engine.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 09757786b..e80bd954e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -2,7 +2,6 @@ #include -#include #include #include @@ -340,10 +339,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { - boost::filesystem::path saves(mCfgMgr.getUserDataPath() / "saves"); - mEnvironment.setStateManager ( - new MWState::StateManager (saves.make_preferred(), mContentFiles.at (0))); + new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0))); Nif::NIFFile::CacheLock cachelock; From fcfc8fcccb90a9cd7429eeb7209bc447ea59879d Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 22 Feb 2014 23:45:13 +1100 Subject: [PATCH 21/36] Revert "Allow MinGW64 compilation in Windows/msys" This reverts commit c3f350e3fb1b7936ad1d78bc79c04054d5489dac. Conflicts: libs/platform/string.h --- .gitignore | 1 - apps/openmw/mwrender/videoplayer.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- components/files/configurationmanager.hpp | 2 +- extern/sdl4ogre/sdlwindowhelper.cpp | 4 ---- libs/platform/string.h | 2 +- 6 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index ca58fc006..3975c4521 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ Doxygen ## ides/editors *~ -*.bak *.kdev4 *.swp *.swo diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4209fb978..adf20dc63 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -385,7 +385,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder } void open(const std::string&) -#ifdef _MSC_VER +#ifdef _WIN32 { fail(std::string("Invalid call to ")+__FUNCSIG__); } #else { fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 21780fd5c..be6c0b338 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,5 +1,5 @@ #include "worldimp.hpp" -#if defined(_WIN32) && !defined(__MINGW32__) +#ifdef _WIN32 #include #elif defined HAVE_UNORDERED_MAP #include diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 9f31a7f2d..35144fe04 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -1,7 +1,7 @@ #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP -#if defined(_WIN32) && !defined(__MINGW32__) +#ifdef _WIN32 #include #elif defined HAVE_UNORDERED_MAP #include diff --git a/extern/sdl4ogre/sdlwindowhelper.cpp b/extern/sdl4ogre/sdlwindowhelper.cpp index 2a54a8636..f819043cf 100644 --- a/extern/sdl4ogre/sdlwindowhelper.cpp +++ b/extern/sdl4ogre/sdlwindowhelper.cpp @@ -31,11 +31,7 @@ SDLWindowHelper::SDLWindowHelper (SDL_Window* window, int w, int h, #ifdef WIN32 case SDL_SYSWM_WINDOWS: // Windows code -#ifdef __MINGW64__ - winHandle = Ogre::StringConverter::toString((DWORD_PTR)wmInfo.info.win.window); -#else winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window); -#endif /* __MINGW64__ */ break; #elif __MACOSX__ case SDL_SYSWM_COCOA: diff --git a/libs/platform/string.h b/libs/platform/string.h index be2e976b2..5368d757c 100644 --- a/libs/platform/string.h +++ b/libs/platform/string.h @@ -9,7 +9,7 @@ #include #if (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) || defined(__MINGW32__) // need our own implementation of strnlen -#if defined(__MINGW32__) && !defined(__MINGW64__) +#ifdef __MINGW32__ static size_t strnlen(const char *s, size_t n) { const char *p = (const char *)memchr(s, 0, n); From d780364842974a201876610eb58093fa659e9cc2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 22 Feb 2014 17:31:44 +0100 Subject: [PATCH 22/36] fixed record size type --- components/esm/esmwriter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 94f0a1004..33650e678 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -17,7 +17,7 @@ class ESMWriter { std::string name; std::streampos position; - size_t size; + uint32_t size; }; public: From 1ab5948f196618c76e839834d0c047f9a34941d2 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 23 Feb 2014 19:11:37 +1100 Subject: [PATCH 23/36] merge upstream changes --- apps/opencs/model/world/columnbase.hpp | 254 +-- apps/opencs/model/world/tablemimedata.cpp | 890 ++++----- apps/opencs/model/world/tablemimedata.hpp | 124 +- apps/opencs/view/filter/editwidget.cpp | 406 ++-- apps/opencs/view/filter/editwidget.hpp | 114 +- apps/opencs/view/filter/filterbox.cpp | 100 +- apps/opencs/view/filter/filterbox.hpp | 90 +- apps/opencs/view/filter/recordfilterbox.cpp | 64 +- apps/opencs/view/filter/recordfilterbox.hpp | 74 +- apps/opencs/view/world/table.cpp | 1090 +++++------ apps/opencs/view/world/table.hpp | 254 +-- apps/opencs/view/world/tablesubview.cpp | 262 +-- apps/opencs/view/world/tablesubview.hpp | 126 +- apps/openmw/mwstate/statemanagerimp.cpp | 704 +++---- components/esm/esmwriter.cpp | 406 ++-- components/esm/esmwriter.hpp | 228 +-- readme.txt | 1902 +++++++++---------- 17 files changed, 3544 insertions(+), 3544 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index e04333608..4ce45ffe8 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -1,127 +1,127 @@ -#ifndef CSM_WOLRD_COLUMNBASE_H -#define CSM_WOLRD_COLUMNBASE_H - -#include - -#include -#include - -#include "record.hpp" - -namespace CSMWorld -{ - struct ColumnBase - { - enum Roles - { - Role_Flags = Qt::UserRole, - Role_Display = Qt::UserRole+1 - }; - - enum Flags - { - Flag_Table = 1, // column should be displayed in table view - Flag_Dialogue = 2 // column should be displayed in dialogue view - }; - - enum Display - { - Display_None, //Do not use - Display_String, - - //CONCRETE TYPES STARTS HERE - Display_Skill, - Display_Class, - Display_Faction, - Display_Race, - Display_Sound, - Display_Region, - Display_Birthsign, - Display_Spell, - Display_Cell, - Display_Referenceable, - Display_Activator, - Display_Potion, - Display_Apparatus, - Display_Armor, - Display_Book, - Display_Clothing, - Display_Container, - Display_Creature, - Display_Door, - Display_Ingredient, - Display_CreatureLevelledList, - Display_ItemLevelledList, - Display_Light, - Display_Lockpick, - Display_Miscellaneous, - Display_Npc, - Display_Probe, - Display_Repair, - Display_Static, - Display_Weapon, - Display_Reference, - Display_Filter, - Display_Topic, - Display_Journal, - Display_TopicInfo, - Display_JournalInfo, - Display_Scene, - //CONCRETE TYPES ENDS HERE - - Display_Integer, - Display_Float, - Display_Var, - Display_GmstVarType, - Display_GlobalVarType, - Display_Specialisation, - Display_Attribute, - Display_Boolean, - Display_SpellType, - Display_Script, - Display_ApparatusType, - Display_ArmorType, - Display_ClothingType, - Display_CreatureType, - Display_WeaponType, - Display_RecordState, - Display_RefRecordType, - Display_DialogueType, - Display_QuestStatusType, - Display_Gender - }; - - int mColumnId; - int mFlags; - Display mDisplayType; - - ColumnBase (int columnId, Display displayType, int flag); - - virtual ~ColumnBase(); - - virtual bool isEditable() const = 0; - - virtual bool isUserEditable() const; - ///< Can this column be edited directly by the user? - - virtual std::string getTitle() const; - }; - - template - struct Column : public ColumnBase - { - int mFlags; - - Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (columnId, displayType, flags) {} - - virtual QVariant get (const Record& record) const = 0; - - virtual void set (Record& record, const QVariant& data) - { - throw std::logic_error ("Column " + getTitle() + " is not editable"); - } - }; -} - -#endif +#ifndef CSM_WOLRD_COLUMNBASE_H +#define CSM_WOLRD_COLUMNBASE_H + +#include + +#include +#include + +#include "record.hpp" + +namespace CSMWorld +{ + struct ColumnBase + { + enum Roles + { + Role_Flags = Qt::UserRole, + Role_Display = Qt::UserRole+1 + }; + + enum Flags + { + Flag_Table = 1, // column should be displayed in table view + Flag_Dialogue = 2 // column should be displayed in dialogue view + }; + + enum Display + { + Display_None, //Do not use + Display_String, + + //CONCRETE TYPES STARTS HERE + Display_Skill, + Display_Class, + Display_Faction, + Display_Race, + Display_Sound, + Display_Region, + Display_Birthsign, + Display_Spell, + Display_Cell, + Display_Referenceable, + Display_Activator, + Display_Potion, + Display_Apparatus, + Display_Armor, + Display_Book, + Display_Clothing, + Display_Container, + Display_Creature, + Display_Door, + Display_Ingredient, + Display_CreatureLevelledList, + Display_ItemLevelledList, + Display_Light, + Display_Lockpick, + Display_Miscellaneous, + Display_Npc, + Display_Probe, + Display_Repair, + Display_Static, + Display_Weapon, + Display_Reference, + Display_Filter, + Display_Topic, + Display_Journal, + Display_TopicInfo, + Display_JournalInfo, + Display_Scene, + //CONCRETE TYPES ENDS HERE + + Display_Integer, + Display_Float, + Display_Var, + Display_GmstVarType, + Display_GlobalVarType, + Display_Specialisation, + Display_Attribute, + Display_Boolean, + Display_SpellType, + Display_Script, + Display_ApparatusType, + Display_ArmorType, + Display_ClothingType, + Display_CreatureType, + Display_WeaponType, + Display_RecordState, + Display_RefRecordType, + Display_DialogueType, + Display_QuestStatusType, + Display_Gender + }; + + int mColumnId; + int mFlags; + Display mDisplayType; + + ColumnBase (int columnId, Display displayType, int flag); + + virtual ~ColumnBase(); + + virtual bool isEditable() const = 0; + + virtual bool isUserEditable() const; + ///< Can this column be edited directly by the user? + + virtual std::string getTitle() const; + }; + + template + struct Column : public ColumnBase + { + int mFlags; + + Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (columnId, displayType, flags) {} + + virtual QVariant get (const Record& record) const = 0; + + virtual void set (Record& record, const QVariant& data) + { + throw std::logic_error ("Column " + getTitle() + " is not editable"); + } + }; +} + +#endif diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index b56c9c8c2..f5e1acfb4 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1,446 +1,446 @@ -#include "tablemimedata.hpp" -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : -mDocument(document) -{ - mUniversalId.push_back (id); - mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); -} - -CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : - mUniversalId (id), mDocument(document) -{ - for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) - { - mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); - } -} - -QStringList CSMWorld::TableMimeData::formats() const -{ - return mObjectsFormats; -} - -CSMWorld::TableMimeData::~TableMimeData() -{ -} - -std::string CSMWorld::TableMimeData::getIcon() const -{ - if (mUniversalId.empty()) - { - throw ("TableMimeData holds no UniversalId"); - } - - std::string tmpIcon; - bool firstIteration = true; - - for (unsigned i = 0; i < mUniversalId.size(); ++i) - { - if (firstIteration) - { - firstIteration = false; - tmpIcon = mUniversalId[i].getIcon(); - continue; - } - - if (tmpIcon != mUniversalId[i].getIcon()) - { - return ":/multitype.png"; //icon stolen from gnome - } - - tmpIcon = mUniversalId[i].getIcon(); - } - - return mUniversalId.begin()->getIcon(); //All objects are of the same type; -} - -std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const -{ - return mUniversalId; -} - - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return true; - } - } - - return false; -} - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return true; - } - } - - return false; -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const -{ - return &document == &mDocument; -} - -CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) -{ - switch (type) - { - case CSMWorld::ColumnBase::Display_Race: - return CSMWorld::UniversalId::Type_Race; - - - case CSMWorld::ColumnBase::Display_Skill: - return CSMWorld::UniversalId::Type_Skill; - - - case CSMWorld::ColumnBase::Display_Class: - return CSMWorld::UniversalId::Type_Class; - - - case CSMWorld::ColumnBase::Display_Faction: - return CSMWorld::UniversalId::Type_Faction; - - - case CSMWorld::ColumnBase::Display_Sound: - return CSMWorld::UniversalId::Type_Sound; - - - case CSMWorld::ColumnBase::Display_Region: - return CSMWorld::UniversalId::Type_Region; - - - case CSMWorld::ColumnBase::Display_Birthsign: - return CSMWorld::UniversalId::Type_Birthsign; - - - case CSMWorld::ColumnBase::Display_Spell: - return CSMWorld::UniversalId::Type_Spell; - - - case CSMWorld::ColumnBase::Display_Cell: - return CSMWorld::UniversalId::Type_Cell; - - - case CSMWorld::ColumnBase::Display_Referenceable: - return CSMWorld::UniversalId::Type_Referenceable; - - - case CSMWorld::ColumnBase::Display_Activator: - return CSMWorld::UniversalId::Type_Activator; - - - case CSMWorld::ColumnBase::Display_Potion: - return CSMWorld::UniversalId::Type_Potion; - - - case CSMWorld::ColumnBase::Display_Apparatus: - return CSMWorld::UniversalId::Type_Apparatus; - - - case CSMWorld::ColumnBase::Display_Armor: - return CSMWorld::UniversalId::Type_Armor; - - - case CSMWorld::ColumnBase::Display_Book: - return CSMWorld::UniversalId::Type_Book; - - - case CSMWorld::ColumnBase::Display_Clothing: - return CSMWorld::UniversalId::Type_Clothing; - - - case CSMWorld::ColumnBase::Display_Container: - return CSMWorld::UniversalId::Type_Container; - - - case CSMWorld::ColumnBase::Display_Creature: - return CSMWorld::UniversalId::Type_Creature; - - - case CSMWorld::ColumnBase::Display_Door: - return CSMWorld::UniversalId::Type_Door; - - - case CSMWorld::ColumnBase::Display_Ingredient: - return CSMWorld::UniversalId::Type_Ingredient; - - - case CSMWorld::ColumnBase::Display_CreatureLevelledList: - return CSMWorld::UniversalId::Type_CreatureLevelledList; - - - case CSMWorld::ColumnBase::Display_ItemLevelledList: - return CSMWorld::UniversalId::Type_ItemLevelledList; - - - case CSMWorld::ColumnBase::Display_Light: - return CSMWorld::UniversalId::Type_Light; - - - case CSMWorld::ColumnBase::Display_Lockpick: - return CSMWorld::UniversalId::Type_Lockpick; - - - case CSMWorld::ColumnBase::Display_Miscellaneous: - return CSMWorld::UniversalId::Type_Miscellaneous; - - - case CSMWorld::ColumnBase::Display_Npc: - return CSMWorld::UniversalId::Type_Npc; - - - case CSMWorld::ColumnBase::Display_Probe: - return CSMWorld::UniversalId::Type_Probe; - - - case CSMWorld::ColumnBase::Display_Repair: - return CSMWorld::UniversalId::Type_Repair; - - - case CSMWorld::ColumnBase::Display_Static: - return CSMWorld::UniversalId::Type_Static; - - - case CSMWorld::ColumnBase::Display_Weapon: - return CSMWorld::UniversalId::Type_Weapon; - - - case CSMWorld::ColumnBase::Display_Reference: - return CSMWorld::UniversalId::Type_Reference; - - - case CSMWorld::ColumnBase::Display_Filter: - return CSMWorld::UniversalId::Type_Filter; - - - case CSMWorld::ColumnBase::Display_Topic: - return CSMWorld::UniversalId::Type_Topic; - - - case CSMWorld::ColumnBase::Display_Journal: - return CSMWorld::UniversalId::Type_Journal; - - - case CSMWorld::ColumnBase::Display_TopicInfo: - return CSMWorld::UniversalId::Type_TopicInfo; - - - case CSMWorld::ColumnBase::Display_JournalInfo: - return CSMWorld::UniversalId::Type_JournalInfo; - - - case CSMWorld::ColumnBase::Display_Scene: - return CSMWorld::UniversalId::Type_Scene; - - - case CSMWorld::ColumnBase::Display_Script: - return CSMWorld::UniversalId::Type_Script; - - - default: - return CSMWorld::UniversalId::Type_None; - - } -} - -CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) -{ - switch (type) - { - case CSMWorld::UniversalId::Type_Race: - return CSMWorld::ColumnBase::Display_Race; - - - case CSMWorld::UniversalId::Type_Skill: - return CSMWorld::ColumnBase::Display_Skill; - - - case CSMWorld::UniversalId::Type_Class: - return CSMWorld::ColumnBase::Display_Class; - - - case CSMWorld::UniversalId::Type_Faction: - return CSMWorld::ColumnBase::Display_Faction; - - - case CSMWorld::UniversalId::Type_Sound: - return CSMWorld::ColumnBase::Display_Sound; - - - case CSMWorld::UniversalId::Type_Region: - return CSMWorld::ColumnBase::Display_Region; - - - case CSMWorld::UniversalId::Type_Birthsign: - return CSMWorld::ColumnBase::Display_Birthsign; - - - case CSMWorld::UniversalId::Type_Spell: - return CSMWorld::ColumnBase::Display_Spell; - - - case CSMWorld::UniversalId::Type_Cell: - return CSMWorld::ColumnBase::Display_Cell; - - - case CSMWorld::UniversalId::Type_Referenceable: - return CSMWorld::ColumnBase::Display_Referenceable; - - - case CSMWorld::UniversalId::Type_Activator: - return CSMWorld::ColumnBase::Display_Activator; - - - case CSMWorld::UniversalId::Type_Potion: - return CSMWorld::ColumnBase::Display_Potion; - - - case CSMWorld::UniversalId::Type_Apparatus: - return CSMWorld::ColumnBase::Display_Apparatus; - - - case CSMWorld::UniversalId::Type_Armor: - return CSMWorld::ColumnBase::Display_Armor; - - - case CSMWorld::UniversalId::Type_Book: - return CSMWorld::ColumnBase::Display_Book; - - - case CSMWorld::UniversalId::Type_Clothing: - return CSMWorld::ColumnBase::Display_Clothing; - - - case CSMWorld::UniversalId::Type_Container: - return CSMWorld::ColumnBase::Display_Container; - - - case CSMWorld::UniversalId::Type_Creature: - return CSMWorld::ColumnBase::Display_Creature; - - - case CSMWorld::UniversalId::Type_Door: - return CSMWorld::ColumnBase::Display_Door; - - - case CSMWorld::UniversalId::Type_Ingredient: - return CSMWorld::ColumnBase::Display_Ingredient; - - - case CSMWorld::UniversalId::Type_CreatureLevelledList: - return CSMWorld::ColumnBase::Display_CreatureLevelledList; - - - case CSMWorld::UniversalId::Type_ItemLevelledList: - return CSMWorld::ColumnBase::Display_ItemLevelledList; - - - case CSMWorld::UniversalId::Type_Light: - return CSMWorld::ColumnBase::Display_Light; - - - case CSMWorld::UniversalId::Type_Lockpick: - return CSMWorld::ColumnBase::Display_Lockpick; - - - case CSMWorld::UniversalId::Type_Miscellaneous: - return CSMWorld::ColumnBase::Display_Miscellaneous; - - - case CSMWorld::UniversalId::Type_Npc: - return CSMWorld::ColumnBase::Display_Npc; - - - case CSMWorld::UniversalId::Type_Probe: - return CSMWorld::ColumnBase::Display_Probe; - - - case CSMWorld::UniversalId::Type_Repair: - return CSMWorld::ColumnBase::Display_Repair; - - - case CSMWorld::UniversalId::Type_Static: - return CSMWorld::ColumnBase::Display_Static; - - - case CSMWorld::UniversalId::Type_Weapon: - return CSMWorld::ColumnBase::Display_Weapon; - - - case CSMWorld::UniversalId::Type_Reference: - return CSMWorld::ColumnBase::Display_Reference; - - - case CSMWorld::UniversalId::Type_Filter: - return CSMWorld::ColumnBase::Display_Filter; - - - case CSMWorld::UniversalId::Type_Topic: - return CSMWorld::ColumnBase::Display_Topic; - - - case CSMWorld::UniversalId::Type_Journal: - return CSMWorld::ColumnBase::Display_Journal; - - - case CSMWorld::UniversalId::Type_TopicInfo: - return CSMWorld::ColumnBase::Display_TopicInfo; - - - case CSMWorld::UniversalId::Type_JournalInfo: - return CSMWorld::ColumnBase::Display_JournalInfo; - - - case CSMWorld::UniversalId::Type_Scene: - return CSMWorld::ColumnBase::Display_Scene; - - - case CSMWorld::UniversalId::Type_Script: - return CSMWorld::ColumnBase::Display_Script; - - - default: - return CSMWorld::ColumnBase::Display_None; - } +#include "tablemimedata.hpp" +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : +mDocument(document) +{ + mUniversalId.push_back (id); + mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); +} + +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : + mUniversalId (id), mDocument(document) +{ + for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) + { + mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); + } +} + +QStringList CSMWorld::TableMimeData::formats() const +{ + return mObjectsFormats; +} + +CSMWorld::TableMimeData::~TableMimeData() +{ +} + +std::string CSMWorld::TableMimeData::getIcon() const +{ + if (mUniversalId.empty()) + { + throw ("TableMimeData holds no UniversalId"); + } + + std::string tmpIcon; + bool firstIteration = true; + + for (unsigned i = 0; i < mUniversalId.size(); ++i) + { + if (firstIteration) + { + firstIteration = false; + tmpIcon = mUniversalId[i].getIcon(); + continue; + } + + if (tmpIcon != mUniversalId[i].getIcon()) + { + return ":/multitype.png"; //icon stolen from gnome + } + + tmpIcon = mUniversalId[i].getIcon(); + } + + return mUniversalId.begin()->getIcon(); //All objects are of the same type; +} + +std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const +{ + return mUniversalId; +} + + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return true; + } + } + + return false; +} + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return true; + } + } + + return false; +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const +{ + return &document == &mDocument; +} + +CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) +{ + switch (type) + { + case CSMWorld::ColumnBase::Display_Race: + return CSMWorld::UniversalId::Type_Race; + + + case CSMWorld::ColumnBase::Display_Skill: + return CSMWorld::UniversalId::Type_Skill; + + + case CSMWorld::ColumnBase::Display_Class: + return CSMWorld::UniversalId::Type_Class; + + + case CSMWorld::ColumnBase::Display_Faction: + return CSMWorld::UniversalId::Type_Faction; + + + case CSMWorld::ColumnBase::Display_Sound: + return CSMWorld::UniversalId::Type_Sound; + + + case CSMWorld::ColumnBase::Display_Region: + return CSMWorld::UniversalId::Type_Region; + + + case CSMWorld::ColumnBase::Display_Birthsign: + return CSMWorld::UniversalId::Type_Birthsign; + + + case CSMWorld::ColumnBase::Display_Spell: + return CSMWorld::UniversalId::Type_Spell; + + + case CSMWorld::ColumnBase::Display_Cell: + return CSMWorld::UniversalId::Type_Cell; + + + case CSMWorld::ColumnBase::Display_Referenceable: + return CSMWorld::UniversalId::Type_Referenceable; + + + case CSMWorld::ColumnBase::Display_Activator: + return CSMWorld::UniversalId::Type_Activator; + + + case CSMWorld::ColumnBase::Display_Potion: + return CSMWorld::UniversalId::Type_Potion; + + + case CSMWorld::ColumnBase::Display_Apparatus: + return CSMWorld::UniversalId::Type_Apparatus; + + + case CSMWorld::ColumnBase::Display_Armor: + return CSMWorld::UniversalId::Type_Armor; + + + case CSMWorld::ColumnBase::Display_Book: + return CSMWorld::UniversalId::Type_Book; + + + case CSMWorld::ColumnBase::Display_Clothing: + return CSMWorld::UniversalId::Type_Clothing; + + + case CSMWorld::ColumnBase::Display_Container: + return CSMWorld::UniversalId::Type_Container; + + + case CSMWorld::ColumnBase::Display_Creature: + return CSMWorld::UniversalId::Type_Creature; + + + case CSMWorld::ColumnBase::Display_Door: + return CSMWorld::UniversalId::Type_Door; + + + case CSMWorld::ColumnBase::Display_Ingredient: + return CSMWorld::UniversalId::Type_Ingredient; + + + case CSMWorld::ColumnBase::Display_CreatureLevelledList: + return CSMWorld::UniversalId::Type_CreatureLevelledList; + + + case CSMWorld::ColumnBase::Display_ItemLevelledList: + return CSMWorld::UniversalId::Type_ItemLevelledList; + + + case CSMWorld::ColumnBase::Display_Light: + return CSMWorld::UniversalId::Type_Light; + + + case CSMWorld::ColumnBase::Display_Lockpick: + return CSMWorld::UniversalId::Type_Lockpick; + + + case CSMWorld::ColumnBase::Display_Miscellaneous: + return CSMWorld::UniversalId::Type_Miscellaneous; + + + case CSMWorld::ColumnBase::Display_Npc: + return CSMWorld::UniversalId::Type_Npc; + + + case CSMWorld::ColumnBase::Display_Probe: + return CSMWorld::UniversalId::Type_Probe; + + + case CSMWorld::ColumnBase::Display_Repair: + return CSMWorld::UniversalId::Type_Repair; + + + case CSMWorld::ColumnBase::Display_Static: + return CSMWorld::UniversalId::Type_Static; + + + case CSMWorld::ColumnBase::Display_Weapon: + return CSMWorld::UniversalId::Type_Weapon; + + + case CSMWorld::ColumnBase::Display_Reference: + return CSMWorld::UniversalId::Type_Reference; + + + case CSMWorld::ColumnBase::Display_Filter: + return CSMWorld::UniversalId::Type_Filter; + + + case CSMWorld::ColumnBase::Display_Topic: + return CSMWorld::UniversalId::Type_Topic; + + + case CSMWorld::ColumnBase::Display_Journal: + return CSMWorld::UniversalId::Type_Journal; + + + case CSMWorld::ColumnBase::Display_TopicInfo: + return CSMWorld::UniversalId::Type_TopicInfo; + + + case CSMWorld::ColumnBase::Display_JournalInfo: + return CSMWorld::UniversalId::Type_JournalInfo; + + + case CSMWorld::ColumnBase::Display_Scene: + return CSMWorld::UniversalId::Type_Scene; + + + case CSMWorld::ColumnBase::Display_Script: + return CSMWorld::UniversalId::Type_Script; + + + default: + return CSMWorld::UniversalId::Type_None; + + } +} + +CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) +{ + switch (type) + { + case CSMWorld::UniversalId::Type_Race: + return CSMWorld::ColumnBase::Display_Race; + + + case CSMWorld::UniversalId::Type_Skill: + return CSMWorld::ColumnBase::Display_Skill; + + + case CSMWorld::UniversalId::Type_Class: + return CSMWorld::ColumnBase::Display_Class; + + + case CSMWorld::UniversalId::Type_Faction: + return CSMWorld::ColumnBase::Display_Faction; + + + case CSMWorld::UniversalId::Type_Sound: + return CSMWorld::ColumnBase::Display_Sound; + + + case CSMWorld::UniversalId::Type_Region: + return CSMWorld::ColumnBase::Display_Region; + + + case CSMWorld::UniversalId::Type_Birthsign: + return CSMWorld::ColumnBase::Display_Birthsign; + + + case CSMWorld::UniversalId::Type_Spell: + return CSMWorld::ColumnBase::Display_Spell; + + + case CSMWorld::UniversalId::Type_Cell: + return CSMWorld::ColumnBase::Display_Cell; + + + case CSMWorld::UniversalId::Type_Referenceable: + return CSMWorld::ColumnBase::Display_Referenceable; + + + case CSMWorld::UniversalId::Type_Activator: + return CSMWorld::ColumnBase::Display_Activator; + + + case CSMWorld::UniversalId::Type_Potion: + return CSMWorld::ColumnBase::Display_Potion; + + + case CSMWorld::UniversalId::Type_Apparatus: + return CSMWorld::ColumnBase::Display_Apparatus; + + + case CSMWorld::UniversalId::Type_Armor: + return CSMWorld::ColumnBase::Display_Armor; + + + case CSMWorld::UniversalId::Type_Book: + return CSMWorld::ColumnBase::Display_Book; + + + case CSMWorld::UniversalId::Type_Clothing: + return CSMWorld::ColumnBase::Display_Clothing; + + + case CSMWorld::UniversalId::Type_Container: + return CSMWorld::ColumnBase::Display_Container; + + + case CSMWorld::UniversalId::Type_Creature: + return CSMWorld::ColumnBase::Display_Creature; + + + case CSMWorld::UniversalId::Type_Door: + return CSMWorld::ColumnBase::Display_Door; + + + case CSMWorld::UniversalId::Type_Ingredient: + return CSMWorld::ColumnBase::Display_Ingredient; + + + case CSMWorld::UniversalId::Type_CreatureLevelledList: + return CSMWorld::ColumnBase::Display_CreatureLevelledList; + + + case CSMWorld::UniversalId::Type_ItemLevelledList: + return CSMWorld::ColumnBase::Display_ItemLevelledList; + + + case CSMWorld::UniversalId::Type_Light: + return CSMWorld::ColumnBase::Display_Light; + + + case CSMWorld::UniversalId::Type_Lockpick: + return CSMWorld::ColumnBase::Display_Lockpick; + + + case CSMWorld::UniversalId::Type_Miscellaneous: + return CSMWorld::ColumnBase::Display_Miscellaneous; + + + case CSMWorld::UniversalId::Type_Npc: + return CSMWorld::ColumnBase::Display_Npc; + + + case CSMWorld::UniversalId::Type_Probe: + return CSMWorld::ColumnBase::Display_Probe; + + + case CSMWorld::UniversalId::Type_Repair: + return CSMWorld::ColumnBase::Display_Repair; + + + case CSMWorld::UniversalId::Type_Static: + return CSMWorld::ColumnBase::Display_Static; + + + case CSMWorld::UniversalId::Type_Weapon: + return CSMWorld::ColumnBase::Display_Weapon; + + + case CSMWorld::UniversalId::Type_Reference: + return CSMWorld::ColumnBase::Display_Reference; + + + case CSMWorld::UniversalId::Type_Filter: + return CSMWorld::ColumnBase::Display_Filter; + + + case CSMWorld::UniversalId::Type_Topic: + return CSMWorld::ColumnBase::Display_Topic; + + + case CSMWorld::UniversalId::Type_Journal: + return CSMWorld::ColumnBase::Display_Journal; + + + case CSMWorld::UniversalId::Type_TopicInfo: + return CSMWorld::ColumnBase::Display_TopicInfo; + + + case CSMWorld::UniversalId::Type_JournalInfo: + return CSMWorld::ColumnBase::Display_JournalInfo; + + + case CSMWorld::UniversalId::Type_Scene: + return CSMWorld::ColumnBase::Display_Scene; + + + case CSMWorld::UniversalId::Type_Script: + return CSMWorld::ColumnBase::Display_Script; + + + default: + return CSMWorld::ColumnBase::Display_None; + } } \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 7687f3555..0b0143abd 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -1,63 +1,63 @@ - -#ifndef TABLEMIMEDATA_H -#define TABLEMIMEDATA_H - -#include - -#include -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -namespace CSMDoc -{ - class Document; -} - -namespace CSMWorld -{ - -/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. -/// -/// This class provides way to construct mimedata object holding the universalid copy -/// Universalid is used in the majority of the tables to store type, id, argument types. -/// This way universalid grants a way to retrive record from the concrete table. -/// Please note, that tablemimedata object can hold multiple universalIds in the vector. - - class TableMimeData : public QMimeData - { - public: - TableMimeData(UniversalId id, const CSMDoc::Document& document); - - TableMimeData(std::vector& id, const CSMDoc::Document& document); - - ~TableMimeData(); - - virtual QStringList formats() const; - - std::string getIcon() const; - - std::vector getData() const; - - bool holdsType(UniversalId::Type type) const; - - bool holdsType(CSMWorld::ColumnBase::Display type) const; - - bool fromDocument(const CSMDoc::Document& document) const; - - UniversalId returnMatching(UniversalId::Type type) const; - - UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; - - static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); - static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); - - private: - std::vector mUniversalId; - QStringList mObjectsFormats; - const CSMDoc::Document& mDocument; - - }; -} + +#ifndef TABLEMIMEDATA_H +#define TABLEMIMEDATA_H + +#include + +#include +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + +/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. +/// +/// This class provides way to construct mimedata object holding the universalid copy +/// Universalid is used in the majority of the tables to store type, id, argument types. +/// This way universalid grants a way to retrive record from the concrete table. +/// Please note, that tablemimedata object can hold multiple universalIds in the vector. + + class TableMimeData : public QMimeData + { + public: + TableMimeData(UniversalId id, const CSMDoc::Document& document); + + TableMimeData(std::vector& id, const CSMDoc::Document& document); + + ~TableMimeData(); + + virtual QStringList formats() const; + + std::string getIcon() const; + + std::vector getData() const; + + bool holdsType(UniversalId::Type type) const; + + bool holdsType(CSMWorld::ColumnBase::Display type) const; + + bool fromDocument(const CSMDoc::Document& document) const; + + UniversalId returnMatching(UniversalId::Type type) const; + + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; + + static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); + static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); + + private: + std::vector mUniversalId; + QStringList mObjectsFormats; + const CSMDoc::Document& mDocument; + + }; +} #endif // TABLEMIMEDATA_H \ No newline at end of file diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index cc1578bdd..841919a9e 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -1,203 +1,203 @@ - -#include "editwidget.hpp" - -#include -#include -#include - -#include "../../model/world/data.hpp" - -CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) -: QLineEdit (parent), mParser (data) -{ - mPalette = palette(); - connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); - - QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); - - connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), - this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), - this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), - Qt::QueuedConnection); -} - -void CSVFilter::EditWidget::textChanged (const QString& text) -{ - if (mParser.parse (text.toUtf8().constData())) - { - setPalette (mPalette); - emit filterChanged (mParser.getFilter()); - } - else - { - QPalette palette (mPalette); - palette.setColor (QPalette::Text, Qt::red); - setPalette (palette); - - /// \todo improve error reporting; mark only the faulty part - } -} - -void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, - const QModelIndex& bottomRight) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, - Qt::DropAction action) -{ - const unsigned count = filterSource.size(); - bool multipleElements = false; - - switch (count) //setting multipleElements; - { - case 0: //empty - return; //nothing to do here - - case 1: //only single - multipleElements = false; - break; - - default: - multipleElements = true; - break; - } - - Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); - QString oldContent (text()); - - bool replaceMode = false; - std::string orAnd; - - switch (key) //setting replaceMode and string used to glue expressions - { - case Qt::ShiftModifier: - orAnd = "!or("; - replaceMode = false; - break; - - case Qt::ControlModifier: - orAnd = "!and("; - replaceMode = false; - break; - - default: - replaceMode = true; - break; - } - - if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode - { - replaceMode = true; - } - - if (!replaceMode) - { - oldContent.remove ('!'); - } - - std::stringstream ss; - - if (multipleElements) - { - if (replaceMode) - { - ss<<"!or("; - } else { - ss << orAnd << oldContent.toStdString() << ','; - } - - for (unsigned i = 0; i < count; ++i) - { - ss<4) - { - clear(); - insert (QString::fromUtf8(ss.str().c_str())); - } -} - -std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const -{ - const unsigned columns = seekedString.second.size(); - - bool multipleColumns = false; - switch (columns) - { - case 0: //empty - return ""; //no column to filter - - case 1: //one column to look for - multipleColumns = false; - break; - - default: - multipleColumns = true; - break; - } - - std::stringstream ss; - if (multipleColumns) - { - ss<<"or("; - for (unsigned i = 0; i < columns; ++i) - { - ss<<"string("<<'"'< +#include +#include + +#include "../../model/world/data.hpp" + +CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) +: QLineEdit (parent), mParser (data) +{ + mPalette = palette(); + connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); + + QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); + + connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), + this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), + this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), + Qt::QueuedConnection); +} + +void CSVFilter::EditWidget::textChanged (const QString& text) +{ + if (mParser.parse (text.toUtf8().constData())) + { + setPalette (mPalette); + emit filterChanged (mParser.getFilter()); + } + else + { + QPalette palette (mPalette); + palette.setColor (QPalette::Text, Qt::red); + setPalette (palette); + + /// \todo improve error reporting; mark only the faulty part + } +} + +void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, + Qt::DropAction action) +{ + const unsigned count = filterSource.size(); + bool multipleElements = false; + + switch (count) //setting multipleElements; + { + case 0: //empty + return; //nothing to do here + + case 1: //only single + multipleElements = false; + break; + + default: + multipleElements = true; + break; + } + + Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); + QString oldContent (text()); + + bool replaceMode = false; + std::string orAnd; + + switch (key) //setting replaceMode and string used to glue expressions + { + case Qt::ShiftModifier: + orAnd = "!or("; + replaceMode = false; + break; + + case Qt::ControlModifier: + orAnd = "!and("; + replaceMode = false; + break; + + default: + replaceMode = true; + break; + } + + if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode + { + replaceMode = true; + } + + if (!replaceMode) + { + oldContent.remove ('!'); + } + + std::stringstream ss; + + if (multipleElements) + { + if (replaceMode) + { + ss<<"!or("; + } else { + ss << orAnd << oldContent.toStdString() << ','; + } + + for (unsigned i = 0; i < count; ++i) + { + ss<4) + { + clear(); + insert (QString::fromUtf8(ss.str().c_str())); + } +} + +std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const +{ + const unsigned columns = seekedString.second.size(); + + bool multipleColumns = false; + switch (columns) + { + case 0: //empty + return ""; //no column to filter + + case 1: //one column to look for + multipleColumns = false; + break; + + default: + multipleColumns = true; + break; + } + + std::stringstream ss; + if (multipleColumns) + { + ss<<"or("; + for (unsigned i = 0; i < columns; ++i) + { + ss<<"string("<<'"'< - -#include -#include -#include - -#include "../../model/filter/parser.hpp" -#include "../../model/filter/node.hpp" - -class QModelIndex; - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class EditWidget : public QLineEdit - { - Q_OBJECT - - CSMFilter::Parser mParser; - QPalette mPalette; - - public: - - EditWidget (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - - private: - std::string generateFilter(std::pair >& seekedString) const; - - private slots: - - void textChanged (const QString& text); - - void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void filterRowsRemoved (const QModelIndex& parent, int start, int end); - - void filterRowsInserted (const QModelIndex& parent, int start, int end); - - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - - void useFilterRequest(const std::string& idOfFilter); - }; -} - -#endif +#ifndef CSV_FILTER_EDITWIDGET_H +#define CSV_FILTER_EDITWIDGET_H + +#include + +#include +#include +#include + +#include "../../model/filter/parser.hpp" +#include "../../model/filter/node.hpp" + +class QModelIndex; + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class EditWidget : public QLineEdit + { + Q_OBJECT + + CSMFilter::Parser mParser; + QPalette mPalette; + + public: + + EditWidget (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + + private: + std::string generateFilter(std::pair >& seekedString) const; + + private slots: + + void textChanged (const QString& text); + + void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void filterRowsRemoved (const QModelIndex& parent, int start, int end); + + void filterRowsInserted (const QModelIndex& parent, int start, int end); + + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + + void useFilterRequest(const std::string& idOfFilter); + }; +} + +#endif diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index a33288025..4954efefb 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -1,50 +1,50 @@ - -#include "filterbox.hpp" - -#include -#include - -#include "recordfilterbox.hpp" - -#include - -CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); - - layout->addWidget (recordFilterBox); - - setLayout (layout); - - connect (recordFilterBox, - SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (recordFilterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); - setAcceptDrops(true); -} - -void CSVFilter::FilterBox::dropEvent (QDropEvent* event) -{ - std::vector data = dynamic_cast (event->mimeData())->getData(); - - emit recordDropped(data, event->proposedAction()); -} - -void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) -{ - event->acceptProposedAction(); -} - -void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) -{ - event->accept(); -} + +#include "filterbox.hpp" + +#include +#include + +#include "recordfilterbox.hpp" + +#include + +CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); + + layout->addWidget (recordFilterBox); + + setLayout (layout); + + connect (recordFilterBox, + SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (recordFilterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); + setAcceptDrops(true); +} + +void CSVFilter::FilterBox::dropEvent (QDropEvent* event) +{ + std::vector data = dynamic_cast (event->mimeData())->getData(); + + emit recordDropped(data, event->proposedAction()); +} + +void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) +{ + event->accept(); +} diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 3817d5e70..263e89e73 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -1,45 +1,45 @@ -#ifndef CSV_FILTER_FILTERBOX_H -#define CSV_FILTER_FILTERBOX_H - -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/universalid.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class FilterBox : public QWidget - { - Q_OBJECT - - void dragEnterEvent (QDragEnterEvent* event); - - void dropEvent (QDropEvent* event); - - void dragMoveEvent(QDragMoveEvent *event); - - public: - - FilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void recordFilterChanged (boost::shared_ptr filter); - void recordDropped (std::vector& types, Qt::DropAction action); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - -#endif - +#ifndef CSV_FILTER_FILTERBOX_H +#define CSV_FILTER_FILTERBOX_H + +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/universalid.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class FilterBox : public QWidget + { + Q_OBJECT + + void dragEnterEvent (QDragEnterEvent* event); + + void dropEvent (QDropEvent* event); + + void dragMoveEvent(QDragMoveEvent *event); + + public: + + FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void recordFilterChanged (boost::shared_ptr filter); + void recordDropped (std::vector& types, Qt::DropAction action); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + +#endif + diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index 2a1a1407f..59c083bd8 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -1,32 +1,32 @@ - -#include "recordfilterbox.hpp" - -#include -#include - -#include "editwidget.hpp" - -CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - layout->addWidget (new QLabel ("Record Filter", this)); - - EditWidget *editWidget = new EditWidget (data, this); - - layout->addWidget (editWidget); - - setLayout (layout); - - connect ( - editWidget, SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (filterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); -} + +#include "recordfilterbox.hpp" + +#include +#include + +#include "editwidget.hpp" + +CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + layout->addWidget (new QLabel ("Record Filter", this)); + + EditWidget *editWidget = new EditWidget (data, this); + + layout->addWidget (editWidget); + + setLayout (layout); + + connect ( + editWidget, SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (filterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); +} diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index 3638dc6c3..fd384a32b 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -1,38 +1,38 @@ -#ifndef CSV_FILTER_RECORDFILTERBOX_H -#define CSV_FILTER_RECORDFILTERBOX_H - -#include - -#include -#include - -#include - -#include "../../model/filter/node.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class RecordFilterBox : public QWidget - { - Q_OBJECT - - public: - - RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - +#ifndef CSV_FILTER_RECORDFILTERBOX_H +#define CSV_FILTER_RECORDFILTERBOX_H + +#include + +#include +#include + +#include + +#include "../../model/filter/node.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class RecordFilterBox : public QWidget + { + Q_OBJECT + + public: + + RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + #endif \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index edf3bc6de..bc67c68b5 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -1,546 +1,546 @@ - -#include "table.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -#include "../../model/world/data.hpp" -#include "../../model/world/commands.hpp" -#include "../../model/world/idtableproxymodel.hpp" -#include "../../model/world/idtable.hpp" -#include "../../model/world/record.hpp" -#include "../../model/world/columns.hpp" -#include "../../model/world/tablemimedata.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "recordstatusdelegate.hpp" -#include "util.hpp" - -void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - QMenu menu (this); - - /// \todo add menu items for select all and clear selection - - if (!mEditLock) - { - if (selectedRows.size()==1) - { - menu.addAction (mEditAction); - if (mCreateAction) - menu.addAction(mCloneAction); - } - - if (mCreateAction) - menu.addAction (mCreateAction); - - /// \todo Reverting temporarily disabled on tables that support reordering, because - /// revert logic currently can not handle reordering. - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) - if (listRevertableSelectedIds().size()>0) - menu.addAction (mRevertAction); - - if (listDeletableSelectedIds().size()>0) - menu.addAction (mDeleteAction); - - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) - { - /// \todo allow reordering of multiple rows - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); - - if (column==-1) - column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); - - if (column!=-1) - { - if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row-1, column))) - { - menu.addAction (mMoveUpAction); - } - - if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row+1, column))) - { - menu.addAction (mMoveDownAction); - } - } - } - } - } - - menu.exec (event->globalPos()); -} - -std::vector CSVWorld::Table::listRevertableSelectedIds() const -{ - std::vector revertableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_BaseOnly) - { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - revertableIds.push_back (id); - } - } - } - - return revertableIds; -} - -std::vector CSVWorld::Table::listDeletableSelectedIds() const -{ - std::vector deletableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - // check record state - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state==CSMWorld::RecordBase::State_Deleted) - continue; - - // check other columns (only relevant for a subset of the tables) - int dialogueTypeIndex = - mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); - - if (dialogueTypeIndex!=-1) - { - int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); - - if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) - continue; - } - - // add the id to the collection - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - deletableIds.push_back (id); - } - } - - return deletableIds; -} - -CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting, const CSMDoc::Document& document) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) -{ - mModel = &dynamic_cast (*data.getTableModel (id)); - - mProxyModel = new CSMWorld::IdTableProxyModel (this); - mProxyModel->setSourceModel (mModel); - - setModel (mProxyModel); - horizontalHeader()->setResizeMode (QHeaderView::Interactive); - verticalHeader()->hide(); - setSortingEnabled (sorting); - setSelectionBehavior (QAbstractItemView::SelectRows); - setSelectionMode (QAbstractItemView::ExtendedSelection); - - int columns = mModel->columnCount(); - - for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); - - if (flags & CSMWorld::ColumnBase::Flag_Table) - { - CSMWorld::ColumnBase::Display display = static_cast ( - mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - undoStack, this); - - mDelegates.push_back (delegate); - setItemDelegateForColumn (i, delegate); - } - else - hideColumn (i); - } - - mEditAction = new QAction (tr ("Edit Record"), this); - connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); - addAction (mEditAction); - - if (createAndDelete) - { - mCreateAction = new QAction (tr ("Add Record"), this); - connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); - addAction (mCreateAction); - - mCloneAction = new QAction (tr ("Clone Record"), this); - connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); - addAction(mCloneAction); - } - - mRevertAction = new QAction (tr ("Revert Record"), this); - connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); - addAction (mRevertAction); - - mDeleteAction = new QAction (tr ("Delete Record"), this); - connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); - addAction (mDeleteAction); - - mMoveUpAction = new QAction (tr ("Move Up"), this); - connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); - addAction (mMoveUpAction); - - mMoveDownAction = new QAction (tr ("Move Down"), this); - connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); - addAction (mMoveDownAction); - - connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (tableSizeUpdate())); - - /// \note This signal could instead be connected to a slot that filters out changes not affecting - /// the records status column (for permanence reasons) - connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (tableSizeUpdate())); - - connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), - this, SLOT (selectionSizeUpdate ())); - - setAcceptDrops(true); -} - -void CSVWorld::Table::setEditLock (bool locked) -{ - for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) - (*iter)->setEditLock (locked); - - mEditLock = locked; -} - -CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const -{ - return CSMWorld::UniversalId ( - static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), - mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); -} - -void CSVWorld::Table::revertRecord() -{ - if (!mEditLock) - { - std::vector revertableIds = listRevertableSelectedIds(); - - if (revertableIds.size()>0) - { - if (revertableIds.size()>1) - mUndoStack.beginMacro (tr ("Revert multiple records")); - - for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); - - if (revertableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::deleteRecord() -{ - if (!mEditLock) - { - std::vector deletableIds = listDeletableSelectedIds(); - - if (deletableIds.size()>0) - { - if (deletableIds.size()>1) - mUndoStack.beginMacro (tr ("Delete multiple records")); - - for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); - - if (deletableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::editRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - emit editRequest (selectedRows.begin()->row()); - } -} - -void CSVWorld::Table::cloneRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); - if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) - { - emit cloneRequest (toClone); - } - } -} - -void CSVWorld::Table::moveUpRecord() -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - { - int row2 =selectedRows.begin()->row(); - - if (row2>0) - { - int row = row2-1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; iselectedRows(); - - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - if (rowrowCount()-1) - { - int row2 = row+1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; icolumnCount(); - - for (int i=0; i (*delegate). - updateEditorSetting (settingName, settingValue)) - emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); -} - -void CSVWorld::Table::tableSizeUpdate() -{ - int size = 0; - int deleted = 0; - int modified = 0; - - if (mProxyModel->columnCount()>0) - { - int rows = mProxyModel->rowCount(); - - for (int i=0; imapToSource (mProxyModel->index (i, 0)); - - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); - int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); - - switch (state) - { - case CSMWorld::RecordBase::State_BaseOnly: ++size; break; - case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; - case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; - case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; - } - } - } - - tableSizeChanged (size, deleted, modified); -} - -void CSVWorld::Table::selectionSizeUpdate() -{ - selectionSizeChanged (selectionModel()->selectedRows().size()); -} - -void CSVWorld::Table::requestFocus (const std::string& id) -{ - QModelIndex index = mProxyModel->getModelIndex (id, 0); - - if (index.isValid()) - scrollTo (index, QAbstractItemView::PositionAtTop); -} - -void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) -{ - mProxyModel->setFilter (filter); -} - -void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) -{ - if (event->buttons() & Qt::LeftButton) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size() == 0) - { - return; - } - - QDrag* drag = new QDrag (this); - CSMWorld::TableMimeData* mime = NULL; - - if (selectedRows.size() == 1) - { - mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); - } - else - { - std::vector idToDrag; - - foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. - { - idToDrag.push_back (getUniversalId (it.row())); - } - - mime = new CSMWorld::TableMimeData (idToDrag, mDocument); - } - - drag->setMimeData (mime); - drag->setPixmap (QString::fromStdString (mime->getIcon())); - - Qt::DropActions action = Qt::IgnoreAction; - switch (QApplication::keyboardModifiers()) - { - case Qt::ControlModifier: - action = Qt::CopyAction; - break; - - case Qt::ShiftModifier: - action = Qt::MoveAction; - break; - } - - drag->exec(action); - } - -} - -void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) -{ - event->acceptProposedAction(); -} - -void CSVWorld::Table::dropEvent(QDropEvent *event) -{ - QModelIndex index = indexAt (event->pos()); - - if (!index.isValid()) - { - return; - } - - const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); - if (mime->fromDocument (mDocument)) - { - CSMWorld::ColumnBase::Display display = static_cast - (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (mime->holdsType (display)) - { - CSMWorld::UniversalId record (mime->returnMatching (display)); - - std::auto_ptr command (new CSMWorld::ModifyCommand - (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); - - mUndoStack.push (command.release()); - } - } //TODO handle drops from different document -} - -void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) -{ - event->accept(); -} - -std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const -{ - const int count = mModel->columnCount(); - - std::vector titles; - for (int i = 0; i < count; ++i) - { - CSMWorld::ColumnBase::Display columndisplay = static_cast - (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (display == columndisplay) - { - titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); - } - } - return titles; + +#include "table.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtableproxymodel.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/record.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "recordstatusdelegate.hpp" +#include "util.hpp" + +void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + QMenu menu (this); + + /// \todo add menu items for select all and clear selection + + if (!mEditLock) + { + if (selectedRows.size()==1) + { + menu.addAction (mEditAction); + if (mCreateAction) + menu.addAction(mCloneAction); + } + + if (mCreateAction) + menu.addAction (mCreateAction); + + /// \todo Reverting temporarily disabled on tables that support reordering, because + /// revert logic currently can not handle reordering. + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) + if (listRevertableSelectedIds().size()>0) + menu.addAction (mRevertAction); + + if (listDeletableSelectedIds().size()>0) + menu.addAction (mDeleteAction); + + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) + { + /// \todo allow reordering of multiple rows + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); + + if (column==-1) + column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); + + if (column!=-1) + { + if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row-1, column))) + { + menu.addAction (mMoveUpAction); + } + + if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row+1, column))) + { + menu.addAction (mMoveDownAction); + } + } + } + } + } + + menu.exec (event->globalPos()); +} + +std::vector CSVWorld::Table::listRevertableSelectedIds() const +{ + std::vector revertableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + revertableIds.push_back (id); + } + } + } + + return revertableIds; +} + +std::vector CSVWorld::Table::listDeletableSelectedIds() const +{ + std::vector deletableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + // check record state + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state==CSMWorld::RecordBase::State_Deleted) + continue; + + // check other columns (only relevant for a subset of the tables) + int dialogueTypeIndex = + mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); + + if (dialogueTypeIndex!=-1) + { + int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); + + if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) + continue; + } + + // add the id to the collection + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); + } + } + + return deletableIds; +} + +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, + bool createAndDelete, bool sorting, const CSMDoc::Document& document) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) +{ + mModel = &dynamic_cast (*data.getTableModel (id)); + + mProxyModel = new CSMWorld::IdTableProxyModel (this); + mProxyModel->setSourceModel (mModel); + + setModel (mProxyModel); + horizontalHeader()->setResizeMode (QHeaderView::Interactive); + verticalHeader()->hide(); + setSortingEnabled (sorting); + setSelectionBehavior (QAbstractItemView::SelectRows); + setSelectionMode (QAbstractItemView::ExtendedSelection); + + int columns = mModel->columnCount(); + + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + + if (flags & CSMWorld::ColumnBase::Flag_Table) + { + CSMWorld::ColumnBase::Display display = static_cast ( + mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, + undoStack, this); + + mDelegates.push_back (delegate); + setItemDelegateForColumn (i, delegate); + } + else + hideColumn (i); + } + + mEditAction = new QAction (tr ("Edit Record"), this); + connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); + addAction (mEditAction); + + if (createAndDelete) + { + mCreateAction = new QAction (tr ("Add Record"), this); + connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); + addAction (mCreateAction); + + mCloneAction = new QAction (tr ("Clone Record"), this); + connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); + addAction(mCloneAction); + } + + mRevertAction = new QAction (tr ("Revert Record"), this); + connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); + addAction (mRevertAction); + + mDeleteAction = new QAction (tr ("Delete Record"), this); + connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); + addAction (mDeleteAction); + + mMoveUpAction = new QAction (tr ("Move Up"), this); + connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); + addAction (mMoveUpAction); + + mMoveDownAction = new QAction (tr ("Move Down"), this); + connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); + addAction (mMoveDownAction); + + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (tableSizeUpdate())); + + /// \note This signal could instead be connected to a slot that filters out changes not affecting + /// the records status column (for permanence reasons) + connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (tableSizeUpdate())); + + connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), + this, SLOT (selectionSizeUpdate ())); + + setAcceptDrops(true); +} + +void CSVWorld::Table::setEditLock (bool locked) +{ + for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) + (*iter)->setEditLock (locked); + + mEditLock = locked; +} + +CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const +{ + return CSMWorld::UniversalId ( + static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), + mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); +} + +void CSVWorld::Table::revertRecord() +{ + if (!mEditLock) + { + std::vector revertableIds = listRevertableSelectedIds(); + + if (revertableIds.size()>0) + { + if (revertableIds.size()>1) + mUndoStack.beginMacro (tr ("Revert multiple records")); + + for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + + if (revertableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::deleteRecord() +{ + if (!mEditLock) + { + std::vector deletableIds = listDeletableSelectedIds(); + + if (deletableIds.size()>0) + { + if (deletableIds.size()>1) + mUndoStack.beginMacro (tr ("Delete multiple records")); + + for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); + + if (deletableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::editRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + emit editRequest (selectedRows.begin()->row()); + } +} + +void CSVWorld::Table::cloneRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); + if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) + { + emit cloneRequest (toClone); + } + } +} + +void CSVWorld::Table::moveUpRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + int row2 =selectedRows.begin()->row(); + + if (row2>0) + { + int row = row2-1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + if (rowrowCount()-1) + { + int row2 = row+1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; icolumnCount(); + + for (int i=0; i (*delegate). + updateEditorSetting (settingName, settingValue)) + emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); +} + +void CSVWorld::Table::tableSizeUpdate() +{ + int size = 0; + int deleted = 0; + int modified = 0; + + if (mProxyModel->columnCount()>0) + { + int rows = mProxyModel->rowCount(); + + for (int i=0; imapToSource (mProxyModel->index (i, 0)); + + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); + + switch (state) + { + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + } + } + } + + tableSizeChanged (size, deleted, modified); +} + +void CSVWorld::Table::selectionSizeUpdate() +{ + selectionSizeChanged (selectionModel()->selectedRows().size()); +} + +void CSVWorld::Table::requestFocus (const std::string& id) +{ + QModelIndex index = mProxyModel->getModelIndex (id, 0); + + if (index.isValid()) + scrollTo (index, QAbstractItemView::PositionAtTop); +} + +void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) +{ + mProxyModel->setFilter (filter); +} + +void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size() == 0) + { + return; + } + + QDrag* drag = new QDrag (this); + CSMWorld::TableMimeData* mime = NULL; + + if (selectedRows.size() == 1) + { + mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); + } + else + { + std::vector idToDrag; + + foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. + { + idToDrag.push_back (getUniversalId (it.row())); + } + + mime = new CSMWorld::TableMimeData (idToDrag, mDocument); + } + + drag->setMimeData (mime); + drag->setPixmap (QString::fromStdString (mime->getIcon())); + + Qt::DropActions action = Qt::IgnoreAction; + switch (QApplication::keyboardModifiers()) + { + case Qt::ControlModifier: + action = Qt::CopyAction; + break; + + case Qt::ShiftModifier: + action = Qt::MoveAction; + break; + } + + drag->exec(action); + } + +} + +void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void CSVWorld::Table::dropEvent(QDropEvent *event) +{ + QModelIndex index = indexAt (event->pos()); + + if (!index.isValid()) + { + return; + } + + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (mime->fromDocument (mDocument)) + { + CSMWorld::ColumnBase::Display display = static_cast + (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (mime->holdsType (display)) + { + CSMWorld::UniversalId record (mime->returnMatching (display)); + + std::auto_ptr command (new CSMWorld::ModifyCommand + (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); + + mUndoStack.push (command.release()); + } + } //TODO handle drops from different document +} + +void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) +{ + event->accept(); +} + +std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const +{ + const int count = mModel->columnCount(); + + std::vector titles; + for (int i = 0; i < count; ++i) + { + CSMWorld::ColumnBase::Display columndisplay = static_cast + (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (display == columndisplay) + { + titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); + } + } + return titles; } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 615a31b4d..04733a53e 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -1,127 +1,127 @@ -#ifndef CSV_WORLD_TABLE_H -#define CSV_WORLD_TABLE_H - -#include -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/columnbase.hpp" - -namespace CSMDoc { - class Document; -} - -class QUndoStack; -class QAction; - -namespace CSMWorld -{ - class Data; - class UniversalId; - class IdTableProxyModel; - class IdTable; -} - -namespace CSVWorld -{ - class CommandDelegate; - - ///< Table widget - class Table : public QTableView - { - Q_OBJECT - - std::vector mDelegates; - QUndoStack& mUndoStack; - QAction *mEditAction; - QAction *mCreateAction; - QAction *mCloneAction; - QAction *mRevertAction; - QAction *mDeleteAction; - QAction *mMoveUpAction; - QAction *mMoveDownAction; - CSMWorld::IdTableProxyModel *mProxyModel; - CSMWorld::IdTable *mModel; - bool mEditLock; - int mRecordStatusDisplay; - - /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you - /// should NOT use it for anything else. - const CSMDoc::Document& mDocument; - - private: - - void contextMenuEvent (QContextMenuEvent *event); - - std::vector listRevertableSelectedIds() const; - - std::vector listDeletableSelectedIds() const; - - void mouseMoveEvent(QMouseEvent *event); - - void dragEnterEvent(QDragEnterEvent *event); - - void dragMoveEvent(QDragMoveEvent *event); - - void dropEvent(QDropEvent *event); - - public: - - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, - bool sorting, const CSMDoc::Document& document); - - ///< \param createAndDelete Allow creation and deletion of records. - /// \param sorting Allow changing order of rows in the view via column headers. - - void setEditLock (bool locked); - - CSMWorld::UniversalId getUniversalId (int row) const; - - void updateEditorSetting (const QString &settingName, const QString &settingValue); - - std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; - - signals: - - void editRequest (int row); - - void selectionSizeChanged (int size); - - void tableSizeChanged (int size, int deleted, int modified); - ///< \param size Number of not deleted records - /// \param deleted Number of deleted records - /// \param modified Number of added and modified records - - void createRequest(); - void cloneRequest(const CSMWorld::UniversalId&); - - private slots: - - void revertRecord(); - - void deleteRecord(); - - void editRecord(); - - void cloneRecord(); - - void moveUpRecord(); - - void moveDownRecord(); - - public slots: - - void tableSizeUpdate(); - - void selectionSizeUpdate(); - - void requestFocus (const std::string& id); - - void recordFilterChanged (boost::shared_ptr filter); - }; -} - -#endif +#ifndef CSV_WORLD_TABLE_H +#define CSV_WORLD_TABLE_H + +#include +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/columnbase.hpp" + +namespace CSMDoc { + class Document; +} + +class QUndoStack; +class QAction; + +namespace CSMWorld +{ + class Data; + class UniversalId; + class IdTableProxyModel; + class IdTable; +} + +namespace CSVWorld +{ + class CommandDelegate; + + ///< Table widget + class Table : public QTableView + { + Q_OBJECT + + std::vector mDelegates; + QUndoStack& mUndoStack; + QAction *mEditAction; + QAction *mCreateAction; + QAction *mCloneAction; + QAction *mRevertAction; + QAction *mDeleteAction; + QAction *mMoveUpAction; + QAction *mMoveDownAction; + CSMWorld::IdTableProxyModel *mProxyModel; + CSMWorld::IdTable *mModel; + bool mEditLock; + int mRecordStatusDisplay; + + /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you + /// should NOT use it for anything else. + const CSMDoc::Document& mDocument; + + private: + + void contextMenuEvent (QContextMenuEvent *event); + + std::vector listRevertableSelectedIds() const; + + std::vector listDeletableSelectedIds() const; + + void mouseMoveEvent(QMouseEvent *event); + + void dragEnterEvent(QDragEnterEvent *event); + + void dragMoveEvent(QDragMoveEvent *event); + + void dropEvent(QDropEvent *event); + + public: + + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, + bool sorting, const CSMDoc::Document& document); + + ///< \param createAndDelete Allow creation and deletion of records. + /// \param sorting Allow changing order of rows in the view via column headers. + + void setEditLock (bool locked); + + CSMWorld::UniversalId getUniversalId (int row) const; + + void updateEditorSetting (const QString &settingName, const QString &settingValue); + + std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; + + signals: + + void editRequest (int row); + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); + void cloneRequest(const CSMWorld::UniversalId&); + + private slots: + + void revertRecord(); + + void deleteRecord(); + + void editRecord(); + + void cloneRecord(); + + void moveUpRecord(); + + void moveDownRecord(); + + public slots: + + void tableSizeUpdate(); + + void selectionSizeUpdate(); + + void requestFocus (const std::string& id); + + void recordFilterChanged (boost::shared_ptr filter); + }; +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index e330d4775..782ccfd24 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,132 +1,132 @@ - -#include "tablesubview.hpp" - -#include -#include - -#include "../../model/doc/document.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "../filter/filterbox.hpp" -#include "table.hpp" -#include "tablebottombox.hpp" -#include "creator.hpp" - -CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting) -: SubView (id) -{ - QVBoxLayout *layout = new QVBoxLayout; - - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - - layout->addWidget (mBottom = - new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); - - layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); - - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); - - layout->insertWidget (0, filterBox); - - QWidget *widget = new QWidget; - - widget->setLayout (layout); - - setWidget (widget); - - connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); - - connect (mTable, SIGNAL (selectionSizeChanged (int)), - mBottom, SLOT (selectionSizeChanged (int))); - connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), - mBottom, SLOT (tableSizeChanged (int, int, int))); - - mTable->tableSizeUpdate(); - mTable->selectionSizeUpdate(); - mTable->viewport()->installEventFilter(this); - mBottom->installEventFilter(this); - filterBox->installEventFilter(this); - - if (mBottom->canCreateAndDelete()) - { - connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - - connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, - SLOT(cloneRequest(const CSMWorld::UniversalId&))); - - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); - } - connect (mBottom, SIGNAL (requestFocus (const std::string&)), - mTable, SLOT (requestFocus (const std::string&))); - - connect (filterBox, - SIGNAL (recordFilterChanged (boost::shared_ptr)), - mTable, SLOT (recordFilterChanged (boost::shared_ptr))); - - connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), - this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); -} - -void CSVWorld::TableSubView::setEditLock (bool locked) -{ - mTable->setEditLock (locked); - mBottom->setEditLock (locked); -} - -void CSVWorld::TableSubView::editRequest (int row) -{ - focusId (mTable->getUniversalId (row)); -} - -void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) -{ - mTable->updateEditorSetting(settingName, settingValue); -} - -void CSVWorld::TableSubView::setStatusBar (bool show) -{ - mBottom->setStatusBar (show); -} - -void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) -{ - emit cloneRequest(toClone.getId(), toClone.getType()); -} - -void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) -{ - std::vector > > filterSource; - - for (std::vector::iterator it = types.begin(); it != types.end(); ++it) - { - std::pair > pair( //splited long line - std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); - - filterSource.push_back(pair); - } - emit createFilterRequest(filterSource, action); -} - -bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) -{ - if (event->type() == QEvent::Drop) - { - QDropEvent* drop = dynamic_cast(event); - const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); - bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); - if (handled) - { - emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); - } - return handled; - } - return false; + +#include "tablesubview.hpp" + +#include +#include + +#include "../../model/doc/document.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "../filter/filterbox.hpp" +#include "table.hpp" +#include "tablebottombox.hpp" +#include "creator.hpp" + +CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting) +: SubView (id) +{ + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); + + layout->insertWidget (0, mTable = + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); + + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + + layout->insertWidget (0, filterBox); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); + + connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); + + connect (mTable, SIGNAL (selectionSizeChanged (int)), + mBottom, SLOT (selectionSizeChanged (int))); + connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), + mBottom, SLOT (tableSizeChanged (int, int, int))); + + mTable->tableSizeUpdate(); + mTable->selectionSizeUpdate(); + mTable->viewport()->installEventFilter(this); + mBottom->installEventFilter(this); + filterBox->installEventFilter(this); + + if (mBottom->canCreateAndDelete()) + { + connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); + + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, + SLOT(cloneRequest(const CSMWorld::UniversalId&))); + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + } + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + mTable, SLOT (requestFocus (const std::string&))); + + connect (filterBox, + SIGNAL (recordFilterChanged (boost::shared_ptr)), + mTable, SLOT (recordFilterChanged (boost::shared_ptr))); + + connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), + this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); +} + +void CSVWorld::TableSubView::setEditLock (bool locked) +{ + mTable->setEditLock (locked); + mBottom->setEditLock (locked); +} + +void CSVWorld::TableSubView::editRequest (int row) +{ + focusId (mTable->getUniversalId (row)); +} + +void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) +{ + mTable->updateEditorSetting(settingName, settingValue); +} + +void CSVWorld::TableSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); +} + +void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) +{ + emit cloneRequest(toClone.getId(), toClone.getType()); +} + +void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) +{ + std::vector > > filterSource; + + for (std::vector::iterator it = types.begin(); it != types.end(); ++it) + { + std::pair > pair( //splited long line + std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); + + filterSource.push_back(pair); + } + emit createFilterRequest(filterSource, action); +} + +bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Drop) + { + QDropEvent* drop = dynamic_cast(event); + const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); + bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); + if (handled) + { + emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + } + return handled; + } + return false; } \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 1f67e0262..4e578b180 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -1,63 +1,63 @@ -#ifndef CSV_WORLD_TABLESUBVIEW_H -#define CSV_WORLD_TABLESUBVIEW_H - -#include "../doc/subview.hpp" - -#include - -class QModelIndex; - -namespace CSMWorld -{ - class IdTable; -} - -namespace CSMDoc -{ - class Document; -} - -namespace CSVWorld -{ - class Table; - class TableBottomBox; - class CreatorFactoryBase; - - class TableSubView : public CSVDoc::SubView - { - Q_OBJECT - - Table *mTable; - TableBottomBox *mBottom; - - public: - - TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting); - - virtual void setEditLock (bool locked); - - virtual void updateEditorSetting (const QString& key, const QString& value); - - virtual void setStatusBar (bool show); - - protected: - bool eventFilter(QObject* object, QEvent *event); - - signals: - void cloneRequest(const std::string&, - const CSMWorld::UniversalId::Type); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - - private slots: - - void editRequest (int row); - void cloneRequest (const CSMWorld::UniversalId& toClone); - void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, - Qt::DropAction action); - }; -} - -#endif +#ifndef CSV_WORLD_TABLESUBVIEW_H +#define CSV_WORLD_TABLESUBVIEW_H + +#include "../doc/subview.hpp" + +#include + +class QModelIndex; + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class Table; + class TableBottomBox; + class CreatorFactoryBase; + + class TableSubView : public CSVDoc::SubView + { + Q_OBJECT + + Table *mTable; + TableBottomBox *mBottom; + + public: + + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting); + + virtual void setEditLock (bool locked); + + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); + + protected: + bool eventFilter(QObject* object, QEvent *event); + + signals: + void cloneRequest(const std::string&, + const CSMWorld::UniversalId::Type); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + + private slots: + + void editRequest (int row); + void cloneRequest (const CSMWorld::UniversalId& toClone); + void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, + Qt::DropAction action); + }; +} + +#endif diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 265069dc4..f9b5b4d04 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,352 +1,352 @@ - -#include "statemanagerimp.hpp" - -#include -#include -#include -#include - -#include - -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/journal.hpp" -#include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/scriptmanager.hpp" -#include "../mwbase/soundmanager.hpp" - -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" - -#include "../mwmechanics/npcstats.hpp" - -#include "../mwscript/globalscripts.hpp" - -void MWState::StateManager::cleanup (bool force) -{ - if (mState!=State_NoGame || force) - { - MWBase::Environment::get().getSoundManager()->clear(); - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); - MWBase::Environment::get().getWorld()->clear(); - MWBase::Environment::get().getWindowManager()->clear(); - - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - mTimePlayed = 0; - } -} - -std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) - const -{ - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - const std::vector& prev = reader.getGameFiles(); - - std::map map; - - for (int iPrev = 0; iPrev (prev.size()); ++iPrev) - { - std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); - - for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) - if (id==Misc::StringUtils::lowerCase (current[iCurrent])) - { - map.insert (std::make_pair (iPrev, iCurrent)); - break; - } - } - - return map; -} - -MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) -{ - -} - -void MWState::StateManager::requestQuit() -{ - mQuitRequest = true; -} - -bool MWState::StateManager::hasQuitRequest() const -{ - return mQuitRequest; -} - -void MWState::StateManager::askLoadRecent() -{ - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) - return; - - if( !mAskLoadRecent ) - { - if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves - { - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - else - { - MWState::Slot lastSave = *getCurrentCharacter()->begin(); - std::vector buttons; - buttons.push_back("#{sYes}"); - buttons.push_back("#{sNo}"); - std::string tag("%s"); - std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); - size_t pos = message.find(tag); - message.replace(pos, tag.length(), lastSave.mProfile.mDescription); - MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); - mAskLoadRecent = true; - } - } -} - -MWState::StateManager::State MWState::StateManager::getState() const -{ - return mState; -} - -void MWState::StateManager::newGame (bool bypass) -{ - cleanup(); - - if (!bypass) - { - MWBase::Environment::get().getWorld()->startNewGame(); - MWBase::Environment::get().getWindowManager()->setNewGame (true); - } - else - MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); - - mState = State_Running; -} - -void MWState::StateManager::endGame() -{ - mState = State_Ended; - MWBase::Environment::get().getWorld()->useDeathCamera(); -} - -void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) -{ - ESM::SavedGame profile; - - MWBase::World& world = *MWBase::Environment::get().getWorld(); - - MWWorld::Ptr player = world.getPlayer().getPlayer(); - - profile.mContentFiles = world.getContentFiles(); - - profile.mPlayerName = player.getClass().getName (player); - profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClass = player.get()->mBase->mClass; - - profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); - profile.mTimePlayed = mTimePlayed; - profile.mDescription = description; - - int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing - Ogre::Image screenshot; - world.screenshot(screenshot, screenshotW, screenshotH); - Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); - profile.mScreenshot.resize(encoded->size()); - encoded->read(&profile.mScreenshot[0], encoded->size()); - - if (!slot) - slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); - else - slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); - - std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); - - ESM::ESMWriter writer; - - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); - ++iter) - writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 - - writer.setFormat (ESM::Header::CurrentFormat); - writer.setRecordCount ( - 1 // saved game header - +MWBase::Environment::get().getJournal()->countSavedGameRecords() - +MWBase::Environment::get().getWorld()->countSavedGameRecords() - +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() - +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() - +1 // global map - ); - - writer.save (stream); - - writer.startRecord (ESM::REC_SAVE); - slot->mProfile.save (writer); - writer.endRecord (ESM::REC_SAVE); - - MWBase::Environment::get().getJournal()->write (writer); - MWBase::Environment::get().getDialogueManager()->write (writer); - MWBase::Environment::get().getWorld()->write (writer); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); - MWBase::Environment::get().getWindowManager()->write(writer); - - writer.close(); - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); -} - -void MWState::StateManager::loadGame (const Character *character, const Slot *slot) -{ - try - { - cleanup(); - - mTimePlayed = slot->mProfile.mTimePlayed; - - ESM::ESMReader reader; - reader.open (slot->mPath.string()); - - std::map contentFileMap = buildContentFileIndexMap (reader); - - while (reader.hasMoreRecs()) - { - ESM::NAME n = reader.getRecName(); - reader.getRecHeader(); - - switch (n.val) - { - case ESM::REC_SAVE: - - // don't need to read that here - reader.skipRecord(); - break; - - case ESM::REC_JOUR: - case ESM::REC_QUES: - - MWBase::Environment::get().getJournal()->readRecord (reader, n.val); - break; - - case ESM::REC_DIAS: - - MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); - break; - - case ESM::REC_ALCH: - case ESM::REC_ARMO: - case ESM::REC_BOOK: - case ESM::REC_CLAS: - case ESM::REC_CLOT: - case ESM::REC_ENCH: - case ESM::REC_NPC_: - case ESM::REC_SPEL: - case ESM::REC_WEAP: - case ESM::REC_GLOB: - case ESM::REC_PLAY: - case ESM::REC_CSTA: - - MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); - break; - - case ESM::REC_GSCR: - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); - break; - - case ESM::REC_GMAP: - - MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); - break; - - default: - - // ignore invalid records - /// \todo log error - reader.skipRecord(); - } - } - - mCharacterManager.setCurrentCharacter(character); - - mState = State_Running; - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); - - MWBase::Environment::get().getWindowManager()->setNewGame(false); - MWBase::Environment::get().getWorld()->setupPlayer(); - MWBase::Environment::get().getWorld()->renderPlayer(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); - MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - - ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); - - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); - } - catch (const std::exception& e) - { - std::cerr << "failed to load saved game: " << e.what() << std::endl; - cleanup (true); - } -} - -MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) -{ - return mCharacterManager.getCurrentCharacter (create); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() -{ - return mCharacterManager.begin(); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() -{ - return mCharacterManager.end(); -} - -void MWState::StateManager::update (float duration) -{ - mTimePlayed += duration; - - // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. - if (mAskLoadRecent) - { - int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); - if(iButton==0) - { - mAskLoadRecent = false; - //Load last saved game for current character - MWState::Character *curCharacter = getCurrentCharacter(); - MWState::Slot lastSave = *curCharacter->begin(); - loadGame(curCharacter, &lastSave); - } - else if(iButton==1) - { - mAskLoadRecent = false; - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - } -} + +#include "statemanagerimp.hpp" + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/scriptmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "../mwscript/globalscripts.hpp" + +void MWState::StateManager::cleanup (bool force) +{ + if (mState!=State_NoGame || force) + { + MWBase::Environment::get().getSoundManager()->clear(); + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); + MWBase::Environment::get().getWorld()->clear(); + MWBase::Environment::get().getWindowManager()->clear(); + + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; + } +} + +std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) + const +{ + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + const std::vector& prev = reader.getGameFiles(); + + std::map map; + + for (int iPrev = 0; iPrev (prev.size()); ++iPrev) + { + std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); + + for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) + if (id==Misc::StringUtils::lowerCase (current[iCurrent])) + { + map.insert (std::make_pair (iPrev, iCurrent)); + break; + } + } + + return map; +} + +MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) +: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) +{ + +} + +void MWState::StateManager::requestQuit() +{ + mQuitRequest = true; +} + +bool MWState::StateManager::hasQuitRequest() const +{ + return mQuitRequest; +} + +void MWState::StateManager::askLoadRecent() +{ + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) + return; + + if( !mAskLoadRecent ) + { + if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else + { + MWState::Slot lastSave = *getCurrentCharacter()->begin(); + std::vector buttons; + buttons.push_back("#{sYes}"); + buttons.push_back("#{sNo}"); + std::string tag("%s"); + std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); + size_t pos = message.find(tag); + message.replace(pos, tag.length(), lastSave.mProfile.mDescription); + MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + mAskLoadRecent = true; + } + } +} + +MWState::StateManager::State MWState::StateManager::getState() const +{ + return mState; +} + +void MWState::StateManager::newGame (bool bypass) +{ + cleanup(); + + if (!bypass) + { + MWBase::Environment::get().getWorld()->startNewGame(); + MWBase::Environment::get().getWindowManager()->setNewGame (true); + } + else + MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + + mState = State_Running; +} + +void MWState::StateManager::endGame() +{ + mState = State_Ended; + MWBase::Environment::get().getWorld()->useDeathCamera(); +} + +void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) +{ + ESM::SavedGame profile; + + MWBase::World& world = *MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world.getPlayer().getPlayer(); + + profile.mContentFiles = world.getContentFiles(); + + profile.mPlayerName = player.getClass().getName (player); + profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); + profile.mPlayerClass = player.get()->mBase->mClass; + + profile.mPlayerCell = world.getCellName(); + + profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); + profile.mInGameTime.mDay = world.getDay(); + profile.mInGameTime.mMonth = world.getMonth(); + profile.mInGameTime.mYear = world.getYear(); + profile.mTimePlayed = mTimePlayed; + profile.mDescription = description; + + int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing + Ogre::Image screenshot; + world.screenshot(screenshot, screenshotW, screenshotH); + Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); + profile.mScreenshot.resize(encoded->size()); + encoded->read(&profile.mScreenshot[0], encoded->size()); + + if (!slot) + slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); + else + slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + + std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); + + ESM::ESMWriter writer; + + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); + ++iter) + writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 + + writer.setFormat (ESM::Header::CurrentFormat); + writer.setRecordCount ( + 1 // saved game header + +MWBase::Environment::get().getJournal()->countSavedGameRecords() + +MWBase::Environment::get().getWorld()->countSavedGameRecords() + +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() + +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() + +1 // global map + ); + + writer.save (stream); + + writer.startRecord (ESM::REC_SAVE); + slot->mProfile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + MWBase::Environment::get().getJournal()->write (writer); + MWBase::Environment::get().getDialogueManager()->write (writer); + MWBase::Environment::get().getWorld()->write (writer); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); + MWBase::Environment::get().getWindowManager()->write(writer); + + writer.close(); + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); +} + +void MWState::StateManager::loadGame (const Character *character, const Slot *slot) +{ + try + { + cleanup(); + + mTimePlayed = slot->mProfile.mTimePlayed; + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + std::map contentFileMap = buildContentFileIndexMap (reader); + + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); + + switch (n.val) + { + case ESM::REC_SAVE: + + // don't need to read that here + reader.skipRecord(); + break; + + case ESM::REC_JOUR: + case ESM::REC_QUES: + + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; + + case ESM::REC_DIAS: + + MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); + break; + + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + case ESM::REC_GLOB: + case ESM::REC_PLAY: + case ESM::REC_CSTA: + + MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); + break; + + case ESM::REC_GSCR: + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; + + case ESM::REC_GMAP: + + MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); + break; + + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } + } + + mCharacterManager.setCurrentCharacter(character); + + mState = State_Running; + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); + + MWBase::Environment::get().getWindowManager()->setNewGame(false); + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + } + catch (const std::exception& e) + { + std::cerr << "failed to load saved game: " << e.what() << std::endl; + cleanup (true); + } +} + +MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) +{ + return mCharacterManager.getCurrentCharacter (create); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() +{ + return mCharacterManager.begin(); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() +{ + return mCharacterManager.end(); +} + +void MWState::StateManager::update (float duration) +{ + mTimePlayed += duration; + + // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. + if (mAskLoadRecent) + { + int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if(iButton==0) + { + mAskLoadRecent = false; + //Load last saved game for current character + MWState::Character *curCharacter = getCurrentCharacter(); + MWState::Slot lastSave = *curCharacter->begin(); + loadGame(curCharacter, &lastSave); + } + else if(iButton==1) + { + mAskLoadRecent = false; + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + } +} diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 91f123eb7..b7207486b 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -1,203 +1,203 @@ -#include "esmwriter.hpp" - -#include -#include -#include - -namespace ESM -{ - ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} - - unsigned int ESMWriter::getVersion() const - { - return mHeader.mData.version; - } - - void ESMWriter::setVersion(unsigned int ver) - { - mHeader.mData.version = ver; - } - - void ESMWriter::setAuthor(const std::string& auth) - { - mHeader.mData.author.assign (auth); - } - - void ESMWriter::setDescription(const std::string& desc) - { - mHeader.mData.desc.assign (desc); - } - - void ESMWriter::setRecordCount (int count) - { - mHeader.mData.records = count; - } - - void ESMWriter::setFormat (int format) - { - mHeader.mFormat = format; - } - - void ESMWriter::clearMaster() - { - mHeader.mMaster.clear(); - } - - void ESMWriter::addMaster(const std::string& name, uint64_t size) - { - Header::MasterData d; - d.name = name; - d.size = size; - mHeader.mMaster.push_back(d); - } - - void ESMWriter::save(std::ostream& file) - { - mRecordCount = 0; - mRecords.clear(); - mCounting = true; - mStream = &file; - - startRecord("TES3", 0); - - mHeader.save (*this); - - endRecord("TES3"); - } - - void ESMWriter::close() - { - if (!mRecords.empty()) - throw std::runtime_error ("Unclosed record remaining"); - } - - void ESMWriter::startRecord(const std::string& name, uint32_t flags) - { - mRecordCount++; - - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? - writeT(flags); - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::startRecord (uint32_t name, uint32_t flags) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - startRecord (type, flags); - } - - void ESMWriter::startSubRecord(const std::string& name) - { - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::endRecord(const std::string& name) - { - RecordData rec = mRecords.back(); - assert(rec.name == name); - mRecords.pop_back(); - - mStream->seekp(rec.position); - - mCounting = false; - write (reinterpret_cast (&rec.size), sizeof(uint32_t)); - mCounting = true; - - mStream->seekp(0, std::ios::end); - - } - - void ESMWriter::endRecord (uint32_t name) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - endRecord (type); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHString(data); - endRecord(name); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) - { - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); - - if (data.size() < size) - { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); - } - - endRecord(name); - } - - void ESMWriter::writeHString(const std::string& data) - { - if (data.size() == 0) - write("\0", 1); - else - { - // Convert to UTF8 and return - std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; - - write(string.c_str(), string.size()); - } - } - - void ESMWriter::writeHCString(const std::string& data) - { - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); - } - - void ESMWriter::writeName(const std::string& name) - { - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); - } - - void ESMWriter::write(const char* data, size_t size) - { - if (mCounting && !mRecords.empty()) - { - for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) - it->size += size; - } - - mStream->write(data, size); - } - - void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) - { - mEncoder = encoder; - } -} +#include "esmwriter.hpp" + +#include +#include +#include + +namespace ESM +{ + ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} + + unsigned int ESMWriter::getVersion() const + { + return mHeader.mData.version; + } + + void ESMWriter::setVersion(unsigned int ver) + { + mHeader.mData.version = ver; + } + + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } + + void ESMWriter::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } + + void ESMWriter::setRecordCount (int count) + { + mHeader.mData.records = count; + } + + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } + + void ESMWriter::clearMaster() + { + mHeader.mMaster.clear(); + } + + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } + + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mCounting = true; + mStream = &file; + + startRecord("TES3", 0); + + mHeader.save (*this); + + endRecord("TES3"); + } + + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } + + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + writeT(0); // Unused header? + writeT(flags); + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::startRecord (uint32_t name, uint32_t flags) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + startRecord (type, flags); + } + + void ESMWriter::startSubRecord(const std::string& name) + { + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); + + mStream->seekp(rec.position); + + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(uint32_t)); + mCounting = true; + + mStream->seekp(0, std::ios::end); + + } + + void ESMWriter::endRecord (uint32_t name) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + endRecord (type); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHString(data); + endRecord(name); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) + { + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } + + endRecord(name); + } + + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; + + write(string.c_str(), string.size()); + } + } + + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } + + void ESMWriter::writeName(const std::string& name) + { + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); + } + + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } + + mStream->write(data, size); + } + + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } +} diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 33650e678..304876dbf 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -1,114 +1,114 @@ -#ifndef OPENMW_ESM_WRITER_H -#define OPENMW_ESM_WRITER_H - -#include -#include - -#include - -#include "esmcommon.hpp" -#include "loadtes3.hpp" - -namespace ESM { - -class ESMWriter -{ - struct RecordData - { - std::string name; - std::streampos position; - uint32_t size; - }; - - public: - - ESMWriter(); - - unsigned int getVersion() const; - void setVersion(unsigned int ver = 0x3fa66666); - void setEncoder(ToUTF8::Utf8Encoder *encoding); - void setAuthor(const std::string& author); - void setDescription(const std::string& desc); - void setRecordCount (int count); - void setFormat (int format); - - void clearMaster(); - - void addMaster(const std::string& name, uint64_t size); - - void save(std::ostream& file); - ///< Start saving a file by writing the TES3 header. - - void close(); - ///< \note Does not close the stream. - - void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, size_t size); - void writeHNCString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHCString(data); - endRecord(name); - } - void writeHNOString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNString(name, data); - } - void writeHNOCString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNCString(name, data); - } - - template - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template - void writeT(const T& data, size_t size) - { - write((char*)&data, size); - } - - void startRecord(const std::string& name, uint32_t flags = 0); - void startRecord(uint32_t name, uint32_t flags = 0); - void startSubRecord(const std::string& name); - void endRecord(const std::string& name); - void endRecord(uint32_t name); - void writeHString(const std::string& data); - void writeHCString(const std::string& data); - void writeName(const std::string& data); - void write(const char* data, size_t size); - - private: - std::list mRecords; - std::ostream* mStream; - std::streampos mHeaderPos; - ToUTF8::Utf8Encoder* mEncoder; - int mRecordCount; - bool mCounting; - - Header mHeader; - }; -} - -#endif +#ifndef OPENMW_ESM_WRITER_H +#define OPENMW_ESM_WRITER_H + +#include +#include + +#include + +#include "esmcommon.hpp" +#include "loadtes3.hpp" + +namespace ESM { + +class ESMWriter +{ + struct RecordData + { + std::string name; + std::streampos position; + uint32_t size; + }; + + public: + + ESMWriter(); + + unsigned int getVersion() const; + void setVersion(unsigned int ver = 0x3fa66666); + void setEncoder(ToUTF8::Utf8Encoder *encoding); + void setAuthor(const std::string& author); + void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); + + void clearMaster(); + + void addMaster(const std::string& name, uint64_t size); + + void save(std::ostream& file); + ///< Start saving a file by writing the TES3 header. + + void close(); + ///< \note Does not close the stream. + + void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, size_t size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHCString(data); + endRecord(name); + } + void writeHNOString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNString(name, data); + } + void writeHNOCString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNCString(name, data); + } + + template + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template + void writeT(const T& data, size_t size) + { + write((char*)&data, size); + } + + void startRecord(const std::string& name, uint32_t flags = 0); + void startRecord(uint32_t name, uint32_t flags = 0); + void startSubRecord(const std::string& name); + void endRecord(const std::string& name); + void endRecord(uint32_t name); + void writeHString(const std::string& data); + void writeHCString(const std::string& data); + void writeName(const std::string& data); + void write(const char* data, size_t size); + + private: + std::list mRecords; + std::ostream* mStream; + std::streampos mHeaderPos; + ToUTF8::Utf8Encoder* mEncoder; + int mRecordCount; + bool mCounting; + + Header mHeader; + }; +} + +#endif diff --git a/readme.txt b/readme.txt index a23cd1077..4cc9c6234 100644 --- a/readme.txt +++ b/readme.txt @@ -1,951 +1,951 @@ -OpenMW: A reimplementation of The Elder Scrolls III: Morrowind - -OpenMW is an attempt at recreating the engine for the popular role-playing game -Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. - -Version: 0.28.0 -License: GPL (see GPL3.txt for more information) -Website: http://www.openmw.org - -Font Licenses: -EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) -DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) - - - -INSTALLATION - -Windows: -Run the installer. - -Linux: -Ubuntu (and most others) -Download the .deb file and install it in the usual way. - -Arch Linux -There's an OpenMW package available in the [community] Repository: -https://www.archlinux.org/packages/?sort=&q=openmw - -OS X: -Open DMG file, copy OpenMW folder anywhere, for example in /Applications - -BUILD FROM SOURCE - -https://wiki.openmw.org/index.php?title=Development_Environment_Setup - - -THE DATA PATH - -The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to -pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly -(installing Morrowind under WINE is considered a proper install). - -COMMAND LINE OPTIONS - -Syntax: openmw -Allowed options: - --help print help message - --version print version information and quit - --data arg (=data) set data directories (later directories - have higher priority) - --data-local arg set local data directory (highest - priority) - --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later - archives have higher priority) - --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or - omwgame/omwaddon - --anim-verbose [=arg(=1)] (=0) output animation indices files - --no-sound [=arg(=1)] (=0) disable all sounds - --script-verbose [=arg(=1)] (=0) verbose script output - --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue - scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script - functionality - --script-run arg select a file containing a list of - console commands that is executed on - startup - --script-warn [=arg(=1)] (=1) handling of warnings when compiling - scripts - 0 - ignore warning - 1 - show warning but consider script as - correctly compiled anyway - 2 - treat warnings as errors - --skip-menu [=arg(=1)] (=0) skip main menu on game startup - --fs-strict [=arg(=1)] (=0) strict file system handling (no case - folding) - --encoding arg (=win1252) Character encoding used in OpenMW game - messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and - Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic - and other languages - - win1252 - Western European (Latin) - alphabet, used by default - --fallback arg fallback values - --no-grab Don't grab mouse cursor - --activate-dist arg (=-1) activation distance override - -CHANGELOG - -0.29.0 - -Bug #556: Video soundtrack not played when music volume is set to zero -Bug #829: OpenMW uses up all available vram, when playing for extended time -Bug #848: Wrong amount of footsteps playing in 1st person -Bug #888: Ascended Sleepers have movement issues -Bug #892: Explicit references are allowed on all script functions -Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly -Bug #1009: Lake Fjalding AI related slowdown. -Bug #1041: Music playback issues on OS X >= 10.9 -Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window -Bug #1060: Some message boxes are cut off at the bottom -Bug #1062: Bittercup script does not work ('end' variable) -Bug #1074: Inventory paperdoll obscures armour rating -Bug #1077: Message after killing an essential NPC disappears too fast -Bug #1078: "Clutterbane" shows empty charge bar -Bug #1083: UndoWerewolf fails -Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered -Bug #1090: Start scripts fail when going to a non-predefined cell -Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. -Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior -Bug #1105: Magicka is depleted when using uncastable spells -Bug #1106: Creatures should be able to run -Bug #1107: TR cliffs have way too huge collision boxes in OpenMW -Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. -Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) -Bug #1115: Memory leak when spying on Fargoth -Bug #1137: Script execution fails (drenSlaveOwners script) -Bug #1143: Mehra Milo quest (vivec informants) is broken -Bug #1145: Issues with moving gold between inventory and containers -Bug #1146: Issues with picking up stacks of gold -Bug #1147: Dwemer Crossbows are held incorrectly -Bug #1158: Armor rating should always stay below inventory mannequin -Bug #1159: Quick keys can be set during character generation -Bug #1160: Crash on equip lockpick when -Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file -Feature #30: Loading/Saving (still missing a few parts) -Feature #101: AI Package: Activate -Feature #103: AI Package: Follow, FollowCell -Feature #138: Editor: Drag & Drop -Feature #428: Player death -Feature #505: Editor: Record Cloning -Feature #701: Levelled creatures -Feature #708: Improved Local Variable handling -Feature #709: Editor: Script verifier -Feature #764: Missing journal backend features -Feature #777: Creature weapons/shields -Feature #789: Editor: Referenceable record verifier -Feature #924: Load/Save GUI (still missing loading screen and progress bars) -Feature #946: Knockdown -Feature #947: Decrease fatigue when running, swimming and attacking -Feature #956: Melee Combat: Blocking -Feature #957: Area magic -Feature #960: Combat/AI combat for creatures -Feature #962: Combat-Related AI instructions -Feature #1075: Damage/Restore skill/attribute magic effects -Feature #1076: Soultrap magic effect -Feature #1081: Disease contraction -Feature #1086: Blood particles -Feature #1092: Interrupt resting -Feature #1101: Inventory equip scripts -Feature #1116: Version/Build number in Launcher window -Feature #1119: Resistance/weakness to normal weapons magic effect -Feature #1123: Slow Fall magic effect -Feature #1130: Auto-calculate spells -Feature #1164: Editor: Case-insensitive sorting in tables - -0.28.0 - -Bug #399: Inventory changes are not visible immediately -Bug #417: Apply weather instantly when teleporting -Bug #566: Global Map position marker not updated for interior cells -Bug #712: Looting corpse delay -Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod -Bug #805: Two TR meshes appear black (v0.24RC) -Bug #841: Third-person activation distance taken from camera rather than head -Bug #845: NPCs hold torches during the day -Bug #855: Vvardenfell Visages Volume I some hairs donĀ“t appear since 0,24 -Bug #856: Maormer race by Mac Kom - The heads are way up -Bug #864: Walk locks during loading in 3rd person -Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog -Bug #882: Hircine's Ring doesn't always work -Bug #909: [Tamriel Rebuilt] crashes in Akamora -Bug #922: Launcher writing merged openmw.cfg files -Bug #943: Random magnitude should be calculated per effect -Bug #948: Negative fatigue level should be allowed -Bug #949: Particles in world space -Bug #950: Hard crash on x64 Linux running --new-game (on startup) -Bug #951: setMagicka and setFatigue have no effect -Bug #954: Problem with equipping inventory items when using a keyboard shortcut -Bug #955: Issues with equipping torches -Bug #966: Shield is visible when casting spell -Bug #967: Game crashes when equipping silver candlestick -Bug #970: Segmentation fault when starting at Bal Isra -Bug #977: Pressing down key in console doesn't go forward in history -Bug #979: Tooltip disappears when changing inventory -Bug #980: Barter: item category is remembered, but not shown -Bug #981: Mod: replacing model has wrong position/orientation -Bug #982: Launcher: Addon unchecking is not saved -Bug #983: Fix controllers to affect objects attached to the base node -Bug #985: Player can talk to NPCs who are in combat -Bug #989: OpenMW crashes when trying to include mod with capital .ESP -Bug #991: Merchants equip items with harmful constant effect enchantments -Bug #994: Don't cap skills/attributes when set via console -Bug #998: Setting the max health should also set the current health -Bug #1005: Torches are visible when casting spells and during hand to hand combat. -Bug #1006: Many NPCs have 0 skill -Bug #1007: Console fills up with text -Bug #1013: Player randomly loses health or dies -Bug #1014: Persuasion window is not centered in maximized window -Bug #1015: Player status window scroll state resets on status change -Bug #1016: Notification window not big enough for all skill level ups -Bug #1020: Saved window positions are not rescaled appropriately on resolution change -Bug #1022: Messages stuck permanently on screen when they pile up -Bug #1023: Journals doesn't open -Bug #1026: Game loses track of torch usage. -Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level -Bug #1029: Quick keys menu: Select compatible replacement when tool used up -Bug #1042: TES3 header data wrong encoding -Bug #1045: OS X: deployed OpenCS won't launch -Bug #1046: All damaged weaponry is worth 1 gold -Bug #1048: Links in "locked" dialogue are still clickable -Bug #1052: Using color codes when naming your character actually changes the name's color -Bug #1054: Spell effects not visible in front of water -Bug #1055: Power-Spell animation starts even though you already casted it that day -Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability -Bug #1063: Crash upon checking out game start ship area in Seyda Neen -Bug #1064: openmw binaries link to unnecessary libraries -Bug #1065: Landing from a high place in water still causes fall damage -Bug #1072: Drawing weapon increases torch brightness -Bug #1073: Merchants sell stacks of gold -Feature #43: Visuals for Magic Effects -Feature #51: Ranged Magic -Feature #52: Touch Range Magic -Feature #53: Self Range Magic -Feature #54: Spell Casting -Feature #70: Vampirism -Feature #100: Combat AI -Feature #171: Implement NIF record NiFlipController -Feature #410: Window to restore enchanted item charge -Feature #647: Enchanted item glow -Feature #723: Invisibility/Chameleon magic effects -Feature #737: Resist Magicka magic effect -Feature #758: GetLOS -Feature #926: Editor: Info-Record tables -Feature #958: Material controllers -Feature #959: Terrain bump, specular, & parallax mapping -Feature #990: Request: unlock mouse when in any menu -Feature #1018: Do not allow view mode switching while performing an action -Feature #1027: Vertex morph animation (NiGeomMorpherController) -Feature #1031: Handle NiBillboardNode -Feature #1051: Implement NIF texture slot DarkTexture -Task #873: Unify OGRE initialisation - -0.27.0 - -Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp -Bug #794: incorrect display of decimal numbers -Bug #840: First-person sneaking camera height -Bug #887: Ambient sounds playing while paused -Bug #902: Problems with Polish character encoding -Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key -Bug #910: Some CDs not working correctly with Unshield installer -Bug #917: Quick character creation plugin does not work -Bug #918: Fatigue does not refill -Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) -Feature #57: Acrobatics Skill -Feature #462: Editor: Start Dialogue -Feature #546: Modify ESX selector to handle new content file scheme -Feature #588: Editor: Adjust name/path of edited content files -Feature #644: Editor: Save -Feature #710: Editor: Configure script compiler context -Feature #790: God Mode -Feature #881: Editor: Allow only one instance of OpenCS -Feature #889: Editor: Record filtering -Feature #895: Extinguish torches -Feature #898: Breath meter enhancements -Feature #901: Editor: Default record filter -Feature #913: Merge --master and --plugin switches - -0.26.0 - -Bug #274: Inconsistencies in the terrain -Bug #557: Already-dead NPCs do not equip clothing/items. -Bug #592: Window resizing -Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) -Bug #664: Heart of lorkhan acts like a dead body (container) -Bug #767: Wonky ramp physics & water -Bug #780: Swimming out of water -Bug #792: Wrong ground alignment on actors when no clipping -Bug #796: Opening and closing door sound issue -Bug #797: No clipping hinders opening and closing of doors -Bug #799: sliders in enchanting window -Bug #838: Pressing key during startup procedure freezes the game -Bug #839: Combat/magic stances during character creation -Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment -Bug #844: Resting "until healed" option given even with full stats -Bug #846: Equipped torches are invisible. -Bug #847: Incorrect formula for autocalculated NPC initial health -Bug #850: Shealt weapon sound plays when leaving magic-ready stance -Bug #852: Some boots do not produce footstep sounds -Bug #860: FPS bar misalignment -Bug #861: Unable to print screen -Bug #863: No sneaking and jumping at the same time -Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. -Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. -Bug #868: Idle animations are repeated -Bug #874: Underwater swimming close to the ground is jerky -Bug #875: Animation problem while swimming on the surface and looking up -Bug #876: Always a starting upper case letter in the inventory -Bug #878: Active spell effects don't update the layout properly when ended -Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load -Bug #896: New game sound issue -Feature #49: Melee Combat -Feature #71: Lycanthropy -Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList -Feature #622: Multiple positions for inventory window -Feature #627: Drowning -Feature #786: Allow the 'Activate' key to close the countdialog window -Feature #798: Morrowind installation via Launcher (Linux/Max OS only) -Feature #851: First/Third person transitions with mouse wheel -Task #689: change PhysicActor::enableCollisions -Task #707: Reorganise Compiler - -0.25.0 - -Bug #411: Launcher crash on OS X < 10.8 -Bug #604: Terrible performance drop in the Census and Excise Office. -Bug #676: Start Scripts fail to load -Bug #677: OpenMW does not accept script names with - -Bug #766: Extra space in front of topic links -Bug #793: AIWander Isn't Being Passed The Repeat Parameter -Bug #795: Sound playing with drawn weapon and crossing cell-border -Bug #800: can't select weapon for enchantment -Bug #801: Player can move while over-encumbered -Bug #802: Dead Keys not working -Bug #808: mouse capture -Bug #809: ini Importer does not work without an existing cfg file -Bug #812: Launcher will run OpenMW with no ESM or ESP selected -Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected -Bug #817: Dead NPCs and Creatures still have collision boxes -Bug #820: Incorrect sorting of answers (Dialogue) -Bug #826: mwinimport dumps core when given an unknown parameter -Bug #833: getting stuck in door -Bug #835: Journals/books not showing up properly. -Feature #38: SoundGen -Feature #105: AI Package: Wander -Feature #230: 64-bit compatibility for OS X -Feature #263: Hardware mouse cursors -Feature #449: Allow mouse outside of window while paused -Feature #736: First person animations -Feature #750: Using mouse wheel in third person mode -Feature #822: Autorepeat for slider buttons - -0.24.0 - -Bug #284: Book's text misalignment -Bug #445: Camera able to get slightly below floor / terrain -Bug #582: Seam issue in Red Mountain -Bug #632: Journal Next Button shows white square -Bug #653: IndexedStore ignores index -Bug #694: Parser does not recognize float values starting with . -Bug #699: Resource handling broken with Ogre 1.9 trunk -Bug #718: components/esm/loadcell is using the mwworld subsystem -Bug #729: Levelled item list tries to add nonexistent item -Bug #730: Arrow buttons in the settings menu do not work. -Bug #732: Erroneous behavior when binding keys -Bug #733: Unclickable dialogue topic -Bug #734: Book empty line problem -Bug #738: OnDeath only works with implicit references -Bug #740: Script compiler fails on scripts with special names -Bug #742: Wait while no clipping -Bug #743: Problem with changeweather console command -Bug #744: No wait dialogue after starting a new game -Bug #748: Player is not able to unselect objects with the console -Bug #751: AddItem should only spawn a message box when called from dialogue -Bug #752: The enter button has several functions in trade and looting that is not impelemted. -Bug #753: Fargoth's Ring Quest Strange Behavior -Bug #755: Launcher writes duplicate lines into settings.cfg -Bug #759: Second quest in mages guild does not work -Bug #763: Enchantment cast cost is wrong -Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly -Bug #773: AIWander Isn't Being Passed The Correct idle Values -Bug #778: The journal can be opened at the start of a new game -Bug #779: Divayth Fyr starts as dead -Bug #787: "Batch count" on detailed FPS counter gets cut-off -Bug #788: chargen scroll layout does not match vanilla -Feature #60: Atlethics Skill -Feature #65: Security Skill -Feature #74: Interaction with non-load-doors -Feature #98: Render Weapon and Shield -Feature #102: AI Package: Escort, EscortCell -Feature #182: Advanced Journal GUI -Feature #288: Trading enhancements -Feature #405: Integrate "new game" into the menu -Feature #537: Highlight dialogue topic links -Feature #658: Rotate, RotateWorld script instructions and local rotations -Feature #690: Animation Layering -Feature #722: Night Eye/Blind magic effects -Feature #735: Move, MoveWorld script instructions. -Feature #760: Non-removable corpses - -0.23.0 - -Bug #522: Player collides with placeable items -Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open -Bug #561: Tooltip word wrapping delay -Bug #578: Bribing works incorrectly -Bug #601: PositionCell fails on negative coordinates -Bug #606: Some NPCs hairs not rendered with Better Heads addon -Bug #609: Bad rendering of bone boots -Bug #613: Messagebox causing assert to fail -Bug #631: Segfault on shutdown -Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard -Bug #635: Scale NPCs depending on race -Bug #643: Dialogue Race select function is inverted -Bug #646: Twohanded weapons don't work properly -Bug #654: Crash when dropping objects without a collision shape -Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell -Bug #660: "g" in "change" cut off in Race Menu -Bug #661: Arrille sells me the key to his upstairs room -Bug #662: Day counter starts at 2 instead of 1 -Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur -Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. -Bug #666: Looking up/down problem -Bug #667: Active effects border visible during loading -Bug #669: incorrect player position at new game start -Bug #670: race selection menu: sex, face and hair left button not totally clickable -Bug #671: new game: player is naked -Bug #674: buying or selling items doesn't change amount of gold -Bug #675: fatigue is not set to its maximum when starting a new game -Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly -Bug #680: different gold coins in Tel Mara -Bug #682: Race menu ignores playable flag for some hairs and faces -Bug #685: Script compiler does not accept ":" after a function name -Bug #688: dispose corpse makes cross-hair to disappear -Bug #691: Auto equipping ignores equipment conditions -Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder -Bug #696: Draugr incorrect head offset -Bug #697: Sail transparency issue -Bug #700: "On the rocks" mod does not load its UV coordinates correctly. -Bug #702: Some race mods don't work -Bug #711: Crash during character creation -Bug #715: Growing Tauryon -Bug #725: Auto calculate stats -Bug #728: Failure to open container and talk dialogue -Bug #731: Crash with Mush-Mere's "background" topic -Feature #55/657: Item Repairing -Feature #62/87: Enchanting -Feature #99: Pathfinding -Feature #104: AI Package: Travel -Feature #129: Levelled items -Feature #204: Texture animations -Feature #239: Fallback-Settings -Feature #535: Console object selection improvements -Feature #629: Add levelup description in levelup layout dialog -Feature #630: Optional format subrecord in (tes3) header -Feature #641: Armor rating -Feature #645: OnDeath script function -Feature #683: Companion item UI -Feature #698: Basic Particles -Task #648: Split up components/esm/loadlocks -Task #695: mwgui cleanup - -0.22.0 - -Bug #311: Potential infinite recursion in script compiler -Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. -Bug #382: Weird effect in 3rd person on water -Bug #387: Always use detailed shape for physics raycasts -Bug #420: Potion/ingredient effects do not stack -Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips -Bug #434/Bug #605: Object movement between cells not properly implemented -Bug #502: Duplicate player collision model at origin -Bug #509: Dialogue topic list shifts inappropriately -Bug #513: Sliding stairs -Bug #515: Launcher does not support non-latin strings -Bug #525: Race selection preview camera wrong position -Bug #526: Attributes / skills should not go below zero -Bug #529: Class and Birthsign menus options should be preselected -Bug #530: Lock window button graphic missing -Bug #532: Missing map menu graphics -Bug #545: ESX selector does not list ESM files properly -Bug #547: Global variables of type short are read incorrectly -Bug #550: Invisible meshes collision and tooltip -Bug #551: Performance drop when loading multiple ESM files -Bug #552: Don't list CG in options if it is not available -Bug #555: Character creation windows "OK" button broken -Bug #558: Segmentation fault when Alt-tabbing with console opened -Bug #559: Dialog window should not be available before character creation is finished -Bug #560: Tooltip borders should be stretched -Bug #562: Sound should not be played when an object cannot be picked up -Bug #565: Water animation speed + timescale -Bug #572: Better Bodies' textures don't work -Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) -Bug #574: Moving left/right should not cancel auto-run -Bug #575: Crash entering the Chamber of Song -Bug #576: Missing includes -Bug #577: Left Gloves Addon causes ESMReader exception -Bug #579: Unable to open container "Kvama Egg Sack" -Bug #581: Mimicking vanilla Morrowind water -Bug #583: Gender not recognized -Bug #586: Wrong char gen behaviour -Bug #587: "End" script statements with spaces don't work -Bug #589: Closing message boxes by pressing the activation key -Bug #590: Ugly Dagoth Ur rendering -Bug #591: Race selection issues -Bug #593: Persuasion response should be random -Bug #595: Footless guard -Bug #599: Waterfalls are invisible from a certain distance -Bug #600: Waterfalls rendered incorrectly, cut off by water -Bug #607: New beast bodies mod crashes -Bug #608: Crash in cell "Mournhold, Royal Palace" -Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt -Bug #613: Messagebox causing assert to fail -Bug #615: Meshes invisible from above water -Bug #617: Potion effects should be hidden until discovered -Bug #619: certain moss hanging from tree has rendering bug -Bug #621: Batching bloodmoon's trees -Bug #623: NiMaterialProperty alpha unhandled -Bug #628: Launcher in latest master crashes the game -Bug #633: Crash on startup: Better Heads -Bug #636: Incorrect Char Gen Menu Behavior -Feature #29: Allow ESPs and multiple ESMs -Feature #94: Finish class selection-dialogue -Feature #149: Texture Alphas -Feature #237: Run Morrowind-ini importer from launcher -Feature #286: Update Active Spell Icons -Feature #334: Swimming animation -Feature #335: Walking animation -Feature #360: Proper collision shapes for NPCs and creatures -Feature #367: Lights that behave more like original morrowind implementation -Feature #477: Special local scripting variables -Feature #528: Message boxes should close when enter is pressed under certain conditions. -Feature #543: Add bsa files to the settings imported by the ini importer -Feature #594: coordinate space and utility functions -Feature #625: Zoom in vanity mode -Task #464: Refactor launcher ESX selector into a re-usable component -Task #624: Unified implementation of type-variable sub-records - -0.21.0 - -Bug #253: Dialogs don't work for Russian version of Morrowind -Bug #267: Activating creatures without dialogue can still activate the dialogue GUI -Bug #354: True flickering lights -Bug #386: The main menu's first entry is wrong (in french) -Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations -Bug #495: Activation Range -Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned -Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available -Bug #500: Disposition for most NPCs is 0/100 -Bug #501: Getdisposition command wrongly returns base disposition -Bug #506: Journal UI doesn't update anymore -Bug #507: EnableRestMenu is not a valid command - change it to EnableRest -Bug #508: Crash in Ald Daedroth Shrine -Bug #517: Wrong price calculation when untrading an item -Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin -Bug #524: Beast races are able to wear shoes -Bug #527: Background music fails to play -Bug #533: The arch at Gnisis entrance is not displayed -Bug #534: Terrain gets its correct shape only some time after the cell is loaded -Bug #536: The same entry can be added multiple times to the journal -Bug #539: Race selection is broken -Bug #544: Terrain normal map corrupt when the map is rendered -Feature #39: Video Playback -Feature #151: ^-escape sequences in text output -Feature #392: Add AI related script functions -Feature #456: Determine required ini fallback values and adjust the ini importer accordingly -Feature #460: Experimental DirArchives improvements -Feature #540: Execute scripts of objects in containers/inventories in active cells -Task #401: Review GMST fixing -Task #453: Unify case smashing/folding -Task #512: Rewrite utf8 component - -0.20.0 - -Bug #366: Changing the player's race during character creation does not change the look of the player character -Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell -Bug #437: Stop animations when paused -Bug #438: Time displays as "0 a.m." when it should be "12 a.m." -Bug #439: Text in "name" field of potion/spell creation window is persistent -Bug #440: Starting date at a new game is off by one day -Bug #442: Console window doesn't close properly sometimes -Bug #448: Do not break container window formatting when item names are very long -Bug #458: Topics sometimes not automatically added to known topic list -Bug #476: Auto-Moving allows player movement after using DisablePlayerControls -Bug #478: After sleeping in a bed the rest dialogue window opens automtically again -Bug #492: On creating potions the ingredients are removed twice -Feature #63: Mercantile skill -Feature #82: Persuasion Dialogue -Feature #219: Missing dialogue filters/functions -Feature #369: Add a FailedAction -Feature #377: Select head/hair on character creation -Feature #391: Dummy AI package classes -Feature #435: Global Map, 2nd Layer -Feature #450: Persuasion -Feature #457: Add more script instructions -Feature #474: update the global variable pcrace when the player's race is changed -Task #158: Move dynamically generated classes from Player class to World Class -Task #159: ESMStore rework and cleanup -Task #163: More Component Namespace Cleanup -Task #402: Move player data from MWWorld::Player to the player's NPC record -Task #446: Fix no namespace in BulletShapeLoader - -0.19.0 - -Bug #374: Character shakes in 3rd person mode near the origin -Bug #404: Gamma correct rendering -Bug #407: Shoes of St. Rilm do not work -Bug #408: Rugs has collision even if they are not supposed to -Bug #412: Birthsign menu sorted incorrectly -Bug #413: Resolutions presented multiple times in launcher -Bug #414: launcher.cfg file stored in wrong directory -Bug #415: Wrong esm order in openmw.cfg -Bug #418: Sound listener position updates incorrectly -Bug #423: wrong usage of "Version" entry in openmw.desktop -Bug #426: Do not use hardcoded splash images -Bug #431: Don't use markers for raycast -Bug #432: Crash after picking up items from an NPC -Feature #21/#95: Sleeping/resting -Feature #61: Alchemy Skill -Feature #68: Death -Feature #69/#86: Spell Creation -Feature #72/#84: Travel -Feature #76: Global Map, 1st Layer -Feature #120: Trainer Window -Feature #152: Skill Increase from Skill Books -Feature #160: Record Saving -Task #400: Review GMST access - -0.18.0 - -Bug #310: Button of the "preferences menu" are too small -Bug #361: Hand-to-hand skill is always 100 -Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to -Bug #372: playSound3D uses original coordinates instead of current coordinates. -Bug #373: Static OGRE build faulty -Bug #375: Alt-tab toggle view -Bug #376: Screenshots are disable -Bug #378: Exception when drinking self-made potions -Bug #380: Cloth visibility problem -Bug #384: Weird character on doors tooltip. -Bug #398: Some objects do not collide in MW, but do so in OpenMW -Feature #22: Implement level-up -Feature #36: Hide Marker -Feature #88: Hotkey Window -Feature #91: Level-Up Dialogue -Feature #118: Keyboard and Mouse-Button bindings -Feature #119: Spell Buying Window -Feature #133: Handle resources across multiple data directories -Feature #134: Generate a suitable default-value for --data-local -Feature #292: Object Movement/Creation Script Instructions -Feature #340: AIPackage data structures -Feature #356: Ingredients use -Feature #358: Input system rewrite -Feature #370: Target handling in actions -Feature #379: Door markers on the local map -Feature #389: AI framework -Feature #395: Using keys to open doors / containers -Feature #396: Loading screens -Feature #397: Inventory avatar image and race selection head preview -Task #339: Move sounds into Action - -0.17.0 - -Bug #225: Valgrind reports about 40MB of leaked memory -Bug #241: Some physics meshes still don't match -Bug #248: Some textures are too dark -Bug #300: Dependency on proprietary CG toolkit -Bug #302: Some objects don't collide although they should -Bug #308: Freeze in Balmora, Meldor: Armorer -Bug #313: openmw without a ~/.config/openmw folder segfault. -Bug #317: adding non-existing spell via console locks game -Bug #318: Wrong character normals -Bug #341: Building with Ogre Debug libraries does not use debug version of plugins -Bug #347: Crash when running openmw with --start="XYZ" -Bug #353: FindMyGUI.cmake breaks path on Windows -Bug #359: WindowManager throws exception at destruction -Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation -Feature #33: Allow objects to cross cell-borders -Feature #59: Dropping Items (replaced stopgap implementation with a proper one) -Feature #93: Main Menu -Feature #96/329/330/331/332/333: Player Control -Feature #180: Object rotation and scaling. -Feature #272: Incorrect NIF material sharing -Feature #314: Potion usage -Feature #324: Skill Gain -Feature #342: Drain/fortify dynamic stats/attributes magic effects -Feature #350: Allow console only script instructions -Feature #352: Run scripts in console on startup -Task #107: Refactor mw*-subsystems -Task #325: Make CreatureStats into a class -Task #345: Use Ogre's animation system -Task #351: Rewrite Action class to support automatic sound playing - -0.16.0 - -Bug #250: OpenMW launcher erratic behaviour -Bug #270: Crash because of underwater effect on OS X -Bug #277: Auto-equipping in some cells not working -Bug #294: Container GUI ignores disabled inventory menu -Bug #297: Stats review dialog shows all skills and attribute values as 0 -Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses -Bug #299: Crash in World::disable -Bug #306: Non-existent ~/.config/openmw "crash" the launcher. -Bug #307: False "Data Files" location make the launcher "crash" -Feature #81: Spell Window -Feature #85: Alchemy Window -Feature #181: Support for x.y script syntax -Feature #242: Weapon and Spell icons -Feature #254: Ingame settings window -Feature #293: Allow "stacking" game modes -Feature #295: Class creation dialog tooltips -Feature #296: Clicking on the HUD elements should show/hide the respective window -Feature #301: Direction after using a Teleport Door -Feature #303: Allow object selection in the console -Feature #305: Allow the use of = as a synonym for == -Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts -Task #176: Restructure enabling/disabling of MW-references -Task #283: Integrate ogre.cfg file in settings file -Task #290: Auto-Close MW-reference related GUI windows - -0.15.0 - -Bug #5: Physics reimplementation (fixes various issues) -Bug #258: Resizing arrow's background is not transparent -Bug #268: Widening the stats window in X direction causes layout problems -Bug #269: Topic pane in dialgoue window is too small for some longer topics -Bug #271: Dialog choices are sorted incorrectly -Bug #281: The single quote character is not rendered on dialog windows -Bug #285: Terrain not handled properly in cells that are not predefined -Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs -Feature #15: Collision with Terrain -Feature #17: Inventory-, Container- and Trade-Windows -Feature #44: Floating Labels above Focussed Objects -Feature #80: Tooltips -Feature #83: Barter Dialogue -Feature #90: Book and Scroll Windows -Feature #156: Item Stacking in Containers -Feature #213: Pulsating lights -Feature #218: Feather & Burden -Feature #256: Implement magic effect bookkeeping -Feature #259: Add missing information to Stats window -Feature #260: Correct case for dialogue topics -Feature #280: GUI texture atlasing -Feature #291: Ability to use GMST strings from GUI layout files -Task #255: Make MWWorld::Environment into a singleton - -0.14.0 - -Bug #1: Meshes rendered with wrong orientation -Bug #6/Task #220: Picking up small objects doesn't always work -Bug #127: tcg doesn't work -Bug #178: Compablity problems with Ogre 1.8.0 RC 1 -Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI -Bug #227: Terrain crashes when moving away from predefined cells -Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces -Bug #235: TGA texture loading problem -Bug #246: wireframe mode does not work in water -Feature #8/#232: Water Rendering -Feature #13: Terrain Rendering -Feature #37: Render Path Grid -Feature #66: Factions -Feature #77: Local Map -Feature #78: Compass/Mini-Map -Feature #97: Render Clothing/Armour -Feature #121: Window Pinning -Feature #205: Auto equip -Feature #217: Contiainer should track changes to its content -Feature #221: NPC Dialogue Window Enhancements -Feature #233: Game settings manager -Feature #240: Spell List and selected spell (no GUI yet) -Feature #243: Draw State -Task #113: Morrowind.ini Importer -Task #215: Refactor the sound code -Task #216: Update MyGUI - -0.13.0 - -Bug #145: Fixed sound problems after cell change -Bug #179: Pressing space in console triggers activation -Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux -Bug #189: ASCII 16 character added to console on it's activation on Mac OS X -Bug #190: Case Folding fails with music files -Bug #192: Keypresses write Text into Console no matter which gui element is active -Bug #196: Collision shapes out of place -Bug #202: ESMTool doesn't not work with localised ESM files anymore -Bug #203: Torch lights only visible on short distance -Bug #207: Ogre.log not written -Bug #209: Sounds do not play -Bug #210: Ogre crash at Dren plantation -Bug #214: Unsupported file format version -Bug #222: Launcher is writing openmw.cfg file to wrong location -Feature #9: NPC Dialogue Window -Feature #16/42: New sky/weather implementation -Feature #40: Fading -Feature #48: NPC Dialogue System -Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) -Feature #161: Load REC_PGRD records -Feature #195: Wireframe-mode -Feature #198/199: Various sound effects -Feature #206: Allow picking data path from launcher if non is set -Task #108: Refactor window manager class -Task #172: Sound Manager Cleanup -Task #173: Create OpenEngine systems in the appropriate manager classes -Task #184: Adjust MSVC and gcc warning levels -Task #185: RefData rewrite -Task #201: Workaround for transparency issues -Task #208: silenced esm_reader.hpp warning - -0.12.0 - -Bug #154: FPS Drop -Bug #169: Local scripts continue running if associated object is deleted -Bug #174: OpenMW fails to start if the config directory doesn't exist -Bug #187: Missing lighting -Bug #188: Lights without a mesh are not rendered -Bug #191: Taking screenshot causes crash when running installed -Feature #28: Sort out the cell load problem -Feature #31: Allow the player to move away from pre-defined cells -Feature #35: Use alternate storage location for modified object position -Feature #45: NPC animations -Feature #46: Creature Animation -Feature #89: Basic Journal Window -Feature #110: Automatically pick up the path of existing MW-installations -Feature #183: More FPS display settings -Task #19: Refactor engine class -Task #109/Feature #162: Automate Packaging -Task #112: Catch exceptions thrown in input handling functions -Task #128/#168: Cleanup Configuration File Handling -Task #131: NPC Activation doesn't work properly -Task #144: MWRender cleanup -Task #155: cmake cleanup - -0.11.1 - -Bug #2: Resources loading doesn't work outside of bsa files -Bug #3: GUI does not render non-English characters -Bug #7: openmw.cfg location doesn't match -Bug #124: The TCL alias for ToggleCollision is missing. -Bug #125: Some command line options can't be used from a .cfg file -Bug #126: Toggle-type script instructions are less verbose compared with original MW -Bug #130: NPC-Record Loading fails for some NPCs -Bug #167: Launcher sets invalid parameters in ogre config -Feature #10: Journal -Feature #12: Rendering Optimisations -Feature #23: Change Launcher GUI to a tabbed interface -Feature #24: Integrate the OGRE settings window into the launcher -Feature #25: Determine openmw.cfg location (Launcher) -Feature #26: Launcher Profiles -Feature #79: MessageBox -Feature #116: Tab-Completion in Console -Feature #132: --data-local and multiple --data -Feature #143: Non-Rendering Performance-Optimisations -Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs -Feature #157: Version Handling -Task #14: Replace tabs with 4 spaces -Task #18: Move components from global namespace into their own namespace -Task #123: refactor header files in components/esm - -0.10.0 - -* NPC dialogue window (not functional yet) -* Collisions with objects -* Refactor the PlayerPos class -* Adjust file locations -* CMake files and test linking for Bullet -* Replace Ogre raycasting test for activation with something more precise -* Adjust player movement according to collision results -* FPS display -* Various Portability Improvements -* Mac OS X support is back! - -0.9.0 - -* Exterior cells loading, unloading and management -* Character Creation GUI -* Character creation -* Make cell names case insensitive when doing internal lookups -* Music player -* NPCs rendering - -0.8.0 - -* GUI -* Complete and working script engine -* In game console -* Sky rendering -* Sound and music -* Tons of smaller stuff - -0.7.0 - -* This release is a complete rewrite in C++. -* All D code has been culled, and all modules have been rewritten. -* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. - -0.6.0 - -* Coded a GUI system using MyGUI -* Skinned MyGUI to look like Morrowind (work in progress) -* Integrated the Monster script engine -* Rewrote some functions into script code -* Very early MyGUI < > Monster binding -* Fixed Windows sound problems (replaced old openal32.dll) - -0.5.0 - -* Collision detection with Bullet -* Experimental walk & fall character physics -* New key bindings: - * t toggle physics mode (walking, flying, ghost), - * n night eye, brightens the scene -* Fixed incompatability with DMD 1.032 and newer compilers -* * (thanks to tomqyp) -* Various minor changes and updates - -0.4.0 - -* Switched from Audiere to OpenAL -* * (BIG thanks to Chris Robinson) -* Added complete Makefile (again) as a alternative build tool -* More realistic lighting (thanks again to Chris Robinson) -* Various localization fixes tested with Russian and French versions -* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' -* Added ns option to disable sound, for debugging -* Various bug fixes -* Cosmetic changes to placate gdc Wall - -0.3.0 - -* Built and tested on Windows XP -* Partial support for FreeBSD (exceptions do not work) -* You no longer have to download Monster separately -* Made an alternative for building without DSSS (but DSSS still works) -* Renamed main program from 'morro' to 'openmw' -* Made the config system more robust -* Added oc switch for showing Ogre config window on startup -* Removed some config files, these are auto generated when missing. -* Separated plugins.cfg into linux and windows versions. -* Updated Makefile and sources for increased portability -* confirmed to work against OIS 1.0.0 (Ubuntu repository package) - -0.2.0 - -* Compiles with gdc -* Switched to DSSS for building D code -* Includes the program esmtool - -0.1.0 - -first release +OpenMW: A reimplementation of The Elder Scrolls III: Morrowind + +OpenMW is an attempt at recreating the engine for the popular role-playing game +Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. + +Version: 0.28.0 +License: GPL (see GPL3.txt for more information) +Website: http://www.openmw.org + +Font Licenses: +EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) +DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) + + + +INSTALLATION + +Windows: +Run the installer. + +Linux: +Ubuntu (and most others) +Download the .deb file and install it in the usual way. + +Arch Linux +There's an OpenMW package available in the [community] Repository: +https://www.archlinux.org/packages/?sort=&q=openmw + +OS X: +Open DMG file, copy OpenMW folder anywhere, for example in /Applications + +BUILD FROM SOURCE + +https://wiki.openmw.org/index.php?title=Development_Environment_Setup + + +THE DATA PATH + +The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to +pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly +(installing Morrowind under WINE is considered a proper install). + +COMMAND LINE OPTIONS + +Syntax: openmw +Allowed options: + --help print help message + --version print version information and quit + --data arg (=data) set data directories (later directories + have higher priority) + --data-local arg set local data directory (highest + priority) + --fallback-archive arg (=fallback-archive) + set fallback BSA archives (later + archives have higher priority) + --resources arg (=resources) set resources directory + --start arg (=Beshara) set initial cell + --content arg content file(s): esm/esp, or + omwgame/omwaddon + --anim-verbose [=arg(=1)] (=0) output animation indices files + --no-sound [=arg(=1)] (=0) disable all sounds + --script-verbose [=arg(=1)] (=0) verbose script output + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue + scripts) at startup + --script-console [=arg(=1)] (=0) enable console-only script + functionality + --script-run arg select a file containing a list of + console commands that is executed on + startup + --script-warn [=arg(=1)] (=1) handling of warnings when compiling + scripts + 0 - ignore warning + 1 - show warning but consider script as + correctly compiled anyway + 2 - treat warnings as errors + --skip-menu [=arg(=1)] (=0) skip main menu on game startup + --fs-strict [=arg(=1)] (=0) strict file system handling (no case + folding) + --encoding arg (=win1252) Character encoding used in OpenMW game + messages: + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and + Albanian languages + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic + and other languages + + win1252 - Western European (Latin) + alphabet, used by default + --fallback arg fallback values + --no-grab Don't grab mouse cursor + --activate-dist arg (=-1) activation distance override + +CHANGELOG + +0.29.0 + +Bug #556: Video soundtrack not played when music volume is set to zero +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #848: Wrong amount of footsteps playing in 1st person +Bug #888: Ascended Sleepers have movement issues +Bug #892: Explicit references are allowed on all script functions +Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly +Bug #1009: Lake Fjalding AI related slowdown. +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window +Bug #1060: Some message boxes are cut off at the bottom +Bug #1062: Bittercup script does not work ('end' variable) +Bug #1074: Inventory paperdoll obscures armour rating +Bug #1077: Message after killing an essential NPC disappears too fast +Bug #1078: "Clutterbane" shows empty charge bar +Bug #1083: UndoWerewolf fails +Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered +Bug #1090: Start scripts fail when going to a non-predefined cell +Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. +Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior +Bug #1105: Magicka is depleted when using uncastable spells +Bug #1106: Creatures should be able to run +Bug #1107: TR cliffs have way too huge collision boxes in OpenMW +Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. +Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) +Bug #1115: Memory leak when spying on Fargoth +Bug #1137: Script execution fails (drenSlaveOwners script) +Bug #1143: Mehra Milo quest (vivec informants) is broken +Bug #1145: Issues with moving gold between inventory and containers +Bug #1146: Issues with picking up stacks of gold +Bug #1147: Dwemer Crossbows are held incorrectly +Bug #1158: Armor rating should always stay below inventory mannequin +Bug #1159: Quick keys can be set during character generation +Bug #1160: Crash on equip lockpick when +Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Feature #30: Loading/Saving (still missing a few parts) +Feature #101: AI Package: Activate +Feature #103: AI Package: Follow, FollowCell +Feature #138: Editor: Drag & Drop +Feature #428: Player death +Feature #505: Editor: Record Cloning +Feature #701: Levelled creatures +Feature #708: Improved Local Variable handling +Feature #709: Editor: Script verifier +Feature #764: Missing journal backend features +Feature #777: Creature weapons/shields +Feature #789: Editor: Referenceable record verifier +Feature #924: Load/Save GUI (still missing loading screen and progress bars) +Feature #946: Knockdown +Feature #947: Decrease fatigue when running, swimming and attacking +Feature #956: Melee Combat: Blocking +Feature #957: Area magic +Feature #960: Combat/AI combat for creatures +Feature #962: Combat-Related AI instructions +Feature #1075: Damage/Restore skill/attribute magic effects +Feature #1076: Soultrap magic effect +Feature #1081: Disease contraction +Feature #1086: Blood particles +Feature #1092: Interrupt resting +Feature #1101: Inventory equip scripts +Feature #1116: Version/Build number in Launcher window +Feature #1119: Resistance/weakness to normal weapons magic effect +Feature #1123: Slow Fall magic effect +Feature #1130: Auto-calculate spells +Feature #1164: Editor: Case-insensitive sorting in tables + +0.28.0 + +Bug #399: Inventory changes are not visible immediately +Bug #417: Apply weather instantly when teleporting +Bug #566: Global Map position marker not updated for interior cells +Bug #712: Looting corpse delay +Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod +Bug #805: Two TR meshes appear black (v0.24RC) +Bug #841: Third-person activation distance taken from camera rather than head +Bug #845: NPCs hold torches during the day +Bug #855: Vvardenfell Visages Volume I some hairs donĀ“t appear since 0,24 +Bug #856: Maormer race by Mac Kom - The heads are way up +Bug #864: Walk locks during loading in 3rd person +Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog +Bug #882: Hircine's Ring doesn't always work +Bug #909: [Tamriel Rebuilt] crashes in Akamora +Bug #922: Launcher writing merged openmw.cfg files +Bug #943: Random magnitude should be calculated per effect +Bug #948: Negative fatigue level should be allowed +Bug #949: Particles in world space +Bug #950: Hard crash on x64 Linux running --new-game (on startup) +Bug #951: setMagicka and setFatigue have no effect +Bug #954: Problem with equipping inventory items when using a keyboard shortcut +Bug #955: Issues with equipping torches +Bug #966: Shield is visible when casting spell +Bug #967: Game crashes when equipping silver candlestick +Bug #970: Segmentation fault when starting at Bal Isra +Bug #977: Pressing down key in console doesn't go forward in history +Bug #979: Tooltip disappears when changing inventory +Bug #980: Barter: item category is remembered, but not shown +Bug #981: Mod: replacing model has wrong position/orientation +Bug #982: Launcher: Addon unchecking is not saved +Bug #983: Fix controllers to affect objects attached to the base node +Bug #985: Player can talk to NPCs who are in combat +Bug #989: OpenMW crashes when trying to include mod with capital .ESP +Bug #991: Merchants equip items with harmful constant effect enchantments +Bug #994: Don't cap skills/attributes when set via console +Bug #998: Setting the max health should also set the current health +Bug #1005: Torches are visible when casting spells and during hand to hand combat. +Bug #1006: Many NPCs have 0 skill +Bug #1007: Console fills up with text +Bug #1013: Player randomly loses health or dies +Bug #1014: Persuasion window is not centered in maximized window +Bug #1015: Player status window scroll state resets on status change +Bug #1016: Notification window not big enough for all skill level ups +Bug #1020: Saved window positions are not rescaled appropriately on resolution change +Bug #1022: Messages stuck permanently on screen when they pile up +Bug #1023: Journals doesn't open +Bug #1026: Game loses track of torch usage. +Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level +Bug #1029: Quick keys menu: Select compatible replacement when tool used up +Bug #1042: TES3 header data wrong encoding +Bug #1045: OS X: deployed OpenCS won't launch +Bug #1046: All damaged weaponry is worth 1 gold +Bug #1048: Links in "locked" dialogue are still clickable +Bug #1052: Using color codes when naming your character actually changes the name's color +Bug #1054: Spell effects not visible in front of water +Bug #1055: Power-Spell animation starts even though you already casted it that day +Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability +Bug #1063: Crash upon checking out game start ship area in Seyda Neen +Bug #1064: openmw binaries link to unnecessary libraries +Bug #1065: Landing from a high place in water still causes fall damage +Bug #1072: Drawing weapon increases torch brightness +Bug #1073: Merchants sell stacks of gold +Feature #43: Visuals for Magic Effects +Feature #51: Ranged Magic +Feature #52: Touch Range Magic +Feature #53: Self Range Magic +Feature #54: Spell Casting +Feature #70: Vampirism +Feature #100: Combat AI +Feature #171: Implement NIF record NiFlipController +Feature #410: Window to restore enchanted item charge +Feature #647: Enchanted item glow +Feature #723: Invisibility/Chameleon magic effects +Feature #737: Resist Magicka magic effect +Feature #758: GetLOS +Feature #926: Editor: Info-Record tables +Feature #958: Material controllers +Feature #959: Terrain bump, specular, & parallax mapping +Feature #990: Request: unlock mouse when in any menu +Feature #1018: Do not allow view mode switching while performing an action +Feature #1027: Vertex morph animation (NiGeomMorpherController) +Feature #1031: Handle NiBillboardNode +Feature #1051: Implement NIF texture slot DarkTexture +Task #873: Unify OGRE initialisation + +0.27.0 + +Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp +Bug #794: incorrect display of decimal numbers +Bug #840: First-person sneaking camera height +Bug #887: Ambient sounds playing while paused +Bug #902: Problems with Polish character encoding +Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key +Bug #910: Some CDs not working correctly with Unshield installer +Bug #917: Quick character creation plugin does not work +Bug #918: Fatigue does not refill +Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) +Feature #57: Acrobatics Skill +Feature #462: Editor: Start Dialogue +Feature #546: Modify ESX selector to handle new content file scheme +Feature #588: Editor: Adjust name/path of edited content files +Feature #644: Editor: Save +Feature #710: Editor: Configure script compiler context +Feature #790: God Mode +Feature #881: Editor: Allow only one instance of OpenCS +Feature #889: Editor: Record filtering +Feature #895: Extinguish torches +Feature #898: Breath meter enhancements +Feature #901: Editor: Default record filter +Feature #913: Merge --master and --plugin switches + +0.26.0 + +Bug #274: Inconsistencies in the terrain +Bug #557: Already-dead NPCs do not equip clothing/items. +Bug #592: Window resizing +Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) +Bug #664: Heart of lorkhan acts like a dead body (container) +Bug #767: Wonky ramp physics & water +Bug #780: Swimming out of water +Bug #792: Wrong ground alignment on actors when no clipping +Bug #796: Opening and closing door sound issue +Bug #797: No clipping hinders opening and closing of doors +Bug #799: sliders in enchanting window +Bug #838: Pressing key during startup procedure freezes the game +Bug #839: Combat/magic stances during character creation +Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment +Bug #844: Resting "until healed" option given even with full stats +Bug #846: Equipped torches are invisible. +Bug #847: Incorrect formula for autocalculated NPC initial health +Bug #850: Shealt weapon sound plays when leaving magic-ready stance +Bug #852: Some boots do not produce footstep sounds +Bug #860: FPS bar misalignment +Bug #861: Unable to print screen +Bug #863: No sneaking and jumping at the same time +Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. +Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. +Bug #868: Idle animations are repeated +Bug #874: Underwater swimming close to the ground is jerky +Bug #875: Animation problem while swimming on the surface and looking up +Bug #876: Always a starting upper case letter in the inventory +Bug #878: Active spell effects don't update the layout properly when ended +Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load +Bug #896: New game sound issue +Feature #49: Melee Combat +Feature #71: Lycanthropy +Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList +Feature #622: Multiple positions for inventory window +Feature #627: Drowning +Feature #786: Allow the 'Activate' key to close the countdialog window +Feature #798: Morrowind installation via Launcher (Linux/Max OS only) +Feature #851: First/Third person transitions with mouse wheel +Task #689: change PhysicActor::enableCollisions +Task #707: Reorganise Compiler + +0.25.0 + +Bug #411: Launcher crash on OS X < 10.8 +Bug #604: Terrible performance drop in the Census and Excise Office. +Bug #676: Start Scripts fail to load +Bug #677: OpenMW does not accept script names with - +Bug #766: Extra space in front of topic links +Bug #793: AIWander Isn't Being Passed The Repeat Parameter +Bug #795: Sound playing with drawn weapon and crossing cell-border +Bug #800: can't select weapon for enchantment +Bug #801: Player can move while over-encumbered +Bug #802: Dead Keys not working +Bug #808: mouse capture +Bug #809: ini Importer does not work without an existing cfg file +Bug #812: Launcher will run OpenMW with no ESM or ESP selected +Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected +Bug #817: Dead NPCs and Creatures still have collision boxes +Bug #820: Incorrect sorting of answers (Dialogue) +Bug #826: mwinimport dumps core when given an unknown parameter +Bug #833: getting stuck in door +Bug #835: Journals/books not showing up properly. +Feature #38: SoundGen +Feature #105: AI Package: Wander +Feature #230: 64-bit compatibility for OS X +Feature #263: Hardware mouse cursors +Feature #449: Allow mouse outside of window while paused +Feature #736: First person animations +Feature #750: Using mouse wheel in third person mode +Feature #822: Autorepeat for slider buttons + +0.24.0 + +Bug #284: Book's text misalignment +Bug #445: Camera able to get slightly below floor / terrain +Bug #582: Seam issue in Red Mountain +Bug #632: Journal Next Button shows white square +Bug #653: IndexedStore ignores index +Bug #694: Parser does not recognize float values starting with . +Bug #699: Resource handling broken with Ogre 1.9 trunk +Bug #718: components/esm/loadcell is using the mwworld subsystem +Bug #729: Levelled item list tries to add nonexistent item +Bug #730: Arrow buttons in the settings menu do not work. +Bug #732: Erroneous behavior when binding keys +Bug #733: Unclickable dialogue topic +Bug #734: Book empty line problem +Bug #738: OnDeath only works with implicit references +Bug #740: Script compiler fails on scripts with special names +Bug #742: Wait while no clipping +Bug #743: Problem with changeweather console command +Bug #744: No wait dialogue after starting a new game +Bug #748: Player is not able to unselect objects with the console +Bug #751: AddItem should only spawn a message box when called from dialogue +Bug #752: The enter button has several functions in trade and looting that is not impelemted. +Bug #753: Fargoth's Ring Quest Strange Behavior +Bug #755: Launcher writes duplicate lines into settings.cfg +Bug #759: Second quest in mages guild does not work +Bug #763: Enchantment cast cost is wrong +Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly +Bug #773: AIWander Isn't Being Passed The Correct idle Values +Bug #778: The journal can be opened at the start of a new game +Bug #779: Divayth Fyr starts as dead +Bug #787: "Batch count" on detailed FPS counter gets cut-off +Bug #788: chargen scroll layout does not match vanilla +Feature #60: Atlethics Skill +Feature #65: Security Skill +Feature #74: Interaction with non-load-doors +Feature #98: Render Weapon and Shield +Feature #102: AI Package: Escort, EscortCell +Feature #182: Advanced Journal GUI +Feature #288: Trading enhancements +Feature #405: Integrate "new game" into the menu +Feature #537: Highlight dialogue topic links +Feature #658: Rotate, RotateWorld script instructions and local rotations +Feature #690: Animation Layering +Feature #722: Night Eye/Blind magic effects +Feature #735: Move, MoveWorld script instructions. +Feature #760: Non-removable corpses + +0.23.0 + +Bug #522: Player collides with placeable items +Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open +Bug #561: Tooltip word wrapping delay +Bug #578: Bribing works incorrectly +Bug #601: PositionCell fails on negative coordinates +Bug #606: Some NPCs hairs not rendered with Better Heads addon +Bug #609: Bad rendering of bone boots +Bug #613: Messagebox causing assert to fail +Bug #631: Segfault on shutdown +Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard +Bug #635: Scale NPCs depending on race +Bug #643: Dialogue Race select function is inverted +Bug #646: Twohanded weapons don't work properly +Bug #654: Crash when dropping objects without a collision shape +Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell +Bug #660: "g" in "change" cut off in Race Menu +Bug #661: Arrille sells me the key to his upstairs room +Bug #662: Day counter starts at 2 instead of 1 +Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur +Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. +Bug #666: Looking up/down problem +Bug #667: Active effects border visible during loading +Bug #669: incorrect player position at new game start +Bug #670: race selection menu: sex, face and hair left button not totally clickable +Bug #671: new game: player is naked +Bug #674: buying or selling items doesn't change amount of gold +Bug #675: fatigue is not set to its maximum when starting a new game +Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly +Bug #680: different gold coins in Tel Mara +Bug #682: Race menu ignores playable flag for some hairs and faces +Bug #685: Script compiler does not accept ":" after a function name +Bug #688: dispose corpse makes cross-hair to disappear +Bug #691: Auto equipping ignores equipment conditions +Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder +Bug #696: Draugr incorrect head offset +Bug #697: Sail transparency issue +Bug #700: "On the rocks" mod does not load its UV coordinates correctly. +Bug #702: Some race mods don't work +Bug #711: Crash during character creation +Bug #715: Growing Tauryon +Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic +Feature #55/657: Item Repairing +Feature #62/87: Enchanting +Feature #99: Pathfinding +Feature #104: AI Package: Travel +Feature #129: Levelled items +Feature #204: Texture animations +Feature #239: Fallback-Settings +Feature #535: Console object selection improvements +Feature #629: Add levelup description in levelup layout dialog +Feature #630: Optional format subrecord in (tes3) header +Feature #641: Armor rating +Feature #645: OnDeath script function +Feature #683: Companion item UI +Feature #698: Basic Particles +Task #648: Split up components/esm/loadlocks +Task #695: mwgui cleanup + +0.22.0 + +Bug #311: Potential infinite recursion in script compiler +Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. +Bug #382: Weird effect in 3rd person on water +Bug #387: Always use detailed shape for physics raycasts +Bug #420: Potion/ingredient effects do not stack +Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips +Bug #434/Bug #605: Object movement between cells not properly implemented +Bug #502: Duplicate player collision model at origin +Bug #509: Dialogue topic list shifts inappropriately +Bug #513: Sliding stairs +Bug #515: Launcher does not support non-latin strings +Bug #525: Race selection preview camera wrong position +Bug #526: Attributes / skills should not go below zero +Bug #529: Class and Birthsign menus options should be preselected +Bug #530: Lock window button graphic missing +Bug #532: Missing map menu graphics +Bug #545: ESX selector does not list ESM files properly +Bug #547: Global variables of type short are read incorrectly +Bug #550: Invisible meshes collision and tooltip +Bug #551: Performance drop when loading multiple ESM files +Bug #552: Don't list CG in options if it is not available +Bug #555: Character creation windows "OK" button broken +Bug #558: Segmentation fault when Alt-tabbing with console opened +Bug #559: Dialog window should not be available before character creation is finished +Bug #560: Tooltip borders should be stretched +Bug #562: Sound should not be played when an object cannot be picked up +Bug #565: Water animation speed + timescale +Bug #572: Better Bodies' textures don't work +Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) +Bug #574: Moving left/right should not cancel auto-run +Bug #575: Crash entering the Chamber of Song +Bug #576: Missing includes +Bug #577: Left Gloves Addon causes ESMReader exception +Bug #579: Unable to open container "Kvama Egg Sack" +Bug #581: Mimicking vanilla Morrowind water +Bug #583: Gender not recognized +Bug #586: Wrong char gen behaviour +Bug #587: "End" script statements with spaces don't work +Bug #589: Closing message boxes by pressing the activation key +Bug #590: Ugly Dagoth Ur rendering +Bug #591: Race selection issues +Bug #593: Persuasion response should be random +Bug #595: Footless guard +Bug #599: Waterfalls are invisible from a certain distance +Bug #600: Waterfalls rendered incorrectly, cut off by water +Bug #607: New beast bodies mod crashes +Bug #608: Crash in cell "Mournhold, Royal Palace" +Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt +Bug #613: Messagebox causing assert to fail +Bug #615: Meshes invisible from above water +Bug #617: Potion effects should be hidden until discovered +Bug #619: certain moss hanging from tree has rendering bug +Bug #621: Batching bloodmoon's trees +Bug #623: NiMaterialProperty alpha unhandled +Bug #628: Launcher in latest master crashes the game +Bug #633: Crash on startup: Better Heads +Bug #636: Incorrect Char Gen Menu Behavior +Feature #29: Allow ESPs and multiple ESMs +Feature #94: Finish class selection-dialogue +Feature #149: Texture Alphas +Feature #237: Run Morrowind-ini importer from launcher +Feature #286: Update Active Spell Icons +Feature #334: Swimming animation +Feature #335: Walking animation +Feature #360: Proper collision shapes for NPCs and creatures +Feature #367: Lights that behave more like original morrowind implementation +Feature #477: Special local scripting variables +Feature #528: Message boxes should close when enter is pressed under certain conditions. +Feature #543: Add bsa files to the settings imported by the ini importer +Feature #594: coordinate space and utility functions +Feature #625: Zoom in vanity mode +Task #464: Refactor launcher ESX selector into a re-usable component +Task #624: Unified implementation of type-variable sub-records + +0.21.0 + +Bug #253: Dialogs don't work for Russian version of Morrowind +Bug #267: Activating creatures without dialogue can still activate the dialogue GUI +Bug #354: True flickering lights +Bug #386: The main menu's first entry is wrong (in french) +Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations +Bug #495: Activation Range +Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned +Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available +Bug #500: Disposition for most NPCs is 0/100 +Bug #501: Getdisposition command wrongly returns base disposition +Bug #506: Journal UI doesn't update anymore +Bug #507: EnableRestMenu is not a valid command - change it to EnableRest +Bug #508: Crash in Ald Daedroth Shrine +Bug #517: Wrong price calculation when untrading an item +Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin +Bug #524: Beast races are able to wear shoes +Bug #527: Background music fails to play +Bug #533: The arch at Gnisis entrance is not displayed +Bug #534: Terrain gets its correct shape only some time after the cell is loaded +Bug #536: The same entry can be added multiple times to the journal +Bug #539: Race selection is broken +Bug #544: Terrain normal map corrupt when the map is rendered +Feature #39: Video Playback +Feature #151: ^-escape sequences in text output +Feature #392: Add AI related script functions +Feature #456: Determine required ini fallback values and adjust the ini importer accordingly +Feature #460: Experimental DirArchives improvements +Feature #540: Execute scripts of objects in containers/inventories in active cells +Task #401: Review GMST fixing +Task #453: Unify case smashing/folding +Task #512: Rewrite utf8 component + +0.20.0 + +Bug #366: Changing the player's race during character creation does not change the look of the player character +Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell +Bug #437: Stop animations when paused +Bug #438: Time displays as "0 a.m." when it should be "12 a.m." +Bug #439: Text in "name" field of potion/spell creation window is persistent +Bug #440: Starting date at a new game is off by one day +Bug #442: Console window doesn't close properly sometimes +Bug #448: Do not break container window formatting when item names are very long +Bug #458: Topics sometimes not automatically added to known topic list +Bug #476: Auto-Moving allows player movement after using DisablePlayerControls +Bug #478: After sleeping in a bed the rest dialogue window opens automtically again +Bug #492: On creating potions the ingredients are removed twice +Feature #63: Mercantile skill +Feature #82: Persuasion Dialogue +Feature #219: Missing dialogue filters/functions +Feature #369: Add a FailedAction +Feature #377: Select head/hair on character creation +Feature #391: Dummy AI package classes +Feature #435: Global Map, 2nd Layer +Feature #450: Persuasion +Feature #457: Add more script instructions +Feature #474: update the global variable pcrace when the player's race is changed +Task #158: Move dynamically generated classes from Player class to World Class +Task #159: ESMStore rework and cleanup +Task #163: More Component Namespace Cleanup +Task #402: Move player data from MWWorld::Player to the player's NPC record +Task #446: Fix no namespace in BulletShapeLoader + +0.19.0 + +Bug #374: Character shakes in 3rd person mode near the origin +Bug #404: Gamma correct rendering +Bug #407: Shoes of St. Rilm do not work +Bug #408: Rugs has collision even if they are not supposed to +Bug #412: Birthsign menu sorted incorrectly +Bug #413: Resolutions presented multiple times in launcher +Bug #414: launcher.cfg file stored in wrong directory +Bug #415: Wrong esm order in openmw.cfg +Bug #418: Sound listener position updates incorrectly +Bug #423: wrong usage of "Version" entry in openmw.desktop +Bug #426: Do not use hardcoded splash images +Bug #431: Don't use markers for raycast +Bug #432: Crash after picking up items from an NPC +Feature #21/#95: Sleeping/resting +Feature #61: Alchemy Skill +Feature #68: Death +Feature #69/#86: Spell Creation +Feature #72/#84: Travel +Feature #76: Global Map, 1st Layer +Feature #120: Trainer Window +Feature #152: Skill Increase from Skill Books +Feature #160: Record Saving +Task #400: Review GMST access + +0.18.0 + +Bug #310: Button of the "preferences menu" are too small +Bug #361: Hand-to-hand skill is always 100 +Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to +Bug #372: playSound3D uses original coordinates instead of current coordinates. +Bug #373: Static OGRE build faulty +Bug #375: Alt-tab toggle view +Bug #376: Screenshots are disable +Bug #378: Exception when drinking self-made potions +Bug #380: Cloth visibility problem +Bug #384: Weird character on doors tooltip. +Bug #398: Some objects do not collide in MW, but do so in OpenMW +Feature #22: Implement level-up +Feature #36: Hide Marker +Feature #88: Hotkey Window +Feature #91: Level-Up Dialogue +Feature #118: Keyboard and Mouse-Button bindings +Feature #119: Spell Buying Window +Feature #133: Handle resources across multiple data directories +Feature #134: Generate a suitable default-value for --data-local +Feature #292: Object Movement/Creation Script Instructions +Feature #340: AIPackage data structures +Feature #356: Ingredients use +Feature #358: Input system rewrite +Feature #370: Target handling in actions +Feature #379: Door markers on the local map +Feature #389: AI framework +Feature #395: Using keys to open doors / containers +Feature #396: Loading screens +Feature #397: Inventory avatar image and race selection head preview +Task #339: Move sounds into Action + +0.17.0 + +Bug #225: Valgrind reports about 40MB of leaked memory +Bug #241: Some physics meshes still don't match +Bug #248: Some textures are too dark +Bug #300: Dependency on proprietary CG toolkit +Bug #302: Some objects don't collide although they should +Bug #308: Freeze in Balmora, Meldor: Armorer +Bug #313: openmw without a ~/.config/openmw folder segfault. +Bug #317: adding non-existing spell via console locks game +Bug #318: Wrong character normals +Bug #341: Building with Ogre Debug libraries does not use debug version of plugins +Bug #347: Crash when running openmw with --start="XYZ" +Bug #353: FindMyGUI.cmake breaks path on Windows +Bug #359: WindowManager throws exception at destruction +Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation +Feature #33: Allow objects to cross cell-borders +Feature #59: Dropping Items (replaced stopgap implementation with a proper one) +Feature #93: Main Menu +Feature #96/329/330/331/332/333: Player Control +Feature #180: Object rotation and scaling. +Feature #272: Incorrect NIF material sharing +Feature #314: Potion usage +Feature #324: Skill Gain +Feature #342: Drain/fortify dynamic stats/attributes magic effects +Feature #350: Allow console only script instructions +Feature #352: Run scripts in console on startup +Task #107: Refactor mw*-subsystems +Task #325: Make CreatureStats into a class +Task #345: Use Ogre's animation system +Task #351: Rewrite Action class to support automatic sound playing + +0.16.0 + +Bug #250: OpenMW launcher erratic behaviour +Bug #270: Crash because of underwater effect on OS X +Bug #277: Auto-equipping in some cells not working +Bug #294: Container GUI ignores disabled inventory menu +Bug #297: Stats review dialog shows all skills and attribute values as 0 +Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses +Bug #299: Crash in World::disable +Bug #306: Non-existent ~/.config/openmw "crash" the launcher. +Bug #307: False "Data Files" location make the launcher "crash" +Feature #81: Spell Window +Feature #85: Alchemy Window +Feature #181: Support for x.y script syntax +Feature #242: Weapon and Spell icons +Feature #254: Ingame settings window +Feature #293: Allow "stacking" game modes +Feature #295: Class creation dialog tooltips +Feature #296: Clicking on the HUD elements should show/hide the respective window +Feature #301: Direction after using a Teleport Door +Feature #303: Allow object selection in the console +Feature #305: Allow the use of = as a synonym for == +Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts +Task #176: Restructure enabling/disabling of MW-references +Task #283: Integrate ogre.cfg file in settings file +Task #290: Auto-Close MW-reference related GUI windows + +0.15.0 + +Bug #5: Physics reimplementation (fixes various issues) +Bug #258: Resizing arrow's background is not transparent +Bug #268: Widening the stats window in X direction causes layout problems +Bug #269: Topic pane in dialgoue window is too small for some longer topics +Bug #271: Dialog choices are sorted incorrectly +Bug #281: The single quote character is not rendered on dialog windows +Bug #285: Terrain not handled properly in cells that are not predefined +Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs +Feature #15: Collision with Terrain +Feature #17: Inventory-, Container- and Trade-Windows +Feature #44: Floating Labels above Focussed Objects +Feature #80: Tooltips +Feature #83: Barter Dialogue +Feature #90: Book and Scroll Windows +Feature #156: Item Stacking in Containers +Feature #213: Pulsating lights +Feature #218: Feather & Burden +Feature #256: Implement magic effect bookkeeping +Feature #259: Add missing information to Stats window +Feature #260: Correct case for dialogue topics +Feature #280: GUI texture atlasing +Feature #291: Ability to use GMST strings from GUI layout files +Task #255: Make MWWorld::Environment into a singleton + +0.14.0 + +Bug #1: Meshes rendered with wrong orientation +Bug #6/Task #220: Picking up small objects doesn't always work +Bug #127: tcg doesn't work +Bug #178: Compablity problems with Ogre 1.8.0 RC 1 +Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI +Bug #227: Terrain crashes when moving away from predefined cells +Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces +Bug #235: TGA texture loading problem +Bug #246: wireframe mode does not work in water +Feature #8/#232: Water Rendering +Feature #13: Terrain Rendering +Feature #37: Render Path Grid +Feature #66: Factions +Feature #77: Local Map +Feature #78: Compass/Mini-Map +Feature #97: Render Clothing/Armour +Feature #121: Window Pinning +Feature #205: Auto equip +Feature #217: Contiainer should track changes to its content +Feature #221: NPC Dialogue Window Enhancements +Feature #233: Game settings manager +Feature #240: Spell List and selected spell (no GUI yet) +Feature #243: Draw State +Task #113: Morrowind.ini Importer +Task #215: Refactor the sound code +Task #216: Update MyGUI + +0.13.0 + +Bug #145: Fixed sound problems after cell change +Bug #179: Pressing space in console triggers activation +Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux +Bug #189: ASCII 16 character added to console on it's activation on Mac OS X +Bug #190: Case Folding fails with music files +Bug #192: Keypresses write Text into Console no matter which gui element is active +Bug #196: Collision shapes out of place +Bug #202: ESMTool doesn't not work with localised ESM files anymore +Bug #203: Torch lights only visible on short distance +Bug #207: Ogre.log not written +Bug #209: Sounds do not play +Bug #210: Ogre crash at Dren plantation +Bug #214: Unsupported file format version +Bug #222: Launcher is writing openmw.cfg file to wrong location +Feature #9: NPC Dialogue Window +Feature #16/42: New sky/weather implementation +Feature #40: Fading +Feature #48: NPC Dialogue System +Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) +Feature #161: Load REC_PGRD records +Feature #195: Wireframe-mode +Feature #198/199: Various sound effects +Feature #206: Allow picking data path from launcher if non is set +Task #108: Refactor window manager class +Task #172: Sound Manager Cleanup +Task #173: Create OpenEngine systems in the appropriate manager classes +Task #184: Adjust MSVC and gcc warning levels +Task #185: RefData rewrite +Task #201: Workaround for transparency issues +Task #208: silenced esm_reader.hpp warning + +0.12.0 + +Bug #154: FPS Drop +Bug #169: Local scripts continue running if associated object is deleted +Bug #174: OpenMW fails to start if the config directory doesn't exist +Bug #187: Missing lighting +Bug #188: Lights without a mesh are not rendered +Bug #191: Taking screenshot causes crash when running installed +Feature #28: Sort out the cell load problem +Feature #31: Allow the player to move away from pre-defined cells +Feature #35: Use alternate storage location for modified object position +Feature #45: NPC animations +Feature #46: Creature Animation +Feature #89: Basic Journal Window +Feature #110: Automatically pick up the path of existing MW-installations +Feature #183: More FPS display settings +Task #19: Refactor engine class +Task #109/Feature #162: Automate Packaging +Task #112: Catch exceptions thrown in input handling functions +Task #128/#168: Cleanup Configuration File Handling +Task #131: NPC Activation doesn't work properly +Task #144: MWRender cleanup +Task #155: cmake cleanup + +0.11.1 + +Bug #2: Resources loading doesn't work outside of bsa files +Bug #3: GUI does not render non-English characters +Bug #7: openmw.cfg location doesn't match +Bug #124: The TCL alias for ToggleCollision is missing. +Bug #125: Some command line options can't be used from a .cfg file +Bug #126: Toggle-type script instructions are less verbose compared with original MW +Bug #130: NPC-Record Loading fails for some NPCs +Bug #167: Launcher sets invalid parameters in ogre config +Feature #10: Journal +Feature #12: Rendering Optimisations +Feature #23: Change Launcher GUI to a tabbed interface +Feature #24: Integrate the OGRE settings window into the launcher +Feature #25: Determine openmw.cfg location (Launcher) +Feature #26: Launcher Profiles +Feature #79: MessageBox +Feature #116: Tab-Completion in Console +Feature #132: --data-local and multiple --data +Feature #143: Non-Rendering Performance-Optimisations +Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs +Feature #157: Version Handling +Task #14: Replace tabs with 4 spaces +Task #18: Move components from global namespace into their own namespace +Task #123: refactor header files in components/esm + +0.10.0 + +* NPC dialogue window (not functional yet) +* Collisions with objects +* Refactor the PlayerPos class +* Adjust file locations +* CMake files and test linking for Bullet +* Replace Ogre raycasting test for activation with something more precise +* Adjust player movement according to collision results +* FPS display +* Various Portability Improvements +* Mac OS X support is back! + +0.9.0 + +* Exterior cells loading, unloading and management +* Character Creation GUI +* Character creation +* Make cell names case insensitive when doing internal lookups +* Music player +* NPCs rendering + +0.8.0 + +* GUI +* Complete and working script engine +* In game console +* Sky rendering +* Sound and music +* Tons of smaller stuff + +0.7.0 + +* This release is a complete rewrite in C++. +* All D code has been culled, and all modules have been rewritten. +* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. + +0.6.0 + +* Coded a GUI system using MyGUI +* Skinned MyGUI to look like Morrowind (work in progress) +* Integrated the Monster script engine +* Rewrote some functions into script code +* Very early MyGUI < > Monster binding +* Fixed Windows sound problems (replaced old openal32.dll) + +0.5.0 + +* Collision detection with Bullet +* Experimental walk & fall character physics +* New key bindings: + * t toggle physics mode (walking, flying, ghost), + * n night eye, brightens the scene +* Fixed incompatability with DMD 1.032 and newer compilers +* * (thanks to tomqyp) +* Various minor changes and updates + +0.4.0 + +* Switched from Audiere to OpenAL +* * (BIG thanks to Chris Robinson) +* Added complete Makefile (again) as a alternative build tool +* More realistic lighting (thanks again to Chris Robinson) +* Various localization fixes tested with Russian and French versions +* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' +* Added ns option to disable sound, for debugging +* Various bug fixes +* Cosmetic changes to placate gdc Wall + +0.3.0 + +* Built and tested on Windows XP +* Partial support for FreeBSD (exceptions do not work) +* You no longer have to download Monster separately +* Made an alternative for building without DSSS (but DSSS still works) +* Renamed main program from 'morro' to 'openmw' +* Made the config system more robust +* Added oc switch for showing Ogre config window on startup +* Removed some config files, these are auto generated when missing. +* Separated plugins.cfg into linux and windows versions. +* Updated Makefile and sources for increased portability +* confirmed to work against OIS 1.0.0 (Ubuntu repository package) + +0.2.0 + +* Compiles with gdc +* Switched to DSSS for building D code +* Includes the program esmtool + +0.1.0 + +first release From 1bd2664cb04b8b0a6c64f1312d43cf9195b07cf4 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 23 Feb 2014 18:42:40 +1100 Subject: [PATCH 24/36] aicombat pathfinding workaround --- apps/openmw/mwmechanics/aicombat.cpp | 35 ++++++++++++++++--------- apps/openmw/mwmechanics/pathfinding.cpp | 2 ++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6a49f2f5e..91c77702e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -240,16 +240,20 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); + buildNewPath(actor); //not guaranteed, check before use //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); - else - mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + //if buildNewPath() failed leave mTargetAngle unchanged + if(!mPathFinder.getPath().empty()) + { + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + } mRotate = true; mMovement.mPosition[1] = 1; @@ -300,9 +304,13 @@ namespace MWMechanics dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + float dist = -1; //hack to indicate first time, to construct a new path + if(!mPathFinder.getPath().empty()) + { + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + } float targetPosThreshold; bool isOutside = actor.getCell()->mCell->isExterior(); @@ -311,7 +319,7 @@ namespace MWMechanics else targetPosThreshold = 100; - if(dist > targetPosThreshold) + if((dist < 0) || (dist > targetPosThreshold)) { //construct new path only if target has moved away more than on ESM::Position pos = actor.getRefData().getPosition(); @@ -332,8 +340,11 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; + if(!mPathFinder.getPath().empty()) + { + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; + } } } } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 4407363a6..5996eb079 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,6 +391,8 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { + if (path.size() < 2) + return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); From 424617225118546b18220e3752f02f243580d3cb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 11:52:13 +0100 Subject: [PATCH 25/36] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 561931cde..601255763 100644 --- a/credits.txt +++ b/credits.txt @@ -20,6 +20,7 @@ Artem Kotsynyak (greye) athile Britt Mathis (galdor557) BrotherBrick +cc9cii Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) From 76c1d60d2395ecd2ef8d670e2f71f54f3d57eef0 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 22 Feb 2014 01:17:58 +0400 Subject: [PATCH 26/36] Fixed OpenCS build on OS X --- apps/opencs/view/filter/editwidget.hpp | 2 +- apps/opencs/view/filter/filterbox.hpp | 2 +- apps/opencs/view/filter/recordfilterbox.hpp | 2 +- apps/opencs/view/world/tablesubview.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp index 555b6d360..e7e34b8e9 100644 --- a/apps/opencs/view/filter/editwidget.hpp +++ b/apps/opencs/view/filter/editwidget.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../../model/filter/parser.hpp" #include "../../model/filter/node.hpp" diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 3817d5e70..5954035fc 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "../../model/filter/node.hpp" #include "../../model/world/universalid.hpp" diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index 3638dc6c3..fa5c9c3c2 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 1f67e0262..b3c253919 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -#include +#include class QModelIndex; From 85c467f00f01e14b8a3cecffe1d722eaed7df3bf Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 24 Feb 2014 18:47:09 +1100 Subject: [PATCH 27/36] Minor cleanup for aicombat pathfinding workaround. --- apps/openmw/mwmechanics/aicombat.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 91c77702e..17a624f0f 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -240,12 +240,12 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); //not guaranteed, check before use + buildNewPath(actor); //may fail to build a path, check before use //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //if buildNewPath() failed leave mTargetAngle unchanged + //if no new path leave mTargetAngle unchanged if(!mPathFinder.getPath().empty()) { //try shortcut @@ -253,8 +253,8 @@ namespace MWMechanics mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); else mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; } - mRotate = true; mMovement.mPosition[1] = 1; mReadyToAttack = false; From 5b48ca114fee7742ad8bc8f18f0baaa9ced7e945 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 25 Feb 2014 07:41:10 +1100 Subject: [PATCH 28/36] aicombat pathfinding fix - check the correct list --- apps/openmw/mwmechanics/pathfinding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 5996eb079..8dbdd8770 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,7 +391,7 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { - if (path.size() < 2) + if (mPath.size() < 2) return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); From 2421f23c2f83ab7d8d2d2b153fcfb43cb761aaee Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 27 Feb 2014 23:59:23 +1100 Subject: [PATCH 29/36] cleanup the mess... --- apps/opencs/model/world/columnbase.hpp | 254 +-- apps/opencs/model/world/tablemimedata.cpp | 890 ++++----- apps/opencs/model/world/tablemimedata.hpp | 124 +- apps/opencs/view/filter/editwidget.cpp | 406 ++-- apps/opencs/view/filter/editwidget.hpp | 114 +- apps/opencs/view/filter/filterbox.cpp | 100 +- apps/opencs/view/filter/filterbox.hpp | 90 +- apps/opencs/view/filter/recordfilterbox.cpp | 64 +- apps/opencs/view/filter/recordfilterbox.hpp | 74 +- apps/opencs/view/world/table.cpp | 1090 +++++------ apps/opencs/view/world/table.hpp | 254 +-- apps/opencs/view/world/tablesubview.cpp | 262 +-- apps/opencs/view/world/tablesubview.hpp | 126 +- apps/openmw/mwmechanics/aicombat.cpp | 37 +- apps/openmw/mwmechanics/pathfinding.cpp | 2 + apps/openmw/mwstate/statemanagerimp.cpp | 704 +++---- components/esm/esmwriter.cpp | 406 ++-- components/esm/esmwriter.hpp | 228 +-- credits.txt | 1 + readme.txt | 1902 +++++++++---------- 20 files changed, 3571 insertions(+), 3557 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 4ce45ffe8..e04333608 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -1,127 +1,127 @@ -#ifndef CSM_WOLRD_COLUMNBASE_H -#define CSM_WOLRD_COLUMNBASE_H - -#include - -#include -#include - -#include "record.hpp" - -namespace CSMWorld -{ - struct ColumnBase - { - enum Roles - { - Role_Flags = Qt::UserRole, - Role_Display = Qt::UserRole+1 - }; - - enum Flags - { - Flag_Table = 1, // column should be displayed in table view - Flag_Dialogue = 2 // column should be displayed in dialogue view - }; - - enum Display - { - Display_None, //Do not use - Display_String, - - //CONCRETE TYPES STARTS HERE - Display_Skill, - Display_Class, - Display_Faction, - Display_Race, - Display_Sound, - Display_Region, - Display_Birthsign, - Display_Spell, - Display_Cell, - Display_Referenceable, - Display_Activator, - Display_Potion, - Display_Apparatus, - Display_Armor, - Display_Book, - Display_Clothing, - Display_Container, - Display_Creature, - Display_Door, - Display_Ingredient, - Display_CreatureLevelledList, - Display_ItemLevelledList, - Display_Light, - Display_Lockpick, - Display_Miscellaneous, - Display_Npc, - Display_Probe, - Display_Repair, - Display_Static, - Display_Weapon, - Display_Reference, - Display_Filter, - Display_Topic, - Display_Journal, - Display_TopicInfo, - Display_JournalInfo, - Display_Scene, - //CONCRETE TYPES ENDS HERE - - Display_Integer, - Display_Float, - Display_Var, - Display_GmstVarType, - Display_GlobalVarType, - Display_Specialisation, - Display_Attribute, - Display_Boolean, - Display_SpellType, - Display_Script, - Display_ApparatusType, - Display_ArmorType, - Display_ClothingType, - Display_CreatureType, - Display_WeaponType, - Display_RecordState, - Display_RefRecordType, - Display_DialogueType, - Display_QuestStatusType, - Display_Gender - }; - - int mColumnId; - int mFlags; - Display mDisplayType; - - ColumnBase (int columnId, Display displayType, int flag); - - virtual ~ColumnBase(); - - virtual bool isEditable() const = 0; - - virtual bool isUserEditable() const; - ///< Can this column be edited directly by the user? - - virtual std::string getTitle() const; - }; - - template - struct Column : public ColumnBase - { - int mFlags; - - Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (columnId, displayType, flags) {} - - virtual QVariant get (const Record& record) const = 0; - - virtual void set (Record& record, const QVariant& data) - { - throw std::logic_error ("Column " + getTitle() + " is not editable"); - } - }; -} - -#endif +#ifndef CSM_WOLRD_COLUMNBASE_H +#define CSM_WOLRD_COLUMNBASE_H + +#include + +#include +#include + +#include "record.hpp" + +namespace CSMWorld +{ + struct ColumnBase + { + enum Roles + { + Role_Flags = Qt::UserRole, + Role_Display = Qt::UserRole+1 + }; + + enum Flags + { + Flag_Table = 1, // column should be displayed in table view + Flag_Dialogue = 2 // column should be displayed in dialogue view + }; + + enum Display + { + Display_None, //Do not use + Display_String, + + //CONCRETE TYPES STARTS HERE + Display_Skill, + Display_Class, + Display_Faction, + Display_Race, + Display_Sound, + Display_Region, + Display_Birthsign, + Display_Spell, + Display_Cell, + Display_Referenceable, + Display_Activator, + Display_Potion, + Display_Apparatus, + Display_Armor, + Display_Book, + Display_Clothing, + Display_Container, + Display_Creature, + Display_Door, + Display_Ingredient, + Display_CreatureLevelledList, + Display_ItemLevelledList, + Display_Light, + Display_Lockpick, + Display_Miscellaneous, + Display_Npc, + Display_Probe, + Display_Repair, + Display_Static, + Display_Weapon, + Display_Reference, + Display_Filter, + Display_Topic, + Display_Journal, + Display_TopicInfo, + Display_JournalInfo, + Display_Scene, + //CONCRETE TYPES ENDS HERE + + Display_Integer, + Display_Float, + Display_Var, + Display_GmstVarType, + Display_GlobalVarType, + Display_Specialisation, + Display_Attribute, + Display_Boolean, + Display_SpellType, + Display_Script, + Display_ApparatusType, + Display_ArmorType, + Display_ClothingType, + Display_CreatureType, + Display_WeaponType, + Display_RecordState, + Display_RefRecordType, + Display_DialogueType, + Display_QuestStatusType, + Display_Gender + }; + + int mColumnId; + int mFlags; + Display mDisplayType; + + ColumnBase (int columnId, Display displayType, int flag); + + virtual ~ColumnBase(); + + virtual bool isEditable() const = 0; + + virtual bool isUserEditable() const; + ///< Can this column be edited directly by the user? + + virtual std::string getTitle() const; + }; + + template + struct Column : public ColumnBase + { + int mFlags; + + Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (columnId, displayType, flags) {} + + virtual QVariant get (const Record& record) const = 0; + + virtual void set (Record& record, const QVariant& data) + { + throw std::logic_error ("Column " + getTitle() + " is not editable"); + } + }; +} + +#endif diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index f5e1acfb4..b56c9c8c2 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1,446 +1,446 @@ -#include "tablemimedata.hpp" -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : -mDocument(document) -{ - mUniversalId.push_back (id); - mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); -} - -CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : - mUniversalId (id), mDocument(document) -{ - for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) - { - mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); - } -} - -QStringList CSMWorld::TableMimeData::formats() const -{ - return mObjectsFormats; -} - -CSMWorld::TableMimeData::~TableMimeData() -{ -} - -std::string CSMWorld::TableMimeData::getIcon() const -{ - if (mUniversalId.empty()) - { - throw ("TableMimeData holds no UniversalId"); - } - - std::string tmpIcon; - bool firstIteration = true; - - for (unsigned i = 0; i < mUniversalId.size(); ++i) - { - if (firstIteration) - { - firstIteration = false; - tmpIcon = mUniversalId[i].getIcon(); - continue; - } - - if (tmpIcon != mUniversalId[i].getIcon()) - { - return ":/multitype.png"; //icon stolen from gnome - } - - tmpIcon = mUniversalId[i].getIcon(); - } - - return mUniversalId.begin()->getIcon(); //All objects are of the same type; -} - -std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const -{ - return mUniversalId; -} - - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return true; - } - } - - return false; -} - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return true; - } - } - - return false; -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const -{ - return &document == &mDocument; -} - -CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) -{ - switch (type) - { - case CSMWorld::ColumnBase::Display_Race: - return CSMWorld::UniversalId::Type_Race; - - - case CSMWorld::ColumnBase::Display_Skill: - return CSMWorld::UniversalId::Type_Skill; - - - case CSMWorld::ColumnBase::Display_Class: - return CSMWorld::UniversalId::Type_Class; - - - case CSMWorld::ColumnBase::Display_Faction: - return CSMWorld::UniversalId::Type_Faction; - - - case CSMWorld::ColumnBase::Display_Sound: - return CSMWorld::UniversalId::Type_Sound; - - - case CSMWorld::ColumnBase::Display_Region: - return CSMWorld::UniversalId::Type_Region; - - - case CSMWorld::ColumnBase::Display_Birthsign: - return CSMWorld::UniversalId::Type_Birthsign; - - - case CSMWorld::ColumnBase::Display_Spell: - return CSMWorld::UniversalId::Type_Spell; - - - case CSMWorld::ColumnBase::Display_Cell: - return CSMWorld::UniversalId::Type_Cell; - - - case CSMWorld::ColumnBase::Display_Referenceable: - return CSMWorld::UniversalId::Type_Referenceable; - - - case CSMWorld::ColumnBase::Display_Activator: - return CSMWorld::UniversalId::Type_Activator; - - - case CSMWorld::ColumnBase::Display_Potion: - return CSMWorld::UniversalId::Type_Potion; - - - case CSMWorld::ColumnBase::Display_Apparatus: - return CSMWorld::UniversalId::Type_Apparatus; - - - case CSMWorld::ColumnBase::Display_Armor: - return CSMWorld::UniversalId::Type_Armor; - - - case CSMWorld::ColumnBase::Display_Book: - return CSMWorld::UniversalId::Type_Book; - - - case CSMWorld::ColumnBase::Display_Clothing: - return CSMWorld::UniversalId::Type_Clothing; - - - case CSMWorld::ColumnBase::Display_Container: - return CSMWorld::UniversalId::Type_Container; - - - case CSMWorld::ColumnBase::Display_Creature: - return CSMWorld::UniversalId::Type_Creature; - - - case CSMWorld::ColumnBase::Display_Door: - return CSMWorld::UniversalId::Type_Door; - - - case CSMWorld::ColumnBase::Display_Ingredient: - return CSMWorld::UniversalId::Type_Ingredient; - - - case CSMWorld::ColumnBase::Display_CreatureLevelledList: - return CSMWorld::UniversalId::Type_CreatureLevelledList; - - - case CSMWorld::ColumnBase::Display_ItemLevelledList: - return CSMWorld::UniversalId::Type_ItemLevelledList; - - - case CSMWorld::ColumnBase::Display_Light: - return CSMWorld::UniversalId::Type_Light; - - - case CSMWorld::ColumnBase::Display_Lockpick: - return CSMWorld::UniversalId::Type_Lockpick; - - - case CSMWorld::ColumnBase::Display_Miscellaneous: - return CSMWorld::UniversalId::Type_Miscellaneous; - - - case CSMWorld::ColumnBase::Display_Npc: - return CSMWorld::UniversalId::Type_Npc; - - - case CSMWorld::ColumnBase::Display_Probe: - return CSMWorld::UniversalId::Type_Probe; - - - case CSMWorld::ColumnBase::Display_Repair: - return CSMWorld::UniversalId::Type_Repair; - - - case CSMWorld::ColumnBase::Display_Static: - return CSMWorld::UniversalId::Type_Static; - - - case CSMWorld::ColumnBase::Display_Weapon: - return CSMWorld::UniversalId::Type_Weapon; - - - case CSMWorld::ColumnBase::Display_Reference: - return CSMWorld::UniversalId::Type_Reference; - - - case CSMWorld::ColumnBase::Display_Filter: - return CSMWorld::UniversalId::Type_Filter; - - - case CSMWorld::ColumnBase::Display_Topic: - return CSMWorld::UniversalId::Type_Topic; - - - case CSMWorld::ColumnBase::Display_Journal: - return CSMWorld::UniversalId::Type_Journal; - - - case CSMWorld::ColumnBase::Display_TopicInfo: - return CSMWorld::UniversalId::Type_TopicInfo; - - - case CSMWorld::ColumnBase::Display_JournalInfo: - return CSMWorld::UniversalId::Type_JournalInfo; - - - case CSMWorld::ColumnBase::Display_Scene: - return CSMWorld::UniversalId::Type_Scene; - - - case CSMWorld::ColumnBase::Display_Script: - return CSMWorld::UniversalId::Type_Script; - - - default: - return CSMWorld::UniversalId::Type_None; - - } -} - -CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) -{ - switch (type) - { - case CSMWorld::UniversalId::Type_Race: - return CSMWorld::ColumnBase::Display_Race; - - - case CSMWorld::UniversalId::Type_Skill: - return CSMWorld::ColumnBase::Display_Skill; - - - case CSMWorld::UniversalId::Type_Class: - return CSMWorld::ColumnBase::Display_Class; - - - case CSMWorld::UniversalId::Type_Faction: - return CSMWorld::ColumnBase::Display_Faction; - - - case CSMWorld::UniversalId::Type_Sound: - return CSMWorld::ColumnBase::Display_Sound; - - - case CSMWorld::UniversalId::Type_Region: - return CSMWorld::ColumnBase::Display_Region; - - - case CSMWorld::UniversalId::Type_Birthsign: - return CSMWorld::ColumnBase::Display_Birthsign; - - - case CSMWorld::UniversalId::Type_Spell: - return CSMWorld::ColumnBase::Display_Spell; - - - case CSMWorld::UniversalId::Type_Cell: - return CSMWorld::ColumnBase::Display_Cell; - - - case CSMWorld::UniversalId::Type_Referenceable: - return CSMWorld::ColumnBase::Display_Referenceable; - - - case CSMWorld::UniversalId::Type_Activator: - return CSMWorld::ColumnBase::Display_Activator; - - - case CSMWorld::UniversalId::Type_Potion: - return CSMWorld::ColumnBase::Display_Potion; - - - case CSMWorld::UniversalId::Type_Apparatus: - return CSMWorld::ColumnBase::Display_Apparatus; - - - case CSMWorld::UniversalId::Type_Armor: - return CSMWorld::ColumnBase::Display_Armor; - - - case CSMWorld::UniversalId::Type_Book: - return CSMWorld::ColumnBase::Display_Book; - - - case CSMWorld::UniversalId::Type_Clothing: - return CSMWorld::ColumnBase::Display_Clothing; - - - case CSMWorld::UniversalId::Type_Container: - return CSMWorld::ColumnBase::Display_Container; - - - case CSMWorld::UniversalId::Type_Creature: - return CSMWorld::ColumnBase::Display_Creature; - - - case CSMWorld::UniversalId::Type_Door: - return CSMWorld::ColumnBase::Display_Door; - - - case CSMWorld::UniversalId::Type_Ingredient: - return CSMWorld::ColumnBase::Display_Ingredient; - - - case CSMWorld::UniversalId::Type_CreatureLevelledList: - return CSMWorld::ColumnBase::Display_CreatureLevelledList; - - - case CSMWorld::UniversalId::Type_ItemLevelledList: - return CSMWorld::ColumnBase::Display_ItemLevelledList; - - - case CSMWorld::UniversalId::Type_Light: - return CSMWorld::ColumnBase::Display_Light; - - - case CSMWorld::UniversalId::Type_Lockpick: - return CSMWorld::ColumnBase::Display_Lockpick; - - - case CSMWorld::UniversalId::Type_Miscellaneous: - return CSMWorld::ColumnBase::Display_Miscellaneous; - - - case CSMWorld::UniversalId::Type_Npc: - return CSMWorld::ColumnBase::Display_Npc; - - - case CSMWorld::UniversalId::Type_Probe: - return CSMWorld::ColumnBase::Display_Probe; - - - case CSMWorld::UniversalId::Type_Repair: - return CSMWorld::ColumnBase::Display_Repair; - - - case CSMWorld::UniversalId::Type_Static: - return CSMWorld::ColumnBase::Display_Static; - - - case CSMWorld::UniversalId::Type_Weapon: - return CSMWorld::ColumnBase::Display_Weapon; - - - case CSMWorld::UniversalId::Type_Reference: - return CSMWorld::ColumnBase::Display_Reference; - - - case CSMWorld::UniversalId::Type_Filter: - return CSMWorld::ColumnBase::Display_Filter; - - - case CSMWorld::UniversalId::Type_Topic: - return CSMWorld::ColumnBase::Display_Topic; - - - case CSMWorld::UniversalId::Type_Journal: - return CSMWorld::ColumnBase::Display_Journal; - - - case CSMWorld::UniversalId::Type_TopicInfo: - return CSMWorld::ColumnBase::Display_TopicInfo; - - - case CSMWorld::UniversalId::Type_JournalInfo: - return CSMWorld::ColumnBase::Display_JournalInfo; - - - case CSMWorld::UniversalId::Type_Scene: - return CSMWorld::ColumnBase::Display_Scene; - - - case CSMWorld::UniversalId::Type_Script: - return CSMWorld::ColumnBase::Display_Script; - - - default: - return CSMWorld::ColumnBase::Display_None; - } +#include "tablemimedata.hpp" +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : +mDocument(document) +{ + mUniversalId.push_back (id); + mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); +} + +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : + mUniversalId (id), mDocument(document) +{ + for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) + { + mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); + } +} + +QStringList CSMWorld::TableMimeData::formats() const +{ + return mObjectsFormats; +} + +CSMWorld::TableMimeData::~TableMimeData() +{ +} + +std::string CSMWorld::TableMimeData::getIcon() const +{ + if (mUniversalId.empty()) + { + throw ("TableMimeData holds no UniversalId"); + } + + std::string tmpIcon; + bool firstIteration = true; + + for (unsigned i = 0; i < mUniversalId.size(); ++i) + { + if (firstIteration) + { + firstIteration = false; + tmpIcon = mUniversalId[i].getIcon(); + continue; + } + + if (tmpIcon != mUniversalId[i].getIcon()) + { + return ":/multitype.png"; //icon stolen from gnome + } + + tmpIcon = mUniversalId[i].getIcon(); + } + + return mUniversalId.begin()->getIcon(); //All objects are of the same type; +} + +std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const +{ + return mUniversalId; +} + + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return true; + } + } + + return false; +} + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return true; + } + } + + return false; +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const +{ + return &document == &mDocument; +} + +CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) +{ + switch (type) + { + case CSMWorld::ColumnBase::Display_Race: + return CSMWorld::UniversalId::Type_Race; + + + case CSMWorld::ColumnBase::Display_Skill: + return CSMWorld::UniversalId::Type_Skill; + + + case CSMWorld::ColumnBase::Display_Class: + return CSMWorld::UniversalId::Type_Class; + + + case CSMWorld::ColumnBase::Display_Faction: + return CSMWorld::UniversalId::Type_Faction; + + + case CSMWorld::ColumnBase::Display_Sound: + return CSMWorld::UniversalId::Type_Sound; + + + case CSMWorld::ColumnBase::Display_Region: + return CSMWorld::UniversalId::Type_Region; + + + case CSMWorld::ColumnBase::Display_Birthsign: + return CSMWorld::UniversalId::Type_Birthsign; + + + case CSMWorld::ColumnBase::Display_Spell: + return CSMWorld::UniversalId::Type_Spell; + + + case CSMWorld::ColumnBase::Display_Cell: + return CSMWorld::UniversalId::Type_Cell; + + + case CSMWorld::ColumnBase::Display_Referenceable: + return CSMWorld::UniversalId::Type_Referenceable; + + + case CSMWorld::ColumnBase::Display_Activator: + return CSMWorld::UniversalId::Type_Activator; + + + case CSMWorld::ColumnBase::Display_Potion: + return CSMWorld::UniversalId::Type_Potion; + + + case CSMWorld::ColumnBase::Display_Apparatus: + return CSMWorld::UniversalId::Type_Apparatus; + + + case CSMWorld::ColumnBase::Display_Armor: + return CSMWorld::UniversalId::Type_Armor; + + + case CSMWorld::ColumnBase::Display_Book: + return CSMWorld::UniversalId::Type_Book; + + + case CSMWorld::ColumnBase::Display_Clothing: + return CSMWorld::UniversalId::Type_Clothing; + + + case CSMWorld::ColumnBase::Display_Container: + return CSMWorld::UniversalId::Type_Container; + + + case CSMWorld::ColumnBase::Display_Creature: + return CSMWorld::UniversalId::Type_Creature; + + + case CSMWorld::ColumnBase::Display_Door: + return CSMWorld::UniversalId::Type_Door; + + + case CSMWorld::ColumnBase::Display_Ingredient: + return CSMWorld::UniversalId::Type_Ingredient; + + + case CSMWorld::ColumnBase::Display_CreatureLevelledList: + return CSMWorld::UniversalId::Type_CreatureLevelledList; + + + case CSMWorld::ColumnBase::Display_ItemLevelledList: + return CSMWorld::UniversalId::Type_ItemLevelledList; + + + case CSMWorld::ColumnBase::Display_Light: + return CSMWorld::UniversalId::Type_Light; + + + case CSMWorld::ColumnBase::Display_Lockpick: + return CSMWorld::UniversalId::Type_Lockpick; + + + case CSMWorld::ColumnBase::Display_Miscellaneous: + return CSMWorld::UniversalId::Type_Miscellaneous; + + + case CSMWorld::ColumnBase::Display_Npc: + return CSMWorld::UniversalId::Type_Npc; + + + case CSMWorld::ColumnBase::Display_Probe: + return CSMWorld::UniversalId::Type_Probe; + + + case CSMWorld::ColumnBase::Display_Repair: + return CSMWorld::UniversalId::Type_Repair; + + + case CSMWorld::ColumnBase::Display_Static: + return CSMWorld::UniversalId::Type_Static; + + + case CSMWorld::ColumnBase::Display_Weapon: + return CSMWorld::UniversalId::Type_Weapon; + + + case CSMWorld::ColumnBase::Display_Reference: + return CSMWorld::UniversalId::Type_Reference; + + + case CSMWorld::ColumnBase::Display_Filter: + return CSMWorld::UniversalId::Type_Filter; + + + case CSMWorld::ColumnBase::Display_Topic: + return CSMWorld::UniversalId::Type_Topic; + + + case CSMWorld::ColumnBase::Display_Journal: + return CSMWorld::UniversalId::Type_Journal; + + + case CSMWorld::ColumnBase::Display_TopicInfo: + return CSMWorld::UniversalId::Type_TopicInfo; + + + case CSMWorld::ColumnBase::Display_JournalInfo: + return CSMWorld::UniversalId::Type_JournalInfo; + + + case CSMWorld::ColumnBase::Display_Scene: + return CSMWorld::UniversalId::Type_Scene; + + + case CSMWorld::ColumnBase::Display_Script: + return CSMWorld::UniversalId::Type_Script; + + + default: + return CSMWorld::UniversalId::Type_None; + + } +} + +CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) +{ + switch (type) + { + case CSMWorld::UniversalId::Type_Race: + return CSMWorld::ColumnBase::Display_Race; + + + case CSMWorld::UniversalId::Type_Skill: + return CSMWorld::ColumnBase::Display_Skill; + + + case CSMWorld::UniversalId::Type_Class: + return CSMWorld::ColumnBase::Display_Class; + + + case CSMWorld::UniversalId::Type_Faction: + return CSMWorld::ColumnBase::Display_Faction; + + + case CSMWorld::UniversalId::Type_Sound: + return CSMWorld::ColumnBase::Display_Sound; + + + case CSMWorld::UniversalId::Type_Region: + return CSMWorld::ColumnBase::Display_Region; + + + case CSMWorld::UniversalId::Type_Birthsign: + return CSMWorld::ColumnBase::Display_Birthsign; + + + case CSMWorld::UniversalId::Type_Spell: + return CSMWorld::ColumnBase::Display_Spell; + + + case CSMWorld::UniversalId::Type_Cell: + return CSMWorld::ColumnBase::Display_Cell; + + + case CSMWorld::UniversalId::Type_Referenceable: + return CSMWorld::ColumnBase::Display_Referenceable; + + + case CSMWorld::UniversalId::Type_Activator: + return CSMWorld::ColumnBase::Display_Activator; + + + case CSMWorld::UniversalId::Type_Potion: + return CSMWorld::ColumnBase::Display_Potion; + + + case CSMWorld::UniversalId::Type_Apparatus: + return CSMWorld::ColumnBase::Display_Apparatus; + + + case CSMWorld::UniversalId::Type_Armor: + return CSMWorld::ColumnBase::Display_Armor; + + + case CSMWorld::UniversalId::Type_Book: + return CSMWorld::ColumnBase::Display_Book; + + + case CSMWorld::UniversalId::Type_Clothing: + return CSMWorld::ColumnBase::Display_Clothing; + + + case CSMWorld::UniversalId::Type_Container: + return CSMWorld::ColumnBase::Display_Container; + + + case CSMWorld::UniversalId::Type_Creature: + return CSMWorld::ColumnBase::Display_Creature; + + + case CSMWorld::UniversalId::Type_Door: + return CSMWorld::ColumnBase::Display_Door; + + + case CSMWorld::UniversalId::Type_Ingredient: + return CSMWorld::ColumnBase::Display_Ingredient; + + + case CSMWorld::UniversalId::Type_CreatureLevelledList: + return CSMWorld::ColumnBase::Display_CreatureLevelledList; + + + case CSMWorld::UniversalId::Type_ItemLevelledList: + return CSMWorld::ColumnBase::Display_ItemLevelledList; + + + case CSMWorld::UniversalId::Type_Light: + return CSMWorld::ColumnBase::Display_Light; + + + case CSMWorld::UniversalId::Type_Lockpick: + return CSMWorld::ColumnBase::Display_Lockpick; + + + case CSMWorld::UniversalId::Type_Miscellaneous: + return CSMWorld::ColumnBase::Display_Miscellaneous; + + + case CSMWorld::UniversalId::Type_Npc: + return CSMWorld::ColumnBase::Display_Npc; + + + case CSMWorld::UniversalId::Type_Probe: + return CSMWorld::ColumnBase::Display_Probe; + + + case CSMWorld::UniversalId::Type_Repair: + return CSMWorld::ColumnBase::Display_Repair; + + + case CSMWorld::UniversalId::Type_Static: + return CSMWorld::ColumnBase::Display_Static; + + + case CSMWorld::UniversalId::Type_Weapon: + return CSMWorld::ColumnBase::Display_Weapon; + + + case CSMWorld::UniversalId::Type_Reference: + return CSMWorld::ColumnBase::Display_Reference; + + + case CSMWorld::UniversalId::Type_Filter: + return CSMWorld::ColumnBase::Display_Filter; + + + case CSMWorld::UniversalId::Type_Topic: + return CSMWorld::ColumnBase::Display_Topic; + + + case CSMWorld::UniversalId::Type_Journal: + return CSMWorld::ColumnBase::Display_Journal; + + + case CSMWorld::UniversalId::Type_TopicInfo: + return CSMWorld::ColumnBase::Display_TopicInfo; + + + case CSMWorld::UniversalId::Type_JournalInfo: + return CSMWorld::ColumnBase::Display_JournalInfo; + + + case CSMWorld::UniversalId::Type_Scene: + return CSMWorld::ColumnBase::Display_Scene; + + + case CSMWorld::UniversalId::Type_Script: + return CSMWorld::ColumnBase::Display_Script; + + + default: + return CSMWorld::ColumnBase::Display_None; + } } \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 0b0143abd..7687f3555 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -1,63 +1,63 @@ - -#ifndef TABLEMIMEDATA_H -#define TABLEMIMEDATA_H - -#include - -#include -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -namespace CSMDoc -{ - class Document; -} - -namespace CSMWorld -{ - -/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. -/// -/// This class provides way to construct mimedata object holding the universalid copy -/// Universalid is used in the majority of the tables to store type, id, argument types. -/// This way universalid grants a way to retrive record from the concrete table. -/// Please note, that tablemimedata object can hold multiple universalIds in the vector. - - class TableMimeData : public QMimeData - { - public: - TableMimeData(UniversalId id, const CSMDoc::Document& document); - - TableMimeData(std::vector& id, const CSMDoc::Document& document); - - ~TableMimeData(); - - virtual QStringList formats() const; - - std::string getIcon() const; - - std::vector getData() const; - - bool holdsType(UniversalId::Type type) const; - - bool holdsType(CSMWorld::ColumnBase::Display type) const; - - bool fromDocument(const CSMDoc::Document& document) const; - - UniversalId returnMatching(UniversalId::Type type) const; - - UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; - - static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); - static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); - - private: - std::vector mUniversalId; - QStringList mObjectsFormats; - const CSMDoc::Document& mDocument; - - }; -} + +#ifndef TABLEMIMEDATA_H +#define TABLEMIMEDATA_H + +#include + +#include +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + +/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. +/// +/// This class provides way to construct mimedata object holding the universalid copy +/// Universalid is used in the majority of the tables to store type, id, argument types. +/// This way universalid grants a way to retrive record from the concrete table. +/// Please note, that tablemimedata object can hold multiple universalIds in the vector. + + class TableMimeData : public QMimeData + { + public: + TableMimeData(UniversalId id, const CSMDoc::Document& document); + + TableMimeData(std::vector& id, const CSMDoc::Document& document); + + ~TableMimeData(); + + virtual QStringList formats() const; + + std::string getIcon() const; + + std::vector getData() const; + + bool holdsType(UniversalId::Type type) const; + + bool holdsType(CSMWorld::ColumnBase::Display type) const; + + bool fromDocument(const CSMDoc::Document& document) const; + + UniversalId returnMatching(UniversalId::Type type) const; + + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; + + static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); + static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); + + private: + std::vector mUniversalId; + QStringList mObjectsFormats; + const CSMDoc::Document& mDocument; + + }; +} #endif // TABLEMIMEDATA_H \ No newline at end of file diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index 841919a9e..cc1578bdd 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -1,203 +1,203 @@ - -#include "editwidget.hpp" - -#include -#include -#include - -#include "../../model/world/data.hpp" - -CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) -: QLineEdit (parent), mParser (data) -{ - mPalette = palette(); - connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); - - QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); - - connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), - this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), - this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), - Qt::QueuedConnection); -} - -void CSVFilter::EditWidget::textChanged (const QString& text) -{ - if (mParser.parse (text.toUtf8().constData())) - { - setPalette (mPalette); - emit filterChanged (mParser.getFilter()); - } - else - { - QPalette palette (mPalette); - palette.setColor (QPalette::Text, Qt::red); - setPalette (palette); - - /// \todo improve error reporting; mark only the faulty part - } -} - -void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, - const QModelIndex& bottomRight) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, - Qt::DropAction action) -{ - const unsigned count = filterSource.size(); - bool multipleElements = false; - - switch (count) //setting multipleElements; - { - case 0: //empty - return; //nothing to do here - - case 1: //only single - multipleElements = false; - break; - - default: - multipleElements = true; - break; - } - - Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); - QString oldContent (text()); - - bool replaceMode = false; - std::string orAnd; - - switch (key) //setting replaceMode and string used to glue expressions - { - case Qt::ShiftModifier: - orAnd = "!or("; - replaceMode = false; - break; - - case Qt::ControlModifier: - orAnd = "!and("; - replaceMode = false; - break; - - default: - replaceMode = true; - break; - } - - if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode - { - replaceMode = true; - } - - if (!replaceMode) - { - oldContent.remove ('!'); - } - - std::stringstream ss; - - if (multipleElements) - { - if (replaceMode) - { - ss<<"!or("; - } else { - ss << orAnd << oldContent.toStdString() << ','; - } - - for (unsigned i = 0; i < count; ++i) - { - ss<4) - { - clear(); - insert (QString::fromUtf8(ss.str().c_str())); - } -} - -std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const -{ - const unsigned columns = seekedString.second.size(); - - bool multipleColumns = false; - switch (columns) - { - case 0: //empty - return ""; //no column to filter - - case 1: //one column to look for - multipleColumns = false; - break; - - default: - multipleColumns = true; - break; - } - - std::stringstream ss; - if (multipleColumns) - { - ss<<"or("; - for (unsigned i = 0; i < columns; ++i) - { - ss<<"string("<<'"'< +#include +#include + +#include "../../model/world/data.hpp" + +CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) +: QLineEdit (parent), mParser (data) +{ + mPalette = palette(); + connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); + + QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); + + connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), + this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), + this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), + Qt::QueuedConnection); +} + +void CSVFilter::EditWidget::textChanged (const QString& text) +{ + if (mParser.parse (text.toUtf8().constData())) + { + setPalette (mPalette); + emit filterChanged (mParser.getFilter()); + } + else + { + QPalette palette (mPalette); + palette.setColor (QPalette::Text, Qt::red); + setPalette (palette); + + /// \todo improve error reporting; mark only the faulty part + } +} + +void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, + Qt::DropAction action) +{ + const unsigned count = filterSource.size(); + bool multipleElements = false; + + switch (count) //setting multipleElements; + { + case 0: //empty + return; //nothing to do here + + case 1: //only single + multipleElements = false; + break; + + default: + multipleElements = true; + break; + } + + Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); + QString oldContent (text()); + + bool replaceMode = false; + std::string orAnd; + + switch (key) //setting replaceMode and string used to glue expressions + { + case Qt::ShiftModifier: + orAnd = "!or("; + replaceMode = false; + break; + + case Qt::ControlModifier: + orAnd = "!and("; + replaceMode = false; + break; + + default: + replaceMode = true; + break; + } + + if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode + { + replaceMode = true; + } + + if (!replaceMode) + { + oldContent.remove ('!'); + } + + std::stringstream ss; + + if (multipleElements) + { + if (replaceMode) + { + ss<<"!or("; + } else { + ss << orAnd << oldContent.toStdString() << ','; + } + + for (unsigned i = 0; i < count; ++i) + { + ss<4) + { + clear(); + insert (QString::fromUtf8(ss.str().c_str())); + } +} + +std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const +{ + const unsigned columns = seekedString.second.size(); + + bool multipleColumns = false; + switch (columns) + { + case 0: //empty + return ""; //no column to filter + + case 1: //one column to look for + multipleColumns = false; + break; + + default: + multipleColumns = true; + break; + } + + std::stringstream ss; + if (multipleColumns) + { + ss<<"or("; + for (unsigned i = 0; i < columns; ++i) + { + ss<<"string("<<'"'< - -#include -#include -#include - -#include "../../model/filter/parser.hpp" -#include "../../model/filter/node.hpp" - -class QModelIndex; - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class EditWidget : public QLineEdit - { - Q_OBJECT - - CSMFilter::Parser mParser; - QPalette mPalette; - - public: - - EditWidget (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - - private: - std::string generateFilter(std::pair >& seekedString) const; - - private slots: - - void textChanged (const QString& text); - - void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void filterRowsRemoved (const QModelIndex& parent, int start, int end); - - void filterRowsInserted (const QModelIndex& parent, int start, int end); - - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - - void useFilterRequest(const std::string& idOfFilter); - }; -} - -#endif +#ifndef CSV_FILTER_EDITWIDGET_H +#define CSV_FILTER_EDITWIDGET_H + +#include + +#include +#include +#include + +#include "../../model/filter/parser.hpp" +#include "../../model/filter/node.hpp" + +class QModelIndex; + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class EditWidget : public QLineEdit + { + Q_OBJECT + + CSMFilter::Parser mParser; + QPalette mPalette; + + public: + + EditWidget (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + + private: + std::string generateFilter(std::pair >& seekedString) const; + + private slots: + + void textChanged (const QString& text); + + void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void filterRowsRemoved (const QModelIndex& parent, int start, int end); + + void filterRowsInserted (const QModelIndex& parent, int start, int end); + + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + + void useFilterRequest(const std::string& idOfFilter); + }; +} + +#endif diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index 4954efefb..a33288025 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -1,50 +1,50 @@ - -#include "filterbox.hpp" - -#include -#include - -#include "recordfilterbox.hpp" - -#include - -CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); - - layout->addWidget (recordFilterBox); - - setLayout (layout); - - connect (recordFilterBox, - SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (recordFilterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); - setAcceptDrops(true); -} - -void CSVFilter::FilterBox::dropEvent (QDropEvent* event) -{ - std::vector data = dynamic_cast (event->mimeData())->getData(); - - emit recordDropped(data, event->proposedAction()); -} - -void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) -{ - event->acceptProposedAction(); -} - -void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) -{ - event->accept(); -} + +#include "filterbox.hpp" + +#include +#include + +#include "recordfilterbox.hpp" + +#include + +CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); + + layout->addWidget (recordFilterBox); + + setLayout (layout); + + connect (recordFilterBox, + SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (recordFilterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); + setAcceptDrops(true); +} + +void CSVFilter::FilterBox::dropEvent (QDropEvent* event) +{ + std::vector data = dynamic_cast (event->mimeData())->getData(); + + emit recordDropped(data, event->proposedAction()); +} + +void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) +{ + event->accept(); +} diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 263e89e73..5954035fc 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -1,45 +1,45 @@ -#ifndef CSV_FILTER_FILTERBOX_H -#define CSV_FILTER_FILTERBOX_H - -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/universalid.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class FilterBox : public QWidget - { - Q_OBJECT - - void dragEnterEvent (QDragEnterEvent* event); - - void dropEvent (QDropEvent* event); - - void dragMoveEvent(QDragMoveEvent *event); - - public: - - FilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void recordFilterChanged (boost::shared_ptr filter); - void recordDropped (std::vector& types, Qt::DropAction action); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - -#endif - +#ifndef CSV_FILTER_FILTERBOX_H +#define CSV_FILTER_FILTERBOX_H + +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/universalid.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class FilterBox : public QWidget + { + Q_OBJECT + + void dragEnterEvent (QDragEnterEvent* event); + + void dropEvent (QDropEvent* event); + + void dragMoveEvent(QDragMoveEvent *event); + + public: + + FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void recordFilterChanged (boost::shared_ptr filter); + void recordDropped (std::vector& types, Qt::DropAction action); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + +#endif + diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index 59c083bd8..2a1a1407f 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -1,32 +1,32 @@ - -#include "recordfilterbox.hpp" - -#include -#include - -#include "editwidget.hpp" - -CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - layout->addWidget (new QLabel ("Record Filter", this)); - - EditWidget *editWidget = new EditWidget (data, this); - - layout->addWidget (editWidget); - - setLayout (layout); - - connect ( - editWidget, SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (filterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); -} + +#include "recordfilterbox.hpp" + +#include +#include + +#include "editwidget.hpp" + +CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + layout->addWidget (new QLabel ("Record Filter", this)); + + EditWidget *editWidget = new EditWidget (data, this); + + layout->addWidget (editWidget); + + setLayout (layout); + + connect ( + editWidget, SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (filterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); +} diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index fd384a32b..fa5c9c3c2 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -1,38 +1,38 @@ -#ifndef CSV_FILTER_RECORDFILTERBOX_H -#define CSV_FILTER_RECORDFILTERBOX_H - -#include - -#include -#include - -#include - -#include "../../model/filter/node.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class RecordFilterBox : public QWidget - { - Q_OBJECT - - public: - - RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - +#ifndef CSV_FILTER_RECORDFILTERBOX_H +#define CSV_FILTER_RECORDFILTERBOX_H + +#include + +#include +#include + +#include + +#include "../../model/filter/node.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class RecordFilterBox : public QWidget + { + Q_OBJECT + + public: + + RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + #endif \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index bc67c68b5..edf3bc6de 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -1,546 +1,546 @@ - -#include "table.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -#include "../../model/world/data.hpp" -#include "../../model/world/commands.hpp" -#include "../../model/world/idtableproxymodel.hpp" -#include "../../model/world/idtable.hpp" -#include "../../model/world/record.hpp" -#include "../../model/world/columns.hpp" -#include "../../model/world/tablemimedata.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "recordstatusdelegate.hpp" -#include "util.hpp" - -void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - QMenu menu (this); - - /// \todo add menu items for select all and clear selection - - if (!mEditLock) - { - if (selectedRows.size()==1) - { - menu.addAction (mEditAction); - if (mCreateAction) - menu.addAction(mCloneAction); - } - - if (mCreateAction) - menu.addAction (mCreateAction); - - /// \todo Reverting temporarily disabled on tables that support reordering, because - /// revert logic currently can not handle reordering. - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) - if (listRevertableSelectedIds().size()>0) - menu.addAction (mRevertAction); - - if (listDeletableSelectedIds().size()>0) - menu.addAction (mDeleteAction); - - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) - { - /// \todo allow reordering of multiple rows - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); - - if (column==-1) - column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); - - if (column!=-1) - { - if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row-1, column))) - { - menu.addAction (mMoveUpAction); - } - - if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row+1, column))) - { - menu.addAction (mMoveDownAction); - } - } - } - } - } - - menu.exec (event->globalPos()); -} - -std::vector CSVWorld::Table::listRevertableSelectedIds() const -{ - std::vector revertableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_BaseOnly) - { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - revertableIds.push_back (id); - } - } - } - - return revertableIds; -} - -std::vector CSVWorld::Table::listDeletableSelectedIds() const -{ - std::vector deletableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - // check record state - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state==CSMWorld::RecordBase::State_Deleted) - continue; - - // check other columns (only relevant for a subset of the tables) - int dialogueTypeIndex = - mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); - - if (dialogueTypeIndex!=-1) - { - int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); - - if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) - continue; - } - - // add the id to the collection - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - deletableIds.push_back (id); - } - } - - return deletableIds; -} - -CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting, const CSMDoc::Document& document) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) -{ - mModel = &dynamic_cast (*data.getTableModel (id)); - - mProxyModel = new CSMWorld::IdTableProxyModel (this); - mProxyModel->setSourceModel (mModel); - - setModel (mProxyModel); - horizontalHeader()->setResizeMode (QHeaderView::Interactive); - verticalHeader()->hide(); - setSortingEnabled (sorting); - setSelectionBehavior (QAbstractItemView::SelectRows); - setSelectionMode (QAbstractItemView::ExtendedSelection); - - int columns = mModel->columnCount(); - - for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); - - if (flags & CSMWorld::ColumnBase::Flag_Table) - { - CSMWorld::ColumnBase::Display display = static_cast ( - mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - undoStack, this); - - mDelegates.push_back (delegate); - setItemDelegateForColumn (i, delegate); - } - else - hideColumn (i); - } - - mEditAction = new QAction (tr ("Edit Record"), this); - connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); - addAction (mEditAction); - - if (createAndDelete) - { - mCreateAction = new QAction (tr ("Add Record"), this); - connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); - addAction (mCreateAction); - - mCloneAction = new QAction (tr ("Clone Record"), this); - connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); - addAction(mCloneAction); - } - - mRevertAction = new QAction (tr ("Revert Record"), this); - connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); - addAction (mRevertAction); - - mDeleteAction = new QAction (tr ("Delete Record"), this); - connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); - addAction (mDeleteAction); - - mMoveUpAction = new QAction (tr ("Move Up"), this); - connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); - addAction (mMoveUpAction); - - mMoveDownAction = new QAction (tr ("Move Down"), this); - connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); - addAction (mMoveDownAction); - - connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (tableSizeUpdate())); - - /// \note This signal could instead be connected to a slot that filters out changes not affecting - /// the records status column (for permanence reasons) - connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (tableSizeUpdate())); - - connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), - this, SLOT (selectionSizeUpdate ())); - - setAcceptDrops(true); -} - -void CSVWorld::Table::setEditLock (bool locked) -{ - for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) - (*iter)->setEditLock (locked); - - mEditLock = locked; -} - -CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const -{ - return CSMWorld::UniversalId ( - static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), - mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); -} - -void CSVWorld::Table::revertRecord() -{ - if (!mEditLock) - { - std::vector revertableIds = listRevertableSelectedIds(); - - if (revertableIds.size()>0) - { - if (revertableIds.size()>1) - mUndoStack.beginMacro (tr ("Revert multiple records")); - - for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); - - if (revertableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::deleteRecord() -{ - if (!mEditLock) - { - std::vector deletableIds = listDeletableSelectedIds(); - - if (deletableIds.size()>0) - { - if (deletableIds.size()>1) - mUndoStack.beginMacro (tr ("Delete multiple records")); - - for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); - - if (deletableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::editRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - emit editRequest (selectedRows.begin()->row()); - } -} - -void CSVWorld::Table::cloneRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); - if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) - { - emit cloneRequest (toClone); - } - } -} - -void CSVWorld::Table::moveUpRecord() -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - { - int row2 =selectedRows.begin()->row(); - - if (row2>0) - { - int row = row2-1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; iselectedRows(); - - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - if (rowrowCount()-1) - { - int row2 = row+1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; icolumnCount(); - - for (int i=0; i (*delegate). - updateEditorSetting (settingName, settingValue)) - emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); -} - -void CSVWorld::Table::tableSizeUpdate() -{ - int size = 0; - int deleted = 0; - int modified = 0; - - if (mProxyModel->columnCount()>0) - { - int rows = mProxyModel->rowCount(); - - for (int i=0; imapToSource (mProxyModel->index (i, 0)); - - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); - int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); - - switch (state) - { - case CSMWorld::RecordBase::State_BaseOnly: ++size; break; - case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; - case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; - case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; - } - } - } - - tableSizeChanged (size, deleted, modified); -} - -void CSVWorld::Table::selectionSizeUpdate() -{ - selectionSizeChanged (selectionModel()->selectedRows().size()); -} - -void CSVWorld::Table::requestFocus (const std::string& id) -{ - QModelIndex index = mProxyModel->getModelIndex (id, 0); - - if (index.isValid()) - scrollTo (index, QAbstractItemView::PositionAtTop); -} - -void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) -{ - mProxyModel->setFilter (filter); -} - -void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) -{ - if (event->buttons() & Qt::LeftButton) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size() == 0) - { - return; - } - - QDrag* drag = new QDrag (this); - CSMWorld::TableMimeData* mime = NULL; - - if (selectedRows.size() == 1) - { - mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); - } - else - { - std::vector idToDrag; - - foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. - { - idToDrag.push_back (getUniversalId (it.row())); - } - - mime = new CSMWorld::TableMimeData (idToDrag, mDocument); - } - - drag->setMimeData (mime); - drag->setPixmap (QString::fromStdString (mime->getIcon())); - - Qt::DropActions action = Qt::IgnoreAction; - switch (QApplication::keyboardModifiers()) - { - case Qt::ControlModifier: - action = Qt::CopyAction; - break; - - case Qt::ShiftModifier: - action = Qt::MoveAction; - break; - } - - drag->exec(action); - } - -} - -void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) -{ - event->acceptProposedAction(); -} - -void CSVWorld::Table::dropEvent(QDropEvent *event) -{ - QModelIndex index = indexAt (event->pos()); - - if (!index.isValid()) - { - return; - } - - const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); - if (mime->fromDocument (mDocument)) - { - CSMWorld::ColumnBase::Display display = static_cast - (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (mime->holdsType (display)) - { - CSMWorld::UniversalId record (mime->returnMatching (display)); - - std::auto_ptr command (new CSMWorld::ModifyCommand - (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); - - mUndoStack.push (command.release()); - } - } //TODO handle drops from different document -} - -void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) -{ - event->accept(); -} - -std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const -{ - const int count = mModel->columnCount(); - - std::vector titles; - for (int i = 0; i < count; ++i) - { - CSMWorld::ColumnBase::Display columndisplay = static_cast - (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (display == columndisplay) - { - titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); - } - } - return titles; + +#include "table.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtableproxymodel.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/record.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "recordstatusdelegate.hpp" +#include "util.hpp" + +void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + QMenu menu (this); + + /// \todo add menu items for select all and clear selection + + if (!mEditLock) + { + if (selectedRows.size()==1) + { + menu.addAction (mEditAction); + if (mCreateAction) + menu.addAction(mCloneAction); + } + + if (mCreateAction) + menu.addAction (mCreateAction); + + /// \todo Reverting temporarily disabled on tables that support reordering, because + /// revert logic currently can not handle reordering. + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) + if (listRevertableSelectedIds().size()>0) + menu.addAction (mRevertAction); + + if (listDeletableSelectedIds().size()>0) + menu.addAction (mDeleteAction); + + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) + { + /// \todo allow reordering of multiple rows + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); + + if (column==-1) + column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); + + if (column!=-1) + { + if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row-1, column))) + { + menu.addAction (mMoveUpAction); + } + + if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row+1, column))) + { + menu.addAction (mMoveDownAction); + } + } + } + } + } + + menu.exec (event->globalPos()); +} + +std::vector CSVWorld::Table::listRevertableSelectedIds() const +{ + std::vector revertableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + revertableIds.push_back (id); + } + } + } + + return revertableIds; +} + +std::vector CSVWorld::Table::listDeletableSelectedIds() const +{ + std::vector deletableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + // check record state + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state==CSMWorld::RecordBase::State_Deleted) + continue; + + // check other columns (only relevant for a subset of the tables) + int dialogueTypeIndex = + mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); + + if (dialogueTypeIndex!=-1) + { + int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); + + if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) + continue; + } + + // add the id to the collection + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); + } + } + + return deletableIds; +} + +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, + bool createAndDelete, bool sorting, const CSMDoc::Document& document) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) +{ + mModel = &dynamic_cast (*data.getTableModel (id)); + + mProxyModel = new CSMWorld::IdTableProxyModel (this); + mProxyModel->setSourceModel (mModel); + + setModel (mProxyModel); + horizontalHeader()->setResizeMode (QHeaderView::Interactive); + verticalHeader()->hide(); + setSortingEnabled (sorting); + setSelectionBehavior (QAbstractItemView::SelectRows); + setSelectionMode (QAbstractItemView::ExtendedSelection); + + int columns = mModel->columnCount(); + + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + + if (flags & CSMWorld::ColumnBase::Flag_Table) + { + CSMWorld::ColumnBase::Display display = static_cast ( + mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, + undoStack, this); + + mDelegates.push_back (delegate); + setItemDelegateForColumn (i, delegate); + } + else + hideColumn (i); + } + + mEditAction = new QAction (tr ("Edit Record"), this); + connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); + addAction (mEditAction); + + if (createAndDelete) + { + mCreateAction = new QAction (tr ("Add Record"), this); + connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); + addAction (mCreateAction); + + mCloneAction = new QAction (tr ("Clone Record"), this); + connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); + addAction(mCloneAction); + } + + mRevertAction = new QAction (tr ("Revert Record"), this); + connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); + addAction (mRevertAction); + + mDeleteAction = new QAction (tr ("Delete Record"), this); + connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); + addAction (mDeleteAction); + + mMoveUpAction = new QAction (tr ("Move Up"), this); + connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); + addAction (mMoveUpAction); + + mMoveDownAction = new QAction (tr ("Move Down"), this); + connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); + addAction (mMoveDownAction); + + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (tableSizeUpdate())); + + /// \note This signal could instead be connected to a slot that filters out changes not affecting + /// the records status column (for permanence reasons) + connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (tableSizeUpdate())); + + connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), + this, SLOT (selectionSizeUpdate ())); + + setAcceptDrops(true); +} + +void CSVWorld::Table::setEditLock (bool locked) +{ + for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) + (*iter)->setEditLock (locked); + + mEditLock = locked; +} + +CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const +{ + return CSMWorld::UniversalId ( + static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), + mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); +} + +void CSVWorld::Table::revertRecord() +{ + if (!mEditLock) + { + std::vector revertableIds = listRevertableSelectedIds(); + + if (revertableIds.size()>0) + { + if (revertableIds.size()>1) + mUndoStack.beginMacro (tr ("Revert multiple records")); + + for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + + if (revertableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::deleteRecord() +{ + if (!mEditLock) + { + std::vector deletableIds = listDeletableSelectedIds(); + + if (deletableIds.size()>0) + { + if (deletableIds.size()>1) + mUndoStack.beginMacro (tr ("Delete multiple records")); + + for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); + + if (deletableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::editRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + emit editRequest (selectedRows.begin()->row()); + } +} + +void CSVWorld::Table::cloneRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); + if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) + { + emit cloneRequest (toClone); + } + } +} + +void CSVWorld::Table::moveUpRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + int row2 =selectedRows.begin()->row(); + + if (row2>0) + { + int row = row2-1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + if (rowrowCount()-1) + { + int row2 = row+1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; icolumnCount(); + + for (int i=0; i (*delegate). + updateEditorSetting (settingName, settingValue)) + emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); +} + +void CSVWorld::Table::tableSizeUpdate() +{ + int size = 0; + int deleted = 0; + int modified = 0; + + if (mProxyModel->columnCount()>0) + { + int rows = mProxyModel->rowCount(); + + for (int i=0; imapToSource (mProxyModel->index (i, 0)); + + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); + + switch (state) + { + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + } + } + } + + tableSizeChanged (size, deleted, modified); +} + +void CSVWorld::Table::selectionSizeUpdate() +{ + selectionSizeChanged (selectionModel()->selectedRows().size()); +} + +void CSVWorld::Table::requestFocus (const std::string& id) +{ + QModelIndex index = mProxyModel->getModelIndex (id, 0); + + if (index.isValid()) + scrollTo (index, QAbstractItemView::PositionAtTop); +} + +void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) +{ + mProxyModel->setFilter (filter); +} + +void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size() == 0) + { + return; + } + + QDrag* drag = new QDrag (this); + CSMWorld::TableMimeData* mime = NULL; + + if (selectedRows.size() == 1) + { + mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); + } + else + { + std::vector idToDrag; + + foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. + { + idToDrag.push_back (getUniversalId (it.row())); + } + + mime = new CSMWorld::TableMimeData (idToDrag, mDocument); + } + + drag->setMimeData (mime); + drag->setPixmap (QString::fromStdString (mime->getIcon())); + + Qt::DropActions action = Qt::IgnoreAction; + switch (QApplication::keyboardModifiers()) + { + case Qt::ControlModifier: + action = Qt::CopyAction; + break; + + case Qt::ShiftModifier: + action = Qt::MoveAction; + break; + } + + drag->exec(action); + } + +} + +void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void CSVWorld::Table::dropEvent(QDropEvent *event) +{ + QModelIndex index = indexAt (event->pos()); + + if (!index.isValid()) + { + return; + } + + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (mime->fromDocument (mDocument)) + { + CSMWorld::ColumnBase::Display display = static_cast + (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (mime->holdsType (display)) + { + CSMWorld::UniversalId record (mime->returnMatching (display)); + + std::auto_ptr command (new CSMWorld::ModifyCommand + (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); + + mUndoStack.push (command.release()); + } + } //TODO handle drops from different document +} + +void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) +{ + event->accept(); +} + +std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const +{ + const int count = mModel->columnCount(); + + std::vector titles; + for (int i = 0; i < count; ++i) + { + CSMWorld::ColumnBase::Display columndisplay = static_cast + (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (display == columndisplay) + { + titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); + } + } + return titles; } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 04733a53e..615a31b4d 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -1,127 +1,127 @@ -#ifndef CSV_WORLD_TABLE_H -#define CSV_WORLD_TABLE_H - -#include -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/columnbase.hpp" - -namespace CSMDoc { - class Document; -} - -class QUndoStack; -class QAction; - -namespace CSMWorld -{ - class Data; - class UniversalId; - class IdTableProxyModel; - class IdTable; -} - -namespace CSVWorld -{ - class CommandDelegate; - - ///< Table widget - class Table : public QTableView - { - Q_OBJECT - - std::vector mDelegates; - QUndoStack& mUndoStack; - QAction *mEditAction; - QAction *mCreateAction; - QAction *mCloneAction; - QAction *mRevertAction; - QAction *mDeleteAction; - QAction *mMoveUpAction; - QAction *mMoveDownAction; - CSMWorld::IdTableProxyModel *mProxyModel; - CSMWorld::IdTable *mModel; - bool mEditLock; - int mRecordStatusDisplay; - - /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you - /// should NOT use it for anything else. - const CSMDoc::Document& mDocument; - - private: - - void contextMenuEvent (QContextMenuEvent *event); - - std::vector listRevertableSelectedIds() const; - - std::vector listDeletableSelectedIds() const; - - void mouseMoveEvent(QMouseEvent *event); - - void dragEnterEvent(QDragEnterEvent *event); - - void dragMoveEvent(QDragMoveEvent *event); - - void dropEvent(QDropEvent *event); - - public: - - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, - bool sorting, const CSMDoc::Document& document); - - ///< \param createAndDelete Allow creation and deletion of records. - /// \param sorting Allow changing order of rows in the view via column headers. - - void setEditLock (bool locked); - - CSMWorld::UniversalId getUniversalId (int row) const; - - void updateEditorSetting (const QString &settingName, const QString &settingValue); - - std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; - - signals: - - void editRequest (int row); - - void selectionSizeChanged (int size); - - void tableSizeChanged (int size, int deleted, int modified); - ///< \param size Number of not deleted records - /// \param deleted Number of deleted records - /// \param modified Number of added and modified records - - void createRequest(); - void cloneRequest(const CSMWorld::UniversalId&); - - private slots: - - void revertRecord(); - - void deleteRecord(); - - void editRecord(); - - void cloneRecord(); - - void moveUpRecord(); - - void moveDownRecord(); - - public slots: - - void tableSizeUpdate(); - - void selectionSizeUpdate(); - - void requestFocus (const std::string& id); - - void recordFilterChanged (boost::shared_ptr filter); - }; -} - -#endif +#ifndef CSV_WORLD_TABLE_H +#define CSV_WORLD_TABLE_H + +#include +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/columnbase.hpp" + +namespace CSMDoc { + class Document; +} + +class QUndoStack; +class QAction; + +namespace CSMWorld +{ + class Data; + class UniversalId; + class IdTableProxyModel; + class IdTable; +} + +namespace CSVWorld +{ + class CommandDelegate; + + ///< Table widget + class Table : public QTableView + { + Q_OBJECT + + std::vector mDelegates; + QUndoStack& mUndoStack; + QAction *mEditAction; + QAction *mCreateAction; + QAction *mCloneAction; + QAction *mRevertAction; + QAction *mDeleteAction; + QAction *mMoveUpAction; + QAction *mMoveDownAction; + CSMWorld::IdTableProxyModel *mProxyModel; + CSMWorld::IdTable *mModel; + bool mEditLock; + int mRecordStatusDisplay; + + /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you + /// should NOT use it for anything else. + const CSMDoc::Document& mDocument; + + private: + + void contextMenuEvent (QContextMenuEvent *event); + + std::vector listRevertableSelectedIds() const; + + std::vector listDeletableSelectedIds() const; + + void mouseMoveEvent(QMouseEvent *event); + + void dragEnterEvent(QDragEnterEvent *event); + + void dragMoveEvent(QDragMoveEvent *event); + + void dropEvent(QDropEvent *event); + + public: + + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, + bool sorting, const CSMDoc::Document& document); + + ///< \param createAndDelete Allow creation and deletion of records. + /// \param sorting Allow changing order of rows in the view via column headers. + + void setEditLock (bool locked); + + CSMWorld::UniversalId getUniversalId (int row) const; + + void updateEditorSetting (const QString &settingName, const QString &settingValue); + + std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; + + signals: + + void editRequest (int row); + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); + void cloneRequest(const CSMWorld::UniversalId&); + + private slots: + + void revertRecord(); + + void deleteRecord(); + + void editRecord(); + + void cloneRecord(); + + void moveUpRecord(); + + void moveDownRecord(); + + public slots: + + void tableSizeUpdate(); + + void selectionSizeUpdate(); + + void requestFocus (const std::string& id); + + void recordFilterChanged (boost::shared_ptr filter); + }; +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 782ccfd24..e330d4775 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,132 +1,132 @@ - -#include "tablesubview.hpp" - -#include -#include - -#include "../../model/doc/document.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "../filter/filterbox.hpp" -#include "table.hpp" -#include "tablebottombox.hpp" -#include "creator.hpp" - -CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting) -: SubView (id) -{ - QVBoxLayout *layout = new QVBoxLayout; - - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - - layout->addWidget (mBottom = - new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); - - layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); - - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); - - layout->insertWidget (0, filterBox); - - QWidget *widget = new QWidget; - - widget->setLayout (layout); - - setWidget (widget); - - connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); - - connect (mTable, SIGNAL (selectionSizeChanged (int)), - mBottom, SLOT (selectionSizeChanged (int))); - connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), - mBottom, SLOT (tableSizeChanged (int, int, int))); - - mTable->tableSizeUpdate(); - mTable->selectionSizeUpdate(); - mTable->viewport()->installEventFilter(this); - mBottom->installEventFilter(this); - filterBox->installEventFilter(this); - - if (mBottom->canCreateAndDelete()) - { - connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - - connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, - SLOT(cloneRequest(const CSMWorld::UniversalId&))); - - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); - } - connect (mBottom, SIGNAL (requestFocus (const std::string&)), - mTable, SLOT (requestFocus (const std::string&))); - - connect (filterBox, - SIGNAL (recordFilterChanged (boost::shared_ptr)), - mTable, SLOT (recordFilterChanged (boost::shared_ptr))); - - connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), - this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); -} - -void CSVWorld::TableSubView::setEditLock (bool locked) -{ - mTable->setEditLock (locked); - mBottom->setEditLock (locked); -} - -void CSVWorld::TableSubView::editRequest (int row) -{ - focusId (mTable->getUniversalId (row)); -} - -void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) -{ - mTable->updateEditorSetting(settingName, settingValue); -} - -void CSVWorld::TableSubView::setStatusBar (bool show) -{ - mBottom->setStatusBar (show); -} - -void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) -{ - emit cloneRequest(toClone.getId(), toClone.getType()); -} - -void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) -{ - std::vector > > filterSource; - - for (std::vector::iterator it = types.begin(); it != types.end(); ++it) - { - std::pair > pair( //splited long line - std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); - - filterSource.push_back(pair); - } - emit createFilterRequest(filterSource, action); -} - -bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) -{ - if (event->type() == QEvent::Drop) - { - QDropEvent* drop = dynamic_cast(event); - const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); - bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); - if (handled) - { - emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); - } - return handled; - } - return false; + +#include "tablesubview.hpp" + +#include +#include + +#include "../../model/doc/document.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "../filter/filterbox.hpp" +#include "table.hpp" +#include "tablebottombox.hpp" +#include "creator.hpp" + +CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting) +: SubView (id) +{ + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); + + layout->insertWidget (0, mTable = + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); + + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + + layout->insertWidget (0, filterBox); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); + + connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); + + connect (mTable, SIGNAL (selectionSizeChanged (int)), + mBottom, SLOT (selectionSizeChanged (int))); + connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), + mBottom, SLOT (tableSizeChanged (int, int, int))); + + mTable->tableSizeUpdate(); + mTable->selectionSizeUpdate(); + mTable->viewport()->installEventFilter(this); + mBottom->installEventFilter(this); + filterBox->installEventFilter(this); + + if (mBottom->canCreateAndDelete()) + { + connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); + + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, + SLOT(cloneRequest(const CSMWorld::UniversalId&))); + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + } + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + mTable, SLOT (requestFocus (const std::string&))); + + connect (filterBox, + SIGNAL (recordFilterChanged (boost::shared_ptr)), + mTable, SLOT (recordFilterChanged (boost::shared_ptr))); + + connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), + this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); +} + +void CSVWorld::TableSubView::setEditLock (bool locked) +{ + mTable->setEditLock (locked); + mBottom->setEditLock (locked); +} + +void CSVWorld::TableSubView::editRequest (int row) +{ + focusId (mTable->getUniversalId (row)); +} + +void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) +{ + mTable->updateEditorSetting(settingName, settingValue); +} + +void CSVWorld::TableSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); +} + +void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) +{ + emit cloneRequest(toClone.getId(), toClone.getType()); +} + +void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) +{ + std::vector > > filterSource; + + for (std::vector::iterator it = types.begin(); it != types.end(); ++it) + { + std::pair > pair( //splited long line + std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); + + filterSource.push_back(pair); + } + emit createFilterRequest(filterSource, action); +} + +bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Drop) + { + QDropEvent* drop = dynamic_cast(event); + const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); + bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); + if (handled) + { + emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + } + return handled; + } + return false; } \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 4e578b180..b3c253919 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -1,63 +1,63 @@ -#ifndef CSV_WORLD_TABLESUBVIEW_H -#define CSV_WORLD_TABLESUBVIEW_H - -#include "../doc/subview.hpp" - -#include - -class QModelIndex; - -namespace CSMWorld -{ - class IdTable; -} - -namespace CSMDoc -{ - class Document; -} - -namespace CSVWorld -{ - class Table; - class TableBottomBox; - class CreatorFactoryBase; - - class TableSubView : public CSVDoc::SubView - { - Q_OBJECT - - Table *mTable; - TableBottomBox *mBottom; - - public: - - TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting); - - virtual void setEditLock (bool locked); - - virtual void updateEditorSetting (const QString& key, const QString& value); - - virtual void setStatusBar (bool show); - - protected: - bool eventFilter(QObject* object, QEvent *event); - - signals: - void cloneRequest(const std::string&, - const CSMWorld::UniversalId::Type); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - - private slots: - - void editRequest (int row); - void cloneRequest (const CSMWorld::UniversalId& toClone); - void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, - Qt::DropAction action); - }; -} - -#endif +#ifndef CSV_WORLD_TABLESUBVIEW_H +#define CSV_WORLD_TABLESUBVIEW_H + +#include "../doc/subview.hpp" + +#include + +class QModelIndex; + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class Table; + class TableBottomBox; + class CreatorFactoryBase; + + class TableSubView : public CSVDoc::SubView + { + Q_OBJECT + + Table *mTable; + TableBottomBox *mBottom; + + public: + + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting); + + virtual void setEditLock (bool locked); + + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); + + protected: + bool eventFilter(QObject* object, QEvent *event); + + signals: + void cloneRequest(const std::string&, + const CSMWorld::UniversalId::Type); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + + private slots: + + void editRequest (int row); + void cloneRequest (const CSMWorld::UniversalId& toClone); + void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, + Qt::DropAction action); + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6a49f2f5e..17a624f0f 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -240,17 +240,21 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); + buildNewPath(actor); //may fail to build a path, check before use //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); - else - mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - mRotate = true; + //if no new path leave mTargetAngle unchanged + if(!mPathFinder.getPath().empty()) + { + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; + } mMovement.mPosition[1] = 1; mReadyToAttack = false; @@ -300,9 +304,13 @@ namespace MWMechanics dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + float dist = -1; //hack to indicate first time, to construct a new path + if(!mPathFinder.getPath().empty()) + { + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + } float targetPosThreshold; bool isOutside = actor.getCell()->mCell->isExterior(); @@ -311,7 +319,7 @@ namespace MWMechanics else targetPosThreshold = 100; - if(dist > targetPosThreshold) + if((dist < 0) || (dist > targetPosThreshold)) { //construct new path only if target has moved away more than on ESM::Position pos = actor.getRefData().getPosition(); @@ -332,8 +340,11 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; + if(!mPathFinder.getPath().empty()) + { + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; + } } } } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 4407363a6..8dbdd8770 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,6 +391,8 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { + if (mPath.size() < 2) + return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f9b5b4d04..265069dc4 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,352 +1,352 @@ - -#include "statemanagerimp.hpp" - -#include -#include -#include -#include - -#include - -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/journal.hpp" -#include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/scriptmanager.hpp" -#include "../mwbase/soundmanager.hpp" - -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" - -#include "../mwmechanics/npcstats.hpp" - -#include "../mwscript/globalscripts.hpp" - -void MWState::StateManager::cleanup (bool force) -{ - if (mState!=State_NoGame || force) - { - MWBase::Environment::get().getSoundManager()->clear(); - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); - MWBase::Environment::get().getWorld()->clear(); - MWBase::Environment::get().getWindowManager()->clear(); - - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - mTimePlayed = 0; - } -} - -std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) - const -{ - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - const std::vector& prev = reader.getGameFiles(); - - std::map map; - - for (int iPrev = 0; iPrev (prev.size()); ++iPrev) - { - std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); - - for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) - if (id==Misc::StringUtils::lowerCase (current[iCurrent])) - { - map.insert (std::make_pair (iPrev, iCurrent)); - break; - } - } - - return map; -} - -MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) -{ - -} - -void MWState::StateManager::requestQuit() -{ - mQuitRequest = true; -} - -bool MWState::StateManager::hasQuitRequest() const -{ - return mQuitRequest; -} - -void MWState::StateManager::askLoadRecent() -{ - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) - return; - - if( !mAskLoadRecent ) - { - if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves - { - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - else - { - MWState::Slot lastSave = *getCurrentCharacter()->begin(); - std::vector buttons; - buttons.push_back("#{sYes}"); - buttons.push_back("#{sNo}"); - std::string tag("%s"); - std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); - size_t pos = message.find(tag); - message.replace(pos, tag.length(), lastSave.mProfile.mDescription); - MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); - mAskLoadRecent = true; - } - } -} - -MWState::StateManager::State MWState::StateManager::getState() const -{ - return mState; -} - -void MWState::StateManager::newGame (bool bypass) -{ - cleanup(); - - if (!bypass) - { - MWBase::Environment::get().getWorld()->startNewGame(); - MWBase::Environment::get().getWindowManager()->setNewGame (true); - } - else - MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); - - mState = State_Running; -} - -void MWState::StateManager::endGame() -{ - mState = State_Ended; - MWBase::Environment::get().getWorld()->useDeathCamera(); -} - -void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) -{ - ESM::SavedGame profile; - - MWBase::World& world = *MWBase::Environment::get().getWorld(); - - MWWorld::Ptr player = world.getPlayer().getPlayer(); - - profile.mContentFiles = world.getContentFiles(); - - profile.mPlayerName = player.getClass().getName (player); - profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClass = player.get()->mBase->mClass; - - profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); - profile.mTimePlayed = mTimePlayed; - profile.mDescription = description; - - int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing - Ogre::Image screenshot; - world.screenshot(screenshot, screenshotW, screenshotH); - Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); - profile.mScreenshot.resize(encoded->size()); - encoded->read(&profile.mScreenshot[0], encoded->size()); - - if (!slot) - slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); - else - slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); - - std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); - - ESM::ESMWriter writer; - - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); - ++iter) - writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 - - writer.setFormat (ESM::Header::CurrentFormat); - writer.setRecordCount ( - 1 // saved game header - +MWBase::Environment::get().getJournal()->countSavedGameRecords() - +MWBase::Environment::get().getWorld()->countSavedGameRecords() - +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() - +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() - +1 // global map - ); - - writer.save (stream); - - writer.startRecord (ESM::REC_SAVE); - slot->mProfile.save (writer); - writer.endRecord (ESM::REC_SAVE); - - MWBase::Environment::get().getJournal()->write (writer); - MWBase::Environment::get().getDialogueManager()->write (writer); - MWBase::Environment::get().getWorld()->write (writer); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); - MWBase::Environment::get().getWindowManager()->write(writer); - - writer.close(); - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); -} - -void MWState::StateManager::loadGame (const Character *character, const Slot *slot) -{ - try - { - cleanup(); - - mTimePlayed = slot->mProfile.mTimePlayed; - - ESM::ESMReader reader; - reader.open (slot->mPath.string()); - - std::map contentFileMap = buildContentFileIndexMap (reader); - - while (reader.hasMoreRecs()) - { - ESM::NAME n = reader.getRecName(); - reader.getRecHeader(); - - switch (n.val) - { - case ESM::REC_SAVE: - - // don't need to read that here - reader.skipRecord(); - break; - - case ESM::REC_JOUR: - case ESM::REC_QUES: - - MWBase::Environment::get().getJournal()->readRecord (reader, n.val); - break; - - case ESM::REC_DIAS: - - MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); - break; - - case ESM::REC_ALCH: - case ESM::REC_ARMO: - case ESM::REC_BOOK: - case ESM::REC_CLAS: - case ESM::REC_CLOT: - case ESM::REC_ENCH: - case ESM::REC_NPC_: - case ESM::REC_SPEL: - case ESM::REC_WEAP: - case ESM::REC_GLOB: - case ESM::REC_PLAY: - case ESM::REC_CSTA: - - MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); - break; - - case ESM::REC_GSCR: - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); - break; - - case ESM::REC_GMAP: - - MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); - break; - - default: - - // ignore invalid records - /// \todo log error - reader.skipRecord(); - } - } - - mCharacterManager.setCurrentCharacter(character); - - mState = State_Running; - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); - - MWBase::Environment::get().getWindowManager()->setNewGame(false); - MWBase::Environment::get().getWorld()->setupPlayer(); - MWBase::Environment::get().getWorld()->renderPlayer(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); - MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - - ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); - - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); - } - catch (const std::exception& e) - { - std::cerr << "failed to load saved game: " << e.what() << std::endl; - cleanup (true); - } -} - -MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) -{ - return mCharacterManager.getCurrentCharacter (create); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() -{ - return mCharacterManager.begin(); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() -{ - return mCharacterManager.end(); -} - -void MWState::StateManager::update (float duration) -{ - mTimePlayed += duration; - - // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. - if (mAskLoadRecent) - { - int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); - if(iButton==0) - { - mAskLoadRecent = false; - //Load last saved game for current character - MWState::Character *curCharacter = getCurrentCharacter(); - MWState::Slot lastSave = *curCharacter->begin(); - loadGame(curCharacter, &lastSave); - } - else if(iButton==1) - { - mAskLoadRecent = false; - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - } -} + +#include "statemanagerimp.hpp" + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/scriptmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "../mwscript/globalscripts.hpp" + +void MWState::StateManager::cleanup (bool force) +{ + if (mState!=State_NoGame || force) + { + MWBase::Environment::get().getSoundManager()->clear(); + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); + MWBase::Environment::get().getWorld()->clear(); + MWBase::Environment::get().getWindowManager()->clear(); + + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; + } +} + +std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) + const +{ + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + const std::vector& prev = reader.getGameFiles(); + + std::map map; + + for (int iPrev = 0; iPrev (prev.size()); ++iPrev) + { + std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); + + for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) + if (id==Misc::StringUtils::lowerCase (current[iCurrent])) + { + map.insert (std::make_pair (iPrev, iCurrent)); + break; + } + } + + return map; +} + +MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) +: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) +{ + +} + +void MWState::StateManager::requestQuit() +{ + mQuitRequest = true; +} + +bool MWState::StateManager::hasQuitRequest() const +{ + return mQuitRequest; +} + +void MWState::StateManager::askLoadRecent() +{ + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) + return; + + if( !mAskLoadRecent ) + { + if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else + { + MWState::Slot lastSave = *getCurrentCharacter()->begin(); + std::vector buttons; + buttons.push_back("#{sYes}"); + buttons.push_back("#{sNo}"); + std::string tag("%s"); + std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); + size_t pos = message.find(tag); + message.replace(pos, tag.length(), lastSave.mProfile.mDescription); + MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + mAskLoadRecent = true; + } + } +} + +MWState::StateManager::State MWState::StateManager::getState() const +{ + return mState; +} + +void MWState::StateManager::newGame (bool bypass) +{ + cleanup(); + + if (!bypass) + { + MWBase::Environment::get().getWorld()->startNewGame(); + MWBase::Environment::get().getWindowManager()->setNewGame (true); + } + else + MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + + mState = State_Running; +} + +void MWState::StateManager::endGame() +{ + mState = State_Ended; + MWBase::Environment::get().getWorld()->useDeathCamera(); +} + +void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) +{ + ESM::SavedGame profile; + + MWBase::World& world = *MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world.getPlayer().getPlayer(); + + profile.mContentFiles = world.getContentFiles(); + + profile.mPlayerName = player.getClass().getName (player); + profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); + profile.mPlayerClass = player.get()->mBase->mClass; + + profile.mPlayerCell = world.getCellName(); + + profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); + profile.mInGameTime.mDay = world.getDay(); + profile.mInGameTime.mMonth = world.getMonth(); + profile.mInGameTime.mYear = world.getYear(); + profile.mTimePlayed = mTimePlayed; + profile.mDescription = description; + + int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing + Ogre::Image screenshot; + world.screenshot(screenshot, screenshotW, screenshotH); + Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); + profile.mScreenshot.resize(encoded->size()); + encoded->read(&profile.mScreenshot[0], encoded->size()); + + if (!slot) + slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); + else + slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + + std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); + + ESM::ESMWriter writer; + + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); + ++iter) + writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 + + writer.setFormat (ESM::Header::CurrentFormat); + writer.setRecordCount ( + 1 // saved game header + +MWBase::Environment::get().getJournal()->countSavedGameRecords() + +MWBase::Environment::get().getWorld()->countSavedGameRecords() + +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() + +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() + +1 // global map + ); + + writer.save (stream); + + writer.startRecord (ESM::REC_SAVE); + slot->mProfile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + MWBase::Environment::get().getJournal()->write (writer); + MWBase::Environment::get().getDialogueManager()->write (writer); + MWBase::Environment::get().getWorld()->write (writer); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); + MWBase::Environment::get().getWindowManager()->write(writer); + + writer.close(); + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); +} + +void MWState::StateManager::loadGame (const Character *character, const Slot *slot) +{ + try + { + cleanup(); + + mTimePlayed = slot->mProfile.mTimePlayed; + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + std::map contentFileMap = buildContentFileIndexMap (reader); + + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); + + switch (n.val) + { + case ESM::REC_SAVE: + + // don't need to read that here + reader.skipRecord(); + break; + + case ESM::REC_JOUR: + case ESM::REC_QUES: + + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; + + case ESM::REC_DIAS: + + MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); + break; + + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + case ESM::REC_GLOB: + case ESM::REC_PLAY: + case ESM::REC_CSTA: + + MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); + break; + + case ESM::REC_GSCR: + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; + + case ESM::REC_GMAP: + + MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); + break; + + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } + } + + mCharacterManager.setCurrentCharacter(character); + + mState = State_Running; + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); + + MWBase::Environment::get().getWindowManager()->setNewGame(false); + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + } + catch (const std::exception& e) + { + std::cerr << "failed to load saved game: " << e.what() << std::endl; + cleanup (true); + } +} + +MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) +{ + return mCharacterManager.getCurrentCharacter (create); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() +{ + return mCharacterManager.begin(); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() +{ + return mCharacterManager.end(); +} + +void MWState::StateManager::update (float duration) +{ + mTimePlayed += duration; + + // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. + if (mAskLoadRecent) + { + int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if(iButton==0) + { + mAskLoadRecent = false; + //Load last saved game for current character + MWState::Character *curCharacter = getCurrentCharacter(); + MWState::Slot lastSave = *curCharacter->begin(); + loadGame(curCharacter, &lastSave); + } + else if(iButton==1) + { + mAskLoadRecent = false; + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + } +} diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index b7207486b..91f123eb7 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -1,203 +1,203 @@ -#include "esmwriter.hpp" - -#include -#include -#include - -namespace ESM -{ - ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} - - unsigned int ESMWriter::getVersion() const - { - return mHeader.mData.version; - } - - void ESMWriter::setVersion(unsigned int ver) - { - mHeader.mData.version = ver; - } - - void ESMWriter::setAuthor(const std::string& auth) - { - mHeader.mData.author.assign (auth); - } - - void ESMWriter::setDescription(const std::string& desc) - { - mHeader.mData.desc.assign (desc); - } - - void ESMWriter::setRecordCount (int count) - { - mHeader.mData.records = count; - } - - void ESMWriter::setFormat (int format) - { - mHeader.mFormat = format; - } - - void ESMWriter::clearMaster() - { - mHeader.mMaster.clear(); - } - - void ESMWriter::addMaster(const std::string& name, uint64_t size) - { - Header::MasterData d; - d.name = name; - d.size = size; - mHeader.mMaster.push_back(d); - } - - void ESMWriter::save(std::ostream& file) - { - mRecordCount = 0; - mRecords.clear(); - mCounting = true; - mStream = &file; - - startRecord("TES3", 0); - - mHeader.save (*this); - - endRecord("TES3"); - } - - void ESMWriter::close() - { - if (!mRecords.empty()) - throw std::runtime_error ("Unclosed record remaining"); - } - - void ESMWriter::startRecord(const std::string& name, uint32_t flags) - { - mRecordCount++; - - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? - writeT(flags); - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::startRecord (uint32_t name, uint32_t flags) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - startRecord (type, flags); - } - - void ESMWriter::startSubRecord(const std::string& name) - { - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::endRecord(const std::string& name) - { - RecordData rec = mRecords.back(); - assert(rec.name == name); - mRecords.pop_back(); - - mStream->seekp(rec.position); - - mCounting = false; - write (reinterpret_cast (&rec.size), sizeof(uint32_t)); - mCounting = true; - - mStream->seekp(0, std::ios::end); - - } - - void ESMWriter::endRecord (uint32_t name) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - endRecord (type); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHString(data); - endRecord(name); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) - { - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); - - if (data.size() < size) - { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); - } - - endRecord(name); - } - - void ESMWriter::writeHString(const std::string& data) - { - if (data.size() == 0) - write("\0", 1); - else - { - // Convert to UTF8 and return - std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; - - write(string.c_str(), string.size()); - } - } - - void ESMWriter::writeHCString(const std::string& data) - { - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); - } - - void ESMWriter::writeName(const std::string& name) - { - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); - } - - void ESMWriter::write(const char* data, size_t size) - { - if (mCounting && !mRecords.empty()) - { - for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) - it->size += size; - } - - mStream->write(data, size); - } - - void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) - { - mEncoder = encoder; - } -} +#include "esmwriter.hpp" + +#include +#include +#include + +namespace ESM +{ + ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} + + unsigned int ESMWriter::getVersion() const + { + return mHeader.mData.version; + } + + void ESMWriter::setVersion(unsigned int ver) + { + mHeader.mData.version = ver; + } + + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } + + void ESMWriter::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } + + void ESMWriter::setRecordCount (int count) + { + mHeader.mData.records = count; + } + + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } + + void ESMWriter::clearMaster() + { + mHeader.mMaster.clear(); + } + + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } + + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mCounting = true; + mStream = &file; + + startRecord("TES3", 0); + + mHeader.save (*this); + + endRecord("TES3"); + } + + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } + + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + writeT(0); // Unused header? + writeT(flags); + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::startRecord (uint32_t name, uint32_t flags) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + startRecord (type, flags); + } + + void ESMWriter::startSubRecord(const std::string& name) + { + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); + + mStream->seekp(rec.position); + + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(uint32_t)); + mCounting = true; + + mStream->seekp(0, std::ios::end); + + } + + void ESMWriter::endRecord (uint32_t name) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + endRecord (type); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHString(data); + endRecord(name); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) + { + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } + + endRecord(name); + } + + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; + + write(string.c_str(), string.size()); + } + } + + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } + + void ESMWriter::writeName(const std::string& name) + { + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); + } + + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } + + mStream->write(data, size); + } + + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } +} diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 304876dbf..33650e678 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -1,114 +1,114 @@ -#ifndef OPENMW_ESM_WRITER_H -#define OPENMW_ESM_WRITER_H - -#include -#include - -#include - -#include "esmcommon.hpp" -#include "loadtes3.hpp" - -namespace ESM { - -class ESMWriter -{ - struct RecordData - { - std::string name; - std::streampos position; - uint32_t size; - }; - - public: - - ESMWriter(); - - unsigned int getVersion() const; - void setVersion(unsigned int ver = 0x3fa66666); - void setEncoder(ToUTF8::Utf8Encoder *encoding); - void setAuthor(const std::string& author); - void setDescription(const std::string& desc); - void setRecordCount (int count); - void setFormat (int format); - - void clearMaster(); - - void addMaster(const std::string& name, uint64_t size); - - void save(std::ostream& file); - ///< Start saving a file by writing the TES3 header. - - void close(); - ///< \note Does not close the stream. - - void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, size_t size); - void writeHNCString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHCString(data); - endRecord(name); - } - void writeHNOString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNString(name, data); - } - void writeHNOCString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNCString(name, data); - } - - template - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template - void writeT(const T& data, size_t size) - { - write((char*)&data, size); - } - - void startRecord(const std::string& name, uint32_t flags = 0); - void startRecord(uint32_t name, uint32_t flags = 0); - void startSubRecord(const std::string& name); - void endRecord(const std::string& name); - void endRecord(uint32_t name); - void writeHString(const std::string& data); - void writeHCString(const std::string& data); - void writeName(const std::string& data); - void write(const char* data, size_t size); - - private: - std::list mRecords; - std::ostream* mStream; - std::streampos mHeaderPos; - ToUTF8::Utf8Encoder* mEncoder; - int mRecordCount; - bool mCounting; - - Header mHeader; - }; -} - -#endif +#ifndef OPENMW_ESM_WRITER_H +#define OPENMW_ESM_WRITER_H + +#include +#include + +#include + +#include "esmcommon.hpp" +#include "loadtes3.hpp" + +namespace ESM { + +class ESMWriter +{ + struct RecordData + { + std::string name; + std::streampos position; + uint32_t size; + }; + + public: + + ESMWriter(); + + unsigned int getVersion() const; + void setVersion(unsigned int ver = 0x3fa66666); + void setEncoder(ToUTF8::Utf8Encoder *encoding); + void setAuthor(const std::string& author); + void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); + + void clearMaster(); + + void addMaster(const std::string& name, uint64_t size); + + void save(std::ostream& file); + ///< Start saving a file by writing the TES3 header. + + void close(); + ///< \note Does not close the stream. + + void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, size_t size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHCString(data); + endRecord(name); + } + void writeHNOString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNString(name, data); + } + void writeHNOCString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNCString(name, data); + } + + template + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template + void writeT(const T& data, size_t size) + { + write((char*)&data, size); + } + + void startRecord(const std::string& name, uint32_t flags = 0); + void startRecord(uint32_t name, uint32_t flags = 0); + void startSubRecord(const std::string& name); + void endRecord(const std::string& name); + void endRecord(uint32_t name); + void writeHString(const std::string& data); + void writeHCString(const std::string& data); + void writeName(const std::string& data); + void write(const char* data, size_t size); + + private: + std::list mRecords; + std::ostream* mStream; + std::streampos mHeaderPos; + ToUTF8::Utf8Encoder* mEncoder; + int mRecordCount; + bool mCounting; + + Header mHeader; + }; +} + +#endif diff --git a/credits.txt b/credits.txt index 561931cde..601255763 100644 --- a/credits.txt +++ b/credits.txt @@ -20,6 +20,7 @@ Artem Kotsynyak (greye) athile Britt Mathis (galdor557) BrotherBrick +cc9cii Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) diff --git a/readme.txt b/readme.txt index 4cc9c6234..a23cd1077 100644 --- a/readme.txt +++ b/readme.txt @@ -1,951 +1,951 @@ -OpenMW: A reimplementation of The Elder Scrolls III: Morrowind - -OpenMW is an attempt at recreating the engine for the popular role-playing game -Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. - -Version: 0.28.0 -License: GPL (see GPL3.txt for more information) -Website: http://www.openmw.org - -Font Licenses: -EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) -DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) - - - -INSTALLATION - -Windows: -Run the installer. - -Linux: -Ubuntu (and most others) -Download the .deb file and install it in the usual way. - -Arch Linux -There's an OpenMW package available in the [community] Repository: -https://www.archlinux.org/packages/?sort=&q=openmw - -OS X: -Open DMG file, copy OpenMW folder anywhere, for example in /Applications - -BUILD FROM SOURCE - -https://wiki.openmw.org/index.php?title=Development_Environment_Setup - - -THE DATA PATH - -The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to -pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly -(installing Morrowind under WINE is considered a proper install). - -COMMAND LINE OPTIONS - -Syntax: openmw -Allowed options: - --help print help message - --version print version information and quit - --data arg (=data) set data directories (later directories - have higher priority) - --data-local arg set local data directory (highest - priority) - --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later - archives have higher priority) - --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or - omwgame/omwaddon - --anim-verbose [=arg(=1)] (=0) output animation indices files - --no-sound [=arg(=1)] (=0) disable all sounds - --script-verbose [=arg(=1)] (=0) verbose script output - --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue - scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script - functionality - --script-run arg select a file containing a list of - console commands that is executed on - startup - --script-warn [=arg(=1)] (=1) handling of warnings when compiling - scripts - 0 - ignore warning - 1 - show warning but consider script as - correctly compiled anyway - 2 - treat warnings as errors - --skip-menu [=arg(=1)] (=0) skip main menu on game startup - --fs-strict [=arg(=1)] (=0) strict file system handling (no case - folding) - --encoding arg (=win1252) Character encoding used in OpenMW game - messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and - Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic - and other languages - - win1252 - Western European (Latin) - alphabet, used by default - --fallback arg fallback values - --no-grab Don't grab mouse cursor - --activate-dist arg (=-1) activation distance override - -CHANGELOG - -0.29.0 - -Bug #556: Video soundtrack not played when music volume is set to zero -Bug #829: OpenMW uses up all available vram, when playing for extended time -Bug #848: Wrong amount of footsteps playing in 1st person -Bug #888: Ascended Sleepers have movement issues -Bug #892: Explicit references are allowed on all script functions -Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly -Bug #1009: Lake Fjalding AI related slowdown. -Bug #1041: Music playback issues on OS X >= 10.9 -Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window -Bug #1060: Some message boxes are cut off at the bottom -Bug #1062: Bittercup script does not work ('end' variable) -Bug #1074: Inventory paperdoll obscures armour rating -Bug #1077: Message after killing an essential NPC disappears too fast -Bug #1078: "Clutterbane" shows empty charge bar -Bug #1083: UndoWerewolf fails -Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered -Bug #1090: Start scripts fail when going to a non-predefined cell -Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. -Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior -Bug #1105: Magicka is depleted when using uncastable spells -Bug #1106: Creatures should be able to run -Bug #1107: TR cliffs have way too huge collision boxes in OpenMW -Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. -Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) -Bug #1115: Memory leak when spying on Fargoth -Bug #1137: Script execution fails (drenSlaveOwners script) -Bug #1143: Mehra Milo quest (vivec informants) is broken -Bug #1145: Issues with moving gold between inventory and containers -Bug #1146: Issues with picking up stacks of gold -Bug #1147: Dwemer Crossbows are held incorrectly -Bug #1158: Armor rating should always stay below inventory mannequin -Bug #1159: Quick keys can be set during character generation -Bug #1160: Crash on equip lockpick when -Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file -Feature #30: Loading/Saving (still missing a few parts) -Feature #101: AI Package: Activate -Feature #103: AI Package: Follow, FollowCell -Feature #138: Editor: Drag & Drop -Feature #428: Player death -Feature #505: Editor: Record Cloning -Feature #701: Levelled creatures -Feature #708: Improved Local Variable handling -Feature #709: Editor: Script verifier -Feature #764: Missing journal backend features -Feature #777: Creature weapons/shields -Feature #789: Editor: Referenceable record verifier -Feature #924: Load/Save GUI (still missing loading screen and progress bars) -Feature #946: Knockdown -Feature #947: Decrease fatigue when running, swimming and attacking -Feature #956: Melee Combat: Blocking -Feature #957: Area magic -Feature #960: Combat/AI combat for creatures -Feature #962: Combat-Related AI instructions -Feature #1075: Damage/Restore skill/attribute magic effects -Feature #1076: Soultrap magic effect -Feature #1081: Disease contraction -Feature #1086: Blood particles -Feature #1092: Interrupt resting -Feature #1101: Inventory equip scripts -Feature #1116: Version/Build number in Launcher window -Feature #1119: Resistance/weakness to normal weapons magic effect -Feature #1123: Slow Fall magic effect -Feature #1130: Auto-calculate spells -Feature #1164: Editor: Case-insensitive sorting in tables - -0.28.0 - -Bug #399: Inventory changes are not visible immediately -Bug #417: Apply weather instantly when teleporting -Bug #566: Global Map position marker not updated for interior cells -Bug #712: Looting corpse delay -Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod -Bug #805: Two TR meshes appear black (v0.24RC) -Bug #841: Third-person activation distance taken from camera rather than head -Bug #845: NPCs hold torches during the day -Bug #855: Vvardenfell Visages Volume I some hairs donĀ“t appear since 0,24 -Bug #856: Maormer race by Mac Kom - The heads are way up -Bug #864: Walk locks during loading in 3rd person -Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog -Bug #882: Hircine's Ring doesn't always work -Bug #909: [Tamriel Rebuilt] crashes in Akamora -Bug #922: Launcher writing merged openmw.cfg files -Bug #943: Random magnitude should be calculated per effect -Bug #948: Negative fatigue level should be allowed -Bug #949: Particles in world space -Bug #950: Hard crash on x64 Linux running --new-game (on startup) -Bug #951: setMagicka and setFatigue have no effect -Bug #954: Problem with equipping inventory items when using a keyboard shortcut -Bug #955: Issues with equipping torches -Bug #966: Shield is visible when casting spell -Bug #967: Game crashes when equipping silver candlestick -Bug #970: Segmentation fault when starting at Bal Isra -Bug #977: Pressing down key in console doesn't go forward in history -Bug #979: Tooltip disappears when changing inventory -Bug #980: Barter: item category is remembered, but not shown -Bug #981: Mod: replacing model has wrong position/orientation -Bug #982: Launcher: Addon unchecking is not saved -Bug #983: Fix controllers to affect objects attached to the base node -Bug #985: Player can talk to NPCs who are in combat -Bug #989: OpenMW crashes when trying to include mod with capital .ESP -Bug #991: Merchants equip items with harmful constant effect enchantments -Bug #994: Don't cap skills/attributes when set via console -Bug #998: Setting the max health should also set the current health -Bug #1005: Torches are visible when casting spells and during hand to hand combat. -Bug #1006: Many NPCs have 0 skill -Bug #1007: Console fills up with text -Bug #1013: Player randomly loses health or dies -Bug #1014: Persuasion window is not centered in maximized window -Bug #1015: Player status window scroll state resets on status change -Bug #1016: Notification window not big enough for all skill level ups -Bug #1020: Saved window positions are not rescaled appropriately on resolution change -Bug #1022: Messages stuck permanently on screen when they pile up -Bug #1023: Journals doesn't open -Bug #1026: Game loses track of torch usage. -Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level -Bug #1029: Quick keys menu: Select compatible replacement when tool used up -Bug #1042: TES3 header data wrong encoding -Bug #1045: OS X: deployed OpenCS won't launch -Bug #1046: All damaged weaponry is worth 1 gold -Bug #1048: Links in "locked" dialogue are still clickable -Bug #1052: Using color codes when naming your character actually changes the name's color -Bug #1054: Spell effects not visible in front of water -Bug #1055: Power-Spell animation starts even though you already casted it that day -Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability -Bug #1063: Crash upon checking out game start ship area in Seyda Neen -Bug #1064: openmw binaries link to unnecessary libraries -Bug #1065: Landing from a high place in water still causes fall damage -Bug #1072: Drawing weapon increases torch brightness -Bug #1073: Merchants sell stacks of gold -Feature #43: Visuals for Magic Effects -Feature #51: Ranged Magic -Feature #52: Touch Range Magic -Feature #53: Self Range Magic -Feature #54: Spell Casting -Feature #70: Vampirism -Feature #100: Combat AI -Feature #171: Implement NIF record NiFlipController -Feature #410: Window to restore enchanted item charge -Feature #647: Enchanted item glow -Feature #723: Invisibility/Chameleon magic effects -Feature #737: Resist Magicka magic effect -Feature #758: GetLOS -Feature #926: Editor: Info-Record tables -Feature #958: Material controllers -Feature #959: Terrain bump, specular, & parallax mapping -Feature #990: Request: unlock mouse when in any menu -Feature #1018: Do not allow view mode switching while performing an action -Feature #1027: Vertex morph animation (NiGeomMorpherController) -Feature #1031: Handle NiBillboardNode -Feature #1051: Implement NIF texture slot DarkTexture -Task #873: Unify OGRE initialisation - -0.27.0 - -Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp -Bug #794: incorrect display of decimal numbers -Bug #840: First-person sneaking camera height -Bug #887: Ambient sounds playing while paused -Bug #902: Problems with Polish character encoding -Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key -Bug #910: Some CDs not working correctly with Unshield installer -Bug #917: Quick character creation plugin does not work -Bug #918: Fatigue does not refill -Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) -Feature #57: Acrobatics Skill -Feature #462: Editor: Start Dialogue -Feature #546: Modify ESX selector to handle new content file scheme -Feature #588: Editor: Adjust name/path of edited content files -Feature #644: Editor: Save -Feature #710: Editor: Configure script compiler context -Feature #790: God Mode -Feature #881: Editor: Allow only one instance of OpenCS -Feature #889: Editor: Record filtering -Feature #895: Extinguish torches -Feature #898: Breath meter enhancements -Feature #901: Editor: Default record filter -Feature #913: Merge --master and --plugin switches - -0.26.0 - -Bug #274: Inconsistencies in the terrain -Bug #557: Already-dead NPCs do not equip clothing/items. -Bug #592: Window resizing -Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) -Bug #664: Heart of lorkhan acts like a dead body (container) -Bug #767: Wonky ramp physics & water -Bug #780: Swimming out of water -Bug #792: Wrong ground alignment on actors when no clipping -Bug #796: Opening and closing door sound issue -Bug #797: No clipping hinders opening and closing of doors -Bug #799: sliders in enchanting window -Bug #838: Pressing key during startup procedure freezes the game -Bug #839: Combat/magic stances during character creation -Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment -Bug #844: Resting "until healed" option given even with full stats -Bug #846: Equipped torches are invisible. -Bug #847: Incorrect formula for autocalculated NPC initial health -Bug #850: Shealt weapon sound plays when leaving magic-ready stance -Bug #852: Some boots do not produce footstep sounds -Bug #860: FPS bar misalignment -Bug #861: Unable to print screen -Bug #863: No sneaking and jumping at the same time -Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. -Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. -Bug #868: Idle animations are repeated -Bug #874: Underwater swimming close to the ground is jerky -Bug #875: Animation problem while swimming on the surface and looking up -Bug #876: Always a starting upper case letter in the inventory -Bug #878: Active spell effects don't update the layout properly when ended -Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load -Bug #896: New game sound issue -Feature #49: Melee Combat -Feature #71: Lycanthropy -Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList -Feature #622: Multiple positions for inventory window -Feature #627: Drowning -Feature #786: Allow the 'Activate' key to close the countdialog window -Feature #798: Morrowind installation via Launcher (Linux/Max OS only) -Feature #851: First/Third person transitions with mouse wheel -Task #689: change PhysicActor::enableCollisions -Task #707: Reorganise Compiler - -0.25.0 - -Bug #411: Launcher crash on OS X < 10.8 -Bug #604: Terrible performance drop in the Census and Excise Office. -Bug #676: Start Scripts fail to load -Bug #677: OpenMW does not accept script names with - -Bug #766: Extra space in front of topic links -Bug #793: AIWander Isn't Being Passed The Repeat Parameter -Bug #795: Sound playing with drawn weapon and crossing cell-border -Bug #800: can't select weapon for enchantment -Bug #801: Player can move while over-encumbered -Bug #802: Dead Keys not working -Bug #808: mouse capture -Bug #809: ini Importer does not work without an existing cfg file -Bug #812: Launcher will run OpenMW with no ESM or ESP selected -Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected -Bug #817: Dead NPCs and Creatures still have collision boxes -Bug #820: Incorrect sorting of answers (Dialogue) -Bug #826: mwinimport dumps core when given an unknown parameter -Bug #833: getting stuck in door -Bug #835: Journals/books not showing up properly. -Feature #38: SoundGen -Feature #105: AI Package: Wander -Feature #230: 64-bit compatibility for OS X -Feature #263: Hardware mouse cursors -Feature #449: Allow mouse outside of window while paused -Feature #736: First person animations -Feature #750: Using mouse wheel in third person mode -Feature #822: Autorepeat for slider buttons - -0.24.0 - -Bug #284: Book's text misalignment -Bug #445: Camera able to get slightly below floor / terrain -Bug #582: Seam issue in Red Mountain -Bug #632: Journal Next Button shows white square -Bug #653: IndexedStore ignores index -Bug #694: Parser does not recognize float values starting with . -Bug #699: Resource handling broken with Ogre 1.9 trunk -Bug #718: components/esm/loadcell is using the mwworld subsystem -Bug #729: Levelled item list tries to add nonexistent item -Bug #730: Arrow buttons in the settings menu do not work. -Bug #732: Erroneous behavior when binding keys -Bug #733: Unclickable dialogue topic -Bug #734: Book empty line problem -Bug #738: OnDeath only works with implicit references -Bug #740: Script compiler fails on scripts with special names -Bug #742: Wait while no clipping -Bug #743: Problem with changeweather console command -Bug #744: No wait dialogue after starting a new game -Bug #748: Player is not able to unselect objects with the console -Bug #751: AddItem should only spawn a message box when called from dialogue -Bug #752: The enter button has several functions in trade and looting that is not impelemted. -Bug #753: Fargoth's Ring Quest Strange Behavior -Bug #755: Launcher writes duplicate lines into settings.cfg -Bug #759: Second quest in mages guild does not work -Bug #763: Enchantment cast cost is wrong -Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly -Bug #773: AIWander Isn't Being Passed The Correct idle Values -Bug #778: The journal can be opened at the start of a new game -Bug #779: Divayth Fyr starts as dead -Bug #787: "Batch count" on detailed FPS counter gets cut-off -Bug #788: chargen scroll layout does not match vanilla -Feature #60: Atlethics Skill -Feature #65: Security Skill -Feature #74: Interaction with non-load-doors -Feature #98: Render Weapon and Shield -Feature #102: AI Package: Escort, EscortCell -Feature #182: Advanced Journal GUI -Feature #288: Trading enhancements -Feature #405: Integrate "new game" into the menu -Feature #537: Highlight dialogue topic links -Feature #658: Rotate, RotateWorld script instructions and local rotations -Feature #690: Animation Layering -Feature #722: Night Eye/Blind magic effects -Feature #735: Move, MoveWorld script instructions. -Feature #760: Non-removable corpses - -0.23.0 - -Bug #522: Player collides with placeable items -Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open -Bug #561: Tooltip word wrapping delay -Bug #578: Bribing works incorrectly -Bug #601: PositionCell fails on negative coordinates -Bug #606: Some NPCs hairs not rendered with Better Heads addon -Bug #609: Bad rendering of bone boots -Bug #613: Messagebox causing assert to fail -Bug #631: Segfault on shutdown -Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard -Bug #635: Scale NPCs depending on race -Bug #643: Dialogue Race select function is inverted -Bug #646: Twohanded weapons don't work properly -Bug #654: Crash when dropping objects without a collision shape -Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell -Bug #660: "g" in "change" cut off in Race Menu -Bug #661: Arrille sells me the key to his upstairs room -Bug #662: Day counter starts at 2 instead of 1 -Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur -Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. -Bug #666: Looking up/down problem -Bug #667: Active effects border visible during loading -Bug #669: incorrect player position at new game start -Bug #670: race selection menu: sex, face and hair left button not totally clickable -Bug #671: new game: player is naked -Bug #674: buying or selling items doesn't change amount of gold -Bug #675: fatigue is not set to its maximum when starting a new game -Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly -Bug #680: different gold coins in Tel Mara -Bug #682: Race menu ignores playable flag for some hairs and faces -Bug #685: Script compiler does not accept ":" after a function name -Bug #688: dispose corpse makes cross-hair to disappear -Bug #691: Auto equipping ignores equipment conditions -Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder -Bug #696: Draugr incorrect head offset -Bug #697: Sail transparency issue -Bug #700: "On the rocks" mod does not load its UV coordinates correctly. -Bug #702: Some race mods don't work -Bug #711: Crash during character creation -Bug #715: Growing Tauryon -Bug #725: Auto calculate stats -Bug #728: Failure to open container and talk dialogue -Bug #731: Crash with Mush-Mere's "background" topic -Feature #55/657: Item Repairing -Feature #62/87: Enchanting -Feature #99: Pathfinding -Feature #104: AI Package: Travel -Feature #129: Levelled items -Feature #204: Texture animations -Feature #239: Fallback-Settings -Feature #535: Console object selection improvements -Feature #629: Add levelup description in levelup layout dialog -Feature #630: Optional format subrecord in (tes3) header -Feature #641: Armor rating -Feature #645: OnDeath script function -Feature #683: Companion item UI -Feature #698: Basic Particles -Task #648: Split up components/esm/loadlocks -Task #695: mwgui cleanup - -0.22.0 - -Bug #311: Potential infinite recursion in script compiler -Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. -Bug #382: Weird effect in 3rd person on water -Bug #387: Always use detailed shape for physics raycasts -Bug #420: Potion/ingredient effects do not stack -Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips -Bug #434/Bug #605: Object movement between cells not properly implemented -Bug #502: Duplicate player collision model at origin -Bug #509: Dialogue topic list shifts inappropriately -Bug #513: Sliding stairs -Bug #515: Launcher does not support non-latin strings -Bug #525: Race selection preview camera wrong position -Bug #526: Attributes / skills should not go below zero -Bug #529: Class and Birthsign menus options should be preselected -Bug #530: Lock window button graphic missing -Bug #532: Missing map menu graphics -Bug #545: ESX selector does not list ESM files properly -Bug #547: Global variables of type short are read incorrectly -Bug #550: Invisible meshes collision and tooltip -Bug #551: Performance drop when loading multiple ESM files -Bug #552: Don't list CG in options if it is not available -Bug #555: Character creation windows "OK" button broken -Bug #558: Segmentation fault when Alt-tabbing with console opened -Bug #559: Dialog window should not be available before character creation is finished -Bug #560: Tooltip borders should be stretched -Bug #562: Sound should not be played when an object cannot be picked up -Bug #565: Water animation speed + timescale -Bug #572: Better Bodies' textures don't work -Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) -Bug #574: Moving left/right should not cancel auto-run -Bug #575: Crash entering the Chamber of Song -Bug #576: Missing includes -Bug #577: Left Gloves Addon causes ESMReader exception -Bug #579: Unable to open container "Kvama Egg Sack" -Bug #581: Mimicking vanilla Morrowind water -Bug #583: Gender not recognized -Bug #586: Wrong char gen behaviour -Bug #587: "End" script statements with spaces don't work -Bug #589: Closing message boxes by pressing the activation key -Bug #590: Ugly Dagoth Ur rendering -Bug #591: Race selection issues -Bug #593: Persuasion response should be random -Bug #595: Footless guard -Bug #599: Waterfalls are invisible from a certain distance -Bug #600: Waterfalls rendered incorrectly, cut off by water -Bug #607: New beast bodies mod crashes -Bug #608: Crash in cell "Mournhold, Royal Palace" -Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt -Bug #613: Messagebox causing assert to fail -Bug #615: Meshes invisible from above water -Bug #617: Potion effects should be hidden until discovered -Bug #619: certain moss hanging from tree has rendering bug -Bug #621: Batching bloodmoon's trees -Bug #623: NiMaterialProperty alpha unhandled -Bug #628: Launcher in latest master crashes the game -Bug #633: Crash on startup: Better Heads -Bug #636: Incorrect Char Gen Menu Behavior -Feature #29: Allow ESPs and multiple ESMs -Feature #94: Finish class selection-dialogue -Feature #149: Texture Alphas -Feature #237: Run Morrowind-ini importer from launcher -Feature #286: Update Active Spell Icons -Feature #334: Swimming animation -Feature #335: Walking animation -Feature #360: Proper collision shapes for NPCs and creatures -Feature #367: Lights that behave more like original morrowind implementation -Feature #477: Special local scripting variables -Feature #528: Message boxes should close when enter is pressed under certain conditions. -Feature #543: Add bsa files to the settings imported by the ini importer -Feature #594: coordinate space and utility functions -Feature #625: Zoom in vanity mode -Task #464: Refactor launcher ESX selector into a re-usable component -Task #624: Unified implementation of type-variable sub-records - -0.21.0 - -Bug #253: Dialogs don't work for Russian version of Morrowind -Bug #267: Activating creatures without dialogue can still activate the dialogue GUI -Bug #354: True flickering lights -Bug #386: The main menu's first entry is wrong (in french) -Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations -Bug #495: Activation Range -Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned -Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available -Bug #500: Disposition for most NPCs is 0/100 -Bug #501: Getdisposition command wrongly returns base disposition -Bug #506: Journal UI doesn't update anymore -Bug #507: EnableRestMenu is not a valid command - change it to EnableRest -Bug #508: Crash in Ald Daedroth Shrine -Bug #517: Wrong price calculation when untrading an item -Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin -Bug #524: Beast races are able to wear shoes -Bug #527: Background music fails to play -Bug #533: The arch at Gnisis entrance is not displayed -Bug #534: Terrain gets its correct shape only some time after the cell is loaded -Bug #536: The same entry can be added multiple times to the journal -Bug #539: Race selection is broken -Bug #544: Terrain normal map corrupt when the map is rendered -Feature #39: Video Playback -Feature #151: ^-escape sequences in text output -Feature #392: Add AI related script functions -Feature #456: Determine required ini fallback values and adjust the ini importer accordingly -Feature #460: Experimental DirArchives improvements -Feature #540: Execute scripts of objects in containers/inventories in active cells -Task #401: Review GMST fixing -Task #453: Unify case smashing/folding -Task #512: Rewrite utf8 component - -0.20.0 - -Bug #366: Changing the player's race during character creation does not change the look of the player character -Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell -Bug #437: Stop animations when paused -Bug #438: Time displays as "0 a.m." when it should be "12 a.m." -Bug #439: Text in "name" field of potion/spell creation window is persistent -Bug #440: Starting date at a new game is off by one day -Bug #442: Console window doesn't close properly sometimes -Bug #448: Do not break container window formatting when item names are very long -Bug #458: Topics sometimes not automatically added to known topic list -Bug #476: Auto-Moving allows player movement after using DisablePlayerControls -Bug #478: After sleeping in a bed the rest dialogue window opens automtically again -Bug #492: On creating potions the ingredients are removed twice -Feature #63: Mercantile skill -Feature #82: Persuasion Dialogue -Feature #219: Missing dialogue filters/functions -Feature #369: Add a FailedAction -Feature #377: Select head/hair on character creation -Feature #391: Dummy AI package classes -Feature #435: Global Map, 2nd Layer -Feature #450: Persuasion -Feature #457: Add more script instructions -Feature #474: update the global variable pcrace when the player's race is changed -Task #158: Move dynamically generated classes from Player class to World Class -Task #159: ESMStore rework and cleanup -Task #163: More Component Namespace Cleanup -Task #402: Move player data from MWWorld::Player to the player's NPC record -Task #446: Fix no namespace in BulletShapeLoader - -0.19.0 - -Bug #374: Character shakes in 3rd person mode near the origin -Bug #404: Gamma correct rendering -Bug #407: Shoes of St. Rilm do not work -Bug #408: Rugs has collision even if they are not supposed to -Bug #412: Birthsign menu sorted incorrectly -Bug #413: Resolutions presented multiple times in launcher -Bug #414: launcher.cfg file stored in wrong directory -Bug #415: Wrong esm order in openmw.cfg -Bug #418: Sound listener position updates incorrectly -Bug #423: wrong usage of "Version" entry in openmw.desktop -Bug #426: Do not use hardcoded splash images -Bug #431: Don't use markers for raycast -Bug #432: Crash after picking up items from an NPC -Feature #21/#95: Sleeping/resting -Feature #61: Alchemy Skill -Feature #68: Death -Feature #69/#86: Spell Creation -Feature #72/#84: Travel -Feature #76: Global Map, 1st Layer -Feature #120: Trainer Window -Feature #152: Skill Increase from Skill Books -Feature #160: Record Saving -Task #400: Review GMST access - -0.18.0 - -Bug #310: Button of the "preferences menu" are too small -Bug #361: Hand-to-hand skill is always 100 -Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to -Bug #372: playSound3D uses original coordinates instead of current coordinates. -Bug #373: Static OGRE build faulty -Bug #375: Alt-tab toggle view -Bug #376: Screenshots are disable -Bug #378: Exception when drinking self-made potions -Bug #380: Cloth visibility problem -Bug #384: Weird character on doors tooltip. -Bug #398: Some objects do not collide in MW, but do so in OpenMW -Feature #22: Implement level-up -Feature #36: Hide Marker -Feature #88: Hotkey Window -Feature #91: Level-Up Dialogue -Feature #118: Keyboard and Mouse-Button bindings -Feature #119: Spell Buying Window -Feature #133: Handle resources across multiple data directories -Feature #134: Generate a suitable default-value for --data-local -Feature #292: Object Movement/Creation Script Instructions -Feature #340: AIPackage data structures -Feature #356: Ingredients use -Feature #358: Input system rewrite -Feature #370: Target handling in actions -Feature #379: Door markers on the local map -Feature #389: AI framework -Feature #395: Using keys to open doors / containers -Feature #396: Loading screens -Feature #397: Inventory avatar image and race selection head preview -Task #339: Move sounds into Action - -0.17.0 - -Bug #225: Valgrind reports about 40MB of leaked memory -Bug #241: Some physics meshes still don't match -Bug #248: Some textures are too dark -Bug #300: Dependency on proprietary CG toolkit -Bug #302: Some objects don't collide although they should -Bug #308: Freeze in Balmora, Meldor: Armorer -Bug #313: openmw without a ~/.config/openmw folder segfault. -Bug #317: adding non-existing spell via console locks game -Bug #318: Wrong character normals -Bug #341: Building with Ogre Debug libraries does not use debug version of plugins -Bug #347: Crash when running openmw with --start="XYZ" -Bug #353: FindMyGUI.cmake breaks path on Windows -Bug #359: WindowManager throws exception at destruction -Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation -Feature #33: Allow objects to cross cell-borders -Feature #59: Dropping Items (replaced stopgap implementation with a proper one) -Feature #93: Main Menu -Feature #96/329/330/331/332/333: Player Control -Feature #180: Object rotation and scaling. -Feature #272: Incorrect NIF material sharing -Feature #314: Potion usage -Feature #324: Skill Gain -Feature #342: Drain/fortify dynamic stats/attributes magic effects -Feature #350: Allow console only script instructions -Feature #352: Run scripts in console on startup -Task #107: Refactor mw*-subsystems -Task #325: Make CreatureStats into a class -Task #345: Use Ogre's animation system -Task #351: Rewrite Action class to support automatic sound playing - -0.16.0 - -Bug #250: OpenMW launcher erratic behaviour -Bug #270: Crash because of underwater effect on OS X -Bug #277: Auto-equipping in some cells not working -Bug #294: Container GUI ignores disabled inventory menu -Bug #297: Stats review dialog shows all skills and attribute values as 0 -Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses -Bug #299: Crash in World::disable -Bug #306: Non-existent ~/.config/openmw "crash" the launcher. -Bug #307: False "Data Files" location make the launcher "crash" -Feature #81: Spell Window -Feature #85: Alchemy Window -Feature #181: Support for x.y script syntax -Feature #242: Weapon and Spell icons -Feature #254: Ingame settings window -Feature #293: Allow "stacking" game modes -Feature #295: Class creation dialog tooltips -Feature #296: Clicking on the HUD elements should show/hide the respective window -Feature #301: Direction after using a Teleport Door -Feature #303: Allow object selection in the console -Feature #305: Allow the use of = as a synonym for == -Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts -Task #176: Restructure enabling/disabling of MW-references -Task #283: Integrate ogre.cfg file in settings file -Task #290: Auto-Close MW-reference related GUI windows - -0.15.0 - -Bug #5: Physics reimplementation (fixes various issues) -Bug #258: Resizing arrow's background is not transparent -Bug #268: Widening the stats window in X direction causes layout problems -Bug #269: Topic pane in dialgoue window is too small for some longer topics -Bug #271: Dialog choices are sorted incorrectly -Bug #281: The single quote character is not rendered on dialog windows -Bug #285: Terrain not handled properly in cells that are not predefined -Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs -Feature #15: Collision with Terrain -Feature #17: Inventory-, Container- and Trade-Windows -Feature #44: Floating Labels above Focussed Objects -Feature #80: Tooltips -Feature #83: Barter Dialogue -Feature #90: Book and Scroll Windows -Feature #156: Item Stacking in Containers -Feature #213: Pulsating lights -Feature #218: Feather & Burden -Feature #256: Implement magic effect bookkeeping -Feature #259: Add missing information to Stats window -Feature #260: Correct case for dialogue topics -Feature #280: GUI texture atlasing -Feature #291: Ability to use GMST strings from GUI layout files -Task #255: Make MWWorld::Environment into a singleton - -0.14.0 - -Bug #1: Meshes rendered with wrong orientation -Bug #6/Task #220: Picking up small objects doesn't always work -Bug #127: tcg doesn't work -Bug #178: Compablity problems with Ogre 1.8.0 RC 1 -Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI -Bug #227: Terrain crashes when moving away from predefined cells -Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces -Bug #235: TGA texture loading problem -Bug #246: wireframe mode does not work in water -Feature #8/#232: Water Rendering -Feature #13: Terrain Rendering -Feature #37: Render Path Grid -Feature #66: Factions -Feature #77: Local Map -Feature #78: Compass/Mini-Map -Feature #97: Render Clothing/Armour -Feature #121: Window Pinning -Feature #205: Auto equip -Feature #217: Contiainer should track changes to its content -Feature #221: NPC Dialogue Window Enhancements -Feature #233: Game settings manager -Feature #240: Spell List and selected spell (no GUI yet) -Feature #243: Draw State -Task #113: Morrowind.ini Importer -Task #215: Refactor the sound code -Task #216: Update MyGUI - -0.13.0 - -Bug #145: Fixed sound problems after cell change -Bug #179: Pressing space in console triggers activation -Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux -Bug #189: ASCII 16 character added to console on it's activation on Mac OS X -Bug #190: Case Folding fails with music files -Bug #192: Keypresses write Text into Console no matter which gui element is active -Bug #196: Collision shapes out of place -Bug #202: ESMTool doesn't not work with localised ESM files anymore -Bug #203: Torch lights only visible on short distance -Bug #207: Ogre.log not written -Bug #209: Sounds do not play -Bug #210: Ogre crash at Dren plantation -Bug #214: Unsupported file format version -Bug #222: Launcher is writing openmw.cfg file to wrong location -Feature #9: NPC Dialogue Window -Feature #16/42: New sky/weather implementation -Feature #40: Fading -Feature #48: NPC Dialogue System -Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) -Feature #161: Load REC_PGRD records -Feature #195: Wireframe-mode -Feature #198/199: Various sound effects -Feature #206: Allow picking data path from launcher if non is set -Task #108: Refactor window manager class -Task #172: Sound Manager Cleanup -Task #173: Create OpenEngine systems in the appropriate manager classes -Task #184: Adjust MSVC and gcc warning levels -Task #185: RefData rewrite -Task #201: Workaround for transparency issues -Task #208: silenced esm_reader.hpp warning - -0.12.0 - -Bug #154: FPS Drop -Bug #169: Local scripts continue running if associated object is deleted -Bug #174: OpenMW fails to start if the config directory doesn't exist -Bug #187: Missing lighting -Bug #188: Lights without a mesh are not rendered -Bug #191: Taking screenshot causes crash when running installed -Feature #28: Sort out the cell load problem -Feature #31: Allow the player to move away from pre-defined cells -Feature #35: Use alternate storage location for modified object position -Feature #45: NPC animations -Feature #46: Creature Animation -Feature #89: Basic Journal Window -Feature #110: Automatically pick up the path of existing MW-installations -Feature #183: More FPS display settings -Task #19: Refactor engine class -Task #109/Feature #162: Automate Packaging -Task #112: Catch exceptions thrown in input handling functions -Task #128/#168: Cleanup Configuration File Handling -Task #131: NPC Activation doesn't work properly -Task #144: MWRender cleanup -Task #155: cmake cleanup - -0.11.1 - -Bug #2: Resources loading doesn't work outside of bsa files -Bug #3: GUI does not render non-English characters -Bug #7: openmw.cfg location doesn't match -Bug #124: The TCL alias for ToggleCollision is missing. -Bug #125: Some command line options can't be used from a .cfg file -Bug #126: Toggle-type script instructions are less verbose compared with original MW -Bug #130: NPC-Record Loading fails for some NPCs -Bug #167: Launcher sets invalid parameters in ogre config -Feature #10: Journal -Feature #12: Rendering Optimisations -Feature #23: Change Launcher GUI to a tabbed interface -Feature #24: Integrate the OGRE settings window into the launcher -Feature #25: Determine openmw.cfg location (Launcher) -Feature #26: Launcher Profiles -Feature #79: MessageBox -Feature #116: Tab-Completion in Console -Feature #132: --data-local and multiple --data -Feature #143: Non-Rendering Performance-Optimisations -Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs -Feature #157: Version Handling -Task #14: Replace tabs with 4 spaces -Task #18: Move components from global namespace into their own namespace -Task #123: refactor header files in components/esm - -0.10.0 - -* NPC dialogue window (not functional yet) -* Collisions with objects -* Refactor the PlayerPos class -* Adjust file locations -* CMake files and test linking for Bullet -* Replace Ogre raycasting test for activation with something more precise -* Adjust player movement according to collision results -* FPS display -* Various Portability Improvements -* Mac OS X support is back! - -0.9.0 - -* Exterior cells loading, unloading and management -* Character Creation GUI -* Character creation -* Make cell names case insensitive when doing internal lookups -* Music player -* NPCs rendering - -0.8.0 - -* GUI -* Complete and working script engine -* In game console -* Sky rendering -* Sound and music -* Tons of smaller stuff - -0.7.0 - -* This release is a complete rewrite in C++. -* All D code has been culled, and all modules have been rewritten. -* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. - -0.6.0 - -* Coded a GUI system using MyGUI -* Skinned MyGUI to look like Morrowind (work in progress) -* Integrated the Monster script engine -* Rewrote some functions into script code -* Very early MyGUI < > Monster binding -* Fixed Windows sound problems (replaced old openal32.dll) - -0.5.0 - -* Collision detection with Bullet -* Experimental walk & fall character physics -* New key bindings: - * t toggle physics mode (walking, flying, ghost), - * n night eye, brightens the scene -* Fixed incompatability with DMD 1.032 and newer compilers -* * (thanks to tomqyp) -* Various minor changes and updates - -0.4.0 - -* Switched from Audiere to OpenAL -* * (BIG thanks to Chris Robinson) -* Added complete Makefile (again) as a alternative build tool -* More realistic lighting (thanks again to Chris Robinson) -* Various localization fixes tested with Russian and French versions -* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' -* Added ns option to disable sound, for debugging -* Various bug fixes -* Cosmetic changes to placate gdc Wall - -0.3.0 - -* Built and tested on Windows XP -* Partial support for FreeBSD (exceptions do not work) -* You no longer have to download Monster separately -* Made an alternative for building without DSSS (but DSSS still works) -* Renamed main program from 'morro' to 'openmw' -* Made the config system more robust -* Added oc switch for showing Ogre config window on startup -* Removed some config files, these are auto generated when missing. -* Separated plugins.cfg into linux and windows versions. -* Updated Makefile and sources for increased portability -* confirmed to work against OIS 1.0.0 (Ubuntu repository package) - -0.2.0 - -* Compiles with gdc -* Switched to DSSS for building D code -* Includes the program esmtool - -0.1.0 - -first release +OpenMW: A reimplementation of The Elder Scrolls III: Morrowind + +OpenMW is an attempt at recreating the engine for the popular role-playing game +Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. + +Version: 0.28.0 +License: GPL (see GPL3.txt for more information) +Website: http://www.openmw.org + +Font Licenses: +EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) +DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) + + + +INSTALLATION + +Windows: +Run the installer. + +Linux: +Ubuntu (and most others) +Download the .deb file and install it in the usual way. + +Arch Linux +There's an OpenMW package available in the [community] Repository: +https://www.archlinux.org/packages/?sort=&q=openmw + +OS X: +Open DMG file, copy OpenMW folder anywhere, for example in /Applications + +BUILD FROM SOURCE + +https://wiki.openmw.org/index.php?title=Development_Environment_Setup + + +THE DATA PATH + +The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to +pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly +(installing Morrowind under WINE is considered a proper install). + +COMMAND LINE OPTIONS + +Syntax: openmw +Allowed options: + --help print help message + --version print version information and quit + --data arg (=data) set data directories (later directories + have higher priority) + --data-local arg set local data directory (highest + priority) + --fallback-archive arg (=fallback-archive) + set fallback BSA archives (later + archives have higher priority) + --resources arg (=resources) set resources directory + --start arg (=Beshara) set initial cell + --content arg content file(s): esm/esp, or + omwgame/omwaddon + --anim-verbose [=arg(=1)] (=0) output animation indices files + --no-sound [=arg(=1)] (=0) disable all sounds + --script-verbose [=arg(=1)] (=0) verbose script output + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue + scripts) at startup + --script-console [=arg(=1)] (=0) enable console-only script + functionality + --script-run arg select a file containing a list of + console commands that is executed on + startup + --script-warn [=arg(=1)] (=1) handling of warnings when compiling + scripts + 0 - ignore warning + 1 - show warning but consider script as + correctly compiled anyway + 2 - treat warnings as errors + --skip-menu [=arg(=1)] (=0) skip main menu on game startup + --fs-strict [=arg(=1)] (=0) strict file system handling (no case + folding) + --encoding arg (=win1252) Character encoding used in OpenMW game + messages: + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and + Albanian languages + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic + and other languages + + win1252 - Western European (Latin) + alphabet, used by default + --fallback arg fallback values + --no-grab Don't grab mouse cursor + --activate-dist arg (=-1) activation distance override + +CHANGELOG + +0.29.0 + +Bug #556: Video soundtrack not played when music volume is set to zero +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #848: Wrong amount of footsteps playing in 1st person +Bug #888: Ascended Sleepers have movement issues +Bug #892: Explicit references are allowed on all script functions +Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly +Bug #1009: Lake Fjalding AI related slowdown. +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window +Bug #1060: Some message boxes are cut off at the bottom +Bug #1062: Bittercup script does not work ('end' variable) +Bug #1074: Inventory paperdoll obscures armour rating +Bug #1077: Message after killing an essential NPC disappears too fast +Bug #1078: "Clutterbane" shows empty charge bar +Bug #1083: UndoWerewolf fails +Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered +Bug #1090: Start scripts fail when going to a non-predefined cell +Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. +Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior +Bug #1105: Magicka is depleted when using uncastable spells +Bug #1106: Creatures should be able to run +Bug #1107: TR cliffs have way too huge collision boxes in OpenMW +Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. +Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) +Bug #1115: Memory leak when spying on Fargoth +Bug #1137: Script execution fails (drenSlaveOwners script) +Bug #1143: Mehra Milo quest (vivec informants) is broken +Bug #1145: Issues with moving gold between inventory and containers +Bug #1146: Issues with picking up stacks of gold +Bug #1147: Dwemer Crossbows are held incorrectly +Bug #1158: Armor rating should always stay below inventory mannequin +Bug #1159: Quick keys can be set during character generation +Bug #1160: Crash on equip lockpick when +Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Feature #30: Loading/Saving (still missing a few parts) +Feature #101: AI Package: Activate +Feature #103: AI Package: Follow, FollowCell +Feature #138: Editor: Drag & Drop +Feature #428: Player death +Feature #505: Editor: Record Cloning +Feature #701: Levelled creatures +Feature #708: Improved Local Variable handling +Feature #709: Editor: Script verifier +Feature #764: Missing journal backend features +Feature #777: Creature weapons/shields +Feature #789: Editor: Referenceable record verifier +Feature #924: Load/Save GUI (still missing loading screen and progress bars) +Feature #946: Knockdown +Feature #947: Decrease fatigue when running, swimming and attacking +Feature #956: Melee Combat: Blocking +Feature #957: Area magic +Feature #960: Combat/AI combat for creatures +Feature #962: Combat-Related AI instructions +Feature #1075: Damage/Restore skill/attribute magic effects +Feature #1076: Soultrap magic effect +Feature #1081: Disease contraction +Feature #1086: Blood particles +Feature #1092: Interrupt resting +Feature #1101: Inventory equip scripts +Feature #1116: Version/Build number in Launcher window +Feature #1119: Resistance/weakness to normal weapons magic effect +Feature #1123: Slow Fall magic effect +Feature #1130: Auto-calculate spells +Feature #1164: Editor: Case-insensitive sorting in tables + +0.28.0 + +Bug #399: Inventory changes are not visible immediately +Bug #417: Apply weather instantly when teleporting +Bug #566: Global Map position marker not updated for interior cells +Bug #712: Looting corpse delay +Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod +Bug #805: Two TR meshes appear black (v0.24RC) +Bug #841: Third-person activation distance taken from camera rather than head +Bug #845: NPCs hold torches during the day +Bug #855: Vvardenfell Visages Volume I some hairs donĀ“t appear since 0,24 +Bug #856: Maormer race by Mac Kom - The heads are way up +Bug #864: Walk locks during loading in 3rd person +Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog +Bug #882: Hircine's Ring doesn't always work +Bug #909: [Tamriel Rebuilt] crashes in Akamora +Bug #922: Launcher writing merged openmw.cfg files +Bug #943: Random magnitude should be calculated per effect +Bug #948: Negative fatigue level should be allowed +Bug #949: Particles in world space +Bug #950: Hard crash on x64 Linux running --new-game (on startup) +Bug #951: setMagicka and setFatigue have no effect +Bug #954: Problem with equipping inventory items when using a keyboard shortcut +Bug #955: Issues with equipping torches +Bug #966: Shield is visible when casting spell +Bug #967: Game crashes when equipping silver candlestick +Bug #970: Segmentation fault when starting at Bal Isra +Bug #977: Pressing down key in console doesn't go forward in history +Bug #979: Tooltip disappears when changing inventory +Bug #980: Barter: item category is remembered, but not shown +Bug #981: Mod: replacing model has wrong position/orientation +Bug #982: Launcher: Addon unchecking is not saved +Bug #983: Fix controllers to affect objects attached to the base node +Bug #985: Player can talk to NPCs who are in combat +Bug #989: OpenMW crashes when trying to include mod with capital .ESP +Bug #991: Merchants equip items with harmful constant effect enchantments +Bug #994: Don't cap skills/attributes when set via console +Bug #998: Setting the max health should also set the current health +Bug #1005: Torches are visible when casting spells and during hand to hand combat. +Bug #1006: Many NPCs have 0 skill +Bug #1007: Console fills up with text +Bug #1013: Player randomly loses health or dies +Bug #1014: Persuasion window is not centered in maximized window +Bug #1015: Player status window scroll state resets on status change +Bug #1016: Notification window not big enough for all skill level ups +Bug #1020: Saved window positions are not rescaled appropriately on resolution change +Bug #1022: Messages stuck permanently on screen when they pile up +Bug #1023: Journals doesn't open +Bug #1026: Game loses track of torch usage. +Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level +Bug #1029: Quick keys menu: Select compatible replacement when tool used up +Bug #1042: TES3 header data wrong encoding +Bug #1045: OS X: deployed OpenCS won't launch +Bug #1046: All damaged weaponry is worth 1 gold +Bug #1048: Links in "locked" dialogue are still clickable +Bug #1052: Using color codes when naming your character actually changes the name's color +Bug #1054: Spell effects not visible in front of water +Bug #1055: Power-Spell animation starts even though you already casted it that day +Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability +Bug #1063: Crash upon checking out game start ship area in Seyda Neen +Bug #1064: openmw binaries link to unnecessary libraries +Bug #1065: Landing from a high place in water still causes fall damage +Bug #1072: Drawing weapon increases torch brightness +Bug #1073: Merchants sell stacks of gold +Feature #43: Visuals for Magic Effects +Feature #51: Ranged Magic +Feature #52: Touch Range Magic +Feature #53: Self Range Magic +Feature #54: Spell Casting +Feature #70: Vampirism +Feature #100: Combat AI +Feature #171: Implement NIF record NiFlipController +Feature #410: Window to restore enchanted item charge +Feature #647: Enchanted item glow +Feature #723: Invisibility/Chameleon magic effects +Feature #737: Resist Magicka magic effect +Feature #758: GetLOS +Feature #926: Editor: Info-Record tables +Feature #958: Material controllers +Feature #959: Terrain bump, specular, & parallax mapping +Feature #990: Request: unlock mouse when in any menu +Feature #1018: Do not allow view mode switching while performing an action +Feature #1027: Vertex morph animation (NiGeomMorpherController) +Feature #1031: Handle NiBillboardNode +Feature #1051: Implement NIF texture slot DarkTexture +Task #873: Unify OGRE initialisation + +0.27.0 + +Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp +Bug #794: incorrect display of decimal numbers +Bug #840: First-person sneaking camera height +Bug #887: Ambient sounds playing while paused +Bug #902: Problems with Polish character encoding +Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key +Bug #910: Some CDs not working correctly with Unshield installer +Bug #917: Quick character creation plugin does not work +Bug #918: Fatigue does not refill +Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) +Feature #57: Acrobatics Skill +Feature #462: Editor: Start Dialogue +Feature #546: Modify ESX selector to handle new content file scheme +Feature #588: Editor: Adjust name/path of edited content files +Feature #644: Editor: Save +Feature #710: Editor: Configure script compiler context +Feature #790: God Mode +Feature #881: Editor: Allow only one instance of OpenCS +Feature #889: Editor: Record filtering +Feature #895: Extinguish torches +Feature #898: Breath meter enhancements +Feature #901: Editor: Default record filter +Feature #913: Merge --master and --plugin switches + +0.26.0 + +Bug #274: Inconsistencies in the terrain +Bug #557: Already-dead NPCs do not equip clothing/items. +Bug #592: Window resizing +Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) +Bug #664: Heart of lorkhan acts like a dead body (container) +Bug #767: Wonky ramp physics & water +Bug #780: Swimming out of water +Bug #792: Wrong ground alignment on actors when no clipping +Bug #796: Opening and closing door sound issue +Bug #797: No clipping hinders opening and closing of doors +Bug #799: sliders in enchanting window +Bug #838: Pressing key during startup procedure freezes the game +Bug #839: Combat/magic stances during character creation +Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment +Bug #844: Resting "until healed" option given even with full stats +Bug #846: Equipped torches are invisible. +Bug #847: Incorrect formula for autocalculated NPC initial health +Bug #850: Shealt weapon sound plays when leaving magic-ready stance +Bug #852: Some boots do not produce footstep sounds +Bug #860: FPS bar misalignment +Bug #861: Unable to print screen +Bug #863: No sneaking and jumping at the same time +Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. +Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. +Bug #868: Idle animations are repeated +Bug #874: Underwater swimming close to the ground is jerky +Bug #875: Animation problem while swimming on the surface and looking up +Bug #876: Always a starting upper case letter in the inventory +Bug #878: Active spell effects don't update the layout properly when ended +Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load +Bug #896: New game sound issue +Feature #49: Melee Combat +Feature #71: Lycanthropy +Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList +Feature #622: Multiple positions for inventory window +Feature #627: Drowning +Feature #786: Allow the 'Activate' key to close the countdialog window +Feature #798: Morrowind installation via Launcher (Linux/Max OS only) +Feature #851: First/Third person transitions with mouse wheel +Task #689: change PhysicActor::enableCollisions +Task #707: Reorganise Compiler + +0.25.0 + +Bug #411: Launcher crash on OS X < 10.8 +Bug #604: Terrible performance drop in the Census and Excise Office. +Bug #676: Start Scripts fail to load +Bug #677: OpenMW does not accept script names with - +Bug #766: Extra space in front of topic links +Bug #793: AIWander Isn't Being Passed The Repeat Parameter +Bug #795: Sound playing with drawn weapon and crossing cell-border +Bug #800: can't select weapon for enchantment +Bug #801: Player can move while over-encumbered +Bug #802: Dead Keys not working +Bug #808: mouse capture +Bug #809: ini Importer does not work without an existing cfg file +Bug #812: Launcher will run OpenMW with no ESM or ESP selected +Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected +Bug #817: Dead NPCs and Creatures still have collision boxes +Bug #820: Incorrect sorting of answers (Dialogue) +Bug #826: mwinimport dumps core when given an unknown parameter +Bug #833: getting stuck in door +Bug #835: Journals/books not showing up properly. +Feature #38: SoundGen +Feature #105: AI Package: Wander +Feature #230: 64-bit compatibility for OS X +Feature #263: Hardware mouse cursors +Feature #449: Allow mouse outside of window while paused +Feature #736: First person animations +Feature #750: Using mouse wheel in third person mode +Feature #822: Autorepeat for slider buttons + +0.24.0 + +Bug #284: Book's text misalignment +Bug #445: Camera able to get slightly below floor / terrain +Bug #582: Seam issue in Red Mountain +Bug #632: Journal Next Button shows white square +Bug #653: IndexedStore ignores index +Bug #694: Parser does not recognize float values starting with . +Bug #699: Resource handling broken with Ogre 1.9 trunk +Bug #718: components/esm/loadcell is using the mwworld subsystem +Bug #729: Levelled item list tries to add nonexistent item +Bug #730: Arrow buttons in the settings menu do not work. +Bug #732: Erroneous behavior when binding keys +Bug #733: Unclickable dialogue topic +Bug #734: Book empty line problem +Bug #738: OnDeath only works with implicit references +Bug #740: Script compiler fails on scripts with special names +Bug #742: Wait while no clipping +Bug #743: Problem with changeweather console command +Bug #744: No wait dialogue after starting a new game +Bug #748: Player is not able to unselect objects with the console +Bug #751: AddItem should only spawn a message box when called from dialogue +Bug #752: The enter button has several functions in trade and looting that is not impelemted. +Bug #753: Fargoth's Ring Quest Strange Behavior +Bug #755: Launcher writes duplicate lines into settings.cfg +Bug #759: Second quest in mages guild does not work +Bug #763: Enchantment cast cost is wrong +Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly +Bug #773: AIWander Isn't Being Passed The Correct idle Values +Bug #778: The journal can be opened at the start of a new game +Bug #779: Divayth Fyr starts as dead +Bug #787: "Batch count" on detailed FPS counter gets cut-off +Bug #788: chargen scroll layout does not match vanilla +Feature #60: Atlethics Skill +Feature #65: Security Skill +Feature #74: Interaction with non-load-doors +Feature #98: Render Weapon and Shield +Feature #102: AI Package: Escort, EscortCell +Feature #182: Advanced Journal GUI +Feature #288: Trading enhancements +Feature #405: Integrate "new game" into the menu +Feature #537: Highlight dialogue topic links +Feature #658: Rotate, RotateWorld script instructions and local rotations +Feature #690: Animation Layering +Feature #722: Night Eye/Blind magic effects +Feature #735: Move, MoveWorld script instructions. +Feature #760: Non-removable corpses + +0.23.0 + +Bug #522: Player collides with placeable items +Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open +Bug #561: Tooltip word wrapping delay +Bug #578: Bribing works incorrectly +Bug #601: PositionCell fails on negative coordinates +Bug #606: Some NPCs hairs not rendered with Better Heads addon +Bug #609: Bad rendering of bone boots +Bug #613: Messagebox causing assert to fail +Bug #631: Segfault on shutdown +Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard +Bug #635: Scale NPCs depending on race +Bug #643: Dialogue Race select function is inverted +Bug #646: Twohanded weapons don't work properly +Bug #654: Crash when dropping objects without a collision shape +Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell +Bug #660: "g" in "change" cut off in Race Menu +Bug #661: Arrille sells me the key to his upstairs room +Bug #662: Day counter starts at 2 instead of 1 +Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur +Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. +Bug #666: Looking up/down problem +Bug #667: Active effects border visible during loading +Bug #669: incorrect player position at new game start +Bug #670: race selection menu: sex, face and hair left button not totally clickable +Bug #671: new game: player is naked +Bug #674: buying or selling items doesn't change amount of gold +Bug #675: fatigue is not set to its maximum when starting a new game +Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly +Bug #680: different gold coins in Tel Mara +Bug #682: Race menu ignores playable flag for some hairs and faces +Bug #685: Script compiler does not accept ":" after a function name +Bug #688: dispose corpse makes cross-hair to disappear +Bug #691: Auto equipping ignores equipment conditions +Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder +Bug #696: Draugr incorrect head offset +Bug #697: Sail transparency issue +Bug #700: "On the rocks" mod does not load its UV coordinates correctly. +Bug #702: Some race mods don't work +Bug #711: Crash during character creation +Bug #715: Growing Tauryon +Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic +Feature #55/657: Item Repairing +Feature #62/87: Enchanting +Feature #99: Pathfinding +Feature #104: AI Package: Travel +Feature #129: Levelled items +Feature #204: Texture animations +Feature #239: Fallback-Settings +Feature #535: Console object selection improvements +Feature #629: Add levelup description in levelup layout dialog +Feature #630: Optional format subrecord in (tes3) header +Feature #641: Armor rating +Feature #645: OnDeath script function +Feature #683: Companion item UI +Feature #698: Basic Particles +Task #648: Split up components/esm/loadlocks +Task #695: mwgui cleanup + +0.22.0 + +Bug #311: Potential infinite recursion in script compiler +Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. +Bug #382: Weird effect in 3rd person on water +Bug #387: Always use detailed shape for physics raycasts +Bug #420: Potion/ingredient effects do not stack +Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips +Bug #434/Bug #605: Object movement between cells not properly implemented +Bug #502: Duplicate player collision model at origin +Bug #509: Dialogue topic list shifts inappropriately +Bug #513: Sliding stairs +Bug #515: Launcher does not support non-latin strings +Bug #525: Race selection preview camera wrong position +Bug #526: Attributes / skills should not go below zero +Bug #529: Class and Birthsign menus options should be preselected +Bug #530: Lock window button graphic missing +Bug #532: Missing map menu graphics +Bug #545: ESX selector does not list ESM files properly +Bug #547: Global variables of type short are read incorrectly +Bug #550: Invisible meshes collision and tooltip +Bug #551: Performance drop when loading multiple ESM files +Bug #552: Don't list CG in options if it is not available +Bug #555: Character creation windows "OK" button broken +Bug #558: Segmentation fault when Alt-tabbing with console opened +Bug #559: Dialog window should not be available before character creation is finished +Bug #560: Tooltip borders should be stretched +Bug #562: Sound should not be played when an object cannot be picked up +Bug #565: Water animation speed + timescale +Bug #572: Better Bodies' textures don't work +Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) +Bug #574: Moving left/right should not cancel auto-run +Bug #575: Crash entering the Chamber of Song +Bug #576: Missing includes +Bug #577: Left Gloves Addon causes ESMReader exception +Bug #579: Unable to open container "Kvama Egg Sack" +Bug #581: Mimicking vanilla Morrowind water +Bug #583: Gender not recognized +Bug #586: Wrong char gen behaviour +Bug #587: "End" script statements with spaces don't work +Bug #589: Closing message boxes by pressing the activation key +Bug #590: Ugly Dagoth Ur rendering +Bug #591: Race selection issues +Bug #593: Persuasion response should be random +Bug #595: Footless guard +Bug #599: Waterfalls are invisible from a certain distance +Bug #600: Waterfalls rendered incorrectly, cut off by water +Bug #607: New beast bodies mod crashes +Bug #608: Crash in cell "Mournhold, Royal Palace" +Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt +Bug #613: Messagebox causing assert to fail +Bug #615: Meshes invisible from above water +Bug #617: Potion effects should be hidden until discovered +Bug #619: certain moss hanging from tree has rendering bug +Bug #621: Batching bloodmoon's trees +Bug #623: NiMaterialProperty alpha unhandled +Bug #628: Launcher in latest master crashes the game +Bug #633: Crash on startup: Better Heads +Bug #636: Incorrect Char Gen Menu Behavior +Feature #29: Allow ESPs and multiple ESMs +Feature #94: Finish class selection-dialogue +Feature #149: Texture Alphas +Feature #237: Run Morrowind-ini importer from launcher +Feature #286: Update Active Spell Icons +Feature #334: Swimming animation +Feature #335: Walking animation +Feature #360: Proper collision shapes for NPCs and creatures +Feature #367: Lights that behave more like original morrowind implementation +Feature #477: Special local scripting variables +Feature #528: Message boxes should close when enter is pressed under certain conditions. +Feature #543: Add bsa files to the settings imported by the ini importer +Feature #594: coordinate space and utility functions +Feature #625: Zoom in vanity mode +Task #464: Refactor launcher ESX selector into a re-usable component +Task #624: Unified implementation of type-variable sub-records + +0.21.0 + +Bug #253: Dialogs don't work for Russian version of Morrowind +Bug #267: Activating creatures without dialogue can still activate the dialogue GUI +Bug #354: True flickering lights +Bug #386: The main menu's first entry is wrong (in french) +Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations +Bug #495: Activation Range +Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned +Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available +Bug #500: Disposition for most NPCs is 0/100 +Bug #501: Getdisposition command wrongly returns base disposition +Bug #506: Journal UI doesn't update anymore +Bug #507: EnableRestMenu is not a valid command - change it to EnableRest +Bug #508: Crash in Ald Daedroth Shrine +Bug #517: Wrong price calculation when untrading an item +Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin +Bug #524: Beast races are able to wear shoes +Bug #527: Background music fails to play +Bug #533: The arch at Gnisis entrance is not displayed +Bug #534: Terrain gets its correct shape only some time after the cell is loaded +Bug #536: The same entry can be added multiple times to the journal +Bug #539: Race selection is broken +Bug #544: Terrain normal map corrupt when the map is rendered +Feature #39: Video Playback +Feature #151: ^-escape sequences in text output +Feature #392: Add AI related script functions +Feature #456: Determine required ini fallback values and adjust the ini importer accordingly +Feature #460: Experimental DirArchives improvements +Feature #540: Execute scripts of objects in containers/inventories in active cells +Task #401: Review GMST fixing +Task #453: Unify case smashing/folding +Task #512: Rewrite utf8 component + +0.20.0 + +Bug #366: Changing the player's race during character creation does not change the look of the player character +Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell +Bug #437: Stop animations when paused +Bug #438: Time displays as "0 a.m." when it should be "12 a.m." +Bug #439: Text in "name" field of potion/spell creation window is persistent +Bug #440: Starting date at a new game is off by one day +Bug #442: Console window doesn't close properly sometimes +Bug #448: Do not break container window formatting when item names are very long +Bug #458: Topics sometimes not automatically added to known topic list +Bug #476: Auto-Moving allows player movement after using DisablePlayerControls +Bug #478: After sleeping in a bed the rest dialogue window opens automtically again +Bug #492: On creating potions the ingredients are removed twice +Feature #63: Mercantile skill +Feature #82: Persuasion Dialogue +Feature #219: Missing dialogue filters/functions +Feature #369: Add a FailedAction +Feature #377: Select head/hair on character creation +Feature #391: Dummy AI package classes +Feature #435: Global Map, 2nd Layer +Feature #450: Persuasion +Feature #457: Add more script instructions +Feature #474: update the global variable pcrace when the player's race is changed +Task #158: Move dynamically generated classes from Player class to World Class +Task #159: ESMStore rework and cleanup +Task #163: More Component Namespace Cleanup +Task #402: Move player data from MWWorld::Player to the player's NPC record +Task #446: Fix no namespace in BulletShapeLoader + +0.19.0 + +Bug #374: Character shakes in 3rd person mode near the origin +Bug #404: Gamma correct rendering +Bug #407: Shoes of St. Rilm do not work +Bug #408: Rugs has collision even if they are not supposed to +Bug #412: Birthsign menu sorted incorrectly +Bug #413: Resolutions presented multiple times in launcher +Bug #414: launcher.cfg file stored in wrong directory +Bug #415: Wrong esm order in openmw.cfg +Bug #418: Sound listener position updates incorrectly +Bug #423: wrong usage of "Version" entry in openmw.desktop +Bug #426: Do not use hardcoded splash images +Bug #431: Don't use markers for raycast +Bug #432: Crash after picking up items from an NPC +Feature #21/#95: Sleeping/resting +Feature #61: Alchemy Skill +Feature #68: Death +Feature #69/#86: Spell Creation +Feature #72/#84: Travel +Feature #76: Global Map, 1st Layer +Feature #120: Trainer Window +Feature #152: Skill Increase from Skill Books +Feature #160: Record Saving +Task #400: Review GMST access + +0.18.0 + +Bug #310: Button of the "preferences menu" are too small +Bug #361: Hand-to-hand skill is always 100 +Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to +Bug #372: playSound3D uses original coordinates instead of current coordinates. +Bug #373: Static OGRE build faulty +Bug #375: Alt-tab toggle view +Bug #376: Screenshots are disable +Bug #378: Exception when drinking self-made potions +Bug #380: Cloth visibility problem +Bug #384: Weird character on doors tooltip. +Bug #398: Some objects do not collide in MW, but do so in OpenMW +Feature #22: Implement level-up +Feature #36: Hide Marker +Feature #88: Hotkey Window +Feature #91: Level-Up Dialogue +Feature #118: Keyboard and Mouse-Button bindings +Feature #119: Spell Buying Window +Feature #133: Handle resources across multiple data directories +Feature #134: Generate a suitable default-value for --data-local +Feature #292: Object Movement/Creation Script Instructions +Feature #340: AIPackage data structures +Feature #356: Ingredients use +Feature #358: Input system rewrite +Feature #370: Target handling in actions +Feature #379: Door markers on the local map +Feature #389: AI framework +Feature #395: Using keys to open doors / containers +Feature #396: Loading screens +Feature #397: Inventory avatar image and race selection head preview +Task #339: Move sounds into Action + +0.17.0 + +Bug #225: Valgrind reports about 40MB of leaked memory +Bug #241: Some physics meshes still don't match +Bug #248: Some textures are too dark +Bug #300: Dependency on proprietary CG toolkit +Bug #302: Some objects don't collide although they should +Bug #308: Freeze in Balmora, Meldor: Armorer +Bug #313: openmw without a ~/.config/openmw folder segfault. +Bug #317: adding non-existing spell via console locks game +Bug #318: Wrong character normals +Bug #341: Building with Ogre Debug libraries does not use debug version of plugins +Bug #347: Crash when running openmw with --start="XYZ" +Bug #353: FindMyGUI.cmake breaks path on Windows +Bug #359: WindowManager throws exception at destruction +Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation +Feature #33: Allow objects to cross cell-borders +Feature #59: Dropping Items (replaced stopgap implementation with a proper one) +Feature #93: Main Menu +Feature #96/329/330/331/332/333: Player Control +Feature #180: Object rotation and scaling. +Feature #272: Incorrect NIF material sharing +Feature #314: Potion usage +Feature #324: Skill Gain +Feature #342: Drain/fortify dynamic stats/attributes magic effects +Feature #350: Allow console only script instructions +Feature #352: Run scripts in console on startup +Task #107: Refactor mw*-subsystems +Task #325: Make CreatureStats into a class +Task #345: Use Ogre's animation system +Task #351: Rewrite Action class to support automatic sound playing + +0.16.0 + +Bug #250: OpenMW launcher erratic behaviour +Bug #270: Crash because of underwater effect on OS X +Bug #277: Auto-equipping in some cells not working +Bug #294: Container GUI ignores disabled inventory menu +Bug #297: Stats review dialog shows all skills and attribute values as 0 +Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses +Bug #299: Crash in World::disable +Bug #306: Non-existent ~/.config/openmw "crash" the launcher. +Bug #307: False "Data Files" location make the launcher "crash" +Feature #81: Spell Window +Feature #85: Alchemy Window +Feature #181: Support for x.y script syntax +Feature #242: Weapon and Spell icons +Feature #254: Ingame settings window +Feature #293: Allow "stacking" game modes +Feature #295: Class creation dialog tooltips +Feature #296: Clicking on the HUD elements should show/hide the respective window +Feature #301: Direction after using a Teleport Door +Feature #303: Allow object selection in the console +Feature #305: Allow the use of = as a synonym for == +Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts +Task #176: Restructure enabling/disabling of MW-references +Task #283: Integrate ogre.cfg file in settings file +Task #290: Auto-Close MW-reference related GUI windows + +0.15.0 + +Bug #5: Physics reimplementation (fixes various issues) +Bug #258: Resizing arrow's background is not transparent +Bug #268: Widening the stats window in X direction causes layout problems +Bug #269: Topic pane in dialgoue window is too small for some longer topics +Bug #271: Dialog choices are sorted incorrectly +Bug #281: The single quote character is not rendered on dialog windows +Bug #285: Terrain not handled properly in cells that are not predefined +Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs +Feature #15: Collision with Terrain +Feature #17: Inventory-, Container- and Trade-Windows +Feature #44: Floating Labels above Focussed Objects +Feature #80: Tooltips +Feature #83: Barter Dialogue +Feature #90: Book and Scroll Windows +Feature #156: Item Stacking in Containers +Feature #213: Pulsating lights +Feature #218: Feather & Burden +Feature #256: Implement magic effect bookkeeping +Feature #259: Add missing information to Stats window +Feature #260: Correct case for dialogue topics +Feature #280: GUI texture atlasing +Feature #291: Ability to use GMST strings from GUI layout files +Task #255: Make MWWorld::Environment into a singleton + +0.14.0 + +Bug #1: Meshes rendered with wrong orientation +Bug #6/Task #220: Picking up small objects doesn't always work +Bug #127: tcg doesn't work +Bug #178: Compablity problems with Ogre 1.8.0 RC 1 +Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI +Bug #227: Terrain crashes when moving away from predefined cells +Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces +Bug #235: TGA texture loading problem +Bug #246: wireframe mode does not work in water +Feature #8/#232: Water Rendering +Feature #13: Terrain Rendering +Feature #37: Render Path Grid +Feature #66: Factions +Feature #77: Local Map +Feature #78: Compass/Mini-Map +Feature #97: Render Clothing/Armour +Feature #121: Window Pinning +Feature #205: Auto equip +Feature #217: Contiainer should track changes to its content +Feature #221: NPC Dialogue Window Enhancements +Feature #233: Game settings manager +Feature #240: Spell List and selected spell (no GUI yet) +Feature #243: Draw State +Task #113: Morrowind.ini Importer +Task #215: Refactor the sound code +Task #216: Update MyGUI + +0.13.0 + +Bug #145: Fixed sound problems after cell change +Bug #179: Pressing space in console triggers activation +Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux +Bug #189: ASCII 16 character added to console on it's activation on Mac OS X +Bug #190: Case Folding fails with music files +Bug #192: Keypresses write Text into Console no matter which gui element is active +Bug #196: Collision shapes out of place +Bug #202: ESMTool doesn't not work with localised ESM files anymore +Bug #203: Torch lights only visible on short distance +Bug #207: Ogre.log not written +Bug #209: Sounds do not play +Bug #210: Ogre crash at Dren plantation +Bug #214: Unsupported file format version +Bug #222: Launcher is writing openmw.cfg file to wrong location +Feature #9: NPC Dialogue Window +Feature #16/42: New sky/weather implementation +Feature #40: Fading +Feature #48: NPC Dialogue System +Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) +Feature #161: Load REC_PGRD records +Feature #195: Wireframe-mode +Feature #198/199: Various sound effects +Feature #206: Allow picking data path from launcher if non is set +Task #108: Refactor window manager class +Task #172: Sound Manager Cleanup +Task #173: Create OpenEngine systems in the appropriate manager classes +Task #184: Adjust MSVC and gcc warning levels +Task #185: RefData rewrite +Task #201: Workaround for transparency issues +Task #208: silenced esm_reader.hpp warning + +0.12.0 + +Bug #154: FPS Drop +Bug #169: Local scripts continue running if associated object is deleted +Bug #174: OpenMW fails to start if the config directory doesn't exist +Bug #187: Missing lighting +Bug #188: Lights without a mesh are not rendered +Bug #191: Taking screenshot causes crash when running installed +Feature #28: Sort out the cell load problem +Feature #31: Allow the player to move away from pre-defined cells +Feature #35: Use alternate storage location for modified object position +Feature #45: NPC animations +Feature #46: Creature Animation +Feature #89: Basic Journal Window +Feature #110: Automatically pick up the path of existing MW-installations +Feature #183: More FPS display settings +Task #19: Refactor engine class +Task #109/Feature #162: Automate Packaging +Task #112: Catch exceptions thrown in input handling functions +Task #128/#168: Cleanup Configuration File Handling +Task #131: NPC Activation doesn't work properly +Task #144: MWRender cleanup +Task #155: cmake cleanup + +0.11.1 + +Bug #2: Resources loading doesn't work outside of bsa files +Bug #3: GUI does not render non-English characters +Bug #7: openmw.cfg location doesn't match +Bug #124: The TCL alias for ToggleCollision is missing. +Bug #125: Some command line options can't be used from a .cfg file +Bug #126: Toggle-type script instructions are less verbose compared with original MW +Bug #130: NPC-Record Loading fails for some NPCs +Bug #167: Launcher sets invalid parameters in ogre config +Feature #10: Journal +Feature #12: Rendering Optimisations +Feature #23: Change Launcher GUI to a tabbed interface +Feature #24: Integrate the OGRE settings window into the launcher +Feature #25: Determine openmw.cfg location (Launcher) +Feature #26: Launcher Profiles +Feature #79: MessageBox +Feature #116: Tab-Completion in Console +Feature #132: --data-local and multiple --data +Feature #143: Non-Rendering Performance-Optimisations +Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs +Feature #157: Version Handling +Task #14: Replace tabs with 4 spaces +Task #18: Move components from global namespace into their own namespace +Task #123: refactor header files in components/esm + +0.10.0 + +* NPC dialogue window (not functional yet) +* Collisions with objects +* Refactor the PlayerPos class +* Adjust file locations +* CMake files and test linking for Bullet +* Replace Ogre raycasting test for activation with something more precise +* Adjust player movement according to collision results +* FPS display +* Various Portability Improvements +* Mac OS X support is back! + +0.9.0 + +* Exterior cells loading, unloading and management +* Character Creation GUI +* Character creation +* Make cell names case insensitive when doing internal lookups +* Music player +* NPCs rendering + +0.8.0 + +* GUI +* Complete and working script engine +* In game console +* Sky rendering +* Sound and music +* Tons of smaller stuff + +0.7.0 + +* This release is a complete rewrite in C++. +* All D code has been culled, and all modules have been rewritten. +* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. + +0.6.0 + +* Coded a GUI system using MyGUI +* Skinned MyGUI to look like Morrowind (work in progress) +* Integrated the Monster script engine +* Rewrote some functions into script code +* Very early MyGUI < > Monster binding +* Fixed Windows sound problems (replaced old openal32.dll) + +0.5.0 + +* Collision detection with Bullet +* Experimental walk & fall character physics +* New key bindings: + * t toggle physics mode (walking, flying, ghost), + * n night eye, brightens the scene +* Fixed incompatability with DMD 1.032 and newer compilers +* * (thanks to tomqyp) +* Various minor changes and updates + +0.4.0 + +* Switched from Audiere to OpenAL +* * (BIG thanks to Chris Robinson) +* Added complete Makefile (again) as a alternative build tool +* More realistic lighting (thanks again to Chris Robinson) +* Various localization fixes tested with Russian and French versions +* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' +* Added ns option to disable sound, for debugging +* Various bug fixes +* Cosmetic changes to placate gdc Wall + +0.3.0 + +* Built and tested on Windows XP +* Partial support for FreeBSD (exceptions do not work) +* You no longer have to download Monster separately +* Made an alternative for building without DSSS (but DSSS still works) +* Renamed main program from 'morro' to 'openmw' +* Made the config system more robust +* Added oc switch for showing Ogre config window on startup +* Removed some config files, these are auto generated when missing. +* Separated plugins.cfg into linux and windows versions. +* Updated Makefile and sources for increased portability +* confirmed to work against OIS 1.0.0 (Ubuntu repository package) + +0.2.0 + +* Compiles with gdc +* Switched to DSSS for building D code +* Includes the program esmtool + +0.1.0 + +first release From 8fa88f448085ee18fadced6a1beabc4bbe050d15 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 28 Feb 2014 00:13:57 +1100 Subject: [PATCH 30/36] sync with upstream --- apps/opencs/view/filter/editwidget.hpp | 2 +- apps/opencs/view/filter/filterbox.hpp | 2 +- apps/opencs/view/filter/recordfilterbox.hpp | 2 +- apps/opencs/view/world/tablesubview.hpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 37 ++++++++------------- apps/openmw/mwmechanics/pathfinding.cpp | 2 -- credits.txt | 1 - 7 files changed, 17 insertions(+), 31 deletions(-) diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp index e7e34b8e9..555b6d360 100644 --- a/apps/opencs/view/filter/editwidget.hpp +++ b/apps/opencs/view/filter/editwidget.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../../model/filter/parser.hpp" #include "../../model/filter/node.hpp" diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 5954035fc..3817d5e70 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "../../model/filter/node.hpp" #include "../../model/world/universalid.hpp" diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index fa5c9c3c2..3638dc6c3 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index b3c253919..1f67e0262 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -#include +#include class QModelIndex; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 17a624f0f..6a49f2f5e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -240,21 +240,17 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); //may fail to build a path, check before use + buildNewPath(actor); //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //if no new path leave mTargetAngle unchanged - if(!mPathFinder.getPath().empty()) - { - //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); - else - mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - mRotate = true; - } + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; mMovement.mPosition[1] = 1; mReadyToAttack = false; @@ -304,13 +300,9 @@ namespace MWMechanics dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - float dist = -1; //hack to indicate first time, to construct a new path - if(!mPathFinder.getPath().empty()) - { - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); - } + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); float targetPosThreshold; bool isOutside = actor.getCell()->mCell->isExterior(); @@ -319,7 +311,7 @@ namespace MWMechanics else targetPosThreshold = 100; - if((dist < 0) || (dist > targetPosThreshold)) + if(dist > targetPosThreshold) { //construct new path only if target has moved away more than on ESM::Position pos = actor.getRefData().getPosition(); @@ -340,11 +332,8 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - if(!mPathFinder.getPath().empty()) - { - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; - } + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; } } } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 8dbdd8770..4407363a6 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,8 +391,6 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { - if (mPath.size() < 2) - return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); diff --git a/credits.txt b/credits.txt index 601255763..561931cde 100644 --- a/credits.txt +++ b/credits.txt @@ -20,7 +20,6 @@ Artem Kotsynyak (greye) athile Britt Mathis (galdor557) BrotherBrick -cc9cii Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) From 56ae85df0c28079b3b228d49b84079ec0e5179f0 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 28 Feb 2014 00:28:02 +1100 Subject: [PATCH 31/36] Fix 32bit Windows crash while taking the save screenshot. --- apps/openmw/mwrender/renderingmanager.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0a82d67fb..a4ce92347 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -980,13 +980,11 @@ void RenderingManager::screenshot(Image &image, int w, int h) Ogre::PixelFormat pf = rt->suggestPixelFormat(); - std::vector data; - data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf)); - - Ogre::PixelBox pb(w, h, 1, pf, &data[0]); - rt->copyContentsToMemory(pb); - - image.loadDynamicImage(&data[0], w, h, pf); + image.loadDynamicImage( + OGRE_ALLOC_T(Ogre::uchar, w * h * Ogre::PixelUtil::getNumElemBytes(pf), Ogre::MEMCATEGORY_GENERAL), + w, h, 1, pf, true // autoDelete=true, frees memory we allocate + ); + rt->copyContentsToMemory(image.getPixelBox()); // getPixelBox returns a box sharing the same memory as the image Ogre::TextureManager::getSingleton().remove(tempName); mRendering.getCamera()->setAspectRatio(oldAspect); From 5c11a9451184d69a801a9c5663ccc58d4d7c1082 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 28 Feb 2014 00:29:57 +1100 Subject: [PATCH 32/36] Fix 32bit Windows non-debug build with MSVC 11.0 crash during startup. --- libs/openengine/bullet/physic.cpp | 6 +++--- libs/openengine/bullet/physic.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 481b99bad..f124abb99 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -23,7 +23,7 @@ namespace Physic mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); Ogre::Quaternion inverse = mBoxRotation.Inverse(); - mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); + mBoxRotationInverse = Ogre::Quaternion(inverse.w, inverse.x, inverse.y,inverse.z); mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } @@ -85,8 +85,8 @@ namespace Physic Ogre::Quaternion PhysicActor::getRotation() { assert(mBody); - btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse; - return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); + btQuaternion quat = mBody->getWorldTransform().getRotation(); + return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()) * mBoxRotationInverse; } void PhysicActor::setScale(float scale){ diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 6cd7244b8..4ef611dc8 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -162,7 +162,7 @@ namespace Physic Ogre::Vector3 mBoxScaledTranslation; Ogre::Quaternion mBoxRotation; - btQuaternion mBoxRotationInverse; + Ogre::Quaternion mBoxRotationInverse; Ogre::Vector3 mForce; bool mOnGround; From a99aa15d148cc85b0918955675c2d9e8cbceb13b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Feb 2014 17:23:58 +0100 Subject: [PATCH 33/36] Fix a copy&paste mistake leaving start rotation uninitialized --- apps/openmw/engine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e80bd954e..af8d49426 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -456,7 +456,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) else { pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; - pos.rot[0] = pos.rot[1] = pos.pos[2] = 0; + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; world->changeToExteriorCell (pos); } @@ -621,4 +621,4 @@ void OMW::Engine::setActivationDistanceOverride (int distance) void OMW::Engine::setWarningsMode (int mode) { mWarningsMode = mode; -} \ No newline at end of file +} From 46867ec0cfb2887b638f4912a59a6d49e1027a2a Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 1 Mar 2014 07:24:20 +1100 Subject: [PATCH 34/36] Fix file handles being left open on windows builds --- components/files/lowlevelfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 71fd1b523..06ee9fb4e 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -219,7 +219,7 @@ LowLevelFile::LowLevelFile () LowLevelFile::~LowLevelFile () { - if (mHandle == INVALID_HANDLE_VALUE) + if (mHandle != INVALID_HANDLE_VALUE) CloseHandle (mHandle); } From 0e0d2331d60002dce43a9fd27a3eb636b62d4c89 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 28 Feb 2014 21:53:11 +0100 Subject: [PATCH 35/36] updated changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index a23cd1077..26abdaecc 100644 --- a/readme.txt +++ b/readme.txt @@ -133,6 +133,7 @@ Bug #1158: Armor rating should always stay below inventory mannequin Bug #1159: Quick keys can be set during character generation Bug #1160: Crash on equip lockpick when Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file Feature #30: Loading/Saving (still missing a few parts) Feature #101: AI Package: Activate Feature #103: AI Package: Follow, FollowCell From 6101916b7d63f2afe5ede3d659a46f11ea9df147 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Mar 2014 08:52:48 +0100 Subject: [PATCH 36/36] bumped version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae9ec8ac0..07b8ce289 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 28) +set(OPENMW_VERSION_MINOR 29) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/readme.txt b/readme.txt index 26abdaecc..bb17a68e7 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.28.0 +Version: 0.29.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org