From d6150b7482ab9eed2f3337cc30d18a55de64d4aa Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 30 Jul 2012 23:28:14 +0400 Subject: [PATCH 001/143] high-level implementation, unstable --- apps/openmw/mwclass/creature.hpp | 5 ++ apps/openmw/mwclass/npc.hpp | 5 ++ apps/openmw/mwrender/actors.cpp | 21 ++++++ apps/openmw/mwrender/actors.hpp | 1 + apps/openmw/mwrender/objects.cpp | 21 ++++++ apps/openmw/mwrender/objects.hpp | 2 + apps/openmw/mwrender/renderingmanager.cpp | 19 ++++- apps/openmw/mwworld/class.hpp | 5 ++ apps/openmw/mwworld/worldimp.cpp | 85 +++++++++++++++-------- 9 files changed, 135 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index f7a5e5874d..4de877b31d 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -59,6 +59,11 @@ namespace MWClass static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool + isActor() const { + return true; + } }; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index e494fbaa70..edb6ca40fa 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -93,6 +93,11 @@ namespace MWClass static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool + isActor() const { + return true; + } }; } diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index a64397d49b..a732443057 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -132,3 +132,24 @@ void Actors::update (float duration){ for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) iter->second->runAnimation(duration); } + +void +Actors::updateObjectCell(const MWWorld::Ptr &ptr) +{ + Ogre::SceneNode *node; + MWWorld::CellStore *newCell = ptr.getCell(); + + if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { + node = mMwRoot->createChildSceneNode(); + mCellSceneNodes[newCell] = node; + } else { + node = mCellSceneNodes[newCell]; + } + node->addChild(ptr.getRefData().getBaseNode()); + if (Animation *anim = mAllActors[ptr]) { + /// \note Update key (Ptr's are compared only with refdata so mCell + /// on key is outdated), maybe redundant + mAllActors.erase(ptr); + mAllActors[ptr] = anim; + } +} diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 4b0b2e572c..336465b161 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -45,6 +45,7 @@ namespace MWRender{ void update (float duration); + void updateObjectCell(const MWWorld::Ptr &ptr); }; } #endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index ecd1328c44..a76ef09d1f 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -11,6 +11,7 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" #include "renderconst.hpp" @@ -474,3 +475,23 @@ void Objects::rebuildStaticGeometry() it->second->build(); } } + +void +Objects::updateObjectCell(const MWWorld::Ptr &ptr) +{ + Ogre::SceneNode *node; + MWWorld::CellStore *newCell = ptr.getCell(); + + if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { + node = mMwRoot->createChildSceneNode(); + mCellSceneNodes[newCell] = node; + } else { + node = mCellSceneNodes[newCell]; + } + node->addChild(ptr.getRefData().getBaseNode()); + + /// \note Still unaware how to move aabb and static w/o full rebuild, + /// moving static objects may cause problems + insertMesh(ptr, MWWorld::Class::get(ptr).getModel(ptr)); +} + diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 443f25ecf3..630e5932c7 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -95,6 +95,8 @@ public: void setMwRoot(Ogre::SceneNode* root); void rebuildStaticGeometry(); + + void updateObjectCell(const MWWorld::Ptr &ptr); }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ae0e572199..cec5569c8b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -256,8 +256,25 @@ void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3 void RenderingManager::rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation){ } -void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store){ +void +RenderingManager::moveObjectToCell( + const MWWorld::Ptr& ptr, + const Ogre::Vector3& pos, + MWWorld::CellStore *store) +{ + Ogre::SceneNode *child = + mRendering.getScene()->getSceneNode(ptr.getRefData().getHandle()); + + Ogre::SceneNode *parent = child->getParentSceneNode(); + parent->removeChild(child); + + if (MWWorld::Class::get(ptr).isActor()) { + mActors.updateObjectCell(ptr); + } else { + mObjects.updateObjectCell(ptr); + } + child->setPosition(pos); } void RenderingManager::update (float duration){ diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 1bc592798c..6ab92319da 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -220,6 +220,11 @@ namespace MWWorld virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; + + virtual bool + isActor() const { + return false; + } }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 67d8a7cec3..abed250799 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -545,47 +545,76 @@ namespace MWWorld bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) { - bool ret = false; - ptr.getRefData().getPosition().pos[0] = x; - ptr.getRefData().getPosition().pos[1] = y; - ptr.getRefData().getPosition().pos[2] = z; - if (ptr==mPlayer->getPlayer()) - { - //std::cout << "X:" << ptr.getRefData().getPosition().pos[0] << " Z: " << ptr.getRefData().getPosition().pos[1] << "\n"; + bool cellChanged = false, haveToMove = false; - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); - if (currentCell) - { - if (!(currentCell->cell->data.flags & ESM::Cell::Interior)) + ESM::Position &pos = ptr.getRefData().getPosition(); + pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; + Ogre::Vector3 vec(x, y, z); + + CellStore *currCell; + // a bit ugly + if (ptr == mPlayer->getPlayer()) { + currCell = mWorldScene->getCurrentCell(); + } else { + currCell = ptr.getCell(); + } + + if (currCell) { + if (!(currCell->cell->data.flags & ESM::Cell::Interior)) { + // exterior -> adjust loaded cells + int cellX = 0, cellY = 0; + positionToIndex (x, y, cellX, cellY); + + if (currCell->cell->data.gridX != cellX || + currCell->cell->data.gridY != cellY) { - // exterior -> adjust loaded cells - int cellX = 0; - int cellY = 0; + if (ptr == mPlayer->getPlayer()) { + mWorldScene->changeCell(cellX, cellY, pos, false); + haveToMove = true; + } else { + CellStore *newCell = + MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); - positionToIndex (x, y, cellX, cellY); + if (!mWorldScene->isCellActive(*currCell)) { + placeObject(ptr, *newCell, pos); + } else if (!mWorldScene->isCellActive(*newCell)) { + MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); + mWorldScene->removeObjectFromScene(ptr); + mLocalScripts.remove(ptr); + } else { + MWWorld::Ptr copy = + MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); - if (currentCell->cell->data.gridX!=cellX || currentCell->cell->data.gridY!=cellY) - { - mWorldScene->changeCell (cellX, cellY, mPlayer->getPlayer().getRefData().getPosition(), false); - ret = true; + mRendering->moveObjectToCell(copy, vec, currCell); + + /// \note Maybe mechanics actors change is redundant + /// because of Ptr comparing operators + if (MWWorld::Class::get(ptr).isActor()) { + MWMechanics::MechanicsManager *mechMgr = + MWBase::Environment::get().getMechanicsManager(); + + mechMgr->removeActor(ptr); + mechMgr->addActor(copy); + + haveToMove = true; + } + } + ptr.getRefData().setCount(0); } - + cellChanged = true; } } } - - /// \todo cell change for non-player ref - - mRendering->moveObject (ptr, Ogre::Vector3 (x, y, z)); - - return ret; + if (haveToMove) { + mRendering->moveObject(ptr, vec); + mPhysics->moveObject(ptr.getRefData().getHandle(), vec); + } + return cellChanged; } void World::moveObject (const Ptr& ptr, float x, float y, float z) { moveObjectImp(ptr, x, y, z); - - mPhysics->moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z)); } void World::scaleObject (const Ptr& ptr, float scale) From 37990b5133c699e461ef9fb5d354e14a13149039 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 30 Jul 2012 23:54:26 +0400 Subject: [PATCH 002/143] fix player update --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index abed250799..79b246ad2e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -555,6 +555,7 @@ namespace MWWorld // a bit ugly if (ptr == mPlayer->getPlayer()) { currCell = mWorldScene->getCurrentCell(); + haveToMove = true; } else { currCell = ptr.getCell(); } @@ -570,7 +571,6 @@ namespace MWWorld { if (ptr == mPlayer->getPlayer()) { mWorldScene->changeCell(cellX, cellY, pos, false); - haveToMove = true; } else { CellStore *newCell = MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); From 288b63350c874e05b9f4a6058d2532457e2f7207 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 31 Jul 2012 11:46:57 +0400 Subject: [PATCH 003/143] fixed objects update, local script handling --- apps/openmw/mwworld/worldimp.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 79b246ad2e..f3fb8b47f8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -545,7 +545,7 @@ namespace MWWorld bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) { - bool cellChanged = false, haveToMove = false; + bool cellChanged = false, haveToMove = true; ESM::Position &pos = ptr.getRefData().getPosition(); pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; @@ -555,7 +555,6 @@ namespace MWWorld // a bit ugly if (ptr == mPlayer->getPlayer()) { currCell = mWorldScene->getCurrentCell(); - haveToMove = true; } else { currCell = ptr.getCell(); } @@ -577,10 +576,13 @@ namespace MWWorld if (!mWorldScene->isCellActive(*currCell)) { placeObject(ptr, *newCell, pos); + haveToMove = false; } else if (!mWorldScene->isCellActive(*newCell)) { MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); + + haveToMove = false; } else { MWWorld::Ptr copy = MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); @@ -595,8 +597,13 @@ namespace MWWorld mechMgr->removeActor(ptr); mechMgr->addActor(copy); - - haveToMove = true; + } else { + std::string script = + MWWorld::Class::get(ptr).getScript(ptr); + if (!script.empty()) { + mLocalScripts.remove(ptr); + mLocalScripts.add(script, copy); + } } } ptr.getRefData().setCount(0); From 0e3f70413edebd2525660c171e215763422e8fd7 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 31 Jul 2012 16:52:21 +0400 Subject: [PATCH 004/143] a very little comments --- apps/openmw/mwrender/actors.hpp | 3 ++- apps/openmw/mwrender/objects.hpp | 1 + apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 6 +++--- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 336465b161..073c5d51f1 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -15,7 +15,7 @@ namespace MWRender{ OEngine::Render::OgreRenderer &mRend; std::map mCellSceneNodes; Ogre::SceneNode* mMwRoot; - std::map mAllActors; + std::map mAllActors; @@ -45,6 +45,7 @@ namespace MWRender{ void update (float duration); + /// Updates containing cell for object rendering data void updateObjectCell(const MWWorld::Ptr &ptr); }; } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 630e5932c7..8594e4fe40 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -96,6 +96,7 @@ public: void rebuildStaticGeometry(); + /// Updates containing cell for object rendering data void updateObjectCell(const MWWorld::Ptr &ptr); }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d6a372d102..e618908c18 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -95,6 +95,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setWaterHeight(const float height); void toggleWater(); + /// Moves object rendering part to proper container /// \param store Cell the object was in previously (\a ptr has already been updated to the new cell). void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f3fb8b47f8..215fbb30e2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -552,7 +552,7 @@ namespace MWWorld Ogre::Vector3 vec(x, y, z); CellStore *currCell; - // a bit ugly + /// \todo fix assertion fail on player ptr.getCell() on start if (ptr == mPlayer->getPlayer()) { currCell = mWorldScene->getCurrentCell(); } else { @@ -574,6 +574,8 @@ namespace MWWorld CellStore *newCell = MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); + // placeObject() handles both target cell states + // with active current cell if (!mWorldScene->isCellActive(*currCell)) { placeObject(ptr, *newCell, pos); haveToMove = false; @@ -589,8 +591,6 @@ namespace MWWorld mRendering->moveObjectToCell(copy, vec, currCell); - /// \note Maybe mechanics actors change is redundant - /// because of Ptr comparing operators if (MWWorld::Class::get(ptr).isActor()) { MWMechanics::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); From ee97a204f2db3970358cd77667208dfcc72379f1 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 6 Aug 2012 15:26:37 +0400 Subject: [PATCH 005/143] fix npc animation key update --- apps/openmw/mwrender/actors.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index a732443057..415d172414 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -146,9 +146,10 @@ Actors::updateObjectCell(const MWWorld::Ptr &ptr) node = mCellSceneNodes[newCell]; } node->addChild(ptr.getRefData().getBaseNode()); - if (Animation *anim = mAllActors[ptr]) { + if (mAllActors.find(ptr) != mAllActors.end()) { /// \note Update key (Ptr's are compared only with refdata so mCell /// on key is outdated), maybe redundant + Animation *anim = mAllActors[ptr]; mAllActors.erase(ptr); mAllActors[ptr] = anim; } From d32a61b928e6df7df164fcdb16ce7248d8057f88 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 6 Aug 2012 18:20:48 +0400 Subject: [PATCH 006/143] fix object moving --- apps/openmw/mwworld/cells.cpp | 44 +++++++++++++++++--------------- apps/openmw/mwworld/worldimp.cpp | 5 ++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index cffaf70eae..822aa78d6e 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -167,67 +167,71 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce else return Ptr(); } + MWWorld::Ptr ptr; if (MWWorld::LiveCellRef *ref = cell.activators.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.potions.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.appas.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.armors.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.books.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.clothes.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.containers.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.creatures.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.doors.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.ingreds.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.creatureLists.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.itemLists.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.lights.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.lockpicks.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.miscItems.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.npcs.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.probes.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.repairs.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.statics.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.weapons.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) { + return ptr; + } return Ptr(); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 215fbb30e2..dc7f25751d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -545,7 +545,7 @@ namespace MWWorld bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) { - bool cellChanged = false, haveToMove = true; + bool cellChanged = false; ESM::Position &pos = ptr.getRefData().getPosition(); pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; @@ -558,6 +558,7 @@ namespace MWWorld } else { currCell = ptr.getCell(); } + bool haveToMove = mWorldScene->isCellActive(*currCell); if (currCell) { if (!(currCell->cell->data.flags & ESM::Cell::Interior)) { @@ -575,7 +576,7 @@ namespace MWWorld MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); // placeObject() handles both target cell states - // with active current cell + // with inactive current cell if (!mWorldScene->isCellActive(*currCell)) { placeObject(ptr, *newCell, pos); haveToMove = false; From 79b7487c8757b3d2c2a587f8129ad17cc99893e5 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 7 Aug 2012 22:21:46 +0400 Subject: [PATCH 007/143] fix using deleted objects --- apps/openmw/mwworld/cellstore.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 0be0ef6516..8eafb6b10a 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -69,7 +69,7 @@ namespace MWWorld { for (typename std::list::iterator iter (list.begin()); iter!=list.end(); ++iter) { - if (iter->ref.refID==name) + if (iter->mData.getCount() > 0 && iter->ref.refID == name) return &*iter; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dc7f25751d..748e6dc044 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -51,7 +51,7 @@ namespace for (iterator iter (refList.list.begin()); iter!=refList.list.end(); ++iter) { - if(iter->mData.getBaseNode()){ + if(iter->mData.getCount() > 0 && iter->mData.getBaseNode()){ if (iter->mData.getHandle()==handle) { return &*iter; From 993c7150dcdc00979f70fe23c421443fe721b057 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Aug 2012 22:43:11 +0200 Subject: [PATCH 008/143] tweaked depth bias --- files/materials/shadows.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/materials/shadows.h b/files/materials/shadows.h index b1dc92d211..65dffe4923 100644 --- a/files/materials/shadows.h +++ b/files/materials/shadows.h @@ -1,5 +1,5 @@ -#define FIXED_BIAS 0.005 +#define FIXED_BIAS 0.0003 float depthShadowPCF (shTexture2D shadowMap, float4 shadowMapPos, float2 offset) { From 7cc2de3e21adfad2605518f96a961181ad585273 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 08:52:08 +0200 Subject: [PATCH 009/143] boost filesystem compatibility fix --- components/files/ogreplugin.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index c5292b3a21..2d56bfb47a 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -32,7 +32,7 @@ namespace Ogre { class Root; } -#if (BOOST_VERSION <= 104300) +#if (BOOST_VERSION <= 104500) namespace boost { namespace filesystem { inline path absolute(const path& p, const path& base=current_path()) { From b6f7f21bcfe3aee9b367681ca18d1cdb914e2e3a Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 8 Aug 2012 14:51:33 +0400 Subject: [PATCH 010/143] fix player cell assertion fail, moveObject(Ptr&, CellStore&, f, f, f) added --- apps/openmw/mwbase/world.hpp | 5 +- apps/openmw/mwworld/cellstore.hpp | 14 ++++ apps/openmw/mwworld/scene.cpp | 10 +-- apps/openmw/mwworld/worldimp.cpp | 122 ++++++++++++++---------------- apps/openmw/mwworld/worldimp.hpp | 3 +- 5 files changed, 81 insertions(+), 73 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f257b723ea..d6037f1072 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -57,7 +57,7 @@ namespace MWBase protected: virtual void - placeObject( + copyObjectToCell( const MWWorld::Ptr &ptr, MWWorld::CellStore &cell, const ESM::Position &pos) = 0; @@ -184,6 +184,9 @@ namespace MWBase virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + virtual void + moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore &newCell, float x, float y, float z) = 0; + virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z) = 0; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8eafb6b10a..3ec4a2e782 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -155,6 +155,20 @@ namespace MWWorld forEachImp (functor, weapons); } + bool operator==(const CellStore &cell) { + return this->cell->name == cell.cell->name && + this->cell->data.gridX == cell.cell->data.gridX && + this->cell->data.gridY == cell.cell->data.gridY; + } + + bool operator!=(const CellStore &cell) { + return !(*this == cell); + } + + bool isExterior() const { + return cell->isExterior(); + } + private: template diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 13e5ecb85f..ef5da7b91e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -138,6 +138,8 @@ namespace MWWorld void Scene::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position, bool adjustPlayerPos) { + MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); + bool hasWater = cell->cell->data.flags & cell->cell->HasWater; mPhysics->setCurrentWater(hasWater, cell->cell->water); if (adjustPlayerPos) @@ -146,7 +148,6 @@ namespace MWWorld MWBase::Environment::get().getWorld()->getPlayer().setRot (position.rot[0], position.rot[1], position.rot[2]); } - MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); MWBase::Environment::get().getMechanicsManager()->addActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); MWBase::Environment::get().getMechanicsManager()->watchActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); @@ -224,7 +225,7 @@ namespace MWWorld // adjust player - playerCellChange (MWBase::Environment::get().getWorld()->getExterior(X, Y), position, adjustPlayerPos); + playerCellChange (mCurrentCell, position, adjustPlayerPos); // Sky system MWBase::Environment::get().getWorld()->adjustSky(); @@ -350,10 +351,7 @@ namespace MWWorld { CellStoreCollection::iterator active = mActiveCells.begin(); while (active != mActiveCells.end()) { - if ((*active)->cell->name == cell.cell->name && - (*active)->cell->data.gridX == cell.cell->data.gridX && - (*active)->cell->data.gridY == cell.cell->data.gridY) - { + if (**active == cell) { return true; } ++active; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 748e6dc044..4dd06ad057 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -543,81 +543,73 @@ namespace MWWorld } } - bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) + void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) { - bool cellChanged = false; - ESM::Position &pos = ptr.getRefData().getPosition(); pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; Ogre::Vector3 vec(x, y, z); - CellStore *currCell; - /// \todo fix assertion fail on player ptr.getCell() on start - if (ptr == mPlayer->getPlayer()) { - currCell = mWorldScene->getCurrentCell(); - } else { - currCell = ptr.getCell(); - } - bool haveToMove = mWorldScene->isCellActive(*currCell); + CellStore *currCell = ptr.getCell(); + bool isPlayer = ptr == mPlayer->getPlayer(); + bool haveToMove = mWorldScene->isCellActive(*currCell) || isPlayer; - if (currCell) { - if (!(currCell->cell->data.flags & ESM::Cell::Interior)) { - // exterior -> adjust loaded cells - int cellX = 0, cellY = 0; - positionToIndex (x, y, cellX, cellY); - - if (currCell->cell->data.gridX != cellX || - currCell->cell->data.gridY != cellY) - { - if (ptr == mPlayer->getPlayer()) { - mWorldScene->changeCell(cellX, cellY, pos, false); - } else { - CellStore *newCell = - MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); - - // placeObject() handles both target cell states - // with inactive current cell - if (!mWorldScene->isCellActive(*currCell)) { - placeObject(ptr, *newCell, pos); - haveToMove = false; - } else if (!mWorldScene->isCellActive(*newCell)) { - MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); - mWorldScene->removeObjectFromScene(ptr); - mLocalScripts.remove(ptr); - - haveToMove = false; - } else { - MWWorld::Ptr copy = - MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); - - mRendering->moveObjectToCell(copy, vec, currCell); - - if (MWWorld::Class::get(ptr).isActor()) { - MWMechanics::MechanicsManager *mechMgr = - MWBase::Environment::get().getMechanicsManager(); - - mechMgr->removeActor(ptr); - mechMgr->addActor(copy); - } else { - std::string script = - MWWorld::Class::get(ptr).getScript(ptr); - if (!script.empty()) { - mLocalScripts.remove(ptr); - mLocalScripts.add(script, copy); - } - } - } - ptr.getRefData().setCount(0); - } - cellChanged = true; + if (*currCell != newCell) { + if (isPlayer) { + if (!newCell.isExterior()) { + changeToInteriorCell(newCell.cell->name, pos); + } else { + changeToExteriorCell(pos); } + } else { + if (!mWorldScene->isCellActive(newCell)) { + copyObjectToCell(ptr, newCell, pos); + } else if (!mWorldScene->isCellActive(*currCell)) { + MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + mWorldScene->removeObjectFromScene(ptr); + mLocalScripts.remove(ptr); + haveToMove = false; + } else { + MWWorld::Ptr copy = + MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + + mRendering->moveObjectToCell(copy, vec, currCell); + + if (MWWorld::Class::get(ptr).isActor()) { + MWMechanics::MechanicsManager *mechMgr = + MWBase::Environment::get().getMechanicsManager(); + + mechMgr->removeActor(ptr); + mechMgr->addActor(copy); + } else { + std::string script = + MWWorld::Class::get(ptr).getScript(ptr); + if (!script.empty()) { + mLocalScripts.remove(ptr); + mLocalScripts.add(script, copy); + } + } + } + ptr.getRefData().setCount(0); } } if (haveToMove) { mRendering->moveObject(ptr, vec); mPhysics->moveObject(ptr.getRefData().getHandle(), vec); } - return cellChanged; + } + + bool World::moveObjectImp(const Ptr& ptr, float x, float y, float z) + { + CellStore *cell = ptr.getCell(); + if (cell->isExterior()) { + int cellX, cellY; + positionToIndex(x, y, cellX, cellY); + + cell = getExterior(cellX, cellY); + } + moveObject(ptr, *cell, x, y, z); + + return cell != ptr.getCell(); } void World::moveObject (const Ptr& ptr, float x, float y, float z) @@ -1058,7 +1050,7 @@ namespace MWWorld pos.pos[1] = -result.second[2]; pos.pos[2] = result.second[1]; - placeObject(object, *cell, pos); + copyObjectToCell(object, *cell, pos); object.getRefData().setCount(0); return true; @@ -1076,7 +1068,7 @@ namespace MWWorld } void - World::placeObject(const Ptr &object, CellStore &cell, const ESM::Position &pos) + World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos) { /// \todo add searching correct cell for position specified MWWorld::Ptr dropped = @@ -1119,7 +1111,7 @@ namespace MWWorld mPhysics->castRay(orig, dir, len); pos.pos[2] = hit.second.z; - placeObject(object, *cell, pos); + copyObjectToCell(object, *cell, pos); object.getRefData().setCount(0); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d39871c21d..e33e90eaa4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -90,7 +90,7 @@ namespace MWWorld ///< @return true if the active cell (cell player is in) changed virtual void - placeObject(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); public: @@ -206,6 +206,7 @@ namespace MWWorld virtual void deleteObject (const Ptr& ptr); virtual void moveObject (const Ptr& ptr, float x, float y, float z); + virtual void moveObject (const Ptr& ptr, CellStore &newCell, float x, float y, float z); virtual void scaleObject (const Ptr& ptr, float scale); From 4977fafb9c252c00fb148288fcd4bae267d5bc6e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 14:27:14 +0200 Subject: [PATCH 011/143] updated credits.txt --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 3a942bfd8a..5b8ede1178 100644 --- a/credits.txt +++ b/credits.txt @@ -14,6 +14,7 @@ Jason “jhooks” Hooks Karl-Felix “k1ll” Glatzer Lukasz “lgro” Gromanowski Marc “Zini” Zinnschlag +Michael Mc Donnell Michael “werdanith” Papageorgiou Nikolay “corristo” Kasyanov Pieter “pvdk” van der Kloet From 9b567e4ba69060286ea9a80e4b596d03b9f6c007 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 14:54:35 +0200 Subject: [PATCH 012/143] Issue #107: Removed a redundant function in world interface; added a comment --- apps/openmw/mwbase/world.hpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d9e19bead8..80669d5869 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -46,6 +46,7 @@ namespace MWWorld namespace MWBase { + /// \brief Interface for the World (implemented in MWWorld) class World { World (const World&); @@ -54,14 +55,6 @@ namespace MWBase World& operator= (const World&); ///< not implemented - protected: - - virtual void - placeObject( - const MWWorld::Ptr &ptr, - MWWorld::CellStore &cell, - const ESM::Position &pos) = 0; - public: enum RenderMode From 28ecfb4290e4e867d11379ec882d2e9439d7cf09 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 15:18:55 +0200 Subject: [PATCH 013/143] Issue #107: ScriptManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +-- apps/openmw/mwbase/environment.hpp | 12 ++-- apps/openmw/mwbase/scriptmanager.hpp | 62 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanager.cpp | 2 +- apps/openmw/mwscript/compilercontext.cpp | 5 +- apps/openmw/mwscript/globalscripts.cpp | 11 ++-- apps/openmw/mwscript/globalscripts.hpp | 5 +- apps/openmw/mwscript/interpretercontext.cpp | 2 +- ...scriptmanager.cpp => scriptmanagerimp.cpp} | 4 +- ...scriptmanager.hpp => scriptmanagerimp.hpp} | 17 ++--- 12 files changed, 96 insertions(+), 37 deletions(-) create mode 100644 apps/openmw/mwbase/scriptmanager.hpp rename apps/openmw/mwscript/{scriptmanager.cpp => scriptmanagerimp.cpp} (98%) rename apps/openmw/mwscript/{scriptmanager.hpp => scriptmanagerimp.hpp} (75%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 556b9a1ba2..768054ed76 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -36,7 +36,7 @@ add_openmw_dir (mwdialogue ) add_openmw_dir (mwscript - locals scriptmanager compilercontext interpretercontext cellextensions miscextensions + locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions animationextensions transformationextensions consoleextensions userextensions @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world + environment world scriptmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5e9aedf66a..d4fa078ae9 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -18,7 +18,7 @@ #include "mwgui/window_manager.hpp" #include "mwgui/cursorreplace.hpp" -#include "mwscript/scriptmanager.hpp" +#include "mwscript/scriptmanagerimp.hpp" #include "mwscript/extensions.hpp" #include "mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 7218f22ebd..e015000039 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -5,8 +5,6 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwscript/scriptmanager.hpp" - #include "../mwsound/soundmanager.hpp" #include "../mwdialogue/dialoguemanager.hpp" @@ -15,6 +13,7 @@ #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" +#include "scriptmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -42,7 +41,7 @@ void MWBase::Environment::setSoundManager (MWSound::SoundManager *soundManager) mSoundManager = soundManager; } -void MWBase::Environment::setScriptManager (MWScript::ScriptManager *scriptManager) +void MWBase::Environment::setScriptManager (ScriptManager *scriptManager) { mScriptManager = scriptManager; } @@ -89,7 +88,7 @@ MWSound::SoundManager *MWBase::Environment::getSoundManager() const return mSoundManager; } -MWScript::ScriptManager *MWBase::Environment::getScriptManager() const +MWBase::ScriptManager *MWBase::Environment::getScriptManager() const { assert (mScriptManager); return mScriptManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index ad5a6f655e..50d9e3940a 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWSound class SoundManager; } -namespace MWScript -{ - class ScriptManager; -} - namespace MWGui { class WindowManager; @@ -35,6 +30,7 @@ namespace MWInput namespace MWBase { class World; + class ScriptManager; /// \brief Central hub for mw-subsystems /// @@ -48,7 +44,7 @@ namespace MWBase World *mWorld; MWSound::SoundManager *mSoundManager; - MWScript::ScriptManager *mScriptManager; + ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; MWDialogue::DialogueManager *mDialogueManager; @@ -72,7 +68,7 @@ namespace MWBase void setSoundManager (MWSound::SoundManager *soundManager); - void setScriptManager (MWScript::ScriptManager *scriptManager); + void setScriptManager (MWBase::ScriptManager *scriptManager); void setWindowManager (MWGui::WindowManager *windowManager); @@ -91,7 +87,7 @@ namespace MWBase MWSound::SoundManager *getSoundManager() const; - MWScript::ScriptManager *getScriptManager() const; + MWBase::ScriptManager *getScriptManager() const; MWGui::WindowManager *getWindowManager() const; diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp new file mode 100644 index 0000000000..a8db868c3e --- /dev/null +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -0,0 +1,62 @@ +#ifndef GAME_MWBASE_SCRIPTMANAGERIMP_H +#define GAME_MWBASE_SCRIPTMANAGERIMP_H + +#include + +namespace Interpreter +{ + class Context; +} + +namespace Compiler +{ + class Locals; +} + +namespace MWScript +{ + class GlobalScripts; +} + +namespace MWBase +{ + /// \brief Interface for script manager (implemented in MWScript) + class ScriptManager + { + ScriptManager (const ScriptManager&); + ///< not implemented + + ScriptManager& operator= (const ScriptManager&); + ///< not implemented + + public: + + ScriptManager() {} + + virtual ~ScriptManager() {} + + virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; + ///< Run the script with the given name (compile first, if not compiled yet) + + virtual bool compile (const std::string& name) = 0; + ///< Compile script with the given namen + /// \return Success? + + virtual std::pair compileAll() = 0; + ///< Compile all scripts + /// \return count, success + + virtual Compiler::Locals& getLocals (const std::string& name) = 0; + ///< Return locals for script \a name. + + virtual MWScript::GlobalScripts& getGlobalScripts() = 0; + + virtual int getLocalIndex (const std::string& scriptId, const std::string& variable, + char type) = 0; + ///< Return index of the variable of the given name and type in the given script. Will + /// throw an exception, if there is no such script or variable or the type does not match. + + }; +} + +#endif diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 6f3f0c9012..3fde7ad6c0 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -26,7 +27,6 @@ #include #include "../mwscript/extensions.hpp" -#include "../mwscript/scriptmanager.hpp" #include #include diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index 57e786b118..139a5cc6cf 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -3,14 +3,15 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" -#include "scriptmanager.hpp" - namespace MWScript { CompilerContext::CompilerContext (Type type) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 3f4cec7235..6407bf0cbb 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -6,13 +6,15 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/scriptmanager.hpp" + #include "interpretercontext.hpp" -#include "scriptmanager.hpp" namespace MWScript { - GlobalScripts::GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager) - : mStore (store), mScriptManager (scriptManager) + GlobalScripts::GlobalScripts (const ESMS::ESMStore& store) + : mStore (store) { addScript ("Main"); @@ -63,9 +65,8 @@ namespace MWScript { MWScript::InterpreterContext interpreterContext ( &iter->second.second, MWWorld::Ptr()); - mScriptManager.run (iter->first, interpreterContext); + MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); } } - } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 3d62e4d7a0..cc2f7ffdd4 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -13,17 +13,14 @@ namespace ESMS namespace MWScript { - class ScriptManager; - class GlobalScripts { const ESMS::ESMStore& mStore; - ScriptManager& mScriptManager; std::map > mScripts; // running, local variables public: - GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager); + GlobalScripts (const ESMS::ESMStore& store); void addScript (const std::string& name); diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 3a824473e9..327eed9136 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -18,7 +19,6 @@ #include "locals.hpp" #include "globalscripts.hpp" -#include "scriptmanager.hpp" namespace MWScript { diff --git a/apps/openmw/mwscript/scriptmanager.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp similarity index 98% rename from apps/openmw/mwscript/scriptmanager.cpp rename to apps/openmw/mwscript/scriptmanagerimp.cpp index d8bd269c69..0b33d43fa7 100644 --- a/apps/openmw/mwscript/scriptmanager.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -1,5 +1,5 @@ -#include "scriptmanager.hpp" +#include "scriptmanagerimp.hpp" #include #include @@ -21,7 +21,7 @@ namespace MWScript Compiler::Context& compilerContext) : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), - mOpcodesInstalled (false), mGlobalScripts (store, *this) + mOpcodesInstalled (false), mGlobalScripts (store) {} bool ScriptManager::compile (const std::string& name) diff --git a/apps/openmw/mwscript/scriptmanager.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp similarity index 75% rename from apps/openmw/mwscript/scriptmanager.hpp rename to apps/openmw/mwscript/scriptmanagerimp.hpp index a466f903dd..bacc232b2c 100644 --- a/apps/openmw/mwscript/scriptmanager.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -10,6 +10,8 @@ #include #include +#include "../mwbase/scriptmanager.hpp" + #include "globalscripts.hpp" namespace ESMS @@ -30,7 +32,7 @@ namespace Interpreter namespace MWScript { - class ScriptManager + class ScriptManager : public MWBase::ScriptManager { Compiler::StreamErrorHandler mErrorHandler; const ESMS::ESMStore& mStore; @@ -51,23 +53,24 @@ namespace MWScript ScriptManager (const ESMS::ESMStore& store, bool verbose, Compiler::Context& compilerContext); - void run (const std::string& name, Interpreter::Context& interpreterContext); + virtual void run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) - bool compile (const std::string& name); + virtual bool compile (const std::string& name); ///< Compile script with the given namen /// \return Success? - std::pair compileAll(); + virtual std::pair compileAll(); ///< Compile all scripts /// \return count, success - Compiler::Locals& getLocals (const std::string& name); + virtual Compiler::Locals& getLocals (const std::string& name); ///< Return locals for script \a name. - GlobalScripts& getGlobalScripts(); + virtual GlobalScripts& getGlobalScripts(); - int getLocalIndex (const std::string& scriptId, const std::string& variable, char type); + virtual int getLocalIndex (const std::string& scriptId, const std::string& variable, + char type); ///< Return index of the variable of the given name and type in the given script. Will /// throw an exception, if there is no such script or variable or the type does not match. }; From ec9cf4d3c60c39edd89f0c734bc84c726640a8d1 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 00:15:52 +0400 Subject: [PATCH 014/143] rotateObject() added, input system rewritten --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwinput/inputmanager.cpp | 14 +++-- apps/openmw/mwinput/mouselookevent.cpp | 28 ++++++++++ apps/openmw/mwinput/mouselookevent.hpp | 57 +++++++++++++++++++++ apps/openmw/mwrender/player.cpp | 50 +++++++++++++----- apps/openmw/mwrender/player.hpp | 34 +++++++++--- apps/openmw/mwrender/renderinginterface.hpp | 15 +++--- apps/openmw/mwrender/renderingmanager.cpp | 33 ++++++++++-- apps/openmw/mwrender/renderingmanager.hpp | 8 +-- apps/openmw/mwsound/soundmanager.cpp | 4 +- apps/openmw/mwworld/player.cpp | 12 +---- apps/openmw/mwworld/player.hpp | 13 +---- apps/openmw/mwworld/scene.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 28 +++++----- apps/openmw/mwworld/worldimp.hpp | 2 +- 16 files changed, 217 insertions(+), 86 deletions(-) create mode 100644 apps/openmw/mwinput/mouselookevent.cpp create mode 100644 apps/openmw/mwinput/mouselookevent.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 556b9a1ba2..82f8680fbd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,6 +21,7 @@ add_openmw_dir (mwrender add_openmw_dir (mwinput inputmanager + mouselookevent ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d9e19bead8..298a2ac799 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -186,7 +186,7 @@ namespace MWBase virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; - virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z) = 0; + virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const = 0; diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 6bda77ea8e..bbd80e8c08 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "../mwgui/window_manager.hpp" @@ -16,12 +15,12 @@ #include +#include "mouselookevent.hpp" + #include "../engine.hpp" #include "../mwworld/player.hpp" -#include "../mwrender/player.hpp" - #include #include #include @@ -82,7 +81,7 @@ namespace MWInput OEngine::Render::ExitListener exit; Mangle::Input::OISDriver input; OEngine::Input::Poller poller; - OEngine::Render::MouseLookEventPtr mouse; + MouseLookEventPtr mouse; OEngine::GUI::EventInjectorPtr guiEvents; MWWorld::Player &player; MWGui::WindowManager &windows; @@ -279,8 +278,7 @@ private: // Add the exit listener ogre.getRoot()->addFrameListener(&exit); - // Set up the mouse handler and tell it about the player camera - mouse = MouseLookEventPtr(new MouseLookEvent(player.getRenderer()->getCamera())); + mouse = MouseLookEventPtr(new MouseLookEvent()); // This event handler pumps events into MyGUI guiEvents = EventInjectorPtr(new EventInjector(windows.getGui())); @@ -419,7 +417,7 @@ private: if(guiMode) { // Disable mouse look - mouse->setCamera(NULL); + mouse->disable(); // Enable GUI events guiEvents->enabled = true; @@ -428,7 +426,7 @@ private: { // Start mouse-looking again. TODO: This should also allow // for other ways to disable mouselook, like paralyzation. - mouse->setCamera(player.getRenderer()->getCamera()); + mouse->enable(); // Disable GUI events guiEvents->enabled = false; diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp new file mode 100644 index 0000000000..4138c481c5 --- /dev/null +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -0,0 +1,28 @@ +#include "mouselookevent.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +#include +#include +#include + +using namespace OIS; +using namespace MWInput; + +void MouseLookEvent::event(Type type, int index, const void *p) +{ + if (type != EV_MouseMove || mDisabled) { + return; + } + + MouseEvent *arg = (MouseEvent*)(p); + + float x = arg->state.X.rel * sensX; + float y = arg->state.Y.rel * sensY; + + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); +} diff --git a/apps/openmw/mwinput/mouselookevent.hpp b/apps/openmw/mwinput/mouselookevent.hpp new file mode 100644 index 0000000000..af996643f3 --- /dev/null +++ b/apps/openmw/mwinput/mouselookevent.hpp @@ -0,0 +1,57 @@ +#ifndef _MWINPUT_MOUSELOOKEVENT_H +#define _MWINPUT_MOUSELOOKEVENT_H + +/* + A mouse-look class for Ogre. Accepts input events from Mangle::Input + and translates them. + + You can adjust the mouse sensibility and switch to a different + camera. The mouselook class also has an optional wrap protection + that keeps the camera from flipping upside down. + + You can disable the mouse looker at any time by calling + setCamera(NULL), and reenable it by setting the camera back. + + NOTE: The current implementation will ONLY work for native OIS + events. + */ + +#include + +namespace MWInput +{ + class MouseLookEvent : public Mangle::Input::Event + { + float sensX, sensY; // Mouse sensibility + bool flipProt; // Flip protection + bool mDisabled; + + public: + MouseLookEvent(float sX = 0.2, float sY = 0.2, bool prot=true) + : sensX(sX), sensY(sY), flipProt(prot) + {} + + void setSens(float sX, float sY) { + sensX = sX; + sensY = sY; + } + + void setProt(bool p) { + flipProt = p; + } + + void disable() { + mDisabled = true; + } + + void enable() { + mDisabled = false; + } + + void event(Type type, int index, const void *p); + }; + + typedef boost::shared_ptr MouseLookEventPtr; +} + +#endif diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 27ae28e14e..a71d9da686 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -1,34 +1,58 @@ - #include "player.hpp" #include +#include + +#include "../mwworld/ptr.hpp" +#include "../mwworld/refdata.hpp" namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) - : mCamera (camera), mNode (node) + : mCamera (camera), + mNode (node), + mFirstPersonView(true), + mVanityModeEnabled(false) {} - void Player::setRot(float x, float y, float z) + bool Player::setRotation(const Ogre::Vector3 &rot) { - Ogre::SceneNode *sceneNode = mNode; - Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); - Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); + Ogre::SceneNode *sceneNode = mNode; + Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); + Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); - // we are only interested in X and Y rotation + // we are only interested in X and Y rotation - // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(x), Ogre::Vector3::UNIT_X); + // Rotate around X axis + Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); - // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-z), Ogre::Vector3::UNIT_Y); + // Rotate around Y axis + Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); - pitchNode->setOrientation(xr); - yawNode->setOrientation(yr); + pitchNode->setOrientation(xr); + yawNode->setOrientation(yr); + + return !mVanityModeEnabled; } std::string Player::getHandle() const { return mNode->getName(); } + + void Player::attachTo(const MWWorld::Ptr &ptr) + { + ptr.getRefData().setBaseNode(mNode); + } + + bool Player::adjustRotation(const Ogre::Vector3 &rot) + { + Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); + Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); + + pitchNode->pitch(Ogre::Degree(rot.x)); + yawNode->yaw(Ogre::Degree(rot.z)); + + return !mVanityModeEnabled; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 0ba936f6f9..a145997a97 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -3,12 +3,19 @@ #include + namespace Ogre -{ +{ + class Vector3; class Camera; class SceneNode; } +namespace MWWorld +{ + class Ptr; +} + namespace MWRender { /// \brief Player character rendering and camera control @@ -17,17 +24,28 @@ namespace MWRender Ogre::Camera *mCamera; Ogre::SceneNode* mNode; - public: + bool mFirstPersonView; + bool mVanityModeEnabled; - Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); + public: - Ogre::Camera *getCamera() { return mCamera; } + Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); - /// Set where the player is looking at. Uses Morrowind (euler) angles - void setRot(float x, float y, float z); + /// Set where the player is looking at. Uses Morrowind (euler) angles + bool setRotation(const Ogre::Vector3 &rot); + bool adjustRotation(const Ogre::Vector3 &rot); - std::string getHandle() const; - Ogre::SceneNode* getNode() {return mNode;} + std::string getHandle() const; + + void attachTo(const MWWorld::Ptr &); + + void toggleViewMode() { + mFirstPersonView = !mFirstPersonView; + } + + void toggleVanityMode() { + mVanityModeEnabled = !mVanityModeEnabled; + } }; } diff --git a/apps/openmw/mwrender/renderinginterface.hpp b/apps/openmw/mwrender/renderinginterface.hpp index 03935bef60..8ae2c0f8f9 100644 --- a/apps/openmw/mwrender/renderinginterface.hpp +++ b/apps/openmw/mwrender/renderinginterface.hpp @@ -1,16 +1,17 @@ #ifndef _GAME_RENDERING_INTERFACE_H #define _GAME_RENDERING_INTERFACE_H -namespace MWRender{ - class Objects; - class Actors; - class Player; + +namespace MWRender +{ + class Objects; + class Actors; -class RenderingInterface{ + class RenderingInterface + { public: virtual MWRender::Objects& getObjects() = 0; - virtual MWRender::Player& getPlayer() = 0; virtual MWRender::Actors& getActors() = 0; virtual ~RenderingInterface(){}; }; } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9eb08d77d7..afab7416f4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -183,10 +183,6 @@ MWRender::Actors& RenderingManager::getActors(){ return mActors; } -MWRender::Player& RenderingManager::getPlayer(){ - return (*mPlayer); -} - OEngine::Render::Fader* RenderingManager::getFader() { return mRendering.getFader(); @@ -251,9 +247,31 @@ void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale){ } -void RenderingManager::rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation){ +bool +RenderingManager::rotateObject( + const MWWorld::Ptr &ptr, + Ogre::Vector3 &rot, + bool adjust) +{ + if (ptr.getRefData().getHandle() == "player") { + if (adjust) { + return mPlayer->adjustRotation(rot); + } else { + return mPlayer->setRotation(rot); + } + } + MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); + + Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Z); + + ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); + + return true; } + void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store){ } @@ -770,4 +788,9 @@ void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned i } } +void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) +{ + mPlayer->attachTo(ptr); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 12cbee9b87..bedd66c461 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -56,11 +56,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine); virtual ~RenderingManager(); - - - virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as - /// MWWorld::Player has been rewritten to not need access - /// to internal details of the rendering system anymore + void attachCameraTo(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); Compositors* getCompositors(); @@ -89,7 +85,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); - void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation); + bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); void setWaterHeight(const float height); void toggleWater(); diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index c6332d9f32..e4af847d54 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -492,11 +492,13 @@ namespace MWSound startRandomTitle(); const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->cell; - Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); +// Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); Ogre::Vector3 nPos, nDir, nUp; + /* nPos = cam->getRealPosition(); nDir = cam->getRealDirection(); nUp = cam->getRealUp(); + */ Environment env = Env_Normal; if((cell->data.flags&cell->HasWater) && nPos.y < cell->water) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 54e5a625fc..0f05ae24da 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -6,8 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwrender/player.hpp" - #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" @@ -15,8 +13,8 @@ namespace MWWorld { - Player::Player (MWRender::Player *renderer, const ESM::NPC *player, const MWBase::World& world) : - mCellStore (0), mRenderer (renderer), mClass (0), + Player::Player (const ESM::NPC *player, const MWBase::World& world) : + mCellStore (0), mClass (0), mAutoMove (false), mForwardBackward (0) { mPlayer.base = player; @@ -28,7 +26,6 @@ namespace MWWorld float* playerPos = mPlayer.mData.getPosition().pos; playerPos[0] = playerPos[1] = playerPos[2] = 0; - mPlayer.mData.setBaseNode(renderer->getNode()); /// \todo Do not make a copy of classes defined in esm/p records. mClass = new ESM::Class (*world.getStore().classes.find (player->cls)); } @@ -38,11 +35,6 @@ namespace MWWorld delete mClass; } - void Player::setRot(float x, float y, float z) - { - mRenderer->setRot(x, y, z); - } - void Player::setClass (const ESM::Class& class_) { ESM::Class *new_class = new ESM::Class (class_); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d2058dea6c..68df2ec6df 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -14,11 +14,6 @@ namespace MWBase class World; } -namespace MWRender -{ - class Player; -} - namespace MWWorld { class CellStore; @@ -28,7 +23,6 @@ namespace MWWorld { LiveCellRef mPlayer; MWWorld::CellStore *mCellStore; - MWRender::Player *mRenderer; std::string mName; bool mMale; std::string mRace; @@ -38,13 +32,10 @@ namespace MWWorld int mForwardBackward; public: - Player(MWRender::Player *renderer, const ESM::NPC *player, const MWBase::World& world); + Player(const ESM::NPC *player, const MWBase::World& world); ~Player(); - /// Set where the player is looking at. Uses Morrowind (euler) angles - void setRot(float x, float y, float z); - void setCell (MWWorld::CellStore *cellStore) { mCellStore = cellStore; @@ -56,8 +47,6 @@ namespace MWWorld return ptr; } - MWRender::Player *getRenderer() { return mRenderer; } - void setName (const std::string& name) { mName = name; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c768fce26d..1fa77f6f6d 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -149,7 +149,7 @@ namespace MWWorld if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); - MWBase::Environment::get().getWorld()->getPlayer().setRot (pos.rot[0], pos.rot[1], pos.rot[2]); + world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); } world->getPlayer().setCell(cell); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8fb7b1ba7d..3cb1e6e320 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -189,8 +189,9 @@ namespace MWWorld mEsm.open (masterPath.string()); mStore.load (mEsm); - MWRender::Player* play = &(mRendering->getPlayer()); - mPlayer = new MWWorld::Player (play, mStore.npcs.find ("player"), *this); + mPlayer = new MWWorld::Player (mStore.npcs.find ("player"), *this); + mRendering->attachCameraTo(mPlayer->getPlayer()); + mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); // global variables @@ -598,19 +599,20 @@ namespace MWWorld mPhysics->scaleObject( ptr.getRefData().getHandle(), scale ); } - void World::rotateObject (const Ptr& ptr,float x,float y,float z) + void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { - MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); + Ogre::Vector3 rot(x, y, z); + if (mRendering->rotateObject(ptr, rot, adjust)) { + float *objRot = ptr.getRefData().getPosition().rot; + objRot[0] = Ogre::Degree(rot.x).valueRadians(); + objRot[1] = Ogre::Degree(rot.y).valueRadians(); + objRot[2] = Ogre::Degree(rot.z).valueRadians(); - ptr.getRefData().getPosition().rot[0] = Ogre::Degree(x).valueRadians(); - ptr.getRefData().getPosition().rot[1] = Ogre::Degree(y).valueRadians(); - ptr.getRefData().getPosition().rot[2] = Ogre::Degree(z).valueRadians(); - - Ogre::Quaternion rotx(Ogre::Degree(-x),Ogre::Vector3::UNIT_X); - Ogre::Quaternion roty(Ogre::Degree(-y),Ogre::Vector3::UNIT_Y); - Ogre::Quaternion rotz(Ogre::Degree(-z),Ogre::Vector3::UNIT_Z); - ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); - mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); + mPhysics->rotateObject( + ptr.getRefData().getHandle(), + ptr.getRefData().getBaseNode()->getOrientation() + ); + } } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f80b88be2c..e2570b97c5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -209,7 +209,7 @@ namespace MWWorld virtual void scaleObject (const Ptr& ptr, float scale); - virtual void rotateObject (const Ptr& ptr,float x,float y,float z); + virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; From a2d87d5f5b55939735a11e21bf2b0831f251a183 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 10:24:18 +0400 Subject: [PATCH 015/143] added camera flip control, fixed input rotation axis --- apps/openmw/mwinput/mouselookevent.cpp | 2 +- apps/openmw/mwrender/player.cpp | 32 +++++++++++++++++++++++--- apps/openmw/mwrender/player.hpp | 2 ++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index 4138c481c5..f318ce6660 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -24,5 +24,5 @@ void MouseLookEvent::event(Type type, int index, const void *p) float y = arg->state.Y.rel * sensY; MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index a71d9da686..0d101a8393 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -24,14 +24,16 @@ namespace MWRender // we are only interested in X and Y rotation // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); pitchNode->setOrientation(xr); yawNode->setOrientation(yr); + controlFlip(); + return !mVanityModeEnabled; } @@ -51,8 +53,32 @@ namespace MWRender Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); pitchNode->pitch(Ogre::Degree(rot.x)); - yawNode->yaw(Ogre::Degree(rot.z)); + yawNode->yaw(Ogre::Degree(-rot.z)); + + controlFlip(); return !mVanityModeEnabled; } + + void Player::controlFlip() + { + Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); + Ogre::Quaternion orient = pitchNode->getOrientation(); + + float pitchAngle = + (2 * Ogre::Degree(Ogre::Math::ACos(orient.w)).valueDegrees()); + + // Limit the pitch between -90 degress and +90 degrees, Quake3-style. + if (pitchAngle > 90.0f) + { + Ogre::Real sqrt = Ogre::Math::Sqrt(0.5f); + if (orient.x > 0) { + // Set orientation to 90 degrees on X-axis. + pitchNode->setOrientation(Ogre::Quaternion(sqrt, sqrt, 0, 0)); + } else if (orient.x < 0) { + // Sets orientation to -90 degrees on X-axis. + pitchNode->setOrientation(Ogre::Quaternion(sqrt, -sqrt, 0, 0)); + } + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index a145997a97..35333c9f10 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -27,6 +27,8 @@ namespace MWRender bool mFirstPersonView; bool mVanityModeEnabled; + void controlFlip(); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); From 1511eb3549264c4573baccca19a5f5446046018b Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 10:55:49 +0400 Subject: [PATCH 016/143] implemented enable/disable player looking switch --- apps/openmw/mwinput/inputmanager.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index bbd80e8c08..ea279cfdfd 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -424,9 +424,10 @@ private: } else { - // Start mouse-looking again. TODO: This should also allow - // for other ways to disable mouselook, like paralyzation. - mouse->enable(); + // Start mouse-looking again if allowed. + if (mControlSwitch["playerlooking"]) { + mouse->enable(); + } // Disable GUI events guiEvents->enabled = false; @@ -439,13 +440,19 @@ private: return; } /// \note 7 switches at all, if-else is relevant - if (sw == "playercontrols") { + if (sw == "playercontrols" && !value) { player.setLeftRight(0); player.setForwardBackward(0); player.setAutoMove(false); - } else if (sw == "playerjumping") { + } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time player.setUpDown(0); + } else if (sw == "playerlooking") { + if (value) { + mouse->enable(); + } else { + mouse->disable(); + } } mControlSwitch[sw] = value; } From 378fcc246078a3273180b91e5574c3ec8e821e43 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 11:10:18 +0400 Subject: [PATCH 017/143] fix DisablePlayerControl logic --- apps/openmw/mwinput/inputmanager.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index ea279cfdfd..4281612f0a 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -399,14 +399,14 @@ private: } else player.setForwardBackward (0); - } - if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) - player.setUpDown (1); - else if (poller.isDown(A_Crouch)) - player.setUpDown (-1); - else - player.setUpDown (0); + if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) + player.setUpDown (1); + else if (poller.isDown(A_Crouch)) + player.setUpDown (-1); + else + player.setUpDown (0); + } } // Switch between gui modes. Besides controlling the Gui windows @@ -444,6 +444,7 @@ private: player.setLeftRight(0); player.setForwardBackward(0); player.setAutoMove(false); + player.setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time player.setUpDown(0); From e6ede480c74a1f4a0d34c92b2a634a610d33fd1a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 09:41:17 +0200 Subject: [PATCH 018/143] Issue #107: fixed up some interfaces --- apps/openmw/mwdialogue/dialoguemanager.cpp | 18 +++++++++--------- apps/openmw/mwdialogue/dialoguemanager.hpp | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 3fde7ad6c0..7c23ecebbd 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -599,12 +599,12 @@ namespace MWDialogue } } - void DialogueManager::addTopic(std::string topic) + void DialogueManager::addTopic (const std::string& topic) { mKnownTopics[toLower(topic)] = true; } - void DialogueManager::parseText(std::string text) + void DialogueManager::parseText (std::string text) { std::list::iterator it; for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it) @@ -804,7 +804,7 @@ namespace MWDialogue mChoice = choice; } - void DialogueManager::keywordSelected(std::string keyword) + void DialogueManager::keywordSelected (const std::string& keyword) { if(!mIsInChoice) { @@ -846,11 +846,11 @@ namespace MWDialogue MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); } - void DialogueManager::questionAnswered(std::string answere) + void DialogueManager::questionAnswered (const std::string& answer) { - if(mChoiceMap.find(answere) != mChoiceMap.end()) + if(mChoiceMap.find(answer) != mChoiceMap.end()) { - mChoice = mChoiceMap[answere]; + mChoice = mChoiceMap[answer]; std::vector::const_iterator iter; if(mDialogueMap.find(mLastTopic) != mDialogueMap.end()) @@ -882,13 +882,13 @@ namespace MWDialogue } } - void DialogueManager::printError(std::string error) + void DialogueManager::printError (std::string error) { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->addText(error); } - void DialogueManager::askQuestion(std::string question, int choice) + void DialogueManager::askQuestion (const std::string& question, int choice) { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->askQuestion(question); @@ -896,7 +896,7 @@ namespace MWDialogue mIsInChoice = true; } - std::string DialogueManager::getFaction() + std::string DialogueManager::getFaction() const { if (mActor.getTypeName() != typeid(ESM::NPC).name()) return ""; diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanager.hpp index d139ddc010..f28b5647c4 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.hpp +++ b/apps/openmw/mwdialogue/dialoguemanager.hpp @@ -52,19 +52,19 @@ namespace MWDialogue void startDialogue (const MWWorld::Ptr& actor); - void addTopic(std::string topic); + void addTopic (const std::string& topic); - void askQuestion(std::string question,int choice); + void askQuestion (const std::string& question,int choice); void goodbye(); ///get the faction of the actor you are talking with - std::string getFaction(); + std::string getFaction() const; //calbacks for the GUI - void keywordSelected(std::string keyword); + void keywordSelected (const std::string& keyword); void goodbyeSelected(); - void questionAnswered(std::string answere); + void questionAnswered (const std::string& answer); }; } From 923109b2607be6c203e4caa4a912ad98ef5c04b4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 10:35:53 +0200 Subject: [PATCH 019/143] Issue #107: DialogueManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/dialoguemanager.hpp | 45 +++++++++++++++++++ apps/openmw/mwbase/environment.cpp | 6 +-- apps/openmw/mwbase/environment.hpp | 8 ++-- ...oguemanager.cpp => dialoguemanagerimp.cpp} | 2 +- ...oguemanager.hpp => dialoguemanagerimp.hpp} | 25 ++++++----- apps/openmw/mwgui/dialogue.cpp | 3 +- apps/openmw/mwscript/dialogueextensions.cpp | 6 ++- apps/openmw/mwscript/statsextensions.cpp | 2 +- apps/openmw/mwworld/actiontalk.cpp | 3 +- 11 files changed, 78 insertions(+), 28 deletions(-) create mode 100644 apps/openmw/mwbase/dialoguemanager.hpp rename apps/openmw/mwdialogue/{dialoguemanager.cpp => dialoguemanagerimp.cpp} (99%) rename apps/openmw/mwdialogue/{dialoguemanager.hpp => dialoguemanagerimp.hpp} (71%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 768054ed76..b9da636cf7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -32,7 +32,7 @@ add_openmw_dir (mwgui ) add_openmw_dir (mwdialogue - dialoguemanager journal journalentry quest topic + dialoguemanagerimp journal journalentry quest topic ) add_openmw_dir (mwscript @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world scriptmanager + environment world scriptmanager dialoguemanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d4fa078ae9..d97d40580d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -29,7 +29,7 @@ #include "mwclass/classes.hpp" -#include "mwdialogue/dialoguemanager.hpp" +#include "mwdialogue/dialoguemanagerimp.hpp" #include "mwdialogue/journal.hpp" #include "mwmechanics/mechanicsmanager.hpp" diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp new file mode 100644 index 0000000000..ca339e81e4 --- /dev/null +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_MWBASE_DIALOGUEMANAGERIMP_H +#define GAME_MWBASE_DIALOGUEMANAGERIMP_H + +#include + +namespace MWWorld +{ + class Ptr; +} + +namespace MWBase +{ + class DialogueManager + { + DialogueManager (const DialogueManager&); + ///< not implemented + + DialogueManager& operator= (const DialogueManager&); + ///< not implemented + + public: + + DialogueManager() {} + + virtual ~DialogueManager() {} + + virtual void startDialogue (const MWWorld::Ptr& actor) = 0; + + virtual void addTopic (const std::string& topic) = 0; + + virtual void askQuestion (const std::string& question,int choice) = 0; + + virtual void goodbye() = 0; + + ///get the faction of the actor you are talking with + virtual std::string getFaction() const = 0; + + //calbacks for the GUI + virtual void keywordSelected (const std::string& keyword) = 0; + virtual void goodbyeSelected() = 0; + virtual void questionAnswered (const std::string& answer) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index e015000039..c0fc56f97a 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -7,13 +7,13 @@ #include "../mwsound/soundmanager.hpp" -#include "../mwdialogue/dialoguemanager.hpp" #include "../mwdialogue/journal.hpp" #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" #include "scriptmanager.hpp" +#include "dialoguemanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -56,7 +56,7 @@ void MWBase::Environment::setMechanicsManager (MWMechanics::MechanicsManager *me mMechanicsManager = mechanicsManager; } -void MWBase::Environment::setDialogueManager (MWDialogue::DialogueManager *dialogueManager) +void MWBase::Environment::setDialogueManager (DialogueManager *dialogueManager) { mDialogueManager = dialogueManager; } @@ -106,7 +106,7 @@ MWMechanics::MechanicsManager *MWBase::Environment::getMechanicsManager() const return mMechanicsManager; } -MWDialogue::DialogueManager *MWBase::Environment::getDialogueManager() const +MWBase::DialogueManager *MWBase::Environment::getDialogueManager() const { assert (mDialogueManager); return mDialogueManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 50d9e3940a..72d92f00fa 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -18,7 +18,6 @@ namespace MWMechanics namespace MWDialogue { - class DialogueManager; class Journal; } @@ -31,6 +30,7 @@ namespace MWBase { class World; class ScriptManager; + class DialogueManager; /// \brief Central hub for mw-subsystems /// @@ -47,7 +47,7 @@ namespace MWBase ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; - MWDialogue::DialogueManager *mDialogueManager; + DialogueManager *mDialogueManager; MWDialogue::Journal *mJournal; MWInput::MWInputManager *mInputManager; float mFrameDuration; @@ -74,7 +74,7 @@ namespace MWBase void setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager); - void setDialogueManager (MWDialogue::DialogueManager *dialogueManager); + void setDialogueManager (DialogueManager *dialogueManager); void setJournal (MWDialogue::Journal *journal); @@ -93,7 +93,7 @@ namespace MWBase MWMechanics::MechanicsManager *getMechanicsManager() const; - MWDialogue::DialogueManager *getDialogueManager() const; + DialogueManager *getDialogueManager() const; MWDialogue::Journal *getJournal() const; diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp similarity index 99% rename from apps/openmw/mwdialogue/dialoguemanager.cpp rename to apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 7c23ecebbd..129e78d0fb 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -1,5 +1,5 @@ -#include "dialoguemanager.hpp" +#include "dialoguemanagerimp.hpp" #include #include diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp similarity index 71% rename from apps/openmw/mwdialogue/dialoguemanager.hpp rename to apps/openmw/mwdialogue/dialoguemanagerimp.hpp index f28b5647c4..69cc88f253 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MMDIALOG_DIALOGUEMANAGER_H -#define GAME_MWDIALOG_DIALOGUEMANAGER_H +#ifndef GAME_MMDIALOG_DIALOGUEMANAGERIMP_H +#define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #include @@ -8,12 +8,15 @@ #include "../mwscript/interpretercontext.hpp" #include +#include "../mwbase/dialoguemanager.hpp" + #include "../mwworld/ptr.hpp" + #include namespace MWDialogue { - class DialogueManager + class DialogueManager : public MWBase::DialogueManager { bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo::SelectStruct& select) const; @@ -50,21 +53,21 @@ namespace MWDialogue DialogueManager (const Compiler::Extensions& extensions); - void startDialogue (const MWWorld::Ptr& actor); + virtual void startDialogue (const MWWorld::Ptr& actor); - void addTopic (const std::string& topic); + virtual void addTopic (const std::string& topic); - void askQuestion (const std::string& question,int choice); + virtual void askQuestion (const std::string& question,int choice); - void goodbye(); + virtual void goodbye(); ///get the faction of the actor you are talking with - std::string getFaction() const; + virtual std::string getFaction() const; //calbacks for the GUI - void keywordSelected (const std::string& keyword); - void goodbyeSelected(); - void questionAnswered (const std::string& answer); + virtual void keywordSelected (const std::string& keyword); + virtual void goodbyeSelected(); + virtual void questionAnswered (const std::string& answer); }; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 760c89f496..30089dd465 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -9,7 +9,8 @@ #include #include "../mwbase/environment.hpp" -#include "../mwdialogue/dialoguemanager.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/world.hpp" #include "dialogue_history.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index ec8ab59b41..4eb1293311 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -7,8 +7,10 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/dialoguemanager.hpp" + #include "../mwdialogue/journal.hpp" -#include "../mwdialogue/dialoguemanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -84,7 +86,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWDialogue::DialogueManager* dialogue = MWBase::Environment::get().getDialogueManager(); + MWBase::DialogueManager* dialogue = MWBase::Environment::get().getDialogueManager(); while(arg0>0) { std::string question = runtime.getStringLiteral (runtime[0].mInteger); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 0cbbe8398e..5113c9dbff 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -16,6 +16,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -26,7 +27,6 @@ #include "interpretercontext.hpp" #include "ref.hpp" -#include "../mwdialogue/dialoguemanager.hpp" namespace MWScript { diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index 78171bbe5a..d94cb67f41 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -2,8 +2,7 @@ #include "actiontalk.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" -#include "../mwdialogue/dialoguemanager.hpp" +#include "../mwbase/dialoguemanager.hpp" namespace MWWorld { From df60f4bf92c80da2fd1265de34de6c4f50de7736 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 13:27:32 +0400 Subject: [PATCH 020/143] stub for soundmanager, adjust rotation mode --- apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwsound/soundmanager.cpp | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index afab7416f4..be8b518b9a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -263,6 +263,11 @@ RenderingManager::rotateObject( } MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); + if (adjust) { + float *f = ptr.getRefData().getPosition().rot; + rot.x += f[0], rot.y += f[1], rot.z += f[2]; + } + Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); Ogre::Quaternion zr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Z); diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index e4af847d54..cb54a70bd6 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -13,8 +13,6 @@ #include "../mwworld/player.hpp" -#include "../mwrender/player.hpp" - #include "sound_output.hpp" #include "sound_decoder.hpp" #include "sound.hpp" @@ -491,9 +489,17 @@ namespace MWSound if(!isMusicPlaying()) startRandomTitle(); - const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->cell; + MWWorld::Ptr player = + MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const ESM::Cell *cell = player.getCell()->cell; // Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); - Ogre::Vector3 nPos, nDir, nUp; + Ogre::Vector3 pos, at, up = Ogre::Vector3::UNIT_Z; + + float *fval = player.getRefData().getPosition().pos; + pos.x = fval[0], pos.y = fval[1], pos.z = fval[2]; + + fval = player.getRefData().getPosition().rot; + at.x = fval[0], at.y = fval[1], at.z = fval[2]; /* nPos = cam->getRealPosition(); nDir = cam->getRealDirection(); @@ -501,15 +507,17 @@ namespace MWSound */ Environment env = Env_Normal; - if((cell->data.flags&cell->HasWater) && nPos.y < cell->water) + if((cell->data.flags&cell->HasWater) && pos.z < cell->water) env = Env_Underwater; // The output handler is expecting vectors oriented like the game // (that is, -Z goes down, +Y goes forward), but that's not what we // get from Ogre's camera, so we have to convert. + /* const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]); const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]); const Ogre::Vector3 up(nUp[0], -nUp[2], nUp[1]); + */ mOutput->updateListener(pos, at, up, env); From 6b996d8c34f9ea0bcaee8c8fd08a6bca51bf5516 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 13:29:13 +0400 Subject: [PATCH 021/143] fix disabled camera on start --- apps/openmw/mwinput/inputmanager.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 4281612f0a..35aafa4467 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -295,6 +295,14 @@ private: lst->add(guiEvents,Event::EV_ALL); } + mControlSwitch["playercontrols"] = true; + mControlSwitch["playerfighting"] = true; + mControlSwitch["playerjumping"] = true; + mControlSwitch["playerlooking"] = true; + mControlSwitch["playermagic"] = true; + mControlSwitch["playerviewswitch"] = true; + mControlSwitch["vanitymode"] = true; + changeInputMode(false); /********************************** @@ -338,14 +346,6 @@ private: poller.bind(A_Jump, KC_E); poller.bind(A_Crouch, KC_LCONTROL); - - mControlSwitch["playercontrols"] = true; - mControlSwitch["playerfighting"] = true; - mControlSwitch["playerjumping"] = true; - mControlSwitch["playerlooking"] = true; - mControlSwitch["playermagic"] = true; - mControlSwitch["playerviewswitch"] = true; - mControlSwitch["vanitymode"] = true; } void setDragDrop(bool dragDrop) From 181d45661f904b198ff8d206c9571eb5c3c6b444 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 13:43:52 +0400 Subject: [PATCH 022/143] fix rotation angles --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index be8b518b9a..9e48cc0ebe 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -269,8 +269,8 @@ RenderingManager::rotateObject( } Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion yr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Degree(rot.z), Ogre::Vector3::UNIT_Z); ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); From a84145a0872ec4b15c4ff08c6ba8432525e24f34 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 12:05:47 +0200 Subject: [PATCH 023/143] Issue #107: minor corrections --- apps/openmw/mwbase/dialoguemanager.hpp | 5 +++-- apps/openmw/mwbase/scriptmanager.hpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index ca339e81e4..ccffc6b214 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWBASE_DIALOGUEMANAGERIMP_H -#define GAME_MWBASE_DIALOGUEMANAGERIMP_H +#ifndef GAME_MWBASE_DIALOGUEMANAGER_H +#define GAME_MWBASE_DIALOGUEMANAGER_H #include @@ -10,6 +10,7 @@ namespace MWWorld namespace MWBase { + /// \brief Interface for dialogue manager (implemented in MWDialogue) class DialogueManager { DialogueManager (const DialogueManager&); diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index a8db868c3e..ae146e0646 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWBASE_SCRIPTMANAGERIMP_H -#define GAME_MWBASE_SCRIPTMANAGERIMP_H +#ifndef GAME_MWBASE_SCRIPTMANAGER_H +#define GAME_MWBASE_SCRIPTMANAGER_H #include From d00d40cc3f567d008f980c83aa447718f08ed8d9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 12:56:03 +0200 Subject: [PATCH 024/143] Issue #107: Journal is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +- apps/openmw/mwbase/environment.hpp | 12 +-- apps/openmw/mwbase/journal.hpp | 76 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 +- .../{journal.cpp => journalimp.cpp} | 2 +- .../{journal.hpp => journalimp.hpp} | 37 +++------ apps/openmw/mwgui/journalwindow.cpp | 5 +- apps/openmw/mwscript/dialogueextensions.cpp | 3 +- 10 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 apps/openmw/mwbase/journal.hpp rename apps/openmw/mwdialogue/{journal.cpp => journalimp.cpp} (98%) rename apps/openmw/mwdialogue/{journal.hpp => journalimp.hpp} (52%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b9da636cf7..1dffc426de 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -32,7 +32,7 @@ add_openmw_dir (mwgui ) add_openmw_dir (mwdialogue - dialoguemanagerimp journal journalentry quest topic + dialoguemanagerimp journalimp journalentry quest topic ) add_openmw_dir (mwscript @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world scriptmanager dialoguemanager + environment world scriptmanager dialoguemanager journal ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d97d40580d..5659b1c372 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -30,7 +30,7 @@ #include "mwclass/classes.hpp" #include "mwdialogue/dialoguemanagerimp.hpp" -#include "mwdialogue/journal.hpp" +#include "mwdialogue/journalimp.hpp" #include "mwmechanics/mechanicsmanager.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index c0fc56f97a..25488af6c7 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -7,13 +7,12 @@ #include "../mwsound/soundmanager.hpp" -#include "../mwdialogue/journal.hpp" - #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" +#include "journal.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -61,7 +60,7 @@ void MWBase::Environment::setDialogueManager (DialogueManager *dialogueManager) mDialogueManager = dialogueManager; } -void MWBase::Environment::setJournal (MWDialogue::Journal *journal) +void MWBase::Environment::setJournal (Journal *journal) { mJournal = journal; } @@ -112,7 +111,7 @@ MWBase::DialogueManager *MWBase::Environment::getDialogueManager() const return mDialogueManager; } -MWDialogue::Journal *MWBase::Environment::getJournal() const +MWBase::Journal *MWBase::Environment::getJournal() const { assert (mJournal); return mJournal; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 72d92f00fa..160a63b0bb 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -16,11 +16,6 @@ namespace MWMechanics class MechanicsManager; } -namespace MWDialogue -{ - class Journal; -} - namespace MWInput { struct MWInputManager; @@ -31,6 +26,7 @@ namespace MWBase class World; class ScriptManager; class DialogueManager; + class Journal; /// \brief Central hub for mw-subsystems /// @@ -48,7 +44,7 @@ namespace MWBase MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; - MWDialogue::Journal *mJournal; + Journal *mJournal; MWInput::MWInputManager *mInputManager; float mFrameDuration; @@ -76,7 +72,7 @@ namespace MWBase void setDialogueManager (DialogueManager *dialogueManager); - void setJournal (MWDialogue::Journal *journal); + void setJournal (Journal *journal); void setInputManager (MWInput::MWInputManager *inputManager); @@ -95,7 +91,7 @@ namespace MWBase DialogueManager *getDialogueManager() const; - MWDialogue::Journal *getJournal() const; + Journal *getJournal() const; MWInput::MWInputManager *getInputManager() const; diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp new file mode 100644 index 0000000000..99f6996cfb --- /dev/null +++ b/apps/openmw/mwbase/journal.hpp @@ -0,0 +1,76 @@ +#ifndef GAME_MWBASE_JOURNAL_H +#define GAME_MWBASE_JOURNAL_H + +#include +#include +#include + +namespace MWDialogue +{ + class Quest; + class Topic; + struct StampedJournalEntry; +} + +namespace MWBase +{ + /// \brief Interface for the player's journal (implemented in MWDialogue) + class Journal + { + Journal (const Journal&); + ///< not implemented + + Journal& operator= (const Journal&); + ///< not implemented + + public: + + typedef std::deque TEntryContainer; + typedef TEntryContainer::const_iterator TEntryIter; + typedef std::map TQuestContainer; // topc, quest + typedef TQuestContainer::const_iterator TQuestIter; + typedef std::map TTopicContainer; // topic-id, topic-content + typedef TTopicContainer::const_iterator TTopicIter; + + public: + + Journal() {} + + virtual ~Journal() {} + + virtual void addEntry (const std::string& id, int index) = 0; + ///< Add a journal entry. + + virtual void setJournalIndex (const std::string& id, int index) = 0; + ///< Set the journal index without adding an entry. + + virtual int getJournalIndex (const std::string& id) const = 0; + ///< Get the journal index. + + virtual void addTopic (const std::string& topicId, const std::string& infoId) = 0; + + virtual TEntryIter begin() const = 0; + ///< Iterator pointing to the begin of the main journal. + /// + /// \note Iterators to main journal entries will never become invalid. + + virtual TEntryIter end() const = 0; + ///< Iterator pointing past the end of the main journal. + + virtual TQuestIter questBegin() const = 0; + ///< Iterator pointing to the first quest (sorted by topic ID) + + virtual TQuestIter questEnd() const = 0; + ///< Iterator pointing past the last quest. + + virtual TTopicIter topicBegin() const = 0; + ///< Iterator pointing to the first topic (sorted by topic ID) + /// + /// \note The topic ID is identical with the user-visible topic string. + + virtual TTopicIter topicEnd() const = 0; + ///< Iterator pointing past the last topic. + }; +} + +#endif diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 129e78d0fb..644da49e27 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -12,6 +12,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/journal.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -22,8 +23,6 @@ #include "../mwgui/dialogue.hpp" #include "../mwgui/window_manager.hpp" -#include "journal.hpp" - #include #include "../mwscript/extensions.hpp" diff --git a/apps/openmw/mwdialogue/journal.cpp b/apps/openmw/mwdialogue/journalimp.cpp similarity index 98% rename from apps/openmw/mwdialogue/journal.cpp rename to apps/openmw/mwdialogue/journalimp.cpp index ad10be3aa8..052b921943 100644 --- a/apps/openmw/mwdialogue/journal.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -1,5 +1,5 @@ -#include "journal.hpp" +#include "journalimp.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwdialogue/journal.hpp b/apps/openmw/mwdialogue/journalimp.hpp similarity index 52% rename from apps/openmw/mwdialogue/journal.hpp rename to apps/openmw/mwdialogue/journalimp.hpp index 62b9f4bed9..5fafc265ac 100644 --- a/apps/openmw/mwdialogue/journal.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -1,9 +1,7 @@ #ifndef GAME_MMDIALOG_JOURNAL_H #define GAME_MWDIALOG_JOURNAL_H -#include -#include -#include +#include "../mwbase/journal.hpp" #include "journalentry.hpp" #include "quest.hpp" @@ -11,19 +9,8 @@ namespace MWDialogue { /// \brief The player's journal - class Journal + class Journal : public MWBase::Journal { - public: - - typedef std::deque TEntryContainer; - typedef TEntryContainer::const_iterator TEntryIter; - typedef std::map TQuestContainer; // topc, quest - typedef TQuestContainer::const_iterator TQuestIter; - typedef std::map TTopicContainer; // topic-id, topic-content - typedef TTopicContainer::const_iterator TTopicIter; - - private: - TEntryContainer mJournal; TQuestContainer mQuests; TTopicContainer mTopics; @@ -34,37 +21,37 @@ namespace MWDialogue Journal(); - void addEntry (const std::string& id, int index); + virtual void addEntry (const std::string& id, int index); ///< Add a journal entry. - void setJournalIndex (const std::string& id, int index); + virtual void setJournalIndex (const std::string& id, int index); ///< Set the journal index without adding an entry. - int getJournalIndex (const std::string& id) const; + virtual int getJournalIndex (const std::string& id) const; ///< Get the journal index. - void addTopic (const std::string& topicId, const std::string& infoId); + virtual void addTopic (const std::string& topicId, const std::string& infoId); - TEntryIter begin() const; + virtual TEntryIter begin() const; ///< Iterator pointing to the begin of the main journal. /// /// \note Iterators to main journal entries will never become invalid. - TEntryIter end() const; + virtual TEntryIter end() const; ///< Iterator pointing past the end of the main journal. - TQuestIter questBegin() const; + virtual TQuestIter questBegin() const; ///< Iterator pointing to the first quest (sorted by topic ID) - TQuestIter questEnd() const; + virtual TQuestIter questEnd() const; ///< Iterator pointing past the last quest. - TTopicIter topicBegin() const; + virtual TTopicIter topicBegin() const; ///< Iterator pointing to the first topic (sorted by topic ID) /// /// \note The topic ID is identical with the user-visible topic string. - TTopicIter topicEnd() const; + virtual TTopicIter topicEnd() const; ///< Iterator pointing past the last topic. }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 8b50c9ef1d..220cd96e73 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -2,11 +2,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwdialogue/journal.hpp" +#include "../mwbase/journal.hpp" #include "../mwsound/soundmanager.hpp" +#include "../mwdialogue/journalentry.hpp" + #include "window_manager.hpp" namespace diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 4eb1293311..21c21acf00 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -9,8 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" - -#include "../mwdialogue/journal.hpp" +#include "../mwbase/journal.hpp" #include "interpretercontext.hpp" #include "ref.hpp" From 6bd48d12af439c621bbbfb38d5f8eef8aa9d5bb5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 14:33:21 +0200 Subject: [PATCH 025/143] Issue #107: SoundManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +- apps/openmw/mwbase/environment.hpp | 12 +- apps/openmw/mwbase/soundmanager.hpp | 122 ++++++++++++++++++ apps/openmw/mwclass/apparatus.cpp | 7 +- apps/openmw/mwclass/armor.cpp | 7 +- apps/openmw/mwclass/book.cpp | 5 +- apps/openmw/mwclass/clothing.cpp | 7 +- apps/openmw/mwclass/container.cpp | 5 +- apps/openmw/mwclass/door.cpp | 3 +- apps/openmw/mwclass/ingredient.cpp | 7 +- apps/openmw/mwclass/light.cpp | 7 +- apps/openmw/mwclass/lockpick.cpp | 7 +- apps/openmw/mwclass/misc.cpp | 9 +- apps/openmw/mwclass/potion.cpp | 5 +- apps/openmw/mwclass/probe.cpp | 8 +- apps/openmw/mwclass/repair.cpp | 7 +- apps/openmw/mwclass/weapon.cpp | 7 +- apps/openmw/mwgui/alchemywindow.cpp | 3 +- apps/openmw/mwgui/bookwindow.cpp | 6 +- apps/openmw/mwgui/charactercreation.cpp | 2 +- apps/openmw/mwgui/container.cpp | 3 +- apps/openmw/mwgui/hud.cpp | 3 +- apps/openmw/mwgui/inventorywindow.cpp | 5 +- apps/openmw/mwgui/journalwindow.cpp | 3 +- apps/openmw/mwgui/scrollwindow.cpp | 6 +- apps/openmw/mwgui/settingswindow.cpp | 3 +- apps/openmw/mwgui/spellwindow.cpp | 3 +- apps/openmw/mwgui/tradewindow.cpp | 2 +- apps/openmw/mwscript/soundextensions.cpp | 7 +- apps/openmw/mwsound/openal_output.cpp | 26 ++-- apps/openmw/mwsound/openal_output.hpp | 6 +- apps/openmw/mwsound/sound.hpp | 4 +- apps/openmw/mwsound/sound_output.hpp | 8 +- .../{soundmanager.cpp => soundmanagerimp.cpp} | 14 +- .../{soundmanager.hpp => soundmanagerimp.hpp} | 61 ++++----- apps/openmw/mwworld/action.cpp | 5 +- apps/openmw/mwworld/scene.cpp | 5 +- apps/openmw/mwworld/weather.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 3 +- 41 files changed, 249 insertions(+), 170 deletions(-) create mode 100644 apps/openmw/mwbase/soundmanager.hpp rename apps/openmw/mwsound/{soundmanager.cpp => soundmanagerimp.cpp} (97%) rename apps/openmw/mwsound/{soundmanager.hpp => soundmanagerimp.hpp} (58%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 1dffc426de..b35b2fbad4 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -43,7 +43,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwsound - soundmanager openal_output audiere_decoder mpgsnd_decoder ffmpeg_decoder + soundmanagerimp openal_output audiere_decoder mpgsnd_decoder ffmpeg_decoder ) add_openmw_dir (mwworld @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world scriptmanager dialoguemanager journal + environment world scriptmanager dialoguemanager journal soundmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5659b1c372..2467f91d13 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -21,7 +21,7 @@ #include "mwscript/scriptmanagerimp.hpp" #include "mwscript/extensions.hpp" -#include "mwsound/soundmanager.hpp" +#include "mwsound/soundmanagerimp.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 25488af6c7..7d109b000c 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -5,14 +5,13 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" +#include "soundmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -35,7 +34,7 @@ void MWBase::Environment::setWorld (World *world) mWorld = world; } -void MWBase::Environment::setSoundManager (MWSound::SoundManager *soundManager) +void MWBase::Environment::setSoundManager (SoundManager *soundManager) { mSoundManager = soundManager; } @@ -81,7 +80,7 @@ MWBase::World *MWBase::Environment::getWorld() const return mWorld; } -MWSound::SoundManager *MWBase::Environment::getSoundManager() const +MWBase::SoundManager *MWBase::Environment::getSoundManager() const { assert (mSoundManager); return mSoundManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 160a63b0bb..d03267c25f 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,11 +1,6 @@ #ifndef GAME_BASE_INVIRONMENT_H #define GAME_BASE_INVIRONMENT_H -namespace MWSound -{ - class SoundManager; -} - namespace MWGui { class WindowManager; @@ -27,6 +22,7 @@ namespace MWBase class ScriptManager; class DialogueManager; class Journal; + class SoundManager; /// \brief Central hub for mw-subsystems /// @@ -39,7 +35,7 @@ namespace MWBase static Environment *sThis; World *mWorld; - MWSound::SoundManager *mSoundManager; + SoundManager *mSoundManager; ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; @@ -62,7 +58,7 @@ namespace MWBase void setWorld (World *world); - void setSoundManager (MWSound::SoundManager *soundManager); + void setSoundManager (SoundManager *soundManager); void setScriptManager (MWBase::ScriptManager *scriptManager); @@ -81,7 +77,7 @@ namespace MWBase World *getWorld() const; - MWSound::SoundManager *getSoundManager() const; + SoundManager *getSoundManager() const; MWBase::ScriptManager *getScriptManager() const; diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp new file mode 100644 index 0000000000..49171c70c2 --- /dev/null +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -0,0 +1,122 @@ +#ifndef GAME_MWBASE_SOUNDMANAGER_H +#define GAME_MWBASE_SOUNDMANAGER_H + +#include + +#include + +#include + +#include "../mwworld/ptr.hpp" + +namespace MWWorld +{ + class CellStore; +} + +namespace MWSound +{ + class Sound; +} + +namespace MWBase +{ + typedef boost::shared_ptr SoundPtr; + + /// \brief Interface for sound manager (implemented in MWSound) + class SoundManager + { + public: + + enum PlayMode { + Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ + Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ + Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ + Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position + * but do not keep it updated (the sound will not move with + * the object and will not stop when the object is deleted. */ + }; + + private: + + SoundManager (const SoundManager&); + ///< not implemented + + SoundManager& operator= (const SoundManager&); + ///< not implemented + + public: + + SoundManager() {} + + virtual ~SoundManager() {} + + virtual void processChangedSettings(const Settings::CategorySettingVector& settings) = 0; + + virtual void stopMusic() = 0; + ///< Stops music if it's playing + + virtual void streamMusic(const std::string& filename) = 0; + ///< Play a soundifle + /// \param filename name of a sound file in "Music/" in the data directory. + + virtual void startRandomTitle() = 0; + ///< Starts a random track from the current playlist + + virtual bool isMusicPlaying() = 0; + ///< Returns true if music is playing + + virtual void playPlaylist(const std::string &playlist) = 0; + ///< Start playing music from the selected folder + /// \param name of the folder that contains the playlist + + virtual void say(MWWorld::Ptr reference, const std::string& filename) = 0; + ///< Make an actor say some text. + /// \param filename name of a sound file in "Sound/" in the data directory. + + virtual void say(const std::string& filename) = 0; + ///< Say some text, without an actor ref + /// \param filename name of a sound file in "Sound/" in the data directory. + + virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const = 0; + ///< Is actor not speaking? + + virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; + ///< Stop an actor speaking + + virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, + int mode=Play_Normal) = 0; + ///< Play a sound, independently of 3D-position + + virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, + float volume, float pitch, int mode=Play_Normal) = 0; + ///< Play a sound from an object + + virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0; + ///< Stop the given object from playing the given sound, + + virtual void stopSound3D(MWWorld::Ptr reference) = 0; + ///< Stop the given object from playing all sounds. + + virtual void stopSound(const MWWorld::CellStore *cell) = 0; + ///< Stop all sounds for the given cell. + + virtual void stopSound(const std::string& soundId) = 0; + ///< Stop a non-3d looping sound + + virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; + ///< Is the given sound currently playing on the given object? + + virtual void updateObject(MWWorld::Ptr reference) = 0; + ///< Update the position of all sounds connected to the given object. + + virtual void update(float duration) = 0; + }; + + inline int operator|(SoundManager::PlayMode a, SoundManager::PlayMode b) + { return static_cast (a) | static_cast (b); } + inline int operator&(SoundManager::PlayMode a, SoundManager::PlayMode b) + { return static_cast (a) & static_cast (b); } +} + +#endif diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 9814b140c1..3c22cc5fb2 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -18,8 +19,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -39,7 +38,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -64,7 +63,7 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 4624b94d9c..43c1e0a436 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -21,8 +22,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -42,7 +41,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Armor::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -67,7 +66,7 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index d8166347ef..29a53140a8 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" @@ -17,8 +18,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,7 +37,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Book::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index f55d6ed881..ef91b0fac4 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Clothing::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index c6d22b3a5a..7d31b945d5 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" @@ -20,8 +21,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace { struct CustomData : public MWWorld::CustomData @@ -69,7 +68,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Container::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 6939c356e8..19eda16ef1 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index d8c8b4b3b6..eb92accb61 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -17,8 +18,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,7 +37,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -63,7 +62,7 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index f09d3ce36c..ed8fd1de60 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -17,8 +18,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" @@ -58,7 +57,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, "meshes\\" + model); } if (!ref->base->sound.empty()) { - MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop); + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->base->sound, 1.0, 1.0, MWBase::SoundManager::Play_Loop); } } @@ -95,7 +94,7 @@ namespace MWClass if (!(ref->base->data.flags & ESM::Light::Carry)) return boost::shared_ptr (new MWWorld::NullAction); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 20441b520c..1472b7e8bd 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index cb6d40c434..8b5b414955 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -20,8 +21,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - #include namespace MWClass @@ -43,7 +42,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -68,7 +67,7 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); @@ -189,7 +188,7 @@ namespace MWClass Miscellaneous::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::Ptr newPtr; - + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index d3f2912ea2..27903fdf73 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 4500162092..8a563865d8 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Probe::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -64,7 +63,7 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); @@ -174,4 +173,3 @@ namespace MWClass return MWWorld::Ptr(&cell.probes.insert(*ref), &cell); } } - diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 829fe311a7..096d3d0eee 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -17,8 +18,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,7 +37,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Repair::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -63,7 +62,7 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index b459531305..bc8e3e648f 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Weapon::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 9d691d371d..81e5640d44 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -4,13 +4,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwsound/soundmanager.hpp" - #include "window_manager.hpp" namespace diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 1e0301d8ea..19a5e93985 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -4,8 +4,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" + #include "../mwinput/inputmanager.hpp" -#include "../mwsound/soundmanager.hpp" + #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" @@ -98,7 +100,7 @@ void BookWindow::onCloseButtonClicked (MyGUI::Widget* _sender) void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) { - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); MWWorld::ActionTake take(mBook); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index c39e305dc9..c0081544b0 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -9,7 +9,7 @@ #include "mode.hpp" #include "../mwbase/environment.hpp" -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" namespace { diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 44cb12d2d4..66dea08497 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" @@ -20,8 +21,6 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index d2eef26ac7..1c79433869 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -8,12 +8,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwgui/widgets.hpp" #include "inventorywindow.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 926c351722..090b7dd6e6 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -9,6 +9,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -17,10 +18,6 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwsound/soundmanager.hpp" - -#include "../mwclass/container.hpp" - #include "window_manager.hpp" #include "widgets.hpp" #include "bookwindow.hpp" diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 220cd96e73..475a70631e 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" - -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwdialogue/journalentry.hpp" diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 00e5a01dc1..67a02e53b4 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -2,10 +2,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" + #include "../mwinput/inputmanager.hpp" + #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" -#include "../mwsound/soundmanager.hpp" #include "formatting.hpp" #include "window_manager.hpp" @@ -62,7 +64,7 @@ void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) { - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); MWWorld::ActionTake take(mScroll); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 4488096fe9..9629c7cca6 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -12,11 +12,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwinput/inputmanager.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 8f81a1761d..b528eecc22 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -16,8 +17,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" -#include "../mwsound/soundmanager.hpp" - #include "window_manager.hpp" #include "inventorywindow.hpp" #include "confirmationdialog.hpp" diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index d9f9692ffa..75e39fd879 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -4,10 +4,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" -#include "../mwsound/soundmanager.hpp" #include "window_manager.hpp" #include "inventorywindow.hpp" diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 3e05d72d3c..6dc8f39197 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -9,8 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -116,7 +115,7 @@ namespace MWScript std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWSound::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : 0); } }; @@ -142,7 +141,7 @@ namespace MWScript Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWSound::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : 0); } }; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index ac4baa8b29..e5169d878b 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -8,7 +8,7 @@ #include "openal_output.hpp" #include "sound_decoder.hpp" #include "sound.hpp" -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 @@ -263,7 +263,7 @@ void OpenAL_SoundStream::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; - if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -400,7 +400,7 @@ void OpenAL_Sound::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; - if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -420,7 +420,7 @@ void OpenAL_Sound3D::update() ALfloat pitch = mPitch; if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance) gain = 0.0f; - else if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + else if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -642,7 +642,7 @@ void OpenAL_Output::bufferFinished(ALuint buf) } -SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -674,7 +674,7 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float alSourcef(src, AL_MAX_DISTANCE, 1000.0f); alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater) + if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) { volume *= 0.9f; pitch *= 0.7f; @@ -683,7 +683,7 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float alSourcef(src, AL_PITCH, pitch); alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE); + alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); throwALerror(); alSourcei(src, AL_BUFFER, buf); @@ -693,7 +693,7 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float return sound; } -SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, +MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, float min, float max, int flags) { boost::shared_ptr sound; @@ -726,7 +726,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector alSourcef(src, AL_MAX_DISTANCE, max); alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f); - if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater) + if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) { volume *= 0.9f; pitch *= 0.7f; @@ -736,7 +736,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector alSourcef(src, AL_PITCH, pitch); alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE); + alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); throwALerror(); alSourcei(src, AL_BUFFER, buf); @@ -747,7 +747,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector } -SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags) { boost::shared_ptr sound; ALuint src; @@ -759,7 +759,7 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa try { - if((flags&Play_Loop)) + if((flags&MWBase::SoundManager::Play_Loop)) std::cout <<"Warning: cannot loop stream "<open(fname); @@ -779,7 +779,7 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa alSourcef(src, AL_MAX_DISTANCE, 1000.0f); alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater) + if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) { volume *= 0.9f; pitch *= 0.7f; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index d62d20286a..90917c53c7 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -42,10 +42,10 @@ namespace MWSound virtual void init(const std::string &devname=""); virtual void deinit(); - virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); - virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, + virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); + virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, float min, float max, int flags); - virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags); + virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 0cba6abca4..729147f75a 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -3,7 +3,7 @@ #include -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" namespace MWSound { @@ -35,7 +35,7 @@ namespace MWSound , mPitch(1.0f) , mMinDistance(20.0f) /* 1 * min_range_scale */ , mMaxDistance(12750.0f) /* 255 * max_range_scale */ - , mFlags(Play_Normal) + , mFlags(MWBase::SoundManager::Play_Normal) { } virtual ~Sound() { } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 100b2208c4..2680ec1dbe 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -6,7 +6,7 @@ #include -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" #include "../mwworld/ptr.hpp" @@ -24,10 +24,10 @@ namespace MWSound virtual void init(const std::string &devname="") = 0; virtual void deinit() = 0; - virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; - virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, + virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; + virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, float min, float max, int flags) = 0; - virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0; + virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp similarity index 97% rename from apps/openmw/mwsound/soundmanager.cpp rename to apps/openmw/mwsound/soundmanagerimp.cpp index c6332d9f32..8a351590dd 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" #include #include @@ -222,7 +222,7 @@ namespace MWSound const ESM::Position &pos = ptr.getCellRef().pos; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, + MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, 20.0f, 12750.0f, Play_Normal); sound->mPos = objpos; sound->mBaseVolume = basevol; @@ -244,7 +244,7 @@ namespace MWSound float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); + MWBase::SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); sound->mBaseVolume = basevol; mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); @@ -277,9 +277,9 @@ namespace MWSound - SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) + MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) { - SoundPtr sound; + MWBase::SoundPtr sound; if(!mOutput->isInitialized()) return sound; try @@ -305,10 +305,10 @@ namespace MWSound return sound; } - SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, + MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, float volume, float pitch, int mode) { - SoundPtr sound; + MWBase::SoundPtr sound; if(!mOutput->isInitialized()) return sound; try diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp similarity index 58% rename from apps/openmw/mwsound/soundmanager.hpp rename to apps/openmw/mwsound/soundmanagerimp.hpp index 83195390c0..78a4990a84 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -11,8 +11,9 @@ #include -#include "../mwworld/ptr.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwworld/ptr.hpp" namespace Ogre { @@ -27,27 +28,13 @@ namespace MWSound class Sound; typedef boost::shared_ptr DecoderPtr; - typedef boost::shared_ptr SoundPtr; - - enum PlayMode { - Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ - Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ - Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position - * but do not keep it updated (the sound will not move with - * the object and will not stop when the object is deleted. */ - }; - static inline int operator|(const PlayMode &a, const PlayMode &b) - { return (int)a | (int)b; } - static inline int operator&(const PlayMode &a, const PlayMode &b) - { return (int)a & (int)b; } enum Environment { Env_Normal, Env_Underwater, }; - class SoundManager + class SoundManager : public MWBase::SoundManager { Ogre::ResourceGroupManager& mResourceMgr; @@ -65,7 +52,7 @@ namespace MWSound std::string mCurrentPlaylist; typedef std::pair PtrIDPair; - typedef std::map SoundMap; + typedef std::map SoundMap; SoundMap mActiveSounds; std::string lookup(const std::string &soundId, @@ -84,67 +71,67 @@ namespace MWSound public: SoundManager(bool useSound); - ~SoundManager(); + virtual ~SoundManager(); - void processChangedSettings(const Settings::CategorySettingVector& settings); + virtual void processChangedSettings(const Settings::CategorySettingVector& settings); - void stopMusic(); + virtual void stopMusic(); ///< Stops music if it's playing - void streamMusic(const std::string& filename); + virtual void streamMusic(const std::string& filename); ///< Play a soundifle /// \param filename name of a sound file in "Music/" in the data directory. - void startRandomTitle(); + virtual void startRandomTitle(); ///< Starts a random track from the current playlist - bool isMusicPlaying(); + virtual bool isMusicPlaying(); ///< Returns true if music is playing - void playPlaylist(const std::string &playlist); + virtual void playPlaylist(const std::string &playlist); ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - void say(MWWorld::Ptr reference, const std::string& filename); + virtual void say(MWWorld::Ptr reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. - void say(const std::string& filename); + virtual void say(const std::string& filename); ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const; + virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const; ///< Is actor not speaking? - void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); + virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); ///< Stop an actor speaking - SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); ///< Play a sound, independently of 3D-position - SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, + virtual MWBase::SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, float volume, float pitch, int mode=Play_Normal); ///< Play a sound from an object - void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); + virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); ///< Stop the given object from playing the given sound, - void stopSound3D(MWWorld::Ptr reference); + virtual void stopSound3D(MWWorld::Ptr reference); ///< Stop the given object from playing all sounds. - void stopSound(const MWWorld::CellStore *cell); + virtual void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. - void stopSound(const std::string& soundId); + virtual void stopSound(const std::string& soundId); ///< Stop a non-3d looping sound - bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; + virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - void updateObject(MWWorld::Ptr reference); + virtual void updateObject(MWWorld::Ptr reference); ///< Update the position of all sounds connected to the given object. - void update(float duration); + virtual void update(float duration); }; } diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 0a57d5f675..c142294bb4 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -2,8 +2,7 @@ #include "action.hpp" #include "../mwbase/environment.hpp" - -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" MWWorld::Action::Action() {} @@ -13,7 +12,7 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, - MWSound::Play_NoTrack); + MWBase::SoundManager::Play_NoTrack); executeImp (actor); } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c768fce26d..6d4996e783 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -2,11 +2,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME +#include "../mwbase/soundmanager.hpp" #include "../mwmechanics/mechanicsmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwgui/window_manager.hpp" #include "player.hpp" @@ -345,7 +344,7 @@ namespace MWWorld mRendering.addObject(ptr); MWWorld::Class::get(ptr).insertObject(ptr, *mPhysics); } - + void Scene::removeObjectFromScene (const Ptr& ptr) { MWBase::Environment::get().getMechanicsManager()->removeActor (ptr); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b5f2e3a575..0adf87dae9 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -9,11 +9,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "player.hpp" using namespace Ogre; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8fb7b1ba7d..be9c00aeef 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -4,14 +4,13 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" #include "../mwmechanics/mechanicsmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwgui/window_manager.hpp" #include "player.hpp" From b5bc7bc424b7ed57ffbe0f1383e56da360c0460d Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 17:01:03 +0400 Subject: [PATCH 026/143] SoundManager dependency on camera pos/dir --- apps/openmw/mwrender/player.cpp | 18 +++++++++++++ apps/openmw/mwrender/player.hpp | 1 + apps/openmw/mwsound/soundmanager.cpp | 39 ++++++++++------------------ apps/openmw/mwsound/soundmanager.hpp | 13 +++++----- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 0d101a8393..d8a9d4b373 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -3,9 +3,13 @@ #include #include +#include "../mwbase/environment.hpp" + #include "../mwworld/ptr.hpp" #include "../mwworld/refdata.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) @@ -33,6 +37,7 @@ namespace MWRender yawNode->setOrientation(yr); controlFlip(); + updateListener(); return !mVanityModeEnabled; } @@ -56,6 +61,7 @@ namespace MWRender yawNode->yaw(Ogre::Degree(-rot.z)); controlFlip(); + updateListener(); return !mVanityModeEnabled; } @@ -81,4 +87,16 @@ namespace MWRender } } } + + void Player::updateListener() + { + Ogre::Vector3 pos = mCamera->getRealPosition(); + Ogre::Vector3 dir = mCamera->getRealDirection(); + + Ogre::Real xch; + xch = pos.y, pos.y = -pos.z, pos.z = xch; + xch = dir.y, dir.y = -dir.z, dir.z = xch; + + MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir); + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 35333c9f10..b1160435da 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -28,6 +28,7 @@ namespace MWRender bool mVanityModeEnabled; void controlFlip(); + void updateListener(); public: diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index cb54a70bd6..17ee8c6829 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include #include "../mwbase/environment.hpp" @@ -492,34 +490,17 @@ namespace MWSound MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); const ESM::Cell *cell = player.getCell()->cell; -// Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); - Ogre::Vector3 pos, at, up = Ogre::Vector3::UNIT_Z; - - float *fval = player.getRefData().getPosition().pos; - pos.x = fval[0], pos.y = fval[1], pos.z = fval[2]; - - fval = player.getRefData().getPosition().rot; - at.x = fval[0], at.y = fval[1], at.z = fval[2]; - /* - nPos = cam->getRealPosition(); - nDir = cam->getRealDirection(); - nUp = cam->getRealUp(); - */ Environment env = Env_Normal; - if((cell->data.flags&cell->HasWater) && pos.z < cell->water) + if((cell->data.flags&cell->HasWater) && mListenerPos.z < cell->water) env = Env_Underwater; - // The output handler is expecting vectors oriented like the game - // (that is, -Z goes down, +Y goes forward), but that's not what we - // get from Ogre's camera, so we have to convert. - /* - const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]); - const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]); - const Ogre::Vector3 up(nUp[0], -nUp[2], nUp[1]); - */ - - mOutput->updateListener(pos, at, up, env); + mOutput->updateListener( + mListenerPos, + mListenerDir, + Ogre::Vector3::UNIT_Z, + env + ); // Check if any sounds are finished playing, and trash them SoundMap::iterator snditer = mActiveSounds.begin(); @@ -577,6 +558,12 @@ namespace MWSound } } + void SoundManager::setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir) + { + mListenerPos = pos; + mListenerDir = dir; + } + // Default readAll implementation, for decoders that can't do anything // better void Sound_Decoder::readAll(std::vector &output) diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 83195390c0..acf2ddeb64 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -7,19 +7,13 @@ #include +#include #include #include #include "../mwworld/ptr.hpp" - -namespace Ogre -{ - class Root; - class Camera; -} - namespace MWSound { class Sound_Output; @@ -68,6 +62,9 @@ namespace MWSound typedef std::map SoundMap; SoundMap mActiveSounds; + Ogre::Vector3 mListenerPos; + Ogre::Vector3 mListenerDir; + std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void streamMusicFull(const std::string& filename); @@ -145,6 +142,8 @@ namespace MWSound ///< Update the position of all sounds connected to the given object. void update(float duration); + + void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir); }; } From 3668b4aabb15f11f75c1adce817a7615b1f51156 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Fri, 10 Aug 2012 05:47:37 +0200 Subject: [PATCH 027/143] Rewrote the credits.txt, added missing people and changed formatting --- credits.txt | 124 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 40 deletions(-) diff --git a/credits.txt b/credits.txt index 5b8ede1178..af54e69bdf 100644 --- a/credits.txt +++ b/credits.txt @@ -1,60 +1,104 @@ -CREDITS +Contributors + +The OpenMW project was started in 2008 by Nicolay Korslund. +In the course of years many people have contributed to the project. + +If you feel your name is missing from this list, +Feel free to modify it directly in our GitHub Repository, +or ask a developer to do it for you. + + +Programmers: +Marc Zinnschlag (Zini) - Lead Programmer/Project Manager -Current Developers: Aleksandar Jovanov -Alexander “Ace” Olofsson -Artem “greye” Kotsynyak +Alexander Olofsson (Ace) +Artem Kotsynyak (greye) athile BrotherBrick -Cris “Mirceam” Mihalache +Cris Mihalache (Mirceam) +Eli2 gugus / gus -Jacob “Yacoby” Essex -Jannik “scrawl” Heller -Jason “jhooks” Hooks -Karl-Felix “k1ll” Glatzer -Lukasz “lgro” Gromanowski -Marc “Zini” Zinnschlag +Jacob Essex (Yacoby) +Jannik Heller (scrawl) +Jason Hooks (jhooks) +Karl-Felix Glatzer (k1ll) +Lukasz Gromanowski (lgro) Michael Mc Donnell -Michael “werdanith” Papageorgiou -Nikolay “corristo” Kasyanov -Pieter “pvdk” van der Kloet -Roman "Kromgart" Melnik -Sebastian “swick” Wick -Sylvain "Garvek" T. +Michael Papageorgiou (werdanith) +Nikolay Kasyanov (corristo) +Pieter van der Kloet (pvdk) +Roman Melnik (Kromgart) +Sebastian Wick (swick) +Sylvain T. (Garvek) -Retired Developers: + +Packagers: +Alexander Olofsson (Ace) - Windows +BrotherBrick - Ubuntu Linux +edmundo - Gentoo Linux +Kenny Armstrong (artorius) - Fedora Linux +Nikolay Kasyanov (corristo) - Mac OS X +Sandy Carter (bwrsandman) - Arch Linux + + +Public Relations: +ElderTroll - Release Manager +sir_herrbatka - News Writer +WeirdSexy - Podcaster + + +Website: +juanmnzsk8 - Spanish News Writer +Julien Voisin (jvoisin/ap0) - French News Writer +Kingpix - Italian News Writer +Lukasz Gromanowski (lgro) - Website Administrator +Nikolay Kasyanov (corristo) - Russian News Writer +Okulo - Dutch News Writer +penguinroad - Indonesian News Writer +Ryan Sardonic (Wry) - Wiki Editor +sir_herrbatka - Forum Admin/Polish News Writer +spyboot - German News Writer + + +Formula Research: +fragonard +Greendogo +HiPhish +modred11 +Myckel +natirips +Sadler + + +Artwork: +Necrod - OpenMW Logo +raevol - Wordpress Theme + + +Inactive Contributors: Ardekantur Armin Preiml Diggory Hardy Jan Borsodi -Jan-Peter “peppe” Nilsson +Jan-Peter Nilsson (peppe) Josua Grawitter +Lordrea Nicolay Korslund sergoz Star-Demon Yuri Krupenin -PR team and Translators: -Julien (jvoisin/ap0) Voisin -sirherrbatka -ElderTroll -spyboot -corristo -Okulo -penguinroad -Kingpix -Reverser and Research: -natirips -Sadler -fragonard -Greendogo -Myckel -modred11 -HiPhish +Additional Credits: +In this section we would like to thank people not part of OpenMW for their work. -OpenMW: -Thanks to DokterDume for kindly providing us with the Moon and Star logo used as the application icon and project logo. +Thanks to Maxim Nikolaev, +for allowing us to use his excellent Morrowind fan-art on our website and in other places. -Launcher: -Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Files tab. +Thanks to DokterDume, +for kindly providing us with the Moon and Star logo, +used as the application icon and project logo. + +Thanks to Kevin Ryan, +for creating the icon used for the Data Files tab of the OpenMW Launcher. From 815e0d67081ec9dcd5812f3414ca5d67ea2cee99 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 10 Aug 2012 08:45:51 +0200 Subject: [PATCH 028/143] modified the section about missing names; We really don't want people to randomly add themselfs --- credits.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/credits.txt b/credits.txt index af54e69bdf..a0be853926 100644 --- a/credits.txt +++ b/credits.txt @@ -4,8 +4,7 @@ The OpenMW project was started in 2008 by Nicolay Korslund. In the course of years many people have contributed to the project. If you feel your name is missing from this list, -Feel free to modify it directly in our GitHub Repository, -or ask a developer to do it for you. +please notify a developer. Programmers: From f72956b9181fc0be981f353f53fb4a82bba6d2d7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Aug 2012 15:15:48 +0200 Subject: [PATCH 029/143] - added a simple main menu (with Return, Options and Exit buttons) - removed OEngine::ExitListener (what a terrible abuse of framelisteners) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwgui/cursorreplace.cpp | 1 + apps/openmw/mwgui/mainmenu.cpp | 75 ++++++++++++++++ apps/openmw/mwgui/mainmenu.hpp | 21 +++-- apps/openmw/mwgui/settingswindow.cpp | 1 + apps/openmw/mwinput/inputmanager.cpp | 34 +++---- apps/openmw/mwworld/worldimp.cpp | 13 ++- apps/openmw/mwworld/worldimp.hpp | 6 ++ files/mygui/CMakeLists.txt | 1 + files/mygui/mainmenu.cfg | 95 ++++++++++++++++++++ files/mygui/openmw_layers.xml | 2 - files/mygui/openmw_mainmenu.layout | 13 +-- files/mygui/openmw_resources.xml | 124 ++++++++++++++++++++++++++ libs/openengine/ogre/exitlistener.hpp | 37 -------- libs/openengine/ogre/fader.hpp | 2 - 17 files changed, 350 insertions(+), 82 deletions(-) create mode 100644 apps/openmw/mwgui/mainmenu.cpp create mode 100644 files/mygui/mainmenu.cfg delete mode 100644 libs/openengine/ogre/exitlistener.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 05aa161e6c..172c6a4948 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -29,7 +29,7 @@ add_openmw_dir (mwgui dialogue_history window_base stats_window messagebox journalwindow charactercreation map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow - confirmationdialog alchemywindow referenceinterface spellwindow + confirmationdialog alchemywindow referenceinterface spellwindow mainmenu ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2467f91d13..5fc9774d1b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -116,7 +116,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) std::cerr << "Error in framelistener: " << e.what() << std::endl; } - return true; + return !MWBase::Environment::get().getWorld()->getExitNow(); } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8b809d399a..f08617d4ce 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -248,6 +248,9 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) = 0; virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; + + virtual void exitNow() = 0; ///< exit after this frame has ended + virtual bool getExitNow() = 0; ///< @return true if the application should exit }; } diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp index e66f543266..a4b6a100b0 100644 --- a/apps/openmw/mwgui/cursorreplace.cpp +++ b/apps/openmw/mwgui/cursorreplace.cpp @@ -16,4 +16,5 @@ CursorReplace::CursorReplace() OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); OEngine::Render::Atlas::createFromFile("atlas1.cfg", "mwgui1", "textures\\"); + OEngine::Render::Atlas::createFromFile("mainmenu.cfg", "mwgui2", "textures\\"); } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp new file mode 100644 index 0000000000..ca9ebbc4a7 --- /dev/null +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -0,0 +1,75 @@ +#include "mainmenu.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" + +namespace MWGui +{ + + MainMenu::MainMenu(int w, int h) + : OEngine::GUI::Layout("openmw_mainmenu.layout") + { + setCoord(0,0,w,h); + + int height = 64 * 3; + + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(w/2 - 64, h/2 - height/2, 128, height), MyGUI::Align::Default); + int curH = 0; + + mReturn = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mReturn->setImageResource ("Menu_Return"); + mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); + curH += 64; + + + /* + mNewGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mNewGame->setImageResource ("Menu_NewGame"); + curH += 64; + + mLoadGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mLoadGame->setImageResource ("Menu_LoadGame"); + curH += 64; + + + mSaveGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mSaveGame->setImageResource ("Menu_SaveGame"); + curH += 64; + */ + + mOptions = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mOptions->setImageResource ("Menu_Options"); + mOptions->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::showOptions); + curH += 64; + + /* + mCredits = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mCredits->setImageResource ("Menu_Credits"); + curH += 64; + */ + + mExitGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mExitGame->setImageResource ("Menu_ExitGame"); + mExitGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::exitGame); + curH += 64; + + } + + void MainMenu::returnToGame(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + } + + void MainMenu::showOptions(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); + } + + void MainMenu::exitGame(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWorld ()->exitNow(); + } + +} diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 06c59396fc..5fa2f69430 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -6,11 +6,22 @@ namespace MWGui class MainMenu : public OEngine::GUI::Layout { public: - MainMenu(int w, int h) - : Layout("openmw_mainmenu.layout") - { - setCoord(0,0,w,h); - } + MainMenu(int w, int h); + + private: + MyGUI::Button* mReturn; + MyGUI::Button* mNewGame; + MyGUI::Button* mLoadGame; + MyGUI::Button* mSaveGame; + MyGUI::Button* mOptions; + MyGUI::Button* mCredits; + MyGUI::Button* mExitGame; + + MyGUI::Widget* mButtonBox; + + void returnToGame(MyGUI::Widget* sender); + void showOptions(MyGUI::Widget* sender); + void exitGame(MyGUI::Widget* sender); }; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 9629c7cca6..d1e1f7095e 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -263,6 +263,7 @@ namespace MWGui dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); dialog->eventCancelClicked.clear(); + dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel); } void SettingsWindow::onResolutionAccept() diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 35aafa4467..5a4718ccf2 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -5,7 +5,6 @@ #include -#include #include #include "../mwgui/window_manager.hpp" @@ -20,6 +19,7 @@ #include "../engine.hpp" #include "../mwworld/player.hpp" +#include "../mwbase/world.hpp" #include #include @@ -68,8 +68,6 @@ namespace MWInput A_ToggleWeapon, A_ToggleSpell, - A_Settings, // Temporary hotkey - A_LAST // Marker for the last item }; @@ -78,7 +76,6 @@ namespace MWInput { OEngine::Input::DispatcherPtr disp; OEngine::Render::OgreRenderer &ogre; - OEngine::Render::ExitListener exit; Mangle::Input::OISDriver input; OEngine::Input::Poller poller; MouseLookEventPtr mouse; @@ -140,15 +137,6 @@ private: windows.messageBox ("Screenshot saved", empty); } - void showSettings() - { - if (mDragDrop) - return; - - if (!windows.isGuiMode() || windows.getMode() != MWGui::GM_Settings) - windows.pushGuiMode(MWGui::GM_Settings); - } - /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ void toggleInventory() { @@ -222,11 +210,19 @@ private: player.toggleRunning(); } + void toggleMainMenu() + { + if (windows.isGuiMode () && windows.getMode () == MWGui::GM_MainMenu) + windows.removeGuiMode (MWGui::GM_MainMenu); + else + windows.pushGuiMode (MWGui::GM_MainMenu); + } + // Exit program now button (which is disabled in GUI mode) void exitNow() { if(!windows.isGuiMode()) - exit.exitNow(); + MWBase::Environment::get().getWorld()->exitNow(); } public: @@ -236,7 +232,6 @@ private: bool debug, OMW::Engine& engine) : ogre(_ogre), - exit(ogre.getWindow()), input(ogre.getWindow(), !debug), poller(input), player(_player), @@ -273,10 +268,8 @@ private: "Draw Weapon"); disp->funcs.bind(A_ToggleSpell,boost::bind(&InputImpl::toggleSpell,this), "Ready hands"); - disp->funcs.bind(A_Settings, boost::bind(&InputImpl::showSettings, this), - "Show settings window"); - // Add the exit listener - ogre.getRoot()->addFrameListener(&exit); + disp->funcs.bind(A_GameMenu, boost::bind(&InputImpl::toggleMainMenu, this), + "Toggle main menu"); mouse = MouseLookEventPtr(new MouseLookEvent()); @@ -316,7 +309,7 @@ private: // NOTE: These keys do not require constant polling - use in conjuction with variables in loops. disp->bind(A_Quit, KC_Q); - disp->bind(A_Quit, KC_ESCAPE); + disp->bind(A_GameMenu, KC_ESCAPE); disp->bind(A_Screenshot, KC_SYSRQ); disp->bind(A_Inventory, KC_I); disp->bind(A_Console, KC_F1); @@ -327,7 +320,6 @@ private: disp->bind(A_ToggleWalk, KC_C); disp->bind(A_ToggleWeapon,KC_F); disp->bind(A_ToggleSpell,KC_R); - disp->bind(A_Settings, KC_F2); // Key bindings for polled keys // NOTE: These keys are constantly being polled. Only add keys that must be checked each frame. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf2d210143..cb33b40f2d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm), - mNumFacing(0) + mNumFacing(0), mExit(false) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -1147,4 +1147,15 @@ namespace MWWorld } return pos.z < cell.water; } + + void World::exitNow() + { + mExit = true; + } + + bool World::getExitNow() + { + return mExit; + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 24645cb8e9..7af71d349f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -84,6 +84,8 @@ namespace MWWorld unsigned long lastTick; Ogre::Timer mTimer; + bool mExit; + int getDaysPerMonth (int month) const; bool moveObjectImp (const Ptr& ptr, float x, float y, float z); @@ -274,6 +276,10 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::Ptr &object); virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); + + virtual void exitNow(); ///< exit after this frame has ended + virtual bool getExitNow(); ///< @return true if the application should exit + }; } diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index ae007f023f..3a5430c6f4 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -5,6 +5,7 @@ set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) set(MYGUI_FILES atlas1.cfg + mainmenu.cfg bigbars.png black.png core.skin diff --git a/files/mygui/mainmenu.cfg b/files/mygui/mainmenu.cfg new file mode 100644 index 0000000000..7aaf8c1c72 --- /dev/null +++ b/files/mygui/mainmenu.cfg @@ -0,0 +1,95 @@ +[settings] + size_x = 512 + size_y = 512 + + +[menu_newgame.dds] + x = 0 + y = 0 + +[menu_newgame_pressed.dds] + x = 128 + y = 0 + +[menu_newgame_over.dds] + x = 256 + y = 0 + + +[menu_loadgame.dds] + x = 384 + y = 0 + +[menu_loadgame_pressed.dds] + x = 0 + y = 64 + +[menu_loadgame_over.dds] + x = 128 + y = 64 + + +[menu_options.dds] + x = 256 + y = 64 + +[menu_options_pressed.dds] + x = 384 + y = 64 + +[menu_options_over.dds] + x = 0 + y = 128 + + +[menu_credits.dds] + x = 128 + y = 128 + +[menu_credits_pressed.dds] + x = 256 + y = 128 + +[menu_credits_over.dds] + x = 384 + y = 128 + + +[menu_exitgame.dds] + x = 0 + y = 192 + +[menu_exitgame_pressed.dds] + x = 128 + y = 192 + +[menu_exitgame_over.dds] + x = 256 + y = 192 + + +[menu_savegame.dds] + x = 384 + y = 192 + +[menu_savegame_pressed.dds] + x = 0 + y = 256 + +[menu_savegame_over.dds] + x = 128 + y = 256 + + +[menu_return.dds] + x = 256 + y = 256 + +[menu_return_pressed.dds] + x = 384 + y = 256 + +[menu_return_over.dds] + x = 0 + y = 320 + diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 56f800ea34..7a8d586d2e 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -4,8 +4,6 @@ - - diff --git a/files/mygui/openmw_mainmenu.layout b/files/mygui/openmw_mainmenu.layout index bf17be7f70..4479a121f6 100644 --- a/files/mygui/openmw_mainmenu.layout +++ b/files/mygui/openmw_mainmenu.layout @@ -2,16 +2,5 @@ - - - - - - - - - - - - + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml index f1a8b0dfc7..83461bbe8a 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -1,6 +1,8 @@ + + @@ -44,6 +46,10 @@ + + + + @@ -113,6 +119,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openengine/ogre/exitlistener.hpp b/libs/openengine/ogre/exitlistener.hpp deleted file mode 100644 index 5a9d1ff68f..0000000000 --- a/libs/openengine/ogre/exitlistener.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef OENGINE_OGRE_EXITLISTEN_H -#define OENGINE_OGRE_EXITLISTEN_H - -/* - This FrameListener simply exits the rendering loop when the window - is closed. You can also tell it to exit manually by setting the exit - member to true; - */ - -#include -#include - -namespace OEngine { -namespace Render -{ - struct ExitListener : Ogre::FrameListener - { - Ogre::RenderWindow *window; - bool exit; - - ExitListener(Ogre::RenderWindow *wnd) - : window(wnd), exit(false) {} - - bool frameStarted(const Ogre::FrameEvent &evt) - { - if(window->isClosed()) - exit = true; - - return !exit; - } - - // Function equivalent of setting exit=true. Handy when you need a - // delegate to bind to an event. - void exitNow() { exit = true; } - }; -}} -#endif diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp index f76ac51ef9..efb12a5d2d 100644 --- a/libs/openengine/ogre/fader.hpp +++ b/libs/openengine/ogre/fader.hpp @@ -9,8 +9,6 @@ inspired by http://www.ogre3d.org/tikiwiki/FadeEffectOverlay (heavily adjusted) */ -#include - namespace Ogre { class TextureUnitState; From 44ff31b50a15915809cb007cf269eee70a3426f2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Aug 2012 16:21:53 +0200 Subject: [PATCH 030/143] removed world exit methods --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 3 --- apps/openmw/mwgui/mainmenu.cpp | 5 ++++- apps/openmw/mwinput/inputmanager.cpp | 4 +++- apps/openmw/mwworld/worldimp.cpp | 12 +----------- apps/openmw/mwworld/worldimp.hpp | 3 --- 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5fc9774d1b..2467f91d13 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -116,7 +116,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) std::cerr << "Error in framelistener: " << e.what() << std::endl; } - return !MWBase::Environment::get().getWorld()->getExitNow(); + return true; } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f08617d4ce..8b809d399a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -248,9 +248,6 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) = 0; virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; - - virtual void exitNow() = 0; ///< exit after this frame has ended - virtual bool getExitNow() = 0; ///< @return true if the application should exit }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ca9ebbc4a7..e2fefd6497 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -1,5 +1,8 @@ #include "mainmenu.hpp" +#include + + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -69,7 +72,7 @@ namespace MWGui void MainMenu::exitGame(MyGUI::Widget* sender) { - MWBase::Environment::get().getWorld ()->exitNow(); + Ogre::Root::getSingleton ().queueEndRendering (); } } diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 5a4718ccf2..e29e3c2683 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -1,5 +1,7 @@ #include "inputmanager.hpp" +#include + #include #include @@ -222,7 +224,7 @@ private: void exitNow() { if(!windows.isGuiMode()) - MWBase::Environment::get().getWorld()->exitNow(); + Ogre::Root::getSingleton().queueEndRendering (); } public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cb33b40f2d..d9fbc5b77d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm), - mNumFacing(0), mExit(false) + mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -1148,14 +1148,4 @@ namespace MWWorld return pos.z < cell.water; } - void World::exitNow() - { - mExit = true; - } - - bool World::getExitNow() - { - return mExit; - } - } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7af71d349f..f2f221b39c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -277,9 +277,6 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::Ptr &object); virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); - virtual void exitNow(); ///< exit after this frame has ended - virtual bool getExitNow(); ///< @return true if the application should exit - }; } From 11fadf4a07c952b34292cd1e540ec70539370fa5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Aug 2012 16:25:27 +0200 Subject: [PATCH 031/143] forgot something --- apps/openmw/mwworld/worldimp.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f2f221b39c..4e545f7180 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -84,8 +84,6 @@ namespace MWWorld unsigned long lastTick; Ogre::Timer mTimer; - bool mExit; - int getDaysPerMonth (int month) const; bool moveObjectImp (const Ptr& ptr, float x, float y, float z); From 225530c69013f458a7eabf543fd3b93ccd0c94ec Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Fri, 10 Aug 2012 23:19:12 +0400 Subject: [PATCH 032/143] implemented better main loop for OS X (carbon version). Input feels far less laggy --- libs/openengine/ogre/renderer.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 70bbf7940e..3096e2e6ee 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -17,6 +17,10 @@ #include #include +#if defined(__APPLE__) && !__LP64__ +#include +#endif + using namespace Ogre; using namespace OEngine::Render; @@ -31,7 +35,33 @@ void OgreRenderer::cleanup() void OgreRenderer::start() { +#if defined(__APPLE__) && !defined(__LP64__) + bool quit = false; + // OSX Carbon Message Pump + do { + EventRef event = NULL; + EventTargetRef targetWindow; + targetWindow = GetEventDispatcherTarget(); + + // If we are unable to get the target then we no longer care about events. + if (!targetWindow) return; + + // Grab the next event while possible + while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &event) == noErr) + { + // Dispatch the event + SendEventToEventTarget(event, targetWindow); + ReleaseEvent(event); + } + + if (!Ogre::Root::getSingleton().renderOneFrame()) { + quit = true; + } + + } while (!quit); +#else mRoot->startRendering(); +#endif } bool OgreRenderer::loadPlugins() const From ebf1fe415e667cd94e1227788f8138cde47cf952 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 11 Aug 2012 01:11:52 +0400 Subject: [PATCH 033/143] tiny fix --- libs/openengine/ogre/renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 3096e2e6ee..4074a1a997 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -17,7 +17,7 @@ #include #include -#if defined(__APPLE__) && !__LP64__ +#if defined(__APPLE__) && !defined(__LP64__) #include #endif From 5018db3332ad1c7b70bb595714031e031e723c68 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 12:13:16 +0400 Subject: [PATCH 034/143] removed some redundant code, added some comments --- apps/openmw/mwrender/player.hpp | 8 ++++++++ apps/openmw/mwrender/renderingmanager.hpp | 4 ++++ apps/openmw/mwworld/scene.cpp | 5 ++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index b1160435da..e5361e64b3 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -28,6 +28,8 @@ namespace MWRender bool mVanityModeEnabled; void controlFlip(); + + /// Updates sound manager listener data void updateListener(); public: @@ -35,11 +37,17 @@ namespace MWRender Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); /// Set where the player is looking at. Uses Morrowind (euler) angles + /// \return true if player object needs to bo rotated physically bool setRotation(const Ogre::Vector3 &rot); + + /// \return true if player object needs to bo rotated physically bool adjustRotation(const Ogre::Vector3 &rot); std::string getHandle() const; + /// Attach camera to object + /// \note there is no protection from attaching the same camera to + /// several different objects void attachTo(const MWWorld::Ptr &); void toggleViewMode() { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 197c1fda01..c0558b7140 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -85,6 +85,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); + + /// Rotates object accordingly to its type + /// \param adjust indicates should rotation be set or adjusted + /// \return true if object needs to be rotated physically bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); void setWaterHeight(const float height); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ec557e35c7..531546a576 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -140,19 +140,18 @@ namespace MWWorld const ESM::Position& pos, bool adjustPlayerPos) { - MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); - bool hasWater = cell->cell->data.flags & cell->cell->HasWater; mPhysics->setCurrentWater(hasWater, cell->cell->water); MWBase::World *world = MWBase::Environment::get().getWorld(); + world->getPlayer().setCell(cell); + MWWorld::Ptr player = world->getPlayer().getPlayer(); if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); } - world->getPlayer().setCell(cell); MWMechanics::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); From 45306e4bc3b98bf2eb29fe1c8761c1c49ea12f77 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 13:23:54 +0400 Subject: [PATCH 035/143] fixed rotation adjustment --- apps/openmw/mwrender/player.cpp | 8 ++++---- apps/openmw/mwrender/player.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 24 +++++++++++++---------- apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/scene.cpp | 6 +++++- apps/openmw/mwworld/worldimp.cpp | 10 ++++++---- apps/openmw/mwworld/worldimp.hpp | 2 ++ 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8b27adbe3e..f05ea80ce8 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -27,10 +27,10 @@ namespace MWRender // we are only interested in X and Y rotation // Rotate around X axis - Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); pitchNode->setOrientation(xr); yawNode->setOrientation(yr); @@ -56,8 +56,8 @@ namespace MWRender Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - pitchNode->pitch(Ogre::Degree(rot.x)); - yawNode->yaw(Ogre::Degree(-rot.z)); + pitchNode->pitch(Ogre::Radian(rot.x)); + yawNode->yaw(Ogre::Radian(-rot.z)); controlFlip(); updateListener(); diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index e5361e64b3..a19e72ab0c 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -37,9 +37,11 @@ namespace MWRender Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); /// Set where the player is looking at. Uses Morrowind (euler) angles + /// \param rot Rotation angles in radians /// \return true if player object needs to bo rotated physically bool setRotation(const Ogre::Vector3 &rot); + /// \param rot Rotation angles in radians /// \return true if player object needs to bo rotated physically bool adjustRotation(const Ogre::Vector3 &rot); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8719557ca3..7180fea667 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -254,27 +254,31 @@ RenderingManager::rotateObject( Ogre::Vector3 &rot, bool adjust) { - if (ptr.getRefData().getHandle() == "player") { + bool isPlayer = ptr.getRefData().getHandle() == "player"; + bool force = true; + + if (isPlayer) { if (adjust) { - return mPlayer->adjustRotation(rot); + force = mPlayer->adjustRotation(rot); } else { - return mPlayer->setRotation(rot); + force = mPlayer->setRotation(rot); } } MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); if (adjust) { + /// \note Stored and passed in radians float *f = ptr.getRefData().getPosition().rot; rot.x += f[0], rot.y += f[1], rot.z += f[2]; } + if (!isPlayer) { + Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Radian(rot.y), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Radian(rot.z), Ogre::Vector3::UNIT_Z); - Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr(Ogre::Degree(rot.z), Ogre::Vector3::UNIT_Z); - - ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); - - return true; + ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); + } + return force; } void diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index c0558b7140..ef6f18a75c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -87,6 +87,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); /// Rotates object accordingly to its type + /// \param rot euler angles in radians /// \param adjust indicates should rotation be set or adjusted /// \return true if object needs to be rotated physically bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 531546a576..e02e101883 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -150,7 +150,11 @@ namespace MWWorld if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); - world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); + + float x = Ogre::Radian(pos.rot[0]).valueDegrees(); + float y = Ogre::Radian(pos.rot[1]).valueDegrees(); + float z = Ogre::Radian(pos.rot[2]).valueDegrees(); + world->rotateObject(player, x, y, z); } MWMechanics::MechanicsManager *mechMgr = diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf2d210143..605858eaf9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -629,12 +629,14 @@ namespace MWWorld void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { - Ogre::Vector3 rot(x, y, z); + Ogre::Vector3 rot; + rot.x = Ogre::Degree(x).valueRadians(); + rot.y = Ogre::Degree(y).valueRadians(); + rot.z = Ogre::Degree(z).valueRadians(); + if (mRendering->rotateObject(ptr, rot, adjust)) { float *objRot = ptr.getRefData().getPosition().rot; - objRot[0] = Ogre::Degree(rot.x).valueRadians(); - objRot[1] = Ogre::Degree(rot.y).valueRadians(); - objRot[2] = Ogre::Degree(rot.z).valueRadians(); + objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; mPhysics->rotateObject( ptr.getRefData().getHandle(), diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 24645cb8e9..c3b0ed8989 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -210,6 +210,8 @@ namespace MWWorld virtual void scaleObject (const Ptr& ptr, float scale); + /// Rotates object, uses degrees + /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) From 6eb9f15b6db30e3a42bd03461132f207f5e8a1fe Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 11:38:26 +0200 Subject: [PATCH 036/143] fixed time stamp operator --- apps/openmw/mwworld/timestamp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/timestamp.cpp b/apps/openmw/mwworld/timestamp.cpp index e2f3d6c634..126d5490c5 100644 --- a/apps/openmw/mwworld/timestamp.cpp +++ b/apps/openmw/mwworld/timestamp.cpp @@ -76,12 +76,12 @@ namespace MWWorld TimeStamp operator+ (const TimeStamp& stamp, double hours) { - return TimeStamp (stamp) + hours; + return TimeStamp (stamp) += hours; } TimeStamp operator+ (double hours, const TimeStamp& stamp) { - return TimeStamp (stamp) + hours; + return TimeStamp (stamp) += hours; } double operator- (const TimeStamp& left, const TimeStamp& right) From b53b27533a8043a3d8a753d7673494e5b18c6ade Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 12:02:51 +0200 Subject: [PATCH 037/143] fixes some include guards --- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 +- apps/openmw/mwdialogue/journalentry.hpp | 4 ++-- apps/openmw/mwdialogue/journalimp.hpp | 2 +- apps/openmw/mwdialogue/quest.hpp | 2 +- apps/openmw/mwdialogue/topic.hpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 69cc88f253..985d255735 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_DIALOGUEMANAGERIMP_H +#ifndef GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #include diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 9a3270439f..3e86401852 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MMDIALOGUE_JOURNALENTRY_H -#define GAME_MMDIALOGUE_JOURNALENTRY_H +#ifndef GAME_MWDIALOGUE_JOURNALENTRY_H +#define GAME_MWDIALOGUE_JOURNALENTRY_H #include diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 5fafc265ac..7d71fd7d06 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_JOURNAL_H +#ifndef GAME_MWDIALOG_JOURNAL_H #define GAME_MWDIALOG_JOURNAL_H #include "../mwbase/journal.hpp" diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index 5fcc894ea4..3afa81fac0 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_QUEST_H +#ifndef GAME_MWDIALOG_QUEST_H #define GAME_MWDIALOG_QUEST_H #include "topic.hpp" diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index 3ad15903f7..566f60ab00 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_TOPIC_H +#ifndef GAME_MWDIALOG_TOPIC_H #define GAME_MWDIALOG_TOPIC_H #include From f3edea77b2805b4cc74886881cbff282bada293b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 12:03:22 +0200 Subject: [PATCH 038/143] MSCV compatibility fix --- apps/openmw/mwbase/journal.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 99f6996cfb..b3dfea45b7 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -5,12 +5,9 @@ #include #include -namespace MWDialogue -{ - class Quest; - class Topic; - struct StampedJournalEntry; -} +#include "../mwdialogue/journalentry.hpp" +#include "../mwdialogue/topic.hpp" +#include "../mwdialogue/quest.hpp" namespace MWBase { From 355ca649c2ca5cfd3d82b7ff0d904c532ef87e81 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 14:54:33 +0400 Subject: [PATCH 039/143] camera flip control rewritten --- apps/openmw/mwrender/player.cpp | 40 +++++++++++++++++++-------------- apps/openmw/mwrender/player.hpp | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index f05ea80ce8..94a3f71c8e 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -27,7 +27,13 @@ namespace MWRender // we are only interested in X and Y rotation // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Radian radx(rot.x); + if (radx.valueDegrees() > 89.5f) { + radx = Ogre::Degree(89.5f); + } else if (radx.valueDegrees() < -89.5f) { + radx = Ogre::Degree(-89.5f); + } + Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); // Rotate around Y axis Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); @@ -35,7 +41,6 @@ namespace MWRender pitchNode->setOrientation(xr); yawNode->setOrientation(yr); - controlFlip(); updateListener(); return !mVanityModeEnabled; @@ -56,35 +61,36 @@ namespace MWRender Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - pitchNode->pitch(Ogre::Radian(rot.x)); + float f = controlFlip(Ogre::Radian(rot.x).valueDegrees()); + if (f != 0.0) { + pitchNode->pitch(Ogre::Degree(f)); + } yawNode->yaw(Ogre::Radian(-rot.z)); - controlFlip(); updateListener(); return !mVanityModeEnabled; } - void Player::controlFlip() + float Player::controlFlip(float shift) { Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::Quaternion orient = pitchNode->getOrientation(); float pitchAngle = - (2 * Ogre::Degree(Ogre::Math::ACos(orient.w)).valueDegrees()); + (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - // Limit the pitch between -90 degress and +90 degrees, Quake3-style. - if (pitchAngle > 90.0f) - { - Ogre::Real sqrt = Ogre::Math::Sqrt(0.5f); - if (orient.x > 0) { - // Set orientation to 90 degrees on X-axis. - pitchNode->setOrientation(Ogre::Quaternion(sqrt, sqrt, 0, 0)); - } else if (orient.x < 0) { - // Sets orientation to -90 degrees on X-axis. - pitchNode->setOrientation(Ogre::Quaternion(sqrt, -sqrt, 0, 0)); - } + if (pitchAngle + shift < 89.5f && pitchAngle + shift > -89.5f) { + return shift; } + if (pitchAngle > 0) { + float f = 89.5f - pitchAngle - shift; + return (f > 0.f) ? f : 0.f; + } else if (pitchAngle < 0) { + float f = -89.5 - pitchAngle - shift; + return (f < 0.f) ? f : 0.f; + } + return 0.f; } void Player::updateListener() diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index a19e72ab0c..981ecfe0b5 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -27,7 +27,7 @@ namespace MWRender bool mFirstPersonView; bool mVanityModeEnabled; - void controlFlip(); + float controlFlip(float shift = 0.f); /// Updates sound manager listener data void updateListener(); From 8ac2d11f5626a1779f10bda70eb712ded7cd33cb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Aug 2012 14:42:14 +0200 Subject: [PATCH 040/143] changed shading to be more like MW (completely replace material ambient with vertex colors if present), fixes some too dark daedric ruins --- files/materials/objects.shader | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/files/materials/objects.shader b/files/materials/objects.shader index dba239c14f..8e5cbf76ef 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -116,8 +116,9 @@ shInput(float3, normalPassthrough) shInput(float3, objSpacePositionPassthrough) shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - //shUniform(float, passIteration) @shAutoConstant(passIteration, pass_iteration_number) + #if !HAS_VERTEXCOLOR shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) + #endif shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) @shForeach(@shGlobalSettingString(num_lights)) @@ -178,8 +179,13 @@ float3 lightDir; float3 diffuse = float3(0,0,0); float d; + +#if HAS_VERTEXCOLOR + // ambient vertex colour tracking, FFP behaviour + float3 ambient = colourPassthrough.xyz * lightAmbient.xyz; +#else float3 ambient = materialAmbient.xyz * lightAmbient.xyz; - +#endif // shadows only for the first (directional) light #if SHADOWS @@ -237,10 +243,6 @@ #endif @shEndForeach - -#if HAS_VERTEXCOLOR - ambient *= colourPassthrough.xyz; -#endif shOutputColour(0).xyz *= (ambient + diffuse + materialEmissive.xyz); #endif @@ -293,6 +295,7 @@ #if MRT shOutputColour(1) = float4(depthPassthrough / far,1,1,1); #endif + } #endif From 47f7a5e988a32b823476d26dd7c384789bb82487 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 17:01:55 +0400 Subject: [PATCH 041/143] fix double pos/rot adjustment --- apps/openmw/mwworld/worldimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 605858eaf9..33eebc3076 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -558,7 +558,9 @@ namespace MWWorld if (!newCell.isExterior()) { changeToInteriorCell(newCell.cell->name, pos); } else { - changeToExteriorCell(pos); + int cellX = newCell.cell->data.gridX; + int cellY = newCell.cell->data.gridY; + mWorldScene->changeCell(cellX, cellY, pos, false); } } else { if (!mWorldScene->isCellActive(newCell)) { From 2ebf4721d1cc607e0bd1be5ea3b9812dff76ae15 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Aug 2012 15:15:09 +0200 Subject: [PATCH 042/143] small main menu fix --- apps/openmw/mwinput/inputmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index e29e3c2683..2f70d14fa2 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -214,8 +214,8 @@ private: void toggleMainMenu() { - if (windows.isGuiMode () && windows.getMode () == MWGui::GM_MainMenu) - windows.removeGuiMode (MWGui::GM_MainMenu); + if (windows.isGuiMode () && (windows.getMode () == MWGui::GM_MainMenu || windows.getMode () == MWGui::GM_Settings)) + windows.popGuiMode(); else windows.pushGuiMode (MWGui::GM_MainMenu); } From f7f8ac0a730b7c4e508869edf5ec047ac5b6a57d Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 18:28:37 +0400 Subject: [PATCH 043/143] fix move/rotateObject code --- apps/openmw/mwrender/renderingmanager.cpp | 5 +++-- apps/openmw/mwworld/worldimp.cpp | 14 ++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7180fea667..5215218757 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -254,7 +254,8 @@ RenderingManager::rotateObject( Ogre::Vector3 &rot, bool adjust) { - bool isPlayer = ptr.getRefData().getHandle() == "player"; + bool isActive = ptr.getRefData().getBaseNode() != 0; + bool isPlayer = isActive && ptr.getRefData().getHandle() == "player"; bool force = true; if (isPlayer) { @@ -271,7 +272,7 @@ RenderingManager::rotateObject( float *f = ptr.getRefData().getPosition().rot; rot.x += f[0], rot.y += f[1], rot.z += f[2]; } - if (!isPlayer) { + if (!isPlayer && isActive) { Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); Ogre::Quaternion yr(Ogre::Radian(rot.y), Ogre::Vector3::UNIT_Y); Ogre::Quaternion zr(Ogre::Radian(rot.z), Ogre::Vector3::UNIT_Z); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8b0ed206d9..79150564eb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -563,9 +563,9 @@ namespace MWWorld mWorldScene->changeCell(cellX, cellY, pos, false); } } else { - if (!mWorldScene->isCellActive(newCell)) { + if (!mWorldScene->isCellActive(*currCell)) { copyObjectToCell(ptr, newCell, pos); - } else if (!mWorldScene->isCellActive(*currCell)) { + } else if (!mWorldScene->isCellActive(newCell)) { MWWorld::Class::get(ptr).copyToCell(ptr, newCell); mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); @@ -640,10 +640,12 @@ namespace MWWorld float *objRot = ptr.getRefData().getPosition().rot; objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; - mPhysics->rotateObject( - ptr.getRefData().getHandle(), - ptr.getRefData().getBaseNode()->getOrientation() - ); + if (ptr.getRefData().getBaseNode() != 0) { + mPhysics->rotateObject( + ptr.getRefData().getHandle(), + ptr.getRefData().getBaseNode()->getOrientation() + ); + } } } From dcf1e6645623b7dda09b658c65dfb8984f01d502 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Aug 2012 17:26:01 +0200 Subject: [PATCH 044/143] fix main menu placement after resolution changes --- apps/openmw/mwgui/mainmenu.cpp | 10 +++++++++- apps/openmw/mwgui/mainmenu.hpp | 2 ++ apps/openmw/mwgui/window_manager.cpp | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e2fefd6497..7618ecb157 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -13,11 +13,20 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") + , mButtonBox(0) + { + onResChange(w,h); + } + + void MainMenu::onResChange(int w, int h) { setCoord(0,0,w,h); int height = 64 * 3; + if (mButtonBox) + MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(w/2 - 64, h/2 - height/2, 128, height), MyGUI::Align::Default); int curH = 0; @@ -57,7 +66,6 @@ namespace MWGui mExitGame->setImageResource ("Menu_ExitGame"); mExitGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::exitGame); curH += 64; - } void MainMenu::returnToGame(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 5fa2f69430..fd583d1876 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -8,6 +8,8 @@ namespace MWGui public: MainMenu(int w, int h); + void onResChange(int w, int h); + private: MyGUI::Button* mReturn; MyGUI::Button* mNewGame; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 659af04470..db7600da9f 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -639,6 +639,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector int y = Settings::Manager::getInt("resolution y", "Video"); mHud->onResChange(x, y); mConsole->onResChange(x, y); + mMenu->onResChange(x, y); mSettingsWindow->center(); mAlchemyWindow->center(); mScrollWindow->center(); From b68f9d6a2861ab9fe8e9a3185e64e83086d16070 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 17:30:55 +0200 Subject: [PATCH 045/143] Issue #107: MechanicsManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +- apps/openmw/mwbase/environment.hpp | 14 ++-- apps/openmw/mwbase/mechanicsmanager.hpp | 77 +++++++++++++++++++ apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 +- apps/openmw/mwgui/charactercreation.cpp | 1 + apps/openmw/mwgui/charactercreation.hpp | 1 - apps/openmw/mwgui/stats_window.cpp | 3 +- apps/openmw/mwgui/window_manager.cpp | 2 +- ...icsmanager.cpp => mechanicsmanagerimp.cpp} | 2 +- ...icsmanager.hpp => mechanicsmanagerimp.hpp} | 35 ++++----- apps/openmw/mwworld/scene.cpp | 5 +- apps/openmw/mwworld/worldimp.cpp | 9 +-- 16 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 apps/openmw/mwbase/mechanicsmanager.hpp rename apps/openmw/mwmechanics/{mechanicsmanager.cpp => mechanicsmanagerimp.cpp} (99%) rename apps/openmw/mwmechanics/{mechanicsmanager.hpp => mechanicsmanagerimp.hpp} (60%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 172c6a4948..f4454a216c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -60,12 +60,12 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanager stat creaturestats magiceffects movement actors drawstate spells + mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells activespells npcstats ) add_openmw_dir (mwbase - environment world scriptmanager dialoguemanager journal soundmanager + environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2467f91d13..98fee43841 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -32,7 +32,7 @@ #include "mwdialogue/dialoguemanagerimp.hpp" #include "mwdialogue/journalimp.hpp" -#include "mwmechanics/mechanicsmanager.hpp" +#include "mwmechanics/mechanicsmanagerimp.hpp" void OMW::Engine::executeLocalScripts() diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 7d109b000c..f75c3588fc 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -5,13 +5,12 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" - #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" #include "soundmanager.hpp" +#include "mechanicsmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -49,7 +48,7 @@ void MWBase::Environment::setWindowManager (MWGui::WindowManager *windowManager) mWindowManager = windowManager; } -void MWBase::Environment::setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager) +void MWBase::Environment::setMechanicsManager (MechanicsManager *mechanicsManager) { mMechanicsManager = mechanicsManager; } @@ -98,7 +97,7 @@ MWGui::WindowManager *MWBase::Environment::getWindowManager() const return mWindowManager; } -MWMechanics::MechanicsManager *MWBase::Environment::getMechanicsManager() const +MWBase::MechanicsManager *MWBase::Environment::getMechanicsManager() const { assert (mMechanicsManager); return mMechanicsManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index d03267c25f..f2e7249d3e 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWGui class WindowManager; } -namespace MWMechanics -{ - class MechanicsManager; -} - namespace MWInput { struct MWInputManager; @@ -23,6 +18,7 @@ namespace MWBase class DialogueManager; class Journal; class SoundManager; + class MechanicsManager; /// \brief Central hub for mw-subsystems /// @@ -38,7 +34,7 @@ namespace MWBase SoundManager *mSoundManager; ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; - MWMechanics::MechanicsManager *mMechanicsManager; + MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; MWInput::MWInputManager *mInputManager; @@ -64,7 +60,7 @@ namespace MWBase void setWindowManager (MWGui::WindowManager *windowManager); - void setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager); + void setMechanicsManager (MechanicsManager *mechanicsManager); void setDialogueManager (DialogueManager *dialogueManager); @@ -79,11 +75,11 @@ namespace MWBase SoundManager *getSoundManager() const; - MWBase::ScriptManager *getScriptManager() const; + ScriptManager *getScriptManager() const; MWGui::WindowManager *getWindowManager() const; - MWMechanics::MechanicsManager *getMechanicsManager() const; + MechanicsManager *getMechanicsManager() const; DialogueManager *getDialogueManager() const; diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp new file mode 100644 index 0000000000..c5f1847af1 --- /dev/null +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -0,0 +1,77 @@ +#ifndef GAME_MWBASE_MECHANICSMANAGER_H +#define GAME_MWBASE_MECHANICSMANAGER_H + +#include +#include + +namespace Ogre +{ + class Vector3; +} + +namespace ESM +{ + struct Class; +} + +namespace MWWorld +{ + class Ptr; + class CellStore; +} + +namespace MWBase +{ + /// \brief Interface for game mechanics manager (implemented in MWMechanics) + class MechanicsManager + { + MechanicsManager (const MechanicsManager&); + ///< not implemented + + MechanicsManager& operator= (const MechanicsManager&); + ///< not implemented + + public: + + MechanicsManager() {} + + virtual ~MechanicsManager() {} + + virtual void addActor (const MWWorld::Ptr& ptr) = 0; + ///< Register an actor for stats management + + virtual void removeActor (const MWWorld::Ptr& ptr) = 0; + ///< Deregister an actor for stats management + + virtual void dropActors (const MWWorld::CellStore *cellStore) = 0; + ///< Deregister all actors in the given cell. + + virtual void watchActor (const MWWorld::Ptr& ptr) = 0; + ///< On each update look for changes in a previously registered actor and update the + /// GUI accordingly. + + virtual void update (std::vector >& movement, + float duration, bool paused) = 0; + ///< Update actor stats and store desired velocity vectors in \a movement + /// + /// \param paused In game type does not currently advance (this usually means some GUI + /// component is up). + + virtual void setPlayerName (const std::string& name) = 0; + ///< Set player name. + + virtual void setPlayerRace (const std::string& id, bool male) = 0; + ///< Set player race. + + virtual void setPlayerBirthsign (const std::string& id) = 0; + ///< Set player birthsign. + + virtual void setPlayerClass (const std::string& id) = 0; + ///< Set player class to stock class. + + virtual void setPlayerClass (const ESM::Class& class_) = 0; + ///< Set player class to custom class. + }; +} + +#endif diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0f3141f5c6..57301e1a25 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -4,10 +4,10 @@ #include #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -104,7 +104,7 @@ namespace MWClass if (!model.empty()) { return "meshes\\" + model; } - return ""; + return ""; } std::string Creature::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 81c0c85f55..30173701ec 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -11,11 +11,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 985d255735..e3e9fd7529 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H +#include "../mwbase/dialoguemanager.hpp" + #include #include @@ -8,8 +10,6 @@ #include "../mwscript/interpretercontext.hpp" #include -#include "../mwbase/dialoguemanager.hpp" - #include "../mwworld/ptr.hpp" #include diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index c0081544b0..fd3a7872e3 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" namespace { diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index e9c90877e6..1fd67bff7b 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -7,7 +7,6 @@ #include "../mwbase/world.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/stat.hpp" namespace MWGui diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 190d195945..3f5162bfba 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -8,11 +8,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwmechanics/npcstats.hpp" #include "window_manager.hpp" #include "tooltips.hpp" diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 659af04470..45aee089cd 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -21,10 +21,10 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwinput/inputmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/cellstore.hpp" diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp similarity index 99% rename from apps/openmw/mwmechanics/mechanicsmanager.cpp rename to apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fe5485d615..5ad67dde46 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,5 +1,5 @@ -#include "mechanicsmanager.hpp" +#include "mechanicsmanagerimp.hpp" #include diff --git a/apps/openmw/mwmechanics/mechanicsmanager.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp similarity index 60% rename from apps/openmw/mwmechanics/mechanicsmanager.hpp rename to apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 97bb369fda..d5fd3b6f2f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -1,8 +1,7 @@ -#ifndef GAME_MWMECHANICS_MECHANICSMANAGER_H -#define GAME_MWMECHANICS_MECHANICSMANAGER_H +#ifndef GAME_MWMECHANICS_MECHANICSMANAGERIMP_H +#define GAME_MWMECHANICS_MECHANICSMANAGERIMP_H -#include -#include +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" @@ -22,7 +21,7 @@ namespace MWWorld namespace MWMechanics { - class MechanicsManager + class MechanicsManager : public MWBase::MechanicsManager { MWWorld::Ptr mWatched; CreatureStats mWatchedCreature; @@ -38,43 +37,41 @@ namespace MWMechanics public: - MechanicsManager (); + MechanicsManager(); - void configureGUI(); - - void addActor (const MWWorld::Ptr& ptr); + virtual void addActor (const MWWorld::Ptr& ptr); ///< Register an actor for stats management - void removeActor (const MWWorld::Ptr& ptr); + virtual void removeActor (const MWWorld::Ptr& ptr); ///< Deregister an actor for stats management - void dropActors (const MWWorld::CellStore *cellStore); + virtual void dropActors (const MWWorld::CellStore *cellStore); ///< Deregister all actors in the given cell. - void watchActor (const MWWorld::Ptr& ptr); + virtual void watchActor (const MWWorld::Ptr& ptr); ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - void update (std::vector >& movement, float duration, - bool paused); + virtual void update (std::vector >& movement, + float duration, bool paused); ///< Update actor stats and store desired velocity vectors in \a movement /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). - void setPlayerName (const std::string& name); + virtual void setPlayerName (const std::string& name); ///< Set player name. - void setPlayerRace (const std::string& id, bool male); + virtual void setPlayerRace (const std::string& id, bool male); ///< Set player race. - void setPlayerBirthsign (const std::string& id); + virtual void setPlayerBirthsign (const std::string& id); ///< Set player birthsign. - void setPlayerClass (const std::string& id); + virtual void setPlayerClass (const std::string& id); ///< Set player class to stock class. - void setPlayerClass (const ESM::Class& class_); + virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ec557e35c7..9318fd6f13 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" - -#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwgui/window_manager.hpp" @@ -154,7 +153,7 @@ namespace MWWorld } world->getPlayer().setCell(cell); - MWMechanics::MechanicsManager *mechMgr = + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->addActor(player); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d9fbc5b77d..3a9547a445 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" - #include "../mwgui/window_manager.hpp" #include "player.hpp" @@ -575,7 +574,7 @@ namespace MWWorld mRendering->moveObjectToCell(copy, vec, currCell); if (MWWorld::Class::get(ptr).isActor()) { - MWMechanics::MechanicsManager *mechMgr = + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->removeActor(ptr); @@ -604,11 +603,11 @@ namespace MWWorld if (cell->isExterior()) { int cellX, cellY; positionToIndex(x, y, cellX, cellY); - + cell = getExterior(cellX, cellY); } moveObject(ptr, *cell, x, y, z); - + return cell != ptr.getCell(); } From 0231533d05eaf715031cdfc155c1e2045aa6c630 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 17:53:39 +0200 Subject: [PATCH 046/143] Issue #107: InputManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 3 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 ++-- apps/openmw/mwbase/environment.hpp | 12 ++---- apps/openmw/mwbase/inputmanager.hpp | 37 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 1 - apps/openmw/mwgui/bookwindow.cpp | 2 - apps/openmw/mwgui/container.cpp | 2 - apps/openmw/mwgui/scrollwindow.cpp | 2 - apps/openmw/mwgui/settingswindow.cpp | 3 +- apps/openmw/mwgui/window_manager.cpp | 3 +- .../{inputmanager.cpp => inputmanagerimp.cpp} | 6 +-- .../{inputmanager.hpp => inputmanagerimp.hpp} | 20 +++++----- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwscript/controlextensions.cpp | 10 +---- apps/openmw/mwscript/guiextensions.cpp | 1 - apps/openmw/mwscript/interpretercontext.cpp | 2 - 17 files changed, 65 insertions(+), 50 deletions(-) create mode 100644 apps/openmw/mwbase/inputmanager.hpp rename apps/openmw/mwinput/{inputmanager.cpp => inputmanagerimp.cpp} (98%) rename apps/openmw/mwinput/{inputmanager.hpp => inputmanagerimp.hpp} (62%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index f4454a216c..7bd7f1882e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - inputmanager + inputmanagerimp mouselookevent ) @@ -66,6 +66,7 @@ add_openmw_dir (mwmechanics add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager + inputmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 98fee43841..c484c22ea2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -13,7 +13,7 @@ #include #include -#include "mwinput/inputmanager.hpp" +#include "mwinput/inputmanagerimp.hpp" #include "mwgui/window_manager.hpp" #include "mwgui/cursorreplace.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index f75c3588fc..28e71fcf0c 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -3,14 +3,13 @@ #include -#include "../mwinput/inputmanager.hpp" - #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" #include "soundmanager.hpp" #include "mechanicsmanager.hpp" +#include "inputmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -63,7 +62,7 @@ void MWBase::Environment::setJournal (Journal *journal) mJournal = journal; } -void MWBase::Environment::setInputManager (MWInput::MWInputManager *inputManager) +void MWBase::Environment::setInputManager (InputManager *inputManager) { mInputManager = inputManager; } @@ -115,7 +114,7 @@ MWBase::Journal *MWBase::Environment::getJournal() const return mJournal; } -MWInput::MWInputManager *MWBase::Environment::getInputManager() const +MWBase::InputManager *MWBase::Environment::getInputManager() const { assert (mInputManager); return mInputManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index f2e7249d3e..4a21b57a4e 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWGui class WindowManager; } -namespace MWInput -{ - struct MWInputManager; -} - namespace MWBase { class World; @@ -19,6 +14,7 @@ namespace MWBase class Journal; class SoundManager; class MechanicsManager; + class InputManager; /// \brief Central hub for mw-subsystems /// @@ -37,7 +33,7 @@ namespace MWBase MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; - MWInput::MWInputManager *mInputManager; + InputManager *mInputManager; float mFrameDuration; Environment (const Environment&); @@ -66,7 +62,7 @@ namespace MWBase void setJournal (Journal *journal); - void setInputManager (MWInput::MWInputManager *inputManager); + void setInputManager (InputManager *inputManager); void setFrameDuration (float duration); ///< Set length of current frame in seconds. @@ -85,7 +81,7 @@ namespace MWBase Journal *getJournal() const; - MWInput::MWInputManager *getInputManager() const; + InputManager *getInputManager() const; float getFrameDuration() const; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp new file mode 100644 index 0000000000..d865bfb0ec --- /dev/null +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -0,0 +1,37 @@ +#ifndef GAME_MWBASE_INPUTMANAGER_H +#define GAME_MWBASE_INPUTMANAGER_H + +#include + +#include + +namespace MWBase +{ + /// \brief Interface for input manager (implemented in MWInput) + class InputManager + { + InputManager (const InputManager&); + ///< not implemented + + InputManager& operator= (const InputManager&); + ///< not implemented + + public: + + InputManager() {} + + virtual ~InputManager() {} + + virtual void update() = 0; + + virtual void changeInputMode(bool guiMode) = 0; + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + + virtual void setDragDrop(bool dragDrop) = 0; + + virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 644da49e27..251577d3bf 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -19,7 +19,6 @@ #include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwinput/inputmanager.hpp" #include "../mwgui/dialogue.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 19a5e93985..92f0226ed1 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -6,8 +6,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 66dea08497..89cd102339 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -19,8 +19,6 @@ #include "../mwclass/container.hpp" -#include "../mwinput/inputmanager.hpp" - #include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 67a02e53b4..fb2239c6da 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -4,8 +4,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index d1e1f7095e..06177aaa8d 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -13,11 +13,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "window_manager.hpp" #include "confirmationdialog.hpp" diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 45aee089cd..b4eaaacf49 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -21,10 +21,9 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/cellstore.hpp" diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp similarity index 98% rename from apps/openmw/mwinput/inputmanager.cpp rename to apps/openmw/mwinput/inputmanagerimp.cpp index e29e3c2683..d53e67d485 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "inputmanager.hpp" +#include "inputmanagerimp.hpp" #include @@ -501,8 +501,8 @@ private: impl->adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); } - void MWInputManager::toggleControlSwitch(std::string sw, bool value) + void MWInputManager::toggleControlSwitch (const std::string& sw, bool value) { - impl->toggleControlSwitch(sw, value); + impl->toggleControlSwitch(sw, value); } } diff --git a/apps/openmw/mwinput/inputmanager.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp similarity index 62% rename from apps/openmw/mwinput/inputmanager.hpp rename to apps/openmw/mwinput/inputmanagerimp.hpp index 2486f82d67..862c4fd207 100644 --- a/apps/openmw/mwinput/inputmanager.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -1,10 +1,12 @@ -#ifndef _MWINPUT_MWINPUTMANAGER_H -#define _MWINPUT_MWINPUTMANAGER_H +#ifndef _MWINPUT_MWINPUTMANAGERIMP_H +#define _MWINPUT_MWINPUTMANAGERIMP_H #include "../mwgui/mode.hpp" #include +#include "../mwbase/inputmanager.hpp" + namespace OEngine { namespace Render @@ -38,7 +40,7 @@ namespace MWInput This class is just an interface. All the messy details are in inputmanager.cpp. */ - struct MWInputManager + struct MWInputManager : public MWBase::InputManager { InputImpl *impl; @@ -48,17 +50,17 @@ namespace MWInput MWGui::WindowManager &_windows, bool debug, OMW::Engine& engine); - ~MWInputManager(); + virtual ~MWInputManager(); - void update(); + virtual void update(); - void changeInputMode(bool guiMode); + virtual void changeInputMode(bool guiMode); - void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); - void setDragDrop(bool dragDrop); + virtual void setDragDrop(bool dragDrop); - void toggleControlSwitch(std::string sw, bool value); + virtual void toggleControlSwitch (const std::string& sw, bool value); }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8719557ca3..590c177097 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -22,12 +22,12 @@ #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" // FIXME #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" #include "../mwgui/window_manager.hpp" // FIXME -#include "../mwinput/inputmanager.hpp" // FIXME #include "shadows.hpp" #include "localmap.hpp" diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 084698c5b5..bd14e7b8dd 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -8,19 +8,16 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwinput/inputmanager.hpp" - #include "interpretercontext.hpp" #include "ref.hpp" -#include - namespace MWScript { namespace Control @@ -41,11 +38,6 @@ namespace MWScript MWBase::Environment::get() .getInputManager() ->toggleControlSwitch(mControl, mEnable); - - if (mEnable) - std::cout << "enable: " << mControl << std::endl; - else - std::cout << "disable: " << mControl << std::endl; } }; diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 8e5897298b..3d14692d9a 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -10,7 +10,6 @@ #include "../mwbase/environment.hpp" #include "../mwgui/window_manager.hpp" -#include "../mwinput/inputmanager.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 327eed9136..131d7865b5 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -15,8 +15,6 @@ #include "../mwgui/window_manager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "locals.hpp" #include "globalscripts.hpp" From 2beaa9d9ae1be8bf9dea6d19bfe1536485550e44 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 11 Aug 2012 23:26:20 +0400 Subject: [PATCH 047/143] Workaround to allow main loop to know Ogre::Root's mQueuedEnd value This change is needed because of changes in exit handling --- libs/openengine/ogre/renderer.cpp | 26 ++++++++++++++++++++++---- libs/openengine/ogre/renderer.hpp | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 4074a1a997..e342f4c5f7 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -24,6 +24,21 @@ using namespace Ogre; using namespace OEngine::Render; +#if defined(__APPLE__) && !defined(__LP64__) + +CustomRoot::CustomRoot(const Ogre::String& pluginFileName, + const Ogre::String& configFileName, + const Ogre::String& logFileName) +: Ogre::Root(pluginFileName, configFileName, logFileName) +{} + +bool CustomRoot::isQueuedEnd() const +{ + return mQueuedEnd; +} + +#endif + void OgreRenderer::cleanup() { delete mFader; @@ -36,7 +51,6 @@ void OgreRenderer::cleanup() void OgreRenderer::start() { #if defined(__APPLE__) && !defined(__LP64__) - bool quit = false; // OSX Carbon Message Pump do { EventRef event = NULL; @@ -54,11 +68,11 @@ void OgreRenderer::start() ReleaseEvent(event); } - if (!Ogre::Root::getSingleton().renderOneFrame()) { - quit = true; + if (!mRoot->renderOneFrame()) { + break; } - } while (!quit); + } while (!mRoot->isQueuedEnd()); #else mRoot->startRendering(); #endif @@ -120,7 +134,11 @@ void OgreRenderer::configure(const std::string &logPath, // Disable logging log->setDebugOutputEnabled(false); +#if defined(__APPLE__) && !defined(__LP64__) + mRoot = new CustomRoot("", "", ""); +#else mRoot = new Root("", "", ""); +#endif #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) loadPlugins(); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 247c8f95a2..9b7003368e 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -27,9 +27,15 @@ #include "OgreTexture.h" #include +#if defined(__APPLE__) && !defined(__LP64__) +#include +#endif + namespace Ogre { +#if !defined(__APPLE__) || defined(__LP64__) class Root; +#endif class RenderWindow; class SceneManager; class Camera; @@ -48,10 +54,25 @@ namespace OEngine std::string fsaa; }; +#if defined(__APPLE__) && !defined(__LP64__) + class CustomRoot : public Ogre::Root { + public: + bool isQueuedEnd() const; + + CustomRoot(const Ogre::String& pluginFileName = "plugins.cfg", + const Ogre::String& configFileName = "ogre.cfg", + const Ogre::String& logFileName = "Ogre.log"); + }; +#endif + class Fader; class OgreRenderer { +#if defined(__APPLE__) && !defined(__LP64__) + CustomRoot *mRoot; +#else Ogre::Root *mRoot; +#endif Ogre::RenderWindow *mWindow; Ogre::SceneManager *mScene; Ogre::Camera *mCamera; From a3b27ae756212eaed6fd2942cf40542e91de3492 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 10:50:03 +0200 Subject: [PATCH 048/143] Issue #107: Moved ClassPoint from window manager to character creation --- apps/openmw/mwgui/charactercreation.cpp | 8 ++++++++ apps/openmw/mwgui/window_manager.hpp | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index fd3a7872e3..2c0b3de85f 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -104,6 +104,14 @@ namespace {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} } } }; + + struct ClassPoint + { + const char *id; + // Specialization points to match, in order: Stealth, Combat, Magic + // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz + unsigned int points[3]; + }; } using namespace MWGui; diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 3653615a69..69a9717dd2 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -79,14 +79,6 @@ namespace MWGui class AlchemyWindow; class SpellWindow; - struct ClassPoint - { - const char *id; - // Specialization points to match, in order: Stealth, Combat, Magic - // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz - unsigned int points[3]; - }; - class WindowManager { public: From dbac3d2e5f7da94bef0baf014e1ec6f0e7dcb658 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 10:52:29 +0200 Subject: [PATCH 049/143] Issue #107: fixing WindowManager::removeDialog --- apps/openmw/mwgui/window_manager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index b4eaaacf49..ca5223a186 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -424,7 +424,6 @@ void WindowManager::updateSkillArea() void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) { - assert(dialog); if (!dialog) return; dialog->setVisible(false); From 020fde70b80e58cfd9feb7419d5d5d782a1d372e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 11:10:17 +0200 Subject: [PATCH 050/143] Issue #107: moving function implementations from hpp to cpp file --- apps/openmw/mwgui/window_manager.cpp | 76 ++++++++++++++++++++++++++++ apps/openmw/mwgui/window_manager.hpp | 71 ++++++++------------------ 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index ca5223a186..38ea74a57a 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -743,3 +743,79 @@ void WindowManager::executeInConsole (const std::string& path) { mConsole->executeFile (path); } + +void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) +{ + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; +} + +MyGUI::Gui* WindowManager::getGui() const { return mGui; } + +MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } +MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } +MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } +MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } +MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } +MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } +MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } +MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } +MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } +MWGui::Console* WindowManager::getConsole() { return mConsole; } + +bool WindowManager::isAllowed (GuiWindow wnd) const +{ + return mAllowed & wnd; +} + +void WindowManager::allow (GuiWindow wnd) +{ + mAllowed = (GuiWindow)(mAllowed | wnd); + updateVisible(); +} + +void WindowManager::disallowAll() +{ + mAllowed = GW_None; + updateVisible(); +} + +void WindowManager::toggleVisible (GuiWindow wnd) +{ + mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); + updateVisible(); +} + +bool WindowManager::isGuiMode() const +{ + return !mGuiModes.empty(); +} + +MWGui::GuiMode WindowManager::getMode() const +{ + if (mGuiModes.empty()) + throw std::runtime_error ("getMode() called, but there is no active mode"); + + return mGuiModes.back(); +} + +std::map > WindowManager::getPlayerSkillValues() +{ + return mPlayerSkillValues; +} + +std::map > WindowManager::getPlayerAttributeValues() +{ + return mPlayerAttributes; +} + +WindowManager::SkillList WindowManager::getPlayerMinorSkills() +{ + return mPlayerMinorSkills; +} + +WindowManager::SkillList WindowManager::getPlayerMajorSkills() +{ + return mPlayerMajorSkills; +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 69a9717dd2..60fc63c8cf 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -100,61 +100,34 @@ namespace MWGui void popGuiMode(); void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack - GuiMode getMode() const - { - if (mGuiModes.empty()) - throw std::runtime_error ("getMode() called, but there is no active mode"); - return mGuiModes.back(); - } + GuiMode getMode() const; - bool isGuiMode() const { return !mGuiModes.empty(); } + bool isGuiMode() const; - void toggleVisible(GuiWindow wnd) - { - mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); - updateVisible(); - } + void toggleVisible(GuiWindow wnd); // Disallow all inventory mode windows - void disallowAll() - { - mAllowed = GW_None; - updateVisible(); - } + void disallowAll(); // Allow one or more windows - void allow(GuiWindow wnd) - { - mAllowed = (GuiWindow)(mAllowed | wnd); - updateVisible(); - } + void allow(GuiWindow wnd); - bool isAllowed(GuiWindow wnd) const - { - return mAllowed & wnd; - } + bool isAllowed(GuiWindow wnd) const; - MWGui::DialogueWindow* getDialogueWindow() {return mDialogueWindow;} - MWGui::ContainerWindow* getContainerWindow() {return mContainerWindow;} - MWGui::InventoryWindow* getInventoryWindow() {return mInventoryWindow;} - MWGui::BookWindow* getBookWindow() {return mBookWindow;} - MWGui::ScrollWindow* getScrollWindow() {return mScrollWindow;} - MWGui::CountDialog* getCountDialog() {return mCountDialog;} - MWGui::ConfirmationDialog* getConfirmationDialog() {return mConfirmationDialog;} - MWGui::TradeWindow* getTradeWindow() {return mTradeWindow;} - MWGui::SpellWindow* getSpellWindow() {return mSpellWindow;} - MWGui::Console* getConsole() {return mConsole;} + MWGui::DialogueWindow* getDialogueWindow(); + MWGui::ContainerWindow* getContainerWindow(); + MWGui::InventoryWindow* getInventoryWindow(); + MWGui::BookWindow* getBookWindow(); + MWGui::ScrollWindow* getScrollWindow(); + MWGui::CountDialog* getCountDialog(); + MWGui::ConfirmationDialog* getConfirmationDialog(); + MWGui::TradeWindow* getTradeWindow(); + MWGui::SpellWindow* getSpellWindow(); + MWGui::Console* getConsole(); - MyGUI::Gui* getGui() const { return mGui; } + MyGUI::Gui* getGui() const; - void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) - { - mFPS = fps; - mTriangleCount = triangleCount; - mBatchCount = batchCount; - } - -// MWMechanics::DynamicStat getValue(const std::string& id); + void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); @@ -211,10 +184,10 @@ namespace MWGui void onFrame (float frameDuration); - std::map > getPlayerSkillValues() { return mPlayerSkillValues; } - std::map > getPlayerAttributeValues() { return mPlayerAttributes; } - SkillList getPlayerMinorSkills() { return mPlayerMinorSkills; } - SkillList getPlayerMajorSkills() { return mPlayerMajorSkills; } + std::map > getPlayerSkillValues(); + std::map > getPlayerAttributeValues(); + SkillList getPlayerMinorSkills(); + SkillList getPlayerMajorSkills(); /** * Fetches a GMST string from the store, if there is no setting with the given From e0ba7cf952104664e08cfcca9f2e025eb2ebe4d3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 13:46:14 +0200 Subject: [PATCH 051/143] Issue #107: Fixing up the window manager interface --- apps/openmw/mwgui/charactercreation.cpp | 12 ++++++------ apps/openmw/mwgui/window_manager.cpp | 12 +++++++----- apps/openmw/mwgui/window_manager.hpp | 10 +++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 2c0b3de85f..5e0df5cbd9 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -268,20 +268,20 @@ void CharacterCreation::spawnDialog(const char id) mReviewDialog->setFatigue(mPlayerFatigue); { - std::map > attributes = mWM->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); + std::map > attributes = mWM->getPlayerAttributeValues(); + for (std::map >::iterator it = attributes.begin(); it != attributes.end(); ++it) { - mReviewDialog->setAttribute(it->first, it->second); + mReviewDialog->setAttribute(static_cast (it->first), it->second); } } { - std::map > skills = mWM->getPlayerSkillValues(); - for (std::map >::iterator it = skills.begin(); + std::map > skills = mWM->getPlayerSkillValues(); + for (std::map >::iterator it = skills.begin(); it != skills.end(); ++it) { - mReviewDialog->setSkillValue(it->first, it->second); + mReviewDialog->setSkillValue(static_cast (it->first), it->second); } mReviewDialog->configureSkills(mWM->getPlayerMajorSkills(), mWM->getPlayerMinorSkills()); } diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 38ea74a57a..94cad28186 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -338,10 +338,12 @@ void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) +void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) { - mStatsWindow->setValue(parSkill, value); - mCharGen->setValue(parSkill, value); + /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we + /// allow custom skills. + mStatsWindow->setValue(static_cast (parSkill), value); + mCharGen->setValue(static_cast (parSkill), value); mPlayerSkillValues[parSkill] = value; } @@ -800,12 +802,12 @@ MWGui::GuiMode WindowManager::getMode() const return mGuiModes.back(); } -std::map > WindowManager::getPlayerSkillValues() +std::map > WindowManager::getPlayerSkillValues() { return mPlayerSkillValues; } -std::map > WindowManager::getPlayerAttributeValues() +std::map > WindowManager::getPlayerAttributeValues() { return mPlayerAttributes; } diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 60fc63c8cf..de90037b9f 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -131,7 +131,7 @@ namespace MWGui ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); + void setValue (int parSkill, const MWMechanics::Stat& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); @@ -184,8 +184,8 @@ namespace MWGui void onFrame (float frameDuration); - std::map > getPlayerSkillValues(); - std::map > getPlayerAttributeValues(); + std::map > getPlayerSkillValues(); + std::map > getPlayerAttributeValues(); SkillList getPlayerMinorSkills(); SkillList getPlayerMajorSkills(); @@ -233,9 +233,9 @@ namespace MWGui ESM::Class mPlayerClass; std::string mPlayerName; std::string mPlayerRaceId; - std::map > mPlayerAttributes; + std::map > mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; + std::map > mPlayerSkillValues; MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; From 4ca3cb81d49e2ce68d56c6d9fe6731780c6f3107 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:07:48 +0200 Subject: [PATCH 052/143] Issue #107: some include clean up --- apps/openmw/mwgui/window_manager.cpp | 37 ++++++++++++++++------------ apps/openmw/mwgui/window_manager.hpp | 21 ++++++++-------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 94cad28186..369eae089a 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -1,4 +1,25 @@ #include "window_manager.hpp" + +#include +#include + +#include "MyGUI_UString.h" + +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/inputmanager.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/cellstore.hpp" + +#include "console.hpp" +#include "journalwindow.hpp" +#include "charactercreation.hpp" #include "text_input.hpp" #include "review.hpp" #include "dialogue.hpp" @@ -21,22 +42,6 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/inputmanager.hpp" - -#include "../mwworld/ptr.hpp" -#include "../mwworld/cellstore.hpp" - -#include "console.hpp" -#include "journalwindow.hpp" -#include "charactercreation.hpp" - -#include - -#include -#include - using namespace MWGui; WindowManager::WindowManager( diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index de90037b9f..59a6ab3950 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -10,12 +10,11 @@ this class. **/ -#include "MyGUI_UString.h" +#include +#include #include #include -#include -#include #include "../mwmechanics/stat.hpp" @@ -23,8 +22,9 @@ namespace MyGUI { - class Gui; - class Widget; + class Gui; + class Widget; + class UString; } namespace Compiler @@ -38,16 +38,17 @@ namespace MWWorld class CellStore; } -namespace MWMechanics -{ - class MechanicsManager; -} - namespace OEngine { namespace GUI { class Layout; + class MyGUIManager; + } + + namespace Render + { + class OgreRenderer; } } From f37d3cd3c9c607ee0932c740d5ebbd571aa1966b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:09:55 +0200 Subject: [PATCH 053/143] Issue #107: added a few todo comments --- apps/openmw/mwgui/window_manager.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 59a6ab3950..fccaa83473 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -115,6 +115,7 @@ namespace MWGui bool isAllowed(GuiWindow wnd) const; + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world MWGui::DialogueWindow* getDialogueWindow(); MWGui::ContainerWindow* getContainerWindow(); MWGui::InventoryWindow* getInventoryWindow(); @@ -185,6 +186,7 @@ namespace MWGui void onFrame (float frameDuration); + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. std::map > getPlayerSkillValues(); std::map > getPlayerAttributeValues(); SkillList getPlayerMinorSkills(); @@ -230,6 +232,7 @@ namespace MWGui CharacterCreation* mCharGen; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; std::string mPlayerName; From 484cce12a85e320b29e818341afe05ace8410830 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:36:46 +0200 Subject: [PATCH 054/143] Issue #107: removed redundant getStore function from window manager --- apps/openmw/mwdialogue/journalimp.cpp | 2 ++ apps/openmw/mwgui/birth.cpp | 15 ++++++++++----- apps/openmw/mwgui/class.cpp | 10 ++++++---- apps/openmw/mwgui/race.cpp | 9 ++++++--- apps/openmw/mwgui/review.cpp | 7 +++++-- apps/openmw/mwgui/stats_window.cpp | 14 +++++++------- apps/openmw/mwgui/widgets.cpp | 17 +++++++++++------ apps/openmw/mwgui/window_manager.cpp | 5 ----- apps/openmw/mwgui/window_manager.hpp | 4 +--- apps/openmw/mwrender/localmap.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwscript/interpretercontext.cpp | 1 + apps/openmw/mwworld/scene.cpp | 2 ++ 13 files changed, 54 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 052b921943..dc322cade8 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -1,6 +1,8 @@ #include "journalimp.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 0f1a04c27b..023702c8b8 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -1,11 +1,16 @@ #include "birth.hpp" -#include "window_manager.hpp" -#include "widgets.hpp" -#include "components/esm_store/store.hpp" #include #include +#include "components/esm_store/store.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" +#include "widgets.hpp" + using namespace MWGui; using namespace Widgets; @@ -114,7 +119,7 @@ void BirthDialog::updateBirths() { mBirthList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.birthSigns.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.birthSigns.list.end(); @@ -144,7 +149,7 @@ void BirthDialog::updateSpells() const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::BirthSign *birth = store.birthSigns.find(mCurrentBirthId); std::string texturePath = std::string("textures\\") + birth->texture; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 48e01eb1e9..e5b3e261d7 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -7,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "tooltips.hpp" @@ -57,8 +60,7 @@ void GenerateClassResultDialog::setClassId(const std::string &classId) { mCurrentClassId = classId; mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); - const ESMS::ESMStore &store = mWindowManager.getStore(); - mClassName->setCaption(store.classes.find(mCurrentClassId)->name); + mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().classes.find(mCurrentClassId)->name); } // widget controls @@ -193,7 +195,7 @@ void PickClassDialog::updateClasses() { mClassList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.classes.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.classes.list.end(); @@ -217,7 +219,7 @@ void PickClassDialog::updateStats() { if (mCurrentClassId.empty()) return; - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Class *klass = store.classes.search(mCurrentClassId); if (!klass) return; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index f90a9eddec..62434cb91a 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -8,6 +8,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -209,7 +212,7 @@ void RaceDialog::updateRaces() { mRaceList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.races.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.races.list.end(); @@ -243,7 +246,7 @@ void RaceDialog::updateSkills() const int lineHeight = 18; MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.races.find(mCurrentRaceId); int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? for (int i = 0; i < count; ++i) @@ -281,7 +284,7 @@ void RaceDialog::updateSpellPowers() const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.races.find(mCurrentRaceId); std::vector::const_iterator it = race->powers.list.begin(); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 7061f65c17..997899c526 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -7,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -138,7 +141,7 @@ void ReviewDialog::setPlayerName(const std::string &name) void ReviewDialog::setRace(const std::string &raceId) { mRaceId = raceId; - const ESM::Race *race = mWindowManager.getStore().races.search(mRaceId); + const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().races.search(mRaceId); if (race) { ToolTips::createRaceToolTip(mRaceWidget, race); @@ -156,7 +159,7 @@ void ReviewDialog::setClass(const ESM::Class& class_) void ReviewDialog::setBirthSign(const std::string& signId) { mBirthSignId = signId; - const ESM::BirthSign *sign = mWindowManager.getStore().birthSigns.search(mBirthSignId); + const ESM::BirthSign *sign = MWBase::Environment::get().getWorld()->getStore().birthSigns.search(mBirthSignId); if (sign) { mBirthSignWidget->setCaption(sign->name); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 3f5162bfba..4ee56abc7f 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -57,7 +57,7 @@ StatsWindow::StatsWindow (WindowManager& parWindowManager) { 0, 0 } }; - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (int i=0; names[i][0]; ++i) { setText (names[i][0], store.gameSettings.find (names[i][1])->str); @@ -383,12 +383,12 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, float modified = stat.getModified(); int progressPercent = (modified - float(static_cast(modified))) * 100; - const ESM::Skill* skill = mWindowManager.getStore().skills.search(skillId); + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().skills.search(skillId); assert(skill); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; - const ESM::Attribute* attr = mWindowManager.getStore().attributes.search(skill->data.attribute); + const ESM::Attribute* attr = MWBase::Environment::get().getWorld()->getStore().attributes.search(skill->data.attribute); assert(attr); std::string state = "normal"; @@ -443,7 +443,7 @@ void StatsWindow::updateSkillArea() if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); // race tooltip const ESM::Race* playerRace = store.races.find (MWBase::Environment::get().getWorld()->getPlayer().getRace()); @@ -485,8 +485,8 @@ void StatsWindow::updateSkillArea() text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->ranks[it->second+1]; ESM::RankData rankData = faction->data.rankData[it->second+1]; - const ESM::Attribute* attr1 = mWindowManager.getStore().attributes.search(faction->data.attribute1); - const ESM::Attribute* attr2 = mWindowManager.getStore().attributes.search(faction->data.attribute2); + const ESM::Attribute* attr1 = MWBase::Environment::get().getWorld()->getStore().attributes.search(faction->data.attribute1); + const ESM::Attribute* attr2 = MWBase::Environment::get().getWorld()->getStore().attributes.search(faction->data.attribute2); assert(attr1 && attr2); text += "\n#BF9959#{" + attr1->name + "}: " + boost::lexical_cast(rankData.attribute1) @@ -496,7 +496,7 @@ void StatsWindow::updateSkillArea() text += "\n#BF9959"; for (int i=0; i<6; ++i) { - const ESM::Skill* skill = mWindowManager.getStore().skills.search(faction->data.skillID[i]); + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().skills.search(faction->data.skillID[i]); assert(skill); text += "#{"+ESM::Skill::sSkillNameIds[faction->data.skillID[i]]+"}"; if (i<5) diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index c54ac6e389..1529387071 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -1,9 +1,14 @@ #include "widgets.hpp" -#include "window_manager.hpp" -#include "components/esm_store/store.hpp" #include +#include "components/esm_store/store.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" + #undef min #undef max @@ -190,7 +195,7 @@ void MWAttribute::initialiseOverride() assignWidget(mAttributeNameWidget, "StatName"); assignWidget(mAttributeValueWidget, "StatValue"); - + MyGUI::ButtonPtr button; assignWidget(button, "StatNameButton"); if (button) @@ -224,7 +229,7 @@ void MWSpell::setSpellId(const std::string &spellId) void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, int flags) { - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.spells.search(mId); MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); @@ -255,7 +260,7 @@ void MWSpell::updateWidgets() { if (mSpellNameWidget && mWindowManager) { - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.spells.search(mId); if (spell) static_cast(mSpellNameWidget)->setCaption(spell->name); @@ -384,7 +389,7 @@ void MWSpellEffect::updateWidgets() if (!mWindowManager) return; - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::MagicEffect *magicEffect = store.magicEffects.search(mEffectParams.mEffectID); if (!magicEffect) return; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 369eae089a..eada0c88a2 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -497,11 +497,6 @@ void WindowManager::onFrame (float frameDuration) mConsole->checkReferenceAvailable(); } -const ESMS::ESMStore& WindowManager::getStore() const -{ - return MWBase::Environment::get().getWorld()->getStore(); -} - void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { if (!(cell->cell->data.flags & ESM::Cell::Interior)) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index fccaa83473..224c2a9203 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -13,8 +13,8 @@ #include #include -#include #include +#include #include "../mwmechanics/stat.hpp" @@ -201,8 +201,6 @@ namespace MWGui */ const std::string &getGameSettingString(const std::string &id, const std::string &default_); - const ESMS::ESMStore& getStore() const; - void processChangedSettings(const Settings::CategorySettingVector& changed); void executeInConsole (const std::string& path); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 962f19a57f..b5fa135e0d 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 590c177097..d039418ba3 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 131d7865b5..0ba04fb381 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 9318fd6f13..1c7093e60e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,5 +1,7 @@ #include "scene.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" From 35a64edf2acd3d588aa114ee1049c04bc4a5ddc8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 16:07:04 +0200 Subject: [PATCH 055/143] Issue #107: removed a template from the window manager --- apps/openmw/mwgui/charactercreation.cpp | 83 ++++++++++++++----------- apps/openmw/mwgui/class.cpp | 31 ++++----- apps/openmw/mwgui/window_manager.hpp | 10 --- 3 files changed, 58 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 5e0df5cbd9..72394de80d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -187,8 +187,8 @@ void CharacterCreation::spawnDialog(const char id) switch (id) { case GM_Name: - if(mNameDialog) - mWM->removeDialog(mNameDialog); + mWM->removeDialog(mNameDialog); + mNameDialog = 0; mNameDialog = new TextInputDialog(*mWM); mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name")); mNameDialog->setTextInput(mPlayerName); @@ -198,8 +198,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Race: - if (mRaceDialog) - mWM->removeDialog(mRaceDialog); + mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; mRaceDialog = new RaceDialog(*mWM); mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); mRaceDialog->setRaceId(mPlayerRaceId); @@ -209,16 +209,16 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Class: - if (mClassChoiceDialog) - mWM->removeDialog(mClassChoiceDialog); + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; mClassChoiceDialog = new ClassChoiceDialog(*mWM); mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); mClassChoiceDialog->open(); break; case GM_ClassPick: - if (mPickClassDialog) - mWM->removeDialog(mPickClassDialog); + mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; mPickClassDialog = new PickClassDialog(*mWM); mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mPickClassDialog->setClassId(mPlayerClass.name); @@ -228,8 +228,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Birth: - if (mBirthSignDialog) - mWM->removeDialog(mBirthSignDialog); + mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; mBirthSignDialog = new BirthDialog(*mWM); mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); @@ -238,8 +238,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_ClassCreate: - if (mCreateClassDialog) - mWM->removeDialog(mCreateClassDialog); + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; mCreateClassDialog = new CreateClassDialog(*mWM); mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); @@ -255,8 +255,8 @@ void CharacterCreation::spawnDialog(const char id) showClassQuestionDialog(); break; case GM_Review: - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mReviewDialog = new ReviewDialog(*mWM); mReviewDialog->setPlayerName(mPlayerName); mReviewDialog->setRace(mPlayerRaceId); @@ -311,24 +311,24 @@ void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& v void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mWM->popGuiMode(); } void CharacterCreation::onReviewDialogBack() { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mWM->pushGuiMode(GM_Birth); } void CharacterCreation::onReviewActivateDialog(int parDialog) { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mCreationStage = CSE_ReviewNext; mWM->popGuiMode(); @@ -363,6 +363,7 @@ void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) mWM->setPlayerClass(mPlayerClass); } mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } //TODO This bit gets repeated a few times; wrap it in a function @@ -391,6 +392,7 @@ void CharacterCreation::onPickClassDialogBack() if (!classId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } mWM->popGuiMode(); @@ -399,10 +401,8 @@ void CharacterCreation::onPickClassDialogBack() void CharacterCreation::onClassChoice(int _index) { - if (mClassChoiceDialog) - { - mWM->removeDialog(mClassChoiceDialog); - } + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; mWM->popGuiMode(); @@ -432,6 +432,7 @@ void CharacterCreation::onNameDialogDone(WindowBase* parWindow) mWM->setValue("name", mPlayerName); MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); mWM->removeDialog(mNameDialog); + mNameDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -459,6 +460,7 @@ void CharacterCreation::onRaceDialogBack() if (!mPlayerRaceId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; } mWM->popGuiMode(); @@ -474,6 +476,7 @@ void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) if (!mPlayerRaceId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -501,6 +504,7 @@ void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) if (!mPlayerBirthSignId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; } if (mCreationStage >= CSE_BirthSignChosen) @@ -521,6 +525,7 @@ void CharacterCreation::onBirthSignDialogBack() { MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; } mWM->popGuiMode(); @@ -556,6 +561,7 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) mWM->setPlayerClass(klass); mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -577,8 +583,8 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) void CharacterCreation::onCreateClassDialogBack() { - if (mCreateClassDialog) - mWM->removeDialog(mCreateClassDialog); + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; mWM->popGuiMode(); mWM->pushGuiMode(GM_Class); @@ -588,8 +594,9 @@ void CharacterCreation::onClassQuestionChosen(int _index) { MWBase::Environment::get().getSoundManager()->stopSay(); - if (mGenerateClassQuestionDialog) - mWM->removeDialog(mGenerateClassQuestionDialog); + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + if (_index < 0 || _index >= 3) { mWM->popGuiMode(); @@ -666,8 +673,9 @@ void CharacterCreation::showClassQuestionDialog() } } - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); mGenerateClassResultDialog->setClassId(mGenerateClass); mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); @@ -683,8 +691,9 @@ void CharacterCreation::showClassQuestionDialog() return; } - if (mGenerateClassQuestionDialog) - mWM->removeDialog(mGenerateClassQuestionDialog); + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); InfoBoxDialog::ButtonList buttons; @@ -704,8 +713,9 @@ void CharacterCreation::onGenerateClassBack() if(mCreationStage < CSE_ClassChosen) mCreationStage = CSE_ClassChosen; - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); mWM->popGuiMode(); @@ -714,8 +724,9 @@ void CharacterCreation::onGenerateClassBack() void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) { - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); const ESM::Class *klass = MWBase::Environment::get().getWorld()->getStore().classes.find(mGenerateClass); mPlayerClass = *klass; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index e5b3e261d7..7e3194d6be 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -558,26 +558,17 @@ void CreateClassDialog::open() void CreateClassDialog::onDialogCancel() { - if (mSpecDialog) - { - mWindowManager.removeDialog(mSpecDialog); - mSpecDialog = 0; - } - if (mAttribDialog) - { - mWindowManager.removeDialog(mAttribDialog); - mAttribDialog = 0; - } - if (mSkillDialog) - { - mWindowManager.removeDialog(mSkillDialog); - mSkillDialog = 0; - } - if (mDescDialog) - { - mWindowManager.removeDialog(mDescDialog); - mDescDialog = 0; - } + mWindowManager.removeDialog(mSpecDialog); + mSpecDialog = 0; + + mWindowManager.removeDialog(mAttribDialog); + mAttribDialog = 0; + + mWindowManager.removeDialog(mSkillDialog); + mSkillDialog = 0; + + mWindowManager.removeDialog(mDescDialog); + mDescDialog = 0; } void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 224c2a9203..db3945e55d 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -177,8 +177,6 @@ namespace MWGui void unsetSelectedSpell(); void unsetSelectedWeapon(); - template - void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. void messageBox (const std::string& message, const std::vector& buttons); @@ -271,14 +269,6 @@ namespace MWGui */ void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); }; - - template - void WindowManager::removeDialog(T*& dialog) - { - OEngine::GUI::Layout *d = static_cast(dialog); - removeDialog(d); - dialog = 0; - } } #endif From b6f427bf5ef9848bfc4a3e190890e3a33ff545d7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 17:20:46 +0200 Subject: [PATCH 056/143] fix OpenMW not exiting when the window is closed (alt f4 etc) --- apps/openmw/mwrender/renderingmanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7180fea667..2ff18fbeb5 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -775,6 +775,7 @@ void RenderingManager::windowResized(Ogre::RenderWindow* rw) void RenderingManager::windowClosed(Ogre::RenderWindow* rw) { + Ogre::Root::getSingleton ().queueEndRendering (); } bool RenderingManager::waterShaderSupported() From 6534c2a55a76a269a2c3f9c498268ba91b2ce047 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 18:11:09 +0200 Subject: [PATCH 057/143] Issue #107: WindowManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 9 +- apps/openmw/mwbase/environment.hpp | 12 +- apps/openmw/mwbase/windowmanager.hpp | 210 ++++++++++++++ apps/openmw/mwclass/activator.cpp | 5 +- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/misc.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 7 +- apps/openmw/mwdialogue/journalimp.cpp | 2 +- apps/openmw/mwgui/alchemywindow.cpp | 5 +- apps/openmw/mwgui/alchemywindow.hpp | 2 +- apps/openmw/mwgui/birth.cpp | 4 +- apps/openmw/mwgui/birth.hpp | 5 +- apps/openmw/mwgui/bookwindow.cpp | 4 +- apps/openmw/mwgui/bookwindow.hpp | 3 +- apps/openmw/mwgui/charactercreation.cpp | 2 +- apps/openmw/mwgui/charactercreation.hpp | 8 +- apps/openmw/mwgui/class.cpp | 20 +- apps/openmw/mwgui/class.hpp | 21 +- apps/openmw/mwgui/confirmationdialog.cpp | 2 +- apps/openmw/mwgui/confirmationdialog.hpp | 2 +- apps/openmw/mwgui/container.cpp | 4 +- apps/openmw/mwgui/container.hpp | 2 +- apps/openmw/mwgui/countdialog.cpp | 2 +- apps/openmw/mwgui/countdialog.hpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 4 +- apps/openmw/mwgui/dialogue.hpp | 2 +- apps/openmw/mwgui/dialogue_history.cpp | 5 +- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 4 +- apps/openmw/mwgui/inventorywindow.hpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 5 +- apps/openmw/mwgui/journalwindow.hpp | 4 +- apps/openmw/mwgui/mainmenu.cpp | 4 +- apps/openmw/mwgui/map_window.cpp | 5 +- apps/openmw/mwgui/map_window.hpp | 4 +- apps/openmw/mwgui/messagebox.cpp | 2 +- apps/openmw/mwgui/messagebox.hpp | 28 +- apps/openmw/mwgui/race.cpp | 4 +- apps/openmw/mwgui/race.hpp | 2 +- apps/openmw/mwgui/review.cpp | 4 +- apps/openmw/mwgui/review.hpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 4 +- apps/openmw/mwgui/scrollwindow.hpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 4 +- apps/openmw/mwgui/settingswindow.hpp | 3 +- apps/openmw/mwgui/spellwindow.cpp | 4 +- apps/openmw/mwgui/spellwindow.hpp | 2 +- apps/openmw/mwgui/stats_window.cpp | 4 +- apps/openmw/mwgui/stats_window.hpp | 3 +- apps/openmw/mwgui/text_input.cpp | 5 +- apps/openmw/mwgui/text_input.hpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 4 +- apps/openmw/mwgui/tooltips.hpp | 8 +- apps/openmw/mwgui/tradewindow.cpp | 4 +- apps/openmw/mwgui/tradewindow.hpp | 2 +- apps/openmw/mwgui/widgets.cpp | 3 +- apps/openmw/mwgui/widgets.hpp | 33 ++- apps/openmw/mwgui/window_base.cpp | 5 +- apps/openmw/mwgui/window_base.hpp | 11 +- apps/openmw/mwgui/window_manager.hpp | 274 ------------------ apps/openmw/mwgui/window_pinnable_base.cpp | 6 +- apps/openmw/mwgui/window_pinnable_base.hpp | 5 +- ...indow_manager.cpp => windowmanagerimp.cpp} | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 254 ++++++++++++++++ apps/openmw/mwinput/inputmanagerimp.cpp | 8 +- apps/openmw/mwinput/inputmanagerimp.hpp | 4 +- .../mwmechanics/mechanicsmanagerimp.cpp | 7 +- apps/openmw/mwrender/localmap.cpp | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 3 +- apps/openmw/mwscript/guiextensions.cpp | 3 +- apps/openmw/mwscript/interpretercontext.cpp | 3 +- apps/openmw/mwworld/actionalchemy.cpp | 2 +- apps/openmw/mwworld/actionopen.cpp | 3 +- apps/openmw/mwworld/actionread.cpp | 3 +- apps/openmw/mwworld/actiontake.cpp | 3 +- apps/openmw/mwworld/scene.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 3 +- 93 files changed, 662 insertions(+), 484 deletions(-) create mode 100644 apps/openmw/mwbase/windowmanager.hpp delete mode 100644 apps/openmw/mwgui/window_manager.hpp rename apps/openmw/mwgui/{window_manager.cpp => windowmanagerimp.cpp} (99%) create mode 100644 apps/openmw/mwgui/windowmanagerimp.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 7bd7f1882e..02fe0b72c7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwinput ) add_openmw_dir (mwgui - text_input widgets race class birth review window_manager console dialogue + text_input widgets race class birth review windowmanagerimp console dialogue dialogue_history window_base stats_window messagebox journalwindow charactercreation map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow @@ -66,7 +66,7 @@ add_openmw_dir (mwmechanics add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager - inputmanager + inputmanager windowmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c484c22ea2..75835120fd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -15,7 +15,7 @@ #include "mwinput/inputmanagerimp.hpp" -#include "mwgui/window_manager.hpp" +#include "mwgui/windowmanagerimp.hpp" #include "mwgui/cursorreplace.hpp" #include "mwscript/scriptmanagerimp.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 28e71fcf0c..8d786db910 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -10,6 +10,7 @@ #include "soundmanager.hpp" #include "mechanicsmanager.hpp" #include "inputmanager.hpp" +#include "windowmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -42,7 +43,7 @@ void MWBase::Environment::setScriptManager (ScriptManager *scriptManager) mScriptManager = scriptManager; } -void MWBase::Environment::setWindowManager (MWGui::WindowManager *windowManager) +void MWBase::Environment::setWindowManager (WindowManager *windowManager) { mWindowManager = windowManager; } @@ -90,7 +91,7 @@ MWBase::ScriptManager *MWBase::Environment::getScriptManager() const return mScriptManager; } -MWGui::WindowManager *MWBase::Environment::getWindowManager() const +MWBase::WindowManager *MWBase::Environment::getWindowManager() const { assert (mWindowManager); return mWindowManager; @@ -145,6 +146,10 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; +/// \todo Re-enable (currently throwing an exception) +// delete mWindowManager; + mWindowManager = 0; + delete mWorld; mWorld = 0; } diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 4a21b57a4e..a80e7ef870 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,11 +1,6 @@ #ifndef GAME_BASE_INVIRONMENT_H #define GAME_BASE_INVIRONMENT_H -namespace MWGui -{ - class WindowManager; -} - namespace MWBase { class World; @@ -15,6 +10,7 @@ namespace MWBase class SoundManager; class MechanicsManager; class InputManager; + class WindowManager; /// \brief Central hub for mw-subsystems /// @@ -29,7 +25,7 @@ namespace MWBase World *mWorld; SoundManager *mSoundManager; ScriptManager *mScriptManager; - MWGui::WindowManager *mWindowManager; + WindowManager *mWindowManager; MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; @@ -54,7 +50,7 @@ namespace MWBase void setScriptManager (MWBase::ScriptManager *scriptManager); - void setWindowManager (MWGui::WindowManager *windowManager); + void setWindowManager (WindowManager *windowManager); void setMechanicsManager (MechanicsManager *mechanicsManager); @@ -73,7 +69,7 @@ namespace MWBase ScriptManager *getScriptManager() const; - MWGui::WindowManager *getWindowManager() const; + WindowManager *getWindowManager() const; MechanicsManager *getMechanicsManager() const; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp new file mode 100644 index 0000000000..931353a900 --- /dev/null +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -0,0 +1,210 @@ +#ifndef GAME_MWBASE_WINDOWMANAGER_H +#define GAME_MWBASE_WINDOWMANAGER_H + +#include +#include +#include + +#include + +#include "../mwmechanics/stat.hpp" + +#include "../mwgui/mode.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; + class UString; +} + +namespace OEngine +{ + namespace GUI + { + class Layout; + } +} + +namespace ESM +{ + struct Class; +} + +namespace MWWorld +{ + class CellStore; + class Ptr; +} + +namespace MWGui +{ + class Console; + class SpellWindow; + class TradeWindow; + class ConfirmationDialog; + class CountDialog; + class ScrollWindow; + class BookWindow; + class InventoryWindow; + class ContainerWindow; + class DialogueWindow; +} + +namespace MWBase +{ + /// \brief Interface for widnow manager (implemented in MWGui) + class WindowManager + { + WindowManager (const WindowManager&); + ///< not implemented + + WindowManager& operator= (const WindowManager&); + ///< not implemented + + public: + + typedef std::vector SkillList; + + WindowManager() {} + + virtual ~WindowManager() {} + + /** + * Should be called each frame to update windows/gui elements. + * This could mean updating sizes of gui elements or opening + * new dialogs. + */ + virtual void update() = 0; + + virtual void pushGuiMode (MWGui::GuiMode mode) = 0; + virtual void popGuiMode() = 0; + + virtual void removeGuiMode (MWGui::GuiMode mode) = 0; + ///< can be anywhere in the stack + + virtual MWGui::GuiMode getMode() const = 0; + + virtual bool isGuiMode() const = 0; + + virtual void toggleVisible (MWGui::GuiWindow wnd) = 0; + + /// Disallow all inventory mode windows + virtual void disallowAll() = 0; + + /// Allow one or more windows + virtual void allow (MWGui::GuiWindow wnd) = 0; + + virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; + + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world + virtual MWGui::DialogueWindow* getDialogueWindow() = 0; + virtual MWGui::ContainerWindow* getContainerWindow() = 0; + virtual MWGui::InventoryWindow* getInventoryWindow() = 0; + virtual MWGui::BookWindow* getBookWindow() = 0; + virtual MWGui::ScrollWindow* getScrollWindow() = 0; + virtual MWGui::CountDialog* getCountDialog() = 0; + virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; + virtual MWGui::TradeWindow* getTradeWindow() = 0; + virtual MWGui::SpellWindow* getSpellWindow() = 0; + virtual MWGui::Console* getConsole() = 0; + + virtual MyGUI::Gui* getGui() const = 0; + + virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) = 0; + + /// Set value for the given ID. + virtual void setValue (const std::string& id, const MWMechanics::Stat& value) = 0; + virtual void setValue (int parSkill, const MWMechanics::Stat& value) = 0; + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; + virtual void setValue (const std::string& id, const std::string& value) = 0; + virtual void setValue (const std::string& id, int value) = 0; + + virtual void setPlayerClass (const ESM::Class &class_) = 0; + ///< set current class of player + + virtual void configureSkills (const SkillList& major, const SkillList& minor) = 0; + ///< configure skill groups, each set contains the skill ID for that group. + + virtual void setReputation (int reputation) = 0; + ///< set the current reputation value + + virtual void setBounty (int bounty) = 0; + ///< set the current bounty value + + virtual void updateSkillArea() = 0; + ///< update display of skills, factions, birth sign, reputation and bounty + + virtual void changeCell(MWWorld::CellStore* cell) = 0; + ///< change the active cell + + virtual void setPlayerPos(const float x, const float y) = 0; + ///< set player position in map space + + virtual void setPlayerDir(const float x, const float y) = 0; + ///< set player view direction in map space + + virtual void setFocusObject(const MWWorld::Ptr& focus) = 0; + virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0; + + virtual void setMouseVisible(bool visible) = 0; + virtual void getMousePosition(int &x, int &y) = 0; + virtual void getMousePosition(float &x, float &y) = 0; + virtual void setDragDrop(bool dragDrop) = 0; + virtual bool getWorldMouseOver() = 0; + + virtual void toggleFogOfWar() = 0; + + virtual void toggleFullHelp() = 0; + ///< show extra info in item tooltips (owner, script) + + virtual bool getFullHelp() const = 0; + + virtual void setInteriorMapTexture(const int x, const int y) = 0; + ///< set the index of the map texture that should be used (for interiors) + + /// sets the visibility of the hud health/magicka/stamina bars + virtual void setHMSVisibility(bool visible) = 0; + + /// sets the visibility of the hud minimap + virtual void setMinimapVisibility(bool visible) = 0; + virtual void setWeaponVisibility(bool visible) = 0; + virtual void setSpellVisibility(bool visible) = 0; + + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) = 0; + virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) = 0; + virtual void unsetSelectedSpell() = 0; + virtual void unsetSelectedWeapon() = 0; + + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; + ///< Hides dialog and schedules dialog to be deleted. + + virtual void messageBox (const std::string& message, const std::vector& buttons) = 0; + virtual int readPressedButton() = 0; + ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + + virtual void onFrame (float frameDuration) = 0; + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + virtual std::map > getPlayerSkillValues() = 0; + virtual std::map > getPlayerAttributeValues() = 0; + virtual SkillList getPlayerMinorSkills() = 0; + virtual SkillList getPlayerMajorSkills() = 0; + + /** + * Fetches a GMST string from the store, if there is no setting with the given + * ID or it is not a string the default string is returned. + * + * @param id Identifier for the GMST setting, e.g. "aName" + * @param default Default value if the GMST setting cannot be used. + */ + virtual const std::string &getGameSettingString(const std::string &id, const std::string &default_) = 0; + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + + virtual void executeInConsole (const std::string& path) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 9b0082efc1..7b8cbd3dad 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -4,6 +4,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" @@ -12,7 +13,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass @@ -34,7 +34,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Activator::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -104,4 +104,3 @@ namespace MWClass return MWWorld::Ptr(&cell.activators.insert(*ref), &cell); } } - diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 3c22cc5fb2..8a117af5dc 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -16,7 +17,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 43c1e0a436..c48d7ff4db 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,7 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 29a53140a8..1e187342d9 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" @@ -15,7 +16,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index ef91b0fac4..597eb22fec 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 7d31b945d5..ad6da90dd3 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/actionopen.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 57301e1a25..a22c2d661d 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -17,7 +18,6 @@ #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 19eda16ef1..3b283a9d1b 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index eb92accb61..8f9f8a315c 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -6,13 +6,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index ed8fd1de60..40ecf0a0f2 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 1472b7e8bd..8fdd95760d 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 8b5b414955..2cd0f63a14 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/manualref.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 30173701ec..94c98ba425 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -12,6 +12,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -26,7 +27,6 @@ #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 27903fdf73..6bff855536 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 8a563865d8..25aae445bd 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 096d3d0eee..ebba8c44e0 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -6,13 +6,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index bc8e3e648f..2a9863cdf3 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 251577d3bf..acd7868921 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -13,6 +13,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/journal.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -20,22 +21,20 @@ #include "../mwworld/containerstore.hpp" #include "../mwgui/dialogue.hpp" -#include "../mwgui/window_manager.hpp" #include -#include "../mwscript/extensions.hpp" - #include #include #include #include #include +#include #include #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" -#include +#include "../mwscript/extensions.hpp" #include "../mwclass/npc.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index dc322cade8..d626cd3159 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -5,8 +5,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/messagebox.hpp" namespace MWDialogue diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 81e5640d44..5cf09b18a3 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -5,13 +5,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" -#include "window_manager.hpp" - namespace { std::string getIconPath(MWWorld::Ptr ptr) @@ -27,7 +26,7 @@ namespace namespace MWGui { - AlchemyWindow::AlchemyWindow(WindowManager& parWindowManager) + AlchemyWindow::AlchemyWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_alchemy_window.layout", parWindowManager) , ContainerBase(0) { diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 81c33a96df..53e7178d50 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class AlchemyWindow : public WindowBase, public ContainerBase { public: - AlchemyWindow(WindowManager& parWindowManager); + AlchemyWindow(MWBase::WindowManager& parWindowManager); virtual void open(); diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 023702c8b8..05a337cbce 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -7,14 +7,14 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" using namespace MWGui; using namespace Widgets; -BirthDialog::BirthDialog(WindowManager& parWindowManager) +BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_birth.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 770e4ba36c..92665081db 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -10,14 +10,13 @@ namespace MWGui { + /// \todo remove using namespace MyGUI; - class WindowManager; - class BirthDialog : public WindowBase { public: - BirthDialog(WindowManager& parWindowManager); + BirthDialog(MWBase::WindowManager& parWindowManager); enum Gender { diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 92f0226ed1..57e59657a7 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -5,16 +5,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" #include "formatting.hpp" -#include "window_manager.hpp" using namespace MWGui; -BookWindow::BookWindow (WindowManager& parWindowManager) : +BookWindow::BookWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_book.layout", parWindowManager) { getWidget(mCloseButton, "CloseButton"); diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 9ea0114338..fedb783b25 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class BookWindow : public WindowBase { public: - BookWindow(WindowManager& parWindowManager); + BookWindow(MWBase::WindowManager& parWindowManager); void open(MWWorld::Ptr book); void setTakeButtonShow(bool show); @@ -43,4 +43,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 72394de80d..8c82b3e43d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -116,7 +116,7 @@ namespace using namespace MWGui; -CharacterCreation::CharacterCreation(WindowManager* _wm) +CharacterCreation::CharacterCreation(MWBase::WindowManager* _wm) : mNameDialog(0) , mRaceDialog(0) , mClassChoiceDialog(0) diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 1fd67bff7b..d65763d0cd 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -1,17 +1,15 @@ #ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP -#include "window_manager.hpp" - #include #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/stat.hpp" namespace MWGui { - class WindowManager; class WindowBase; class TextInputDialog; @@ -31,7 +29,7 @@ namespace MWGui public: typedef std::vector SkillList; - CharacterCreation(WindowManager* _wm); + CharacterCreation(MWBase::WindowManager* _wm); ~CharacterCreation(); //Show a dialog @@ -60,7 +58,7 @@ namespace MWGui BirthDialog* mBirthSignDialog; ReviewDialog* mReviewDialog; - WindowManager* mWM; + MWBase::WindowManager* mWM; //Player data std::string mPlayerName; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 7e3194d6be..eaf1918198 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -9,8 +9,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "tooltips.hpp" #undef min @@ -20,7 +20,7 @@ using namespace MWGui; /* GenerateClassResultDialog */ -GenerateClassResultDialog::GenerateClassResultDialog(WindowManager& parWindowManager) +GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager) { // Centre dialog @@ -77,7 +77,7 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) /* PickClassDialog */ -PickClassDialog::PickClassDialog(WindowManager& parWindowManager) +PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_class.layout", parWindowManager) { // Centre dialog @@ -283,7 +283,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin) widget->setSize(width, pos); } -InfoBoxDialog::InfoBoxDialog(WindowManager& parWindowManager) +InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_infobox.layout", parWindowManager) , mCurrentButton(-1) { @@ -367,7 +367,7 @@ void InfoBoxDialog::onButtonClicked(MyGUI::WidgetPtr _sender) /* ClassChoiceDialog */ -ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) +ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager) : InfoBoxDialog(parWindowManager) { setText(""); @@ -381,7 +381,7 @@ ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) /* CreateClassDialog */ -CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) +CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_create_class.layout", parWindowManager) , mSpecDialog(nullptr) , mAttribDialog(nullptr) @@ -694,7 +694,7 @@ void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) /* SelectSpecializationDialog */ -SelectSpecializationDialog::SelectSpecializationDialog(WindowManager& parWindowManager) +SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_specialization.layout", parWindowManager) { // Centre dialog @@ -759,7 +759,7 @@ void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectAttributeDialog */ -SelectAttributeDialog::SelectAttributeDialog(WindowManager& parWindowManager) +SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_attribute.layout", parWindowManager) { // Centre dialog @@ -811,7 +811,7 @@ void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectSkillDialog */ -SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager) +SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_skill.layout", parWindowManager) { // Centre dialog @@ -907,7 +907,7 @@ void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) /* DescriptionDialog */ -DescriptionDialog::DescriptionDialog(WindowManager& parWindowManager) +DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_class_description.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 4d8d9fa231..4baceed1ef 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -12,14 +12,13 @@ namespace MWGui { + /// \todo remove! using namespace MyGUI; - class WindowManager; - class InfoBoxDialog : public WindowBase { public: - InfoBoxDialog(WindowManager& parWindowManager); + InfoBoxDialog(MWBase::WindowManager& parWindowManager); typedef std::vector ButtonList; @@ -64,13 +63,13 @@ namespace MWGui Class_Create = 2, Class_Back = 3 }; - ClassChoiceDialog(WindowManager& parWindowManager); + ClassChoiceDialog(MWBase::WindowManager& parWindowManager); }; class GenerateClassResultDialog : public WindowBase { public: - GenerateClassResultDialog(WindowManager& parWindowManager); + GenerateClassResultDialog(MWBase::WindowManager& parWindowManager); std::string getClassId() const; void setClassId(const std::string &classId); @@ -99,7 +98,7 @@ namespace MWGui class PickClassDialog : public WindowBase { public: - PickClassDialog(WindowManager& parWindowManager); + PickClassDialog(MWBase::WindowManager& parWindowManager); const std::string &getClassId() const { return mCurrentClassId; } void setClassId(const std::string &classId); @@ -138,7 +137,7 @@ namespace MWGui class SelectSpecializationDialog : public WindowBase { public: - SelectSpecializationDialog(WindowManager& parWindowManager); + SelectSpecializationDialog(MWBase::WindowManager& parWindowManager); ~SelectSpecializationDialog(); ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } @@ -169,7 +168,7 @@ namespace MWGui class SelectAttributeDialog : public WindowBase { public: - SelectAttributeDialog(WindowManager& parWindowManager); + SelectAttributeDialog(MWBase::WindowManager& parWindowManager); ~SelectAttributeDialog(); ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } @@ -202,7 +201,7 @@ namespace MWGui class SelectSkillDialog : public WindowBase { public: - SelectSkillDialog(WindowManager& parWindowManager); + SelectSkillDialog(MWBase::WindowManager& parWindowManager); ~SelectSkillDialog(); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } @@ -238,7 +237,7 @@ namespace MWGui class DescriptionDialog : public WindowBase { public: - DescriptionDialog(WindowManager& parWindowManager); + DescriptionDialog(MWBase::WindowManager& parWindowManager); ~DescriptionDialog(); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } @@ -254,7 +253,7 @@ namespace MWGui class CreateClassDialog : public WindowBase { public: - CreateClassDialog(WindowManager& parWindowManager); + CreateClassDialog(MWBase::WindowManager& parWindowManager); virtual ~CreateClassDialog(); std::string getName() const; diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 00bdca6382..1c68da9e57 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -7,7 +7,7 @@ namespace MWGui { - ConfirmationDialog::ConfirmationDialog(WindowManager& parWindowManager) : + ConfirmationDialog::ConfirmationDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_confirmation_dialog.layout", parWindowManager) { getWidget(mMessage, "Message"); diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index d278274a03..a78028b1c4 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -8,7 +8,7 @@ namespace MWGui class ConfirmationDialog : public WindowBase { public: - ConfirmationDialog(WindowManager& parWindowManager); + ConfirmationDialog(MWBase::WindowManager& parWindowManager); void open(const std::string& message); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 89cd102339..75e2bb3b85 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" @@ -19,7 +20,6 @@ #include "../mwclass/container.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" @@ -591,7 +591,7 @@ MWWorld::ContainerStore& ContainerBase::getContainerStore() // ------------------------------------------------------------------------------------------------ -ContainerWindow::ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) +ContainerWindow::ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) , WindowBase("openmw_container_window.layout", parWindowManager) { diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 88e445a7bb..27c3288ae7 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -127,7 +127,7 @@ namespace MWGui class ContainerWindow : public ContainerBase, public WindowBase { public: - ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); virtual ~ContainerWindow(); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 07f1acb73b..6d94153540 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -7,7 +7,7 @@ namespace MWGui { - CountDialog::CountDialog(WindowManager& parWindowManager) : + CountDialog::CountDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_count_window.layout", parWindowManager) { getWidget(mSlider, "CountSlider"); diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index aac17b846d..6002dadfed 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -8,7 +8,7 @@ namespace MWGui class CountDialog : public WindowBase { public: - CountDialog(WindowManager& parWindowManager); + CountDialog(MWBase::WindowManager& parWindowManager); void open(const std::string& item, const std::string& message, const int maxCount); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 30089dd465..4342b1130a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -11,9 +11,9 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "dialogue_history.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" @@ -42,7 +42,7 @@ std::string::size_type find_str_ci(const std::string& str, const std::string& su } -DialogueWindow::DialogueWindow(WindowManager& parWindowManager) +DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_dialogue_window.layout", parWindowManager) , mEnabled(true) , mShowTrade(false) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index e2824bead0..e7f2b076cd 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -29,7 +29,7 @@ namespace MWGui class DialogueWindow: public WindowBase, public ReferenceInterface { public: - DialogueWindow(WindowManager& parWindowManager); + DialogueWindow(MWBase::WindowManager& parWindowManager); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp index 009f42044d..f72f199ea3 100644 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ b/apps/openmw/mwgui/dialogue_history.cpp @@ -1,5 +1,7 @@ #include "dialogue_history.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" + #include "widgets.hpp" #include "components/esm_store/store.hpp" @@ -71,4 +73,3 @@ void DialogueHistory::addDialogText(const UString& parText) addText(parText); addText("\n"); } - diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 1c79433869..bdbb316b05 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -9,6 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -16,7 +17,6 @@ #include "../mwgui/widgets.hpp" #include "inventorywindow.hpp" -#include "window_manager.hpp" #include "container.hpp" #include "console.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 090b7dd6e6..7b68f7b6d6 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -10,6 +10,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -18,7 +19,6 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" @@ -40,7 +40,7 @@ namespace namespace MWGui { - InventoryWindow::InventoryWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) + InventoryWindow::InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) , mTrading(false) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 82da3efea7..fbdb79977a 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -9,7 +9,7 @@ namespace MWGui class InventoryWindow : public ContainerBase, public WindowPinnableBase { public: - InventoryWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); virtual void open(); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 475a70631e..597c27c6df 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -4,11 +4,10 @@ #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwdialogue/journalentry.hpp" -#include "window_manager.hpp" - namespace { struct book @@ -82,7 +81,7 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) } -MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) +MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_journal.layout", parWindowManager) , mLastPos(0) , mVisible(false) diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 0c85ebf081..fc05bbdbcd 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -10,12 +10,10 @@ namespace MWGui { - class WindowManager; - class JournalWindow : public WindowBase { public: - JournalWindow(WindowManager& parWindowManager); + JournalWindow(MWBase::WindowManager& parWindowManager); void open(); virtual void setVisible(bool visible); // only used to play close sound diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e2fefd6497..bb6b8163e9 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -2,11 +2,9 @@ #include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "window_manager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 4ebeb38742..1ffedaac48 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -1,5 +1,6 @@ #include "map_window.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" #include @@ -154,7 +155,7 @@ void LocalMapBase::setPlayerDir(const float x, const float y) // ------------------------------------------------------------------------------------------ -MapWindow::MapWindow(WindowManager& parWindowManager) : +MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager), mGlobal(false) { diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 8d3392b827..447c16901a 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -45,11 +45,11 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase { public: - MapWindow(WindowManager& parWindowManager); + MapWindow(MWBase::WindowManager& parWindowManager); virtual ~MapWindow(){} void setCellName(const std::string& cellName); - + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 2b00ca05ce..b660af7dd4 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -2,7 +2,7 @@ using namespace MWGui; -MessageBoxManager::MessageBoxManager (WindowManager *windowManager) +MessageBoxManager::MessageBoxManager (MWBase::WindowManager *windowManager) { mWindowManager = windowManager; // defines diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 75393ec947..5e4c468d56 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -5,13 +5,13 @@ #include #include "window_base.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" #undef MessageBox namespace MWGui { - class InteractiveMessageBox; class MessageBoxManager; class MessageBox; @@ -25,27 +25,27 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (WindowManager* windowManager); + MessageBoxManager (MWBase::WindowManager* windowManager); void onFrame (float frameDuration); void createMessageBox (const std::string& message); bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); - + void removeMessageBox (float time, MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - + int readPressedButton (); - - WindowManager *mWindowManager; - + + MWBase::WindowManager *mWindowManager; + private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; std::vector mTimers; float mMessageBoxSpeed; }; - + class MessageBox : public OEngine::GUI::Layout { public: @@ -53,9 +53,9 @@ namespace MWGui void setMessage (const std::string& message); int getHeight (); void update (int height); - + bool mMarkedToDelete; - + protected: MessageBoxManager& mMessageBoxManager; int mHeight; @@ -65,16 +65,16 @@ namespace MWGui int mBottomPadding; int mNextBoxPadding; }; - + class InteractiveMessageBox : public OEngine::GUI::Layout { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); - + bool mMarkedToDelete; - + private: MessageBoxManager& mMessageBoxManager; MyGUI::EditPtr mMessageWidget; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 62434cb91a..ceb0452fba 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -10,15 +10,15 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" using namespace MWGui; using namespace Widgets; -RaceDialog::RaceDialog(WindowManager& parWindowManager) +RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_race.layout", parWindowManager) , mGenderIndex(0) , mFaceIndex(0) diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index b523b86902..3da6b0ace8 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -24,7 +24,7 @@ namespace MWGui class RaceDialog : public WindowBase { public: - RaceDialog(WindowManager& parWindowManager); + RaceDialog(MWBase::WindowManager& parWindowManager); enum Gender { diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 997899c526..8dd894c258 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -9,8 +9,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -22,7 +22,7 @@ using namespace Widgets; const int ReviewDialog::sLineHeight = 18; -ReviewDialog::ReviewDialog(WindowManager& parWindowManager) +ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_review.layout", parWindowManager) , mLastPos(0) { diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 27b1670333..f0bde6ecde 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -30,7 +30,7 @@ namespace MWGui }; typedef std::vector SkillList; - ReviewDialog(WindowManager& parWindowManager); + ReviewDialog(MWBase::WindowManager& parWindowManager); void setPlayerName(const std::string &name); void setRace(const std::string &raceId); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index fb2239c6da..ff83b0e3e4 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -3,16 +3,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" #include "formatting.hpp" -#include "window_manager.hpp" using namespace MWGui; -ScrollWindow::ScrollWindow (WindowManager& parWindowManager) : +ScrollWindow::ScrollWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_scroll.layout", parWindowManager) { getWidget(mTextView, "TextView"); diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index d58596b4be..b8f52fb658 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class ScrollWindow : public WindowBase { public: - ScrollWindow (WindowManager& parWindowManager); + ScrollWindow (MWBase::WindowManager& parWindowManager); void open (MWWorld::Ptr scroll); void setTakeButtonShow(bool show); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 06177aaa8d..f6597a64ef 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -14,10 +14,10 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "window_manager.hpp" #include "confirmationdialog.hpp" namespace @@ -81,7 +81,7 @@ namespace namespace MWGui { - SettingsWindow::SettingsWindow(WindowManager& parWindowManager) : + SettingsWindow::SettingsWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_settings_window.layout", parWindowManager) { getWidget(mOkButton, "OkButton"); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 63fbed46b6..ca11b6f9c2 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -13,7 +13,7 @@ namespace MWGui class SettingsWindow : public WindowBase { public: - SettingsWindow(WindowManager& parWindowManager); + SettingsWindow(MWBase::WindowManager& parWindowManager); private: static int const sFovMin = 30; @@ -77,4 +77,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index b528eecc22..8754f5d105 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -9,6 +9,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -17,7 +18,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" -#include "window_manager.hpp" #include "inventorywindow.hpp" #include "confirmationdialog.hpp" @@ -42,7 +42,7 @@ namespace namespace MWGui { - SpellWindow::SpellWindow(WindowManager& parWindowManager) + SpellWindow::SpellWindow(MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_spell_window.layout", parWindowManager) , mHeight(0) , mWidth(0) diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 87e4783ba2..caa67fd740 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -8,7 +8,7 @@ namespace MWGui class SpellWindow : public WindowPinnableBase { public: - SpellWindow(WindowManager& parWindowManager); + SpellWindow(MWBase::WindowManager& parWindowManager); void updateSpells(); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 4ee56abc7f..32dac71deb 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -9,20 +9,20 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" -#include "window_manager.hpp" #include "tooltips.hpp" using namespace MWGui; const int StatsWindow::sLineHeight = 18; -StatsWindow::StatsWindow (WindowManager& parWindowManager) +StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) , mSkillAreaWidget(NULL) , mSkillClientWidget(NULL) diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index 2469c12e9c..a46ab7531d 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -22,7 +22,7 @@ namespace MWGui typedef std::vector SkillList; - StatsWindow(WindowManager& parWindowManager); + StatsWindow(MWBase::WindowManager& parWindowManager); /// automatically updates all the data in the stats window, but only if it has changed. void onFrame(); @@ -82,4 +82,3 @@ namespace MWGui }; } #endif - diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 7d5b0cc6db..802cd30637 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -1,9 +1,10 @@ #include "text_input.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" using namespace MWGui; -TextInputDialog::TextInputDialog(WindowManager& parWindowManager) +TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_text_input.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index 835d5deaa5..7a33257224 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -18,7 +18,7 @@ namespace MWGui class TextInputDialog : public WindowBase { public: - TextInputDialog(WindowManager& parWindowManager); + TextInputDialog(MWBase::WindowManager& parWindowManager); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 679c7a59eb..edc787cef7 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -8,16 +8,16 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" -#include "window_manager.hpp" #include "widgets.hpp" using namespace MWGui; using namespace MyGUI; -ToolTips::ToolTips(WindowManager* windowManager) : +ToolTips::ToolTips(MWBase::WindowManager* windowManager) : Layout("openmw_tooltips.layout") , mGameMode(true) , mWindowManager(windowManager) diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 036bdfaa34..700f5f7238 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -9,8 +9,6 @@ namespace MWGui { - class WindowManager; - // Info about tooltip that is supplied by the MWWorld::Class object struct ToolTipInfo { @@ -29,7 +27,7 @@ namespace MWGui class ToolTips : public OEngine::GUI::Layout { public: - ToolTips(WindowManager* windowManager); + ToolTips(MWBase::WindowManager* windowManager); void onFrame(float frameDuration); @@ -45,7 +43,7 @@ namespace MWGui void setFocusObject(const MWWorld::Ptr& focus); void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); - ///< set the screen-space position of the tooltip for focused object + ///< set the screen-space position of the tooltip for focused object static std::string getValueString(const int value, const std::string& prefix); ///< @return "prefix: value" or "" if value is 0 @@ -71,7 +69,7 @@ namespace MWGui private: MyGUI::Widget* mDynamicToolTipBox; - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; MWWorld::Ptr mFocusObject; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 75e39fd879..8471367c68 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -5,16 +5,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" -#include "window_manager.hpp" #include "inventorywindow.hpp" namespace MWGui { - TradeWindow::TradeWindow(WindowManager& parWindowManager) : + TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_trade_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop , mCurrentBalance(0) diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index b391fd8fa2..1daeefa960 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -23,7 +23,7 @@ namespace MWGui class TradeWindow : public ContainerBase, public WindowBase { public: - TradeWindow(WindowManager& parWindowManager); + TradeWindow(MWBase::WindowManager& parWindowManager); void startTrade(MWWorld::Ptr actor); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 1529387071..40ee1c7adf 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -6,8 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #undef min #undef max diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index d4947895b1..9a2762369c 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -10,14 +10,19 @@ #undef MYGUI_EXPORT #define MYGUI_EXPORT +namespace MWBase +{ + class WindowManager; +} + /* This file contains various custom widgets used in OpenMW. */ namespace MWGui { + /// \todo remove! using namespace MyGUI; - class WindowManager; namespace Widgets { @@ -81,12 +86,12 @@ namespace MWGui typedef MWMechanics::Stat SkillValue; - void setWindowManager(WindowManager *m) { mManager = m; } + void setWindowManager(MWBase::WindowManager *m) { mManager = m; } /// \todo remove void setSkillId(ESM::Skill::SkillEnum skillId); void setSkillNumber(int skillId); void setSkillValue(const SkillValue& value); - WindowManager *getWindowManager() const { return mManager; } + MWBase::WindowManager *getWindowManager() const { return mManager; } ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } const SkillValue& getSkillValue() const { return mValue; } @@ -109,7 +114,7 @@ namespace MWGui void updateWidgets(); - WindowManager *mManager; + MWBase::WindowManager *mManager; ESM::Skill::SkillEnum mSkillId; SkillValue mValue; MyGUI::WidgetPtr mSkillNameWidget, mSkillValueWidget; @@ -124,11 +129,11 @@ namespace MWGui typedef MWMechanics::Stat AttributeValue; - void setWindowManager(WindowManager *m) { mManager = m; } + void setWindowManager(MWBase::WindowManager *m) { mManager = m; } void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); - WindowManager *getWindowManager() const { return mManager; } + MWBase::WindowManager *getWindowManager() const { return mManager; } int getAttributeId() const { return mId; } const AttributeValue& getAttributeValue() const { return mValue; } @@ -151,7 +156,7 @@ namespace MWGui void updateWidgets(); - WindowManager *mManager; + MWBase::WindowManager *mManager; int mId; AttributeValue mValue; MyGUI::WidgetPtr mAttributeNameWidget, mAttributeValueWidget; @@ -170,7 +175,7 @@ namespace MWGui typedef MWMechanics::Stat SpellValue; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellId(const std::string &id); /** @@ -192,7 +197,7 @@ namespace MWGui private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; std::string mId; MyGUI::TextBox* mSpellNameWidget; }; @@ -212,7 +217,7 @@ namespace MWGui EF_Constant = 0x02 // constant effect means that duration will not be displayed }; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setEffectList(const SpellEffectList& list); static SpellEffectList effectListFromESM(const ESM::EffectList* effects); @@ -234,7 +239,7 @@ namespace MWGui private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; @@ -247,7 +252,7 @@ namespace MWGui typedef ESM::ENAMstruct SpellEffectValue; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellEffect(const SpellEffectParams& params); std::string effectIDToString(const short effectID); @@ -262,12 +267,12 @@ namespace MWGui virtual ~MWSpellEffect(); virtual void initialiseOverride(); - + private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/window_base.cpp index 4330579311..dbb37efbb0 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/window_base.cpp @@ -1,11 +1,12 @@ #include "window_base.hpp" -#include "window_manager.hpp" #include +#include "../mwbase/windowmanager.hpp" + using namespace MWGui; -WindowBase::WindowBase(const std::string& parLayout, WindowManager& parWindowManager) +WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) : Layout(parLayout) , mWindowManager(parWindowManager) { diff --git a/apps/openmw/mwgui/window_base.hpp b/apps/openmw/mwgui/window_base.hpp index 9cfdbe2612..74d874bb82 100644 --- a/apps/openmw/mwgui/window_base.hpp +++ b/apps/openmw/mwgui/window_base.hpp @@ -3,6 +3,11 @@ #include +namespace MWBase +{ + class WindowManager; +} + namespace MWGui { class WindowManager; @@ -10,7 +15,7 @@ namespace MWGui class WindowBase: public OEngine::GUI::Layout { public: - WindowBase(const std::string& parLayout, WindowManager& parWindowManager); + WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; @@ -25,9 +30,9 @@ namespace MWGui EventHandle_WindowBase eventDone; protected: - WindowManager& mWindowManager; + /// \todo remove + MWBase::WindowManager& mWindowManager; }; } #endif - diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp deleted file mode 100644 index db3945e55d..0000000000 --- a/apps/openmw/mwgui/window_manager.hpp +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef MWGUI_WINDOWMANAGER_H -#define MWGUI_WINDOWMANAGER_H - -/** - This class owns and controls all the MW specific windows in the - GUI. It can enable/disable Gui mode, and is responsible for sending - and retrieving information from the Gui. - - MyGUI should be initialized separately before creating instances of - this class. -**/ - -#include -#include - -#include -#include - -#include "../mwmechanics/stat.hpp" - -#include "mode.hpp" - -namespace MyGUI -{ - class Gui; - class Widget; - class UString; -} - -namespace Compiler -{ - class Extensions; -} - -namespace MWWorld -{ - class Ptr; - class CellStore; -} - -namespace OEngine -{ - namespace GUI - { - class Layout; - class MyGUIManager; - } - - namespace Render - { - class OgreRenderer; - } -} - -namespace MWGui -{ - class WindowBase; - class HUD; - class MapWindow; - class MainMenu; - class StatsWindow; - class InventoryWindow; - class Console; - class JournalWindow; - class CharacterCreation; - class ContainerWindow; - class DragAndDrop; - class InventoryWindow; - class ToolTips; - class ScrollWindow; - class BookWindow; - class TextInputDialog; - class InfoBoxDialog; - class DialogueWindow; - class MessageBoxManager; - class CountDialog; - class TradeWindow; - class SettingsWindow; - class ConfirmationDialog; - class AlchemyWindow; - class SpellWindow; - - class WindowManager - { - public: - typedef std::pair Faction; - typedef std::vector FactionList; - typedef std::vector SkillList; - - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); - virtual ~WindowManager(); - - /** - * Should be called each frame to update windows/gui elements. - * This could mean updating sizes of gui elements or opening - * new dialogs. - */ - void update(); - - void pushGuiMode(GuiMode mode); - void popGuiMode(); - void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack - - GuiMode getMode() const; - - bool isGuiMode() const; - - void toggleVisible(GuiWindow wnd); - - // Disallow all inventory mode windows - void disallowAll(); - - // Allow one or more windows - void allow(GuiWindow wnd); - - bool isAllowed(GuiWindow wnd) const; - - /// \todo investigate, if we really need to expose every single lousy UI element to the outside world - MWGui::DialogueWindow* getDialogueWindow(); - MWGui::ContainerWindow* getContainerWindow(); - MWGui::InventoryWindow* getInventoryWindow(); - MWGui::BookWindow* getBookWindow(); - MWGui::ScrollWindow* getScrollWindow(); - MWGui::CountDialog* getCountDialog(); - MWGui::ConfirmationDialog* getConfirmationDialog(); - MWGui::TradeWindow* getTradeWindow(); - MWGui::SpellWindow* getSpellWindow(); - MWGui::Console* getConsole(); - - MyGUI::Gui* getGui() const; - - void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); - - ///< Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue (int parSkill, const MWMechanics::Stat& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - void setValue (const std::string& id, const std::string& value); - void setValue (const std::string& id, int value); - - void setPlayerClass (const ESM::Class &class_); ///< set current class of player - void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. - void setReputation (int reputation); ///< set the current reputation value - void setBounty (int bounty); ///< set the current bounty value - void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty - - void changeCell(MWWorld::CellStore* cell); ///< change the active cell - void setPlayerPos(const float x, const float y); ///< set player position in map space - void setPlayerDir(const float x, const float y); ///< set player view direction in map space - - void setFocusObject(const MWWorld::Ptr& focus); - void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); - - void setMouseVisible(bool visible); - void getMousePosition(int &x, int &y); - void getMousePosition(float &x, float &y); - void setDragDrop(bool dragDrop); - bool getWorldMouseOver(); - - void toggleFogOfWar(); - void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) - bool getFullHelp() const; - - void setInteriorMapTexture(const int x, const int y); - ///< set the index of the map texture that should be used (for interiors) - - // sets the visibility of the hud health/magicka/stamina bars - void setHMSVisibility(bool visible); - // sets the visibility of the hud minimap - void setMinimapVisibility(bool visible); - void setWeaponVisibility(bool visible); - void setSpellVisibility(bool visible); - - void setSelectedSpell(const std::string& spellId, int successChancePercent); - void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); - void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); - void unsetSelectedSpell(); - void unsetSelectedWeapon(); - - void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - - void messageBox (const std::string& message, const std::vector& buttons); - int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) - - void onFrame (float frameDuration); - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - std::map > getPlayerSkillValues(); - std::map > getPlayerAttributeValues(); - SkillList getPlayerMinorSkills(); - SkillList getPlayerMajorSkills(); - - /** - * Fetches a GMST string from the store, if there is no setting with the given - * ID or it is not a string the default string is returned. - * - * @param id Identifier for the GMST setting, e.g. "aName" - * @param default Default value if the GMST setting cannot be used. - */ - const std::string &getGameSettingString(const std::string &id, const std::string &default_); - - void processChangedSettings(const Settings::CategorySettingVector& changed); - - void executeInConsole (const std::string& path); - - private: - OEngine::GUI::MyGUIManager *mGuiManager; - HUD *mHud; - MapWindow *mMap; - MainMenu *mMenu; - ToolTips *mToolTips; - StatsWindow *mStatsWindow; - MessageBoxManager *mMessageBoxManager; - Console *mConsole; - JournalWindow* mJournal; - DialogueWindow *mDialogueWindow; - ContainerWindow *mContainerWindow; - DragAndDrop* mDragAndDrop; - InventoryWindow *mInventoryWindow; - ScrollWindow* mScrollWindow; - BookWindow* mBookWindow; - CountDialog* mCountDialog; - TradeWindow* mTradeWindow; - SettingsWindow* mSettingsWindow; - ConfirmationDialog* mConfirmationDialog; - AlchemyWindow* mAlchemyWindow; - SpellWindow* mSpellWindow; - - CharacterCreation* mCharGen; - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - // Various stats about player as needed by window manager - ESM::Class mPlayerClass; - std::string mPlayerName; - std::string mPlayerRaceId; - std::map > mPlayerAttributes; - SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; - MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; - - - MyGUI::Gui *mGui; // Gui - std::vector mGuiModes; - - std::vector mGarbageDialogs; - void cleanupGarbage(); - - GuiWindow mShown; // Currently shown windows in inventory mode - - /* Currently ALLOWED windows in inventory mode. This is used at - the start of the game, when windows are enabled one by one - through script commands. You can manipulate this through using - allow() and disableAll(). - */ - GuiWindow mAllowed; - - void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings - - int mShowFPSLevel; - float mFPS; - unsigned int mTriangleCount; - unsigned int mBatchCount; - - void onDialogueWindowBye(); - - /** - * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, - * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result - */ - void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); - }; -} - -#endif diff --git a/apps/openmw/mwgui/window_pinnable_base.cpp b/apps/openmw/mwgui/window_pinnable_base.cpp index ecdf311c6e..4ddf49d27b 100644 --- a/apps/openmw/mwgui/window_pinnable_base.cpp +++ b/apps/openmw/mwgui/window_pinnable_base.cpp @@ -1,9 +1,10 @@ #include "window_pinnable_base.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" using namespace MWGui; -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager) +WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) : WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false) { MyGUI::WindowPtr t = static_cast(mMainWidget); @@ -30,4 +31,3 @@ void WindowPinnableBase::onWindowButtonPressed(MyGUI::Window* sender, const std: eventDone(this); } - diff --git a/apps/openmw/mwgui/window_pinnable_base.hpp b/apps/openmw/mwgui/window_pinnable_base.hpp index 86bc3b85c8..250dde1f85 100644 --- a/apps/openmw/mwgui/window_pinnable_base.hpp +++ b/apps/openmw/mwgui/window_pinnable_base.hpp @@ -10,13 +10,13 @@ namespace MWGui class WindowPinnableBase: public WindowBase { public: - WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager); + WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); void setVisible(bool b); bool pinned() { return mPinned; } private: void onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName); - + protected: virtual void onPinToggled() = 0; @@ -26,4 +26,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp similarity index 99% rename from apps/openmw/mwgui/window_manager.cpp rename to apps/openmw/mwgui/windowmanagerimp.cpp index eada0c88a2..db8401fce9 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "window_manager.hpp" +#include "windowmanagerimp.hpp" #include #include diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp new file mode 100644 index 0000000000..eaa6a16832 --- /dev/null +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -0,0 +1,254 @@ +#ifndef MWGUI_WINDOWMANAGERIMP_H +#define MWGUI_WINDOWMANAGERIMP_H + +/** + This class owns and controls all the MW specific windows in the + GUI. It can enable/disable Gui mode, and is responsible for sending + and retrieving information from the Gui. + + MyGUI should be initialized separately before creating instances of + this class. +**/ + +#include +#include + +#include + +#include "../mwbase/windowmanager.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; + class UString; +} + +namespace Compiler +{ + class Extensions; +} + +namespace OEngine +{ + namespace GUI + { + class Layout; + class MyGUIManager; + } + + namespace Render + { + class OgreRenderer; + } +} + +namespace MWGui +{ + class WindowBase; + class HUD; + class MapWindow; + class MainMenu; + class StatsWindow; + class InventoryWindow; + class JournalWindow; + class CharacterCreation; + class DragAndDrop; + class ToolTips; + class TextInputDialog; + class InfoBoxDialog; + class MessageBoxManager; + class SettingsWindow; + class AlchemyWindow; + + class WindowManager : public MWBase::WindowManager + { + public: + typedef std::pair Faction; + typedef std::vector FactionList; + + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); + virtual ~WindowManager(); + + /** + * Should be called each frame to update windows/gui elements. + * This could mean updating sizes of gui elements or opening + * new dialogs. + */ + virtual void update(); + + virtual void pushGuiMode(GuiMode mode); + virtual void popGuiMode(); + virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack + + virtual GuiMode getMode() const; + + virtual bool isGuiMode() const; + + virtual void toggleVisible(GuiWindow wnd); + + // Disallow all inventory mode windows + virtual void disallowAll(); + + // Allow one or more windows + virtual void allow(GuiWindow wnd); + + virtual bool isAllowed(GuiWindow wnd) const; + + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world + virtual MWGui::DialogueWindow* getDialogueWindow(); + virtual MWGui::ContainerWindow* getContainerWindow(); + virtual MWGui::InventoryWindow* getInventoryWindow(); + virtual MWGui::BookWindow* getBookWindow(); + virtual MWGui::ScrollWindow* getScrollWindow(); + virtual MWGui::CountDialog* getCountDialog(); + virtual MWGui::ConfirmationDialog* getConfirmationDialog(); + virtual MWGui::TradeWindow* getTradeWindow(); + virtual MWGui::SpellWindow* getSpellWindow(); + virtual MWGui::Console* getConsole(); + + virtual MyGUI::Gui* getGui() const; + + virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); + + ///< Set value for the given ID. + virtual void setValue (const std::string& id, const MWMechanics::Stat& value); + virtual void setValue (int parSkill, const MWMechanics::Stat& value); + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + virtual void setValue (const std::string& id, const std::string& value); + virtual void setValue (const std::string& id, int value); + + virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player + virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. + virtual void setReputation (int reputation); ///< set the current reputation value + virtual void setBounty (int bounty); ///< set the current bounty value + virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty + + virtual void changeCell(MWWorld::CellStore* cell); ///< change the active cell + virtual void setPlayerPos(const float x, const float y); ///< set player position in map space + virtual void setPlayerDir(const float x, const float y); ///< set player view direction in map space + + virtual void setFocusObject(const MWWorld::Ptr& focus); + virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); + + virtual void setMouseVisible(bool visible); + virtual void getMousePosition(int &x, int &y); + virtual void getMousePosition(float &x, float &y); + virtual void setDragDrop(bool dragDrop); + virtual bool getWorldMouseOver(); + + virtual void toggleFogOfWar(); + virtual void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) + virtual bool getFullHelp() const; + + virtual void setInteriorMapTexture(const int x, const int y); + ///< set the index of the map texture that should be used (for interiors) + + // sets the visibility of the hud health/magicka/stamina bars + virtual void setHMSVisibility(bool visible); + // sets the visibility of the hud minimap + virtual void setMinimapVisibility(bool visible); + virtual void setWeaponVisibility(bool visible); + virtual void setSpellVisibility(bool visible); + + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); + virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); + virtual void unsetSelectedSpell(); + virtual void unsetSelectedWeapon(); + + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. + + virtual void messageBox (const std::string& message, const std::vector& buttons); + virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + + virtual void onFrame (float frameDuration); + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + virtual std::map > getPlayerSkillValues(); + virtual std::map > getPlayerAttributeValues(); + virtual SkillList getPlayerMinorSkills(); + virtual SkillList getPlayerMajorSkills(); + + /** + * Fetches a GMST string from the store, if there is no setting with the given + * ID or it is not a string the default string is returned. + * + * @param id Identifier for the GMST setting, e.g. "aName" + * @param default Default value if the GMST setting cannot be used. + */ + virtual const std::string &getGameSettingString(const std::string &id, const std::string &default_); + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + + virtual void executeInConsole (const std::string& path); + + private: + OEngine::GUI::MyGUIManager *mGuiManager; + HUD *mHud; + MapWindow *mMap; + MainMenu *mMenu; + ToolTips *mToolTips; + StatsWindow *mStatsWindow; + MessageBoxManager *mMessageBoxManager; + Console *mConsole; + JournalWindow* mJournal; + DialogueWindow *mDialogueWindow; + ContainerWindow *mContainerWindow; + DragAndDrop* mDragAndDrop; + InventoryWindow *mInventoryWindow; + ScrollWindow* mScrollWindow; + BookWindow* mBookWindow; + CountDialog* mCountDialog; + TradeWindow* mTradeWindow; + SettingsWindow* mSettingsWindow; + ConfirmationDialog* mConfirmationDialog; + AlchemyWindow* mAlchemyWindow; + SpellWindow* mSpellWindow; + + CharacterCreation* mCharGen; + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + // Various stats about player as needed by window manager + ESM::Class mPlayerClass; + std::string mPlayerName; + std::string mPlayerRaceId; + std::map > mPlayerAttributes; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map > mPlayerSkillValues; + MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; + + + MyGUI::Gui *mGui; // Gui + std::vector mGuiModes; + + std::vector mGarbageDialogs; + void cleanupGarbage(); + + GuiWindow mShown; // Currently shown windows in inventory mode + + /* Currently ALLOWED windows in inventory mode. This is used at + the start of the game, when windows are enabled one by one + through script commands. You can manipulate this through using + allow() and disableAll(). + */ + GuiWindow mAllowed; + + void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings + + int mShowFPSLevel; + float mFPS; + unsigned int mTriangleCount; + unsigned int mBatchCount; + + void onDialogueWindowBye(); + + /** + * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, + * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result + */ + void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); + }; +} + +#endif diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d53e67d485..0b409fd506 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -9,7 +9,7 @@ #include -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include #include @@ -83,7 +83,7 @@ namespace MWInput MouseLookEventPtr mouse; OEngine::GUI::EventInjectorPtr guiEvents; MWWorld::Player &player; - MWGui::WindowManager &windows; + MWBase::WindowManager &windows; OMW::Engine& mEngine; bool mDragDrop; @@ -230,7 +230,7 @@ private: public: InputImpl(OEngine::Render::OgreRenderer &_ogre, MWWorld::Player &_player, - MWGui::WindowManager &_windows, + MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine) : ogre(_ogre), @@ -457,7 +457,7 @@ private: /***CONSTRUCTOR***/ MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre, MWWorld::Player &player, - MWGui::WindowManager &windows, + MWBase::WindowManager &windows, bool debug, OMW::Engine& engine) { diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 862c4fd207..5092198da8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -20,7 +20,7 @@ namespace MWWorld class Player; } -namespace MWGui +namespace MWBase { class WindowManager; } @@ -47,7 +47,7 @@ namespace MWInput public: MWInputManager(OEngine::Render::OgreRenderer &_ogre, MWWorld::Player&_player, - MWGui::WindowManager &_windows, + MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine); virtual ~MWInputManager(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5ad67dde46..7fd0b29c38 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" - namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -265,8 +264,8 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getPlayer().getClass().name); mUpdatePlayer = false; - MWGui::WindowManager::SkillList majorSkills (5); - MWGui::WindowManager::SkillList minorSkills (5); + MWBase::WindowManager::SkillList majorSkills (5); + MWBase::WindowManager::SkillList minorSkills (5); for (int i=0; i<5; ++i) { diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index b5fa135e0d..704a10cfef 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -8,8 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "renderconst.hpp" #include "renderingmanager.hpp" diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d039418ba3..8fb151b5c4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -24,12 +24,11 @@ #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" // FIXME +#include "../mwbase/windowmanager.hpp" // FIXME #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" // FIXME - #include "shadows.hpp" #include "localmap.hpp" #include "water.hpp" diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 3d14692d9a..d740e5feba 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -8,8 +8,7 @@ #include #include "../mwbase/environment.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 0ba04fb381..075ac56462 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -10,12 +10,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" - #include "locals.hpp" #include "globalscripts.hpp" diff --git a/apps/openmw/mwworld/actionalchemy.cpp b/apps/openmw/mwworld/actionalchemy.cpp index a7ee4fd0e3..bba75bc499 100644 --- a/apps/openmw/mwworld/actionalchemy.cpp +++ b/apps/openmw/mwworld/actionalchemy.cpp @@ -1,7 +1,7 @@ #include "actionalchemy.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWWorld { diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index c73ef91494..15a9f510dd 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -1,9 +1,8 @@ #include "actionopen.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" -#include "../mwclass/container.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/container.hpp" #include "class.hpp" diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index c81d79e03b..5c6ab93c12 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -1,7 +1,8 @@ #include "actionread.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" + #include "../mwgui/bookwindow.hpp" #include "../mwgui/scrollwindow.hpp" diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 5207f1a100..90f3c000e2 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "class.hpp" #include "containerstore.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1c7093e60e..e67280ee83 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -6,8 +6,7 @@ #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "player.hpp" #include "localscripts.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3a9547a445..4eda332ef9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -6,12 +6,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" -#include "../mwgui/window_manager.hpp" - #include "player.hpp" #include "manualref.hpp" #include "cellfunctors.hpp" From 86d6f190bf3fd958b996afd245282e14b939c061 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 20:45:02 +0200 Subject: [PATCH 058/143] Input system rewrite --- CMakeLists.txt | 12 +- apps/openmw/CMakeLists.txt | 1 + apps/openmw/engine.cpp | 24 +- apps/openmw/mwbase/inputmanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 825 +++---- apps/openmw/mwinput/inputmanagerimp.hpp | 173 +- apps/openmw/mwinput/mouselookevent.cpp | 28 - apps/openmw/mwinput/mouselookevent.hpp | 57 - extern/oics/CMakeLists.txt | 20 + extern/oics/ICSChannel.cpp | 258 +++ extern/oics/ICSChannel.h | 122 ++ extern/oics/ICSChannelListener.h | 46 + extern/oics/ICSControl.cpp | 161 ++ extern/oics/ICSControl.h | 107 + extern/oics/ICSControlListener.h | 46 + extern/oics/ICSInputControlSystem.cpp | 929 ++++++++ extern/oics/ICSInputControlSystem.h | 256 +++ .../oics/ICSInputControlSystem_joystick.cpp | 665 ++++++ .../oics/ICSInputControlSystem_keyboard.cpp | 156 ++ extern/oics/ICSInputControlSystem_mouse.cpp | 397 ++++ extern/oics/ICSPrerequisites.cpp | 27 + extern/oics/ICSPrerequisites.h | 111 + extern/oics/tinystr.cpp | 116 + extern/oics/tinystr.h | 319 +++ extern/oics/tinyxml.cpp | 1888 +++++++++++++++++ extern/oics/tinyxml.h | 1802 ++++++++++++++++ extern/oics/tinyxmlerror.cpp | 53 + extern/oics/tinyxmlparser.cpp | 1638 ++++++++++++++ files/input-default.xml | 100 + libs/mangle/.gitignore | 3 - libs/mangle/Doxyfile | 1510 ------------- libs/mangle/LICENSE.txt | 26 - libs/mangle/README.txt | 129 -- .../input/clients/ogre_input_capture.hpp | 29 - libs/mangle/input/driver.hpp | 69 - libs/mangle/input/event.hpp | 46 - libs/mangle/input/filters/eventlist.hpp | 47 - libs/mangle/input/servers/ois_driver.cpp | 154 -- libs/mangle/input/servers/ois_driver.hpp | 50 - libs/mangle/input/servers/sdl_driver.cpp | 54 - libs/mangle/input/servers/sdl_driver.hpp | 27 - libs/mangle/input/tests/.gitignore | 2 - libs/mangle/input/tests/Makefile | 15 - libs/mangle/input/tests/common.cpp | 35 - libs/mangle/input/tests/evtlist_test.cpp | 45 - libs/mangle/input/tests/ois_driver_test.cpp | 51 - .../input/tests/output/evtlist_test.out | 12 - .../input/tests/output/ois_driver_test.out | 5 - .../input/tests/output/sdl_driver_test.out | 5 - libs/mangle/input/tests/plugins.cfg | 12 - libs/mangle/input/tests/sdl_driver_test.cpp | 16 - libs/mangle/input/tests/test.sh | 18 - libs/mangle/rend2d/driver.hpp | 63 - libs/mangle/rend2d/servers/sdl_driver.cpp | 259 --- libs/mangle/rend2d/servers/sdl_driver.hpp | 125 -- libs/mangle/rend2d/servers/sdl_gl_driver.cpp | 311 --- libs/mangle/rend2d/servers/sdl_gl_driver.hpp | 132 -- libs/mangle/rend2d/sprite.hpp | 57 - libs/mangle/rend2d/tests/.gitignore | 1 - libs/mangle/rend2d/tests/Makefile | 15 - .../rend2d/tests/output/sdl_move_test.out | 0 libs/mangle/rend2d/tests/output/sdl_test.out | 11 - .../rend2d/tests/output/sdlgl_move_test.out | 0 libs/mangle/rend2d/tests/sdl_move_test.cpp | 30 - libs/mangle/rend2d/tests/sdl_test.cpp | 65 - libs/mangle/rend2d/tests/sdlgl_move_test.cpp | 31 - libs/mangle/rend2d/tests/test.sh | 18 - libs/mangle/rend2d/tests/tile1-blue.png | Bin 273 -> 0 bytes libs/mangle/rend2d/tests/tile1-yellow.png | Bin 257 -> 0 bytes libs/mangle/testall.sh | 16 - libs/mangle/tests/.gitignore | 1 - libs/mangle/tests/Makefile | 14 - .../tests/ogrevfs_audiere_openal_test.cpp | 49 - .../output/ogrevfs_audiere_openal_test.out | 1 - libs/mangle/tests/sound.zip | Bin 18159 -> 0 bytes libs/mangle/tests/test.sh | 18 - libs/mangle/tools/shared_ptr.hpp | 3 - libs/openengine/gui/events.cpp | 77 - libs/openengine/gui/events.hpp | 32 - libs/openengine/ogre/mouselook.cpp | 57 - libs/openengine/ogre/mouselook.hpp | 56 - 81 files changed, 9806 insertions(+), 4335 deletions(-) delete mode 100644 apps/openmw/mwinput/mouselookevent.cpp delete mode 100644 apps/openmw/mwinput/mouselookevent.hpp create mode 100644 extern/oics/CMakeLists.txt create mode 100644 extern/oics/ICSChannel.cpp create mode 100644 extern/oics/ICSChannel.h create mode 100644 extern/oics/ICSChannelListener.h create mode 100644 extern/oics/ICSControl.cpp create mode 100644 extern/oics/ICSControl.h create mode 100644 extern/oics/ICSControlListener.h create mode 100644 extern/oics/ICSInputControlSystem.cpp create mode 100644 extern/oics/ICSInputControlSystem.h create mode 100644 extern/oics/ICSInputControlSystem_joystick.cpp create mode 100644 extern/oics/ICSInputControlSystem_keyboard.cpp create mode 100644 extern/oics/ICSInputControlSystem_mouse.cpp create mode 100644 extern/oics/ICSPrerequisites.cpp create mode 100644 extern/oics/ICSPrerequisites.h create mode 100644 extern/oics/tinystr.cpp create mode 100644 extern/oics/tinystr.h create mode 100644 extern/oics/tinyxml.cpp create mode 100644 extern/oics/tinyxml.h create mode 100644 extern/oics/tinyxmlerror.cpp create mode 100644 extern/oics/tinyxmlparser.cpp create mode 100644 files/input-default.xml delete mode 100644 libs/mangle/.gitignore delete mode 100644 libs/mangle/Doxyfile delete mode 100644 libs/mangle/LICENSE.txt delete mode 100644 libs/mangle/README.txt delete mode 100644 libs/mangle/input/clients/ogre_input_capture.hpp delete mode 100644 libs/mangle/input/driver.hpp delete mode 100644 libs/mangle/input/event.hpp delete mode 100644 libs/mangle/input/filters/eventlist.hpp delete mode 100644 libs/mangle/input/servers/ois_driver.cpp delete mode 100644 libs/mangle/input/servers/ois_driver.hpp delete mode 100644 libs/mangle/input/servers/sdl_driver.cpp delete mode 100644 libs/mangle/input/servers/sdl_driver.hpp delete mode 100644 libs/mangle/input/tests/.gitignore delete mode 100644 libs/mangle/input/tests/Makefile delete mode 100644 libs/mangle/input/tests/common.cpp delete mode 100644 libs/mangle/input/tests/evtlist_test.cpp delete mode 100644 libs/mangle/input/tests/ois_driver_test.cpp delete mode 100644 libs/mangle/input/tests/output/evtlist_test.out delete mode 100644 libs/mangle/input/tests/output/ois_driver_test.out delete mode 100644 libs/mangle/input/tests/output/sdl_driver_test.out delete mode 100644 libs/mangle/input/tests/plugins.cfg delete mode 100644 libs/mangle/input/tests/sdl_driver_test.cpp delete mode 100755 libs/mangle/input/tests/test.sh delete mode 100644 libs/mangle/rend2d/driver.hpp delete mode 100644 libs/mangle/rend2d/servers/sdl_driver.cpp delete mode 100644 libs/mangle/rend2d/servers/sdl_driver.hpp delete mode 100644 libs/mangle/rend2d/servers/sdl_gl_driver.cpp delete mode 100644 libs/mangle/rend2d/servers/sdl_gl_driver.hpp delete mode 100644 libs/mangle/rend2d/sprite.hpp delete mode 100644 libs/mangle/rend2d/tests/.gitignore delete mode 100644 libs/mangle/rend2d/tests/Makefile delete mode 100644 libs/mangle/rend2d/tests/output/sdl_move_test.out delete mode 100644 libs/mangle/rend2d/tests/output/sdl_test.out delete mode 100644 libs/mangle/rend2d/tests/output/sdlgl_move_test.out delete mode 100644 libs/mangle/rend2d/tests/sdl_move_test.cpp delete mode 100644 libs/mangle/rend2d/tests/sdl_test.cpp delete mode 100644 libs/mangle/rend2d/tests/sdlgl_move_test.cpp delete mode 100755 libs/mangle/rend2d/tests/test.sh delete mode 100644 libs/mangle/rend2d/tests/tile1-blue.png delete mode 100644 libs/mangle/rend2d/tests/tile1-yellow.png delete mode 100755 libs/mangle/testall.sh delete mode 100644 libs/mangle/tests/.gitignore delete mode 100644 libs/mangle/tests/Makefile delete mode 100644 libs/mangle/tests/ogrevfs_audiere_openal_test.cpp delete mode 100644 libs/mangle/tests/output/ogrevfs_audiere_openal_test.out delete mode 100644 libs/mangle/tests/sound.zip delete mode 100755 libs/mangle/tests/test.sh delete mode 100644 libs/mangle/tools/shared_ptr.hpp delete mode 100644 libs/openengine/gui/events.cpp delete mode 100644 libs/openengine/gui/events.hpp delete mode 100644 libs/openengine/ogre/mouselook.cpp delete mode 100644 libs/openengine/ogre/mouselook.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb983..79e33f1815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,19 +96,13 @@ ENDIF() set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) -set(MANGLE_INPUT ${LIBDIR}/mangle/input/servers/ois_driver.cpp) -set(MANGLE_ALL ${MANGLE_INPUT}) -source_group(libs\\mangle FILES ${MANGLE_ALL}) - set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp - ${LIBDIR}/openengine/ogre/mouselook.cpp ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/atlas.cpp ) set(OENGINE_GUI - ${LIBDIR}/openengine/gui/events.cpp ${LIBDIR}/openengine/gui/manager.cpp ) @@ -135,7 +129,7 @@ set(OENGINE_BULLET set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_BULLET}) source_group(libs\\openengine FILES ${OENGINE_ALL}) -set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL}) +set(OPENMW_LIBS ${OENGINE_ALL}) set(OPENMW_LIBS_HEADER) # Sound setup @@ -291,6 +285,9 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") +configure_file(${OpenMW_SOURCE_DIR}/files/input-default.xml + "${OpenMW_BINARY_DIR}/input-default.xml") + configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg "${OpenMW_BINARY_DIR}/transparency-overrides.cfg") @@ -443,6 +440,7 @@ endif(WIN32) # Extern add_subdirectory (extern/shiny) +add_subdirectory (extern/oics) # Components add_subdirectory (components) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 02fe0b72c7..72c5117d24 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -104,6 +104,7 @@ target_link_libraries(openmw ${MYGUI_PLATFORM_LIBRARIES} "shiny" "shiny.OgrePlatform" + "oics" components ) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 75835120fd..128d975534 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -67,7 +67,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.setFrameDuration (evt.timeSinceLastFrame); // update input - MWBase::Environment::get().getInputManager()->update(); + MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame); // sound if (mUseSound) @@ -270,6 +270,24 @@ void OMW::Engine::go() else if (boost::filesystem::exists(globaldefault)) settings.loadUser(globaldefault); + // Get the path for the keybinder xml file + std::string keybinderDefault; + + // load user settings if they exist, otherwise just load the default settings as user settings + const std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + const std::string keybinderDefaultLocal = (mCfgMgr.getLocalPath() / "input-default.xml").string(); + const std::string keybinderDefaultGlobal = (mCfgMgr.getGlobalPath() / "input-default.xml").string(); + + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); + + if (boost::filesystem::exists(keybinderDefaultLocal)) + keybinderDefault = keybinderDefaultLocal; + else if (boost::filesystem::exists(keybinderDefaultGlobal)) + keybinderDefault = keybinderDefaultGlobal; + else + throw std::runtime_error ("No default input settings found! Make sure the file \"input-default.xml\" was properly installed."); + + mFpsLevel = settings.getInt("fps", "HUD"); // load nif overrides @@ -366,9 +384,9 @@ void OMW::Engine::go() // Sets up the input system - mEnvironment.setInputManager (new MWInput::MWInputManager (*mOgre, + mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderDefault, keybinderUser, keybinderUserExists)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index d865bfb0ec..5d73025a7d 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -22,7 +22,7 @@ namespace MWBase virtual ~InputManager() {} - virtual void update() = 0; + virtual void update(float dt) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8def5c74d4..d1c400ceab 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,357 +1,200 @@ #include "inputmanagerimp.hpp" #include +#include -#include -#include +#include +#include -#include +#include + +#include +#include + +#include #include -#include "../mwbase/windowmanager.hpp" - -#include -#include - -#include - -#include "mouselookevent.hpp" - #include "../engine.hpp" #include "../mwworld/player.hpp" #include "../mwbase/world.hpp" - -#include -#include -#include -#include +#include "../mwbase/windowmanager.hpp" namespace MWInput { - enum Actions + InputManager::InputManager(OEngine::Render::OgreRenderer &ogre, + MWWorld::Player &player, + MWBase::WindowManager &windows, + bool debug, + OMW::Engine& engine, + const std::string& defaultFile, + const std::string& userFile, bool userFileExists) + : mOgre(ogre) + , mPlayer(player) + , mWindows(windows) + , mEngine(engine) + , mMouseLookEnabled(true) + , mMouseX(ogre.getWindow()->getWidth ()/2.f) + , mMouseY(ogre.getWindow()->getHeight ()/2.f) + , mUserFile(userFile) + , mDragDrop(false) { - A_Quit, // Exit the program + Ogre::RenderWindow* window = ogre.getWindow (); + size_t windowHnd; - A_Screenshot, // Take a screenshot + window->getCustomAttribute("WINDOW", &windowHnd); - A_Inventory, // Toggle inventory screen + std::ostringstream windowHndStr; + OIS::ParamList pl; - A_Console, // Toggle console screen + windowHndStr << windowHnd; + pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); - A_MoveLeft, // Move player left / right - A_MoveRight, - A_MoveForward, // Forward / Backward - A_MoveBackward, - - A_Activate, - - A_Use, //Use weapon, spell, etc. - A_Jump, - A_AutoMove, //Toggle Auto-move forward - A_Rest, //Rest - A_Journal, //Journal - A_Weapon, //Draw/Sheath weapon - A_Spell, //Ready/Unready Casting - A_AlwaysRun, //Toggle Always Run - A_CycleSpellLeft, //cycling through spells - A_CycleSpellRight, - A_CycleWeaponLeft,//Cycling through weapons - A_CycleWeaponRight, - A_ToggleSneak, //Toggles Sneak, add Push-Sneak later - A_ToggleWalk, //Toggle Walking/Running - A_Crouch, - - A_QuickSave, - A_QuickLoad, - A_QuickMenu, - A_GameMenu, - A_ToggleWeapon, - A_ToggleSpell, - - A_LAST // Marker for the last item - }; - - // Class that handles all input and key bindings for OpenMW - class InputImpl - { - OEngine::Input::DispatcherPtr disp; - OEngine::Render::OgreRenderer &ogre; - Mangle::Input::OISDriver input; - OEngine::Input::Poller poller; - MouseLookEventPtr mouse; - OEngine::GUI::EventInjectorPtr guiEvents; - MWWorld::Player &player; - MWBase::WindowManager &windows; - OMW::Engine& mEngine; - - bool mDragDrop; - - std::map mControlSwitch; - - /* InputImpl Methods */ -public: - void adjustMouseRegion(int width, int height) - { - input.adjustMouseClippingSize(width, height); - } -private: - void toggleSpell() - { - if (windows.isGuiMode()) return; - - MWMechanics::DrawState_ state = player.getDrawState(); - if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) + // Set non-exclusive mouse and keyboard input if the user requested + // it. + if (debug) { - player.setDrawState(MWMechanics::DrawState_Spell); - std::cout << "Player has now readied his hands for spellcasting!\n"; + #if defined OIS_WIN32_PLATFORM + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_FOREGROUND" ))); + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_NONEXCLUSIVE"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_FOREGROUND"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_NONEXCLUSIVE"))); + #elif defined OIS_LINUX_PLATFORM + pl.insert(std::make_pair(std::string("x11_mouse_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_mouse_hide"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_keyboard_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("XAutoRepeatOn"), + std::string("true"))); + #endif } + + #ifdef __APPLE_CC__ + // Give the application window focus to receive input events + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + #endif + + mInputManager = OIS::InputManager::createInputSystem( pl ); + + // Create all devices + mKeyboard = static_cast(mInputManager->createInputObject + ( OIS::OISKeyboard, true )); + mMouse = static_cast(mInputManager->createInputObject + ( OIS::OISMouse, true )); + + mKeyboard->setEventCallback (this); + mMouse->setEventCallback (this); + + adjustMouseRegion (window->getWidth(), window->getHeight()); + + MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); + + std::string configFile; + if (userFileExists) + configFile = userFile; else + configFile = defaultFile; + + std::cout << "Loading input configuration: " << configFile << std::endl; + + mInputCtrl = new ICS::InputControlSystem(configFile, true, NULL, NULL, A_LAST); + + for (int i = 0; i < A_LAST; ++i) { - player.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n"; + mInputCtrl->getChannel (i)->addListener (this); } + + mControlSwitch["playercontrols"] = true; + mControlSwitch["playerfighting"] = true; + mControlSwitch["playerjumping"] = true; + mControlSwitch["playerlooking"] = true; + mControlSwitch["playermagic"] = true; + mControlSwitch["playerviewswitch"] = true; + mControlSwitch["vanitymode"] = true; + + changeInputMode(false); } - void toggleWeapon() + InputManager::~InputManager() { - if (windows.isGuiMode()) return; + mInputCtrl->save (mUserFile); - MWMechanics::DrawState_ state = player.getDrawState(); - if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - { - player.setDrawState(MWMechanics::DrawState_Weapon); - std::cout << "Player is now drawing his weapon.\n"; - } - else - { - player.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n"; - } + delete mInputCtrl; + + mInputManager->destroyInputObject(mKeyboard); + mInputManager->destroyInputObject(mMouse); + OIS::InputManager::destroyInputSystem(mInputManager); } - void screenshot() + void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) { - mEngine.screenshot(); - - std::vector empty; - windows.messageBox ("Screenshot saved", empty); - } - - /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ - void toggleInventory() - { - using namespace MWGui; - if (mDragDrop) return; - bool gameMode = !windows.isGuiMode(); + int action = channel->getNumber(); + if (currentValue == 1) + { + // trigger action activated - // Toggle between game mode and inventory mode - if(gameMode) - windows.pushGuiMode(GM_Inventory); - else if(windows.getMode() == GM_Inventory) - windows.popGuiMode(); - - // .. but don't touch any other mode. + switch (action) + { + case A_GameMenu: + toggleMainMenu (); + break; + case A_Quit: + exitNow(); + break; + case A_Screenshot: + screenshot(); + break; + case A_Inventory: + toggleInventory (); + break; + case A_Console: + toggleConsole (); + break; + case A_Activate: + activate(); + break; + case A_Journal: + toggleJournal (); + break; + case A_AutoMove: + toggleAutoMove (); + break; + case A_ToggleSneak: + /// \todo implement + break; + case A_ToggleWalk: + toggleWalking (); + break; + case A_ToggleWeapon: + toggleWeapon (); + break; + case A_ToggleSpell: + toggleSpell (); + break; + } + } } - // Toggle console - void toggleConsole() - { - using namespace MWGui; - - if (mDragDrop) - return; - - bool gameMode = !windows.isGuiMode(); - - // Switch to console mode no matter what mode we are currently - // in, except of course if we are already in console mode - if (!gameMode) - { - if (windows.getMode() == GM_Console) - windows.popGuiMode(); - else - windows.pushGuiMode(GM_Console); - } - else - windows.pushGuiMode(GM_Console); - } - - void toggleJournal() - { - using namespace MWGui; - - // Toggle between game mode and journal mode - bool gameMode = !windows.isGuiMode(); - - if(gameMode) - windows.pushGuiMode(GM_Journal); - else if(windows.getMode() == GM_Journal) - windows.popGuiMode(); - // .. but don't touch any other mode. - } - - void activate() - { - mEngine.activate(); - } - - void toggleAutoMove() - { - if (windows.isGuiMode()) return; - player.setAutoMove (!player.getAutoMove()); - } - - void toggleWalking() - { - if (windows.isGuiMode()) return; - player.toggleRunning(); - } - - void toggleMainMenu() - { - if (windows.isGuiMode () && (windows.getMode () == MWGui::GM_MainMenu || windows.getMode () == MWGui::GM_Settings)) - windows.popGuiMode(); - else - windows.pushGuiMode (MWGui::GM_MainMenu); - } - - // Exit program now button (which is disabled in GUI mode) - void exitNow() - { - if(!windows.isGuiMode()) - Ogre::Root::getSingleton().queueEndRendering (); - } - - public: - InputImpl(OEngine::Render::OgreRenderer &_ogre, - MWWorld::Player &_player, - MWBase::WindowManager &_windows, - bool debug, - OMW::Engine& engine) - : ogre(_ogre), - input(ogre.getWindow(), !debug), - poller(input), - player(_player), - windows(_windows), - mEngine (engine), - mDragDrop(false) - { - using namespace OEngine::Input; - using namespace OEngine::Render; - using namespace OEngine::GUI; - using namespace Mangle::Input; - using namespace OIS; - - disp = DispatcherPtr(new Dispatcher(A_LAST)); - - // Bind MW-specific functions - disp->funcs.bind(A_Quit, boost::bind(&InputImpl::exitNow, this), - "Quit program"); - disp->funcs.bind(A_Screenshot, boost::bind(&InputImpl::screenshot, this), - "Screenshot"); - disp->funcs.bind(A_Inventory, boost::bind(&InputImpl::toggleInventory, this), - "Toggle inventory screen"); - disp->funcs.bind(A_Console, boost::bind(&InputImpl::toggleConsole, this), - "Toggle console"); - disp->funcs.bind(A_Journal, boost::bind(&InputImpl::toggleJournal, this), - "Toggle journal"); - disp->funcs.bind(A_Activate, boost::bind(&InputImpl::activate, this), - "Activate"); - disp->funcs.bind(A_AutoMove, boost::bind(&InputImpl::toggleAutoMove, this), - "Auto Move"); - disp->funcs.bind(A_ToggleWalk, boost::bind(&InputImpl::toggleWalking, this), - "Toggle Walk/Run"); - disp->funcs.bind(A_ToggleWeapon,boost::bind(&InputImpl::toggleWeapon,this), - "Draw Weapon"); - disp->funcs.bind(A_ToggleSpell,boost::bind(&InputImpl::toggleSpell,this), - "Ready hands"); - disp->funcs.bind(A_GameMenu, boost::bind(&InputImpl::toggleMainMenu, this), - "Toggle main menu"); - - mouse = MouseLookEventPtr(new MouseLookEvent()); - - // This event handler pumps events into MyGUI - guiEvents = EventInjectorPtr(new EventInjector(windows.getGui())); - - // Hook 'mouse' and 'disp' up as event handlers into 'input' - // (the OIS driver and event source.) We do this through an - // EventList which dispatches the event to multiple handlers for - // us. - { - EventList *lst = new EventList; - input.setEvent(EventPtr(lst)); - lst->add(mouse,Event::EV_MouseMove); - lst->add(disp,Event::EV_KeyDown); - lst->add(guiEvents,Event::EV_ALL); - } - - mControlSwitch["playercontrols"] = true; - mControlSwitch["playerfighting"] = true; - mControlSwitch["playerjumping"] = true; - mControlSwitch["playerlooking"] = true; - mControlSwitch["playermagic"] = true; - mControlSwitch["playerviewswitch"] = true; - mControlSwitch["vanitymode"] = true; - - changeInputMode(false); - - /********************************** - Key binding section - - The rest of this function has hard coded key bindings, and is - intended to be replaced by user defined bindings later. - **********************************/ - - // Key bindings for keypress events - // NOTE: These keys do not require constant polling - use in conjuction with variables in loops. - - disp->bind(A_Quit, KC_Q); - disp->bind(A_GameMenu, KC_ESCAPE); - disp->bind(A_Screenshot, KC_SYSRQ); - disp->bind(A_Inventory, KC_I); - disp->bind(A_Console, KC_F1); - disp->bind(A_Journal, KC_J); - disp->bind(A_Activate, KC_SPACE); - disp->bind(A_AutoMove, KC_Z); - disp->bind(A_ToggleSneak, KC_X); - disp->bind(A_ToggleWalk, KC_C); - disp->bind(A_ToggleWeapon,KC_F); - disp->bind(A_ToggleSpell,KC_R); - - // Key bindings for polled keys - // NOTE: These keys are constantly being polled. Only add keys that must be checked each frame. - - // Arrow keys - poller.bind(A_MoveLeft, KC_LEFT); - poller.bind(A_MoveRight, KC_RIGHT); - poller.bind(A_MoveForward, KC_UP); - poller.bind(A_MoveBackward, KC_DOWN); - - // WASD keys - poller.bind(A_MoveLeft, KC_A); - poller.bind(A_MoveRight, KC_D); - poller.bind(A_MoveForward, KC_W); - poller.bind(A_MoveBackward, KC_S); - - poller.bind(A_Jump, KC_E); - poller.bind(A_Crouch, KC_LCONTROL); - } - - void setDragDrop(bool dragDrop) - { - mDragDrop = dragDrop; - } - - //NOTE: Used to check for movement keys - void update () + void InputManager::update(float dt) { // Tell OIS to handle all input events - input.capture(); + mKeyboard->capture(); + mMouse->capture(); + + // update values of channels (as a result of pressed keys) + mInputCtrl->update(dt); // Update windows/gui as a result of input events // For instance this could mean opening a new window/dialog, @@ -359,150 +202,314 @@ private: // ensure that window/gui changes appear quickly while // avoiding that window/gui changes does not happen in // event callbacks (which may crash) - windows.update(); + mWindows.update(); // Disable movement in Gui mode + if (mWindows.isGuiMode()) return; - if (windows.isGuiMode()) return; // Configure player movement according to keyboard input. Actual movement will // be done in the physics system. - if (mControlSwitch["playercontrols"]) { - if (poller.isDown(A_MoveLeft)) + if (mControlSwitch["playercontrols"]) + { + if (actionIsActive(A_MoveLeft)) { - player.setAutoMove (false); - player.setLeftRight (1); + mPlayer.setAutoMove (false); + mPlayer.setLeftRight (1); } - else if (poller.isDown(A_MoveRight)) + else if (actionIsActive(A_MoveRight)) { - player.setAutoMove (false); - player.setLeftRight (-1); + mPlayer.setAutoMove (false); + mPlayer.setLeftRight (-1); } else - player.setLeftRight (0); + mPlayer.setLeftRight (0); - if (poller.isDown(A_MoveForward)) + if (actionIsActive(A_MoveForward)) { - player.setAutoMove (false); - player.setForwardBackward (1); + mPlayer.setAutoMove (false); + mPlayer.setForwardBackward (1); } - else if (poller.isDown(A_MoveBackward)) + else if (actionIsActive(A_MoveBackward)) { - player.setAutoMove (false); - player.setForwardBackward (-1); + mPlayer.setAutoMove (false); + mPlayer.setForwardBackward (-1); } else - player.setForwardBackward (0); + mPlayer.setForwardBackward (0); - if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) - player.setUpDown (1); - else if (poller.isDown(A_Crouch)) - player.setUpDown (-1); + if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) + mPlayer.setUpDown (1); + else if (actionIsActive(A_Crouch)) + mPlayer.setUpDown (-1); else - player.setUpDown (0); + mPlayer.setUpDown (0); } + } - // Switch between gui modes. Besides controlling the Gui windows - // this also makes sure input is directed to the right place - void changeInputMode(bool guiMode) + void InputManager::setDragDrop(bool dragDrop) { - // Are we in GUI mode now? - if(guiMode) - { - // Disable mouse look - mouse->disable(); + mDragDrop = dragDrop; + } - // Enable GUI events - guiEvents->enabled = true; + void InputManager::changeInputMode(bool guiMode) + { + // Are we in GUI mode now? + if(guiMode) + { + // Disable mouse look + mMouseLookEnabled = false; + + // Enable GUI events + mGuiCursorEnabled = true; } - else + else { // Start mouse-looking again if allowed. if (mControlSwitch["playerlooking"]) { - mouse->enable(); + mMouseLookEnabled = true; } - // Disable GUI events - guiEvents->enabled = false; + // Disable GUI events + mGuiCursorEnabled = false; } } - void toggleControlSwitch(std::string sw, bool value) + void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + bool changeRes = false; + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); + it != changed.end(); ++it) + { + if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y")) + changeRes = true; + } + + if (changeRes) + adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); + } + + void InputManager::toggleControlSwitch (const std::string& sw, bool value) { if (mControlSwitch[sw] == value) { return; } /// \note 7 switches at all, if-else is relevant if (sw == "playercontrols" && !value) { - player.setLeftRight(0); - player.setForwardBackward(0); - player.setAutoMove(false); - player.setUpDown(0); + mPlayer.setLeftRight(0); + mPlayer.setForwardBackward(0); + mPlayer.setAutoMove(false); + mPlayer.setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time - player.setUpDown(0); + mPlayer.setUpDown(0); } else if (sw == "playerlooking") { if (value) { - mouse->enable(); + mMouseLookEnabled = true; } else { - mouse->disable(); + mMouseLookEnabled = false; } } mControlSwitch[sw] = value; } - }; - - /***CONSTRUCTOR***/ - MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre, - MWWorld::Player &player, - MWBase::WindowManager &windows, - bool debug, - OMW::Engine& engine) - { - impl = new InputImpl(ogre,player,windows,debug, engine); - } - - /***DESTRUCTOR***/ - MWInputManager::~MWInputManager() - { - delete impl; - } - - void MWInputManager::update() - { - impl->update(); - } - - void MWInputManager::setDragDrop(bool dragDrop) - { - impl->setDragDrop(dragDrop); - } - - void MWInputManager::changeInputMode(bool guiMode) - { - impl->changeInputMode(guiMode); - } - - void MWInputManager::processChangedSettings(const Settings::CategorySettingVector& changed) - { - bool changeRes = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) - { - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) - changeRes = true; - } - - if (changeRes) - impl->adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); - } - - void MWInputManager::toggleControlSwitch (const std::string& sw, bool value) + void InputManager::adjustMouseRegion(int width, int height) { - impl->toggleControlSwitch(sw, value); + const OIS::MouseState &ms = mMouse->getMouseState(); + ms.width = width; + ms.height = height; } + + bool InputManager::keyPressed( const OIS::KeyEvent &arg ) + { + mInputCtrl->keyPressed (arg); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + + return true; + } + + bool InputManager::keyReleased( const OIS::KeyEvent &arg ) + { + mInputCtrl->keyReleased (arg); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); + + return true; + } + + bool InputManager::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + mInputCtrl->mousePressed (arg, id); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + + return true; + } + + bool InputManager::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + mInputCtrl->mouseReleased (arg, id); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + + return true; + } + + bool InputManager::mouseMoved( const OIS::MouseEvent &arg ) + { + mInputCtrl->mouseMoved (arg); + + if (mGuiCursorEnabled) + { + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // We keep track of our own mouse position, so that moving the mouse while in + // game mode does not move the position of the GUI cursor + mMouseX += arg.state.X.rel; + mMouseY += arg.state.Y.rel; + mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); + mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); + + MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, arg.state.Z.abs); + } + + if (mMouseLookEnabled) + { + float x = arg.state.X.rel * 0.2; + float y = arg.state.Y.rel * 0.2; + + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + } + + return true; + } + + void InputManager::toggleMainMenu() + { + if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) + mWindows.popGuiMode(); + else + mWindows.pushGuiMode (MWGui::GM_MainMenu); + } + + void InputManager::toggleSpell() + { + if (mWindows.isGuiMode()) return; + + MWMechanics::DrawState_ state = mPlayer.getDrawState(); + if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) + { + mPlayer.setDrawState(MWMechanics::DrawState_Spell); + std::cout << "Player has now readied his hands for spellcasting!\n" << std::endl; + } + else + { + mPlayer.setDrawState(MWMechanics::DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; + } + } + + void InputManager::toggleWeapon() + { + if (mWindows.isGuiMode()) return; + + MWMechanics::DrawState_ state = mPlayer.getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + { + mPlayer.setDrawState(MWMechanics::DrawState_Weapon); + std::cout << "Player is now drawing his weapon.\n" << std::endl; + } + else + { + mPlayer.setDrawState(MWMechanics::DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; + } + } + + void InputManager::screenshot() + { + mEngine.screenshot(); + + std::vector empty; + mWindows.messageBox ("Screenshot saved", empty); + } + + void InputManager::toggleInventory() + { + bool gameMode = !mWindows.isGuiMode(); + + // Toggle between game mode and inventory mode + if(gameMode) + mWindows.pushGuiMode(MWGui::GM_Inventory); + else if(mWindows.getMode() == MWGui::GM_Inventory) + mWindows.popGuiMode(); + + // .. but don't touch any other mode. + } + + void InputManager::toggleConsole() + { + bool gameMode = !mWindows.isGuiMode(); + + // Switch to console mode no matter what mode we are currently + // in, except of course if we are already in console mode + if (!gameMode) + { + if (mWindows.getMode() == MWGui::GM_Console) + mWindows.popGuiMode(); + else + mWindows.pushGuiMode(MWGui::GM_Console); + } + else + mWindows.pushGuiMode(MWGui::GM_Console); + } + + void InputManager::toggleJournal() + { + // Toggle between game mode and journal mode + bool gameMode = !mWindows.isGuiMode(); + + if(gameMode) + mWindows.pushGuiMode(MWGui::GM_Journal); + else if(mWindows.getMode() == MWGui::GM_Journal) + mWindows.popGuiMode(); + // .. but don't touch any other mode. + } + + void InputManager::activate() + { + mEngine.activate(); + } + + void InputManager::toggleAutoMove() + { + if (mWindows.isGuiMode()) return; + mPlayer.setAutoMove (!mPlayer.getAutoMove()); + } + + void InputManager::toggleWalking() + { + if (mWindows.isGuiMode()) return; + mPlayer.toggleRunning(); + } + + // Exit program now button (which is disabled in GUI mode) + void InputManager::exitNow() + { + if(!mWindows.isGuiMode()) + Ogre::Root::getSingleton().queueEndRendering (); + } + + bool InputManager::actionIsActive (int id) + { + return mInputCtrl->getChannel (id)->getValue () == 1; + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 5092198da8..ba42327eeb 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -9,20 +9,20 @@ namespace OEngine { - namespace Render - { - class OgreRenderer; - } + namespace Render + { + class OgreRenderer; + } } namespace MWWorld { - class Player; + class Player; } namespace MWBase { - class WindowManager; + class WindowManager; } namespace OMW @@ -30,37 +30,154 @@ namespace OMW class Engine; } +namespace ICS +{ + class InputControlSystem; +} + +namespace OIS +{ + class Keyboard; + class Mouse; + class InputManager; +} + +#include +#include + +#include + namespace MWInput { - // Forward declaration of the real implementation. - class InputImpl; - /* Class that handles all input and key bindings for OpenMW. + /** + * @brief Class that handles all input and key bindings for OpenMW. + */ + class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener + { + public: + InputManager(OEngine::Render::OgreRenderer &_ogre, + MWWorld::Player&_player, + MWBase::WindowManager &_windows, + bool debug, + OMW::Engine& engine, + const std::string& defaultFile, + const std::string& userFile, bool userFileExists); - This class is just an interface. All the messy details are in - inputmanager.cpp. - */ - struct MWInputManager : public MWBase::InputManager - { - InputImpl *impl; + virtual ~InputManager(); - public: - MWInputManager(OEngine::Render::OgreRenderer &_ogre, - MWWorld::Player&_player, - MWBase::WindowManager &_windows, - bool debug, - OMW::Engine& engine); - virtual ~MWInputManager(); + virtual void update(float dt); - virtual void update(); + virtual void changeInputMode(bool guiMode); - virtual void changeInputMode(bool guiMode); + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); - virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void setDragDrop(bool dragDrop); - virtual void setDragDrop(bool dragDrop); + virtual void toggleControlSwitch (const std::string& sw, bool value); - virtual void toggleControlSwitch (const std::string& sw, bool value); - }; + + public: + virtual bool keyPressed( const OIS::KeyEvent &arg ); + virtual bool keyReleased( const OIS::KeyEvent &arg ); + + virtual bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); + virtual bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); + virtual bool mouseMoved( const OIS::MouseEvent &arg ); + + virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); + + private: + OEngine::Render::OgreRenderer &mOgre; + MWWorld::Player &mPlayer; + MWBase::WindowManager &mWindows; + OMW::Engine& mEngine; + + ICS::InputControlSystem* mInputCtrl; + + OIS::Keyboard* mKeyboard; + OIS::Mouse* mMouse; + OIS::InputManager* mInputManager; + + std::string mUserFile; + + bool mDragDrop; + + bool mMouseLookEnabled; + bool mGuiCursorEnabled; + + int mMouseX; + int mMouseY; + + std::map mControlSwitch; + + + private: + void adjustMouseRegion(int width, int height); + + private: + void toggleMainMenu(); + void toggleSpell(); + void toggleWeapon(); + void toggleInventory(); + void toggleConsole(); + void screenshot(); + void toggleJournal(); + void activate(); + void toggleWalking(); + void toggleAutoMove(); + void exitNow(); + + bool actionIsActive (int id); + + private: + enum Actions + { + // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files + + A_GameMenu, + + A_Quit, // Exit the program + + A_Screenshot, // Take a screenshot + + A_Inventory, // Toggle inventory screen + + A_Console, // Toggle console screen + + A_MoveLeft, // Move player left / right + A_MoveRight, + A_MoveForward, // Forward / Backward + A_MoveBackward, + + A_Activate, + + A_Use, //Use weapon, spell, etc. + A_Jump, + A_AutoMove, //Toggle Auto-move forward + A_Rest, //Rest + A_Journal, //Journal + A_Weapon, //Draw/Sheath weapon + A_Spell, //Ready/Unready Casting + A_AlwaysRun, //Toggle Always Run + A_CycleSpellLeft, //cycling through spells + A_CycleSpellRight, + A_CycleWeaponLeft,//Cycling through weapons + A_CycleWeaponRight, + A_ToggleSneak, //Toggles Sneak, add Push-Sneak later + A_ToggleWalk, //Toggle Walking/Running + A_Crouch, + + A_QuickSave, + A_QuickLoad, + A_QuickMenu, + A_ToggleWeapon, + A_ToggleSpell, + + A_LAST // Marker for the last item + }; + + + }; } #endif diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp deleted file mode 100644 index f318ce6660..0000000000 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "mouselookevent.hpp" - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -#include "../mwworld/player.hpp" - -#include -#include -#include - -using namespace OIS; -using namespace MWInput; - -void MouseLookEvent::event(Type type, int index, const void *p) -{ - if (type != EV_MouseMove || mDisabled) { - return; - } - - MouseEvent *arg = (MouseEvent*)(p); - - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; - - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); -} diff --git a/apps/openmw/mwinput/mouselookevent.hpp b/apps/openmw/mwinput/mouselookevent.hpp deleted file mode 100644 index af996643f3..0000000000 --- a/apps/openmw/mwinput/mouselookevent.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _MWINPUT_MOUSELOOKEVENT_H -#define _MWINPUT_MOUSELOOKEVENT_H - -/* - A mouse-look class for Ogre. Accepts input events from Mangle::Input - and translates them. - - You can adjust the mouse sensibility and switch to a different - camera. The mouselook class also has an optional wrap protection - that keeps the camera from flipping upside down. - - You can disable the mouse looker at any time by calling - setCamera(NULL), and reenable it by setting the camera back. - - NOTE: The current implementation will ONLY work for native OIS - events. - */ - -#include - -namespace MWInput -{ - class MouseLookEvent : public Mangle::Input::Event - { - float sensX, sensY; // Mouse sensibility - bool flipProt; // Flip protection - bool mDisabled; - - public: - MouseLookEvent(float sX = 0.2, float sY = 0.2, bool prot=true) - : sensX(sX), sensY(sY), flipProt(prot) - {} - - void setSens(float sX, float sY) { - sensX = sX; - sensY = sY; - } - - void setProt(bool p) { - flipProt = p; - } - - void disable() { - mDisabled = true; - } - - void enable() { - mDisabled = false; - } - - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr MouseLookEventPtr; -} - -#endif diff --git a/extern/oics/CMakeLists.txt b/extern/oics/CMakeLists.txt new file mode 100644 index 0000000000..7c14387a4b --- /dev/null +++ b/extern/oics/CMakeLists.txt @@ -0,0 +1,20 @@ +set(OICS_LIBRARY "oics") + +# Sources + +set(OICS_SOURCE_FILES + ICSChannel.cpp + ICSControl.cpp + ICSInputControlSystem.cpp + ICSInputControlSystem_keyboard.cpp + ICSInputControlSystem_mouse.cpp + ICSInputControlSystem_joystick.cpp + tinyxml.cpp + tinyxmlparser.cpp + tinyxmlerror.cpp + tinystr.cpp +) + +add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES}) + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/extern/oics/ICSChannel.cpp b/extern/oics/ICSChannel.cpp new file mode 100644 index 0000000000..703f2207c9 --- /dev/null +++ b/extern/oics/ICSChannel.cpp @@ -0,0 +1,258 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +#define B1(t) (t*t) +#define B2(t) (2*t*(1-t)) +#define B3(t) ((1-t)*(1-t)) + +namespace ICS +{ + Channel::Channel(int number, float initialValue + , float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep) + : mNumber(number) + , mValue(initialValue) + , mSymmetricAt(symmetricAt) + , mBezierStep(bezierStep) + { + mBezierMidPoint.x = bezierMidPointX; + mBezierMidPoint.y = bezierMidPointY; + + setBezierFunction(bezierMidPointY, bezierMidPointX, symmetricAt, bezierStep); + } + + float Channel::getValue() + { + if(mValue == 0 || mValue == 1) + { + return mValue; + } + + BezierFunction::iterator it = mBezierFunction.begin(); + //size_t size_minus_1 = mBezierFunction.size() - 1; + BezierFunction::iterator last = mBezierFunction.end(); + last--; + for ( ; it != last ; ) + { + BezierPoint left = (*it); + BezierPoint right = (*(++it)); + + if( (left.x <= mValue) && (right.x > mValue) ) + { + float val = left.y - (left.x - mValue) * (left.y - right.y) / (left.x - right.x); + + return std::max(0.0,std::min(1.0, val)); + } + } + + return -1; + } + + void Channel::setValue(float value) + { + float previousValue = this->getValue(); + + mValue = value; + + if(previousValue != value) + { + notifyListeners(previousValue); + } + } + + void Channel::notifyListeners(float previousValue) + { + std::list::iterator pos = mListeners.begin(); + while (pos != mListeners.end()) + { + ((ChannelListener* )(*pos))->channelChanged((Channel*)this, this->getValue(), previousValue); + ++pos; + } + } + + void Channel::addControl(Control* control, Channel::ChannelDirection dir, float percentage) + { + ControlChannelBinderItem ccBinderItem; + ccBinderItem.control = control; + ccBinderItem.direction = dir; + ccBinderItem.percentage = percentage; + + mAttachedControls.push_back(ccBinderItem); + } + + Channel::ControlChannelBinderItem Channel::getAttachedControlBinding(Control* control) + { + for(std::vector::iterator it = mAttachedControls.begin() ; + it != mAttachedControls.end() ; it++) + { + if((*it).control == control) + { + return (*it); + } + } + + ControlChannelBinderItem nullBinderItem; + nullBinderItem.control = NULL; + nullBinderItem.direction = Channel/*::ChannelDirection*/::DIRECT; + nullBinderItem.percentage = 0; + return nullBinderItem; + } + + void Channel::update() + { + if(this->getControlsCount() == 1) + { + ControlChannelBinderItem ccBinderItem = mAttachedControls.back(); + float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue(); + + if(ccBinderItem.direction == ICS::Channel::DIRECT) + { + this->setValue(ccBinderItem.control->getInitialValue() + (ccBinderItem.percentage * diff)); + } + else + { + this->setValue(ccBinderItem.control->getInitialValue() - (ccBinderItem.percentage * diff)); + } + } + else + { + float val = 0; + std::vector::const_iterator it; + for(it=mAttachedControls.begin(); it!=mAttachedControls.end(); ++it) + { + ControlChannelBinderItem ccBinderItem = (*it); + float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue(); + + if(ccBinderItem.direction == ICS::Channel::DIRECT) + { + val += (ccBinderItem.percentage * diff); + } + else + { + val -= (ccBinderItem.percentage * diff); + } + } + + if(mAttachedControls.size() > 0) + { + this->setValue(mAttachedControls.begin()->control->getInitialValue() + val); + } + } + } + + void Channel::setBezierFunction(float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep) + { + mBezierMidPoint.x = bezierMidPointX; + mBezierMidPoint.y = bezierMidPointY; + mBezierStep = bezierStep; + mSymmetricAt = symmetricAt; + + mBezierFunction.clear(); + + BezierPoint start; + start.x = 0; + start.y = 0; + + BezierPoint end; + end.x = 1; + end.y = 1; + mBezierFunction.push_front(end); + + FilterInterval interval; + interval.startX = start.x; + interval.startY = start.y; + interval.midX = mBezierMidPoint.x; + interval.midY = mBezierMidPoint.y; + interval.endX = end.x; + interval.endY = end.y; + interval.step = bezierStep; + mIntervals.push_back(interval); + + if(!(mBezierMidPoint.x == 0.5 && mBezierMidPoint.y == 0.5)) + { + float t = mBezierStep; + while(t < 1) + { + BezierPoint p; + p.x = start.x * B1(t) + mBezierMidPoint.x * B2(t) + end.x * B3(t); + p.y = start.y * B1(t) + mBezierMidPoint.y * B2(t) + end.y * B3(t); + mBezierFunction.push_front(p); + + t += mBezierStep; + } + } + + mBezierFunction.push_front(start); + } + + void Channel::addBezierInterval(float startX, float startY, float midX, float midY + , float endX, float endY, float step) + { + FilterInterval interval; + interval.startX = startX; + interval.startY = startY; + interval.midX = midX; + interval.midY = midY; + interval.endX = endX; + interval.endY = endY; + interval.step = step; + mIntervals.push_back(interval); + + float t = 0; + while(t <= 1) + { + BezierPoint p; + p.x = startX * B1(t) + midX * B2(t) + endX * B3(t); + p.y = startY * B1(t) + midY * B2(t) + endY * B3(t); + + BezierFunction::iterator it = mBezierFunction.begin(); + while( it != mBezierFunction.end() ) + { + BezierPoint left = (*it); + BezierPoint right; + ++it; + if( it != mBezierFunction.end() ) + { + right = (*it); + } + else + { + right.x = endX; + right.y = endY; + } + + if(p.x > left.x && p.x < right.x) + { + mBezierFunction.insert(it, p); + break; + } + } + + t += 1.0f / ((endX-startX)/step); + } + } +} \ No newline at end of file diff --git a/extern/oics/ICSChannel.h b/extern/oics/ICSChannel.h new file mode 100644 index 0000000000..f98f0d94d3 --- /dev/null +++ b/extern/oics/ICSChannel.h @@ -0,0 +1,122 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _Channel_H_ +#define _Channel_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannelListener.h" + +namespace ICS +{ + struct FilterInterval{ + //std::string type; //! @todo uncomment when more types implemented + float startX; + float startY; + float midX; + float midY; + float endX; + float endY; + float step; + }; + + typedef std::list IntervalList; + + class DllExport Channel + { + public: + enum ChannelDirection + { + INVERSE = -1, DIRECT = 1 + }; + + typedef struct { + ChannelDirection direction; + float percentage; + Control* control; + } ControlChannelBinderItem; + + + Channel(int number, float initialValue = 0.5 + , float bezierMidPointY = 0.5, float bezierMidPointX = 0.5 + , float symmetricAt = 0, float bezierStep = 0.2); //! @todo implement symetry + ~Channel(){}; + + void setValue(float value); + float getValue(); + + inline int getNumber(){ return mNumber; }; + + void addControl(Control* control, Channel::ChannelDirection dir, float percentage); + inline size_t getControlsCount(){ return mAttachedControls.size(); }; + std::vector getAttachedControls(){ return mAttachedControls; }; + ControlChannelBinderItem getAttachedControlBinding(Control* control); + + void addListener(ChannelListener* ob){ mListeners.push_back(ob); }; + void removeListener(ChannelListener* ob){ mListeners.remove(ob); }; + + void update(); + + void setBezierFunction(float bezierMidPointY, float bezierMidPointX = 0.5 + , float symmetricAt = 0, float bezierStep = 0.2); + + void addBezierInterval(float startX, float startY, float midX, float midY + , float endX, float endY, float step = 0.1); + + IntervalList& getIntervals(){ return mIntervals; }; + + protected: + + int mNumber; + float mValue; + + struct BezierPoint{ + float x; + float y; + bool operator < (const BezierPoint& other){ return x < other.x; } + }; + + typedef std::list BezierFunction; + + BezierPoint mBezierMidPoint; + BezierFunction mBezierFunction; + float mSymmetricAt; + float mBezierStep; + + IntervalList mIntervals; + + std::vector mAttachedControls; + + std::list mListeners; + void notifyListeners(float previousValue); + + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSChannelListener.h b/extern/oics/ICSChannelListener.h new file mode 100644 index 0000000000..d520b3bceb --- /dev/null +++ b/extern/oics/ICSChannelListener.h @@ -0,0 +1,46 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _ChannelListener_H_ +#define _ChannelListener_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannel.h" + +namespace ICS +{ + + class DllExport ChannelListener + { + public: + virtual void channelChanged(Channel* channel, float currentValue, float previousValue) = 0; + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSControl.cpp b/extern/oics/ICSControl.cpp new file mode 100644 index 0000000000..d43733727f --- /dev/null +++ b/extern/oics/ICSControl.cpp @@ -0,0 +1,161 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +#include "ICSControl.h" + +namespace ICS +{ + Control::Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop, bool autoReverseToInitialValue + , float initialValue, float stepSize, float stepsPerSeconds, bool axisBindable) + : mName(name) + , mValue(initialValue) + , mInitialValue(initialValue) + , mStepSize(stepSize) + , mStepsPerSeconds(stepsPerSeconds) + , mAutoReverseToInitialValue(autoReverseToInitialValue) + , mIgnoreAutoReverse(false) + , mAutoChangeDirectionOnLimitsAfterStop(autoChangeDirectionOnLimitsAfterStop) + , mAxisBindable(axisBindable) + , currentChangingDirection(STOP) + { + + } + + Control::~Control() + { + mAttachedChannels.clear(); + } + + void Control::setValue(float value) + { + float previousValue = mValue; + + mValue = std::max(0.0,std::min(1.0,value)); + + if(mValue != previousValue) + { + updateChannels(); + + notifyListeners(previousValue); + } + } + + void Control::attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage) + { + mAttachedChannels.push_back(channel); + channel->addControl(this, direction, percentage); + } + + void Control::updateChannels() + { + std::list::iterator pos = mAttachedChannels.begin(); + while (pos != mAttachedChannels.end()) + { + ((Channel* )(*pos))->update(); + ++pos; + } + } + + void Control::notifyListeners(float previousValue) + { + std::list::iterator pos = mListeners.begin(); + while (pos != mListeners.end()) + { + ((ControlListener* )(*pos))->controlChanged((Control*)this, this->getValue(), previousValue); + ++pos; + } + } + + void Control::setChangingDirection(ControlChangingDirection direction) + { + currentChangingDirection = direction; + mPendingActions.push_back(direction); + } + + void Control::update(float timeSinceLastFrame) + { + if(mPendingActions.size() > 0) + { + size_t timedActionsCount = 0; + + std::list::iterator cached_end = mPendingActions.end(); + for(std::list::iterator it = mPendingActions.begin() ; + it != cached_end ; it++) + { + if( (*it) != Control::STOP ) + { + timedActionsCount++; + } + } + + float timeSinceLastFramePart = timeSinceLastFrame / std::max(1, timedActionsCount); + for(std::list::iterator it = mPendingActions.begin() ; + it != cached_end ; it++) + { + if( (*it) != Control::STOP ) + { + this->setValue(mValue + + (((int)(*it)) * mStepSize * mStepsPerSeconds * (timeSinceLastFramePart))); + } + else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue ) + { + + if(mValue > mInitialValue) + { + this->setValue( std::max( mInitialValue, + mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)))); + } + else if(mValue < mInitialValue) + { + this->setValue( std::min( mInitialValue, + mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)))); + } + } + } + mPendingActions.clear(); + } + else if( currentChangingDirection != Control::STOP ) + { + this->setValue(mValue + + (((int)currentChangingDirection) * mStepSize * mStepsPerSeconds * (timeSinceLastFrame))); + } + else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue ) + { + if(mValue > mInitialValue) + { + this->setValue( std::max( mInitialValue, + mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFrame)))); + } + else if(mValue < mInitialValue) + { + this->setValue( std::min( mInitialValue, + mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFrame)))); + } + } + } +} diff --git a/extern/oics/ICSControl.h b/extern/oics/ICSControl.h new file mode 100644 index 0000000000..73f1d5494b --- /dev/null +++ b/extern/oics/ICSControl.h @@ -0,0 +1,107 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _Control_H_ +#define _Control_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannel.h" +#include "ICSControlListener.h" + +namespace ICS +{ + + class DllExport Control + { + public: + + enum ControlChangingDirection + { + DECREASE = -1, STOP = 0, INCREASE = 1 + }; + + Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop = false, bool autoReverseToInitialValue = false, float initialValue = 0.5, float stepSize = 0.1, float stepsPerSeconds = 2.0, bool axisBindable = true); + ~Control(); + + void setChangingDirection(ControlChangingDirection direction); + inline ControlChangingDirection getChangingDirection(){ return currentChangingDirection; }; + + void setValue(float value); + inline float getValue(){ return mValue; }; + inline float getInitialValue(){ return mInitialValue; }; + + void attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage = 1.0); + std::list getAttachedChannels(){ return mAttachedChannels; }; + + inline float getStepSize(){ return mStepSize; }; + inline float getStepsPerSeconds(){ return mStepsPerSeconds; }; + + inline void setIgnoreAutoReverse(bool value){ mIgnoreAutoReverse = value; }; // mouse disable autoreverse + inline bool isAutoReverseIgnored(){ return mIgnoreAutoReverse; }; + inline bool getAutoReverse(){ return mAutoReverseToInitialValue; }; + + inline bool getAutoChangeDirectionOnLimitsAfterStop(){ return mAutoChangeDirectionOnLimitsAfterStop; }; + + inline std::string getName(){ return mName; }; + + inline bool isAxisBindable(){ return mAxisBindable; }; + inline void setAxisBindable(bool value){ mAxisBindable = value; }; + + inline void addListener(ControlListener* ob){ mListeners.push_back(ob); }; + inline void removeListener(ControlListener* ob){ mListeners.remove(ob); }; + + void update(float timeSinceLastFrame); + + protected: + float mValue; + float mInitialValue; + std::string mName; + float mStepSize; + float mStepsPerSeconds; + bool mAutoReverseToInitialValue; + bool mIgnoreAutoReverse; + bool mAutoChangeDirectionOnLimitsAfterStop; + bool mAxisBindable; + + Control::ControlChangingDirection currentChangingDirection; + std::list mAttachedChannels; + + std::list mListeners; + + std::list mPendingActions; + + protected: + + void updateChannels(); + void notifyListeners(float previousValue); + + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSControlListener.h b/extern/oics/ICSControlListener.h new file mode 100644 index 0000000000..067b2d6f24 --- /dev/null +++ b/extern/oics/ICSControlListener.h @@ -0,0 +1,46 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _ControlListener_H_ +#define _ControlListener_H_ + +#include "ICSPrerequisites.h" + +#include "ICSControl.h" + +namespace ICS +{ + + class DllExport ControlListener + { + public: + virtual void controlChanged(Control* control, float currentValue, float previousValue) = 0; + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp new file mode 100644 index 0000000000..1702c853ed --- /dev/null +++ b/extern/oics/ICSInputControlSystem.cpp @@ -0,0 +1,929 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + InputControlSystem::InputControlSystem(std::string file, bool active + , DetectingBindingListener* detectingBindingListener + , InputControlSystemLog* log, size_t channelCount) + : mFileName(file) + , mDetectingBindingListener(detectingBindingListener) + , mDetectingBindingControl(NULL) + , mLog(log) + , mXmouseAxisBinded(false), mYmouseAxisBinded(false) + { + ICS_LOG(" - Creating InputControlSystem - "); + + this->mActive = active; + + this->fillOISKeysMap(); + + ICS_LOG("Channel count = " + ToString(channelCount) ); + for(size_t i=0;iLoadFile(); + + if(xmlDoc->Error()) + { + std::ostringstream message; + message << "TinyXml reported an error reading \""+ file + "\". Row " << + (int)xmlDoc->ErrorRow() << ", Col " << (int)xmlDoc->ErrorCol() << ": " << + xmlDoc->ErrorDesc() ; + ICS_LOG(message.str()); + + delete xmlDoc; + return; + } + + xmlRoot = xmlDoc->RootElement(); + if(std::string(xmlRoot->Value()) != "Controller") { + ICS_LOG("Error: Invalid Controller file. Missing element."); + delete xmlDoc; + return; + } + + TiXmlElement* xmlControl = xmlRoot->FirstChildElement("Control"); + + size_t controlChannelCount = 0; + while(xmlControl) + { + TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel"); + while(xmlChannel) + { + controlChannelCount = std::max(channelCount, FromString(xmlChannel->Attribute("number"))); + + xmlChannel = xmlChannel->NextSiblingElement("Channel"); + } + + xmlControl = xmlControl->NextSiblingElement("Control"); + } + + if(controlChannelCount > channelCount) + { + size_t dif = controlChannelCount - channelCount; + ICS_LOG("Warning: default channel count exceeded. Adding " + ToString(dif) + " channels" ); + for(size_t i = channelCount ; i < controlChannelCount ; i++) + { + mChannels.push_back(new Channel((int)i)); + } + } + + ICS_LOG("Applying filters to channels"); + // + // + // + // + + TiXmlElement* xmlChannelFilter = xmlRoot->FirstChildElement("ChannelFilter"); + while(xmlChannelFilter) + { + int ch = FromString(xmlChannelFilter->Attribute("number")); + + TiXmlElement* xmlInterval = xmlChannelFilter->FirstChildElement("Interval"); + while(xmlInterval) + { + std::string type = xmlInterval->Attribute("type"); + + if(type == "bezier") + { + float step = 0.1; + + float startX = FromString(xmlInterval->Attribute("startX")); + float startY = FromString(xmlInterval->Attribute("startY")); + float midX = FromString(xmlInterval->Attribute("midX")); + float midY = FromString(xmlInterval->Attribute("midY")); + float endX = FromString(xmlInterval->Attribute("endX")); + float endY = FromString(xmlInterval->Attribute("endY")); + + step = FromString(xmlInterval->Attribute("step")); + + ICS_LOG("Applying Bezier filter to channel [number=" + + ToString(ch) + ", startX=" + + ToString(startX) + ", startY=" + + ToString(startY) + ", midX=" + + ToString(midX) + ", midY=" + + ToString(midY) + ", endX=" + + ToString(endX) + ", endY=" + + ToString(endY) + ", step=" + + ToString(step) + "]"); + + mChannels.at(ch)->addBezierInterval(startX, startY, midX, midY, endX, endY, step); + } + + xmlInterval = xmlInterval->NextSiblingElement("Interval"); + } + + + xmlChannelFilter = xmlChannelFilter->NextSiblingElement("ChannelFilter"); + } + + xmlControl = xmlRoot->FirstChildElement("Control"); + while(xmlControl) + { + bool axisBindable = true; + if(xmlControl->Attribute("axisBindable")) + { + axisBindable = (std::string( xmlControl->Attribute("axisBindable") ) == "true"); + } + + ICS_LOG("Adding Control [name=" + + std::string( xmlControl->Attribute("name") ) + ", autoChangeDirectionOnLimitsAfterStop=" + + std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) + ", autoReverseToInitialValue=" + + std::string( xmlControl->Attribute("autoReverseToInitialValue") ) + ", initialValue=" + + std::string( xmlControl->Attribute("initialValue") ) + ", stepSize=" + + std::string( xmlControl->Attribute("stepSize") ) + ", stepsPerSeconds=" + + std::string( xmlControl->Attribute("stepsPerSeconds") ) + ", axisBindable=" + + std::string( (axisBindable)? "true" : "false" ) + "]"); + + float _stepSize = 0; + if(xmlControl->Attribute("stepSize")) + { + std::string value(xmlControl->Attribute("stepSize")); + if(value == "MAX") + { + _stepSize = ICS_MAX; + } + else + { + _stepSize = FromString(value.c_str()); + } + } + else + { + ICS_LOG("Warning: no stepSize value found. Default value is 0."); + } + + float _stepsPerSeconds = 0; + if(xmlControl->Attribute("stepsPerSeconds")) + { + std::string value(xmlControl->Attribute("stepsPerSeconds")); + if(value == "MAX") + { + _stepsPerSeconds = ICS_MAX; + } + else + { + _stepsPerSeconds = FromString(value.c_str()); + } + } + else + { + ICS_LOG("Warning: no stepSize value found. Default value is 0."); + } + + addControl( new Control(xmlControl->Attribute("name") + , std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) == "true" + , std::string( xmlControl->Attribute("autoReverseToInitialValue") ) == "true" + , FromString(xmlControl->Attribute("initialValue")) + , _stepSize + , _stepsPerSeconds + , axisBindable) ); + + loadKeyBinders(xmlControl); + + loadMouseAxisBinders(xmlControl); + + loadMouseButtonBinders(xmlControl); + + loadJoystickAxisBinders(xmlControl); + + loadJoystickButtonBinders(xmlControl); + + loadJoystickPOVBinders(xmlControl); + + loadJoystickSliderBinders(xmlControl); + + // Attach controls to channels + TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel"); + while(xmlChannel) + { + ICS_LOG("\tAttaching control to channel [number=" + + std::string( xmlChannel->Attribute("number") ) + ", direction=" + + std::string( xmlChannel->Attribute("direction") ) + "]"); + + float percentage = 1; + if(xmlChannel->Attribute("percentage")) + { + if(StringIsNumber(xmlChannel->Attribute("percentage"))) + { + float val = FromString(xmlChannel->Attribute("percentage")); + if(val > 1 || val < 0) + { + ICS_LOG("ERROR: attaching percentage value range is [0,1]"); + } + else + { + percentage = val; + } + } + else + { + ICS_LOG("ERROR: attaching percentage value range is [0,1]"); + } + } + + int chNumber = FromString(xmlChannel->Attribute("number")); + if(std::string(xmlChannel->Attribute("direction")) == "DIRECT") + { + mControls.back()->attachChannel(mChannels[ chNumber ],Channel::DIRECT, percentage); + } + else if(std::string(xmlChannel->Attribute("direction")) == "INVERSE") + { + mControls.back()->attachChannel(mChannels[ chNumber ],Channel::INVERSE, percentage); + } + + xmlChannel = xmlChannel->NextSiblingElement("Channel"); + } + + xmlControl = xmlControl->NextSiblingElement("Control"); + } + + std::vector::const_iterator o; + for(o = mChannels.begin(); o != mChannels.end(); ++o) + { + (*o)->update(); + } + + delete xmlDoc; + } + + ICS_LOG(" - InputControlSystem Created - "); + } + + InputControlSystem::~InputControlSystem() + { + ICS_LOG(" - Deleting InputControlSystem (" + mFileName + ") - "); + + mJoystickIDList.clear(); + + std::vector::const_iterator o; + for(o = mChannels.begin(); o != mChannels.end(); ++o) + { + delete (*o); + } + mChannels.clear(); + + std::vector::const_iterator o2; + for(o2 = mControls.begin(); o2 != mControls.end(); ++o2) + { + delete (*o2); + } + mControls.clear(); + + mControlsKeyBinderMap.clear(); + mControlsMouseButtonBinderMap.clear(); + mControlsJoystickButtonBinderMap.clear(); + + mKeys.clear(); + mKeyCodes.clear(); + + ICS_LOG(" - InputControlSystem deleted - "); + } + + std::string InputControlSystem::getBaseFileName() + { + size_t found = mFileName.find_last_of("/\\"); + std::string file = mFileName.substr(found+1); + + return file.substr(0, file.find_last_of(".")); + } + + bool InputControlSystem::save(std::string fileName) + { + if(fileName != "") + { + mFileName = fileName; + } + + TiXmlDocument doc( mFileName.c_str() ); + + TiXmlDeclaration dec; + dec.Parse( "", 0, TIXML_ENCODING_UNKNOWN ); + doc.InsertEndChild(dec); + + TiXmlElement Controller( "Controller" ); + + for(std::vector::const_iterator o = mChannels.begin() ; o != mChannels.end(); o++) + { + ICS::IntervalList intervals = (*o)->getIntervals(); + + if(intervals.size() > 1) // all channels have a default linear filter + { + TiXmlElement ChannelFilter( "ChannelFilter" ); + + ChannelFilter.SetAttribute("number", ToString((*o)->getNumber()).c_str()); + + ICS::IntervalList::const_iterator interval = intervals.begin(); + while( interval != intervals.end() ) + { + // if not default linear filter + if(!( interval->step == 0.2f + && interval->startX == 0.0f + && interval->startY == 0.0f + && interval->midX == 0.5f + && interval->midY == 0.5f + && interval->endX == 1.0f + && interval->endY == 1.0f )) + { + TiXmlElement XMLInterval( "Interval" ); + + XMLInterval.SetAttribute("type", "bezier"); + XMLInterval.SetAttribute("step", ToString(interval->step).c_str()); + + XMLInterval.SetAttribute("startX", ToString(interval->startX).c_str()); + XMLInterval.SetAttribute("startY", ToString(interval->startY).c_str()); + XMLInterval.SetAttribute("midX", ToString(interval->midX).c_str()); + XMLInterval.SetAttribute("midY", ToString(interval->midY).c_str()); + XMLInterval.SetAttribute("endX", ToString(interval->endX).c_str()); + XMLInterval.SetAttribute("endY", ToString(interval->endY).c_str()); + + ChannelFilter.InsertEndChild(XMLInterval); + } + + interval++; + } + + Controller.InsertEndChild(ChannelFilter); + } + } + + for(std::vector::const_iterator o = mControls.begin() ; o != mControls.end(); o++) + { + TiXmlElement control( "Control" ); + + control.SetAttribute( "name", (*o)->getName().c_str() ); + if((*o)->getAutoChangeDirectionOnLimitsAfterStop()) + { + control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "true" ); + } + else + { + control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "false" ); + } + if((*o)->getAutoReverse()) + { + control.SetAttribute( "autoReverseToInitialValue", "true" ); + } + else + { + control.SetAttribute( "autoReverseToInitialValue", "false" ); + } + control.SetAttribute( "initialValue", ToString((*o)->getInitialValue()).c_str() ); + + if((*o)->getStepSize() == ICS_MAX) + { + control.SetAttribute( "stepSize", "MAX" ); + } + else + { + control.SetAttribute( "stepSize", ToString((*o)->getStepSize()).c_str() ); + } + + if((*o)->getStepsPerSeconds() == ICS_MAX) + { + control.SetAttribute( "stepsPerSeconds", "MAX" ); + } + else + { + control.SetAttribute( "stepsPerSeconds", ToString((*o)->getStepsPerSeconds()).c_str() ); + } + + if(!(*o)->isAxisBindable()) + { + control.SetAttribute( "axisBindable", "false" ); + } + + if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) != OIS::KC_UNASSIGNED) + { + TiXmlElement keyBinder( "KeyBinder" ); + + keyBinder.SetAttribute( "key", keyCodeToString( + getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + keyBinder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(keyBinder); + } + + if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) != OIS::KC_UNASSIGNED) + { + TiXmlElement keyBinder( "KeyBinder" ); + + keyBinder.SetAttribute( "key", keyCodeToString( + getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + keyBinder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(keyBinder); + } + + if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) + != InputControlSystem/*::NamedAxis*/::UNASSIGNED) + { + TiXmlElement binder( "MouseBinder" ); + + InputControlSystem::NamedAxis axis = + getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE); + if(axis == InputControlSystem/*::NamedAxis*/::X) + { + binder.SetAttribute( "axis", "X" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Y) + { + binder.SetAttribute( "axis", "Y" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Z) + { + binder.SetAttribute( "axis", "Z" ); + } + + binder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) + != InputControlSystem/*::NamedAxis*/::UNASSIGNED) + { + TiXmlElement binder( "MouseBinder" ); + + InputControlSystem::NamedAxis axis = + getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE); + if(axis == InputControlSystem/*::NamedAxis*/::X) + { + binder.SetAttribute( "axis", "X" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Y) + { + binder.SetAttribute( "axis", "Y" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Z) + { + binder.SetAttribute( "axis", "Z" ); + } + + binder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "MouseButtonBinder" ); + + unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE); + if(button == OIS::/*MouseButtonID::*/MB_Left) + { + binder.SetAttribute( "button", "LEFT" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Middle) + { + binder.SetAttribute( "button", "MIDDLE" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Right) + { + binder.SetAttribute( "button", "RIGHT" ); + } + else + { + binder.SetAttribute( "button", ToString(button).c_str() ); + } + binder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "MouseButtonBinder" ); + + unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE); + if(button == OIS::/*MouseButtonID::*/MB_Left) + { + binder.SetAttribute( "button", "LEFT" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Middle) + { + binder.SetAttribute( "button", "MIDDLE" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Right) + { + binder.SetAttribute( "button", "RIGHT" ); + } + else + { + binder.SetAttribute( "button", ToString(button).c_str() ); + } + binder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(binder); + } + + JoystickIDList::const_iterator it = mJoystickIDList.begin(); + while(it != mJoystickIDList.end()) + { + int deviceId = *it; + + if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickAxisBinder" ); + + binder.SetAttribute( "axis", ToString( + getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickAxisBinder" ); + + binder.SetAttribute( "axis", ToString( + getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "JoystickButtonBinder" ); + + binder.SetAttribute( "button", ToString( + getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "JoystickButtonBinder" ); + + binder.SetAttribute( "button", ToString( + getJoystickButtonBinding(*o, *it, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE).index >= 0) + { + TiXmlElement binder( "JoystickPOVBinder" ); + + POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE); + + binder.SetAttribute( "pov", ToString(POVPair.index).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + if(POVPair.axis == ICS::InputControlSystem::EastWest) + { + binder.SetAttribute( "axis", "EastWest" ); + } + else + { + binder.SetAttribute( "axis", "NorthSouth" ); + } + + control.InsertEndChild(binder); + } + + if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE).index >= 0) + { + TiXmlElement binder( "JoystickPOVBinder" ); + + POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE); + + binder.SetAttribute( "pov", ToString(POVPair.index).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + if(POVPair.axis == ICS::InputControlSystem::EastWest) + { + binder.SetAttribute( "axis", "EastWest" ); + } + else + { + binder.SetAttribute( "axis", "NorthSouth" ); + } + + control.InsertEndChild(binder); + } + + if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickSliderBinder" ); + + binder.SetAttribute( "slider", ToString( + getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickSliderBinder" ); + + binder.SetAttribute( "slider", ToString( + getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + it++; + } + + + std::list channels = (*o)->getAttachedChannels(); + for(std::list::iterator it = channels.begin() ; + it != channels.end() ; it++) + { + TiXmlElement binder( "Channel" ); + + binder.SetAttribute( "number", ToString((*it)->getNumber()).c_str() ); + + Channel::ChannelDirection direction = (*it)->getAttachedControlBinding(*o).direction; + if(direction == Channel/*::ChannelDirection*/::DIRECT) + { + binder.SetAttribute( "direction", "DIRECT" ); + } + else + { + binder.SetAttribute( "direction", "INVERSE" ); + } + + float percentage = (*it)->getAttachedControlBinding(*o).percentage; + binder.SetAttribute( "percentage", ToString(percentage).c_str() ); + + control.InsertEndChild(binder); + } + + Controller.InsertEndChild(control); + } + + doc.InsertEndChild(Controller); + return doc.SaveFile(); + } + + void InputControlSystem::update(float lTimeSinceLastFrame) + { + if(mActive) + { + std::vector::const_iterator it; + for(it=mControls.begin(); it!=mControls.end(); ++it) + { + (*it)->update(lTimeSinceLastFrame); + } + } + + //! @todo Future versions should consider channel exponentials and mixtures, so + // after updating Controls, Channels should be updated according to their values + } + + float InputControlSystem::getChannelValue(int i) + { + return std::max(0.0,std::min(1.0,mChannels[i]->getValue())); + } + + float InputControlSystem::getControlValue(int i) + { + return mControls[i]->getValue(); + } + + void InputControlSystem::addJoystick(int deviceId) + { + ICS_LOG("Adding joystick (device id: " + ToString(deviceId) + ")"); + + for(int j = 0 ; j < ICS_MAX_JOYSTICK_AXIS ; j++) + { + if(mControlsJoystickAxisBinderMap[deviceId].find(j) == mControlsJoystickAxisBinderMap[deviceId].end()) + { + ControlAxisBinderItem controlJoystickBinderItem; + controlJoystickBinderItem.direction = Control::STOP; + controlJoystickBinderItem.control = NULL; + mControlsJoystickAxisBinderMap[deviceId][j] = controlJoystickBinderItem; + } + } + + mJoystickIDList.push_back(deviceId); + } + + Control* InputControlSystem::findControl(std::string name) + { + if(mActive) + { + std::vector::const_iterator it; + for(it = mControls.begin(); it != mControls.end(); ++it) + { + if( ((Control*)(*it))->getName() == name) + { + return (Control*)(*it); + } + } + } + + return NULL; + } + + void InputControlSystem::enableDetectingBindingState(Control* control + , Control::ControlChangingDirection direction) + { + mDetectingBindingControl = control; + mDetectingBindingDirection = direction; + + mMouseAxisBindingInitialValues[0] = ICS_MOUSE_AXIS_BINDING_NULL_VALUE; + } + + void InputControlSystem::cancelDetectingBindingState() + { + mDetectingBindingControl = NULL; + } + + void InputControlSystem::fillOISKeysMap() + { + mKeys["UNASSIGNED"]= OIS::KC_UNASSIGNED; + mKeys["ESCAPE"]= OIS::KC_ESCAPE; + mKeys["1"]= OIS::KC_1; + mKeys["2"]= OIS::KC_2; + mKeys["3"]= OIS::KC_3; + mKeys["4"]= OIS::KC_4; + mKeys["5"]= OIS::KC_5; + mKeys["6"]= OIS::KC_6; + mKeys["7"]= OIS::KC_7; + mKeys["8"]= OIS::KC_8; + mKeys["9"]= OIS::KC_9; + mKeys["0"]= OIS::KC_0; + mKeys["MINUS"]= OIS::KC_MINUS; + mKeys["EQUALS"]= OIS::KC_EQUALS; + mKeys["BACK"]= OIS::KC_BACK; + mKeys["TAB"]= OIS::KC_TAB; + mKeys["Q"]= OIS::KC_Q; + mKeys["W"]= OIS::KC_W; + mKeys["E"]= OIS::KC_E; + mKeys["R"]= OIS::KC_R; + mKeys["T"]= OIS::KC_T; + mKeys["Y"]= OIS::KC_Y; + mKeys["U"]= OIS::KC_U; + mKeys["I"]= OIS::KC_I; + mKeys["O"]= OIS::KC_O; + mKeys["P"]= OIS::KC_P; + mKeys["LBRACKET"]= OIS::KC_LBRACKET; + mKeys["RBRACKET"]= OIS::KC_RBRACKET; + mKeys["RETURN"]= OIS::KC_RETURN; + mKeys["LCONTROL"]= OIS::KC_LCONTROL; + mKeys["A"]= OIS::KC_A; + mKeys["S"]= OIS::KC_S; + mKeys["D"]= OIS::KC_D; + mKeys["F"]= OIS::KC_F; + mKeys["G"]= OIS::KC_G; + mKeys["H"]= OIS::KC_H; + mKeys["J"]= OIS::KC_J; + mKeys["K"]= OIS::KC_K; + mKeys["L"]= OIS::KC_L; + mKeys["SEMICOLON"]= OIS::KC_SEMICOLON; + mKeys["APOSTROPHE"]= OIS::KC_APOSTROPHE; + mKeys["GRAVE"]= OIS::KC_GRAVE; + mKeys["LSHIFT"]= OIS::KC_LSHIFT; + mKeys["BACKSLASH"]= OIS::KC_BACKSLASH; + mKeys["Z"]= OIS::KC_Z; + mKeys["X"]= OIS::KC_X; + mKeys["C"]= OIS::KC_C; + mKeys["V"]= OIS::KC_V; + mKeys["B"]= OIS::KC_B; + mKeys["N"]= OIS::KC_N; + mKeys["M"]= OIS::KC_M; + mKeys["COMMA"]= OIS::KC_COMMA; + mKeys["PERIOD"]= OIS::KC_PERIOD; + mKeys["SLASH"]= OIS::KC_SLASH; + mKeys["RSHIFT"]= OIS::KC_RSHIFT; + mKeys["MULTIPLY"]= OIS::KC_MULTIPLY; + mKeys["LMENU"]= OIS::KC_LMENU; + mKeys["SPACE"]= OIS::KC_SPACE; + mKeys["CAPITAL"]= OIS::KC_CAPITAL; + mKeys["F1"]= OIS::KC_F1; + mKeys["F2"]= OIS::KC_F2; + mKeys["F3"]= OIS::KC_F3; + mKeys["F4"]= OIS::KC_F4; + mKeys["F5"]= OIS::KC_F5; + mKeys["F6"]= OIS::KC_F6; + mKeys["F7"]= OIS::KC_F7; + mKeys["F8"]= OIS::KC_F8; + mKeys["F9"]= OIS::KC_F9; + mKeys["F10"]= OIS::KC_F10; + mKeys["F11"]= OIS::KC_F11; + mKeys["F12"]= OIS::KC_F12; + mKeys["NUMLOCK"]= OIS::KC_NUMLOCK; + mKeys["SCROLL"]= OIS::KC_SCROLL; + mKeys["NUMPAD7"]= OIS::KC_NUMPAD7; + mKeys["NUMPAD8"]= OIS::KC_NUMPAD8; + mKeys["NUMPAD9"]= OIS::KC_NUMPAD9; + mKeys["SUBTRACT"]= OIS::KC_SUBTRACT; + mKeys["NUMPAD4"]= OIS::KC_NUMPAD4; + mKeys["NUMPAD5"]= OIS::KC_NUMPAD5; + mKeys["NUMPAD6"]= OIS::KC_NUMPAD6; + mKeys["ADD"]= OIS::KC_ADD; + mKeys["NUMPAD1"]= OIS::KC_NUMPAD1; + mKeys["NUMPAD2"]= OIS::KC_NUMPAD2; + mKeys["NUMPAD3"]= OIS::KC_NUMPAD3; + mKeys["NUMPAD0"]= OIS::KC_NUMPAD0; + mKeys["DECIMAL"]= OIS::KC_DECIMAL; + mKeys["RCONTROL"]= OIS::KC_RCONTROL; + mKeys["DIVIDE"]= OIS::KC_DIVIDE; + mKeys["SYSRQ"]= OIS::KC_SYSRQ; + mKeys["RMENU"]= OIS::KC_RMENU; + mKeys["PAUSE"]= OIS::KC_PAUSE; + mKeys["HOME"]= OIS::KC_HOME; + mKeys["UP"]= OIS::KC_UP; + mKeys["PGUP"]= OIS::KC_PGUP; + mKeys["LEFT"]= OIS::KC_LEFT; + mKeys["RIGHT"]= OIS::KC_RIGHT; + mKeys["END"]= OIS::KC_END; + mKeys["DOWN"]= OIS::KC_DOWN; + mKeys["PGDOWN"]= OIS::KC_PGDOWN; + mKeys["INSERT"]= OIS::KC_INSERT; + mKeys["DELETE"]= OIS::KC_DELETE; + mKeys["LWIN"]= OIS::KC_LWIN; + mKeys["RWIN"]= OIS::KC_RWIN; + mKeys["APPS"]= OIS::KC_APPS; + + mKeys["NUMPADENTER"]= OIS::KC_NUMPADENTER; + + for(std::map::iterator it = mKeys.begin() + ; it != mKeys.end() ; it++) + { + mKeyCodes[ it->second ] = it->first; + } + } + + std::string InputControlSystem::keyCodeToString(OIS::KeyCode key) + { + return mKeyCodes[key]; + } + + OIS::KeyCode InputControlSystem::stringToKeyCode(std::string key) + { + return mKeys[key]; + } +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h new file mode 100644 index 0000000000..f1c12d3b59 --- /dev/null +++ b/extern/oics/ICSInputControlSystem.h @@ -0,0 +1,256 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _InputControlSystem_H_ +#define _InputControlSystem_H_ + +#include "ICSPrerequisites.h" + +#include "ICSControl.h" +#include "ICSChannel.h" + +#define ICS_LOG(text) if(mLog) mLog->logMessage( ("ICS: " + std::string(text)).c_str() ); +#define ICS_MAX_JOYSTICK_AXIS 16 +#define ICS_MOUSE_BINDING_MARGIN 30 +#define ICS_JOYSTICK_AXIS_BINDING_MARGIN 10000 +#define ICS_JOYSTICK_SLIDER_BINDING_MARGIN 10000 +#define ICS_MOUSE_AXIS_BINDING_NULL_VALUE std::numeric_limits::max() + +namespace ICS +{ + class DllExport InputControlSystemLog + { + public: + virtual void logMessage(const char* text) = 0; + }; + + class DllExport InputControlSystem : + public OIS::MouseListener, + public OIS::KeyListener, + public OIS::JoyStickListener + { + + public: + + enum NamedAxis { X = -1, Y = -2, Z = -3, UNASSIGNED = -4 }; + enum POVAxis { NorthSouth = 0, EastWest = 1 }; + + typedef NamedAxis MouseAxis; // MouseAxis is deprecated. It will be removed in future versions + + typedef std::list JoystickIDList; + + typedef struct + { + int index; + POVAxis axis; + } POVBindingPair; + + InputControlSystem(std::string file = "", bool active = true + , DetectingBindingListener* detectingBindingListener = NULL + , InputControlSystemLog* log = NULL, size_t channelCount = 16); + ~InputControlSystem(); + + std::string getFileName(){ return mFileName; }; + std::string getBaseFileName(); + + void setDetectingBindingListener(DetectingBindingListener* detectingBindingListener){ mDetectingBindingListener = detectingBindingListener; }; + DetectingBindingListener* getDetectingBindingListener(){ return mDetectingBindingListener; }; + + // in seconds + void update(float timeSinceLastFrame); + + inline Channel* getChannel(int i){ return mChannels[i]; }; + float getChannelValue(int i); + inline int getChannelCount(){ return (int)mChannels.size(); }; + + inline Control* getControl(int i){ return mControls[i]; }; + float getControlValue(int i); + inline int getControlCount(){ return (int)mControls.size(); }; + inline void addControl(Control* control){ mControls.push_back(control); }; + + Control* findControl(std::string name); + + inline void activate(){ this->mActive = true; }; + inline void deactivate(){ this->mActive = false; }; + + void addJoystick(int deviceId); + JoystickIDList& getJoystickIdList(){ return mJoystickIDList; }; + + // MouseListener + bool mouseMoved(const OIS::MouseEvent &evt); + bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID); + bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID); + + // KeyListener + bool keyPressed(const OIS::KeyEvent &evt); + bool keyReleased(const OIS::KeyEvent &evt); + + // JoyStickListener + bool buttonPressed(const OIS::JoyStickEvent &evt, int button); + bool buttonReleased(const OIS::JoyStickEvent &evt, int button); + bool axisMoved(const OIS::JoyStickEvent &evt, int axis); + bool povMoved(const OIS::JoyStickEvent &evt, int index); + bool sliderMoved(const OIS::JoyStickEvent &evt, int index); + + void addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction); + void addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction); + void addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction); + void addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction); + void addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction); + void addJoystickPOVBinding(Control* control, int deviceId, int index, POVAxis axis, Control::ControlChangingDirection direction); + void addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction); + void removeKeyBinding(OIS::KeyCode key); + void removeMouseAxisBinding(NamedAxis axis); + void removeMouseButtonBinding(unsigned int button); + void removeJoystickAxisBinding(int deviceId, int axis); + void removeJoystickButtonBinding(int deviceId, unsigned int button); + void removeJoystickPOVBinding(int deviceId, int index, POVAxis axis); + void removeJoystickSliderBinding(int deviceId, int index); + + OIS::KeyCode getKeyBinding(Control* control, ICS::Control::ControlChangingDirection direction); + NamedAxis getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction); + unsigned int getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction); + int getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + unsigned int getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + POVBindingPair getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + int getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + + std::string keyCodeToString(OIS::KeyCode key); + OIS::KeyCode stringToKeyCode(std::string key); + + void enableDetectingBindingState(Control* control, Control::ControlChangingDirection direction); + void cancelDetectingBindingState(); + + bool save(std::string fileName = ""); + + protected: + + void loadKeyBinders(TiXmlElement* xmlControlNode); + void loadMouseAxisBinders(TiXmlElement* xmlControlNode); + void loadMouseButtonBinders(TiXmlElement* xmlControlNode); + void loadJoystickAxisBinders(TiXmlElement* xmlControlNode); + void loadJoystickButtonBinders(TiXmlElement* xmlControlNode); + void loadJoystickPOVBinders(TiXmlElement* xmlControlNode); + void loadJoystickSliderBinders(TiXmlElement* xmlControlNode); + + void addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction); + void removeMouseAxisBinding_(int axis); + + protected: + + typedef struct { + Control::ControlChangingDirection direction; + Control* control; + } ControlKeyBinderItem; + + typedef ControlKeyBinderItem ControlAxisBinderItem; + typedef ControlKeyBinderItem ControlButtonBinderItem; + typedef ControlKeyBinderItem ControlPOVBinderItem; + typedef ControlKeyBinderItem ControlSliderBinderItem; + + typedef struct { + Control* control; + Control::ControlChangingDirection direction; + } PendingActionItem; + + std::list mPendingActions; + + std::string mFileName; + + typedef std::map ControlsKeyBinderMapType; // + typedef std::map ControlsAxisBinderMapType; // + typedef std::map ControlsButtonBinderMapType; // + typedef std::map ControlsPOVBinderMapType; // + typedef std::map ControlsSliderBinderMapType; // + + typedef std::map JoystickAxisBinderMapType; // > + typedef std::map JoystickButtonBinderMapType; // > + typedef std::map > JoystickPOVBinderMapType; // > > + typedef std::map JoystickSliderBinderMapType; // > + + ControlsAxisBinderMapType mControlsMouseAxisBinderMap; // + ControlsButtonBinderMapType mControlsMouseButtonBinderMap; // + JoystickAxisBinderMapType mControlsJoystickAxisBinderMap; // > + JoystickButtonBinderMapType mControlsJoystickButtonBinderMap; // > + JoystickPOVBinderMapType mControlsJoystickPOVBinderMap; // > > + JoystickSliderBinderMapType mControlsJoystickSliderBinderMap; // > + + std::vector mControls; + std::vector mChannels; + + ControlsKeyBinderMapType mControlsKeyBinderMap; + std::map mKeys; + std::map mKeyCodes; + + bool mActive; + InputControlSystemLog* mLog; + + DetectingBindingListener* mDetectingBindingListener; + Control* mDetectingBindingControl; + Control::ControlChangingDirection mDetectingBindingDirection; + + bool mXmouseAxisBinded; + bool mYmouseAxisBinded; + + JoystickIDList mJoystickIDList; + + int mMouseAxisBindingInitialValues[3]; + + private: + + void fillOISKeysMap(); + }; + + class DllExport DetectingBindingListener + { + public: + virtual void keyBindingDetected(InputControlSystem* ICS, Control* control + , OIS::KeyCode key, Control::ControlChangingDirection direction); + + virtual void mouseAxisBindingDetected(InputControlSystem* ICS, Control* control + , InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction); + + virtual void mouseButtonBindingDetected(InputControlSystem* ICS, Control* control + , unsigned int button, Control::ControlChangingDirection direction); + + virtual void joystickAxisBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int axis, Control::ControlChangingDirection direction); + + virtual void joystickButtonBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, unsigned int button, Control::ControlChangingDirection direction); + + virtual void joystickPOVBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction); + + virtual void joystickSliderBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int slider, Control::ControlChangingDirection direction); + }; + + static const float ICS_MAX = std::numeric_limits::max(); +} + + +#endif diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp new file mode 100644 index 0000000000..1e66599ead --- /dev/null +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -0,0 +1,665 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + // load xml + void InputControlSystem::loadJoystickAxisBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickBinder = xmlControlNode->FirstChildElement("JoystickAxisBinder"); + while(xmlJoystickBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickAxisBinding(mControls.back(), FromString(xmlJoystickBinder->Attribute("deviceId")) + , FromString(xmlJoystickBinder->Attribute("axis")), dir); + + xmlJoystickBinder = xmlJoystickBinder->NextSiblingElement("JoystickAxisBinder"); + } + } + + void InputControlSystem::loadJoystickButtonBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickButtonBinder = xmlControlNode->FirstChildElement("JoystickButtonBinder"); + while(xmlJoystickButtonBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickButtonBinding(mControls.back(), FromString(xmlJoystickButtonBinder->Attribute("deviceId")) + , FromString(xmlJoystickButtonBinder->Attribute("button")), dir); + + xmlJoystickButtonBinder = xmlJoystickButtonBinder->NextSiblingElement("JoystickButtonBinder"); + } + } + + void InputControlSystem::loadJoystickPOVBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickPOVBinder = xmlControlNode->FirstChildElement("JoystickPOVBinder"); + while(xmlJoystickPOVBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + InputControlSystem::POVAxis axis = /*POVAxis::*/NorthSouth; + if(std::string(xmlJoystickPOVBinder->Attribute("axis")) == "EastWest") + { + axis = /*POVAxis::*/EastWest; + } + + addJoystickPOVBinding(mControls.back(), FromString(xmlJoystickPOVBinder->Attribute("deviceId")) + , FromString(xmlJoystickPOVBinder->Attribute("pov")), axis, dir); + + xmlJoystickPOVBinder = xmlJoystickPOVBinder->NextSiblingElement("JoystickPOVBinder"); + } + } + + void InputControlSystem::loadJoystickSliderBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickSliderBinder = xmlControlNode->FirstChildElement("JoystickSliderBinder"); + while(xmlJoystickSliderBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickSliderBinding(mControls.back(), FromString(xmlJoystickSliderBinder->Attribute("deviceId")) + , FromString(xmlJoystickSliderBinder->Attribute("slider")), dir); + + xmlJoystickSliderBinder = xmlJoystickSliderBinder->NextSiblingElement("JoystickSliderBinder"); + } + } + + // add bindings + void InputControlSystem::addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding AxisBinder [deviceid=" + + ToString(deviceId) + ", axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlAxisBinderItem controlAxisBinderItem; + controlAxisBinderItem.control = control; + controlAxisBinderItem.direction = direction; + mControlsJoystickAxisBinderMap[ deviceId ][ axis ] = controlAxisBinderItem; + } + + void InputControlSystem::addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickButtonBinder [deviceId=" + + ToString(deviceId) + ", button=" + + ToString(button) + ", direction=" + + ToString(direction) + "]"); + + ControlButtonBinderItem controlJoystickButtonBinderItem; + controlJoystickButtonBinderItem.direction = direction; + controlJoystickButtonBinderItem.control = control; + mControlsJoystickButtonBinderMap[ deviceId ][ button ] = controlJoystickButtonBinderItem; + } + + void InputControlSystem::addJoystickPOVBinding(Control* control, int deviceId, int index, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickPOVBinder [deviceId=" + + ToString(deviceId) + ", pov=" + + ToString(index) + ", axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlPOVBinderItem ControlPOVBinderItem; + ControlPOVBinderItem.direction = direction; + ControlPOVBinderItem.control = control; + mControlsJoystickPOVBinderMap[ deviceId ][ index ][ axis ] = ControlPOVBinderItem; + } + + void InputControlSystem::addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickSliderBinder [deviceId=" + + ToString(deviceId) + ", direction=" + + ToString(index) + ", direction=" + + ToString(direction) + "]"); + + ControlSliderBinderItem ControlSliderBinderItem; + ControlSliderBinderItem.direction = direction; + ControlSliderBinderItem.control = control; + mControlsJoystickSliderBinderMap[ deviceId ][ index ] = ControlSliderBinderItem; + } + + // get bindings + int InputControlSystem::getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end()) + { + ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].begin(); + while(it != mControlsJoystickAxisBinderMap[deviceId].end()) + { + if(it->first >= 0 && it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return /*NamedAxis::*/UNASSIGNED; + } + + unsigned int InputControlSystem::getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].begin(); + while(it != mControlsJoystickButtonBinderMap[deviceId].end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return ICS_MAX_DEVICE_BUTTONS; + } + + InputControlSystem::POVBindingPair InputControlSystem::getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + POVBindingPair result; + result.index = -1; + + if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end()) + { + //ControlsAxisBinderMapType::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin(); + std::map::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin(); + while(it != mControlsJoystickPOVBinderMap[deviceId].end()) + { + ControlsPOVBinderMapType::const_iterator it2 = it->second.begin(); + while(it2 != it->second.end()) + { + if(it2->second.control == control && it2->second.direction == direction) + { + result.index = it->first; + result.axis = (POVAxis)it2->first; + return result; + } + it2++; + } + + it++; + } + } + + return result; + } + + int InputControlSystem::getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].begin(); + while(it != mControlsJoystickSliderBinderMap[deviceId].end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return /*NamedAxis::*/UNASSIGNED; + } + + // remove bindings + void InputControlSystem::removeJoystickAxisBinding(int deviceId, int axis) + { + if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].find(axis); + if(it != mControlsJoystickAxisBinderMap[deviceId].end()) + { + mControlsJoystickAxisBinderMap[deviceId].erase(it); + } + } + } + + void InputControlSystem::removeJoystickButtonBinding(int deviceId, unsigned int button) + { + if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].find(button); + if(it != mControlsJoystickButtonBinderMap[deviceId].end()) + { + mControlsJoystickButtonBinderMap[deviceId].erase(it); + } + } + } + + void InputControlSystem::removeJoystickPOVBinding(int deviceId, int index, POVAxis axis) + { + if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end()) + { + std::map::iterator it = mControlsJoystickPOVBinderMap[deviceId].find(index); + if(it != mControlsJoystickPOVBinderMap[deviceId].end()) + { + if(it->second.find(axis) != it->second.end()) + { + mControlsJoystickPOVBinderMap[deviceId].find(index)->second.erase( it->second.find(axis) ); + } + } + } + } + + void InputControlSystem::removeJoystickSliderBinding(int deviceId, int index) + { + if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].find(index); + if(it != mControlsJoystickSliderBinderMap[deviceId].end()) + { + mControlsJoystickSliderBinderMap[deviceId].erase(it); + } + } + } + + // joyStick listeners + bool InputControlSystem::buttonPressed(const OIS::JoyStickEvent &evt, int button) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->joystickButtonBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), button, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::buttonReleased(const OIS::JoyStickEvent &evt, int button) + { + if(mActive) + { + if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + } + return true; + } + + bool InputControlSystem::axisMoved(const OIS::JoyStickEvent &evt, int axis) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickAxisBinderMap.find(evt.device->getID()) != mControlsJoystickAxisBinderMap.end()) + { + ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + Control* ctrl = joystickBinderItem.control; + if(ctrl) + { + ctrl->setIgnoreAutoReverse(true); + if(joystickBinderItem.direction == Control::INCREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)( evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( valDisplaced / axisRange ); + } + else if(joystickBinderItem.direction == Control::DECREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)(evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( 1 - ( valDisplaced / axisRange ) ); + } + } + } + } + else if(mDetectingBindingListener) + { + //ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + //Control* ctrl = joystickBinderItem.control; + //if(ctrl && ctrl->isAxisBindable()) + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if( abs( evt.state.mAxes[axis].abs ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN) + { + mDetectingBindingListener->joystickAxisBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), axis, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::povMoved(const OIS::JoyStickEvent &evt, int index) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickPOVBinderMap.find(evt.device->getID()) != mControlsJoystickPOVBinderMap.end()) + { + std::map::const_iterator i = mControlsJoystickPOVBinderMap[ evt.device->getID() ].find(index); + if(i != mControlsJoystickPOVBinderMap[ evt.device->getID() ].end()) + { + if(evt.state.mPOV[index].direction != OIS::Pov::West + && evt.state.mPOV[index].direction != OIS::Pov::East + && evt.state.mPOV[index].direction != OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); + if(it != i->second.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::North + || evt.state.mPOV[index].direction == OIS::Pov::NorthWest + || evt.state.mPOV[index].direction == OIS::Pov::NorthEast) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction)); + } + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + + if(evt.state.mPOV[index].direction != OIS::Pov::North + && evt.state.mPOV[index].direction != OIS::Pov::South + && evt.state.mPOV[index].direction != OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/EastWest ); + if(it != i->second.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::East + || evt.state.mPOV[index].direction == OIS::Pov::NorthEast + || evt.state.mPOV[index].direction == OIS::Pov::SouthEast) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction)); + } + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + + if(evt.state.mPOV[index].direction == OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); + if(it != i->second.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + + it = i->second.find( /*POVAxis::*/EastWest ); + if(it != i->second.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + } + } + } + else if(mDetectingBindingListener) + { + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::West + || evt.state.mPOV[index].direction == OIS::Pov::East + || evt.state.mPOV[index].direction == OIS::Pov::North + || evt.state.mPOV[index].direction == OIS::Pov::South) + { + POVAxis povAxis = NorthSouth; + if(evt.state.mPOV[index].direction == OIS::Pov::West + || evt.state.mPOV[index].direction == OIS::Pov::East) + { + povAxis = EastWest; + } + + mDetectingBindingListener->joystickPOVBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), index, povAxis, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickSliderBinderMap.find(evt.device->getID()) != mControlsJoystickSliderBinderMap.end()) + { + ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; + Control* ctrl = joystickBinderItem.control; + if(ctrl) + { + ctrl->setIgnoreAutoReverse(true); + if(joystickBinderItem.direction == Control::INCREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)( evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( valDisplaced / axisRange ); + } + else if(joystickBinderItem.direction == Control::DECREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)(evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( 1 - ( valDisplaced / axisRange ) ); + } + } + } + } + else if(mDetectingBindingListener) + { + /*ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; + Control* ctrl = joystickBinderItem.control; + if(ctrl && ctrl->isAxisBindable()) + {*/ + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if( abs( evt.state.mSliders[index].abX ) > ICS_JOYSTICK_SLIDER_BINDING_MARGIN) + { + mDetectingBindingListener->joystickSliderBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), index, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + // joystick auto bindings + void DetectingBindingListener::joystickAxisBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int axis, Control::ControlChangingDirection direction) + { + // if the joystick axis is used by another control, remove it + ICS->removeJoystickAxisBinding(deviceId, axis); + + // if the control has an axis assigned, remove it + int oldAxis = ICS->getJoystickAxisBinding(control, deviceId, direction); + if(oldAxis != InputControlSystem::UNASSIGNED) + { + ICS->removeJoystickAxisBinding(deviceId, oldAxis); + } + + ICS->addJoystickAxisBinding(control, deviceId, axis, direction); + ICS->cancelDetectingBindingState(); + } + void DetectingBindingListener::joystickButtonBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, unsigned int button, Control::ControlChangingDirection direction) + { + // if the joystick button is used by another control, remove it + ICS->removeJoystickButtonBinding(deviceId, button); + + // if the control has a joystick button assigned, remove it + unsigned int oldButton = ICS->getJoystickButtonBinding(control, deviceId, direction); + if(oldButton != ICS_MAX_DEVICE_BUTTONS) + { + ICS->removeJoystickButtonBinding(deviceId, oldButton); + } + + ICS->addJoystickButtonBinding(control, deviceId, button, direction); + ICS->cancelDetectingBindingState(); + } + + + void DetectingBindingListener::joystickPOVBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction) + { + // if the joystick slider is used by another control, remove it + ICS->removeJoystickPOVBinding(deviceId, pov, axis); + + // if the control has a joystick button assigned, remove it + ICS::InputControlSystem::POVBindingPair oldPOV = ICS->getJoystickPOVBinding(control, deviceId, direction); + if(oldPOV.index >= 0 && oldPOV.axis == axis) + { + ICS->removeJoystickPOVBinding(deviceId, oldPOV.index, oldPOV.axis); + } + + ICS->addJoystickPOVBinding(control, deviceId, pov, axis, direction); + ICS->cancelDetectingBindingState(); + } + + void DetectingBindingListener::joystickSliderBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int slider, Control::ControlChangingDirection direction) + { + // if the joystick slider is used by another control, remove it + ICS->removeJoystickSliderBinding(deviceId, slider); + + // if the control has a joystick slider assigned, remove it + int oldSlider = ICS->getJoystickSliderBinding(control, deviceId, direction); + if(oldSlider != InputControlSystem::/*NamedAxis::*/UNASSIGNED) + { + ICS->removeJoystickSliderBinding(deviceId, oldSlider); + } + + ICS->addJoystickSliderBinding(control, deviceId, slider, direction); + ICS->cancelDetectingBindingState(); + } +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp new file mode 100644 index 0000000000..8ef81d9794 --- /dev/null +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -0,0 +1,156 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + void InputControlSystem::loadKeyBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlKeyBinder = xmlControlNode->FirstChildElement("KeyBinder"); + while(xmlKeyBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlKeyBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlKeyBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addKeyBinding(mControls.back(), mKeys[xmlKeyBinder->Attribute("key")], dir); + + xmlKeyBinder = xmlKeyBinder->NextSiblingElement("KeyBinder"); + } + } + + void InputControlSystem::addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding KeyBinder [key=" + + keyCodeToString(key) + ", direction=" + + ToString(direction) + "]"); + + ControlKeyBinderItem controlKeyBinderItem; + controlKeyBinderItem.control = control; + controlKeyBinderItem.direction = direction; + mControlsKeyBinderMap[ key ] = controlKeyBinderItem; + } + + void InputControlSystem::removeKeyBinding(OIS::KeyCode key) + { + ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.find(key); + if(it != mControlsKeyBinderMap.end()) + { + mControlsKeyBinderMap.erase(it); + } + } + + OIS::KeyCode InputControlSystem::getKeyBinding(Control* control + , ICS::Control::ControlChangingDirection direction) + { + ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.begin(); + while(it != mControlsKeyBinderMap.end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + + return OIS::KC_UNASSIGNED; + } + bool InputControlSystem::keyPressed(const OIS::KeyEvent &evt) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + if(it != mControlsKeyBinderMap.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->keyBindingDetected(this, + mDetectingBindingControl, evt.key, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::keyReleased(const OIS::KeyEvent &evt) + { + if(mActive) + { + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + if(it != mControlsKeyBinderMap.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + + return true; + } + + void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control + , OIS::KeyCode key, Control::ControlChangingDirection direction) + { + // if the key is used by another control, remove it + ICS->removeKeyBinding(key); + + // if the control has a key assigned, remove it + OIS::KeyCode oldKey = ICS->getKeyBinding(control, direction); + if(oldKey != OIS::KC_UNASSIGNED) + { + ICS->removeKeyBinding(oldKey); + } + + ICS->addKeyBinding(control, key, direction); + ICS->cancelDetectingBindingState(); + } + +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp new file mode 100644 index 0000000000..c62f1765e6 --- /dev/null +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -0,0 +1,397 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + // load xml + void InputControlSystem::loadMouseAxisBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlMouseBinder = xmlControlNode->FirstChildElement("MouseBinder"); + while(xmlMouseBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlMouseBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlMouseBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + NamedAxis axis = /*NamedAxis::*/ X; + if((*xmlMouseBinder->Attribute("axis")) == 'Y') + { + axis = /*NamedAxis::*/ Y; + } + else if((*xmlMouseBinder->Attribute("axis")) == 'Z') + { + axis = /*NamedAxis::*/ Z; + } + + addMouseAxisBinding(mControls.back(), axis, dir); + + xmlMouseBinder = xmlMouseBinder->NextSiblingElement("MouseBinder"); + } + } + + void InputControlSystem::loadMouseButtonBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlMouseButtonBinder = xmlControlNode->FirstChildElement("MouseButtonBinder"); + while(xmlMouseButtonBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + int button = 0; + if(std::string(xmlMouseButtonBinder->Attribute("button")) == "LEFT") + { + button = OIS::/*MouseButtonID::*/MB_Left; + } + else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "RIGHT") + { + button = OIS::/*MouseButtonID::*/MB_Right; + } + else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "MIDDLE") + { + button = OIS::/*MouseButtonID::*/MB_Middle; + } + else + { + button = FromString(xmlMouseButtonBinder->Attribute("button")); + } + + addMouseButtonBinding(mControls.back(), button, dir); + + xmlMouseButtonBinder = xmlMouseButtonBinder->NextSiblingElement("MouseButtonBinder"); + } + } + + + // add bindings + void InputControlSystem::addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction) + { + if(axis == /*NamedAxis::*/X) + { + mXmouseAxisBinded = true; + } + else if(axis == /*NamedAxis::*/Y) + { + mYmouseAxisBinded = true; + } + + addMouseAxisBinding_(control, axis, direction); + } + + /*protected*/ void InputControlSystem::addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding AxisBinder [axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlAxisBinderItem controlAxisBinderItem; + controlAxisBinderItem.control = control; + controlAxisBinderItem.direction = direction; + mControlsMouseAxisBinderMap[ axis ] = controlAxisBinderItem; + } + + void InputControlSystem::addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding MouseButtonBinder [button=" + + ToString(button) + ", direction=" + + ToString(direction) + "]"); + + ControlButtonBinderItem controlMouseButtonBinderItem; + controlMouseButtonBinderItem.direction = direction; + controlMouseButtonBinderItem.control = control; + mControlsMouseButtonBinderMap[ button ] = controlMouseButtonBinderItem; + } + + // get bindings + InputControlSystem::NamedAxis InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction) + { + ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin(); + while(it != mControlsMouseAxisBinderMap.end()) + { + if(it->first < 0 && it->second.control == control && it->second.direction == direction) + { + return (InputControlSystem::NamedAxis)(it->first); + } + it++; + } + + return /*NamedAxis::*/UNASSIGNED; + } + + //int InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction) + //{ + // ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin(); + // while(it != mControlsMouseAxisBinderMap.end()) + // { + // if(it->first >= 0 && it->second.control == control && it->second.direction == direction) + // { + // return it->first; + // } + // it++; + // } + + // return /*NamedAxis::*/UNASSIGNED; + //} + + unsigned int InputControlSystem::getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction) + { + ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.begin(); + while(it != mControlsMouseButtonBinderMap.end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + + return ICS_MAX_DEVICE_BUTTONS; + } + + // remove bindings + void InputControlSystem::removeMouseAxisBinding(NamedAxis axis) + { + if(axis == /*NamedAxis::*/X) + { + mXmouseAxisBinded = false; + } + else if(axis == /*NamedAxis::*/Y) + { + mYmouseAxisBinded = false; + } + + removeMouseAxisBinding_(axis); + } + /*protected*/ void InputControlSystem::removeMouseAxisBinding_(int axis) + { + ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.find(axis); + if(it != mControlsMouseAxisBinderMap.end()) + { + mControlsMouseAxisBinderMap.erase(it); + } + } + + + void InputControlSystem::removeMouseButtonBinding(unsigned int button) + { + ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.find(button); + if(it != mControlsMouseButtonBinderMap.end()) + { + mControlsMouseButtonBinderMap.erase(it); + } + } + + // mouse Listeners + bool InputControlSystem::mouseMoved(const OIS::MouseEvent &evt) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mXmouseAxisBinded && evt.state.X.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/X ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.X.abs) / float(evt.state.width) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( 1 - float( evt.state.X.abs / float(evt.state.width) ) ); + } + } + + if(mYmouseAxisBinded && evt.state.Y.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/Y ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.Y.abs) / float(evt.state.height) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( 1 - float( evt.state.Y.abs / float(evt.state.height) ) ); + } + } + + //! @todo Whats the range of the Z axis? + /*if(evt.state.Z.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsAxisBinderMap[ NamedAxis::Z ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.Z.abs) / float(evt.state.width?) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( float( (1 - evt.state.Z.abs) / float(evt.state.width?) ) ); + } + }*/ + } + else if(mDetectingBindingListener) + { + if(mDetectingBindingControl->isAxisBindable()) + { + if(mMouseAxisBindingInitialValues[0] == ICS_MOUSE_AXIS_BINDING_NULL_VALUE) + { + mMouseAxisBindingInitialValues[0] = 0; + mMouseAxisBindingInitialValues[1] = 0; + mMouseAxisBindingInitialValues[2] = 0; + } + + mMouseAxisBindingInitialValues[0] += evt.state.X.rel; + mMouseAxisBindingInitialValues[1] += evt.state.Y.rel; + mMouseAxisBindingInitialValues[2] += evt.state.Z.rel; + + if( abs(mMouseAxisBindingInitialValues[0]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, X, mDetectingBindingDirection); + } + else if( abs(mMouseAxisBindingInitialValues[1]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, Y, mDetectingBindingDirection); + } + else if( abs(mMouseAxisBindingInitialValues[2]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, Z, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn); + if(it != mControlsMouseButtonBinderMap.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->mouseButtonBindingDetected(this, + mDetectingBindingControl, btn, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + { + if(mActive) + { + ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn); + if(it != mControlsMouseButtonBinderMap.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + + return true; + } + + // mouse auto bindings + void DetectingBindingListener::mouseAxisBindingDetected(InputControlSystem* ICS, Control* control + , InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction) + { + // if the mouse axis is used by another control, remove it + ICS->removeMouseAxisBinding(axis); + + // if the control has an axis assigned, remove it + InputControlSystem::NamedAxis oldAxis = ICS->getMouseAxisBinding(control, direction); + if(oldAxis != InputControlSystem::UNASSIGNED) + { + ICS->removeMouseAxisBinding(oldAxis); + } + + ICS->addMouseAxisBinding(control, axis, direction); + ICS->cancelDetectingBindingState(); + } + + void DetectingBindingListener::mouseButtonBindingDetected(InputControlSystem* ICS, Control* control + , unsigned int button, Control::ControlChangingDirection direction) + { + // if the mouse button is used by another control, remove it + ICS->removeMouseButtonBinding(button); + + // if the control has a mouse button assigned, remove it + unsigned int oldButton = ICS->getMouseButtonBinding(control, direction); + if(oldButton != ICS_MAX_DEVICE_BUTTONS) + { + ICS->removeMouseButtonBinding(oldButton); + } + + ICS->addMouseButtonBinding(control, button, direction); + ICS->cancelDetectingBindingState(); + } + +} \ No newline at end of file diff --git a/extern/oics/ICSPrerequisites.cpp b/extern/oics/ICSPrerequisites.cpp new file mode 100644 index 0000000000..2824950ed1 --- /dev/null +++ b/extern/oics/ICSPrerequisites.cpp @@ -0,0 +1,27 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSPrerequisites.h" diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h new file mode 100644 index 0000000000..864dad15fd --- /dev/null +++ b/extern/oics/ICSPrerequisites.h @@ -0,0 +1,111 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +//! @todo add mouse wheel support + +#ifndef _InputControlSystem_Prerequisites_H_ +#define _InputControlSystem_Prerequisites_H_ + +/// Include external headers +#include +#include +#include +#include +#include +#include + +#include "tinyxml.h" + +#define OIS_DYNAMIC_LIB +#include +#include +#include +#include +#include + +/// Define the dll export qualifier if compiling for Windows + +/* +#ifdef ICS_PLATFORM_WIN32 + #ifdef ICS_LIB + #define DllExport __declspec (dllexport) + #else + #define DllExport __declspec (dllimport) + #endif +#else + #define DllExport +#endif +*/ +#define DllExport + +// Define some macros +#define ICS_DEPRECATED __declspec(deprecated("Deprecated. It will be removed in future versions.")) + +/// Version defines +#define ICS_VERSION_MAJOR 0 +#define ICS_VERSION_MINOR 3 +#define ICS_VERSION_PATCH 1 + +#define ICS_MAX_DEVICE_BUTTONS 30 + +namespace ICS +{ + template + bool StringIsNumber ( const std::string &Text ) + { + std::stringstream ss(Text); + T result; + return ss >> result ? true : false; + } + + // from http://www.cplusplus.com/forum/articles/9645/ + template + std::string ToString ( T value ) + { + std::stringstream ss; + ss << value; + return ss.str(); + } + + // from http://www.cplusplus.com/forum/articles/9645/ + template + T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a + { //character array as argument + std::stringstream ss(Text); + T result; + return ss >> result ? result : 0; + } + + class InputControlSystem; + class Channel; + class ChannelListener; + class Control; + class ControlListener; + class DetectingBindingListener; + class InputControlSystemLog; +} + +#endif diff --git a/extern/oics/tinystr.cpp b/extern/oics/tinystr.cpp new file mode 100644 index 0000000000..681250714b --- /dev/null +++ b/extern/oics/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/extern/oics/tinystr.h b/extern/oics/tinystr.h new file mode 100644 index 0000000000..419e647e16 --- /dev/null +++ b/extern/oics/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp new file mode 100644 index 0000000000..841a41cd39 --- /dev/null +++ b/extern/oics/tinyxml.cpp @@ -0,0 +1,1888 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/extern/oics/tinyxml.h b/extern/oics/tinyxml.h new file mode 100644 index 0000000000..e69913b59c --- /dev/null +++ b/extern/oics/tinyxml.h @@ -0,0 +1,1802 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +//#define TIXML_USE_STL + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/mangle/.gitignore b/libs/mangle/.gitignore deleted file mode 100644 index cd24d78972..0000000000 --- a/libs/mangle/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -upload_docs.sh -docs -*~ diff --git a/libs/mangle/Doxyfile b/libs/mangle/Doxyfile deleted file mode 100644 index f3e0180029..0000000000 --- a/libs/mangle/Doxyfile +++ /dev/null @@ -1,1510 +0,0 @@ -# Doxyfile 1.5.8 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = Mangle - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 1 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, -# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, -# Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = YES - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = sound stream vfs input - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.h - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */tests/* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = docs - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to FRAME, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. Other possible values -# for this tag are: HIERARCHIES, which will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list; -# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which -# disables this behavior completely. For backwards compatibility with previous -# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE -# respectively. - -GENERATE_TREEVIEW = NONE - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = YES - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Options related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/libs/mangle/LICENSE.txt b/libs/mangle/LICENSE.txt deleted file mode 100644 index ccfcc9f220..0000000000 --- a/libs/mangle/LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -Minimal Abstraction Game Layer (Mangle) is licensed under the -'zlib/libpng' license: - ----- - -Copyright (c) 2009 Nicolay Korslund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. - diff --git a/libs/mangle/README.txt b/libs/mangle/README.txt deleted file mode 100644 index f4849bebda..0000000000 --- a/libs/mangle/README.txt +++ /dev/null @@ -1,129 +0,0 @@ -Welcome to Mangle v0.1 ----------------------- - -Written by: Nicolay Korslund (korslund@gmail.com) -License: zlib/png (see LICENSE.txt) -WWW: http://asm-soft.com/mangle/ -Documentation: http://asm-soft.com/mangle/docs - - - -Mangle is the project name for a small set of generic interfaces for -various game middleware libraries, such as sound, input, graphics, and -so on. You can imagine that it stands for "Minimal Abstraction Game -Layer", if you like. It will consist of several more or less -independent modules, one for each of these areas. These may be used -together to build an entire game engine, or they can be used -individually as separate libraries. - -However, Mangle does NOT actually implement a game engine, or any new -fundamental functionality. More on that below. - -Currently there's modules for sound and streams / archives (virtual -file systems.) More will come in the future (including input, 2D/3D -graphics, GUI, physics, and more.) - - -Main idea ---------- - -The idea behind Mangle is to provide a uniform, consistent interface -to other game libraries. The library does not provide ANY -functionality on its own. Instead it connects to a backend -implementation of your choice (or of your making.) - -The Sound module, for example, currently has backends for OpenAL -(output only), FFmpeg (input only) and for Audiere. Hopefully we'll -add IrrKlang, FMod, DirectSound, Miles and more in the future. It can -combine libraries to get more complete functionality (like using -OpenAL for output and FFmpeg to decode sound files), and it's also -easy to write your own backend if you're using a different (or -home-brewed) sound system. - -Regardless of what backend you use, the front-end interfaces (found -eg. in sound/output.h) is identical, and as a library user you -shouldn't notice much difference at all if you swap one backend for -another at a later point. It should Just Work. - -The interfaces themselves are also quite simple. Setting up a sound -stream from FFmpeg or other decoder into OpenAL can be quite hairy - -but with Mangle the hairy parts have already been written for you. You -just plug the parts together. - -The goal in the long run is to support a wide variety of game-related -libraries, and as many backend libraries (free and commercial) as -possible, so that you the user will have to write as little code as -possible. - - - -What is it good for -------------------- - -The main point of Mangle, as we said above, is that it connects to any -library of your choice "behind the scenes" but provides the same, -super-simple interface front-end for all of them. There can benefit -you in many ways: - -- If you want to use a new library that Mangle support. You don't have - to scour the net for tutorials and usage examples, since much of the - common usage code is already included in the implementation classes. - -- If you don't want to pollute your code with library-specific code. - The Mangle interfaces can help you keep your code clean, and its - user interface is often simpler than the exteral library one. - -- If you want to quickly connect different libraries together, it - really helps if they speak a common language. The Mangle interfaces - are exactly that - a common language between libraries. Do you need - Audiere to load sounds from a weird archive format only implemented - for PhysFS, all channeled through the OGRE resource system? No - problem! - -- If you are creating a library that depends on a specific feature - (such as sound), but you don't want to lock your users into any - specific sound library. Mangle works as an abstraction that lets - your users select their own implementation. - -- If you want to support multiple backends for your game/app, or want - to make it possible to easily switch backends later. You can select - backends at compile time or even at runtime. For example you might - want to switch to to a commercial sound library at a later stage in - development, or you may want to use a different input library on - console platforms than on PC. - -The Mangle implementations are extremely light-weight - often just one -or two cpp/h pairs per module. You can plug them directly into your -program, there's no separate library building step required. - -Since the library aims to be very modularly put together, you can -also, in many cases, just copy-and-paste the parts you need and ignore -the rest. Or modify stuff without fearing that the whole 'system' will -come crashing down, because there is no big 'system' to speak of. - - -Past and future ---------------- - -Mangle started out as (and still is) a spin-off from OpenMW, another -project I am personally working on ( http://openmw.com/ ). OpenMW is -an attempt to recreate the engine behind the commercial game -Morrowind, using only open source software. - -The projects are still tightly interlinked, and they will continue to -be until OpenMW is finished. Most near-future work on Mangle will be -focused chiefly on OpenMW at the moment. However I will gladly include -external contributions and suggestions that are not OpenMW-related if -someone sends them to me. - - -Conclusion ----------- - -As you might have guessed, Mangle is more a concept in development -than a finished library right now. - -All feedback, ideas, concepts, questions and code are very -welcome. Send them to: korslund@gmail.com - -I will put up a forum later as well if there's enough interest. diff --git a/libs/mangle/input/clients/ogre_input_capture.hpp b/libs/mangle/input/clients/ogre_input_capture.hpp deleted file mode 100644 index 2e77dc10b1..0000000000 --- a/libs/mangle/input/clients/ogre_input_capture.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MANGLE_INPUT_OGREINPUTFRAME_H -#define MANGLE_INPUT_OGREINPUTFRAME_H - -/* - This Ogre FrameListener calls capture() on an input driver every frame. - */ - -#include -#include "../driver.hpp" - -namespace Mangle { -namespace Input { - - struct OgreInputCapture : Ogre::FrameListener - { - Mangle::Input::Driver &driver; - - OgreInputCapture(Mangle::Input::Driver &drv) - : driver(drv) {} - - bool frameStarted(const Ogre::FrameEvent &evt) - { - driver.capture(); - return true; - } - }; -}} - -#endif diff --git a/libs/mangle/input/driver.hpp b/libs/mangle/input/driver.hpp deleted file mode 100644 index f4ba159c52..0000000000 --- a/libs/mangle/input/driver.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MANGLE_INPUT_DRIVER_H -#define MANGLE_INPUT_DRIVER_H - -#include "event.hpp" - -namespace Mangle -{ - namespace Input - { - /** Input::Driver is the main interface to any input system that - handles keyboard and/or mouse input, along with any other - input source like joysticks. - - It is really a generalized event system, and could also be - used for non-input related events. The definition of the event - codes and structures are entirely dependent on the - implementation. - - A system-independent key code list will be found in keys.hpp, - and input drivers should privide optional translations to/from - this list for full compatibility. - */ - struct Driver - { - Driver() {} - virtual ~Driver() {} - - /** Captures input and produces the relevant events from it. An - event callback must be set with setEvent(), or all events - will be ignored. - */ - virtual void capture() = 0; - - /** Check the state of a given key or button. The key/button - definitions depends on the driver. - */ - virtual bool isDown(int index) = 0; - - /** Show or hide system mouse cursor - */ - virtual void showMouse(bool show) = 0; - - /** Set the event handler for input events. The evt->event() - function is called for each event. The meaning of the index - and *p parameters will be specific to each driver and to - each input system. - */ - void setEvent(EventPtr evt) - { event = evt; } - - /** Instigate an event. Is used internally for all events, but - can also be called from the outside to "fake" events from - this driver. - */ - void makeEvent(Event::Type type, int index, const void *p=NULL) - { - if(event) - event->event(type,index,p); - } - - private: - /// Holds the event callback set byt setEvent() - EventPtr event; - }; - - typedef boost::shared_ptr DriverPtr; - } -} -#endif diff --git a/libs/mangle/input/event.hpp b/libs/mangle/input/event.hpp deleted file mode 100644 index dc7b470887..0000000000 --- a/libs/mangle/input/event.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef MANGLE_INPUT_EVENT_H -#define MANGLE_INPUT_EVENT_H - -#include "../tools/shared_ptr.hpp" - -namespace Mangle -{ - namespace Input - { - /** Generic callback for input events. The meaning of the - parameters depend on the system producing the events. - */ - struct Event - { - /// Event types - enum Type - { - EV_Unknown = 1, // Unknown event type - EV_KeyDown = 2, // Keyboard button was pressed - EV_KeyUp = 4, // Keyboard button was released - EV_Keyboard = 6, // All keyboard events - - EV_MouseMove = 8, // Mouse movement - EV_MouseDown = 16, // Mouse button pressed - EV_MouseUp = 32, // Mouse button released - EV_Mouse = 56, // All mouse events - - EV_ALL = 63 // All events - }; - - /** - Called upon all events. The first parameter give the event - type, the second gives additional data (usually the local - keysym or button index as defined by the driver), and the - pointer points to the full custom event structure provided by - the driver (the type may vary depending on the EventType, - this is defined in the Driver documentation.) - */ - virtual void event(Type type, int index, const void *p) = 0; - virtual ~Event() {} - }; - - typedef boost::shared_ptr EventPtr; - } -} -#endif diff --git a/libs/mangle/input/filters/eventlist.hpp b/libs/mangle/input/filters/eventlist.hpp deleted file mode 100644 index b3e2ff8f24..0000000000 --- a/libs/mangle/input/filters/eventlist.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef MANGLE_INPUT_EVENTLIST_H -#define MANGLE_INPUT_EVENTLIST_H - -#include "../event.hpp" -#include - -namespace Mangle -{ - namespace Input - { - /** And Event handler that distributes each event to a list of - other handlers. Supports filtering events by their Type - parameter. - */ - struct EventList : Event - { - struct Filter - { - EventPtr evt; - int flags; - }; - std::vector list; - - void add(EventPtr e, int flags = EV_ALL) - { - Filter f; - f.evt = e; - f.flags = flags; - list.push_back(f); - } - - virtual void event(Type type, int index, const void *p) - { - std::vector::iterator it; - - for(it=list.begin(); it!=list.end(); it++) - { - if(type & it->flags) - it->evt->event(type,index,p); - } - } - }; - - typedef boost::shared_ptr EventListPtr; - } -} -#endif diff --git a/libs/mangle/input/servers/ois_driver.cpp b/libs/mangle/input/servers/ois_driver.cpp deleted file mode 100644 index 07ba3e83a7..0000000000 --- a/libs/mangle/input/servers/ois_driver.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "ois_driver.hpp" - -#include -#include -#include -#include - -#ifdef __APPLE_CC__ -#include -#endif - -using namespace Mangle::Input; -using namespace OIS; - -struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener -{ - OISDriver &drv; - - OISListener(OISDriver &driver) - : drv(driver) {} - - bool keyPressed( const OIS::KeyEvent &arg ) - { - drv.makeEvent(Event::EV_KeyDown, arg.key, &arg); - return true; - } - - bool keyReleased( const OIS::KeyEvent &arg ) - { - drv.makeEvent(Event::EV_KeyUp, arg.key, &arg); - return true; - } - - bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) - { - // Mouse button events are handled as key events - // TODO: Translate mouse buttons into pseudo-keysyms - drv.makeEvent(Event::EV_MouseDown, id, &arg); - return true; - } - - bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) - { - // TODO: ditto - drv.makeEvent(Event::EV_MouseUp, id, &arg); - return true; - } - - bool mouseMoved( const OIS::MouseEvent &arg ) - { - drv.makeEvent(Event::EV_MouseMove, -1, &arg); - return true; - } -}; - -OISDriver::OISDriver(Ogre::RenderWindow *window, bool exclusive) -{ - assert(window); - - size_t windowHnd; - - window->getCustomAttribute("WINDOW", &windowHnd); - - std::ostringstream windowHndStr; - ParamList pl; - - windowHndStr << windowHnd; - pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); - - // Set non-exclusive mouse and keyboard input if the user requested - // it. - if(!exclusive) - { -#if defined OIS_WIN32_PLATFORM - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_FOREGROUND" ))); - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_NONEXCLUSIVE"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_FOREGROUND"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_NONEXCLUSIVE"))); -#elif defined OIS_LINUX_PLATFORM - pl.insert(std::make_pair(std::string("x11_mouse_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_mouse_hide"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_keyboard_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("XAutoRepeatOn"), - std::string("true"))); -#endif - } - -#ifdef __APPLE_CC__ - // Give the application window focus to receive input events - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - SetFrontProcess(&psn); -#endif - - inputMgr = InputManager::createInputSystem( pl ); - - // Create all devices - keyboard = static_cast(inputMgr->createInputObject - ( OISKeyboard, true )); - mouse = static_cast(inputMgr->createInputObject - ( OISMouse, true )); - - // Set mouse region - const MouseState &ms = mouse->getMouseState(); - ms.width = window->getWidth(); - ms.height = window->getHeight(); - - // Set up the input listener - listener = new OISListener(*this); - keyboard-> setEventCallback(listener); - mouse-> setEventCallback(listener); -} - -OISDriver::~OISDriver() -{ - // Delete the listener object - delete listener; - - if(inputMgr == NULL) return; - - // Kill the input systems. This will reset input options such as key - // repeat rate. - inputMgr->destroyInputObject(keyboard); - inputMgr->destroyInputObject(mouse); - InputManager::destroyInputSystem(inputMgr); - inputMgr = NULL; -} - -void OISDriver::capture() -{ - // Capture keyboard and mouse events - keyboard->capture(); - mouse->capture(); -} - -bool OISDriver::isDown(int index) -{ - // TODO: Extend to mouse buttons as well - return keyboard->isKeyDown((OIS::KeyCode)index); -} - -void OISDriver::adjustMouseClippingSize(int width, int height) -{ - const OIS::MouseState &ms = mouse->getMouseState(); - ms.width = width; - ms.height = height; -} diff --git a/libs/mangle/input/servers/ois_driver.hpp b/libs/mangle/input/servers/ois_driver.hpp deleted file mode 100644 index 81633542fb..0000000000 --- a/libs/mangle/input/servers/ois_driver.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef MANGLE_INPUT_OIS_DRIVER_H -#define MANGLE_INPUT_OIS_DRIVER_H - -#include "../driver.hpp" - -namespace OIS -{ - class InputManager; - class Mouse; - class Keyboard; -} - -namespace Ogre -{ - class RenderWindow; -} - -namespace Mangle -{ - namespace Input - { - struct OISListener; - - /** Input driver for OIS, the input manager typically used with - Ogre. - */ - struct OISDriver : Driver - { - /// If exclusive=true, then we capture mouse and keyboard from - /// the OS. - OISDriver(Ogre::RenderWindow *window, bool exclusive=true); - ~OISDriver(); - - void adjustMouseClippingSize(int width, int height); - - void capture(); - bool isDown(int index); - /// Not currently supported. - void showMouse(bool) {} - - private: - OIS::InputManager *inputMgr; - OIS::Mouse *mouse; - OIS::Keyboard *keyboard; - - OISListener *listener; - }; - } -} -#endif diff --git a/libs/mangle/input/servers/sdl_driver.cpp b/libs/mangle/input/servers/sdl_driver.cpp deleted file mode 100644 index 93884a6e6c..0000000000 --- a/libs/mangle/input/servers/sdl_driver.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "sdl_driver.hpp" - -#include - -using namespace Mangle::Input; - -void SDLDriver::capture() -{ - // Poll for events - SDL_Event evt; - while(SDL_PollEvent(&evt)) - { - Event::Type type = Event::EV_Unknown; - int index = -1; - - switch(evt.type) - { - // For key events, send the keysym as the index. - case SDL_KEYDOWN: - type = Event::EV_KeyDown; - index = evt.key.keysym.sym; - break; - case SDL_KEYUP: - type = Event::EV_KeyUp; - index = evt.key.keysym.sym; - break; - case SDL_MOUSEMOTION: - type = Event::EV_MouseMove; - break; - // Add more event types later - } - - // Pass the event along, using -1 as index for unidentified - // event types. - makeEvent(type, index, &evt); - } -} - -bool SDLDriver::isDown(int index) -{ - int num; - Uint8 *keys = SDL_GetKeyState(&num); - assert(index >= 0 && index < num); - - // The returned array from GetKeyState is indexed by the - // SDLK_KEYNAME enums and is just a list of bools. If the indexed - // value is true, the button is down. - return keys[index]; -} - -void SDLDriver::showMouse(bool show) -{ - SDL_ShowCursor(show?SDL_ENABLE:SDL_DISABLE); -} diff --git a/libs/mangle/input/servers/sdl_driver.hpp b/libs/mangle/input/servers/sdl_driver.hpp deleted file mode 100644 index b71346cba1..0000000000 --- a/libs/mangle/input/servers/sdl_driver.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MANGLE_INPUT_SDL_DRIVER_H -#define MANGLE_INPUT_SDL_DRIVER_H - -#include "../driver.hpp" - -namespace Mangle -{ - namespace Input - { - /** Input driver for SDL. As the input system of SDL is seldomly - used alone (most often along with the video system), it is - assumed that you do your own initialization and cleanup of SDL - before and after using this driver. - - The Event.event() calls will be given the proper EV_ type, the - key index (for key up/down events), and a pointer to the full - SDL_Event structure. - */ - struct SDLDriver : Driver - { - void capture(); - bool isDown(int index); - void showMouse(bool); - }; - } -} -#endif diff --git a/libs/mangle/input/tests/.gitignore b/libs/mangle/input/tests/.gitignore deleted file mode 100644 index 460c76f00b..0000000000 --- a/libs/mangle/input/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*_test -ogre.cfg diff --git a/libs/mangle/input/tests/Makefile b/libs/mangle/input/tests/Makefile deleted file mode 100644 index 8760adfe73..0000000000 --- a/libs/mangle/input/tests/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -GCC=g++ -Wall - -all: sdl_driver_test ois_driver_test evtlist_test - -sdl_driver_test: sdl_driver_test.cpp - $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL - -ois_driver_test: ois_driver_test.cpp - $(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem - -evtlist_test: evtlist_test.cpp ../filters/eventlist.hpp ../event.hpp - $(GCC) $< -o $@ - -clean: - rm *_test diff --git a/libs/mangle/input/tests/common.cpp b/libs/mangle/input/tests/common.cpp deleted file mode 100644 index 0c7c76466b..0000000000 --- a/libs/mangle/input/tests/common.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include "../driver.hpp" -#include -using namespace std; -using namespace Mangle::Input; - -Driver *input; - -struct MyCB : Event -{ - void event(Event::Type type, int i, const void *p) - { - cout << "got event: type=" << type << " index=" << i << endl; - } -}; - -void mainLoop(int argc, int quitKey) -{ - cout << "Hold the Q key to quit:\n"; - input->setEvent(EventPtr(new MyCB)); - while(!input->isDown(quitKey)) - { - input->capture(); - usleep(20000); - - if(argc == 1) - { - cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; - break; - } - } - - delete input; - cout << "\nBye bye!\n"; -} diff --git a/libs/mangle/input/tests/evtlist_test.cpp b/libs/mangle/input/tests/evtlist_test.cpp deleted file mode 100644 index fbd980cbd9..0000000000 --- a/libs/mangle/input/tests/evtlist_test.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include "../filters/eventlist.hpp" - -using namespace std; -using namespace Mangle::Input; - -struct MyEvent : Event -{ - int ii; - MyEvent(int i) : ii(i) {} - - void event(Event::Type type, int i, const void *p) - { - cout << " #" << ii << " got event: type=" << type << " index=" << i << endl; - } -}; - -EventList lst; - -int iii=1; -void make(int flags) -{ - lst.add(EventPtr(new MyEvent(iii++)), flags); -} - -void send(Event::Type type) -{ - cout << "Sending type " << type << endl; - lst.event(type,0,NULL); -} - -int main() -{ - make(Event::EV_ALL); - make(Event::EV_KeyDown); - make(Event::EV_KeyUp | Event::EV_MouseDown); - - send(Event::EV_Unknown); - send(Event::EV_KeyDown); - send(Event::EV_KeyUp); - send(Event::EV_MouseDown); - - cout << "Enough of that\n"; - return 0; -} diff --git a/libs/mangle/input/tests/ois_driver_test.cpp b/libs/mangle/input/tests/ois_driver_test.cpp deleted file mode 100644 index 386f24055e..0000000000 --- a/libs/mangle/input/tests/ois_driver_test.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "common.cpp" - -#include "../servers/ois_driver.hpp" -#include -#include -#include - -bool isFile(const char *name) -{ - boost::filesystem::path cfg_file_path(name); - return boost::filesystem::exists(cfg_file_path); -} - -using namespace Ogre; -using namespace OIS; - -Root *root; -RenderWindow *window; - -void setupOgre() -{ - // Disable logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - bool useConfig = isFile("ogre.cfg"); - - // Set up Root - root = new Root("plugins.cfg", "ogre.cfg", ""); - - // Configure - if(!useConfig) - root->showConfigDialog(); - else - root->restoreConfig(); - - // Initialize OGRE window - window = root->initialise(true, "test", ""); -} - -int main(int argc, char** argv) -{ - setupOgre(); - input = new OISDriver(window); - - mainLoop(argc, KC_Q); - - delete root; - return 0; -} diff --git a/libs/mangle/input/tests/output/evtlist_test.out b/libs/mangle/input/tests/output/evtlist_test.out deleted file mode 100644 index 180dcc58a8..0000000000 --- a/libs/mangle/input/tests/output/evtlist_test.out +++ /dev/null @@ -1,12 +0,0 @@ -Sending type 1 - #1 got event: type=1 index=0 -Sending type 2 - #1 got event: type=2 index=0 - #2 got event: type=2 index=0 -Sending type 4 - #1 got event: type=4 index=0 - #3 got event: type=4 index=0 -Sending type 16 - #1 got event: type=16 index=0 - #3 got event: type=16 index=0 -Enough of that diff --git a/libs/mangle/input/tests/output/ois_driver_test.out b/libs/mangle/input/tests/output/ois_driver_test.out deleted file mode 100644 index 7d273fd46d..0000000000 --- a/libs/mangle/input/tests/output/ois_driver_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Hold the Q key to quit: -got event: type=8 index=-1 -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/mangle/input/tests/output/sdl_driver_test.out b/libs/mangle/input/tests/output/sdl_driver_test.out deleted file mode 100644 index 2df2e4014e..0000000000 --- a/libs/mangle/input/tests/output/sdl_driver_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Hold the Q key to quit: -got event: type=1 index=-1 -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/mangle/input/tests/plugins.cfg b/libs/mangle/input/tests/plugins.cfg deleted file mode 100644 index 57ec54e1a0..0000000000 --- a/libs/mangle/input/tests/plugins.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=/usr/local/lib/OGRE/ - -# Define plugins -Plugin=RenderSystem_GL -Plugin=Plugin_ParticleFX -Plugin=Plugin_OctreeSceneManager -# Plugin=Plugin_CgProgramManager - - diff --git a/libs/mangle/input/tests/sdl_driver_test.cpp b/libs/mangle/input/tests/sdl_driver_test.cpp deleted file mode 100644 index 5db6dbba8f..0000000000 --- a/libs/mangle/input/tests/sdl_driver_test.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "common.cpp" - -#include "../servers/sdl_driver.hpp" -#include - -int main(int argc, char** argv) -{ - SDL_Init(SDL_INIT_VIDEO); - SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); - input = new SDLDriver(); - - mainLoop(argc, SDLK_q); - - SDL_Quit(); - return 0; -} diff --git a/libs/mangle/input/tests/test.sh b/libs/mangle/input/tests/test.sh deleted file mode 100755 index 2d07708adc..0000000000 --- a/libs/mangle/input/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/rend2d/driver.hpp b/libs/mangle/rend2d/driver.hpp deleted file mode 100644 index 08a15b0aeb..0000000000 --- a/libs/mangle/rend2d/driver.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef MANGLE_REND2D_DRIVER_H -#define MANGLE_REND2D_DRIVER_H - -#include -#include "sprite.hpp" - -namespace Mangle -{ - namespace Rend2D - { - /** - The driver is the connection to the backend system that powers - 2D sprite rendering. For example the backend could be SDL or - any other 2D-capable graphics library. - */ - struct Driver - { - /// Get the screen sprite - virtual Sprite *getScreen() = 0; - - /// Sets the video mode. - virtual void setVideoMode(int width, int height, int bpp=32, bool fullscreen=false) = 0; - - /** Update the screen. Until this function is called, none of - the changes written to the screen sprite will be visible. - */ - virtual void update() = 0; - - /// Set the window title, as well as the title of the window - /// when "iconified" - virtual void setWindowTitle(const std::string &title, - const std::string &icon) = 0; - - /// Set the window title - void setWindowTitle(const std::string &title) { setWindowTitle(title,title); } - - /// Load sprite from an image file. Thows an exception on - /// failure. - virtual Sprite* loadImage(const std::string &file) = 0; - - /// Load a sprite from an image file stored in memory. Throws - /// exception on failure. - virtual Sprite* loadImage(const void* data, size_t size) = 0; - - /** @brief Set gamma value for all colors. - - Note: Setting this in windowed mode will affect the ENTIRE - SCREEN! - */ - virtual void setGamma(float gamma) = 0; - - /// Set gamma individually for red, green, blue - virtual void setGamma(float red, float green, float blue) = 0; - - /// Get screen width - virtual int width() = 0; - - /// Get screen height - virtual int height() = 0; - }; - } -} -#endif diff --git a/libs/mangle/rend2d/servers/sdl_driver.cpp b/libs/mangle/rend2d/servers/sdl_driver.cpp deleted file mode 100644 index 84a17933ff..0000000000 --- a/libs/mangle/rend2d/servers/sdl_driver.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "sdl_driver.hpp" - -#include -#include -#include -#include - -using namespace Mangle::Rend2D; - -const SpriteData *SDL_Sprite::lock() -{ - // Make sure we aren't already locked - assert(!data.pixels); - - // Lock the surface and set up the data structure - SDL_LockSurface(surface); - - data.pixels = surface->pixels; - data.w = surface->w; - data.h = surface->h; - data.pitch = surface->pitch; - data.bypp = surface->format->BytesPerPixel; - - return &data; -} - -void SDL_Sprite::unlock() -{ - if(data.pixels) - { - SDL_UnlockSurface(surface); - data.pixels = NULL; - } -} - -// This is a really crappy and slow implementation, only intended for -// testing purposes. Use lock/unlock for faster pixel drawing. -void SDL_Sprite::pixel(int x, int y, int color) -{ - SDL_LockSurface(surface); - - int bpp = surface->format->BytesPerPixel; - char *p = (char*)surface->pixels + y*surface->pitch + x*bpp; - - switch(bpp) - { - case 1: *p = color; break; - case 3: - if(SDL_BYTEORDER == SDL_BIG_ENDIAN) - { - p[0] = (color >> 16) & 0xff; - p[1] = (color >> 8) & 0xff; - p[2] = color & 0xff; - } - else - { - p[0] = color & 0xff; - p[1] = (color >> 8) & 0xff; - p[2] = (color >> 16) & 0xff; - } - break; - case 4: - *(int*)p = color; - break; - } - SDL_UnlockSurface(surface); -} - -void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite - int x, int y, // Destination position - int sx, int sy, // Source position - int w, int h // Amount to draw. -1 means remainder. - ) -{ - // Get source surface - SDL_Sprite *other = dynamic_cast(s); - assert(other != NULL); - SDL_Surface *img = other->getSurface(); - - // Check coordinate validity - assert(sx <= img->w && sy <= img->h); - assert(x <= surface->w && y <= surface->h); - assert(sx >= 0 && sy >= 0); - - // Compute width and height if necessary - if(w == -1) w = img->w - sx; - if(h == -1) h = img->h - sy; - - // Check them if they're valid - assert(w >= 0 && w <= img->w); - assert(h >= 0 && h <= img->h); - - SDL_Rect dest; - dest.x = x; - dest.y = y; - dest.w = w; - dest.h = h; - - SDL_Rect src; - src.x = sx; - src.y = sy; - src.w = w; - src.h = h; - - // Do the Blitman - SDL_BlitSurface(img, &src, surface, &dest); -} - -SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete) - : surface(s), autoDel(autoDelete) -{ - assert(surface != NULL); - data.pixels = NULL; -} - -SDL_Sprite::~SDL_Sprite() -{ - if(autoDel) - SDL_FreeSurface(surface); -} - -void SDL_Sprite::fill(int value) -{ - SDL_FillRect(surface, NULL, value); -} - -int SDL_Sprite::width() { return surface->w; } -int SDL_Sprite::height() { return surface->h; } - -SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false) -{ - if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw std::runtime_error("Error initializing SDL video"); -} -SDLDriver::~SDLDriver() -{ - if(display) delete display; - SDL_Quit(); -} - -void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) -{ - unsigned int flags; - - if(display) delete display; - - if (fullscreen) - // Assume fullscreen mode allows a double-bufferd hardware - // mode. We need more test code for this to be safe though. - flags = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; - else - flags = SDL_SWSURFACE; - - // Create the surface and check it - realDisp = SDL_SetVideoMode(width, height, bpp, flags); - if(realDisp == NULL) - throw std::runtime_error("Failed setting SDL video mode"); - - // Code for software double buffering. I haven't found this to be - // any speed advantage at all in windowed mode (it's slower, as one - // would expect.) Not properly tested in fullscreen mode with - // hardware buffers, but it will probably only be an improvement if - // we do excessive writing (ie. write each pixel on average more - // than once) or try to read from the display buffer. - if(softDouble) - { - // Make a new surface with the same attributes as the real - // display surface. - SDL_Surface *back = SDL_DisplayFormat(realDisp); - assert(back != NULL); - - // Create a sprite representing the double buffer - display = new SDL_Sprite(back); - } - else - { - // Create a sprite directly representing the display surface. - // The 'false' parameter means do not autodelete the screen - // surface upon exit (since SDL manages it) - display = new SDL_Sprite(realDisp, false); - } -} - -/// Update the screen -void SDLDriver::update() -{ - // Blit the soft double buffer onto the real display buffer - if(softDouble) - SDL_BlitSurface(display->getSurface(), NULL, realDisp, NULL ); - - if(realDisp) - SDL_Flip(realDisp); -} - -/// Set the window title, as well as the title of the window when -/// "iconified" -void SDLDriver::setWindowTitle(const std::string &title, - const std::string &icon) -{ - SDL_WM_SetCaption( title.c_str(), icon.c_str() ); -} - -// Convert the given surface to display format. -static SDL_Surface* convertImage(SDL_Surface* surf) -{ - if(surf != NULL) - { - // Convert the image to the display buffer format, for faster - // blitting - SDL_Surface *surf2 = SDL_DisplayFormat(surf); - SDL_FreeSurface(surf); - surf = surf2; - } - return surf; -} - -/// Load sprite from an image file, using SDL_image. -Sprite* SDLDriver::loadImage(const std::string &file) -{ - SDL_Surface *surf = IMG_Load(file.c_str()); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image file '" + file + "'"); - return spriteFromSDL(surf); -} - -/// Load sprite from an SDL_RWops structure. autoFree determines -/// whether the RWops struct should be closed/freed after use. -Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree) -{ - SDL_Surface *surf = IMG_Load_RW(src, autoFree); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image"); - return spriteFromSDL(surf); -} - -/// Load a sprite from an image file stored in memory. Uses -/// SDL_image. -Sprite* SDLDriver::loadImage(const void* data, size_t size) -{ - SDL_RWops *rw = SDL_RWFromConstMem(data, size); - return loadImage(rw, true); -} - -void SDLDriver::setGamma(float red, float green, float blue) -{ - SDL_SetGamma(red,green,blue); -} - -/// Convert an existing SDL surface into a sprite -Sprite* SDLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) -{ - assert(surf); - return new SDL_Sprite(surf, autoFree); -} - -void SDLDriver::sleep(int ms) { SDL_Delay(ms); } -unsigned int SDLDriver::ticks() { return SDL_GetTicks(); } diff --git a/libs/mangle/rend2d/servers/sdl_driver.hpp b/libs/mangle/rend2d/servers/sdl_driver.hpp deleted file mode 100644 index 0f205ba34c..0000000000 --- a/libs/mangle/rend2d/servers/sdl_driver.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef MANGLE_DRAW2D_SDL_H -#define MANGLE_DRAW2D_SDL_H - -#include "../driver.hpp" - -// Predeclarations keep the streets safe at night -struct SDL_Surface; -struct SDL_RWops; - -namespace Mangle -{ - namespace Rend2D - { - /// SDL-implementation of Sprite - struct SDL_Sprite : Sprite - { - /** Draw a sprite in the given position. Can only draw other SDL - sprites. - */ - void draw(Sprite *s, // Must be SDL_Sprite - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ); - - SDL_Sprite(SDL_Surface *s, bool autoDelete=true); - ~SDL_Sprite(); - - // Information retrieval - int width(); - int height(); - SDL_Surface *getSurface() { return surface; } - - // Fill with a given pixel value - void fill(int value); - - // Set one pixel - void pixel(int x, int y, int value); - - const SpriteData *lock(); - void unlock(); - - private: - // The SDL surface - SDL_Surface* surface; - - // Used for locking - SpriteData data; - - // If true, delete this surface when the canvas is destructed - bool autoDel; - }; - - class SDLDriver : public Driver - { - // The main display surface - SDL_Sprite *display; - - // The actual display surface. May or may not be the same - // surface pointed to by 'display' above, depending on the - // softDouble flag. - SDL_Surface *realDisp; - - // If true, we do software double buffering. - bool softDouble; - - public: - SDLDriver(); - ~SDLDriver(); - - /// Sets the video mode. Will create the window if it is not - /// already set up. Note that for SDL, bpp=0 means use current - /// bpp. - void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); - - /// Update the screen - void update(); - - /// Set the window title, as well as the title of the window - /// when "iconified" - void setWindowTitle(const std::string &title, - const std::string &icon); - - // Include overloads from our Glorious parent - using Driver::setWindowTitle; - - /// Load sprite from an image file, using SDL_image. - Sprite* loadImage(const std::string &file); - - /// Load sprite from an SDL_RWops structure. autoFree determines - /// whether the RWops struct should be closed/freed after use. - Sprite* loadImage(SDL_RWops *src, bool autoFree=false); - - /// Load a sprite from an image file stored in memory. Uses - /// SDL_image. - Sprite* loadImage(const void* data, size_t size); - - /// Set gamma value - void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } - - /// Set gamma individually for red, green, blue - void setGamma(float red, float green, float blue); - - /// Convert an existing SDL surface into a sprite - Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); - - // Get width and height - int width() { return display ? display->width() : 0; } - int height() { return display ? display->height() : 0; } - - /// Get the screen sprite - Sprite *getScreen() { return display; } - - /// Not really a graphic-related function, but very - /// handly. Sleeps the given number of milliseconds using - /// SDL_Delay(). - void sleep(int ms); - - /// Get the number of ticks since SDL initialization, using - /// SDL_GetTicks(). - unsigned int ticks(); - }; - } -} -#endif diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp b/libs/mangle/rend2d/servers/sdl_gl_driver.cpp deleted file mode 100644 index db519e0911..0000000000 --- a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp +++ /dev/null @@ -1,311 +0,0 @@ -#include "sdl_gl_driver.hpp" - -#include -#include -#include -#include -#include - -using namespace Mangle::Rend2D; - -void SDLGL_Sprite::draw(Sprite *s, // Must be SDLGL_Sprite - int x, int y, // Destination position - int sx, int sy, // Source position - int w, int h // Amount to draw. -1 means remainder. - ) -{ - // Get source surface - SDLGL_Sprite *other = dynamic_cast(s); - assert(other != NULL); - SDL_Surface *img = other->getSurface(); - - // Check coordinate validity - assert(sx <= img->w && sy <= img->h); - assert(x <= surface->w && y <= surface->h); - assert(sx >= 0 && sy >= 0); - - // Compute width and height if necessary - if(w == -1) w = img->w - sx; - if(h == -1) h = img->h - sy; - - // Check them if they're valid - assert(w >= 0 && w <= img->w); - assert(h >= 0 && h <= img->h); - - SDL_Rect dest; - dest.x = x; - dest.y = y; - dest.w = w; - dest.h = h; - - SDL_Rect src; - src.x = sx; - src.y = sy; - src.w = w; - src.h = h; - - // Do the Blitman - SDL_BlitSurface(img, &src, surface, &dest); -} - -SDLGL_Sprite::SDLGL_Sprite(SDL_Surface *s, bool autoDelete) - : surface(s), autoDel(autoDelete) -{ - assert(surface != NULL); -} - -SDLGL_Sprite::~SDLGL_Sprite() -{ - if(autoDel) - SDL_FreeSurface(surface); -} - -void SDLGL_Sprite::fill(int value) -{ - SDL_FillRect(surface, NULL, value); -} - -int SDLGL_Sprite::width() { return surface->w; } -int SDLGL_Sprite::height() { return surface->h; } - -SDLGLDriver::SDLGLDriver() : display(NULL), realDisp(NULL) -{ - if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw std::runtime_error("Error initializing SDL video"); -} -SDLGLDriver::~SDLGLDriver() -{ - if(display) delete display; - SDL_Quit(); -} - -// Surface used for the screen. Since OpenGL surfaces must have sizes -// that are powers of 2, we have to "fake" the returned display size -// to match the screen, not the surface itself. If we don't use this, -// the client program will get confused about the actual size of our -// screen, thinking it is bigger than it is. -struct FakeSizeSprite : SDLGL_Sprite -{ - int fakeW, fakeH; - - FakeSizeSprite(SDL_Surface *s, int fw, int fh) - : SDLGL_Sprite(s), fakeW(fw), fakeH(fh) - {} - - int width() { return fakeW; } - int height() { return fakeH; } -}; - -static int makePow2(int num) -{ - assert(num); - if((num & (num-1)) != 0) - { - int cnt = 0; - while(num) - { - num >>= 1; - cnt++; - } - num = 1 << cnt; - } - return num; -} - -void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) -{ - unsigned int flags; - - if(display) delete display; - - flags = SDL_OPENGL; - - if (fullscreen) - flags |= SDL_FULLSCREEN; - - SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); - - // Create the surface and check it - screen = SDL_SetVideoMode(width, height, bpp, flags); - if(screen == NULL) - throw std::runtime_error("Failed setting SDL video mode"); - - // Expand width and height to be powers of 2 - int width2 = makePow2(width); - int height2 = makePow2(height); - - // Create a new SDL surface of this size - const SDL_PixelFormat& fmt = *(screen->format); - realDisp = SDL_CreateRGBSurface(SDL_SWSURFACE,width2,height2, - fmt.BitsPerPixel, - fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask); - - // Create a sprite directly representing the display surface. This - // allows the user to blit to it directly. - display = new FakeSizeSprite(realDisp, width, height); - - // Set up the OpenGL format - nOfColors = fmt.BytesPerPixel; - - if(nOfColors == 4) - { - if (fmt.Rmask == 0x000000ff) - texture_format = GL_RGBA; - else - texture_format = GL_BGRA; - } - else if(nOfColors == 3) - { - if (fmt.Rmask == 0x000000ff) - texture_format = GL_RGB; - else - texture_format = GL_BGR; - } - else - assert(0 && "unsupported screen format"); - - glEnable(GL_TEXTURE_2D); - - // Have OpenGL generate a texture object handle for us - glGenTextures( 1, &texture ); - - // Bind the texture object - glBindTexture( GL_TEXTURE_2D, texture ); - - // Set the texture's stretching properties - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); -} - -void SDLGLDriver::updateNoSwap() -{ - if(!realDisp) return; - - // Fist, set up the screen texture: - - // Bind the texture object - glBindTexture( GL_TEXTURE_2D, texture ); - - // Edit the texture object's image data - glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, realDisp->w, realDisp->h, 0, - texture_format, GL_UNSIGNED_BYTE, realDisp->pixels ); - - glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); - - // OpenGL barf. Set up the projection to match our screen - int vPort[4]; - glGetIntegerv(GL_VIEWPORT, vPort); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, vPort[2], 0, vPort[3], -1, 1); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glBegin( GL_QUADS ); - - // Needed to move the screen into the right place - int diff = screen->h - realDisp->h; - - // Bottom-left vertex (corner) - glTexCoord2i( 0, 1 ); - glVertex3f(0,diff,0); - - // Bottom-right vertex (corner) - glTexCoord2i( 1, 1 ); - glVertex3f( realDisp->w, diff, 0.f ); - - // Top-right vertex (corner) - glTexCoord2i( 1, 0 ); - glVertex3f( realDisp->w, screen->h, 0.f ); - - // Top-left vertex (corner) - glTexCoord2i( 0, 0 ); - glVertex3f( 0, screen->h, 0.f ); - glEnd(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); -} - -void SDLGLDriver::swap() -{ - SDL_GL_SwapBuffers(); -} - -void SDLGLDriver::update() -{ - updateNoSwap(); - swap(); -} - -/// Set the window title, as well as the title of the window when -/// "iconified" -void SDLGLDriver::setWindowTitle(const std::string &title, - const std::string &icon) -{ - SDL_WM_SetCaption( title.c_str(), icon.c_str() ); -} - -// Convert the given surface to display format. -static SDL_Surface* convertImage(SDL_Surface* surf) -{ - if(surf != NULL) - { - // Convert the image to the display buffer format, for faster - // blitting - SDL_Surface *surf2 = SDL_DisplayFormat(surf); - SDL_FreeSurface(surf); - surf = surf2; - } - return surf; -} - -/// Load sprite from an image file, using SDL_image. -Sprite* SDLGLDriver::loadImage(const std::string &file) -{ - SDL_Surface *surf = IMG_Load(file.c_str()); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image file '" + file + "'"); - return spriteFromSDL(surf); -} - -/// Load sprite from an SDL_RWops structure. autoFree determines -/// whether the RWops struct should be closed/freed after use. -Sprite* SDLGLDriver::loadImage(SDL_RWops *src, bool autoFree) -{ - SDL_Surface *surf = IMG_Load_RW(src, autoFree); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image"); - return spriteFromSDL(surf); -} - -/// Load a sprite from an image file stored in memory. Uses -/// SDL_image. -Sprite* SDLGLDriver::loadImage(const void* data, size_t size) -{ - SDL_RWops *rw = SDL_RWFromConstMem(data, size); - return loadImage(rw, true); -} - -void SDLGLDriver::setGamma(float red, float green, float blue) -{ - SDL_SetGamma(red,green,blue); -} - -/// Convert an existing SDL surface into a sprite -Sprite* SDLGLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) -{ - assert(surf); - return new SDLGL_Sprite(surf, autoFree); -} - -void SDLGLDriver::sleep(int ms) { SDL_Delay(ms); } -unsigned int SDLGLDriver::ticks() { return SDL_GetTicks(); } diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.hpp b/libs/mangle/rend2d/servers/sdl_gl_driver.hpp deleted file mode 100644 index d116e3659b..0000000000 --- a/libs/mangle/rend2d/servers/sdl_gl_driver.hpp +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef MANGLE_DRAW2D_SDLGL_H -#define MANGLE_DRAW2D_SDLGL_H - -/** This driver is similar to SDLDriver, except that it uses SDL on - top of OpenGL. - - I've decided to make it a separate file instead of just adding - optional OpenGL support to the original, so that pure SDL users - don't have to add OpenGL as a dependency. - */ - -#include "../driver.hpp" - -// Predeclarations keep the streets safe at night -struct SDL_Surface; -struct SDL_RWops; - -namespace Mangle -{ - namespace Rend2D - { - /// SDL-implementation of Sprite - struct SDLGL_Sprite : Sprite - { - /** Draw a sprite in the given position. Can only draw other SDL - sprites. - */ - void draw(Sprite *s, // Must be SDLGL_Sprite - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ); - - SDLGL_Sprite(SDL_Surface *s, bool autoDelete=true); - ~SDLGL_Sprite(); - - // Information retrieval - virtual int width(); - virtual int height(); - SDL_Surface *getSurface() { return surface; } - - // Fill with a given pixel value - void fill(int value); - - private: - // The SDL surface - SDL_Surface* surface; - - // If true, delete this surface when the canvas is destructed - bool autoDel; - }; - - class SDLGLDriver : public Driver - { - // The main display surface - SDLGL_Sprite *display; - - // The screen surface. This is completely unused. - SDL_Surface *screen; - - // The display surface and main GL texture. These are used when - // drawing the entire screen as one surface, as a drop-in - // replacement for SDLDriver. - SDL_Surface *realDisp; - unsigned int texture; - int nOfColors, texture_format; - - public: - SDLGLDriver(); - ~SDLGLDriver(); - - /// Sets the video mode. Will create the window if it is not - /// already set up. Note that for SDL, bpp=0 means use current - /// bpp. - void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); - - /// Update the screen - void update(); - - /// Calls SDL_GL_SwapBuffers - void swap(); - - /// Draw surface to screen but do not call SDL_GL_SwapBuffers() - void updateNoSwap(); - - /// Set the window title, as well as the title of the window - /// when "iconified" - void setWindowTitle(const std::string &title, - const std::string &icon); - - // Include overloads from our Glorious parent - using Driver::setWindowTitle; - - /// Load sprite from an image file, using SDL_image. - Sprite* loadImage(const std::string &file); - - /// Load sprite from an SDL_RWops structure. autoFree determines - /// whether the RWops struct should be closed/freed after use. - Sprite* loadImage(SDL_RWops *src, bool autoFree=false); - - /// Load a sprite from an image file stored in memory. Uses - /// SDL_image. - Sprite* loadImage(const void* data, size_t size); - - /// Set gamma value - void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } - - /// Set gamma individually for red, green, blue - void setGamma(float red, float green, float blue); - - /// Convert an existing SDL surface into a sprite - Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); - - // Get width and height - int width() { return display ? display->width() : 0; } - int height() { return display ? display->height() : 0; } - - /// Get the screen sprite - Sprite *getScreen() { return display; } - - /// Not really a graphic-related function, but very - /// handly. Sleeps the given number of milliseconds using - /// SDL_Delay(). - void sleep(int ms); - - /// Get the number of ticks since SDL initialization, using - /// SDL_GetTicks(). - unsigned int ticks(); - }; - } -} -#endif diff --git a/libs/mangle/rend2d/sprite.hpp b/libs/mangle/rend2d/sprite.hpp deleted file mode 100644 index f49da6cb6d..0000000000 --- a/libs/mangle/rend2d/sprite.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef MANGLE_REND2D_SPRITE_H -#define MANGLE_REND2D_SPRITE_H - -namespace Mangle -{ - namespace Rend2D - { - /** - A pointer to sprite data for direct drawing. Only to be used - while the corresponding sprite is locked. - */ - struct SpriteData - { - void *pixels; // Pixel data - int w, h; // Width and height - int pitch, bypp; // Pitch (bytes) and bytes per pixel - }; - - /** - A Sprite is either a bitmap to be drawn or an output of area - for blitting other bitmaps, or both. They are created by the - Driver. - */ - struct Sprite - { - /// Draw a sprite in the given position - virtual void draw(Sprite *s, // The sprite to draw - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ) = 0; - - virtual ~Sprite() {} - - // Information retrieval - virtual int width() = 0; - virtual int height() = 0; - - /// Fill the sprite with the given pixel value. The pixel format - /// depends on the format of the sprite. - virtual void fill(int value) = 0; - - /// Set one pixel value. The pixel format depends on the sprite - /// format. This is not expected to be fast, and in some - /// implementations may not work at all. - virtual void pixel(int x, int y, int value) {} - - /// Lock sprite for direct drawing, and return a struct - /// containing the necessary pointer. When finished, unlock the - /// sprite with unlock(). May return NULL, if so then direct - /// drawing is not possible. - virtual const SpriteData *lock() { return NULL; } - virtual void unlock() {} - }; - } -} -#endif diff --git a/libs/mangle/rend2d/tests/.gitignore b/libs/mangle/rend2d/tests/.gitignore deleted file mode 100644 index 8144904045..0000000000 --- a/libs/mangle/rend2d/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/libs/mangle/rend2d/tests/Makefile b/libs/mangle/rend2d/tests/Makefile deleted file mode 100644 index d430f60a93..0000000000 --- a/libs/mangle/rend2d/tests/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -GCC=g++ -Wall -Werror - -all: sdl_test sdl_move_test sdlgl_move_test - -sdl_test: sdl_test.cpp - $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image - -sdl_move_test: sdl_move_test.cpp ../servers/sdl_driver.cpp - $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image - -sdlgl_move_test: sdlgl_move_test.cpp ../servers/sdl_gl_driver.cpp - $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image -lGL - -clean: - rm *_test diff --git a/libs/mangle/rend2d/tests/output/sdl_move_test.out b/libs/mangle/rend2d/tests/output/sdl_move_test.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libs/mangle/rend2d/tests/output/sdl_test.out b/libs/mangle/rend2d/tests/output/sdl_test.out deleted file mode 100644 index 4528e1a98a..0000000000 --- a/libs/mangle/rend2d/tests/output/sdl_test.out +++ /dev/null @@ -1,11 +0,0 @@ -Loading SDL driver. -Creating window. -Current mode: 640x480 -Setting fancy title, cause we like fancy titles. -Loading tile1-blue.png from file. -Loading tile1-yellow.png from memory. -Going bananas. -Taking a breather. -WOW DID YOU SEE THAT!? -Mucking about with the gamma settings -Done. diff --git a/libs/mangle/rend2d/tests/output/sdlgl_move_test.out b/libs/mangle/rend2d/tests/output/sdlgl_move_test.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libs/mangle/rend2d/tests/sdl_move_test.cpp b/libs/mangle/rend2d/tests/sdl_move_test.cpp deleted file mode 100644 index bfbca98fa7..0000000000 --- a/libs/mangle/rend2d/tests/sdl_move_test.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - SDLDriver sdl; - - sdl.setVideoMode(640,480,0,false); - sdl.setWindowTitle("Testing 123"); - Sprite *screen = sdl.getScreen(); - const char* imgName = "tile1-blue.png"; - Sprite *image = sdl.loadImage(imgName); - - for(int frames=0; frames<170; frames++) - { - screen->fill(0); - for(int j=0; j<10; j++) - for(int i=0; i<25; i++) - screen->draw(image, 2*frames+30*j, 20*i); - sdl.update(); - sdl.sleep(10); - } - return 0; -} diff --git a/libs/mangle/rend2d/tests/sdl_test.cpp b/libs/mangle/rend2d/tests/sdl_test.cpp deleted file mode 100644 index 0355112e61..0000000000 --- a/libs/mangle/rend2d/tests/sdl_test.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - cout << "Loading SDL driver.\n"; - SDLDriver sdl; - - cout << "Creating window.\n"; - sdl.setVideoMode(640,480); - cout << "Current mode: " << sdl.width() << "x" << sdl.height() << endl; - - cout << "Setting fancy title, cause we like fancy titles.\n"; - sdl.setWindowTitle("Chief executive window"); - - // Display surface - Sprite *screen = sdl.getScreen(); - - const char* imgName = "tile1-blue.png"; - cout << "Loading " << imgName << " from file.\n"; - Sprite *image = sdl.loadImage(imgName); - - const char* imgName2 = "tile1-yellow.png"; - cout << "Loading " << imgName2 << " from memory.\n"; - Sprite *image2; - { - // This is hard-coded for file sizes below 500 bytes, so obviously - // you shouldn't mess with the image files. - ifstream file(imgName2, ios::binary); - char buf[500]; - file.read(buf, 500); - int size = file.gcount(); - image2 = sdl.loadImage(buf, size); - } - - cout << "Going bananas.\n"; - for(int i=1; i<20; i++) - screen->draw(image, 30*i, 20*i); - - cout << "Taking a breather.\n"; - sdl.update(); - for(int i=1; i<20; i++) - screen->draw(image2, 30*(20-i), 20*i); - sdl.sleep(800); - sdl.update(); - cout << "WOW DID YOU SEE THAT!?\n"; - sdl.sleep(800); - - cout << "Mucking about with the gamma settings\n"; - sdl.setGamma(2.0, 0.1, 0.8); - sdl.sleep(100); - sdl.setGamma(0.6, 2.1, 2.1); - sdl.sleep(100); - sdl.setGamma(1.6); - sdl.sleep(100); - - cout << "Done.\n"; - return 0; -} diff --git a/libs/mangle/rend2d/tests/sdlgl_move_test.cpp b/libs/mangle/rend2d/tests/sdlgl_move_test.cpp deleted file mode 100644 index b769ee837d..0000000000 --- a/libs/mangle/rend2d/tests/sdlgl_move_test.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_gl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - SDLGLDriver sdl; - - sdl.setVideoMode(640,480,0,false); - sdl.setWindowTitle("Testing 123"); - Sprite *screen = sdl.getScreen(); - const char* imgName = "tile1-blue.png"; - Sprite *image = sdl.loadImage(imgName); - - for(int frames=0; frames<170; frames++) - { - screen->fill(0); - for(int j=0; j<10; j++) - for(int i=0; i<25; i++) - screen->draw(image, 2*frames+30*j, 20*i); - sdl.update(); - sdl.sleep(5); - } - - return 0; -} diff --git a/libs/mangle/rend2d/tests/test.sh b/libs/mangle/rend2d/tests/test.sh deleted file mode 100755 index 2d07708adc..0000000000 --- a/libs/mangle/rend2d/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/rend2d/tests/tile1-blue.png b/libs/mangle/rend2d/tests/tile1-blue.png deleted file mode 100644 index 066e6f8eb932e81d0ab391bc16a3b9e0fb063d7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg4HgO>q^~cs6azG);64!_l=ltB< z)VvY~=c3falGGH1^30M91$R&1fbd2>aiF3cPZ!4!jq}MON!e)%tQ!)zB&BycNVusb zWdv<3H_ml_#P;5c#V!`t_IO|Ks0DIaVrPLG@TdWn2J z|KvBBkM=tLPH;V9awjzAOkRtr!tdY5?;n;qvu%fo@uXRM82JyY98GH4whCx3gQu&X J%Q~loCIFm~TL1t6 diff --git a/libs/mangle/rend2d/tests/tile1-yellow.png b/libs/mangle/rend2d/tests/tile1-yellow.png deleted file mode 100644 index 2aaf9015d313d5cd00915ffbb1df2f0262ea2116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEjDo>Htxq)+3JBpk|nMYCC>S|xv6<249-QVi6yBi3gww4 z84B*6z5(HleBwYwd7dtgAsXkCb@CIF6<9YUa9LVOJ4;MTQp*TBJ?;FFMT!6KZ8mro zxg%{yo!B3~hU~W2DM<=~6P7+aaBe@#)q{84nt0F0&sDtcsm}jj_2%Ux)y>}yc6DU# wSS@pBCTF(8x9=wp*PqRuS(uroVp_?}Ab(0lNY${m9cVLyr>mdKI;Vst0EqflTmS$7 diff --git a/libs/mangle/testall.sh b/libs/mangle/testall.sh deleted file mode 100755 index b93fee2159..0000000000 --- a/libs/mangle/testall.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -function run() -{ - echo "TESTING $1" - cd "$1/tests/" - ./test.sh - cd ../../ -} - -run stream -run vfs -run sound -run input -run rend2d -run . diff --git a/libs/mangle/tests/.gitignore b/libs/mangle/tests/.gitignore deleted file mode 100644 index 8144904045..0000000000 --- a/libs/mangle/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/libs/mangle/tests/Makefile b/libs/mangle/tests/Makefile deleted file mode 100644 index d912c0784b..0000000000 --- a/libs/mangle/tests/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -GCC=g++ -I../ - -all: ogrevfs_audiere_openal_test - -I_OGRE=$(shell pkg-config --cflags OGRE) -L_OGRE=$(shell pkg-config --libs OGRE) -L_OPENAL=$(shell pkg-config --libs openal) -L_AUDIERE=-laudiere - -ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/sources/audiere_source.cpp ../sound/outputs/openal_out.cpp ../stream/clients/audiere_file.cpp - $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) - -clean: - rm *_test diff --git a/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp b/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp deleted file mode 100644 index 4936538c55..0000000000 --- a/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - This example combines: - - - the OGRE VFS system (to read from zip) - - Audiere (for decoding sound data) - - OpenAL (for sound playback) - - */ - -#include "sound/filters/openal_audiere.hpp" -#include "vfs/servers/ogre_vfs.hpp" -#include -#include - -using namespace Ogre; -using namespace Mangle; -using namespace std; - -int main() -{ - // Disable Ogre logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - // Set up Root - Root *root = new Root("","",""); - - // Add zip file with a sound in it - root->addResourceLocation("sound.zip", "Zip", "General"); - - // Ogre file system - VFS::OgreVFS vfs; - - // The main sound system - Sound::OpenAL_Audiere_Factory mg; - Sound::SoundPtr snd = mg.load(vfs.open("owl.ogg")); - - cout << "Playing 'owl.ogg' from 'sound.zip'\n"; - snd->play(); - - while(snd->isPlaying()) - { - usleep(10000); - if(mg.needsUpdate) mg.update(); - } - - return 0; -} diff --git a/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out b/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out deleted file mode 100644 index 28ea8a71b7..0000000000 --- a/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out +++ /dev/null @@ -1 +0,0 @@ -Playing 'owl.ogg' from 'sound.zip' diff --git a/libs/mangle/tests/sound.zip b/libs/mangle/tests/sound.zip deleted file mode 100644 index fd32b35299859ba2d92e702db719367110d8b994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18159 zcmV(wKAfrl z3^|OCtA~ZX=U)O0HuJxFD7gRYoeQJ(cO>9{j)Z}}D_^#G-mt#@r-0%9C4&sg(zAE7 zV^j06rF62l(D{c-sYuDi!NJeL$Hn!}BnbcNj3Xtj1C<2>Km*}X(GqC_Es--pAVvf- zOLaqeGRp-)IVvkG%@{?ZiU2jHOg-Q?Y>*zKESaXVreY>23tF?p(^R0+T;jAY@x)V# zQbeJ3Qfc%eP&&-D%;JS0iY1HkctUc+hZcbxLYbvf41J>z z&=G57oMBw=GO^|fYXqT^{UEZr=NWkM2hd@d&@zE@WOzrDA$%r9`>&n=33!-)bVdaL zzNn+gYJ~n}NFV}20eG_Rc%$y5@@elTVE^eN0r+cfhNNwVG#bS;EB!2M{j5sAtm@8M z_NPX+lg6{-E{uq%dU(J$0Dv%xDjK3VnWCAZNXMu>DTrKhiy^p&XEg^J$+1Y#m`>3| z<3!+$nqiE@T2K|NK%+*MRw%TZrc@F=+_E?jz#?v?2E;&TOOQ;U8cFd)10qs+Bq@HQ zl$jP}EX+^_GBkY3Qbb|RT%tZG$WR5+ReT}Bada4TUQ(B=h?UP2JasAyx z4CseIs)ULeHdFux@Zp_(UWI+BoqZmIe`R%TDMM}To?qorZe^{1X-#cqZC7JWZKIQ4 zk^Nt85o2vFwCPw|dlFacFi}~z)_zpmemdUj(%o9WUQ|2YUend;Qrqa<-RgAG$g&&L0bt$(UK zIc`55?`j-xJzsBcd}?iY(|y?0-Z1l-FfpZw5h$i6seu%y}1-* zo#S1|(FIkg{q&@k<)p9iWQ60Si=)2F`MI&-&DXkq(b;0<>An^3-)a{XsG||<(*4i}D z#fH}1Nb=QT{mc1Um)m438(PN&^Jvo>>BaWQ?M3ghy7dv)H(%%Yefj`^P#7erm*b90 z{AY_1sgUt4v6x0EBd|cIROMQnh*Xvsz!B06ETAG)#vGJk1$9BvbS$7kt44tC^OsvVw8>tR@13fWNb-_B04y^6!QjR6%1WHVtU;<0%*AS|On?L)0APk@nILJ0=P@bBmuJo!ZJMQ zW>*kGMOENgPD_3x^qiJtB!brHX(OM^20o{m3_Vh)LijNMQpf_0 zGm6p+B*cgvp$w$2NX0RD!)ToajH4ZyG>B~&T2Vl0h+N`wq#c>FkZTyyvtR-MPN52& z4oETZSkVGfkN^^YHJBE(#j=2gM=BmpcB3F2+0u^a_6C@cbJaM3-KFk<6 zKu}AXK5*Ju0)j?aS=eKc=LD0l;$sp@#qKM^vCVPUt@Tt;qm6_y0wUfNs@aw}N_9 z%0J%tmj)7o($s+{f7gHT5AAOah3a3&1G+kB?cdg+{V!U@f7depyN2_Rum02dUsC=V z#_-pj|EZzS{+FSt{)fo{p>TgAHO=9>vTQZ)$34V4$pFU}Ruups%l^u5Dm!tgEA)z@&xTc*N5+y+hCGsTu=yDhBANton$nDs84j%z<9uPCSree(22lugv*l5+swi8#=mz%Aj2qdHP zYX#d5c;C0Np6>Ufi!vM3hFiawWnPqZYT!s04U>PD1cQ>30|#)7o!y5DI7=5-R;275 z@aG{PTCw6d(6#qa<2Gp+Da5-PjsYG3*+SU&YkfCIy|0@TL)3J9uH?-zx8E*8bnUsN zZlh6P5JL4oUOjNKU9d0FSN66f+E?!M`1K@cU`1TK;AQ!qM)*JC5yld(_qC#qw05d|fc-T{(6^Z$-d)1+n=N{czg3udVj4h>l?tbH2Y!mzeD@Hj{FXNOprBWtYlco9XU_4 z0dZWrG~`7e16326e)vDC8PsTRHj7~v1Mql8C3S*I?F3D=-S8hRmbBEIabE%)xC*<; zw6>e5MI2P*myjo1Rj%anzy*80lFbisIB-$M$-^WzhJ>Pr1EnzPV$QlFH^G`M_a)$D zc^jz;GXMEJk2J2$H?5Xlh?bYJN&D4D<6M0EuYK&pqE69t9MKcGjZRCsYm9sjkffbc zW=fHbgp{NFeTebvSd+R3YWSu!R=;viG%a-rjG}DpJdM=)r6+%D)yPQek#NgdC1sv> z^f}Y&>v7%TITnW%e*T*~*`iM_CN~sq6MypPc-cBPFhg8Oa;}{Qe=t6~?D)A9iTK1u zFT*Ao>(AQ`2)C44!OETKl74s$I{o(RBACUL7J=Ty*7CUPW8>U}*<^Aw{V?dFKGJ*W z6wPSq;C#k(RPOaE3uP^=pD< zadC0P?fuN%!RJ(kn?39pRs&)44Fzodgu;cw*9Ye$CzWIW3Z z4w}SQhxTK)KR@N3vYY7`Qt4tb1b>=(ASRiN?SC9(ZZU)HTHGY^RZ<;3Fc7_~%-AN` z{#yU>7s^D5Zo>T1I};|nYbsj4)w<%|KJDd_r02;|Lc{nrG+w}O^)1)#;M^t4NJFK}T$3pwW?lHH-Qwajgzw2ono0 zD;kNK+_k6RLBc-W!PYjz-9u0k`t@+zFIPbT4n!B?={TkZ> z=RKg|N{Fv>4o-ejs7x1Cgd>Hfji&=8ZbKTmjTu*`bi2j_$rbC9A(8lmF!Ni@IB>4+ ziM-RhIbIL$ufZ#Vqp59S>!qw>$ba_ri1sRmzDHJx7}QE9+<1{Uk{kM28SDBKWbDqA zTXq?JruO-zvHT`$lLnYf@}(XS8|&VW^!~2ttRk`IeBMjaVzkG%o?B#NYQ6pYF7b}{ z<`{;g@zDC{nWY&$2wx!(oLLst=%~UUQw=YR#i^U5zl^6KmYUSfpCaPaj02{Y&Vp3% zglzf31R)3&<9mY;$VFI(mtK6;fD$S$=k!L*r#S{fCIaD(*c3jKs9$>pTb)-UBAtBj zHh_8tYzkuWZ40FSope_!7(g20Gzd+Y_+>OvK!Q1;8!a6mY6qM$A%6kHxonC$5G2hL z`dy9CPi*!MbMxFSSmZ{B)BMNJ;4f}RR6#+Mg^eD+HA!Sxd};scDd@ zk{oo8@lz(KGs9+y$&RhRPTQIy;9^P-S0!6ItO2ayX4+eYWaPxFKqCVU*9 z1tZyOS1NZ1D~W#DP#iZ*327HomNHoj?ACWfaxOZ56KlXnnFhHw>2a7@>6Jb*m{cvN zaW@*Z_P@%OisME;(qc4yB7OUUOg@#YNx&?dJSBnlEcLENd16XCHX%k7ML{OeMStb_ z1IHu+5=z4``v6|MTuF-k49HZbI-3zD+@xud;z5`i4V_FcP@Cs_S>DEJ#!mN&xJ$%O z5%MK))%W{3?arlPFq%p7B7tw@&JJM}e>&5gRG3fu!AilA=dJD+HKuY+zxOOd8-pV& z+uyp9-ri0b*zklyD8oa-Er2A%QId!fTDC)cwMe5QnQGDP4~PJEIE0qh=0k7hw<$`Zpz_48 zgY)h!V(Wcx9_~%t`KMlWc)nU`3r?@axaY;d(@b8;e}*dn>#REraea7dv_7^!=7}fE zGfsZzfRxQtxuEjo0`K_hTY4jVfe^O|r>XPXeG|_L`ZclZM_~>Snws~%Pbr_f&bDk! zSH>?tO-Atc80#w|19m1`$QXp<5+W+sMV8reXm83SmVZRVZYqeRc)^Hucl%$!;kqky>0(cswbhFBILtPS*c+yP|K4k_AEF&4K|G!_}4o z&O?`^lUfc+mOnVy;?t#3uo zfp&YjYGIX8zDGi&)6*pLS`vgtI}82!k<5vYjtM@V?8)mysr7mjs3_Dn+A$ii>S~D| zN}TNN+X=IUu4bIsU0|_y094JK}L zicFu6kg|%Kg%fAc;^_PP77bIIgyQ^BV}9>p@mD2Oa?z`!O9XDDFoke-?` zD~pNEbxxNHN&Ni$YNc%`H;7p>=4;YRuvT4TC5;5vubMq8Q??48%Q8KR!a9ieVT376Y2`#qL=YhXIgZqSfv&QM|zxKro!>%$40cB zxntd)+~Cr5O}URGp05qF%+|<}m3l)IHx9+$yi&``SN$_+<2W@4?@`=zEtk?3oC<$k&*GvXFj9cTcV9K~Yh{crgGpNu-FU;1-9{b07*ZDN3yU zDeO~7kU62HjeBhOS`ACe9ozHN#V>Z<-ie!0jZ;@A&HkKbpwcDP)A%LDL2eRz8c|zP zGl>^?CuYJe0x^XO{F}jo?h!=P0e6}35`XY~^+LMu{QT{zgH@LqXGEPBP^vWfB39cn zQws>h3-w%tvHazOzkHEBCjE40nXf_1LX>gO-xd@M?yk=y$%4=hlDplT`I>D23>W-E zN4~N43wqHatzTpzBK>(Zz>h2bRA_1zwfrcTFM_#xFfHG^9e*5k#6V3X;JRou7_G*d zq|{QDr-1ea2@7gLN_8eU0CH#v7wn_(+m$ES(*5s>ZUzEZFe?+@EzUTy<#!5#nr`m^ zplIuFl_uFEvMeAZi}TZ<856$bgq%vTJI_Y=tWC-c@tM~mEK01cpK+)Irw|T$mpY#B z2u9t8^U|r-iWTB;JU2PE4XwV0=&Q`=_0{W|C8X^6C}V}%>U=O_kH^eu5CozL03cj) zD-QJ#6Xn!r)D=@a*t$5;(WN>xfajaLSiRiarQ6fWC-iX**Mu*4eYu@wza^vHOK=)7 zD&7m}$o&dX&2TUCBVs;zm~E?*f=k=9{=^0c*dnIHL%1ZK8DXMY3qp;P5)?xy&2?C= zxl<$($xUWu7ce+hTz?Of>Kxm2Mxf}i8<4k1V%t16c})@{1f;4~QxW;Bh6s&i(DR$z zisPT18(ORLDHOkB?h%wh+m&(V#l|^Zz+s$fgIF^HpFjBD~FhTnl>isW9Fwi3#CVJVy&;q9g@bLZxF!F7r11_*fYwAWFJRDD3 zo#e2)Gn3{NBVb{cUq^Lt%eR0gW7fS4By;Q<8D-n$3u{SR0={@(XN3C|qLjay&2vG& z#ii>87wXbf<7<}eNK?@1WFOS&2W%`okNQ9KAWkg#kJ*_7r#oyi9QUAV2ER`xpqa2| zV+6_zvL$`d|Ey!@Y7=Jtq?^ujr4cXAcc&`PCK>y$1a9D;r?U=sD<7YW5mx2Mq+V`(Sm%jv!F~*kH*oNdBe=B}CYpwx3fyz-z zd-k`u`trzm=iK>mPyX|_tN8!EynutgQT(jYIQ8`nbqsV(jP!Mk4V8@zK>FGsSrs)k z6-8AweLX!*9W6CYke0EruBsYHTV2!8&>-p`#PL!_ER>BBb7*TBOU6)`Kirr&7a8cB zaeO>;LNDA`kV&Oh+vsMXQT4`t`QGMJJOPn$FKo3Usd8v6p^^PuOI|t9$%Ea%`?Mgz zbx6|rDft%u?i$JZc!FOSY-#o7$1^#m@Trr-x?W zYAjx3{?TU~MI+QjRi!oJLG3J(#MZ3WCA3`{kOGRv^-3dW?(DJm8? zfI?{b8dB1CK*Fv_C2tq^tJkN2LP2N-uP!)+clHiznSGLBK-8uzuh?Z!t)LDMtU7+YIg8{E zm#K`_+NWdhRG?xZRgb zWb$vVWPdQ4QfId4TrXT4gi`Li>dHN$SM$)JFUrbIRFSir84h;<=z9alAY%SFJ?BW*oB4uVLC=df zc+jkv8uVpaHx?eDSq7YKsxVnJ-gqi)UdyX?M{v{M+ugx@ej4@T;quvZCnseZYr?qP zZ+ddk<3z-AvnbA8tKr>uJJy6Ia&-M9!Y_q?B3w?FpA9cOk=+iXz9Lpz?GQC*)`sZXKnl<{|vte^cm=iJ{8Zm2M6Mpm? zXqX9C+@MMISVF$%_3?@0`|61G`)@mYjxbQn&-e^Kx{Uh?aM-stnmUsVdA$N*aYu= zg9;6-scw2s4QL-yz33fifVJ$!LR*AYVWNR{ygBcDNQjsp)$B?yJ!hcv+7 z_!ScV@s$y|6ng-N0TCx98m)3lhxfy{EPYhogYV;RoD7Hkb+pPsISat`T&%oP*hyfm zWWu9<-h10fBrn^)=X6qT{~mI*cNHvU&^rBbnjwfF2~s9!+%nrT%3XVg{~X_a&!#4( z^0Tiharb8P>pEqRY@I1~0_557#3utA5Soi2(3||Rr7>}^wYS5uZhY=rPvK$0SG--; z^p2{)YMrmq>?m(4i@O`&q4JpV!6j{4YW+d^qc3Y=-xxMoO9=RFbH854)z2p0_x|Ih z0I5;`tJs2~JY7@0t%68hRxUJ&eaYK2Y>9pn>DKD|H5oI&^JtjDtX~Y9o-vfu@F%gg zd$D)d#4^DFCeI{!*ct#BL_+%j`>R_65RYtNdq1~>otzvDFnQ{JY|5aP7fl+`P5#O0 zLoC72{3DYl=l-Z7#!-Sf1y76f?L8O=RljOw++fTe8p&zGpm+w}d!`Kb^P*%DKf0Ce zPF;wHA>S-ZFl_Ua)+Wd8FIg!3W(W^^2OE4m)NsYRRdP* zEkX(EXwVSTAaJx3h}{zmtWTSou>_g@>;z8i2B7Vu%cgNMSLDcUUb2meRV2diJA z1NE(o=5NNT>nA(&IP?B=TeyT&I-lL`hh*o3h2fctyy?0uNkFTPFZVaupWLj8Hei(z z`O5xw!8r7$v6ZN7caM08eUvl(B^d2*uEY=6auFhm3Pt76Kv!q(<33g0S+Hd_MY@E;RuM+ zZL`wu5DL#}Jop6kJPd(ct5It#g z&(0VTFaTG72eSvSBF&cj%yEXCVTL@kZ5o^>Ue7LayHT*pKsIA_OPci5OlMSmfUsZd zJ=~~Bj5^4RB&*vEYNEo{I!iv)K)HWG%YseAqlQxVNK4pQ% zx4OvSwn~UF3c|qC7(H<9-9$jI9pwA6Q>Ukl8KID1R3e#5u>Tvunmz2R!cvS;o-~MM zMDW%hdgW%U&>&NUa826VTc_xu>>1@>=ZUHC0EPh>*W5%F8>X@M`}5qL8C*cdR>@-JuHzqVWH}I95b;5%n;Bfp}ev{ zU$15P&1MK_qQ0eelcv?B!!T}~sMFTN|4Z?W;J45T`^T?_PEc}Zs@Htn994VA%ryKw_C}qh&mg)gv4wOzAllb0r(G-xw1JSw zvyCao_6s+gDDIlOu1=m4cbEx!XwyUwLsrLX?Zg1_dTE4!p;iV{ z+XeWh#d=%aw|2?&q-mcFsf*9OkuJCGppTx%m8EKg{_-2vb~3bGubim!52oXlu+UaVqB@4mj!E8Ew;ug8nX#s z|EkX9Sqo#OW=d=4w7#gYML}01n{&0918qFNsXu4!(dTzpK4JW6LhMR@e{q^@%TneIiu+ zc-7v0eMd3#!9`lcm#V%N9At*DNq~C|X_ZIt0X!C#nenIV%G*9Mir zQSmi8>-{m`^ZpKIY`pqIg+r7c*2N-`eJLP!$(Grl9+1Dh@nLi@de7kwM}U;*AN{&T zejEebYMbHmDM>Sa79K9(iGDTF0jbi5Yc|)gSv3O_76X$(zFO%)m@r8uaI4~jpTu(W zRoEmh;mVlI6&po5={CPcCD%!F;EJC)_e62VB-i63!&_j-nU`x3oF~$gH6 zd|2M&I<2MGkR@N&{PS3nRFC%vdrY%D>gT75et0O5gA2E%Kiov%OS4K4!>1H>p&TtuD%;T%xOGX|-vBC_ShshkL+~no zmrT~F2&mYDbymOa;An~Ub3X$lZPKK_z*oVg0;0)-;7AKxKiKYG`u4SVm*++=l9l%WK$G31RUT~6gx=*m%f6mUnzI^KzWG8X?x(hkM>?}dE^!zX>GHFN$ z@GA1Y0TWM?X7MFKMs{=QN{vu$TXiA%S;#ntG2dBF2AHK@GQ>@1q7-35(I&0P>m-5&UtnDy%?b`RYGO64lOcL9ele5%2 z-Nvic0F(XNFRoJ?f*_={%?c@T_16#rQf^ZOCv7Flq5Q&VXY(1O#!ceSjbbkwaKqeQ zLho6V_d|biY)Jb}XJuz(kt&kDv9xX)-<-+H@OR0o8VBM<)T_QVFoh;Fgfa4mTgKV4 zAqHjSVKoIaD_(BypZmiW6X-MdDPaDRI%m1mfZU+)ll{tXr&ujg+3LQc9m^L-{3cGc zUo3^RbH3G|{e-DlR4i(+jGKp<=SPNr=v7=tSn|HW zes4fVQ%vTO-847(?r2#h ztn{0nSC}vgfTjLHUY9ATGts8%5VCmQ`TM7UhagvS;Ap=u#uV_W`*0w34_i_!AX;UwRrkno!M=d0=G&iT zL`{|cnIBQh;V`%CXcO$6Jv-Qu|9u_N^WWDI@PGfsqWqmUr>>qMNLy1&6J)5Tr=zb0 z($?06K0vzK209wrn%a6G9W8A`U2SzuO4aiPpVIeu6%7FcS5jE|z%!t3p-5tKrr^6@T~WSAlPw)_ z8v|TfGXMaK1BII-IvV~;r_0(VxZfAeKk`$Zrt$OBpS`xX^e49-tVyypRT^N`rHIt} z{OC$-<^8RRvVN4o)JT)5*djkvZ0#)sop_@%RWot)4q){kh#G|$6ktGrd zc+IX3ZtEAsT|P64i7p&j1U-Zw3)<~kAoJa^@wR289KjxIYq43_@DpRoIOsQ#Pqnt= zmAdgi_L=$;_*8mi!5xi-+;_r>|s8{t}zi%;kR61R(h?K{~>1@W{!6AfL~8z0tLl{@rQQ8AmTI zzMyf^tH=fqPGpvp&Y8ed+%s;QH9>p+)*d{WE=9lbi%nGk9UonGc<5?cMn0I<95kPB z-%BO;{sb;+6Aqi={i{5O3?Q`j?gK(Vp7T4B?;Ubd&phuJZ+`h2upbw_jXav|#qibk z@N|yPC0DV0T$byh`hm=N&9FL@W~EMgy{#ex89%QQkfhk>bwZ4Sz%kmui8qEby#1mJ z3)}jEnL8kxL%^v>g#YmQAb@*$W)25tu_sfd@JEB_kV%&;zNQ8Tst6Efmpn&=)`A16Js{s8~~3CRF=}gFTAJpM93;V#XPW z4x-H0t0zSKA{B{WFq4SV$nBh%u46&EK?N2;Z=$m^Yhv>^z-r16|6qG#*V9^QPQQcO zW2_8%;G3$N=?--CO(9KDhkrtnHj3K|yFy5Qz`sSQl8k)9@14 z+Dw!ob*>~1f!qtf(DNqdmD(?Q(mjA-~iwLFd!^kT;Box?0df0-KnSj z5P2?Zs<;2DafqJxQD$$c4i1Nm>)Lcy+Ag(|!KTNl$PV|-W`_#n&^S8vy1|*q>X!s` zF+yb*ARul7pgoSbPSGX9$?oBeN5`?W!hGNruJ&y39CL){ zi#FRQ$7>EVf0r`R^OoD*8OSwGHeHiexX5MD0V_N4|+F zTS9*_vkr2F&Q_j16|Y^yq=(byVAkUvLTlgrgyw%>vtzT`ETD|XmtOSRUVM0x6 zQ-mG};dO>jsavFycwqVT<9OQERoYag_{!w2pVJ1Rc;$B$=_eQ8hVPaY7>@N~`ok%U zYD~5Ro>SX|4uk~RqB4q{`wta)4qd!I(8m?Y;DR@c<-Q|6309E-c$uh?9hUohlkAk1 zZ^rGT-IVIDF=t5uvUUdNf;a?Ui|UW$gcLLCk_aB3E5<=mg+X(mTgai!A#m}_#& zq>o%xm82{4npuhFI2cqW5<;Vu1dT1+&9c;o!P+E?QP8hyM1NbWAS(f;6gPCXXA$g9 zWqH&j$oZ1Kl$K@y7ntoM;aJ=X(x0;XmM+dl5Ysodb1v_mgb)*Vu9YXBZ?vdO2$-jv zaN(N(8quxHc6b2da7U`_>7OOy(OdZ^Z?D!AM}gIktO1G@c&^B8I$spt3?38W$rI~m ztVxTSjuXRLrZ!hiKY&%VQrSWpUCwOv>V!f}<(utT?@{AS=Vitb?_5@OVd`XA;Un%; z`$d9fZRATtmWkMxrGK;#&`aQnlA2T4Xykw60_bDWZ=!SGk9%k>b%M98AhTBP)c&|j zYLk}%Q3{m{N5bfI=bAh2x@w6qKAXjK!CS{$yvrOgbxk~}Ne}$pHw=;4g%{w`{c&&m zeMQ3wyk?X)Xa0pApDM60teLMLa+7Rbz{mB8>0|0^%Eh9N$D5oVB#Jh`gE+QK?hm2u z`19c#1Bl~@QNohwTEoetD84|ot{<<)EcyPRSM0vA$26vJB8KKla%P7d+7x=T!~Ls7}c?L$^M2w#p=X<_KW~7YXu=A(ESa3oT`!rAoD_nIW$v z%dgr1?oOUvZf=?WfGsu~Sts^r2T_IPDJg>wiJGj+uV&x&cKI*7wIoU~R;!i1a5h;b zF?h!-a1|$ZXP>_eA7wg^Z~dXMpI`>9PfBWVx(<`;D>&J{ppQTzU-&oVRghMq9Lt~F ze+=(D!%%LWexYHT*b`F)#1u_V<;qZWG4c|9LF1t|M$}-ErH;8q8W^Y#-ThedZ^t<> zNTsDdZsrraapsrnN3DnNORiQ0UJ}w^KD8hFuNOT_=aF?wU%6#;Kjcsp%fz0pX8e?U zuwsH7CFh?qL#6e|HWZ`DK91I2CL6&4{i@Q}`mF1;C!**|uzjeQfoYy`@-<<0xNz@_tDjd; z$1W-iux-?~)uOW;wy&3~s&-KEcJzZ{Qs{4<2QV3iZgUPTYA|cs@}_2LyOVD>WQSSf zkERWPa|Jlrc>+%nr{=MPtWD^_n-a0)r+&yz0LmNuc`xJ-7aGj9*A<Y=z&-#Y2@##DP9|_JM zGEmFogf)Q_TB_rZTLD%UYuO&p456#68fI>S&@P1w0}9>*(gkCHst@Q&AS;YFx=tMY zj#n#~#3PR*&W=-9_*MQ_q#~S5eHX%CIiGu!0$9*}iaKS3C_jF28H3ehxuOZfy1g?0 z@sKT2V49fobDDo5-P#^jyD+z}vr%0BK?=>`vh*S`NbryI6%xS-PugnIZCgtAVAKz5yaf zfQ-g<7@_(7pUA0#_q8|IOGQkw5Y8)12&rNeCum=1qv3%?<_hT5Iuyj&=HG#y&{lqc z>a$5H=U*&8#s_D?AfB>G-N~xkN6h{lb)9tkVh!ohu z(q|{>bL04Od)>IAYox;E!KadHOoVH(SS-D%LsDX20OwPL0E(vSjfJg`E1d_5q@80L ziL6+PiQ5eBKL5Nj!pCZ^Y7z=Y!yS*_Fb^I1?e4gGmh=sjSdcI-97!V^uI;~;gkGc` ztKj-v<$15H3gchcnU>(#2pcM?VgFqq`9n((vv}nTI3YxLe%9!f93)YbMdBDttwyuq z*mKk5X%o&t`+4SD{hyi!u4ucmi%*};w8s^RfWX-V0!;T+56u{N41cIOtv)2Fbs?a| z;S5XJXwSucnYh(H8rpG~HS|iw0fZKyd;QUsYoDAd5Btb=NWeIlOhug~ROnSl{!PNd zJJc+l+oo{v%eR`+f<-|^qG=RtpTbV<>*5Ve=P<;$QjAhYr+H2C{N=UY%|++iTGjy9 zY$$SfuV{5q54uuB>4^AoM*nuF>2QetV?$HKzWS9ycYw~5ND#XZBYjH5g2-qD zpCpWb(JKC`#0&o_PTblY4>gIeb~3Bnc>r6c2B<1}b?kenlq5W@1rO1TG|R`|6(?0m z`@RlZuUzhqr`8Qb{;Eh$WB7tqS=~176^C^z(M2g<%)etOi4Xu#by3MbQ`>W;n6)rL zSi=*lULQu9e7nAn{*trq%YKbOey?#Ftt+-f)8 zi~1B+j(<4wOu;U9Ee2&od`g^2u1oyF{pTdV@vba(MArV~#SiQPH+jm6n*TfycZ2B4) zp0~lm-3R`XiVaQhPiEu*e^b#4q@uTo5^!>T770$Ze#b|B$ZH{%LB9ng%|_1Q9I0jH ztlsaoUu%A5L&DBwyyo_;`bpfeRUPLceHk`u)g3!=AqF{3%(NlsDj? z3krB&R+#^~pg@4W*WL$vc<35vXq)QlYJ;@3bhNeg^z}4#wDmxGx(2$sn!1{Le}Ckm zudQS7fAO1;|6TDZ6r`p#W1EwkUP`X5eYZ5?x+@S7b!{QS)Urv>-$dEX#YvGC%~+u# zBb}TeI8J(g7*2h#pzj((6niSMB%KIvTTuIX)<#y6@8u?Dy>i#VBA?JO2;Qdw$A~w#4V6-v3eU839XgM!p{;jH-Q=d`9oQ?)p1gDqu<95fF>OEl-GhO3C(tg|~ zxh%JC2ke>v%@ms{!Kvz~kQezxQA3y=GntCcKrUMtpCX&uuluKd3TEnoH(vD~>yOsr zJVhJ1AMck3ER-1<@k2!(5+Mw|$?t}D;f*C*?PA)Vr8+HK@!uil?nDpi>OBD&YH2aY zyRT%bvW|+%1ECgxPn;4Imc8m0PNbd9Pjk$5KYE~_Sh-|rY_P%e4?zz0I2BeH$>tBD ze}n1wB5}Li9b>I?ub|xZIohXf+`TC9g5a$nzOrJx(q`XJ-{;7DG!c(NK}y=0@AO@I zL_y$L)a@SM#6-Qq!RueoIzQ$Eg$nb#Q{~Y$VvfQ_DMd9Hs8_faZIs!bh$aP}#)Zpsk0Ukv*HJGPElvTXQDK)Ir#(6%r z*N}3p?w2}DrsJZ|CxGJx`!OXD_|H~>tL$~_>v439noAIACQ9TxfMrJexljQ&KE=*q z>&AtHoKuSUo@K++Bbz7Gww*9Rlp)v)+%pmy8T-;<95+PchhFQ8Os|Is(>|No{9Fw( zb+c9z3-Wn5qmMe11mLRVM6uHpCRrU&gi368y zwKyh9&0H);eMN73@^gG*TD65$fMmJOQRi?&!;w&-bg4)_8IM`-j*Gu#Ol(N~9P-g^ z5!clZVZ$lr*6e2>u06MS^9=<);u*z)5!3b8brf*E4|}QqXTA0O;L$qQLApEkW|9PM zOa4zb7aM*o7>^_wA!L)@1E>1{PdU|ZB{V6CDg-Er|3 zb@%R_8X}~0VIB(nH=lbY&{Iw7&&|{$0}hM=CnrMo+{;W7$==Lqq;#uG?XU5O}Vs^-48 zeL_=Z&X4Es7qZhwL#JFxOsCl?$bt1JqRT{@%t6F512fpP#&mXYH^viR<%HQ#!v=H& z&`A9bKOhv^*%cc3!Hv&zO*uXAL2zqU=A6a2x@uU@ln=@oG}a{wZO5VDCx3H>OCqoj^}5312|rrLN_Ck-5hMlTh_GA^J$nI)A)htCDoM#epUK`C8~UOmBM*7qVjHcVAB4@&JPZc)o0R^pKLaVyd2LWArTD?&hLI;-V3A7W@xGNmL<`G3o{#AX zhnllm<_Q#HO<}{`A^?-`YP^u8{e#o#G$5nXUy+^DIN?oUMZ>lW!P%c!r}jL2Vn507 zW@n#|&kB5IY`NAQ!==A$CX@#?BgK6-nJe~);PxR_j>Jn;m>WjD=Y-YMk-xtPob7;) ze$q~NyLUSjUF6D2dLd$NTxOiYMN*t=rytEKwZt_Xi~-pOCi(ng4<3;s@gQSPL4S~L zZ5d^9h_}e>!V76aFZAzll(JRKH6FLOxT<|ILxK3EUCGK#sDY=Z>0IW8n?1dIt0wc@ z-p9EdHiC>fxNo_|$hntQ7wpOWQr(M;Ro@6vxfI$+@r6H>ndPhF(Fz=j^7YC`1nk*b zi7~-8I@E-=a~r=y5y)GF zpQtxerIqBX#Ps~zZ*UE)9E|4N(j%0@@<-?ijBvU6<+7%{U8&f>2tEZO6&CRnK&=c$ zUVpWbj$o5F-Zy)%g-Wu;pK{t<_8lmKl>+4rcO-cMkQ>3G?{!z1(~9n*KCsGaOh)i3X?6aG z-}gK{k?381BZCWc!@$Kr9cEDny5_ zrB)wB>-<~_Thq`31>KlQpl+a_vx1rYCaL?tLJ4l^c`IxM8(iF3v6ZR&(1EstA2=6A z;TY_nKP5OlAz5T4j9{l%{=lgfHi9D`EK)~YBuc+(mzRa)Q}B(JSn@(4;j;$KKyrCHda+tHvCf8z<}x3v~7nw#F}$kvvx~Acv{P z(mn>(s0B>?(ORWg|4e=QBUXXm#)HgRq?x;jR3a3p@hP#?ZSif#e4&!P8*VtA<&D#Y zv(=>e?GLq&*02HW?gsJ;v%7n9!-P@I+jLPvF73rkcx1ufIv&#^HRZc*!9eAy$ib6{+{1r?*B(8jz&qRhH;nhHy0mQ(tCgp=^zo?GN4Pg zp*5fE$WX^>=$Io6-6hD!({d&OfW`;khEnHyRL3kuf^J5E6QRL946P^{$AyEsH z9IiHuf?`~$3m%m``!{y|&3|@WJg3Ibi@AJAMDW);=`Jnlt<`WReIIL0*X`(C7YSz|Ewm?D@idR_c2;LL zNMN(Ba=ItP0$hl#wOhh&t6AbeS8iNSuT4MbU)lok6MkE8hEb1#Sr%sJqZhq>2|N?1 z=BSx|r+t!Nj@-oHd1Rh1bDvc}@4&&hn>kuT*e%wvEt(DD^0rt)_8}!AcS8^cNN8wj z=m(qRTQaXDE$wzU+lx9JGmtC2v-|FyFriu*CpXCRCxg~j&rhx*A-Be4Uyk^B0hhS@ z{dSQeH@JE3^2X-&Hy`{*gE^3-$C)!QlUA~_Se&pxW_fU7461bVIfGpSw3UL#wz1&q-&~bAQ z&i=u^x??NXx*4xPu{4n8Ea|X z?qh$Z{S=Gr8<&NyVfFc9{VIph zNM{=J#Y)BYQJ=$M%KA5NVFF-|0a&K{ZDgl#L1sbtZ${sACv(b)TRveexextj>1iS$ zifNmQyh4q(Q4f+Yq)IBzf42MEnIuL z|1|LUJ)mUz?PmpJNI!0+@SB2(lni>t&Mi}=IlTXipLPH5%KryYO928G0~7!N00;of zteQKiI=P}1^@s600962073u&0GCDp00027KSfyp diff --git a/libs/mangle/tests/test.sh b/libs/mangle/tests/test.sh deleted file mode 100755 index 2d07708adc..0000000000 --- a/libs/mangle/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/tools/shared_ptr.hpp b/libs/mangle/tools/shared_ptr.hpp deleted file mode 100644 index 3d073fc24f..0000000000 --- a/libs/mangle/tools/shared_ptr.hpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file should include whatever it needs to define the boost/tr1 -// shared_ptr<> and weak_ptr<> templates. -#include diff --git a/libs/openengine/gui/events.cpp b/libs/openengine/gui/events.cpp deleted file mode 100644 index 35b01158bc..0000000000 --- a/libs/openengine/gui/events.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include - -#include "events.hpp" - -using namespace OIS; -using namespace OEngine::GUI; - -EventInjector::EventInjector(MyGUI::Gui *g) - : gui(g), enabled(true) - , mMouseX(0) - , mMouseY(0) -{ - assert(gui); - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - mMouseX = viewSize.width/2; - mMouseY = viewSize.height/2; -} - -void EventInjector::event(Type type, int index, const void *p) -{ - if(!enabled) return; - - if(type & EV_Keyboard) - { - KeyEvent *key = (KeyEvent*)p; - MyGUI::KeyCode code = MyGUI::KeyCode::Enum(key->key); - if(type == EV_KeyDown) - { - /* - This is just a first approximation. Apparently, OIS is - unable to provide reliable unicode characters on all - platforms. At least that's what I surmise from the amount - of workaround that the MyGUI folks have put in place for - this. See Common/Input/OIS/InputManager.cpp in the MyGUI - sources for details. - - If the work they have done there is indeed necessary (I - haven't tested that it is, although I have had dubious - experinces with OIS events in the past), then we should - probably adapt all that code here. Or even better, - directly into the OIS input manager in Mangle. - - Note that all this only affects the 'text' field, and - should thus only affect typed text in input boxes (which - is still pretty significant.) - */ - MyGUI::Char text = (MyGUI::Char)key->text; - MyGUI::InputManager::getInstance().injectKeyPress(code,text); - } - else - { - MyGUI::InputManager::getInstance().injectKeyRelease(code); - } - } - else if(type & EV_Mouse) - { - MouseEvent *mouse = (MouseEvent*)p; - MyGUI::MouseButton id = MyGUI::MouseButton::Enum(index); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // Update mouse position - mMouseX += mouse->state.X.rel; - mMouseY += mouse->state.Y.rel; - mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); - mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); - - if(type == EV_MouseDown) - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, id); - else if(type == EV_MouseUp) - MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, id); - else - MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mouse->state.Z.abs); - } -} diff --git a/libs/openengine/gui/events.hpp b/libs/openengine/gui/events.hpp deleted file mode 100644 index 10c5309bc3..0000000000 --- a/libs/openengine/gui/events.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef OENGINE_MYGUI_EVENTS_H -#define OENGINE_MYGUI_EVENTS_H - -#include - -namespace MyGUI -{ - class Gui; -} - -namespace OEngine { -namespace GUI -{ - /** Event handler that injects OIS events into MyGUI - */ - class EventInjector : public Mangle::Input::Event - { - MyGUI::Gui *gui; - - int mMouseX; - int mMouseY; - - public: - bool enabled; - - EventInjector(MyGUI::Gui *g); - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr EventInjectorPtr; -}} -#endif diff --git a/libs/openengine/ogre/mouselook.cpp b/libs/openengine/ogre/mouselook.cpp deleted file mode 100644 index 841bab603a..0000000000 --- a/libs/openengine/ogre/mouselook.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "mouselook.hpp" - -#include -#include -#include - -using namespace OIS; -using namespace Ogre; -using namespace OEngine::Render; - -void MouseLookEvent::event(Type type, int index, const void *p) -{ - if(type != EV_MouseMove || camera == NULL) return; - - MouseEvent *arg = (MouseEvent*)(p); - - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; - - camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x)); - camera->getParentSceneNode()->pitch(Degree(-y)); - if(flipProt) - { - // The camera before pitching - /*Quaternion nopitch = camera->getParentSceneNode()->getOrientation(); - - camera->getParentSceneNode()->pitch(Degree(-y)); - - // Apply some failsafe measures against the camera flipping - // upside down. Is the camera close to pointing straight up or - // down? - if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1) - // If so, undo the last pitch - camera->getParentSceneNode()->setOrientation(nopitch);*/ - //camera->getU - - // Angle of rotation around the X-axis. - float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees()); - - // Just to determine the sign of the angle we pick up above, the - // value itself does not interest us. - float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x; - - // Limit the pitch between -90 degress and +90 degrees, Quake3-style. - if (pitchAngle > 90.0f) - { - if (pitchAngleSign > 0) - // Set orientation to 90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - Ogre::Math::Sqrt(0.5f), 0, 0)); - else if (pitchAngleSign < 0) - // Sets orientation to -90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - -Ogre::Math::Sqrt(0.5f), 0, 0)); - } - } -} diff --git a/libs/openengine/ogre/mouselook.hpp b/libs/openengine/ogre/mouselook.hpp deleted file mode 100644 index 6e09ff4a10..0000000000 --- a/libs/openengine/ogre/mouselook.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef OENGINE_OGRE_MOUSELOOK_H -#define OENGINE_OGRE_MOUSELOOK_H - -/* - A mouse-look class for Ogre. Accepts input events from Mangle::Input - and translates them. - - You can adjust the mouse sensibility and switch to a different - camera. The mouselook class also has an optional wrap protection - that keeps the camera from flipping upside down. - - You can disable the mouse looker at any time by calling - setCamera(NULL), and reenable it by setting the camera back. - - NOTE: The current implementation will ONLY work for native OIS - events. - */ - -#include - -namespace Ogre -{ - class Camera; -} - -namespace OEngine { -namespace Render -{ - class MouseLookEvent : public Mangle::Input::Event - { - Ogre::Camera* camera; - float sensX, sensY; // Mouse sensibility - bool flipProt; // Flip protection - - public: - MouseLookEvent(Ogre::Camera *cam=NULL, - float sX=0.2, float sY=0.2, - bool prot=true) - : camera(cam) - , sensX(sX) - , sensY(sY) - , flipProt(prot) - {} - - void setCamera(Ogre::Camera *cam) - { camera = cam; } - void setSens(float sX, float sY) - { sensX = sX; sensY = sY; } - void setProt(bool p) { flipProt = p; } - - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr MouseLookEventPtr; -}} -#endif From 97c45455fd762196a583b6da361caf313b314124 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 21:21:23 +0200 Subject: [PATCH 059/143] fix WindowManager destruction --- apps/openmw/mwbase/environment.cpp | 3 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- libs/openengine/gui/manager.cpp | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 8d786db910..9aaa5af85a 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -146,8 +146,7 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; -/// \todo Re-enable (currently throwing an exception) -// delete mWindowManager; + delete mWindowManager; mWindowManager = 0; delete mWorld; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2a6f7ec9b3..183c435fdf 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -159,7 +159,6 @@ WindowManager::WindowManager( WindowManager::~WindowManager() { - delete mGuiManager; delete mConsole; delete mMessageBoxManager; delete mHud; @@ -182,6 +181,8 @@ WindowManager::~WindowManager() delete mSpellWindow; cleanupGarbage(); + + delete mGuiManager; } void WindowManager::cleanupGarbage() diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 9c6ca37eb1..58929ba8b5 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -36,6 +36,7 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool void MyGUIManager::shutdown() { + mGui->shutdown (); delete mGui; if(mPlatform) { From 90f1d9c2f2989011878c83852a0779e85f0fdcca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 21:25:13 +0200 Subject: [PATCH 060/143] OSX suggestion by corristo --- apps/openmw/mwinput/inputmanagerimp.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d1c400ceab..e1eacae055 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,5 +1,9 @@ #include "inputmanagerimp.hpp" +#if defined(__APPLE__) && !defined(__LP64__) +#include +#endif + #include #include @@ -76,12 +80,12 @@ namespace MWInput #endif } - #ifdef __APPLE_CC__ +#if defined(__APPLE__) && !defined(__LP64__) // Give the application window focus to receive input events ProcessSerialNumber psn = { 0, kCurrentProcess }; TransformProcessType(&psn, kProcessTransformToForegroundApplication); SetFrontProcess(&psn); - #endif +#endif mInputManager = OIS::InputManager::createInputSystem( pl ); From 976ad7a301bff286a65a119cc7d7532e61daea25 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 22:59:58 +0200 Subject: [PATCH 061/143] key defaults specified in the code now, required in order to keep the configuration files valid across multiple versions of openmw --- CMakeLists.txt | 3 - apps/openmw/engine.cpp | 20 +---- apps/openmw/mwinput/inputmanagerimp.cpp | 67 ++++++++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +- files/input-default.xml | 100 ------------------------ 5 files changed, 58 insertions(+), 137 deletions(-) delete mode 100644 files/input-default.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 79e33f1815..68fdee1e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,9 +285,6 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") -configure_file(${OpenMW_SOURCE_DIR}/files/input-default.xml - "${OpenMW_BINARY_DIR}/input-default.xml") - configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg "${OpenMW_BINARY_DIR}/transparency-overrides.cfg") diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 128d975534..edd0dead11 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -271,22 +271,10 @@ void OMW::Engine::go() settings.loadUser(globaldefault); // Get the path for the keybinder xml file - std::string keybinderDefault; - - // load user settings if they exist, otherwise just load the default settings as user settings - const std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - const std::string keybinderDefaultLocal = (mCfgMgr.getLocalPath() / "input-default.xml").string(); - const std::string keybinderDefaultGlobal = (mCfgMgr.getGlobalPath() / "input-default.xml").string(); - - bool keybinderUserExists = boost::filesystem::exists(keybinderUser); - - if (boost::filesystem::exists(keybinderDefaultLocal)) - keybinderDefault = keybinderDefaultLocal; - else if (boost::filesystem::exists(keybinderDefaultGlobal)) - keybinderDefault = keybinderDefaultGlobal; - else - throw std::runtime_error ("No default input settings found! Make sure the file \"input-default.xml\" was properly installed."); + std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + if (!boost::filesystem::exists(keybinderUser)) + keybinderUser = ""; mFpsLevel = settings.getInt("fps", "HUD"); @@ -386,7 +374,7 @@ void OMW::Engine::go() mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderDefault, keybinderUser, keybinderUserExists)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index e1eacae055..0318995389 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -7,8 +7,7 @@ #include #include -#include -#include +#include #include @@ -32,8 +31,7 @@ namespace MWInput MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, - const std::string& defaultFile, - const std::string& userFile, bool userFileExists) + const std::string& userFile) : mOgre(ogre) , mPlayer(player) , mWindows(windows) @@ -43,6 +41,7 @@ namespace MWInput , mMouseY(ogre.getWindow()->getHeight ()/2.f) , mUserFile(userFile) , mDragDrop(false) + , mGuiCursorEnabled(false) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -102,15 +101,9 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - std::string configFile; - if (userFileExists) - configFile = userFile; - else - configFile = defaultFile; + mInputCtrl = new ICS::InputControlSystem(userFile, true, NULL, NULL, A_LAST); - std::cout << "Loading input configuration: " << configFile << std::endl; - - mInputCtrl = new ICS::InputControlSystem(configFile, true, NULL, NULL, A_LAST); + loadKeyDefaults(); for (int i = 0; i < A_LAST; ++i) { @@ -339,8 +332,7 @@ namespace MWInput { mInputCtrl->keyReleased (arg); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); return true; } @@ -359,8 +351,7 @@ namespace MWInput { mInputCtrl->mouseReleased (arg, id); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); return true; } @@ -449,11 +440,15 @@ namespace MWInput { bool gameMode = !mWindows.isGuiMode(); + std::cout << "gameMode: " << gameMode << std::endl; + // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); else if(mWindows.getMode() == MWGui::GM_Inventory) mWindows.popGuiMode(); + else + std::cout << "toggleInv didnt do anything!!!" << std::endl; // .. but don't touch any other mode. } @@ -516,4 +511,44 @@ namespace MWInput return mInputCtrl->getChannel (id)->getValue () == 1; } + void InputManager::loadKeyDefaults () + { + // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid + // across different versions of OpenMW (in the case where another input action is added) + std::map defaultKeyBindings; + + defaultKeyBindings[A_Activate] = OIS::KC_SPACE; + defaultKeyBindings[A_MoveBackward] = OIS::KC_S; + defaultKeyBindings[A_MoveForward] = OIS::KC_W; + defaultKeyBindings[A_MoveLeft] = OIS::KC_A; + defaultKeyBindings[A_MoveRight] = OIS::KC_D; + defaultKeyBindings[A_ToggleWeapon] = OIS::KC_F; + defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; + defaultKeyBindings[A_Console] = OIS::KC_F1; + defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL; + defaultKeyBindings[A_AutoMove] = OIS::KC_Q; + defaultKeyBindings[A_Jump] = OIS::KC_E; + defaultKeyBindings[A_Journal] = OIS::KC_J; + defaultKeyBindings[A_Rest] = OIS::KC_T; + defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; + + std::map defaultMouseButtonBindings; + defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; + + for (int i = 0; i < A_LAST; ++i) + { + if (mInputCtrl->getChannel(i)->getControlsCount () == 0) + { + ICS::Control* control1 = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); + mInputCtrl->addControl(control1); + control1->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + + if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) + mInputCtrl->addKeyBinding(control1, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); + else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) + mInputCtrl->addMouseButtonBinding (control1, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + } + } + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index ba42327eeb..9fc77aeeb4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -61,8 +61,7 @@ namespace MWInput MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine, - const std::string& defaultFile, - const std::string& userFile, bool userFileExists); + const std::string& userFile); virtual ~InputManager(); @@ -130,6 +129,8 @@ namespace MWInput bool actionIsActive (int id); + void loadKeyDefaults(); + private: enum Actions { diff --git a/files/input-default.xml b/files/input-default.xml deleted file mode 100644 index e44b3455ac..0000000000 --- a/files/input-default.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From bc6e4feedc6d68958ea72a7689ca73f86b913db1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 01:26:15 +0200 Subject: [PATCH 062/143] hotkey window first version --- apps/openmw/engine.cpp | 6 +- apps/openmw/mwbase/inputmanager.hpp | 6 + apps/openmw/mwbase/windowmanager.hpp | 4 + apps/openmw/mwgui/hud.cpp | 3 + apps/openmw/mwgui/hud.hpp | 2 + apps/openmw/mwgui/settingswindow.cpp | 51 +++++++ apps/openmw/mwgui/settingswindow.hpp | 9 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 24 ++++ apps/openmw/mwgui/windowmanagerimp.hpp | 6 + apps/openmw/mwinput/inputmanagerimp.cpp | 158 ++++++++++++++++++++-- apps/openmw/mwinput/inputmanagerimp.hpp | 36 ++++- files/mygui/openmw_settings_window.layout | 7 + 12 files changed, 291 insertions(+), 21 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index edd0dead11..0bfd97c5dc 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -272,9 +272,7 @@ void OMW::Engine::go() // Get the path for the keybinder xml file std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - - if (!boost::filesystem::exists(keybinderUser)) - keybinderUser = ""; + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); mFpsLevel = settings.getInt("fps", "HUD"); @@ -374,7 +372,7 @@ void OMW::Engine::go() mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 5d73025a7d..bf1d8e45f6 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -31,6 +31,12 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + + virtual std::string getActionDescription (int action) = 0; + virtual std::string getActionBindingName (int action) = 0; + virtual std::vector getActionSorting () = 0; + virtual int getNumActions() = 0; + virtual void enableDetectingBindingMode (int action) = 0; }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 931353a900..fde965256c 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -177,6 +177,10 @@ namespace MWBase virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; + virtual void disallowMouse() = 0; + virtual void allowMouse() = 0; + virtual void notifyInputActionBound() = 0; + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index bdbb316b05..92dc4e495f 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -237,6 +237,9 @@ void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible void HUD::onWorldClicked(MyGUI::Widget* _sender) { + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) + return; + if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 485c788fc3..baa350a10c 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -48,6 +48,8 @@ namespace MWGui MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; + MyGUI::Widget* mDummy; + MyGUI::WidgetPtr fpsbox; MyGUI::TextBox* fpscounter; MyGUI::TextBox* trianglecounter; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f6597a64ef..477ffe0e24 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -115,6 +115,7 @@ namespace MWGui getWidget(mMiscShadows, "MiscShadows"); getWidget(mShadowsDebug, "ShadowsDebug"); getWidget(mUnderwaterButton, "UnderwaterButton"); + getWidget(mControlsBox, "ControlsBox"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -511,4 +512,54 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); } + + void SettingsWindow::updateControlsBox() + { + while (mControlsBox->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0)); + + std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); + + const int h = 18; + const int w = mControlsBox->getWidth() - 34; + int curH = 6; + for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) + { + std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); + if (desc == "") + continue; + + std::string binding = MWBase::Environment::get().getInputManager()->getActionBindingName (*it); + + MyGUI::TextBox* leftText = mControlsBox->createWidget("SandText", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + leftText->setCaptionWithReplacing(desc); + + MyGUI::Button* rightText = mControlsBox->createWidget("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + rightText->setCaptionWithReplacing(binding); + rightText->setTextAlign (MyGUI::Align::Right); + rightText->setUserData(*it); // save the action id for callbacks + rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); + curH += h; + } + + mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); + } + + void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) + { + int actionId = *_sender->getUserData(); + + static_cast(_sender)->setCaptionWithReplacing("#{sNone}"); + + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sControlsMenu3}", std::vector()); + MWBase::Environment::get().getWindowManager ()->disallowMouse(); + + MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId); + + } + + void SettingsWindow::open() + { + updateControlsBox (); + } } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index ca11b6f9c2..61614da014 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -15,6 +15,10 @@ namespace MWGui public: SettingsWindow(MWBase::WindowManager& parWindowManager); + virtual void open(); + + void updateControlsBox(); + private: static int const sFovMin = 30; static int const sFovMax = 140; @@ -60,6 +64,9 @@ namespace MWGui MyGUI::ScrollBar* mFootstepsVolumeSlider; MyGUI::ScrollBar* mMusicVolumeSlider; + // controls + MyGUI::ScrollView* mControlsBox; + void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); void onTextureFilteringToggled(MyGUI::Widget* _sender); @@ -72,6 +79,8 @@ namespace MWGui void onShadersToggled(MyGUI::Widget* _sender); void onShadowTextureSize(MyGUI::Widget* _sender); + void onRebindAction(MyGUI::Widget* _sender); + void apply(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 183c435fdf..676eb2046e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -134,6 +134,8 @@ WindowManager::WindowManager( mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + // The HUD is always on mHud->setVisible(true); @@ -230,11 +232,16 @@ void WindowManager::updateVisible() bool gameMode = !isGuiMode(); + mInputBlocker->setVisible (gameMode); + if (gameMode) mToolTips->enterGameMode(); else mToolTips->enterGuiMode(); + if (gameMode) + MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); @@ -646,6 +653,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mScrollWindow->center(); mBookWindow->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); } } @@ -823,3 +831,19 @@ WindowManager::SkillList WindowManager::getPlayerMajorSkills() { return mPlayerMajorSkills; } + +void WindowManager::disallowMouse() +{ + mInputBlocker->setVisible (true); +} + +void WindowManager::allowMouse() +{ + mInputBlocker->setVisible (!isGuiMode ()); +} + +void WindowManager::notifyInputActionBound () +{ + mSettingsWindow->updateControlsBox (); + allowMouse(); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index eaa6a16832..3913055942 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -157,6 +157,10 @@ namespace MWGui virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); + virtual void disallowMouse(); + virtual void allowMouse(); + virtual void notifyInputActionBound(); + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. virtual void messageBox (const std::string& message, const std::vector& buttons); @@ -208,6 +212,8 @@ namespace MWGui CharacterCreation* mCharGen; + MyGUI::Widget* mInputBlocker; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 0318995389..09ed50944c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include "../engine.hpp" @@ -31,7 +29,7 @@ namespace MWInput MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, - const std::string& userFile) + const std::string& userFile, bool userFileExists) : mOgre(ogre) , mPlayer(player) , mWindows(windows) @@ -101,11 +99,12 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - mInputCtrl = new ICS::InputControlSystem(userFile, true, NULL, NULL, A_LAST); + std::string file = userFileExists ? userFile : ""; + mInputCtrl = new ICS::InputControlSystem(file, true, this, NULL, A_Last); loadKeyDefaults(); - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { mInputCtrl->getChannel (i)->addListener (this); } @@ -322,8 +321,7 @@ namespace MWInput { mInputCtrl->keyPressed (arg); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); return true; } @@ -341,8 +339,7 @@ namespace MWInput { mInputCtrl->mousePressed (arg, id); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); return true; } @@ -440,15 +437,11 @@ namespace MWInput { bool gameMode = !mWindows.isGuiMode(); - std::cout << "gameMode: " << gameMode << std::endl; - // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); else if(mWindows.getMode() == MWGui::GM_Inventory) mWindows.popGuiMode(); - else - std::cout << "toggleInv didnt do anything!!!" << std::endl; // .. but don't touch any other mode. } @@ -535,7 +528,7 @@ namespace MWInput std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { if (mInputCtrl->getChannel(i)->getControlsCount () == 0) { @@ -551,4 +544,141 @@ namespace MWInput } } + std::string InputManager::getActionDescription (int action) + { + std::map descriptions; + + descriptions[A_Activate] = "sActivate"; + descriptions[A_MoveBackward] = "sBack"; + descriptions[A_MoveForward] = "sForward"; + descriptions[A_MoveLeft] = "sLeft"; + descriptions[A_MoveRight] = "sRight"; + descriptions[A_ToggleWeapon] = "sReady_Weapon"; + descriptions[A_ToggleSpell] = "sReady_Magic"; + descriptions[A_Console] = "sConsoleTitle"; + descriptions[A_Crouch] = "sCrouch_Sneak"; + descriptions[A_AutoMove] = "sAuto_Run"; + descriptions[A_Jump] = "sJump"; + descriptions[A_Journal] = "sJournal"; + descriptions[A_Rest] = "sRestKey"; + descriptions[A_Inventory] = "sInventory"; + + if (action == A_GameMenu) + return "Menu"; // not configurable in morrowind so no GMST + + if (descriptions[action] == "") + return ""; // not configurable + + return "#{" + descriptions[action] + "}"; + } + + std::string InputManager::getActionBindingName (int action) + { + if (mInputCtrl->getChannel (action)->getControlsCount () == 0) + return "#{sNone}"; + + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + if (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + return mInputCtrl->keyCodeToString (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE)); + else if (mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + return "#{sMouse} " + boost::lexical_cast(mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE)); + else + return "#{sNone}"; + } + + std::vector InputManager::getActionSorting() + { + std::vector ret; + ret.push_back(A_MoveForward); + ret.push_back(A_MoveBackward); + ret.push_back(A_MoveLeft); + ret.push_back(A_MoveRight); + ret.push_back(A_Crouch); + ret.push_back(A_Activate); + ret.push_back(A_ToggleWeapon); + ret.push_back(A_AutoMove); + ret.push_back(A_Jump); + ret.push_back(A_Inventory); + ret.push_back(A_Journal); + ret.push_back(A_Rest); + ret.push_back(A_Console); + ret.push_back(A_GameMenu); + + return ret; + } + + void InputManager::enableDetectingBindingMode (int action) + { + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + mInputCtrl->enableDetectingBindingState (c, ICS::Control::INCREASE); + } + + void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) + { + // we don't want mouse movement bindings + return; + } + + void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::mouseButtonBindingDetected (ICS, control, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickAxisBindingDetected (ICS, control, deviceId, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, control, deviceId, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickPOVBindingDetected (ICS, control, deviceId, pov, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickSliderBindingDetected (ICS, control, deviceId, slider, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::clearAllBindings (ICS::Control* control) + { + // right now we don't really need multiple bindings for the same action, so remove all others first + if (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + mInputCtrl->removeKeyBinding (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE)); + if (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + mInputCtrl->removeMouseButtonBinding (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE)); + + /// \todo add joysticks here once they are added + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 9fc77aeeb4..d3d4d13853 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -46,6 +46,7 @@ namespace OIS #include #include +#include namespace MWInput { @@ -53,7 +54,7 @@ namespace MWInput /** * @brief Class that handles all input and key bindings for OpenMW. */ - class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener + class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { public: InputManager(OEngine::Render::OgreRenderer &_ogre, @@ -61,7 +62,7 @@ namespace MWInput MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine, - const std::string& userFile); + const std::string& userFile, bool userFileExists); virtual ~InputManager(); @@ -75,6 +76,12 @@ namespace MWInput virtual void toggleControlSwitch (const std::string& sw, bool value); + virtual std::string getActionDescription (int action); + virtual std::string getActionBindingName (int action); + virtual int getNumActions() { return A_Last; } + virtual std::vector getActionSorting (); + virtual void enableDetectingBindingMode (int action); + public: virtual bool keyPressed( const OIS::KeyEvent &arg ); @@ -86,6 +93,29 @@ namespace MWInput virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); + virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction); + + virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction); + + void clearAllBindings (ICS::Control* control); + private: OEngine::Render::OgreRenderer &mOgre; MWWorld::Player &mPlayer; @@ -175,7 +205,7 @@ namespace MWInput A_ToggleWeapon, A_ToggleSpell, - A_LAST // Marker for the last item + A_Last // Marker for the last item }; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index a14606ade9..ee4855f381 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -82,6 +82,13 @@ + + + + + + + From c7b8787c323701253175d86819a0a12f08f6ab42 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 02:55:22 +0200 Subject: [PATCH 063/143] "reset to defaults" button, invert y axis button --- apps/openmw/mwbase/inputmanager.hpp | 1 + apps/openmw/mwgui/settingswindow.cpp | 38 +++++++++++++++++++++-- apps/openmw/mwgui/settingswindow.hpp | 5 +++ apps/openmw/mwinput/inputmanagerimp.cpp | 37 +++++++++++++++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 5 ++- files/mygui/openmw_settings_window.layout | 9 +++++- files/settings-default.cfg | 5 +++ 7 files changed, 88 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index bf1d8e45f6..4c73c72b98 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -37,6 +37,7 @@ namespace MWBase virtual std::vector getActionSorting () = 0; virtual int getNumActions() = 0; virtual void enableDetectingBindingMode (int action) = 0; + virtual void resetToDefaultBindings() = 0; }; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 477ffe0e24..5bcec9f70f 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -116,7 +116,10 @@ namespace MWGui getWidget(mShadowsDebug, "ShadowsDebug"); getWidget(mUnderwaterButton, "UnderwaterButton"); getWidget(mControlsBox, "ControlsBox"); + getWidget(mResetControlsButton, "ResetControlsButton"); + getWidget(mInvertYButton, "InvertYButton"); + mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -155,6 +158,9 @@ namespace MWGui mOkButton->setCoord(mMainWidget->getWidth()-16-okSize, mOkButton->getTop(), okSize, mOkButton->getHeight()); + mResetControlsButton->setSize (mResetControlsButton->getTextSize ().width + 24, mResetControlsButton->getHeight()); + mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); + // fill resolution list Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues; @@ -220,6 +226,8 @@ namespace MWGui mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); + std::string shaders; if (!Settings::Manager::getBool("shaders", "Objects")) shaders = "off"; @@ -383,6 +391,8 @@ namespace MWGui Settings::Manager::setBool("misc shadows", "Shadows", newState); else if (_sender == mShadowsDebug) Settings::Manager::setBool("debug", "Shadows", newState); + else if (_sender == mInvertYButton) + Settings::Manager::setBool("invert y axis", "Input", newState); apply(); } @@ -521,8 +531,8 @@ namespace MWGui std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); const int h = 18; - const int w = mControlsBox->getWidth() - 34; - int curH = 6; + const int w = mControlsBox->getWidth() - 28; + int curH = 0; for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) { std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); @@ -539,6 +549,7 @@ namespace MWGui rightText->setTextAlign (MyGUI::Align::Right); rightText->setUserData(*it); // save the action id for callbacks rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); + rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel); curH += h; } @@ -558,6 +569,29 @@ namespace MWGui } + void SettingsWindow::onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mControlsBox->getViewOffset().top + _rel*0.3 > 0) + mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mControlsBox->setViewOffset(MyGUI::IntPoint(0, mControlsBox->getViewOffset().top + _rel*0.3)); + } + + void SettingsWindow::onResetDefaultBindings(MyGUI::Widget* _sender) + { + ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); + dialog->open("#{sNotifyMessage66}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindingsAccept); + dialog->eventCancelClicked.clear(); + } + + void SettingsWindow::onResetDefaultBindingsAccept() + { + MWBase::Environment::get().getInputManager ()->resetToDefaultBindings (); + updateControlsBox (); + } + void SettingsWindow::open() { updateControlsBox (); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 61614da014..aad37826e9 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -66,6 +66,8 @@ namespace MWGui // controls MyGUI::ScrollView* mControlsBox; + MyGUI::Button* mResetControlsButton; + MyGUI::Button* mInvertYButton; void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); @@ -80,6 +82,9 @@ namespace MWGui void onShadowTextureSize(MyGUI::Widget* _sender); void onRebindAction(MyGUI::Widget* _sender); + void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel); + void onResetDefaultBindings(MyGUI::Widget* _sender); + void onResetDefaultBindingsAccept (); void apply(); }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 09ed50944c..9f757c6867 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -40,6 +40,7 @@ namespace MWInput , mUserFile(userFile) , mDragDrop(false) , mGuiCursorEnabled(false) + , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -280,6 +281,9 @@ namespace MWInput { if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y")) changeRes = true; + + if (it->first == "Input" && it->second == "invert y axis") + mInvertY = Settings::Manager::getBool("invert y axis", "Input"); } if (changeRes) @@ -374,7 +378,7 @@ namespace MWInput if (mMouseLookEnabled) { float x = arg.state.X.rel * 0.2; - float y = arg.state.Y.rel * 0.2; + float y = arg.state.Y.rel * 0.2 * (mInvertY ? -1 : 1); MWBase::World *world = MWBase::Environment::get().getWorld(); world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); @@ -504,7 +508,7 @@ namespace MWInput return mInputCtrl->getChannel (id)->getValue () == 1; } - void InputManager::loadKeyDefaults () + void InputManager::loadKeyDefaults (bool force) { // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid // across different versions of OpenMW (in the case where another input action is added) @@ -530,16 +534,27 @@ namespace MWInput for (int i = 0; i < A_Last; ++i) { - if (mInputCtrl->getChannel(i)->getControlsCount () == 0) + ICS::Control* control; + bool controlExists = mInputCtrl->getChannel(i)->getControlsCount () != 0; + if (!controlExists) { - ICS::Control* control1 = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); - mInputCtrl->addControl(control1); - control1->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + control = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); + mInputCtrl->addControl(control); + control->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + } + else + { + control = mInputCtrl->getChannel(i)->getAttachedControls ().front().control; + } + + if (!controlExists || force) + { + clearAllBindings (control); if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) - mInputCtrl->addKeyBinding(control1, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); + mInputCtrl->addKeyBinding(control, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) - mInputCtrl->addMouseButtonBinding (control1, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + mInputCtrl->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } } } @@ -597,6 +612,7 @@ namespace MWInput ret.push_back(A_Crouch); ret.push_back(A_Activate); ret.push_back(A_ToggleWeapon); + ret.push_back(A_ToggleSpell); ret.push_back(A_AutoMove); ret.push_back(A_Jump); ret.push_back(A_Inventory); @@ -681,4 +697,9 @@ namespace MWInput /// \todo add joysticks here once they are added } + void InputManager::resetToDefaultBindings() + { + loadKeyDefaults(true); + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d3d4d13853..42c90a73a5 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -81,6 +81,7 @@ namespace MWInput virtual int getNumActions() { return A_Last; } virtual std::vector getActionSorting (); virtual void enableDetectingBindingMode (int action); + virtual void resetToDefaultBindings(); public: @@ -132,6 +133,8 @@ namespace MWInput bool mDragDrop; + bool mInvertY; + bool mMouseLookEnabled; bool mGuiCursorEnabled; @@ -159,7 +162,7 @@ namespace MWInput bool actionIsActive (int id); - void loadKeyDefaults(); + void loadKeyDefaults(bool force = false); private: enum Actions diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ee4855f381..5908c16f91 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -84,9 +84,16 @@ - + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index a723e307c8..fd0c6e7af1 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -144,3 +144,8 @@ sfx volume = 1.0 music volume = 0.4 footsteps volume = 0.6 voice volume = 1.0 + + +[Input] + +invert y axis = false From 67577c61924860e1c67ccb303ef17149e42f9f39 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 18:48:50 +0200 Subject: [PATCH 064/143] UI cursor & camera sensitivity sliders --- apps/openmw/mwgui/settingswindow.cpp | 14 +++++++ apps/openmw/mwgui/settingswindow.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 23 +++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +++ files/mygui/openmw_settings_window.layout | 48 +++++++++++++++++++---- files/settings-default.cfg | 8 ++++ 6 files changed, 85 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 5bcec9f70f..4039136a17 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -118,6 +118,8 @@ namespace MWGui getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mInvertYButton, "InvertYButton"); + getWidget(mUISensitivitySlider, "UISensitivitySlider"); + getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); @@ -226,6 +228,14 @@ namespace MWGui mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + float cameraSens = (Settings::Manager::getFloat("camera sensitivity", "Input")-0.2)/(5.0-0.2); + mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1)); + float uiSens = (Settings::Manager::getFloat("ui sensitivity", "Input")-0.2)/(5.0-0.2); + mUISensitivitySlider->setScrollPosition (uiSens * (mUISensitivitySlider->getScrollRange()-1)); + mCameraSensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + mUISensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + + mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); std::string shaders; @@ -510,6 +520,10 @@ namespace MWGui Settings::Manager::setFloat("footsteps volume", "Sound", val); else if (scroller == mMusicVolumeSlider) Settings::Manager::setFloat("music volume", "Sound", val); + else if (scroller == mUISensitivitySlider) + Settings::Manager::setFloat("ui sensitivity", "Input", (1-val) * 0.2 + val * 5.f); + else if (scroller == mCameraSensitivitySlider) + Settings::Manager::setFloat("camera sensitivity", "Input", (1-val) * 0.2 + val * 5.f); apply(); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index aad37826e9..159d52bdcc 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -68,6 +68,8 @@ namespace MWGui MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; MyGUI::Button* mInvertYButton; + MyGUI::ScrollBar* mUISensitivitySlider; + MyGUI::ScrollBar* mCameraSensitivitySlider; void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9f757c6867..febe528c0d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -41,6 +41,10 @@ namespace MWInput , mDragDrop(false) , mGuiCursorEnabled(false) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) + , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) + , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) + , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -284,6 +288,13 @@ namespace MWInput if (it->first == "Input" && it->second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); + + if (it->first == "Input" && it->second == "camera sensitivity") + mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); + + if (it->first == "Input" && it->second == "ui sensitivity") + mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); + } if (changeRes) @@ -367,8 +378,8 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - mMouseX += arg.state.X.rel; - mMouseY += arg.state.Y.rel; + mMouseX += arg.state.X.rel * mUISensitivity; + mMouseY += arg.state.Y.rel * mUISensitivity * mUIYMultiplier; mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); @@ -377,8 +388,8 @@ namespace MWInput if (mMouseLookEnabled) { - float x = arg.state.X.rel * 0.2; - float y = arg.state.Y.rel * 0.2 * (mInvertY ? -1 : 1); + float x = arg.state.X.rel * mCameraSensitivity * 0.2; + float y = arg.state.Y.rel * mCameraSensitivity * 0.2 * (mInvertY ? -1 : 1) * mUIYMultiplier; MWBase::World *world = MWBase::Environment::get().getWorld(); world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); @@ -578,9 +589,6 @@ namespace MWInput descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; - if (action == A_GameMenu) - return "Menu"; // not configurable in morrowind so no GMST - if (descriptions[action] == "") return ""; // not configurable @@ -619,7 +627,6 @@ namespace MWInput ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); - ret.push_back(A_GameMenu); return ret; } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 42c90a73a5..79e5a70fc9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -135,6 +135,11 @@ namespace MWInput bool mInvertY; + float mCameraSensitivity; + float mUISensitivity; + float mCameraYMultiplier; + float mUIYMultiplier; + bool mMouseLookEnabled; bool mGuiCursorEnabled; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 5908c16f91..ad906d2a13 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -83,19 +83,53 @@ - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,11 +169,11 @@ - + - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index fd0c6e7af1..effd9f3da5 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -149,3 +149,11 @@ voice volume = 1.0 [Input] invert y axis = false + +camera sensitivity = 1.0 + +ui sensitivity = 1.0 + +camera y multiplier = 1.0 + +ui y multiplier = 1.0 From b373d0ec7bafb013032a05d88e8f183d90348242 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Mon, 13 Aug 2012 13:53:54 -0400 Subject: [PATCH 065/143] Correct struct to class in forward declarations Fixes http://bugs.openmw.org/issues/362 --- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/inventorystore.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 6ab92319da..3c3b0e34be 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -21,7 +21,7 @@ namespace MWRender namespace MWMechanics { - struct CreatureStats; + class CreatureStats; class NpcStats; struct Movement; } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 45b3cab268..e55eca0ae7 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -7,7 +7,7 @@ namespace MWMechanics { - struct NpcStats; + class NpcStats; } namespace MWWorld From f9efd543e438cc832ec5b695cc901d3a1f43ab96 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 21:33:53 +0200 Subject: [PATCH 066/143] use float for the mouse position tracking, should be more accurate for sensitivity multipliers != 1 --- apps/openmw/mwinput/inputmanagerimp.cpp | 10 +++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index febe528c0d..0c0bb74e6f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -378,12 +378,12 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - mMouseX += arg.state.X.rel * mUISensitivity; - mMouseY += arg.state.Y.rel * mUISensitivity * mUIYMultiplier; - mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); - mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); + mMouseX += float(arg.state.X.rel) * mUISensitivity; + mMouseY += float(arg.state.Y.rel) * mUISensitivity * mUIYMultiplier; + mMouseX = std::max(0.f, std::min(mMouseX, float(viewSize.width))); + mMouseY = std::max(0.f, std::min(mMouseY, float(viewSize.height))); - MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, arg.state.Z.abs); + MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), arg.state.Z.abs); } if (mMouseLookEnabled) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 79e5a70fc9..c8b48e727f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -143,8 +143,8 @@ namespace MWInput bool mMouseLookEnabled; bool mGuiCursorEnabled; - int mMouseX; - int mMouseY; + float mMouseX; + float mMouseY; std::map mControlSwitch; From b1bd2aab7423c6a7d46867bd1c7bb70655bd6c6c Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 14 Aug 2012 02:24:46 +0200 Subject: [PATCH 067/143] Added font creators and a formulae researcher to credits.txt --- credits.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/credits.txt b/credits.txt index a0be853926..ca0ff7323b 100644 --- a/credits.txt +++ b/credits.txt @@ -61,6 +61,7 @@ spyboot - German News Writer Formula Research: +Epsilon fragonard Greendogo HiPhish @@ -101,3 +102,12 @@ used as the application icon and project logo. Thanks to Kevin Ryan, for creating the icon used for the Data Files tab of the OpenMW Launcher. + +Thanks to Georg Duffner, +for the open-source EB Garamond fontface. + +Thanks to Dongle, +for his Daedric fontface, see Daedric Font License.txt for his license terms. + +Thanks to Bitstream Inc. +for their Bitstream Vera fontface, see Bitstream Vera License.txt for their license terms. From 9501e133f5c840836c4a79881ddba6efc140d7ce Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 14 Aug 2012 03:23:57 +0200 Subject: [PATCH 068/143] Updated the EB Garamond font to a higher-resolution version --- files/launcher.qss | 9 ++++++--- files/mygui/EBGaramond-Regular.ttf | Bin 231904 -> 405476 bytes 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/files/launcher.qss b/files/launcher.qss index 8be235f71b..1eb056d4d3 100644 --- a/files/launcher.qss +++ b/files/launcher.qss @@ -22,7 +22,8 @@ stop:0.9 rgba(0, 0, 0, 55), stop:1 rgba(0, 0, 0, 100)); - font: 26pt "EB Garamond"; + font-size: 26pt; + font-family: "EB Garamond", "EB Garamond 08"; color: black; border-right: 1px solid rgba(0, 0, 0, 155); @@ -54,7 +55,8 @@ } #ProfileLabel { - font: 18pt "EB Garamond"; + font-size: 18pt; + font-family: "EB Garamond", "EB Garamond 08"; } #ProfilesComboBox { @@ -82,7 +84,8 @@ padding-top: 3px; padding-left: 4px; - font: 12pt "EB Garamond"; + font-size: 12pt; + font-family: "EB Garamond", "EB Garamond 08"; } #ProfilesComboBox::drop-down { diff --git a/files/mygui/EBGaramond-Regular.ttf b/files/mygui/EBGaramond-Regular.ttf index dde4869030022bf4d25ac5ea4403795ebd864fd1..3f6f6c191d9d47870201c2979caf8acbb41a7e63 100644 GIT binary patch literal 405476 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%0Dqvm!AngL*}zW3O_YU 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%0AXjnz^wYyL zQi=}5TkJL#o>se!cPcTpQltTAK!N!QC=!4*A2TDf07?0TM-vb>)lNu&?|SK*_*8=t!2t|fQ$eyd_#;fIFp zzbu^D_1vEdKXO#hSaz(x>z20OrnYSlZ&+|{RTKZ#gN1+p`uRd(#1Jysbh2Jk{ZvJ^ zZe}p9=Vez#+q|r=BIRFk|ArQvDC21M%`~UJqsbn9n^}LuAD-AU`or0GoISWSGt}1m z!iha^Jh*n@X+(Lz8&$X`e1@W{(^^$xUI`Z+>Jo44AG;(DBgT3fzq%FvuPDw%Ytmd) z6^W8OE@g=>f`5Es2#-W72@VEWnCJ9d4mdRkzakbgIs-|^3&O``A3g+C28Z-;>Z7Et z!Dy1!eP!!iY$BoLpxi?0u{wqj*;}!Vt0SSkT}Im(630u1;)M}ezee8^s1N zTeJscH?A<^E8AKt729i4&FqWV5Ao@nkF;8pnpM;V^PSvNIEId#3$<)$I`NIgOTMx3 zG#>aLNt#a>(JJE2CfdA!9V)#CvT`^A3Lb7^Ejn>}7KxwHz;d#dMNfe@S$$r)a!cP; zm9j4#^;l~-qrLE^Lnl}?B4D2O$(2j{hVZhE!@-p`QS<+nwG3Myu zS%Pf)jS08>rkhFFC&$?gI$4rrom;Qfx*VZ{TET5fSzIP57Sx&S$~%XQOlHZONwV=k zy@Rcfx-6qDk;=v(d*H6G%c5X(9lOPD(rN)2PSuV&=*PJ8sya~cTb7}Q;88)5ol{kk%XOIft%BXFz?wUw8^-_@ACju+ z`ZC;ODwGCa#4Zv2OL3VfJDRNJKrEo8%eHvR0yFHv=9xYYNxC}OV-#g?P3TAA{(QYp zPTn|jCRw%?-|a!}F4Pr@M^_k9*pVkaan-6?15#ISz=X;RHli!zAAFziqo+Hm@mWoP_!=dQCF*rhs7K z`%s^F<`G`4*Wo5@uR)B?j+WzzX=R6A7m)U`ZNL!I=r7Dh)SPm}NvOc`C~$@3pspUE zu#6$;bi#d1_zpC1N(TzRCpi@WtpH~wao$GB@pQ^-n%HkA*hTD%Pe3asyMY;sxmk-O zXq_5&UBi#DI`RHMNaidOltEM=um+0(F-Ey@*tWt^`+D05BGQ6ZZ5O?aQ@5k=Q0G)k zVAR`L zZ!Y@#Bva|oXq{3)<@0Rpo7<=nC4*S+?i|o*jW|%#tPihbAH&IxIFPbA=4dintY410 zj7F4b+cienZy4=}^F@C!_yW!!Jg?e`y$%vTAJOSc)Lv4F!SgHKBt*Ng3Xo)Wuq_;7b%_Ik|S!d#m9y5c=KUp$ptd_cOx@@dfZz|@@ z0zcS(;0G$>FXD=$SSnVMjZ+zC+4hzp3$uGN*ChL_Y*{|1?5S`CUa9Pz)SJiVDIB%P zm-v&&^E!dK>>zQHcu`y@OZ<}*2H`A6u$r7^&1KpvVCyA@bBAOk`Uz zIzovYl@W}CAA&Tf{65ZQtdV`3PO$P;UM~DE(Wf!Sni_m^)%s9%QBq8x=~jxSHBCld zXOf+=Pq=Bdz~h`#R5VB&hT9dZ+My=sUzv|pcU{cNHpXxwL(o>s@CKcLLS`+ijmS*9 z){-m~T56?9D9xtFQCFmRFU+0xoS&Dp7RjOBu~la@qs$D4CyRp4fb+I=1{bGsIs&OP zS=m*1iyS>e?My{ynhE$Oh>aE(ptAp2od}x0Qjk-oHZ>)w%wlSI0a>~R-Jel|^s?7o z;VRdprpp%bO{hY`Daj(sA}XmrRqYA*>pgCDnc8GR=u(kw%Gxqx@2VF8k9T8jbAyD> znGh*0t*snGOvwbq4KOB|pc)?T0EKVxvX-D-3)G&;yb>BB)0DGVacpC59CJYX{CN}C z+)B`9N37y_1^8O7Qee&6!n+2$+mw5*4#g;p-N4CVHyWjoAu*Ifqorn7bFPPSx_m5J zrNc=|X+e^NBgc4ygcH37l3%b9oSA2~bKb&}g`d`>bQ5Ty!^yOLFS|JV=;m;IKl9Rd zRKH8VFMMqE#9iu%W|kGVFwduhejU}Z1fCy7LAXXnq_IItdi(}XAx2q(hmKKY#(u@$ z12`@K9P0@`K=nO|e^Qz=6&>P)+{!>oAq79!gjfL;;b72m#j=dh7+w{wsx1g|PyR3UJe+D^>_hHx?iE#qYBh2+ zrDv}>)Y}*_Qr=$_@Z_`w)S+SdFuZ7B34*^Es!Bw0lwz)tW_s~H$O;rZ8Aya0#vNr# zO1Lw?`_RK-g;|U$7AhQw0*lJF(uh#8lnxm2aHy3>q!;-StWl?R8Z|6fgS*kAb7&+E z{$ztr$9ETQWB#x#EwCD1)PR6^a$>s0$+KqD)saGX)i#ZYbU(Fd$32dy<@6r(*4V;A z%qFCQYP(i%ceXHJ%|@*zy`-)-%6b{r)AM1E>~q_sp89TA&~B*7x^+^;eQ|p{Y@S(o zc_SmMWdX7k_lgK8bQKwG_oh0X+h((C`MH||%^Zi&g<4{f%Jrbx925LTt#bz-g zjz2v5#Yxya*+jZraw@inE}dm}hux+1;XKsyimgQA_whe(7Lk>e*gQdXV)8-CGPh1q z%u04%6xQI%FKzh7O|DE=@0=scmfnB4wl!|oNp<@d-Sl;*!L@H_?G0^oj{ReQ5dJ2HN_J+Z<$LP}sMRH@FWDFKp)kb?V7c0DBkNje9KG7hsZZ~=;T)D#7&@+N~$kpP4EkVNs$F)8mtcD>{IZD zN(7uWdnHU`#fMKQqlL6^yges;x^GKGgH# z$#b=7t+lf7kN@kLsq^Dhd&b`5PjJtuW~c^K3&FRC250s+TEMsC#1{*%4t4%aRrFxl z(nHc&oWFPll2>P~X6TV{c$n!~&1`Dnm#&>N`|7VOT1f@<#&~oi)%8{`{PH#Q_xc(l z_%}u3`w696<*>n~xo7`fcNYF*_ZQD@)zl6gADDM)aieIfHxpq*{XVc<%=I=+dhpoX z!BZny(G>J*AhHdzY=Bn;CvRwFvyMGG`aRv(_8s`<=xc1J_T2X4TN@oXnQPIRTn zVz|zD}zTJx=xAf#1`s;^)oc zV~N>{zBovJYTwxO49@IiKhC7|CMUc=3X9VO;-P0HR(zqcYW!!f9ubs~`}dNw@N7+m zeq0@N>YbVQ9MZowI!a2UQ!h7n|C7ckl=R+b0I%hM*PH;+X>5+NqEZkXk;zxaT^vI| zJb~baN(u=u#_7Nr$x}5$0xzNyuupQ@TRXf5v$4FxxHKD|v2UppIPR5`r$-cnx8J#R z-;8*6nbFY|%UiJZ?dAJ`?v^2U-QwGl}^y>LZNWq5wIadE3W{ zxfKgf%TlEF@X|LdjKTcj1ka^r0!%CgXO@rsk^d01vKlZ*5I;bihMwJncds0|ZD8R>lNsKh!rQi44vmP~cCS&d z)tLm7VANX7PQ$|@cUS$I8yBp6aCd(F_pkboyTHpM3EqbD6olHFyf`sgEgJl@we@fA zS^LD^tq(1`@#P_TIWBA0h+6gPp^#B@i!RZsft8OWboiB}u~F8_yg_215@MiglIkNq zy?kDepyccX#zvXfaHkyJ2grMJFOBqUe|Ja5!IjQ)ByDbEUK?*?K^u5i_)=bv8Sn2= zzF{U~ko0%)>FT((cpf9^f3fTvr0PPrxMr}_zXEwd?=U|n<5!UWcKW{2UV4xI?Ao&a z4*DK5oKjtRYM(te13SjwkK!bkx5nR-bWh}WSxWD1fMKh$59HiVb(FrZ z8t9Me90!+EasvTDng!9X`@L<0kfK;k~M!;f^L>GqUrTmdwVx7p;DD z@6N9+UH;%!e3en{13W)wUsP3N1tyvcOk`D2+bJoZMTygtfdM|DeS-%vi$olv!srwI zaGL76<}?kpOD3bP%O1{G>&<$xcPsmpXwolkDO{KFN{mgXw+ez)5C4eXmI1%6*Dz*L zGP9Z-nG19qHiO*0R?H=?1wSOYpB#$%WdLCa#s`1MA{_Ii4Y#ar zTDrPxa0CrVTN-R?o@_w z_5yM_8-PP`GtK2>8iTL`w{SF5xZMaqH(|^epGrx#ipe3Gb+P zpxI+7>@~3lQw7Dhtz#cBm)M7qcZqx!5;Ka!YA6YEvVpZc$yq5*M9Gkb5l$pLocEV* z->}1Aw%BXubjQ81j;cOgu)!QTcb&P?UHdgl*ng^WhmkAXHm_N)(c3MRbFzVkfJblU zw2{iwl{g4Q?%{-(-J%Jb)9Z*7VO3esdky;>@?>Lxlh=x)g)k>2L`>6NlSSQ)&Joih z`vzT&d9HI>^TNT7T+eTMH_Y2~)3HNGZa(+}K}yC37fkoteTvNpf~Fy3(k2h+C7UtQAdY^_>QSeL zn-$oI4UQEK`D?#ogIKSOSXdW0TW2nbQd{Czp$axofokARqK;4CFzh`=WiNVCh*nif z1}1tKGf)|Asi^9%@6^^;S2w%9v}WW6PqscGN|nBh<@$vK`zq6wI&NFi)6vw>*3l*- zZTw7ldTmv{F45RoueH13or^5pw|XGbQX8=GQpDYoi%cKr?~3;%eWIo-Sl8IGtf!%F zc9O{EAaTKU~;hD?HFWJCYB$6vFkZ}XX7^=?>@%P#By%8)oX zH=FVEkJ0<>@NCwo^8hW0dArOdIoHfBRsAP_k+#7z+H}!2sA^zKa=^wdVXXY)v<;ro zrn}fCA(h(Ha*I@jVjDc8&GcfM>hYXsk(tq{oT>BsS zZ_ze*Mw{N^IDv5;$-^yROnmWHIq9mmWKGhY5tIu4%EW=3(GsJS%f zC^x`pi{s!KZTe{&>Dhd9g~K3}gUcOZ+J(l&TT z8$cCpCgCqMDJyp?ZDV6w(FXWS+mzuin^P5vZED#Z+5oz=O&R{Os2$~Qq2q{bE!qIW zbUtPH%hsv>$lXNSG_rMQ0|?V`%J7%XGA8bJ+Q!3X(FRbabSylh9iUCWSAsUHS^z)xkN6*=q9O(O zh8(&wpDNMj)LEj<5f3$tJ8J%M%tzA}Ra*s1ym_Rha(Z>hB5@yVcxY=&bk^p(MmBtN z3w_}CEW79Yk{JhX*}k$N(Y9>KzNIm}r8`YnhC`CtPFa7m)O%IlQA5=}8R@8|vUi*5C9f ziwYr}wkNhOcU+cJWLzs(5>T3wTvBu>_TkFHTNa%O(VWedvpqVG$s`N@d9HBbf93}Y zztoChhKIVw?B1|^&HRPu!g{~m)x7qB6{laA#S|I}?;vZ3WBF*%5V6~|nf|BrdXE-o zJ(4cF*1dRg<=N#uzOc_fvNZqtE!*!Qb6-C8B)1fE$Fcht9V8}d9XHyEzXARpB8C)U z4(*|MP#(l2`HdRyh;-tg(8D5$wb>P4Th@1U{+87|$Ey!MeD8nV)&9juDCk?ECvjYE(t8RODcRo-x=vwpS!M$HH z86CcA$rjCIXI1qq>+cQ21Y=Yd!25Tw*hmbi%Yr$Ri)H{_qWBSwgMb(FhC7!8KZusE z_z_7)JCs49tddXwIot0OaED{py44E`_iuye##LotyPef>iy)7_&*A#;R?(!<`<9sO zlJuO`5YyZ)%IbL5m=9N%VO1n=jJcEg*Y6Z{2F57xY#!Sb$6-#fo*`*+^i zJEzun;Wx~Qo*jKvIJq*qlu?;ca_wlaY2$l&wahVxd@)Yjw(cvd_x#{QzuOo8J1_{$ z>qG85|28-|(aqCIyP~_0xgc;PfbvQnT+Bvs3FVrQ$q7FYHF1DBoLHHOuN;l>HoZn# z6WRFe;op2`)q^`aHh<%lHPZ_V&572VX1OV`gYAc)J`kgLZBctZDyv&Wh+v0F}!Zpk<7_ZfnDjbi`&}ou3i#}$ySG)NO z)_@?1hFSIj#i z%W-S7rBCBBOYAe%ht|*DzqGGnW?eoIF{EAQYIk+4J{#C{|Gci#%OdSH_5PI2AzEwm zBWv0go^0>BddS-lOF0tI>n-LJb(?3by?x7UcU`pFQDvxA6Gs+sCN=grcOQ68Le)rc zrqVdB@3Le?f?`5Z6K>qv>|k&vUny*4U|I2j|9is&AMDrH`>K3Fm)3n>crhl zo?Y{={@IbA9K7$Uk^UR@)@xtt-n|#|TY%Wn>!?#OK~EB&_E;64g(BHXyD>hUTKpog?EG(BJMYMw z$M*eX-{GJ9;QL38zp(C}!F^A1dpft?HSg;8Z@T*D2X?%6ywG*{`M{)+yWqtLd zCL-z1XlE$ms_w|JUJ&AfWN56hW;U-qnC_cV*R1G8o3~{AME5*S#Gl z@%~iK5iFb!M`cq$%lu8@e%TQCji?h4z}N5^?!c+h-+HXcSmW@kslIk5=yv#RlEw}gLIV9pIk97$|IjGU#D&Aq698P|{2=ZR z64+s0y4_-I4LQDPa(KIEbaxdVcTTrE>*6ujwb7ln`o#QrTB{DnUrM)fBYMVVaxW9r zdQnPQ9kaXIF7$u5@CS>z(iUkGg==P3&mYPSg(^heAE}Kzl_Czwq8z$`C+FkT0fyDF zGC4#9qM`v3AK*AY1qRTYDoS$5z_1@d5N&xas&;C{hOQM)UA6W3ZP)(Q>eXNU-ny>B zuC~2Bnf|!(u|4%2HvZPQzQ$7j#8v}bMWzLJE#CUdiR<1yv+niB&Za}7n%Q68-nM@8 zv<(j|*g|D@VFB01jZoP=fvWuh z6@En|Py#pB#|;Kw(8`TW5ka;NZ&2TES;*B4TWqo-f=ufo!3N^ZCpjq}u?7A*oO$Mj z?k7E)iMovv&F3z$*)+fg4;vT}QYLUxL9R(d^$n_95jYx2bg$&1!126{Fw#A3Yklji z-ae*dSv$^Yv9@a9iTnNGRD9#TBOA{8e1UzpWz%y{+%dTIZmq}EVClx8&$EsV=jI=8 zoZj2VitSrAwL5E_O`2?d%e&hv7Pbzb=)8Snpd}R8bMF6P@6E&HD2}}0uA{4}tE;Q; z`<}jLx~J#9PiZ8LuF+^Tx<*0>A%P@>KnM^Lw=u|E3o?hnUAi!Y1~80^JhE@LjU ztc@`*VAgnT)@zn!yi~zxS{2@nidOY4X>RnURs15s{e@-6wx` z?^yctyW;H~i4N}o-7myiU3p!&6>D_?FM}|5igSAjT&%UNglQqYi2iMMpt_Z?wHw2p zx%-thhhE-S+e`3fV9}}5SKE0lo@)2s^x(mtTy^!1nH~9c1FH`pS?H^GZ+ZPk_ss1j zfi5XL+;T<~7iLAP7=8G<)4#i|(!cYHM{k{X&W8Q>81ng7+v%^i)13YSx%;c_^jF*I zueQ@)ZKwaA*-m?B--q8aZn41!tvu*gDSjYQjF|^8i1E+-NtAS+E_d{NX znbIQAUO1MaH&*1T8|BE!G{TMY^f8JwQ5^v%Zb31%$jQ+`qm#$U8Xg2Xd1k|nas~YA zd;QifctrXpWV_&UOtntR_T-v=YY+R|XZnRkk1q9&d!nw<)}^wBhc=?(P{w=3Zdv7f zPrIo7;JeRzHTg%#`JslBocJxPT^i-Z7yY{YBjrWp?0#0%17%Kp0J&KN#JNP0mS;Sg z^z>5Ow@FW8K8L;hgs>g_+JQWrh>^T=S;Elw1gVp}k%he9*PDQ{$NWaPqD4*b%CyMc zxQ9cRC*hLl>GrY*k8Jbm>86XybeW4CXu?jxPngb+2dPrP7Z8|lf;kMkd%L(TO@h8+ zd}V`>75aT$!K5QOyl06lXhI69KXfIw5&>Ufo!FC{(=Ir}skW#fmtE7F&fBZW)&@o8 zMZp%C@`^rHl)_?US`Z3?Rcq>BwwV{D3^!4E#bI5#rs+7G-wONqoU@Q(^==79qkCoe6;6M_%Wvbe>Yr*^rWx<;EX-W{bk4p$`k1Ij1c6Ws!wivs$bWL^^sj z+!Y1#%#q35^QZpqH6qFT=-mKprXK#yY)5-jq0|6ue1o3kCA_(Ml%a^}R1 ztJdvO_?#;zIrzh`YZ(RSa95r>vi!2s7A?LweZktzS6;Mf?G;@AQGZ%@`A6>x2|vt0 z{-d|Ocs|Orxg=QHXWtXvJ-b_kc~4=-F8fGh@GRt#l$l;AKp3)co0Gj&ptxG z;sp{*%fpPjR*v4f^coIGvI&^2K;|_QhN{;B;$g+l4R00Z1iY8HI%xQgAtu7pYesx) z7rDH_Sfsr->+jYbircPrhgu`!ll>k=9Sn5mdK2QYm%kPD>AD(H)tyVEv8@Y6{0{GV zNtf>e7d)!)bIak$Nc1>acOKpwYwnLI8%Gyz>v14){Z41bv#hs}>zm--QU`tO7dc(= ze7fw8i-OfH$r;TtJRO#8L%t$jx@}nTjpH@1Q`b_VjC*J)Ki)gAE+A>XGmAn01s!;l z5*B&8BDxewAGf*MhdNVz6YX7UFy429bD@;pnH_3Oj;>|?6+UhG4%R6a=gUuVuqZ^D zxi;xw33S1AyfhSYiK^_B-JBI4{IH|h=AMoOn;X3OLcHL~33hhN3ug_L54kPy88Lbe z!|%bO+v3p0gWqwtF>|91TsDz`0= zoSL$X+kNdJt~`m<&5U+CXm1s^Q2a31{}_KP$81vL3IpD%;FcmaI~YK|nlDc(PDeyq zdf@csN*woO)?UwTysmP|Qbl(O3%RMAZr~cmtybBlO;!Gh(e`l_ZcJ#0pIEv-O7~%C z6$$2hdvHs11TSe|uOYDuLX$x@;F0}&+nQA^$uI&s$@xiH)U$zLw%6u5L2t&@WSi{f z&qv-QWF9;q3yq!8a9We_q}7?6Z}+Tenh<$cI#g!$PR@S7KhLj&uMv0gb?VdH5dfSj z=pAm2V8Sw=3Wp;63ro76dqnVR_$jh+>|pEH+3rjgYZ4(%A$AepA%fn%sBZupN%%_IO5!gd*{? z)}N)RUQrop53rkP9PHVbxp8ii;?hwp;vU0dz=cXslRGs{$y~HSz>VIQmC!IJy6j4< zpEJgBdiH(G@6nE0Ojz1~F^r#D*wV7FC)@Bk)FFTLRSCD$5%t*c!Mtzz0QVM; zh#g$5yBe4F5^4jQQELgei~eQMh%+7%jTrV`kAw7w>TkUyGv;|328=XPQ(`i=X|{9XJgjgynbd-PiAc32_D1iUxbgNY~@RPj}H@{S}^J=ovYvrO(DrKh>6g~(bUc3CCLPYM+e*m8(kGMA1@o%E-4xI5T z55m2}{CK@b#Ns^SUnAUB{v_ER^c}v-h8K2o-=gciuoBD!Y_8@vgGw+9e^iKFfJEEd z(f044?JN!*#dGC)(Ck7UGl)8Yc2g(#jBKs+CRGmaX!xb>Nblh%t(wb?^J3u#dWVSQ zqW>uuR4wp<5R{x{rX=u|jnLh0f!-tI2A%R?z=-`^9X~Jmzv)$kZ0z*xG>17>XboqT z-pob@E{dkmj7Y5Y!|V3U(+dvnYJVcFW3* zH$K>dn5fhoe%Qolm}wIRnB83G|L9LS99FSosWDG z>?@jN6}<{BUyXM^KXvZZEw66*9$!qC3-)cv_%Yr>X;s68=UQbR>X+YR3%Zeo z``vI)ODX3%Ihv1e(DS82Ow5Ra7Ql5OIr#1EE0;UZogqgh-54oShHL9QPa z3W!C!n9ju3PmHD{dLNB8vr%WKUC`a7`p-quu+~*Uy*P8Q+Q~E3{gM zFu%Ku{T*Ib)@)ph$HvIUlyQUfD0aHaBg&KDi=)5LuGntP)<|U2Ngv;=Pbm&~`;o+0 zRjhJ{Qr_Bk>Dq1tRB$1ohRP9@)ut$HFt&F660iJGv8#gTzUDCZ~mts-q<(DpMKxA&pyY? zh=)b%rVH|QuaL1E2fi+7sp!xStQeBu*&gJ{|i zCtxu?puiMfru;)1In{CHsF!z%RwvSJI_z%V(LUwM7I1;1P`W~$Rzpb~$Y=r4xv}z3 zBe9u$q1@TV`n;ywHRc(6#^nj%z~7?@9xXX=pB7_T>z|LdTV1+5VAsa|ex+zdJm{5w z)86V-T*D5$S6Jm!6vmLIQjH-9#MnOJaI|abpf3Ld_K!IDa5nhB-l{BWXMDhVX)pjC z!GDP*P%&yO!Ky+%XOoW}JR5&FdR*%`L24CaMTYD5`b7i=aU}NWv67AViY`&q5RgM} z*LyTanc6sb;OD}xw!YlsqQ@y|7 zD_fduE*_a_k^F>9HUyea{`%Hyf8LZCRXenVis@_(b%xkLfpfdCTnc>PWI3gk6=!xXKi0<)}i;#kuK9U8jy? zSUVx@2)e;?y;P?&%CX6U6@KAiKGae!h<{OzTm&50_=h^;>_KY>POf_ zWUZb=J3C8VUDSuvj%E1d?<&&zPSq><;^9}CO10)Gr6wAK+3(DbbN8TbcD5g*x{YUY z)m_UFB{vC_&VqWdL7}|~(sv)1>nKzGjFFwq2r3gz#wU}?MM>M@Y}n0%CBI$p=dm!_0bq?{p!#+?`Sv7YpTcr_Q@TadU&MEj^`tafdsq z4W$aXtgrHpPn9Hbx|Ay{3Z}N@3gz}f-sre*bh6wMmYmMWp@D%Q|DUQC`>F1gyu({B zQXG}Xa6+#*5HlWXg`~LbJ}&KxY`AW-R$T|=QVZWy$00f2^v}BHzof4(C=*}UI`{#` zrvrS+u{&glLtsA0fcF9&0$13YFCV?mvGv{*9nRG>@`T*&iiX_IM1~ig$5nnh{6H?z z(;e@GM6ZrfpN0nchYvs2ltC2rWI7kl?(+B`+<0JCEb#rMaBUE6c*4ZuOa4}z!FX*& zyN)ZMU0zHFl2Nw&kerat?Z%6_LW|Y##bN(pC$!!G!auQSFz~jrn0idI*{|lF*1jCneeWwrRN*#zRP)zkbcim=hjI&w!bh$J+FW?&~zWWN1nuu| zw(g|QtD?^y&I)Ci-&nc)JYKhjx&7yaivlJ$vnjD>J&Qfd$fc7ERO1+6SkX&EZ))K! z(D>kiV}s4Sw|VZv&6f=?zwS8e#!E+=CeASTF|A9b+Ey-|bD(@yd_=U`;pfjJ_l~wrGzU5lf9E&{oAAtb9k0A3 z-UHj!NjcnxBV>D%rN>5_UGQt+1SSA@{DI4obW7RFE=p1mZkDf-t%YD6{AbEl-#Oud zlb4<|QQUIhKHolPZKZ2ledo?#?tX^1bH_Svwu2NGaec^tG!h8;0PKgBI9Wy!NIh z?{G%en_Z28Lb^Ga8ta46_h`h)cXV$~boZuG>6ydlEtL9PoteRO!W|SjUHi$gG)f;E z)00ogt#WsHc;BeMJJOPB@4rpSPbS^BV{CfAONgxKV+R6VW8URMfl@itia~MJP8iY- zC2(qk%jfACEYjS8TV03>YhcMr_oqq!Vybg(9cCAj$xI;qk?s3rWw4>aZDrHqfDt0G z6|JiUuiLN#aF3qM*4;a>?rsnG;tSXNm5f7GVh#j=LUg>rIQK|=zy{k}3n8YLY?FDL zfES7}&+d-kRa36&!s=Yb2irxlpr;Wpxod6IscJ3{Qc1|6twFqf<8-{xbqlZ>ao*Cw z)`2vwKN)%-=%RSX@HLa(i$@9QM$HVbDN|Sz*kog}IcKD^$t7%*B8QuS>itP)8azS!3_sX1X47=1$Vco_M>Z9xT$agjhZJ{m5ce9<+JsGI_bzAOoqbn)9BpN} z7be53t!&d``jgOWo)A=3MbZ(Ol7{`ghc(!2uekwAh&By7#Ms@Kc|u|Z)vdl;jQ?;M zBoV&{mw(y03(jqh;0hK6gyLPom1Vn4W#K#U`m4QwBqxko5?t=ms{1IlWm1^ z!8hK#0OPbQ_lg>oR7c}>KLkePrx#hhvei-fWoEdP^(_v>j?oI`s8tY7IaQIZI}~-o zwG{3o-?C6}siV-#zsjHD3PfCu@dgn(f;(z+Fb*k!_`s%&R{5Ulx{O-~MZ;blXxFia zzZ>rltp2bK!H8t(frlL72L9$T7jc})N7f#dl}hDp2XBAZ<*-G+nd?@KLfkh)PPo|* z$;Vb8{c0WMya+J6$TaPVFDyO*2C~?jWwHn@f^`e> z2yq+{$w-)!z2fvJLUn4wwQv*@b%U3(K2n`mv=#@F|+&cj^A;TSc4ABTF5g#*9Dc z3FWC+(BUsVd!H;%sh=^`QgL_?D zQTedjx72+*0xnMaw&zxr7tPBeR+ea2Td&`q&Mk7g8$C%xa&vmMwWaG*TON=cqNClj z@5Gx|O0qg|>@l6f$N(Z$*sp-UjJH`u=hn!lTXqg@n&}YmSeA!xg>Pyqw5{p%AuWmM z7M=6_*M92xy-*@08rvIYCD_25DYuoM$vg5jF$;lg2{IAzFZxHm`v%STp3O15Uw7-l zt5c~rLN1{-7}Aws!1>X(CT}3FlNZukJs(%a|q@mLYD12Nq4J=mM!c8R)@YGt1o za^!8WRK3DFn;ju2*gGkS+laQX+-CubvFoOnINe=n13kt!>>K=xL+HBK)9I^%Xvwm@Px z!Z9W{(e(YSIoz@Pxis-qIX%xA~%5y;g^lUpR1FBZ8=<^Iz6nDtkD=BX-^ge2(W)H_KDp zg~LyFKENEPn4rCQ46h@qXA8P&c9+GMUb+(_Rq75ez%}!a2h|He5a{=)rq_B zO}*W-eyl09_{P>rekU8RFk(r(23|Knu99rtU{_|ECrK9eTCmi(%23tU(6;bhXYT5e z8jzaQ-_bU*V&g=yKO#!@$d%K_UGm-;UmC<|SlN?~?p(NWPZ&=WY}u995I^+U_xSU1 zH>VpKFY-_Kq}aJ3@|Hl+7D#SUGt=8Kl%(4lhdy8pv+!vcneqj%JzKT$Z#LxI(|Gf3 zv!xn4!+S2h{rYgDpO?yt!(CWeWT}<$fZy3Ht7>f3!2N;d*7&u>V^(+;%aYIQxa|1P z+@>gro2L;p!sZna>R1vAE7}kLVM*B;jtFo`bFX=u)(-q+QQxgS2=33b_jue(;8hf- z*2;rfxc=FAlH-)I%L*3~nhCE8xJRPdOAi<5+)(n|b;juNo_OUyv`tGEcc`vl%L#+q zt~!I0?Jp?px%QRspK;s7U{?e0Ub(MZjoI|Jme2lNDTG21jQ}wPW$;jyEiDm zEeE~!o=n{8Q20!)FuZCa-`bP%lzljO_m+}lg3fYI6nx&TTLcX8%}u1+fcw*sDJP*=y%ENyvxeeXKbEq zufcF4M!F$q_#^aKvKCs*00wNgVSYI_x6y%uguNIqUszuN#H$O}^7ZAyOre<6?b=z* zLxVl--2=Df+I^Xz94*8y+mb#(9iDVJ8Z+X-V=|%iq9T|6#yP_$O}CBzdSzcSE;|b@ zJ?TDy|Mpn9cVhlf$AU_$+}ap$CmJ|caxlC8m;{I0bE+-1;ZusMB_f8-IPuEBNNc2< z1s6bWR*cQ(G0&L~q208D%z>c^ZZ1t#9l{(VS^^9?@4*rfBss?n2>AKYfn-R2T@EFY zhhlgtjMos#Yxq|`)2#=$@lcPX@w1j~-`?mzv}>q7E{DDNllI9)^Ii`6ts1su_9sV{ zpIYJI5vy3xa*B|c7_59Wv3VSFoI|4*3O?*>{gC615XH}^Qv*4B8e-)vqsxJ_{->~b`lSc!+?c$15>prCp8s-6e4ep|rf zLC#B6xkI$d*N6NP`$JHX{Xu@24PqsTe%Zy@8X?B$8(cG7H`W*ySBP@Y^nQ{Yz!o;d zfGu+VH!=D;MnB=Tzisy>F#0-1f5aU8fGBSHQzOn#PHN_WD{SF4+8`qL`zw6Vat_*X zBJ<!>#R5fca`zxpG9=3qKWcT^(UUu3n z`&_t{Cxr^Wtjh761;MmH|n$wQQwpc(hOWD4ISoCkTEKa+Yyna*( zBKSHrv&iLl&h#Bhnzp}Tk-l}2R&LEq-<8ozELmWnk`B0vICc0yKf z=Y6zibo}XcsV-z+b-pugip?yxFtL>k95Q&rPjs70Q}1|hwj3_S{O00>L~c0$-#pAq zUX^d$-?!-aoh4*nHP#}n^m6xz-4h>v(I2lZP}aEreJoH>-rI(2sq^d#AMc)AWa@w8 z*fZF@Q{_Q+)fD?I`8cr=(5^v^GE@exectfu`J*G#r4w$x{^FCayOVoz_sW?IcduG= z(dc#C&baf6-KXCLno{L0{tVuQbC6Nc9PBRGKr=|kf6OsO&ca$R4Xv6y&qqt1moyP$ zOZRV1#~hB)3tbm6zpZ5VetT^=SoSiXJsCY!u>!IJTHE9Z~SJkAH9k_kjdyX*TYp#G-RnE<1hUC?*Bo5m(;iZ{Q`o zt7Gv#dfL5A@1Tp=#?n(2b1;P2_gH~Ocs2>hTaACQO+~KmudIOky^zG!IvH|o_l2d% zP|ILM3Pw^9SyB3q*>TfD7nE0Dzv#HF6TM3obFbR7I)9Gm@Ee-TE?5QJ(Q-->j^cf? z-*c}Y;@5E5wgX>@q|aHt_JU>IclQj_S^~3$dzRPfJ$j5{Ycf|uZ5A7Iv9^>1eX_4k z!OS$gv?LLf{{?%V$K&IMH7(gb##ue0m|nOb(U$YN<~vT{TA$CjssbiTKId;Hbhdc= z3!K7F+9SQOpuZoNA~;KQ_K)1fe3aTkaf&UB8O)o3_JCh(%^qQo{7AZ(!^q%SRSV8@ zLSLbKrm5-Dnaq^rO2q`RC!1_7O*gk3Sdm>Wy9>I@DsitDVujpLw6CyiO~R3Flw7#5 zUCJkVV!g59<^`*g-a_7{hgpC=v`K<4xd$pZdutjX{|q&Sb^|y>4F!nJIW$scH<}FA zQFKjq8o~}yxjox^`qzoLQ)d%ZacP54bXjeJEq*Edzo+Ni`G#1dOUFU|Sh&2Pi_6I} zEOLa4(-ko~B_kOEx6T#T3zGIhq1%Sc8ZJAv>jVB8-h&*h@JX}%&5DCTY-@~9#-d=T zl-T1!;Dr)X)&H4b$2@2r;Ecv>6B^_c&e-a$C+@ux6ehD`MwKs*h9J zo3g3UU^wD-!SoVG`g%lOjp8tz6X4sG=S!lbEIuAl>eYN)ShsxqUs?kQfu{HoAyiYk z2489!Q(R45Kk$TEIBhS&!Ac%5Oq%EVLnwgUd${YR%4sEovl9+C&wdZb-yi9Hs(UV{ z9hl1m6Mh~68E%oxj>fTUaM^ z&&R^7g5*fPb8 zcM7-59FY(^CQ{XrpkTWwGz=MIt1=v!Ox3C`E}+%R2CJ~knM#DDliV4n6pqNEH685Ao^9i^iSbYF}x63LG`8hLF;W71&4QlS5zWs$;9>4Jyye z0gpW@&Dhl`CCHSLyVz+?zrXUof}_#av|f}|TV_}hKdaYEs4eBto{2>6cZCbmoQ%5L z4`13D_O;+zR`2z`v{U0IQ>@+0!SzdCq&ZX$vUao8h;5%x;n^UvWt@$o@q8aKN4PwO z3jbyo4A>A~b4q&iVnNANKC(wY8!I+=H4m;Ru z+bf^9!&l~N1e#!5Z{C#b?E1G*&q&3;$86GX1lkH#aqxUA9<^0)<EZS&Qtxs6w9%KnQ~W5D@!@$>dyKwJoy4sZdEe^(>^X(eVRS}IujpyMBG-oQ zc+sQ2_BVPal6G`vX!NQ6{Z5H%>b?YU@*IoiLHS=Z`4*!OWxI+ z+~H%_()?$4WFII-j`s?7<@5ZqWo;qeD~Y8umf;qeO}7@$0(s~+b9^##m@Dio*o$aJ zCbNRXG5WW`wk?apU>|oWcQM#kIX(7O{LjVyVbIG}KIDDEB<|QQhrZP6!)p-G!`Y-5 zB<-<9C`jH8V;RAMYC5@0Evm{+^Oh1B3`}B}KXKndelpw}mmR~))TwNXE%Lj>e9e>f z+to;euP_*nFAW~MaN8}*nil$k_CR4U+m&rv={w}e1{0qB-0R0)#!tkC%f86K#NOrV ziu`c6m7C|*Ja%o78uFJjvDWCIfBLqQ*FSME<#)AsZ0>Y>CO+J>*njQC?aRi}*~L3( zJRYdLCd>%8Sc({rgp=*jvvFv zef~hXyHVDHnZy@#m#=c{|JOUIg5f`eTsm=Et{1+ru9ilR#^NDW-+R>9M=Cp0PXD}5 zu@c*n^z{3^Eg?Dd`~Swj$nQyC>T*Of(VIQ!&1HYu+eq^fXDMvmV)GGe1lb$4>DVpI zeR3OV9eeZ0)l7JGZdt?n++Dek-y?KlHo~u9t{>=};qR+1(pW;6&H^dbt<2eni6>Q; zzBw5t4V|ro+;q5<(UkP=OgI@0W_G7lhtMslnnRW1X)885WPk5=S~DjVf{A!2y`#bA z%an!ANVw(77Fo|U#-fdxkY5fjQmytte&s3mpEj{<$KUQ=T}-IB?6fefv}8)5NHL?? zIxg-AGNqlx;(3_gj((hpeym|<*||^X1a$!o{?lzY&MNB5X}Ixzlx2n2%x^ed5T4lT^-3O>W}aIj%VT66VAqTLL%s|pGyOW}n{RZ5?l zDHk%QrtNM9*>77Amg9+BD4a{WI@WjiL*?9F#3f2*qTy^JYzr?`rDRKR04ertqait# zEOca(&W?2*{y-^rT28Vhhvy{`7k-{9MT>w2Xrr*$a-Z;1!r&X2OHEA3Z5IuUonOfLgy#u^Z(uGnF^d-9`VrA-;Qlw46y6~W zzJa-%FzQ2lPFkL{3$>V~e3^R`d5I`yDPIQe3c}$v>tJUwmk4`sQe{P;Z?r$ecX7WG zzD!tr1AC=O^UOUbZ0ZYFX|g_SIHC`KLpbb^wfON7?)#Rn3%@2>_y(=3Obo8gn{mZM zzXTy0`}b1~wD9?KVn7YU;ZJ)m>Q#Nei@Nhiz{(Jd5Ew}N%Bn-ZRxz@zsQG{7nBgXhFlMcRtxz5B` zYZxCNLJU!(&G-i9dJ}^iD<+*H=o}CdR2RO1VPUYDtT=2{S1bnp#2pEv3Fop`lFbV8uB+N6VTU~V$$cxq$X zj5a@O$|b&mL3l}ObFkLtM(!zI7w)9?;2W4Pn3$EzaZ8@nH9L#CerU>%h3dM+#H?MB zaT@KBfLSm6i0I%ObiPO!MABO^FD=jIAb`wXE4)Cs1j+2Jgu{t{Z9KeuKliHeEMf2s zTDO_?wdIpxqdo=otrUJp7<>bByGdtZYn6{WFx@5w-@tsy#Ef@TF>%CneZp)HzJa;J z#7r%zVv_s-ubOo54a}V;roT33h)-+zxrs4i)!t=d;88MUX!Z^6ZOcn0#=^Y~I$t(1 zh%{%)8Gg$>X3l?u&fQgvy^3M|I{k=#{jG_~d#f0M@8FXr9eks%drZuh#wtcZCW1eh ze8x90UokNwwRxBVe_k{BgKuEIN*KhN4OU}Od(oaP=GfyKn6H_1Lbcou4ctBaxG9VH z2IlJ~osno&roDU{_p=%u7K8U2CT4-xVXn17wAm*-N8^rfVD2^R>e8$I;^0xdMEC_^ z@D0p2O-w_L&o1hhAwT#A<~|eSuFZK1Wc9n%@giCMRuxmFgVX;`e!7NX@qWK;Vpc}X z_E1KMADaC#=Fj~mW_^u6_&axXjSl1C116>~SM8Svm8u^f^ZMj zg>PWKYhskzT()q3&n@B*JsUB&@eR!POpIK^M1*CQt4$fhH!y!^V)QDXEn(~#n$S;l z@D0o(gi#*sMsNyaP0;xs%=h0C2H(JZ-^Adph}Xbqh&%m`NeAD+JZjdJs`XvQxc8cS zHNJs)%%n5in>N>98|b`k(!n<{KQJ-5WY}zv6LRP=>EIif$IZIhgH_r1i2E91K^y(D za9;!F3Bsrkjh7HRp|);HptaEK8@_>i(xlZ<n!b)PH;f^OV`9PNZ2k`fuUB zZaLS);2W5yO-#0iv7xRiAMp*$56!x=wK21!t{ct1;TxD|OiaEei#$KXy(KK5_TU?s zADNi#wY7{#T}y;96T|2{YhpTU{bFMuJ7VmgBMj5`tMTUr?mn(Vm`8N*4LZ-8n4z9V z)#$sQ`zE(Q7$gk7fqB8i^mf24M*FIae!YeFzD!>64a|#Xdk`|-lzRu?Z~4Bkh%opD z=Eo*xF3*er`M6&=*=P^R$4e##8KF%&0{S&ym~Ud3O#i)!>8kOWgG|STF2dj&n3qiq zLhqY&6v*pSCI;WY{DX<{*4oVYEaPq+>leO(`H6`csEvifeS^zZ>2Tiw=BFm6RI3YN zFf6|l28a&6fqBKmw1ledadE%2ydnIKF!%=MXC@|7lZ|2I$k}bKLHGvdRTHDs_!9t~ zw@o_u2Il7`rclcTkQXf6G-CR*e&HLK*Gx>X#>0=-^}h(i;sGGpCXKs?`#v&UzD{)T z4LYxzm^Q!HW?*K(pF2e4*FatP2IdX3uB5BlchXD5V+dpDC2yK^){nVNI>y>z)Mcz4 zzaWhIPwkj#BG+tU z@D0o_P0ZTr9%bpRJY_kESD1+FkGw==2~k{s(-NY1^o_GWv)qRa^1vWR6gU1{gJHx;dB8+JX5vHN?87^r#i(wiV29^*ob(Rox`&8u-?oqtC2Zr(tzzPE9 zV^$E+3ss)veuX%#z=jw-SV2UuW(9#BYx{<}w!;Df#j&i_lz#(JnyPic@}Y9Fd{C~=K5h9T-iM$ZIhn}np>nc%P@bg*ki<5?!1Mxq0I zhcI>aPUUZrkAm7t`6^)T5Q3~7w513d2WG#)+FE2%4oe5+HA{zhu2XvnqcaRF9m1HF z4q=?gi?M9>H;j(+H^9KsG3sF}JL*Zz`nc^zJt;;5Ru0iHtsHY~pXF8?G(wC9tQ?|2 zRt{)1qpi;x^484w4hx59m=+GvXsoQp>23#W2gxlg9Kx6u4#UjOqOCtPRE{x8^BOL5Lh-ugDe}+&{5uPl$$CM#fH=}gS(}PA&hCQ5GIJW`mpj~EQ5^KuvVxZvQ|(JS9yWE1Tnr)j^O@esZcpt zDkyhRUq}{QtQ=Mfm6Mf%atq|`yLDp$3&kim=Gg29l_&V=x^h@2R8H0j#_}-u@I#{y zG?uVTsGKYlqW>&+b=~h_l~6fZB`Ejc_ZL_;v-W#f`(Tk!IawqqUs{=lES=8Em$GtL zBUDb-2+F-!i~h@4i)b8RiBLINA}EjG_g^u7PwO(Q5Gp4t1m(%uv$z60)SyqY3=4$H z$pS&SrE(m%sABk$ER{>p0@xpfsIxz2U$b0o$P3j0>w^$veSk(7YqvK0BUVTFN1y=9 z1LZZ#gT`5}JdHVdfMIA(!tx-DX?YMvLmr`b3_2R41IvTzAVW|vS&NVP3Td*_;V_F(! ze_p8a&Bpo+OM~hmO9S=TQO}J=Ur3*Tl|kiXWuTm|JkPx~`wy%g{6FCLurR2cEDS1t zfmaWx~3kaq|m4y%I7 z$*MrPfU&=S_Cr=Ke28*b6jV+Y1(iR;&7Y;*k^-}|VNFmuSraJdAfxfwC)w}GqK73x zFT3U?Bi^#2E?yhUM#4k!VHuH$V$k0%7XTNPzidr_|*AGqku~1#GAP7@;Mgq)w(KrK?2m=cOm^urBWHAAmy;d~*C-_Ev zup)pv;*3O?;a(O0*&NpyU~V((t2-k>eJe%dOi!WuU`Y_2x-$}Bx=oBu7+4d8sXHS< z-o_`)_QZ)tuqX&qcSa&?;8l}OlIXyyAWYpEiLk-)a}#6aql9Han7T6(fwR11Vk~&~ zF0(Td!qlCSpv{k&^WUHY3&X(Fd|BuVclr_if|Ws-x-$}C6`wTe2*N7V1xtf4b!Q~P zD((*^pB2Ku+8|8b8439Fn#rFO@dp+MFg1$cZwwPm&Z`1{ggXoaOLF3-QKf{lk zvWRbBV093kx-$}CCHJ!$9hPSkRtI6~&PdQ^pJ@2OgVbhN9aLA{8HuooyF@%p^1}(6 zKnG_egsD3tp?(?i<7X$KV8F20|P6BFm-1n z;Li`uei`!zmIz_$&Paf{x<-fb5Y`A`>dr`jxj=l6`sE=$!y+L}-5Cl0w&mwmo-j*! zb`A%tgfMkyBv|kM%UtigwBEroAxzyF3Fv&qq!S@Juuce5cSa&2nxbf&=O;x8b-_X* zOx+m?|8LwPyd7eGMGOCLz`#Z!Or4D)o@BY&l(C3-5-_k*2vc`Pf;~eMXNXRi_6%4m zz|<@iT7Qvi&Z-bb7uendONB6XXC%li`;JLRqujEvRH&}HGZKt@uen#tv_`>NAv$$u zB%t%QNe5Y;Q5UQg!qlCSKn^`79Vf{ltQD%O?u>+g4*6bdr_o_OTS=n-5H6n7WtA_6NV$1hGj#Tx-$~U>r*C1A$f&$LzucV67X5ZeL*@S zp?<-_AxzyF3I76@to1poe>xPj=XLogn!?%+gyW&`S(#5EFQwtosodf+a{d=(Sg-Nn7T6(G09Df=TcpHF$p@bddr{`N4RDali(i#29^+E%rg?)sY7l+;Ueon!~#aX&$+vS?3prq8%!_!kTd|d z-Ek`pS6Xp<+I*J7O6hJZyVT9X#2Np<4PQisy>9aM#;hI=>S1es{fP$@B`6&XhGO1p zQyVvCa-lCceODlT;KqET+L-mWo*zB+73;x${e@n8$di|QFWUU79dGxq zi$vWaO$)iB5&Mb~_o|J(ugx{vIN6S*al4jp&fWyHRdW8)*6ck{1W9d=FRE8Sy#HecEsIGS(SvaNH+ za|a6vN7T0SjKRhp;(0i+@%+)->$zS{>e!SB>if9oSuAW1g5id9JyX`chjHqLtkL^cFQT5(lYnZD zNAT3(I$s^PDwn}hhV*Xloi4fTh1Gj*pSj@r&Rl$9U&C~0U~LhPyEcr6u5$X!ER{U} zYisior{wkI_g}GpYS&DG;uSR1=m&TBh<@y)e!$d3KiYyu zKls`AXFn1PIKx~5|ElufhUN1m$QxpxVur}oxEOR7A1%g{V|EdoJ&^e%Ko1^h;AT5= zHwRdBZ6niS4_g6ACNIPbfL$Ec*Ab|1y zorRYeLG%H(#<-1p%H3U=25z)C(+4K#(Qr;x-MDKk3S7eyuifD-M+%QNFF0lIMBA|| zysFhPbKrz8Ooy**YE5@`W&9_O7Sjv+2bVoPjHmm&&tY@vb|;JS=dpo}a#rNNJJIO1 z3SwAy`&_nI%T&*j#z;dF*b2%nRkN3kcyVby%rT=T@aKrPux`SIJS zpDo$GW!qJwFCN|egu*{w=~%FC|9v~I{b3htKjm5%#)aE0yTQW- zdW&i9ET1ScSf^PQYI9MCAhG#nK6XR^#@hPLF#054up`CY z)qb+IL5-=%j-}cWV#=<&Y}KX4`{#M4s%l(Y=BzR;oQ!~HBg=_rBtuh}hrh)*lOe#E=0^L&FTK;6>KK{@2hV(!y5?e z;iS)f!MHJ3d)AgV>E3d*>69f4Gu<9-;#NIvcVckOx!GZB3icf+_O$dEGq*RlyCD)0 zBf8tGs#!fEhKG%@6@Q7by%Fv1VlgPuE+etHjyh>XBq7vs$!cdILmVUOs|exYk^>CddUO{Xr|n2DA>j%7OBJrVBqLVN)hUaZ7T zYnSY$5<3E~%N{}IRJ~{t6-SRR6(^dji@4J*^L(_jF2Kp{9{-=8+uB7O?vG0E1V zIel0r?qMZI0MNg1%60T0QW4lwV*-aH7T(&F0vrDHUIyOo6>g{Q)px;ai$WKBd>L$w?cI42O z*Il)K;=1v2AQx&1$6FFUJTr;%ch7C79~+9pcW(aH^&93lw=B=M=0oYBw#k;-J|Om^ zjn|-!oBeDquuym2gz|~16Vt5_mX#FrPXK)KI#`pQc3b#A%@Rjin+8;SR#02J?TsQynJ>gJOX?I8t!O1z2N9k+X?*bWmI+gq>n*P7s?PeORkx2zRcXO}`6XUCsr$m-hLOyo%0qvF;`T8~!=!Ir)9Q}p zbB@Iu7HDjKx58Gu68$nxlP#T$fm8X7^3Rj zl4wswbcfYyi%G_U>ho8!+b-em>oQi|m@_iU@s-bQ+n%+@N^{bOH4`}?-xVjoV-{PU z@*$Y<@DI!i6 z&p*C9gSi0X!t2o^g0{S?a!u}5-*schbC1iOQ15$e6VV;9Sj}{*YI<&aSWVdqR_^@m z@%Es>vX1V`n5afn(ZSX?-U1y(?1YZuM0Psm!MM+ev0jU&Zed;n0bT^GxClRCcpvzw zPrY&J%=OQo_Pt8w(s%F3Ek3Y(!O8O)CO)@$<$-?T$vw}Xf7-LxuI23CzxwxwDvMU# za>=TtXZ_8RZTIing|j+y9&Z=!#mzG5{vk8}!2e_$1G_1Px#8V`8f_X1y1?D~KiJT+ z4-QO!;nL|Xm(L5OHQmv?=%gvH3vohV3@rXX+ST;bywjJiIJC;+bm2+6cG}uwH}Lid z+epCYiX{I8d}f(d5$CW2dA_ihbXZ_rB19*mSy`E@hgoCH+RWy&Oti$KxR0ek=lOGg zz`0ZN_bf)PRH5+Xp2x2J?%Kf(oqA^9m&Z@3{KqdU-#X{y(fKpS_vdb@JXxH&WO~U- zec{H5bC)kXbwNgWvhwdgn7sMWo`|3-yzj)`)#V1n)=tbXuGo5gbD`9{aLeZN$FKSQ zWfkB0FJHEL<-u#Gr*1rV+4vb(l1=x3xKFsta-wA~`f$n)pV0?eO`!H0YKWoCvIy9j zBCqpoKl`7IEf#Ho#c?$3Y(HN<@U7!kh5hZj#@A%L&5@A$*~0Ez=dG>fKp+J2c)0OD z7;&uWH}71qboZ{sse*9V-<-DQ{D9On6Bl*ZE?5t6dW$&FvWaKN12} zuUxh4)TIk%GASnO6SME~4`AH;AnP$hcSEpiX7hY|GLXSdwpkc;_v`lNwS~g|%yQsr*nm z-O-wg&A;H(^%wNEhQiwtna-ikss7YpzSSStc<-w7hGp>(kb?S_QHU}-TdSuB_T&ERrRd%}0QTbr~%w3KwVjhDEG+cu5ZOPzj4$kh&Zu(dqE9g+ zGIB{Qez`YNj`#=t{%aDcJ3XNs0w6?Le|EF}EIp2&s@#~{J=CHnlRbq+KJIg0$5 zamkhKu6%yMV(x@u_gr7_>i!gnoY4o#hd%5_A8>mCGWYT3F`byAzk5GH9mY^VK|+{& z1n?8cGpRG#1P>^pAIU8nN3ep-2m~Zga6M3p^8CHomLvPP+E;X)ol0Ekix{1pL?``` za>Mw{_*GiW8%_s2nNlFK(P~r0jgnvUx&u)J1oV4?u84A_qd3!ZB)vhe_iV)%vYjEh zg+k0x&lY3?Y^!{J$pm*oQ%^l#yeC~!xiLA@-4o6oJ1~wYf_9|)dB?XP-gSh z__@#JmQFeAskt&eh=t>I#JLmPjl@?U=JF``x(9q+K>1=?kUEMvH;+_ySgzuaa~!*o zlVe(9oo|@ADDZQ@S^i^V{W{VYfIWDWS}wjlE(i_P#Olg_!Xw8{G@ ze~r>*FW6(c*EVNT$0bKg%isQOYM!lbzPLT}#~-WwCQK zdW1_kmC?I{zhyGXgLfKux0A^v=_W=*9i|61^z{F66N%vZpnS z)U#B#W&26K#*pvuR^d@vds>*?o|`kQASv1EAsjR_7_n>@5g=3){(_=CArY)GB{6-G zx|Dw;x716%%5h(BWqoJEIeMrZ>GyKChV4!72Qx8uM6ubuT5HxBNJ_f~xXH+;^;l50 zs+GuC+rjlMo@hQ7`?;=n1swT>5k8tiR+DfmGIMGpuIn+1JHe^HZ)Q3(!c6G>EeH%vDYwEBz#2z>jYv;}Q4}@*!{~vbd zM08cIzYcrGiN~Zgu^!vXKVHMyaWFMU_ss2^(vIqzRH8eNf2f9aa`770F;CsKX*$S) zBZxTAs`amxr~Av)4i~qp9=~_nh6EDy*6^0LSsUMg7ZzxD8#^m|p4)5*f**E}Qyv^k zYvx2qT_=Xojtj)}nKumU@Q!?Jl0f($Q#9zYMxS1nxcD{z=?lSmAI2A9$ z=I9N@aAHXBSXjOH8~rDo6ZxI1IQ5}Um`>IzuPh9kuGO7alw}O*Hd^SR7Sg#I!yT;Q z3X0A08qusG>)V*CwK1=#HQ*(C+!*%;#zVX1 zJ*qcAE$qRl8+|SS=P=q=Fl2Osa6#bIhg$Oy^raeigkdkO!`islkHXeu6!{B2;0s27 zh_1@D)?s^F8qlYDx;dk*gtc2fsKfTPW}-D2CAvLE{|W2F%Lv8+)?UbHOD;@IsLCk# z@CSa}XfyFa;jSZmocORNR+S}&`*9u4#%GSeRmX|pPBr>P^i*zt9d1*$DoYG^fzeLF zIk}BBochq}XuT|PkmKb>-3ZPH8R2dxES$2C_vP7Y-MQHh#2bwHlLJnU3JdWLLz1I} zTQ?9l#+2rZR<=NskGSQ0Qqaf_+MxAiOqHN$!LQy*E>DcJ~3RU!B4`e+#uC& z;06uJH|cfp&sWzDMd$9R!!;Y{>?W?2|3eMu!3!KF9|palXmxzp+VOoM$#68+gNV<2 z3U~=e8RUN5%F)=|yInSiZ!-7^Y+lheGK$7()Q;0&%%s}fyvg7t;T*f>aD5?!vZ}*< z+2{+^t#e^&qd~D;djp@Cl@O=#>1rFn?<ZLmaCT?74Vf`SS zkGsKgJ~(L9+nCbo*7MVh_EA4n?rfsxqV`P|s_k=NJTk_5;b7xo{{!Q*v7!!Rt*G)j zNNY6BgCLFD>vOnvBl1pd+|u>q_U;_6FHk>j5;s#n9+s_E$bagq=A# zd5*iwVgX0IggmP(&+UV}tBON?tQ;9I19-(*YPnfKH}c3 z(!oCd9P}DipMe{U*42kN2KK7_D*rEg?*d92_zxGki;ZhMMXu$iY-;zVv7}(R%%hHqEbtHu%)MHX~mXSthB|7r+7+_ zv_(rTCjaluvv=4;)SkER=l$PK^v5&LtTk(`Su?X{p4rbO$%v5!=ymu?8DUQURdZEm zaO^dHtlQn0tMZcrdNZkKwWC+XRP;Wl^*Vf`G6H&)ycB0V=-3-;Bn8PEGbqrHRR4`| z#Nx3)3IBZR@QxamK|cxR{hWERufN9_O?`5iqo#}< zJ~%Bd%yi}`XK&Qi#NJJkTu;1juIKhIb&cx3e}WwvcHW(vN5+24&;LQcWUIHG`gcv# zPdr!n{a-Je^@b~#5atI<*Rz|d_Ewio5AnFnkortxtp5f8{d@N32l3k7DF5&O-cWah zz5(DmZUD&Yy8$4>e*?hpXAXUJk>0JnxB4mbV)M$K^VPQOtdN+9l<=$&<2$Kwk6*e2 zATDEH;K%Ii<07tj2SA#$yBp}AZqujw_KmZCx;8toj#TTNxz1S9V=mmt4&s}e5_l(+;+yHjTFn>1)1s;-j=TlFz}%wv zTAgbP%@^&T?-if-`9+!P!XnTniuYH}c+l6yHCb^`Pd9 zlGpn9ywA@Z9K0U%^H8hB|Q)i;WPf|%)gp1lItnuONVbWn$){P?ccQgdgP<@Ajo)_cZlfoni`)Vhqj4T9#z9&@mvpUvg@MQ1(cd3Ms+ zoSrT7J1 z#Upme0`J#yuIGa4r^YS2kA2UHsd4Lb=q@EcL({o^)$e~ju7l`mM7y6E-}jt@7yrgN zbl&^o4)JMvdEfi<^mupZyx+kcqQ*Jp`~0KE+F?UK^X*e~HOAb}6b9Pg8AGNXBf5VK zhVFg7bxB=HbYT7P5uU6*p76kXMg2U#RPChYWNRM1pX2h!IzP{YeLO=n&*~M|^6TE^ zJooqUPv@J>eKF?&Q~N>3i+oL=m>%H@rtA7? zy84xrw;#u=$_3^GJ*IbN#q>nO?@KrI6G?9Vf%kEgF+Iq|`% zi?yXj?Q-iRKwsl$F-kulzi^&YDN<=+a-*hEj09B3J*>U&ibHqUQ4H zj<0n+)vt!0XRl5Vv_*gVL_a;y77kta2VH)Wrn64*_Xmfr?b7tg(7n&sr3JTzGpB1` z>M=e=%c#x{=-^cSVRM*uaU{?m2EuWd2=n3>OKhM|t##x2tQF|u5`YSNM zvStmQU#DvxwT&7V5okj_e>(mtQhjL#JnV_a1#ENZy8fD8sp+MCebJ#mrtHx4DoxK1 z4_rGrbp6f=O`oah6Zv7OZ#JR@^>o!&6>j6{y zN0*rek#E28YWzN#(6w(geYU2LONsOZ)3wc-K1b6Fc=p3j=Xx}FO;Drh<5S}zg3IeV z>GE?meNslCPI^rc)aPq8eK_A@cFH?+ZJ+MT^E5qYV8A|yuI|4aEy z)34U_p@RZzUA^WAiXZbeeNJvbKd0&se_o^g8IM1QntJ^4J|=g_0*I`U7=4%OJ1EHC z3pIUYbg;h%1^IiCrjJVs)JI+42d{BHqv_+5gZ-Ip-RkrKu7M)u0lCP!^K+W7AUv?{ z%;+6wE_LQg?e`^`zN9qQX%}a$KZP1zhbGTuPq3?D^K;S!$EO* znWm3P42%U;kKpy}a!oHyyS)5w`ucK%rcb0NJAUL;{o(gU?RV!?oj2)oCnoDSp<~Gk z7(CAwqy+S<{fZQ){+iyb>BR#B`knH+Uut@brjN}G)c=ycm0BPDi2VvZj&wh|9tQ98 z`B}-dY#=^3JeT@vtL7;l&MlOIIOC7aC;Iy1DtL&^WhHr8>G8aHLWLu>hpS{g8no7H zBir+A%d)`rhhFOi`M+K3+O#2uXcVwVwPEmjutW3QxhX$|kH+<_iPiP@rSW-#=DGX! zBBD-!N3Rx^32KdTr51`_ejp-%<12rTKil=3g=-FovCZ zO1HJ{8yg&Xsev)<&~-a&`i+{ta8O_jJM>?vHrDiw(7n%pX0TeU1$^r8=zgGiZqhtW z1*#MD@z8$LgZl8zTF)Xr!0U_=y`FZ)uwHN9qUm*geb|{xb-U=fbdy8R5A<<|uIFP- zzZJUo`Kz*m=VQI5cG}*j;vFkq`e9i{V4OL09e*|bc1<6j6o|hLUAL{KZ`SmgIRP7V z90`h3cR=SUJ?9V{(zM;#D^|ZY2nykMs&K>5LzS*JfHqny&pO2qEq-gw*^4irkafz18hb#Ugu;l+8A7Th>BJH0W_zR}F`rKDXmdCv9mQQpic8AJNXiB!+#I6ii6 z{&j8~!rOa&d5vk=Q#UM_lvg|)IYTn0WQIn^Uq5H^HEAinEaZ5tSB)3#SNN_F-*XD; zL)>bkTZ{W{GTqgY36U{tkF1M{OpFMtTQk?X$Tu>pZ{0L2A#M2J(OfdH^<OE%^z#2?blQCwF01oV{;MeSTO}e9z46?COj0QDON_BT|sd zJFd-Du6Rj7F3*nPYyX2BiGjzw^lgUhf7eDv-Itm2_{gGxsjjHlnM;#WpH9h`XT6gg z6Ze44Ms!?MSi@4Y6qz^Lm1dLuBZ)w!Tgx2eczlqSz1P^qmy$AiZU~D_wto~II%HB* zXzEpzyTuMSN4t`A|6xIdEg9-TNdJg(Dm!ZALC@T(yJJEVBctzbTHX*7nP8tCTC{L@ zdcxc-w_THvu6$)z@(!(`5{iD8(jTkUE9ILOPn)`As%vOjLE*G%h3dkV-xAX89J9_P zpVIfgyP&LW!IYlg>w`;3PtnOKEG11DXOA+zz`MG-1^MuPeyS96U02V5aNtYp2a>X)2#Ku5iYbVZ*0R&^DRoV&1J4t?&IF5bOND5oqy1 z5Onp=FjnR!guB;et6o(0RL>#RrvhhoAyG2ny)xaWeo+;4@=K?WPPE>nkKLsEg44(J z*WvX2d3c#-p$WNlL*wiN6Ryn-mMq9-CXVfDu`{rF`iawMl@isn&}| zv+H&|9f&jj0j=(X)1Tu}_kqSIo*h1V%&?hDpRCJ@iYQ;4WiK5yctAnN{3TzD4~eiM z7Zndy7ud)Xz7g@ht4@C}?Fw-VsgqnfbOp^7CZB_}y(W{zel=O-t(!A0v@3}HY4jxe0J#WcZEaiuY z1;vBaW`vgcC36w)D^al^%!vg~izf!|)AYxJNTW7=)WY19$5RH+wxVK3%+E=EJSCxI znDwQMh`BYRM#n{kPnk4g=rCl~_KY)@_TH|2o2chYH4yy$SKWA~yvxsZxZw53gt+V^ zBO)X&bK>C9<#uFxQkAFYVorKwRAiiINZz2u)w!dJl17IYBR8J8=DdATBKz0hfB*Ku z=pEDJQW8Vwghg6EiheyhVQ6;r#RRo8&G!ebZy4e)%NvMj6PiNbLh0O$x?o_WcTQ+x zN?gSb>qi5l6D~$)4^4=EU6=J*_Zz>kf1~{#roRyw=)*h|#?Ww{5#TBQ-`zVPHNw5P zAfM29!0?17XWScYkG^X_cvR#a6H2qQ|2Qx#G-SYCQP|_P9ybf^RXVm`9)tTlrTPjn zI43gd3xiYlr20mt6NB#=obq%kA^7p+nAjV~W@g4m#ojh%s?(ND)>-pLyY}N^@DAfh zXzbwhp31P;RJ%4hG;?@VXu>#bX|ne5{;kKgEm3;zRWaCkHcLGZ!SAG_i*eJw$ojD* z4Uv2-bU;?=wN2NSW(|l;vv0{BJ2pFdz=*W+$-{9c5Ry1SZ`in8!eo?|`D^K5d z6`eVF$So6w=S`fLH++Jo46IpYhw)5Q6v*(b!)O!wYRjh#~_uc#{-nv*+r z_Hg@bUFnpjYbTbD%$+(uYxp>|8>f8iw406@+J?Zq!aeTHn>upvt`n^&1ge|FIv|1^xc6MM?Sn3va69zK>XN~(mYuvtX0Xp6J|Fvt};J*C7a=qns z-EG#o-_Uc1{yuh`{zx8S-FY2{UgkP;o_BI?)}%qxN?W?ptXMnxTN~S-b}ufh$sH0u zJvt-rhGk)qgV#Q?mc11A?99#P%j$YOUbr7%le!y0)Wpip%ZjD5^v$Je=LKGMU4ECt z%kSAkJX{mQt9~_5JLuWTm+o-nWl_E7U6H)+ZVGQ?so~0bR#in=ta|r|5u@I4!LuX! zS!d@;!+$%pT2aI}&)W5o34@oY{jZhm8++b!l;FJf3?xLsiq(#EHz8flEadUjL#wj0s3F!Xl3tP z))!nmWin3>d7hn9J~~$2C!;^rsNQg42BIKK1~tL(cy!-GL);mzZi~?qqrn%hA?tM& z&q%4;YYgKX5z~BQ2KXYjH+9}TwK6@$bh|wF)`k^-eq2a~r#N>=_4L+^NtMgT*2J(eX`_Y|k50elrU>q$2#<(0 z<2(ho8UrjVePBZI6ygX)9NGAVMY4s#u0Z#$& zSS7WJb9|y-zQO}3A?ne}41+KC+EjgTCJ$K*HixGsSfTgvO=JFc@}=|an(>()yD%)> zFhip}38tZ5H4|f34gYp1Z-_CCs8Oy+w-xICogL}sO){}5(XN=NxG+`^aiM$;#Yhh; zv^|;QYX%MHr3hi3kPuH;&-@WFsWr7-3x+0*y?Rnwp%EI!^SGMb$IR(pi|Sc1;VH3RZ)`$zXjoL*bYoYH#}m^tKQ<$g zSM|7DE>BdbLylGLtLkO9Q?CfVkKlQ>B!j)xtl?Su6u%h3QEI~CUJ9oJ=7nKiMcX~TQodR$k^o|r>@im1-K+!2Kn2YtjF zcSX9dYQ6K(?DCHCVN<71hG~R(=hGXqQbO$GL&uLW{^Hav+C8WwJg8zR{oT@YcZkQz z-_V`^nK>i0Ya-LRrBv^Os{O-H`@Q~S>5PwMz45O5?LywXv;_av&MKdP|FSdH1({yp zJ1g=e9o&)<)r0Q%kwu{MjIm9Rh~))T`r1$}33;l7AQ3x;x9r&L?eQLH{ryw5cGX{Y zSDrkZU62ssnzm}(%CF46`O&%&b=mO)`G&H`%!nH9&R%uz)<50*XwP51{;(1ANYmf0 zN(oPE_`>&QKiBi}!5fbm-Yi~FesuHif%4Hg_U+tr#onxzLq8st?Egj=axUZB?B{d#VEufoKl8cg zDcf4B-zAkAs;*xQD1IF>{EeFOt-A(XL;{ z#U_qP%%3!QWMci`nVl&M>Y}1XkMDZ)n&of2x@SdNd0~FJFEunRE+l{B+_B3m=hv>E zUei82B_uS=v!KcR_RSFq;j!K*UtC)D!pXk8xXi?1*NkYoZJ9eFBtEsYJ3lHV$(=Q` zxoT(XlA=NRcUOid`0^V{$JPzLZ)HSLsK=8!ZDieT6Gt`P^_`}1D`$@wnvv)66iuzT zs&vuHMWxrz8C(^^tmUambS1eqJR6@JUp^#l@?zeNv}Ur$ojW}{IXTk9izA*`TpVgy zE^kTcNOQ`J#!-WF3WI!pliY>RH{YDA;;u{W5VJSr*4I!h3oh*wDSvRY1=(*kdp+nqAJy=MCQ+WD2s$IjiD z9}<@qn(8ahFDy@6vFFt{mS6K|*Z9#|I2Nf*| z&l_1<;&oY8Xz}7F5l@4%BMWL0rw`>co|9%ZPbsTJ`Y-nEcph0{7(Q~e|Y5!ecT6llOH*=Tw{4lchWY0bA z2R`WeQDWYV&upwL``m&Dox?e6*G!@GKV7DW1@-9TXNH?j4sB5)~5` z>yB`FL%d-jDZcq?8TkKK-KKhT*?{cj* zR=R&8+2rz2(dF&JOn0ea{QJaeGrqu>kdi!L*20O)#tiX!L$VU%hr|v{Nf{J3CeP)H zat|MpGB~R6>IoS$-L5p%G9wa`28Mfm8S#VXyY%AJki}NB`HDSU?>p+fRbz~rZ9`mc z723y|pHCe!cu>!j@uPFI?BP5PoHNnLc15LU&9Z4y>XvUjZd_-E>bjARaeKIbhdOYb zr0-q)L^^$<)|ziUX}ziS>)(Ei;GdpGSq+5)igG_3o1HjesP$%eQpU`P`;N>uNP@SVOC4*wqrmS-hBU-&mMfIxQj9 z8#8E9Zr1p$@ezfQ33Z>lp=`|yD;HV=!bW@3ZSRnb52u>zv*SjO&#f93JuoYxu=Ih% zq?|c-EnM})Lhir&TknTPx@(47%W}6)j`6GB*R5S~ZdWqiX2)bqRz_-KY}lpyT9V-} zFdG$rvf5vX@}Bo|d!Nq^JmrhrSJjuE&71vl;X51--(aMApC6yz_pEL2KE>x+8l?X| z>wupV{Z|!SNx$_uzy2Iglz5GF_|<)--sgw#8neFVj2#;sUR5_wRPSNUt9F*X&riq* z;^ke$7tA>GX(gBMgt>XIqUvpvh6V0@PT+a^TUmcYPoUmhaY{^x%r~9;l2*>h(ofFu z8ZLO3Ncg9GdUm>!%wI@?^V?{mTdg^= z)i_tHM70`Xs?p2QxS$^f?fmQRS#Zbr^um(D(M5|YmKRN)TT)m)@9dq6r#*3VYS^f; zvocE;ghrMYOf>#bJ!*7$d0~mV=~DiW>*KpWuiN45Pk!RP7*?0FU${hX_qm=6-u7ed z_#!f1`!pH$L|ynp?-gbIKg15kN#9?{(=NkRv9#Rjqk0VOpZ(bfuZt>u&kXZuwxcs2 zOHv7r$vrRsw4wGSBMPK^YY){j5^@Y*QM1zp4HYo-bKfs@icf$ zW4^ggR`NBt`BsHYcc)67dz#EQT4kjg;u&OH&Q{leCw{>7p*j8yd{G< z=9?R+(_!9pv;f_sCCYfc_aD@4psK6;W@M6Yy-m5#i<|e!<(r4$YnI{W1C)E2-)|QQ zuiS(7u%xK8)h#*ZKV+0qCO2@b#D}%=56AcRQRemE$ZSpfCbGUNqwqzoD?=JotDAFs z-!tF9@A%W)D1~N|ETP@9@cm?aAL)msmh)W2uYEc~;_>N65R$BYT!4S$gMokJgMsep z_%{OoX7-0=%3|$z<>O!|AJZ20O6p%BiB|$T^YLvwNDPMZ?mfXEF4AqdUWq={^KeC@vqJiU#Rw=pSi|SE|T-xd%vstAN?twKFBY&GLZhP zc%nYx7zsU6wWIQ7|KnwOw6pTFYU|)*h)F;AgXA-|;_2V<{g3!KlRlsDaYx4DV8%n# zM~?Ix)pn}?{4+;2j;Xt)#xY|_AE|dXV=Auy(X7I6{(1!Dt1;y5miW*@iRaGvn2?DQ z@4nyhZODfb5i(CA-2U;R`c0r6`pXH(`?#ah)4$I#=G0hA^!JVD`}&B#Edx3NMF?ux@&j$>LO^Zh`bskVAHPodO!tmm}xSZ@ayOZr{#6Vgkesk>a%Gk)qhAN5Sr zjX?dW`;1kdq74A*_NM?JN&TnFsz1d$upg8eYTHNYS4VE{kSL&JdO^UpNu(80L8&@Y zQ2lfv=FerUYaLHf&*=S)dqeLB<^hhWjGuStZ;wh2UtWD0`bFlx96M8TT+LEW%q+Bj zF5}!Aq}pA`oOpozPMNIuLPkrDJ6CMt`UK=GcDG3^@pT0Jqww!oK1%$(+~0oPJpD zV6Gko{j8c>tXPRQe+PXO<%Sd2o8?C2a~~afGdOP`4#ktc6@KPR`(~-6jxQ)(lo_mb z-YkQaJ=!K!SAgvUb^SBBo<6|(fjVn6^>&4PU-ChxJ-7pO0_Gw_q=-neo?-= z2j2z!_kfI6@y=GRRXQ zISSlYo#TEIJ?L^}$`Vfo$6JXvBdF{5L7R*)kMrHsN5!^MWuj{?ZT+r{vPV0xzR6nY zIrb<-Qm|{3^&b99Wv(6~IeZ(l(i+E_;8x<_lcYOnt2BK5MPl`%*bUU0!G4o+6G0*J zXK=iqZ|tE<;}Gi*rGG4K_(hp$yeA`!_j+G9_V*s(`+9EbILi7v?Smc4CanwobWyz~ zP%)16Ry=E-H~Sy^=Eh5NVPHX;HAZR}9eb+hf{Zk;HHDiSPX2xN^WB+;aBj^3%R{yw0|BGkcVFiwXxOwGc z@4JEc`Dw>MeErztKRI`*b+D!8O#d-3mwwujb#pxZFud;=n0r5F-c{=fHP@?oTIo}L zjJY@}5Zl!;FehF4*n9TM`BzxS`RAJc{GZkbKaKA6vA*?JRR6aA-+kVHZvCgv18wm! zN9Ai3|33+8p86!Hx#LQ3jp{w3@z>s$T;B)NKw|H!YM%KdgjDq&QSf+sUkW)67J>ZU zSHTGdHAgwYr%%_v*p2}`A9wt; zcImU@W9t7&byl=My{~k#M)_zNFSztediVw@rcZpiER{ zR9^v4fv5d=g5wivU8G>m>wQVB%iu4doG$Z`KF}Zg7S&|}npG{4E(k+i`+3+n|jzRn5PiaaTD*`MZvSM9$jAg5LV zS+y)|7n}3t2IEa5-FQ<{!Dy?K^~SxDs-W~~oqv{L?CTVpuS>CAA;qjeeyTA?hV}83 z$uR4P1D^R1o}U?LuaWeycv+xeeZJ>+uB&<{(?0pGFVbF%^gf7XJ=yyKYo|o=6V+Vn zq`6Cr^Ux+q3cE{q$-fL{Ef;0G8GA>VFE(=gokVf{73G@1T4*2VioO?}Gr%(TTV{b~ z(yxPeg{a5FEaMoP-Rw88&I$0Ty$8k1_4`0%4n$`mI)@@F3Z5urML~~3Rur6w6w)1FR$NzQ(>^yxy;x$oDmGH1FVi1c1-=7;ECAKxDR}xxAG50kjO(#7*D; z?enhOpsycT`?1ztOgS&|GmxKw{21hCAYbioWgw#z`5DO1P<+TwL4FGIhbS8IGmxKw z{K3c{jQn`yk41hId|u?|vwt=abb+C2FAYS4m0&a&40yszrh+I?0^&gd@PJ~F3Sxlb z^@0Dbe0DO|L~rA#w;01u;*TtewVK%XQhTazOOo}VOy@deI6nEO_Fn&4dAvLA2IB$t zaoZ%xT#AlkGL^C;43quEhh?3bRY z?l`=!$s+n_t@U~Cbp9*PV0P2)!nNLC@%LBRBR(h-?71@8I8OQFz0X;9v#09ed9{_| zCO$;l(a3q5IfVChT3@AHn-p3;ezEu&&$w(PR{nsmob4sP@C*d`*~Yu_B{LSA-lZKM zkjX%dPSUwD%Dh+7jdvu)yj3ENH<)w(ojx#@U&kJkaAPrL*-5~rSn83)UaU>NZLXsn zUtcnh%5ZFqG#-)x#snEUzpRLl&>)F;C*{|xX z^s?`Klf^eb!9@H$7JuhbJ`8_n;%^`RPR8H4_&XJUC*yA~{vLvF!|`_<{?5hU@z^%M{2haR@Bh#C?|)|Q4)CZof?8wgbp>k! zl^=Y)ZkP3*vmPT}C(1JWVaCusU>4`L)VPMQZf4)f{9^9{zLP$Y@jKCVn7zF@{QkB< zYM8So+D}RibMHic>#DZPNWTY-`P!Yq++N0<+(7*N1M^E+?}aNt*@NyP^kxJDy&1vK zeB9HF-kkmj)D_*Cm!UxnMRQM{51E ze`6l`o46EgzU$8QhSqtd_Eve??oV&?L}gn*@0Hs8vbI+B_P6(CdV|~8Utgs^(6+(H z;5Jt2pmvTAw6Q+opWrtB@3t@ca{_Ivj=^mk@aezOzQ|AaL-)0y^KjCEJar9nrQcYB ze_sjyZRHlqP7NwQnzVwtE>+iigFxVVsEM?KDx@6=-dwg6+Gz7z(#x(&5lvl@)J7k~&_i!x@&R1!5zkvct z^*d7l>h~e9|JppCqgn&@U7uTvo!|2N%L<&Ecd!<{Lz3+Sy*~%v4&-0y*vGe$Yno%i z17LlB7g!nR_pY>FuwF1`%8Lqpv|>`hIK=9>fyl(sI)zsk<2~Blvq>a07aXHH>_UV! zgX3Ht#{+)74u?OY()o!j@(e$bU7)*gCq+z$NNf?yO=QHKr|A!hB%m*WvWdt@JSUR0 zRV2BDpGEfLHr$iO^>Z3B(%~Pto34^8lGz}Vl_!!7Z3xIk#?UDu!z)GdE{f#u5GmLv zQi#nXuz4i;qmEKF^o%|xGIkGLt43t}R*?yum$m|UCvF7TJ}Dj`Z!(yI4O8GRJ4+Yd zB~mUT6&$A@5t)$(nnkL@!CsN8&|7^{WEOd|(LaYe*PIfWdzxk@zYe>vUMe!5^97_A zZWdXzU*t3Ez(KZWwuoGt?8k1A>o$qh9}-zsBC>oVpj=}b==K8}nvl058k`qt*#?la z@|?)^9Iq#@wHRy_Syc*-iL@S7!(YI!o$XevCg?}C8 z){(cKy58s$QD0=+xC5LOxruT&LAx27Z>IjYY!lf;dee51TZ_Q~k=v$-+`dudj(D&c zoEN!sm&jeY;JC;ZY`goE$Y=M6+~WbwB3n6b<@{dC+)J6yr2)=AUnjDy9P9+h`NBGI zTI4>;+z;>l$oV3?Us@{i<&z>0L<7ow1>3&bEwa-CHi|q5?}O<5S{~RZ@{kW~5!n?E z_KQ561vq~M{zoYHD0O=DkjU;Dk*}W>`35|XQRcBTB73SuzG(w=KE74tTiCZ38}@Dz zd7?z*$vSXRJx|#K z>qNd24>pRtK>iB{M7~=LpdCc-_u&6N`VRTP4&l}}fXo-6|A4YTI3w~y_62Rk_WY(p=rqLUGI?pNA&;kw8)eSpD!lL>EWbSuxz( z#0Vi2hQR0H=uxYMgJOhI#=A?5uo^MKPl*w+S&XP?a7>JtW-(%?h!JJRj`=6WC_sK8x(e5cQG~3bePWF80m}02Ph;dB zF-8f%-ci_591V_$F&g@q1~JB#1J1{hKW>*8XJbv;JogT7*~5hIoJZwr!e0KHi>afGdLs0f?Tj$jD^&9kq4X< zV=>2T8^lQYsB~*v~9@ShWz_-#kimH_n#4C`$aLnyjYAK zd15@!AjVftiLtX%j0Z_Scv_6Fq5Es_JhV-WU6k3iM~sI{#ds8+uY<=Hh_MGe-YUko zc8ReUIeV$s6FbFt@|+m^$bb5f7|-I%=PCD{tzx{eLyYep7UO%^_5HnK{2RIsZ5HDP zMPmFYSBxL0iE#w}m!Thp{!?VVf{tSxUtKK5&(Zq}WE@BCFVXcH`L9#%*ZakI#P~gO-f9-(?L0C5W49P*_KES%MKS*96XPs;{)DXmIw{7x$Hn;T zHZk7wi19vlUO?VIz=z20*)OIPfz#|%akNXsbd`(g-XUhl1u;X{iRnEcW_YQX5glSi z@h#iv^J2zs6f++F1b7oqh?%@e%v7J41D1-Jwp+{$$_*rc&|xt%kBT|?l$cp(#mqh? zW=@@$LvzLCrxCLNnMKGR*&ybq<6;)?6?1g8m}6`}-WYU`Md#RNfSi&%F~^~IJl_Q# zzemgol>nKg$l=x}bK-6>Cm~}p_D%5tXe{^5shh=|)(XyuS$;^&iYzgwhXe8}v9+o} z%$d>Pte97A6SI0Lpq{gkGaJ6yFa8H;8u_RT8?r^T#m74zy`G3QhE zni4S=a9o%r=As2+eulE2*(K&;0XxOKmi%jXi@5}yOUS>DIxfY|dK;V*bJ-~|mmd+c zVZWG-@HbV8xdPcMj*HoRNX(XvJmj!R%!5`bgifCddjRnF6M?Q!mEn_yc@#-vTx#e z6X!Q0{}$4>oE3A^W-)K=7V|db+@1xFin$rtcOd6Z^6uIp=9WA$?}q2IC1P&H{(E!9 z{M;@vKVL28Hgs-V2QG;Dg}q|l*C6Ko9&kv^?agdR)PQqhehK@&Tnf&JxdXlj>csqt z4^Zx_o5b814UUQVAmzTcRLqC4{UP{vEdXc5eE6uCkAO$_iMbp3yH8$@uT$ph(7$m& z%*UpPxhEW;^PAZ4P4s^&7eL>;SIj5iebNU`i}}>a=&|C%!8Eso&qxd4ZcJ0yomf44~zMOgJS*=-owS< zh?qah1L*t_W&eE&pv;f!z%en8;GZMtdMQiHmu;|B%%3EKy<#3E@8}^he~PU?-7euOHHi7>a29zH_`LrTHx%3inOstG%u?E6FaHm*4@_fg|8njug%wlj_tie0P$|7$_ zG(bmAIiLNCe zKBI6-ti{N`HVqsSYe}!yWmx|Rue!~&58aIj6g#H!h#A-ez zR?9K5R-O>+`jcX{o)(KZX|?YX>xLS!h>2ELu~^*#kh2+-->a8|7K zg9CU|d+2heXL?>3INcYs4;ZH@+e#kylDI4#zl*moCl zw-kX3V%=Q|HiN@rebxqbV7FNJOc84^?7)=LHmLa92D!m8gNpq z`&-4@u7Lb6BIk=G;D}gX;`k+wU*0Iz4$?dJi}e7yzXH!!PKfo@ZQ!t2T$}#us6@sV zfISbQ<3VhF5F5G9wZ7I2c8T>+G*}FFgL9X#i?X}0aToj#d%#Yy9zn(<+k@WC_W?}u-H zE~o^}U<=p-j)1dbJsS?N{aIu`+X}XdbpU-YWQlb!T&#avEEX}^I($eh;;_b%3u3)= zQLLZTh;@{_pW^eMq4$-uV!c`|*3a_*>EqCjcZ2O ziN!Up^&08d&Wm*dIVZM*Gh)3S53v1p(!^)$*WKWNSZ{bhGdL{PN%Bwb0Op#)+N9cb%Cf3;wvHrA8tp8HjC)PP%5T=m+Z{W{0 zV*RBEoDl1;_~|?{-=qHTbNt&Du`a}m^>=Le0KR|h5Q}SXt49F5y^F;b0jI<^c8hJ6 zi*1#PZMTZ;>JZz#4(tF2!AWpY?2t523KoM+U>7(9kim7j?ePKRaJ_DOHiO;ZFgPuC zs13MtKC}`vgDv2^*xpTIhmjw4RP68uKwbp&2+Bn66*~%AR5ie+sO{hYI4S%v4zfTc zfEEpnJx@FO1h^n}OftaM7;KF}N6ap87@QG1HXIazI3_9GjhM!qxOki41IKm*kh1AHd*YFctH8_HrOQg1mu?b0DKdV zi9P9v*psJ#b7D^+uk4`MQ#Xn|trDCUyL^|}(~-m8tX)aDD#}+a2HU|Iv1cv-+rV+L zui7bgHL|Mriao0pK%Y(dIh)0_z*;{tS7GDSNFA&~xokv6nOe=v;T$*PR!8DfIfiVlS%$yTxAKDt5y* zupgWN7sYPO0ySVAKu+T!a9Zpp>eYn4CTLCFU(js#&;Yi8 zz2F!)FLrA(C3UDrl>qu~x9#ptY5N#b6WI4UT}bVz)Y0``C-;EdR7 zJRlcTf@ZK4>;=cbd9k@pwAUhgEwa~kgY95HI1bK>y)GUUgF4U+c7Ou_ySc8k*ZV*@ zKsM_rdp)w(WA}RO-ay$6l-)qt4V2xm9Z+@yWpAV`d;a!~Q@~QN3G4!gz$vje3h;p` zU@6!Hc7em-jMz7MKpv<8$i4~LH|+-}z(uid_JK+O?PmOS^C5u0ZlT;Ql)Hs;w?Mmv z`rL95oDzEzzTA`xszC=p<|gdjguS;4fOadiTcObTcOJWmU>7(H&WL??I4A;jU>(>A4uR8R zf7S!?0JP6S`z*B2Li_LF9_+l=2FSVhs2}G!`tf;muy0^*gYOIFVzXyp-@ird?Nh}5 z;xVzmv`6eOSBt&lgxC-46Z@<2V(&!O&Rt?ZxLxe8QKyHtioL5??1z)VX|W%n%x+|U zy;SUPEEW4PA3(-q)Zww+0J(dh@1for$k?+9P;QT+T@?G9S%5O%>;}|Z<1oi_Vn3b+ zDgm^|k=KW#q|b}}E$H8Z{;du_c5qan{I@8-7y90E&~6I;MO za6-5l0_1`ku+EQN9FG8GK9%eTWuKz#QGOAiV<|^5d-7-$ur_ z;dvU`(>npOpQb*~VDB^7`waFzgT2pS?=z<<@ve=%4L(7AsiWVNvA^p9xu6=ff~{a5I1Vm|eJ~AB z_Fx_82HU}YZ~{R89_7E62WkNH??L|_^zR)9=f(bhGC=S3!vi% z)bR(@@dtasQE*o5ABKZGPzn|UeE7qi;1D=1_F)@P{%|?K#=~2{9&iMl5&K8Epc%mb z@5x{R*a1$8{bS1gxE0`&A72prND0^mPK*6g8fXC6`qBxpUq;T$=zST!pP=g}b6AUFvwiv8;>fbL%(5&MnG z%W)DuuG#G09{VVMN8Ty$CS~8G?C;U@`(|)T?6-Vi3RnuT`z`oRd%%`Y!2#%$e;eMn zOFjkE=k0A^A2{KF>p=TI(EA_VU=N^qz#FbP5!h=$9T8JwO!cQDB{lXwe>z&iV)+$EYmE_a?DPI~tR%+1=%gWr#J+}& zhHYVaczf%OJ@4h(UMqlvp*dp z@g-cPYnicJ>*i zb{x9`$Y);{o-sO|KstJyQ?EpwU#s&|NJoxaqSFI3PpeL+YaXuLkuy-Ixxy#SUN!uV z{rNimh|2e9o5$_bX-ED+l@53EkLa|vf86nDosIR~jSaqK>wPmC+dEhI%2zivwKaD7 z*0yvv`=+GDnQY;0TJ?3>fp z=$ldB+S*dz+0dBhTT?Kips2_M={tPVUu+GOn zYkiq3W|U`EObr-YRFdyEV_JL1`p%XW&E5VAI&Lg02zW9tFVB}-x_0f_f)&ca4UT^c z>bnaYm#qj?eWPzhfuAyE#j5(2)`I2jtA^%zbt}|1b~Sdc!TriFzS;HoKB&G0UTD*DT5WY(0}a~U+~}KERqdP8(b(oNS33-OzJQ-b&|r=* ze_17Fd3#5T>L87+?Q8RV^|TY2^{rj)zWOy7TEDEd(djJpzUgIief8brz0KX-9peiN zyOwvhbaZzWbWt5@UN~oZwb$#-|6l*}YWr&{X8WeknO*0ru9{XcdtODb`M!ML$T7a@ zjmtV$*LSX04LJ5+Qs}Lzttgu{wYmbgH2PMwQ*U2;llJ9D{OHT2f# z>W+@~&Tg$uQo*x{@9HFY*}XlwActKQJW0Bpp}>lvNCwaqOIWu>pnx2k?U4e#q}rcw<~+pkhW zIDxaHzO$Q9(be42;dIn?`dDX|?vRZ>oIy9C({$+y^z}wZGS(n?HQh50SFLWKi{<%N zwKudhsm`V>LnAfmY+1Hi#V2KO>v~^3-MYPPg*u{(bDZ7Q-tFsZZ`Co3%vD{Dt!o;) z3VgJfS1VINXL&0+RKd3OK1M^!8pr9X()ee2eVa0886%}t8Pd3FSz|+kO7$}X!wTCw z9d&waDtTRjLFe}`zHYAX)~Y)FL)Y7ehZ#10b5*@n$n9^5Hrnrd)n=FIU3uQ-_O3QdZy5(YoFXuXbSjawY(}tL~6gWp$VGEzbALL^vlcoxv5LTa;H!ubN#|S2bt$ zJa6Xo;CPjZ4NdeN)k~F_u0}mVnp#@X-&adKrigtJ*IP-mH+JTB4ZV`>%7M$7WIE|L ztLi(iSN*EX*R^_ivvLQOq<4Fro`J&l)t$>RUn$I^Bec+;{P9cmMjYW(S&u)elKIh~ z99#$GC8x=|IvSVzJGGz^J)yoJ%-K$lGFF!vJEPRl3glT8aAY1oWCz=Ks?Zj7ae;3dy>wU|c z>)UY53i?^+s(KXxJ5{y{lYt(1xieIc^|m#x^)xpOy1Q|Cb6d;u`d07S&KA{*sCNcj2c4J3Slir=|J(Y)mcKh+ zW)tdbDr#p{&6~$S@D1@zn=`xIKPYM%J6E-I=`{=elVI9N<BI z?9xKZFD)>2T;?=Ak$wDpq5GDako^-zV?7hEY7hcLn`%sF=46S$@<8P~%S^&C3pX8= zSbz7e^K^{wf>F<9fxy<_^ZQeUzHTB}emxT?E0o4{-GOM=yn0oATRv-x2ECYTCZR%c zd#7@yiZ%rQ&i0PZ7PXpLg^`SmOYEx}yD8Ysnxdt#wV_L|1ywnv01?XwW30rSNPgM$ z-Hx&TGXD9O3xXDc^_rH(wa)ygl+#-~X`NAYjrNZ;=f}4R#kB30#c8ivJUi0`alXsf zxUPd4vZdRnhI2P7A;wbB0Q8SM6=9s9+sS-RY*j(eJFTxloG9v_RN!Fr`$Kb^TCy;6 zs&U~D1-OyP2#2WYPfZajNOw?*z@$2YGykbdtA$kYP_>NFe9CP9knXQqlbjX46W4-x z)fnm)YDbvTR!6IJ__XF{Urs@ey|^lA2hRV}p$LUwD$s>Y;!0crP^%z8UTK=CUB+4_;~w zNhK}?C$$9B>s6Iak~rPknCD%!x=XL4f+tDZfkhGx_tE8$69jc3$Ay>8t`5JR(=fmD zvb7g$m@BWnh!_9d+A9$40}GMB+RJ-s?RA+aa8R9^WoDPD_15KAb;}xwm}+6Sn$Dma zVNH7rEBYqY)`312h&qAjtk$kxRcppOvdgRHO{*@enpIKjt*fkXuE*xhnO=8IS#5={ zYM!sAcFz2&@``d_X4yQ>GxL1cRMl0^xw_5=M{U{cx`kXI_{wH4^j%doyFAZZv4AVB zdGma8YJF9+YO1R$%JY0xv!_*GU0yYNhHon6X3tSqe6yTcry1J~^S95i3%^WTq%Vw7&c6Qb5>9uI7 zm{l>m&Wlyk=F}{#t(s9;mq(#GGV^?OwPocMv&w3(%2O83p~|&B%~F7s$naInhtD^! zvaGt=2i4mr!&f<{x*YDQ6#AxirePqQxSPt8izL<*^D5?EO$K~kzY5x*vO=4V&1L*QO?3^` z4zN>0C8usqZC#%!*Hq1`$n%xeR?Wjb-s!b-W+9xWpoD7lt8pQ^=wgn&s##Q?qUj6t zz77GGD(!U~S6)$8jX?Uv?2qymeEjA?(56RGiO;|N5|qgXQXtn)l-bfQow7>mrIpk& zS#Ly0BfG0@@(#E}nMsj?zR$Ab0CN&+NHqz>-cpG`I%v5A``DH1plC_!=t^8L{Y85me z?|)ZmC8b46&7q8vr)+7H%G*8q+@c-OF-JOUK)pHF5YP1)y@F`7D( z+!5>+E6x@u%3@W%m2;(|6*zWQV6mg4lblt$M#>LGQX;i*tw6G}yz>gR^`Svo;FIy- zlO*~2$#P1Jplm*R@{!}%``?i61>kCwr6xotk#h?Es*xV+31^3+IEy|@4`wR1KUjozTKp!$mH z_nr9G8A*ZB;bW|-o~2sl%I#Z#B(K!z7IMacGp>9x4_!^zyH;DOB(&&}qDI4VB(`at zs=uoK-=OuYc5l|&=1G-QLz{zl74M}H>mxr{g7RMf{-j2iLa=3*>kU|=Jko)@Km<|a zs~ww_R44WXIMoR3f~NA<0OiqoT0)J-;K)+X*>ox6cOAvq&G_=_e$kDd@w8qcQkLuf z(gC##E7dqqt>d(LA$6Wk9hG0b^tb;z{`1m$0sq&~^0RfF=5VYdRSj*L&Yj1VMT(RDH2Sk3T2+ zIku=i>&!{ZbXg^-MUPz{wg$#u2U6RaRU7GTs-LMI9k60GQaf~66|VwyyHr;7PiJOz zBD9w?6^E3iN>;!t^~kQ*)~>>W<>Uv(j2dli_)*QDPOTNyi9f3Tfq6|up5U?OSm@Zk zhO){ZN~)@vvdKA9@xqHWf!LsEPMmVQc|H6}zw)k{r=3|jP(NkAiixTpF4Gdb;Bs0@ z#iKwSR7(Xc`M)~54#2pIqy4r&or(*_6cacjOtUO>x|1c*Rc?*3aTm5sv8A(Y3(J_;EVV`j9aD1mAO{*B@U@Uj>~;yhvt z)t=(WC6HK-tI#S( z!i?<-_C;fx%rlKJ_P!d4qK0V{Pe!Kd2RH(Ig1;tfN3E?_`%ANOF|>!#9L^i!KaL!& zKehUhPf$6;WVC^FPjK816nA974#uHn9P320A?V*u&}t8i0=1jyfn`h3ZnZDf6%wrp zWg?1`-6q~#wQ$rKt%#1GUML?|k6NovR*QZ>BM&IM4F1-@A0qE+=+)7cNdDRJ)zQ!< zj>G2AF8L~APSP8+H5^645L_8>{;=G$=aSB(lA3)`r#8mTL}x)BdOPZHJhXyX_rDwW z!~)UV5n~Yh&>wI`-W6?wJRDD2Z6=^iT)PwNpqZq#I}^q`Su=2LML(Q^HZZzGD@ioO z^#xZ18hEyhB@-*9c}e=d99qD06tq%0o2-qRpR3=A6(msy#S_Vy z8(EWw{*aHZhE(lZNSKe&2I3`_Lj7=lG0r(K5&We;F~iJs?xq+0Cz-|3mm&w>1s ztP_tU>n}z z$?AyEdE9@qjAV^P;1GLJBITnW;Rs-yiugAMGr1d%;nf)OMVi+%!eKOu5g+!QB2Qd@ z(f-luv2HrrBJGp8d+u=}?eQ#Z6`ZLo1{}b1Gem@JbS(V!g>?UZNUow zT0e1SaLuQgBi*rwNnDh~feBWSCD2tew2x%oDEg_@8n5T!I)rvWwvYjiMgcJ-Y4iE@ zu1ccV2l*t@-NSJSt%7)jW)838Aypm&shwOUNXj7I zJv-8-wHeFeI+ymoDq7(Ab?rKdvT@BHuAH$Ybo*|Q@qLIt$x1D|y~x1~{pgCqI> zIln+#`EO?!7*(OqR=tiQ2J~_AOq|KY7VsL@c!>6^X;bj7XeA6T1Kljn-# zF;2o3(T-?@_K(|T)i@f zs~ugVz_l4?cUQ$St7CwXKcWu);?;JPMe~aziFQ14z}9iy!QP@oq|`UG$4x<6a<)jG zP}P8XF&tV(%ttG#MywPc9r)~f9NNXcQN)r~fN+#GXjb(!5`{PG4)5$qzARZ6h$&Xm3drRDyF>?B8zaD|#5lAXN)T zTdIxz$OaBH9?x%GNiFO(=})tF;E_h1ll3R*5nX$DKJP($dbalvzQR{zq20u8k{Nt9 zI^u{uJ36A7I8fw-YNtP#oet^anf4*ecI3DFi;{~aKgR)%DjE`g(X zwH0j$SD5O^r`MbPV;zcRlXV$e#+4SYPA79s_)h)CbF9^X0863Qi($pUGcxS;S&%}l zllx<|=|EtJDMOxaK`8}2tPhkKQa;SO^V{_uWt541?fj}9x>S~!X=E{_I)X1R@3 zP!8|C4MGmKjce<&z%7CD%b+dviDI;l?`h#VB9>eQzu40i;hr7JM9an+sKo&An8rKe zy)17T)Izy-zhwv4^P-r_aVWzfc|Ko>!)*aI9b zyxM_dg=fesKsJ_G1pQhQdV!KqJ1mK#vIdU#K&x0Er7j7%)H56toMr4wwH3sAluiA> zJLKnbduaKn|5EseEn{6=-S_VYUdLJvWl$=%-3ye)9!CxDNv21KX1r>D*z10fg8fCk zt8$7lM2)b=cv>_)ni0X5g`!!}!LXnBkm%6pF!qWO7x1My zIwqP2*9nh=Bfjp(ddNP?S^Ydr@(Var@^@&g>(N)EuYraCCi-1;6I?eN zg-CpRn8ka+f-Vl{M0ibY5~BXy(XZi*@iMT4%cCpcS*eGk```=LSHfQHYog!63$;HW ztZ*V0bs{6`qo?(@ zVwG4e)`$UdZuIZyspzNC&%|1>P7I3m;yiJ_xIkPeHi(U4NDPaT*d#{8X0b(V6{F%J zu}zFc9~9fg4lyovMn8{!AublX#Dtg>Q{ob_TU;vkh|9#~;)UW0aizFQTrI8<*NW@J zi^TQf2JvEXqqs@z6*r5Qh+D+1;x=)+xI?^D+$mlrUM^lC?h>yQuM)2ocZ=7E*NWGP zd&IrsK5@Tzy?BFoqj-~evv`Ymt9YAuyLg9qr+Almw=gv_>1_f_?!5<_=otX_?P&%cuG7i{v)1| zkrYx&CABotN+)AkCo{5MHpoWVB&W&ga)z8K4~pI*XUT)*YEJ`mI$+P5QdA3|4m&$YGGPzu?kSpaXxmvD~1M*zC zR<4tSa=koHo-Z$u7s?HCqa2dMvLrXj5xH4zkz3`cyhv`7V{*IPA;;xTd9mCjC*-7@ zl9$Nc@>01+UM4S>FO*lvE9F)4YI%*kR$eDxB(Ik@$QR2S$@}H&GRDM=|PCh0-FTWtaC?A(!l3$izkx$64%CE_<%WueU%5TYU%kRkV%J0eV%OA)e z${)!e%b&=f%Ad)f%U{S}%3sM}%iqY~%HPS~%Rk5`NIt_IzyeQdQ`9KQ~j!_yjrBrQj68uYKdB^&QZ(MaO6J6xen# zZdZ4xm#RC}%hb!&E7V=;mFiXS6!+cgHR`qMb?P2Ou98dXIXqdY^hYJo)`W^&xl${3Gxr_#^6L>f`DY>XYhI z>eK2o>QVJs^*QyJ`n>vr`l5PVeMx;;eMLQ?zN)^azOKHZzNx;YzOBBazN@~czOR0u zeyDz=eyo0?eyV<^ey)C@eyM(?eyx6^eye_`ey{$Zo>YHSe^P%|e^Gx`e^Y-~|4{!_ z|5E=}PpPNXf7CNN(n3qEwAMyj?Q{&URm|vm-JlzFlb)uh>lu2cK1k2f2kY7T5PhgV zOdqc2=p*zCAPPB3XZ2itv~JeN=z02BeVjgCpP*0F^L2|}pj-7xx(yyqB2NAzZRZQfQrsxQ*p^qAhRcj$4wQ(vri z=?OgvuW-9W@79;7fWd-cuwCGZNZ zTlH=Fc72C_slHRcOut;eLf@rdsb8gEt?$;a(XZ97)A#6mqX(h~^?myO=)KYVqEAK- zMem6|6i6k~_51Y)^au5a^oR9F^hfn0`eXXz`V;W-pHJye>(A&%^=I|x^ke$-`V0Dt z`f>dw{bl_X{e=Fi{+j-}{)Yah{+9l>{*L~x{+|B6{(=6X{*nH%{)zsn{+a%{{)PUf z{+0f<{*C^v{+<54{)2u}|55)*|5^V<|5g7@|6Tt>|5N`<|64z$pVt4;&zQ&vBaJfJ z7-Nkyv8gi|Q*RnfqiHhJ%ycuu%rpm?S>|9f+Zd@k;_%gqbT z73NBFmATqnW3DyVnHQPs%?;+o=0w&na`Uq zm@k^g&6muV%~#A5=Bwsw=IiDg=9}hQ=G*2w=DX&5=KJOc=7;7-=EvqI=BMUo=I7=Y z=9lJI=GW#o=C|f|=J)0g=1KEM^C$CX^B411^EdN%^AGb+^Dpyn^OSko{Kq_FBP*=5 z3O>_nthLU@w$5g3y=}0Kw#iPj)9nm9(;j4J*@NwDdx$;M9%c`>bLElWd#K*>;<^1>0d4+D_YLyY0#L6nm;Y&7N-0 zuxHvH+iUx5zb#sC7umDyVtck-Vwc);>@vIDuCOcZD!baQu>+Kut8||Cyo9$cdTkYHI+wD8-JMFvdyKUL- zvk%w@?L+oG_PzFf_F?;e`vLnw`yu;b`w{z5`-uIR{kZ*v{iOYr{j~jzebj!|e$GB- zKX1Qazi1z~U$S4eU$IZvuiCHKuiJ0fZ`yC!Z`<$K@7nL#@7o{PAKD+;AKRbUpW2_< zpW9#9U)o>UU)$f<-`d~V-`hXfC+#2YpX{IQU+iD)-|XM*KkPs4zwE#5Q}${5AN!1p zoN&@9r=4-uITyP+mvQy3!8N)jH_c6VGu%vfkelTWcC+0f?ofA_JKW82N4OWbBi&Ih z>*l(nU9&sJ&2z`P2C%aSJsqQp) zx;w+2>3UqR>vR3C=)7Cx&T@<0*=~tj>dtY?+;X?Vt#qs0YPZG>xO3fFx6Tc^_3k`( zzPrF(=r*{GZpaP0lH24)+-A4MZFQsWBDc+rx$SO;8+SY1#cr3IaFcGzUE+4TOWhuK znY-M*&|TrKbXU2n-8Jr7cb$8YyWZX4UhHmkH@Us;X7>_zi@Vj`=5BX)xR<&+-OJp| z-7DN(?v?IU?$z#Y_Zs(F_d0ivyVu?4?suU zWw*~g;2v}jx%asDy7#$<-TU1K+y~u<+=tyq+(+Fb?qlxb?i22l?o;m5?lbOD_gVKj z_n7;<`-1zTd)$4=ec64*J>kCUzUIE}zTv*{@zURL0e&BxSe&l}ae&T-W ze&&Aee&K%Ue&v4ce&c@Ye&>Gg{@|W;e{_Fxe|CRye|3Lze|P_I|8)Oy|8`Hgr`>u69?ytp#s|f-;)CPa@geb{@nP}d@tpXG z_yzHi@lkO$o*N$>H^;}s^WtOUMJ|#XiJ}o{yJ|jLe?umQjzPLXw#y(yYpA|2T&yJVGOXG9mW%2TOMZ7Xz6|auh z!~^lU@!EJ@JQ%N!&x_BGFNiOUH^dv`p?Ekh#hc=hcyqiZ-Wrd_7scD+v3PsDBOZ@; z#uvxC;)!@No{BGtcgL5;d*aLD%i|ZuSHxGwSH)My*TmPx*TpZ2ua9qtUmV{U-xTkS zZ;oFQ-xA*%-xl8<-x0qwzB7JV{POq}@m=vN<5$J6j_;0N6TdcoU3^b`Z+u^Tzk|2s zw6*oL5$$bfnrGVKdSL52yg|YZ5w$(TyWo8+b|?_nGrk4()o*h{MC^++?#2jFTcj380;ykA?MVHWYC5y1(m-ZQCE2hDb_s7EqRuvKNip?jRqNJot)}%^ z!$Z6DR`{`tC*ch$BNpE50K_e3Om2{ zC8>@yY^!0mz)OO5jKPNH(UM&{RDu@=*|9(xmZr;%r5L-E252l0y%e<9WAI~_QT-iM ze_5(Z!;TbVmr?y4G{8HCc8nam!ixaaMjgDL)=aqKJ21CtV)S(*p*`JD(b^7BCDy^L?HF6t3y{mQB4P{&MhP>&n?W` zHB@;r5WNN`auO$UfF=@N<$@DAz`DR2Bf>-ukS7J!~mhM@)XX&1`_bktMXL`_*Xp)C2N!L>hS#Aj4mjW(czkFhB z2;OTE(&N>1=smmujwwlRZzJjLZ6v+Djik4?k@WU98vFJ(8vFJ(>P>qaY2V&P+PAln z_U&z?eR~^e-`+;rx3`h@?Kzg8WBECjpJVwsmY-w!IhLR6Y*?Lg@rH>MLmK6{Cpqp( zjwR<)<^qm*6WWhU>yCVJhg5Z)>j$>q;tz#v{-3k>~ly^L*snx*E5Rk8cZar%2TzUGh9D zd7hO#&q|(WCC{^x=UK_~tmJuC@;obfo|Qb$N}gvW53>R*On4PyRd_phxt*pk&jX+5 zfzR{6=XrqhJivJ#;5-j-o(DM31Dr3=K;{eFexaigUXurJihG!pp=kQMVXE4LZIY-Vg{O1Th1t!kOjUN48L*8KY3VMuG_vbT7Q& z$1H+ZU=2Bzqt`>00yzfWPBS^AQIJ^zZ@L)*+}%FBX-J+kC6`Ufb)$fOL9$#is#a|s zH>*eC^_FVQ(3Im|s1;jBRUiDVm>6xqc9I@U;{pkv8LFx~lF}fC4v@Oo|B#wBMaPq> zA=8BVdKikEEvSrvSC&ls2C8n^JW zuZX4M=G!* z71)sq>_`Q6qyjrqfgP#9j#S9eVo}J^qEyJyqEz5Tt-y;~AxDc+AxDc+ffu;~FLH%; zT9gXytbaTCOrf0?r2_kAfqk>UzFA=3EU<4Dcu_3yqFCTXvA{lCV4p3p&lcEc3+%H6 z_Sph2iUnR23wiS0LY~%!LY@}o0xy<@JnNrl{qwYt74ozw7xJusp7qbuqFTt)qFgAj z{sq>b*Nj4e_2;#tP+<85mS5oUFR=Uq%P+9}0?RM3`~u4_u>1nc?_l{IEWd-tzk}s> zu>Kt^zk}s>u>1~|-@)=bSbhh~?_l{IEWd;0cLe#le)5N0k^BH?=m*fy51^qRKtn%( zhJFAI{Qw&J0W|ajXy^yf&<~)YA3#GtfQEhm4gCO0{V0+jDt0MGKt4}fR+rZ|FJnK(>06gnYegHh{&wfx`82m6-T*&&8H#0{V)*}#QxKdhcC-H7tbaG_-_7#7S$;Rm?`HYkEWexOceDI% zmfy{C>2g)B*u!#rc-(tfZV$`tVYxjlw}<8Su-qP&+rx5uSZ)u??P0k+talH~?`6Gv zSw3Ah%N2WBelN@KW%<1^6Atbc$QD6_Q11zI<*I$<Es^D zv3xqY2cG5A$vyBapHA+9XZdt;pDX&D9V7?b69{=3o!Y}umhM@)XX$jR564-$C#jy% z+%uYcMsv?-?itNJqq%1^_l)MA(cCkddq#85Xzm%!J)^m2H1~|=p3&ShntMib&uH!$ z%{`;JXY}@r-k#ChGkSYQZ_nuM8Ld5|wP&>UjMkpf+A~^vMr+S#?HR2-qqS$W_Ken^ z(b_Xwdq!)|XzdxTJ)^Z}wDyeFp3&MfT6;!o&uHx#tv#c)XSDW=)}GPYGg^B_YtLxy z8Ld5|wP&>UjMkpf+A~^vMrY6H>=~UsqqApp_KeP+(bzK@dq!i=XzUq{J)^N_H1>?f zo>AB{`g%rP&uHrzZ9Su{XSDTltl5qpfGO^^CTj(bhBCdPZB%XzLkmJ)^B>wDpX(p3&Ab+WIb9WqlW|vc8K}8Q(># ztnZ>##&?mQdq!8!=;|3=J)^5A5_%6djw&nW8|Wj&*;XO#7fvYt`aGs=2KSKRQvqp4>!^^B&TQPeYvdPY&tDC!wSJ)@{+6!navo>9~@ih4#-&nW5{MLnaaXVmnJ znx0Y9GirK9P0y(587)1drDwGCjFz6!(lc6mMoZ6V=@~6OqorrG^o*9C(b6+odPYmn zXz3X(J)@;(wDff8JLl=rH&FH)#zoJ#=ouG1=mwc2R{WG z{1j;LQ=q|5fd+X%gP#Hocnmb)G0+eV0S$NzG{^-Sa2aTj3pB_Dn#c{&0DOms<>zvl z&7)hU;0qpbVID&PIK(1z3+?vN9e8JM014_qpG_G(FASekmbQUPJ+e_<3v6t2hpj57x)(PN4zlt2C0uAHt z3pD@W{$X5!hIs`V<`rnr3uu^EprKu$VP3)PyzMEr4vkH!(uPU3dBZLj?uu;~Wja<* zeE4llr7Ih!@fWSGg0As*lec zNoM*2Q<(%XfTn6&Sgxrm4cTecqoK`d)x&{rOk~i}YA4c13$1BxVGY+=!}Zi~eZ-l~ zt^LITGupal6+AfD+SjwjjNxyMStdp{(b$um&tt8|{yT z*3^aH?eLu;b4lRCFY=k-3!!M!7WgCpJW?<`Hi4dGS5gB5SV67XuoYYj|F*^83gM8N z48FD%{9_yZ4Z|P$-bL&m2_0sBE7NV$Q~nnkio*?y)`^nCdlo?BaN7bC;m!plGJNv_ z3*r3>pq9&qyBL^{cOHPo;a&zL>gaX`q|Ejy{M`|LFCB$6+;0eXYcP8!W>~n*0VEE$ zIFN_C96&OBlLB+#UI!Am-4VD6ByqTJfr)Sj2@EY`CLU(P2gC6Wn)#{5aftf`o;;Pgrh% zZW3V*+=D^_x1j<@I)po8z}Hf_c zaucJMhD2rp9@*JJ$r1cm92JDmwQyyYu``hY36imMq9SAGL?Q!IBm>Hk3@De#fMg;A z3bG7Hk&MwDn@JB$1R0P>WI&Q-gjA4$<$??>$1*U0Z^WQ2l2Mo0x2V>_m{2N@xOGBCk1FiA2fg)%}plo862jF2Q5 zpZEy;nBr%UA*6Dq2YaZ=z;2{6reSYV{0!<;I*0m} z%9$Rzn968MdJ5lu!{=_epUiPDiJMmGBk@g14-=OA$91`PT({CU<|{qp%1OV7t*3A< zSb5{cQ}A^WSU>|e4MukuK5hcvSqgmpCj1H>IZgw7-vghF3kRBpw&RzLVCfC)^w0`^ zXKajIrU^bj2TyyAY^LJV0vo=N6UsLZO~415Mklrfu5Rbp6nuyfd>Fp11NVMIvVQ9x zcutGE0kTHNNmM30M3R(-WjQ39Ch#+R;|W`bACRFk@M(`BQmz4>K!cCukT#$wvi(GgL_Cp2M9w0zn8?{gmJnG=+q{IxZX&db+dafx7DyB73|~EC1B~HmxsX3CPIyxFM!1Zir0E4Uw&J!*IOnhRCMf5c#SbB1dkBDRx6lCT5ft20rD6$V0uv4Uw(5VK`QGLu3;-3~6@5 zP$zN2aDd$q(@j-3q&nf^27Cy2$L6XVBA0eUPH{ zbwlJB4I!1FAtVzw!~*pR8e$P%<*9UlKS-wBFtkW$h-DZJAz5+5(1R)(!l8hMKoJd* z02(3zG(-Yuhy>6O37{boKtm*ehDZPnkpLPZ0W?GcXov*R5DB0m56O37{boKtm*ehDZPnkpLPZ0W?Gc z2!aIA5DB0m5 z8zP&yVMr%vh;gWNpK?RslAsjM*OH(VL|1}RNGEOxg%dZ#WD1f@VQCPjZ2nMA*lmfw^6bJ^TK)?-=U{DGKgHj+Elmfw^6h^-`D235T2c*vjM@N6bJ^TKrko; zf5)4X#U{DGKgHj+El;RMi zf>O9RKv5ZA8Z4!x!A?pVtfZvDMoJpQmNeK$ zNrQEiG}uN-gJqO7*hNW$Rg^T?L`j21lr-2wNrN?%G}uB(gC&$S*g;8y6_hmCKuLoI zlr+qLNyF@yG|YWT!_1fJ@T>u^IzcL2y@eE9<*n>N3kUcdq|{J(6dAJPu(bjbW(cdp zW;M6~3a8{)Bbi-DRT_oQStlFYphyE0rf1Tz{Ai^Uqm}Y}4u~7!u>5GMJY1{9QK^G3 zZo=jwOyQ@NNsIAH1LHLU>)`9RRJZ}YS6vZZzkOY31-c(I{ z4fyT(@K8P0+>?}w@$=9)DUGS?zfd52a~Qjt;cb9ei?#rUOxn?enWdzurquR7IM7(# z_7{qUtu~mV&3}}_{eO_k@YX-fq-}qYYOHSe3q`6TX_p^lHl;TAVcjiZe-_rQZtnwb zT6$9-R!i;W1J-Q~s|QSLGUOTFzy}BGX!9PVGQ4jOYlO@9U_`J>0-*@a-Hf8WH>I}l z0Xr?daWC+xy?emJMQYs7gGbW4^}>;4z#Q(=3pthPrrmm2E4@b#%j`&MS=f>X8E~aL z>|n!VXW%MMuBbXWDKiKG`9Scc&I zJ&!eM%N?W~Zn(o7+H41@4DYiGnMp6>u)z+prq}GM3&kt8(3ILo2S=Jx+vk97*poJT z+D8Z3jn(~gz|~!r^wxztF|g9~K(>bUu%uxbENNK%O1g+Yu%4DQga#!Ifk8<_SWwas z6qGcC1SJgtK}kb6P|^?#lr)3_B@KZRF!#kR>HQLM!sSIyq#>~E?N>ge#GaP8FZek9_ z5<98uPo^~V#t77T9w6Wc`^>uYjzGpDYG06yKYsb+4P3t za3pEAj+ch_Wc6eFqrbCI@ml~W^RVtbDiPFsd%^=B28A2tiY6@VPp&}Ur z_#8C@PoM1B)s#wC9-V=K#z$wWV76&wD)qzza@B<%xX>W%NNZias<(b(C)}f;aiRRS zZnQy4=L)HGZ#A7y<>yoR9ic8=I-&a;@I+F05(3tP@Gu3eLJ+vN<=~0{o|DbkGBr8| zmtMAvh1(<`cxEMs8i1@&Xl!IVhKz78B`FkgDupcG*KEtp*zY!MTQ2>4#zZMcPr%TS z)iCX7*7%4Rv=Wl=lo%u%s^_+Cxi~y%F}ic8EtY(1w|ZrI}>WWh5&gU^BN zKB#6dG7a4g5l-YQJC1>GtnO3= zZdQ7`Yo&>FCKQ^6H`o%n3>hJ0iK2m$7fcf#!6PT6s!{amrAl4S|=wr!>dTfw~p?Z zg!clAXP$vK0N4Ka7`%HI{`BL|JY^=-#m_wX^!HNfCn9)xY)f`iIlFOlb2(bEZe(yl zSwKd%{K$&3K6ZUs7tdQaw|VZIz3Z~&6)V=wEuT3!CtL2qRM+5Owwx&rZ7Ls+Y^Ink zw_&0UOFXh-U3PPJ@7^IOvSQsv$jOF6m|BRbg&XH=92^{+Q;y~j4zg?zE?ctDj-EFJ zT`+ws)|E|jZ`n5Y&Y3%Ruq-w%D65uc=udXjJ~P~##Ul@7ggkLBB>S>^vwNYreQjpm z-j(Y%u9!2lYH(fiARIe$^*T5(2YX*pQ@503XxqVGgnxZy*SB(AIqsYP0E}hd#@_Pm zkt3i-rUi~+r)05I7RBL>{R_%A$&2PU@3VT|#%ytK^AHYO&@!5XBT>%I0eOkQvYOXC z)DLxC%RXa@WibTlxCL~|!VvYHjiV2V=H9__J#wpns|RjDxvnMqu#SfDrvy^U4SgH4 zdpBmw4b8pH3(A?6eNkiIz`A{njeXE)Z@F&%ASxs0^|BuOGLYU^4?krwyEzLTfxbZr zJ+F5!j2N^SpE$P}>LzKJlTaVp2*(DYe~X}3i#C?894VxxXy>%F*c$z-y3U z$QG3Ap*yCyW?fWnZ0^l&gjV0v)Fj~b)V;lXH}1>m`Q_31bB+eB8lYhP`~~I4mVE+g zQ_DVybXv-t$K8VUwE&D9egIe}Eq_bM~#Yhis*;j{jc1zjK|6j^J1Z2;K zI){R6q=$iQq=$oSq;o(v(j!1N(iebiq(_2mq(^~lq*;)SbZ!f1+O#p-2cx(VhYtQ< zxvn{TDzMGvnIlJ_^)4tM-BO-AzdZNE1?A?JY<3aMdty?}LtV|;y#wp^&zytRj;Rc( zm|bo@u`CX53nMr$t#R$4V_UKv!CQ`NiOP!X791dvhskL6Lt&-pKfSqY-?8Fg=+*Hp z*;AnhNvVxs218v7$|tn69&+k}@`(pH3X@-g67ymFqS^DZt=UDvm*l*~d-pDCUer7^ zTo&M=Ckk=!ED+EFZZjL~4*m~E%eFXwWN&M8Hhbz`Xl6n6aJDrRfRzBprkiaE@p`Ek3euKobwMlbt@ z=0JMmAou_j8v?nm+>kDl`6Vwz)04E0qhGTx&fJYdt3-w@W1JB3S z;TM*)In8qit1ZIBwW2;*kYSExeVR`NHBSoJY}Pl!kj;nrUr=rf>O;>Yk505^ zU2FDKSbWK%ctk*aW%%YnXBNZJp|U8Jnulq9YUTlj?kp(hSXf^&q8o9o2L+NrZ*OVN zwxZ@9R-97@TlXC;W`RlPE7>sa)$BqgJCshU)|5E~L>^3*04os8{6Ved<6-tseUALo zz`n(-gUTmBnbTX!^MQ1tSR2#IsCIZUj@<}k4io5MtBm>0C#WE#;T!Mn4q;5sy1 z=U_XXPKNcIGMPhoZ8$qXKu>G<_>ai zd$6}%(4}7NEhhS~x0vY1-eTg+7PQdv8IbJ8*+>kdh;_Rm*JE8wEW)~&I1B4y;$)nF zxoBai8xK-t%vWLyCquDS*a9Y2V+)vAgDqg<3>@f-99019eh8$2{I~WVR`Ha zL~h<$o>hc};`ogaGY!B(m3VYnHupc6$rwHV!TRa=J7YHd4nCldkBh7NAg8U mcZ|3Zf|oOITo<@wk%M@FSO~KQ9~RGCU)HzmGw|hx=zjoaQiNsz literal 231904 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 T4s-SWr@ZM*DnIDIeBu8Aczgq4 From 4ae0a5c02e1b4721da6041b8c00e2633942320c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Aug 2012 19:29:00 +0200 Subject: [PATCH 069/143] fix silhouettes around objects above the water --- files/materials/water.shader | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/files/materials/water.shader b/files/materials/water.shader index 08a19ace93..8f46aed344 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -118,7 +118,6 @@ #define WAVE_CHOPPYNESS 0.15 // wave choppyness #define WAVE_SCALE 75 // overall wave scale - #define ABBERATION 0.001 // chromatic abberation amount #define BUMP 1.5 // overall water surface bumpiness #define REFL_BUMP 0.08 // reflection distortion amount #define REFR_BUMP 0.06 // refraction distortion amount @@ -256,11 +255,15 @@ // refraction float3 R = reflect(vVec, normal); + + // check the depth at the refracted coords, and don't do any normal distortion for the refraction if the object to refract + // is actually above the water (objectDepth < waterDepth) + // this solves silhouettes around objects above the water + float refractDepth = shSample(depthMap, screenCoords-(shoreFade * normal.xz*REFR_BUMP)).x * far - depthPassthrough; + float doRefraction = (refractDepth < 0) ? 0.f : 1.f; float3 refraction = float3(0,0,0); - refraction.r = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0).r; - refraction.g = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION)).g; - refraction.b = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION*2.0)).b; + refraction.rgb = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP * doRefraction))*1.0).rgb; // brighten up the refraction underwater refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; From e97d23e626ca688ed503c31d410cf5543410b762 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Aug 2012 15:42:37 +0200 Subject: [PATCH 070/143] Issue #361: reset skill level in race selection stage --- .../mwmechanics/mechanicsmanagerimp.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7fd0b29c38..2ac146c2f7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -66,15 +66,18 @@ namespace MWMechanics static_cast (male ? attribute->male : attribute->female)); } - for (int i=0; i<7; ++i) + for (int i=0; i<27; ++i) { - int index = race->data.bonus[i].skill; - - if (index>=0 && index<27) - { - npcStats.getSkill (index).setBase ( - npcStats.getSkill (index).getBase() + race->data.bonus[i].bonus); - } + int bonus = 0; + + for (int i2=0; i2<7; ++i2) + if (race->data.bonus[i2].skill==i) + { + bonus = race->data.bonus[i2].bonus; + break; + } + + npcStats.getSkill (i).setBase (5 + bonus); } for (std::vector::const_iterator iter (race->powers.list.begin()); From ebe131b3268b7d4a301e010234639763caab6eec Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Tue, 14 Aug 2012 14:45:16 -0400 Subject: [PATCH 071/143] Use debug dlls when debugging in vs2010 Using the Debug build in vs2010 is not working because the debug dlls are not loaded when debugging. The reason they are not loaded is that CMAKE_BUILD_TYPE is not defined when doing multiple builds. This in turns causes OGRE_PLUGIN_DEBUG_SUFFIX not to be set. This patch makes sure that OGRE_PLUGIN_DEBUG_SUFFIX is always set but only used when debugging. There are still other bugs that have broken Debug mode in vs2010 but those will be addressed in other patches. --- CMakeLists.txt | 8 ++++---- components/files/ogreplugin.cpp | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb983..e3cc8df3b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,11 +259,11 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix -# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) -if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") -else() +if (APPLE) + # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") +else () + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index c434114b37..85fe661de8 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,7 +6,11 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { + // Append plugin suffix if debugging. +#if defined(DEBUG) || defined(_DEBUG) pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; +#endif + #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; From d57698501905a8e2dc987343de6d38f199a27991 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Thu, 16 Aug 2012 20:52:08 +0200 Subject: [PATCH 072/143] credits.txt: fix name --- credits.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credits.txt b/credits.txt index ca0ff7323b..c7ec4936e7 100644 --- a/credits.txt +++ b/credits.txt @@ -35,7 +35,7 @@ Sylvain T. (Garvek) Packagers: Alexander Olofsson (Ace) - Windows BrotherBrick - Ubuntu Linux -edmundo - Gentoo Linux +Edmondo Tommasina - Gentoo Linux Kenny Armstrong (artorius) - Fedora Linux Nikolay Kasyanov (corristo) - Mac OS X Sandy Carter (bwrsandman) - Arch Linux From 2a11a28e81e4a9bded90f5132a6b837b068a8701 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 10:05:29 +0200 Subject: [PATCH 073/143] Revert "Use debug dlls when debugging in vs2010" This reverts commit ebe131b3268b7d4a301e010234639763caab6eec. --- CMakeLists.txt | 8 ++++---- components/files/ogreplugin.cpp | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3cc8df3b6..543d9cb983 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,11 +259,11 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix -if (APPLE) - # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") -else () +# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) +if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") +else() + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index 85fe661de8..c434114b37 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,11 +6,7 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { - // Append plugin suffix if debugging. -#if defined(DEBUG) || defined(_DEBUG) pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; -#endif - #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; From c46eeaa100687d9af473c83a638df1dd87966fda Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 12 Aug 2012 15:50:37 +0400 Subject: [PATCH 074/143] initial 3d-person camera support --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 31 +++- apps/openmw/mwinput/inputmanagerimp.hpp | 2 +- apps/openmw/mwrender/player.cpp | 172 +++++++++++++++------- apps/openmw/mwrender/player.hpp | 32 ++-- apps/openmw/mwrender/renderingmanager.cpp | 14 +- apps/openmw/mwrender/renderingmanager.hpp | 4 + apps/openmw/mwworld/worldimp.cpp | 1 - apps/openmw/mwworld/worldimp.hpp | 3 + 10 files changed, 184 insertions(+), 79 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 75835120fd..b8421cc628 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -67,7 +67,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.setFrameDuration (evt.timeSinceLastFrame); // update input - MWBase::Environment::get().getInputManager()->update(); + MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame); // sound if (mUseSound) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8b809d399a..c2db2acf1c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -248,6 +248,8 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) = 0; virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; + + virtual void togglePOV() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8def5c74d4..6bdd87d2ab 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -62,6 +62,7 @@ namespace MWInput A_ToggleSneak, //Toggles Sneak, add Push-Sneak later A_ToggleWalk, //Toggle Walking/Running A_Crouch, + A_TogglePOV, A_QuickSave, A_QuickLoad, @@ -90,6 +91,8 @@ namespace MWInput std::map mControlSwitch; + float mPreviewPOVDelay; + /* InputImpl Methods */ public: void adjustMouseRegion(int width, int height) @@ -340,6 +343,8 @@ private: poller.bind(A_Jump, KC_E); poller.bind(A_Crouch, KC_LCONTROL); + + poller.bind(A_TogglePOV, KC_TAB); } void setDragDrop(bool dragDrop) @@ -348,7 +353,7 @@ private: } //NOTE: Used to check for movement keys - void update () + void update (float duration) { // Tell OIS to handle all input events input.capture(); @@ -400,6 +405,21 @@ private: player.setUpDown (-1); else player.setUpDown (0); + + if (poller.isDown(A_TogglePOV)) { + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += duration) > 0.5) + { + // enable preview mode + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + } else if (mPreviewPOVDelay > 0.f) { + togglePOV(); + } + mPreviewPOVDelay = 0.f; + } } } @@ -452,6 +472,11 @@ private: mControlSwitch[sw] = value; } + void togglePOV() + { + MWBase::Environment::get().getWorld()->togglePOV(); + } + }; /***CONSTRUCTOR***/ @@ -470,9 +495,9 @@ private: delete impl; } - void MWInputManager::update() + void MWInputManager::update(float duration) { - impl->update(); + impl->update(duration); } void MWInputManager::setDragDrop(bool dragDrop) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 5092198da8..70436e2078 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -52,7 +52,7 @@ namespace MWInput OMW::Engine& engine); virtual ~MWInputManager(); - virtual void update(); + void update(float duration); virtual void changeInputMode(bool guiMode); diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 94a3f71c8e..a99d699450 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -12,82 +13,119 @@ namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) - : mCamera (camera), - mNode (node), + : mCamera(camera), + mPlayerNode(node), + mCameraNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), - mVanityModeEnabled(false) - {} - - bool Player::setRotation(const Ogre::Vector3 &rot) + mVanityMode(false), + mPreviewMode(false) { - Ogre::SceneNode *sceneNode = mNode; - Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); - Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); + Ogre::SceneNode *pitchNode = mCameraNode->createChildSceneNode(); + pitchNode->attachObject(mCamera); + } + + bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) + { + bool force = !mVanityMode && !mPreviewMode; + Ogre::Vector3 newRot = rot; - // we are only interested in X and Y rotation + rotateCamera(newRot, adjust); - // Rotate around X axis - Ogre::Radian radx(rot.x); - if (radx.valueDegrees() > 89.5f) { - radx = Ogre::Degree(89.5f); - } else if (radx.valueDegrees() < -89.5f) { - radx = Ogre::Degree(-89.5f); + if (!force || !mFirstPersonView) { + moveCamera(400.f, 1600.f); } - Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); - - // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); - - pitchNode->setOrientation(xr); - yawNode->setOrientation(yr); - updateListener(); - return !mVanityModeEnabled; + return force; } - std::string Player::getHandle() const - { - return mNode->getName(); - } - - void Player::attachTo(const MWWorld::Ptr &ptr) - { - ptr.getRefData().setBaseNode(mNode); - } - - bool Player::adjustRotation(const Ogre::Vector3 &rot) + void Player::rotateCamera(Ogre::Vector3 &rot, bool adjust) { Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - float f = controlFlip(Ogre::Radian(rot.x).valueDegrees()); - if (f != 0.0) { - pitchNode->pitch(Ogre::Degree(f)); + if (adjust) { + float f = + limitPitchAngle(89.5f, Ogre::Radian(rot.x).valueDegrees()); + + if (f != 0.0) { + pitchNode->pitch(Ogre::Degree(f)); + } + yawNode->yaw(Ogre::Radian(-rot.z)); + } else { + Ogre::Radian radx(rot.x); + if (radx.valueDegrees() > 89.5f) { + radx = Ogre::Degree(89.5f); + } else if (radx.valueDegrees() < -89.5f) { + radx = Ogre::Degree(-89.5f); + } + Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); + + pitchNode->setOrientation(xr); + yawNode->setOrientation(yr); } - yawNode->yaw(Ogre::Radian(-rot.z)); - - updateListener(); - - return !mVanityModeEnabled; } - float Player::controlFlip(float shift) + void Player::moveCamera(float rsq, float hsq) { - Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); - Ogre::Quaternion orient = pitchNode->getOrientation(); +/* + Ogre::Quaternion orient = + mCamera->getParentSceneNode()->getOrientation(); float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - if (pitchAngle + shift < 89.5f && pitchAngle + shift > -89.5f) { + orient = mCameraNode->getOrientation(); + float yawAngle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); + + float tana = Ogre::Math::Tan(Ogre::Degree(pitchAngle)); + float tansq = tana * tana; + + float r1 = hsq * rsq / (hsq + rsq * tansq); + float zsq = r1 * tansq; + r1 = Ogre::Math::Sqrt(r1); + + Ogre::Vector3 pos; + pos.y = -Ogre::Math::Sqrt(zsq); + pos.z = r1 * Ogre::Math::Sin(Ogre::Degree(yawAngle).valueDegrees()); + pos.x = r1 * Ogre::Math::Cos(Ogre::Degree(yawAngle).valueDegrees()); +*/ + Ogre::Vector3 dir = mCamera->getRealDirection(); + dir.x = -dir.x, dir.y = -dir.y, dir.z = -dir.z; + + Ogre::Ray ray(Ogre::Vector3(0, 0, 0), dir); + + mCameraNode->setPosition(ray.getPoint(800.f)); + } + + std::string Player::getHandle() const + { + return mPlayerNode->getName(); + } + + void Player::attachTo(const MWWorld::Ptr &ptr) + { + ptr.getRefData().setBaseNode(mPlayerNode); + } + + float Player::limitPitchAngle(float limitAbs, float shift) + { + Ogre::Quaternion orient = + mCamera->getParentSceneNode()->getOrientation(); + + float pitchAngle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); + + if (pitchAngle + shift < limitAbs && pitchAngle + shift > -limitAbs) { return shift; } if (pitchAngle > 0) { - float f = 89.5f - pitchAngle - shift; + float f = limitAbs - pitchAngle - shift; return (f > 0.f) ? f : 0.f; } else if (pitchAngle < 0) { - float f = -89.5 - pitchAngle - shift; + float f = -limitAbs - pitchAngle - shift; return (f < 0.f) ? f : 0.f; } return 0.f; @@ -104,4 +142,38 @@ namespace MWRender MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir); } + + void Player::update(float duration) + { + if (mFirstPersonView && !mVanityMode) { + return; + } + if (mVanityMode) { + /// \todo adjust rotation constantly + } else { + /// \todo move camera closer or change view mode if needed + } + } + + void Player::toggleViewMode() + { + mFirstPersonView = !mFirstPersonView; + if (mFirstPersonView) { + mCameraNode->setPosition(0.f, 0.f, 0.f); + } else { + moveCamera(400.f, 1600.f); + } + } + + void Player::toggleVanityMode() + { + /// \todo move camera + mVanityMode = !mVanityMode; + } + + void Player::togglePreviewMode() + { + /// \todo move camera + mPreviewMode = !mPreviewMode; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 981ecfe0b5..739b4cd8b2 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -22,16 +22,24 @@ namespace MWRender class Player { Ogre::Camera *mCamera; - Ogre::SceneNode* mNode; + + Ogre::SceneNode *mPlayerNode; + Ogre::SceneNode *mCameraNode; bool mFirstPersonView; - bool mVanityModeEnabled; + bool mVanityMode; + bool mPreviewMode; - float controlFlip(float shift = 0.f); + float mTimeIdle; + + float limitPitchAngle(float limitAbs, float shift = 0.f); /// Updates sound manager listener data void updateListener(); + void rotateCamera(Ogre::Vector3 &rot, bool adjust); + void moveCamera(float r, float h); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); @@ -39,11 +47,7 @@ namespace MWRender /// Set where the player is looking at. Uses Morrowind (euler) angles /// \param rot Rotation angles in radians /// \return true if player object needs to bo rotated physically - bool setRotation(const Ogre::Vector3 &rot); - - /// \param rot Rotation angles in radians - /// \return true if player object needs to bo rotated physically - bool adjustRotation(const Ogre::Vector3 &rot); + bool rotate(const Ogre::Vector3 &rot, bool adjust); std::string getHandle() const; @@ -52,13 +56,13 @@ namespace MWRender /// several different objects void attachTo(const MWWorld::Ptr &); - void toggleViewMode() { - mFirstPersonView = !mFirstPersonView; - } + void toggleViewMode(); - void toggleVanityMode() { - mVanityModeEnabled = !mVanityModeEnabled; - } + void toggleVanityMode(); + + void togglePreviewMode(); + + void update(float duration); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d0019c6b8e..91d83caf3c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -133,11 +133,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mObjects.setMwRoot(mMwRoot); mActors.setMwRoot(mMwRoot); + Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); playerNode->pitch(Degree(90)); - Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode(); - Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode(); - cameraPitchNode->attachObject(mRendering.getCamera()); + mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mShadows = new Shadows(&mRendering); @@ -147,7 +146,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mSun = 0; mDebugging = new Debugging(mMwRoot, engine); @@ -259,11 +257,7 @@ RenderingManager::rotateObject( bool force = true; if (isPlayer) { - if (adjust) { - force = mPlayer->adjustRotation(rot); - } else { - force = mPlayer->setRotation(rot); - } + force = mPlayer->rotate(rot, adjust); } MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); @@ -329,6 +323,8 @@ void RenderingManager::update (float duration){ ); mWater->update(duration); } + + mPlayer->update(duration); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ef6f18a75c..f100fdd592 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -56,6 +56,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine); virtual ~RenderingManager(); + void togglePOV() { + mPlayer->toggleViewMode(); + } + void attachCameraTo(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8ace54378b..20706438da 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1151,5 +1151,4 @@ namespace MWWorld } return pos.z < cell.water; } - } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4031a180a8..2e5f73702b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -277,6 +277,9 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::Ptr &object); virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); + virtual void togglePOV() { + mRendering->togglePOV(); + } }; } From a7aeda9a3b78d8939745a07fb55c7adb0f5b38cd Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 12 Aug 2012 18:35:35 +0400 Subject: [PATCH 075/143] initial vanity mode support --- apps/openmw/mwrender/player.cpp | 85 +++++++++++++++++++---- apps/openmw/mwrender/player.hpp | 12 +++- apps/openmw/mwrender/renderingmanager.cpp | 13 +++- 3 files changed, 90 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index a99d699450..53642d6152 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -24,19 +24,29 @@ namespace MWRender pitchNode->attachObject(mCamera); } - bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) + void Player::rotateImpl(Ogre::Vector3 &rot, bool adjust, float r) { - bool force = !mVanityMode && !mPreviewMode; - Ogre::Vector3 newRot = rot; + rotateCamera(rot, adjust); - rotateCamera(newRot, adjust); - - if (!force || !mFirstPersonView) { - moveCamera(400.f, 1600.f); + if (mVanityMode || mPreviewMode || !mFirstPersonView) { + moveCamera(r); } updateListener(); + } - return force; + bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) + { + Ogre::Vector3 _rot = rot; + rotateImpl(_rot, adjust, 400.f); + + mUpdates = 0; + mTimeIdle = 0.f; + + if (mVanityMode) { + toggleVanityMode(); + } + + return !mVanityMode && !mPreviewMode; } void Player::rotateCamera(Ogre::Vector3 &rot, bool adjust) @@ -67,7 +77,7 @@ namespace MWRender } } - void Player::moveCamera(float rsq, float hsq) + void Player::moveCamera(float r) { /* Ogre::Quaternion orient = @@ -97,7 +107,7 @@ namespace MWRender Ogre::Ray ray(Ogre::Vector3(0, 0, 0), dir); - mCameraNode->setPosition(ray.getPoint(800.f)); + mCameraNode->setPosition(ray.getPoint(r)); } std::string Player::getHandle() const @@ -145,13 +155,20 @@ namespace MWRender void Player::update(float duration) { + if (!mVanityMode) { + ++mUpdates; + mTimeIdle += duration; + if (mTimeIdle > 30.f) { + toggleVanityMode(); + } + } if (mFirstPersonView && !mVanityMode) { return; } if (mVanityMode) { - /// \todo adjust rotation constantly - } else { - /// \todo move camera closer or change view mode if needed + Ogre::Vector3 rot(0.f, 0.f, 0.f); + rot.z = Ogre::Degree(3.f * duration).valueRadians(); + rotateImpl(rot, true, 300.f); } } @@ -161,14 +178,28 @@ namespace MWRender if (mFirstPersonView) { mCameraNode->setPosition(0.f, 0.f, 0.f); } else { - moveCamera(400.f, 1600.f); + moveCamera(400.f); } } void Player::toggleVanityMode() { - /// \todo move camera mVanityMode = !mVanityMode; + + float r = 400.f; + Ogre::Vector3 rot(0.f, 0.f, 0.f); + if (mVanityMode) { + mPitch = getPitchAngle(); + mYaw = getYawAngle(); + + rot.x = Ogre::Degree(-30.f).valueRadians(); + rot.z = 0; + r = 300.f; + } else { + rot.x = Ogre::Degree(mPitch).valueRadians(); + rot.z = Ogre::Degree(mYaw).valueRadians(); + } + rotateImpl(rot, false, r); } void Player::togglePreviewMode() @@ -176,4 +207,28 @@ namespace MWRender /// \todo move camera mPreviewMode = !mPreviewMode; } + + float Player::getPitchAngle() + { + Ogre::Quaternion orient + = mCamera->getParentSceneNode()->getOrientation(); + + float angle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); + + return angle; + } + + float Player::getYawAngle() + { + Ogre::Quaternion orient + = mCameraNode->getOrientation(); + + float angle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); + if (orient.w < 0) { + angle = -angle; + } + return -angle; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 739b4cd8b2..d4c0eab5dc 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -3,9 +3,8 @@ #include - namespace Ogre -{ +{ class Vector3; class Camera; class SceneNode; @@ -30,15 +29,22 @@ namespace MWRender bool mVanityMode; bool mPreviewMode; + float mPitch, mYaw; + float mTimeIdle; + int mUpdates; float limitPitchAngle(float limitAbs, float shift = 0.f); /// Updates sound manager listener data void updateListener(); + void rotateImpl(Ogre::Vector3 &rot, bool adjust, float r); void rotateCamera(Ogre::Vector3 &rot, bool adjust); - void moveCamera(float r, float h); + void moveCamera(float r); + + float getYawAngle(); + float getPitchAngle(); public: diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 91d83caf3c..d5e28e4397 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -309,7 +309,17 @@ void RenderingManager::update (float duration){ mRendering.update(duration); - mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealOrientation() ); + float *fpos = + MWBase::Environment::get() + .getWorld() + ->getPlayer() + .getPlayer() + .getRefData() + .getPosition() + .pos; + Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + + mLocalMap->updatePlayer(pos, mRendering.getCamera()->getRealOrientation() ); if (mWater) { Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); @@ -323,7 +333,6 @@ void RenderingManager::update (float duration){ ); mWater->update(duration); } - mPlayer->update(duration); } From db94018865bef15fe5e3df7f06abbf18edceaf3a Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 13 Aug 2012 08:37:32 +0400 Subject: [PATCH 076/143] far better camera movement --- apps/openmw/mwrender/player.cpp | 66 ++++++--------------------------- apps/openmw/mwrender/player.hpp | 6 +-- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 53642d6152..8753d7c5da 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -24,20 +24,9 @@ namespace MWRender pitchNode->attachObject(mCamera); } - void Player::rotateImpl(Ogre::Vector3 &rot, bool adjust, float r) - { - rotateCamera(rot, adjust); - - if (mVanityMode || mPreviewMode || !mFirstPersonView) { - moveCamera(r); - } - updateListener(); - } - bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - Ogre::Vector3 _rot = rot; - rotateImpl(_rot, adjust, 400.f); + rotateCamera(rot, adjust); mUpdates = 0; mTimeIdle = 0.f; @@ -49,7 +38,7 @@ namespace MWRender return !mVanityMode && !mPreviewMode; } - void Player::rotateCamera(Ogre::Vector3 &rot, bool adjust) + void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) { Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); @@ -75,39 +64,7 @@ namespace MWRender pitchNode->setOrientation(xr); yawNode->setOrientation(yr); } - } - - void Player::moveCamera(float r) - { -/* - Ogre::Quaternion orient = - mCamera->getParentSceneNode()->getOrientation(); - - float pitchAngle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - - orient = mCameraNode->getOrientation(); - float yawAngle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); - - float tana = Ogre::Math::Tan(Ogre::Degree(pitchAngle)); - float tansq = tana * tana; - - float r1 = hsq * rsq / (hsq + rsq * tansq); - float zsq = r1 * tansq; - r1 = Ogre::Math::Sqrt(r1); - - Ogre::Vector3 pos; - pos.y = -Ogre::Math::Sqrt(zsq); - pos.z = r1 * Ogre::Math::Sin(Ogre::Degree(yawAngle).valueDegrees()); - pos.x = r1 * Ogre::Math::Cos(Ogre::Degree(yawAngle).valueDegrees()); -*/ - Ogre::Vector3 dir = mCamera->getRealDirection(); - dir.x = -dir.x, dir.y = -dir.y, dir.z = -dir.z; - - Ogre::Ray ray(Ogre::Vector3(0, 0, 0), dir); - - mCameraNode->setPosition(ray.getPoint(r)); + updateListener(); } std::string Player::getHandle() const @@ -168,7 +125,7 @@ namespace MWRender if (mVanityMode) { Ogre::Vector3 rot(0.f, 0.f, 0.f); rot.z = Ogre::Degree(3.f * duration).valueRadians(); - rotateImpl(rot, true, 300.f); + rotateCamera(rot, true); } } @@ -176,9 +133,9 @@ namespace MWRender { mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { - mCameraNode->setPosition(0.f, 0.f, 0.f); + mCamera->setPosition(0.f, 0.f, 0.f); } else { - moveCamera(400.f); + mCamera->setPosition(0.f, 0.f, 400.f); } } @@ -186,20 +143,21 @@ namespace MWRender { mVanityMode = !mVanityMode; - float r = 400.f; + float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanityMode) { - mPitch = getPitchAngle(); mYaw = getYawAngle(); + mPitch = getPitchAngle(); + mOffset = mCamera->getPosition().z; rot.x = Ogre::Degree(-30.f).valueRadians(); - rot.z = 0; - r = 300.f; } else { rot.x = Ogre::Degree(mPitch).valueRadians(); rot.z = Ogre::Degree(mYaw).valueRadians(); + offset = mOffset; } - rotateImpl(rot, false, r); + mCamera->setPosition(0.f, 0.f, offset); + rotateCamera(rot, false); } void Player::togglePreviewMode() diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index d4c0eab5dc..ed4923817b 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -29,7 +29,7 @@ namespace MWRender bool mVanityMode; bool mPreviewMode; - float mPitch, mYaw; + float mPitch, mYaw, mOffset; float mTimeIdle; int mUpdates; @@ -39,9 +39,7 @@ namespace MWRender /// Updates sound manager listener data void updateListener(); - void rotateImpl(Ogre::Vector3 &rot, bool adjust, float r); - void rotateCamera(Ogre::Vector3 &rot, bool adjust); - void moveCamera(float r); + void rotateCamera(const Ogre::Vector3 &rot, bool adjust); float getYawAngle(); float getPitchAngle(); From fe1a9ac3c575306ec2ef8a5ab866792d228f61e5 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 02:36:18 +0400 Subject: [PATCH 077/143] poor camera with some fixes --- apps/openmw/mwinput/mouselookevent.cpp | 2 +- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/player.cpp | 163 +++++++++++----------- apps/openmw/mwrender/player.hpp | 21 ++- apps/openmw/mwrender/renderingmanager.cpp | 19 ++- apps/openmw/mwworld/physicssystem.cpp | 42 +++--- 6 files changed, 129 insertions(+), 120 deletions(-) diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index f318ce6660..4138c481c5 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -24,5 +24,5 @@ void MouseLookEvent::event(Type type, int index, const void *p) float y = arg->state.Y.rel * sensY; MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 704a10cfef..0e85d32e07 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -290,7 +290,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } - Vector3 playerdirection = -mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); + Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8753d7c5da..5b74b99033 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -16,12 +16,16 @@ namespace MWRender : mCamera(camera), mPlayerNode(node), mCameraNode(mPlayerNode->createChildSceneNode()), + mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mVanityMode(false), - mPreviewMode(false) + mPreviewMode(false), + mHeight(40.f) { - Ogre::SceneNode *pitchNode = mCameraNode->createChildSceneNode(); - pitchNode->attachObject(mCamera); + mCameraNode->attachObject(mCamera); + mCameraNode->setPosition(0.f, 0.f, mHeight); + + mPreviewCam.yaw = 0.f; } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) @@ -32,7 +36,7 @@ namespace MWRender mTimeIdle = 0.f; if (mVanityMode) { - toggleVanityMode(); + toggleVanityMode(false); } return !mVanityMode && !mPreviewMode; @@ -44,26 +48,21 @@ namespace MWRender Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); if (adjust) { - float f = - limitPitchAngle(89.5f, Ogre::Radian(rot.x).valueDegrees()); - - if (f != 0.0) { - pitchNode->pitch(Ogre::Degree(f)); - } - yawNode->yaw(Ogre::Radian(-rot.z)); + setYaw(getYaw() + rot.z); + setPitch(getPitch() + rot.x); } else { - Ogre::Radian radx(rot.x); - if (radx.valueDegrees() > 89.5f) { - radx = Ogre::Degree(89.5f); - } else if (radx.valueDegrees() < -89.5f) { - radx = Ogre::Degree(-89.5f); - } - Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); - - pitchNode->setOrientation(xr); - yawNode->setOrientation(yr); + setYaw(rot.z); + setPitch(rot.x); } + Ogre::Quaternion xr( + Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), + Ogre::Vector3::UNIT_X + ); + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z); + + pitchNode->setOrientation(xr); + yawNode->setOrientation(zr); + updateListener(); } @@ -77,27 +76,6 @@ namespace MWRender ptr.getRefData().setBaseNode(mPlayerNode); } - float Player::limitPitchAngle(float limitAbs, float shift) - { - Ogre::Quaternion orient = - mCamera->getParentSceneNode()->getOrientation(); - - float pitchAngle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - - if (pitchAngle + shift < limitAbs && pitchAngle + shift > -limitAbs) { - return shift; - } - if (pitchAngle > 0) { - float f = limitAbs - pitchAngle - shift; - return (f > 0.f) ? f : 0.f; - } else if (pitchAngle < 0) { - float f = -limitAbs - pitchAngle - shift; - return (f < 0.f) ? f : 0.f; - } - return 0.f; - } - void Player::updateListener() { Ogre::Vector3 pos = mCamera->getRealPosition(); @@ -116,7 +94,7 @@ namespace MWRender ++mUpdates; mTimeIdle += duration; if (mTimeIdle > 30.f) { - toggleVanityMode(); + toggleVanityMode(true); } } if (mFirstPersonView && !mVanityMode) { @@ -139,54 +117,83 @@ namespace MWRender } } - void Player::toggleVanityMode() + void Player::toggleVanityMode(bool enable, bool force) { - mVanityMode = !mVanityMode; + if (mVanityMode == enable) { + return; + } + mVanityMode = enable; float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanityMode) { - mYaw = getYawAngle(); - mPitch = getPitchAngle(); - mOffset = mCamera->getPosition().z; - rot.x = Ogre::Degree(-30.f).valueRadians(); + mMainCam.offset = mCamera->getPosition().z; + + mPlayerNode->removeChild(mCameraNode); + mVanityNode->addChild(mCameraNode); } else { - rot.x = Ogre::Degree(mPitch).valueRadians(); - rot.z = Ogre::Degree(mYaw).valueRadians(); - offset = mOffset; + rot.x = getPitch(); + offset = mMainCam.offset; + + mVanityNode->removeChild(mCameraNode); + mPlayerNode->addChild(mCameraNode); } + rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); } - void Player::togglePreviewMode() + void Player::togglePreviewMode(bool enable) { /// \todo move camera - mPreviewMode = !mPreviewMode; - } - - float Player::getPitchAngle() - { - Ogre::Quaternion orient - = mCamera->getParentSceneNode()->getOrientation(); - - float angle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - - return angle; - } - - float Player::getYawAngle() - { - Ogre::Quaternion orient - = mCameraNode->getOrientation(); - - float angle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); - if (orient.w < 0) { - angle = -angle; + if (mPreviewMode == enable) { + return; + } + mPreviewMode = enable; + } + + float Player::getYaw() + { + if (mVanityMode || mPreviewMode) { + return mPreviewCam.yaw; + } + return mMainCam.yaw; + } + + void Player::setYaw(float angle) + { + if (angle > Ogre::Math::PI) { + angle -= Ogre::Math::TWO_PI; + } else if (angle < -Ogre::Math::PI) { + angle += Ogre::Math::TWO_PI; + } + if (mVanityMode || mPreviewMode) { + mPreviewCam.yaw = angle; + } else { + mMainCam.yaw = angle; + } + } + + float Player::getPitch() + { + if (mVanityMode || mPreviewMode) { + return mPreviewCam.pitch; + } + return mMainCam.pitch; + } + + void Player::setPitch(float angle) + { + if (angle > Ogre::Math::HALF_PI) { + angle = Ogre::Math::HALF_PI - 0.01; + } else if (angle < -Ogre::Math::HALF_PI) { + angle = -Ogre::Math::HALF_PI + 0.01; + } + if (mVanityMode || mPreviewMode) { + mPreviewCam.pitch = angle; + } else { + mMainCam.pitch = angle; } - return -angle; } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index ed4923817b..060d62fa00 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -20,29 +20,36 @@ namespace MWRender /// \brief Player character rendering and camera control class Player { + struct CamData { + float pitch, yaw, offset; + }; + Ogre::Camera *mCamera; Ogre::SceneNode *mPlayerNode; Ogre::SceneNode *mCameraNode; + Ogre::SceneNode *mVanityNode; bool mFirstPersonView; bool mVanityMode; bool mPreviewMode; - float mPitch, mYaw, mOffset; + float mHeight; + CamData mMainCam, mPreviewCam; float mTimeIdle; int mUpdates; - float limitPitchAngle(float limitAbs, float shift = 0.f); - /// Updates sound manager listener data void updateListener(); void rotateCamera(const Ogre::Vector3 &rot, bool adjust); - float getYawAngle(); - float getPitchAngle(); + float getYaw(); + void setYaw(float angle); + + float getPitch(); + void setPitch(float angle); public: @@ -62,9 +69,9 @@ namespace MWRender void toggleViewMode(); - void toggleVanityMode(); + void toggleVanityMode(bool enable, bool force = false); - void togglePreviewMode(); + void togglePreviewMode(bool enable); void update(float duration); }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d5e28e4397..cc08eae91f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -135,7 +135,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); - playerNode->pitch(Degree(90)); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mShadows = new Shadows(&mRendering); @@ -309,17 +308,23 @@ void RenderingManager::update (float duration){ mRendering.update(duration); - float *fpos = + MWWorld::RefData &data = MWBase::Environment::get() .getWorld() ->getPlayer() .getPlayer() - .getRefData() - .getPosition() - .pos; - Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + .getRefData(); - mLocalMap->updatePlayer(pos, mRendering.getCamera()->getRealOrientation() ); + float *fpos = data.getPosition().pos; + + /// \note only for LocalMap::updatePlayer() + Ogre::Vector3 pos(fpos[0], -fpos[2], fpos[1]); + + Ogre::SceneNode *node = data.getBaseNode(); + Ogre::Quaternion orient = + node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); + + mLocalMap->updatePlayer(mRendering.getCamera()->getRealPosition(), orient); if (mWater) { Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 45cfdd1235..1bbfe33fdc 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -11,7 +11,7 @@ #include -#include "../mwbase/world.hpp" // FIXME +//#include "../mwbase/world.hpp" // FIXME #include "ptr.hpp" #include "class.hpp" @@ -172,7 +172,7 @@ namespace MWWorld OEngine::Physic::PhysicActor* act = it->second; act->setWalkDirection(btVector3(0,0,0)); } - playerMove::playercmd& pm_ref = playerphysics->cmd; + playerMove::playercmd& pm_ref = playerphysics->cmd; pm_ref.rightmove = 0; pm_ref.forwardmove = 0; @@ -183,35 +183,25 @@ namespace MWWorld iter!=actors.end(); ++iter) { //dirty stuff to get the camera orientation. Must be changed! + Ogre::Quaternion orient = + mRender.getScene()->getSceneNode(iter->first)->getOrientation(); - Ogre::SceneNode *sceneNode = mRender.getScene()->getSceneNode (iter->first); - Ogre::Vector3 dir; - Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); - Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); - Ogre::Quaternion yawQuat = yawNode->getOrientation(); - Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); - - - - playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); - - playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() *-1 + 90; - - - Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); - - pm_ref.rightmove = -iter->second.x; - pm_ref.forwardmove = -iter->second.y; - pm_ref.upmove = iter->second.z; + float yaw = orient.getRoll().valueDegrees(); + float pitch = 0.f; + if (iter->first == "player") { + /// \fixme should not rely on player camera, real pitch needed + Ogre::SceneNode *node = mRender.getCamera()->getParentSceneNode(); + pitch = node->getOrientation().getPitch().valueDegrees(); + playerphysics->ps.viewangles.x = pitch - 90; + playerphysics->ps.viewangles.y = -yaw + 90; + pm_ref.rightmove = -iter->second.x; + pm_ref.forwardmove = -iter->second.y; + pm_ref.upmove = iter->second.z; + } } - - - - - mEngine->stepSimulation(dt); } From 6f87c0c36d912de51b943e7989d6c4117d568760 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 14:37:48 +0400 Subject: [PATCH 078/143] preview mode, advanced vanity support --- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwinput/inputmanagerimp.cpp | 27 ++-- apps/openmw/mwrender/player.cpp | 145 +++++++++++++++++----- apps/openmw/mwrender/player.hpp | 14 ++- apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwrender/renderingmanager.hpp | 12 ++ apps/openmw/mwworld/worldimp.hpp | 12 ++ 7 files changed, 169 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index c2db2acf1c..819a242ec4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -250,6 +250,9 @@ namespace MWBase virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; virtual void togglePOV() = 0; + virtual void togglePreviewMode(bool enable) = 0; + virtual bool toggleVanityMode(bool enable, bool force) = 0; + virtual void allowVanityMode(bool allow) = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 6bdd87d2ab..f0ca510ea5 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -406,19 +406,22 @@ private: else player.setUpDown (0); - if (poller.isDown(A_TogglePOV)) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += duration) > 0.5) - { - // enable preview mode + if (mControlSwitch["playerviewswitch"]) { + if (poller.isDown(A_TogglePOV)) { + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += duration) > 0.5) + { + MWBase::Environment::get().getWorld()->togglePreviewMode(true); + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + togglePOV(); + } + mPreviewPOVDelay = 0.f; } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - } else if (mPreviewPOVDelay > 0.f) { - togglePOV(); - } - mPreviewPOVDelay = 0.f; } } } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 5b74b99033..2a1feacad5 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -18,28 +18,49 @@ namespace MWRender mCameraNode(mPlayerNode->createChildSceneNode()), mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), - mVanityMode(false), mPreviewMode(false), - mHeight(40.f) + mHeight(40.f), + mCameraDistance(400.f) { + mVanity.enabled = false; + mVanity.allowed = true; + mVanity.forced = false; + mCameraNode->attachObject(mCamera); mCameraNode->setPosition(0.f, 0.f, mHeight); mPreviewCam.yaw = 0.f; + mPreviewCam.offset = 600.f; } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - rotateCamera(rot, adjust); - mUpdates = 0; mTimeIdle = 0.f; - if (mVanityMode) { + if (mVanity.enabled) { toggleVanityMode(false); } - return !mVanityMode && !mPreviewMode; + Ogre::Vector3 trueRot = rot; + + /// \note rotate player on forced vanity + if (mVanity.forced) { + moveCameraNode(mPlayerNode); + mVanity.enabled = false; + + rotateCamera(rot, adjust); + + moveCameraNode(mVanityNode); + mVanity.enabled = true; + + trueRot.z = 0.f; + } + + rotateCamera(trueRot, adjust); + + /// \note if vanity mode is forced by TVM then rotate player + return (!mVanity.enabled && !mPreviewMode) || mVanity.forced; } void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) @@ -90,19 +111,19 @@ namespace MWRender void Player::update(float duration) { - if (!mVanityMode) { + if (!mVanity.enabled) { ++mUpdates; mTimeIdle += duration; if (mTimeIdle > 30.f) { toggleVanityMode(true); } } - if (mFirstPersonView && !mVanityMode) { + if (mFirstPersonView && !mVanity.enabled) { return; } - if (mVanityMode) { + if (mVanity.enabled) { Ogre::Vector3 rot(0.f, 0.f, 0.f); - rot.z = Ogre::Degree(3.f * duration).valueRadians(); + rot.z = Ogre::Degree(-3.f * duration).valueRadians(); rotateCamera(rot, true); } } @@ -113,49 +134,75 @@ namespace MWRender if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); } else { - mCamera->setPosition(0.f, 0.f, 400.f); + mCamera->setPosition(0.f, 0.f, mCameraDistance); } } - - void Player::toggleVanityMode(bool enable, bool force) + + void Player::allowVanityMode(bool allow) { - if (mVanityMode == enable) { - return; + if (!allow && mVanity.enabled && !mVanity.forced) { + toggleVanityMode(false); } - mVanityMode = enable; + mVanity.allowed = allow; + } + + bool Player::toggleVanityMode(bool enable, bool force) + { + if ((mVanity.forced && !force) || + (!mVanity.allowed && (force || enable))) + { + return false; + } else if (mVanity.enabled == enable) { + return true; + } + mVanity.enabled = enable; + mVanity.forced = force && enable; float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); - if (mVanityMode) { + if (mVanity.enabled) { rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - mPlayerNode->removeChild(mCameraNode); - mVanityNode->addChild(mCameraNode); + moveCameraNode(mVanityNode); } else { rot.x = getPitch(); offset = mMainCam.offset; - mVanityNode->removeChild(mCameraNode); - mPlayerNode->addChild(mCameraNode); + moveCameraNode(mPlayerNode); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); + + return true; } void Player::togglePreviewMode(bool enable) { - /// \todo move camera if (mPreviewMode == enable) { return; } mPreviewMode = enable; + float offset = mCamera->getPosition().z; + if (mPreviewMode) { + mMainCam.offset = offset; + offset = mPreviewCam.offset; + + moveCameraNode(mVanityNode); + } else { + mPreviewCam.offset = offset; + offset = mMainCam.offset; + + moveCameraNode(mPlayerNode); + } + mCamera->setPosition(0.f, 0.f, mPreviewCam.offset); + rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } float Player::getYaw() { - if (mVanityMode || mPreviewMode) { + if (mVanity.enabled || mPreviewMode) { return mPreviewCam.yaw; } return mMainCam.yaw; @@ -168,7 +215,7 @@ namespace MWRender } else if (angle < -Ogre::Math::PI) { angle += Ogre::Math::TWO_PI; } - if (mVanityMode || mPreviewMode) { + if (mVanity.enabled || mPreviewMode) { mPreviewCam.yaw = angle; } else { mMainCam.yaw = angle; @@ -177,23 +224,59 @@ namespace MWRender float Player::getPitch() { - if (mVanityMode || mPreviewMode) { + if (mVanity.enabled || mPreviewMode) { return mPreviewCam.pitch; } return mMainCam.pitch; } void Player::setPitch(float angle) - { - if (angle > Ogre::Math::HALF_PI) { - angle = Ogre::Math::HALF_PI - 0.01; - } else if (angle < -Ogre::Math::HALF_PI) { - angle = -Ogre::Math::HALF_PI + 0.01; + { + float limit = Ogre::Math::HALF_PI; + if (mVanity.forced || mPreviewMode) { + limit /= 2; } - if (mVanityMode || mPreviewMode) { + if (angle > limit) { + angle = limit - 0.01; + } else if (angle < -limit) { + angle = -limit + 0.01; + } + if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; } else { mMainCam.pitch = angle; } } + + void Player::moveCameraNode(Ogre::SceneNode *node) + { + mCameraNode->getParentSceneNode()->removeChild(mCameraNode); + node->addChild(mCameraNode); + } + + void Player::setCameraDistance(float dist, bool adjust) + { + /// \note non-Morrowind feature: allow to change camera distance + /// int 3d-person mode + /// \todo review and simplify condition if possible + if (mPreviewMode || + mVanity.forced || + (!mVanity.enabled && !mFirstPersonView)) + { + Ogre::Vector3 v(0.f, 0.f, dist); + if (adjust) { + v += mCamera->getPosition(); + } + if (v.z > 800.f) { + v.z = 800.f; + } else if (v.z < 100.f) { + v.z = 100.f; + } + mCamera->setPosition(v); + + if (!mVanity.enabled && !mFirstPersonView) { + mCameraDistance = v.z; + } + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 060d62fa00..1ca9bfc21f 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -31,10 +31,13 @@ namespace MWRender Ogre::SceneNode *mVanityNode; bool mFirstPersonView; - bool mVanityMode; bool mPreviewMode; - float mHeight; + struct { + bool enabled, allowed, forced; + } mVanity; + + float mHeight, mCameraDistance; CamData mMainCam, mPreviewCam; float mTimeIdle; @@ -51,6 +54,8 @@ namespace MWRender float getPitch(); void setPitch(float angle); + void moveCameraNode(Ogre::SceneNode *node); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); @@ -69,11 +74,14 @@ namespace MWRender void toggleViewMode(); - void toggleVanityMode(bool enable, bool force = false); + bool toggleVanityMode(bool enable, bool force = false); + void allowVanityMode(bool allow); void togglePreviewMode(bool enable); void update(float duration); + + void setCameraDistance(float dist, bool adjust = false); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index cc08eae91f..05902d662d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -318,13 +318,13 @@ void RenderingManager::update (float duration){ float *fpos = data.getPosition().pos; /// \note only for LocalMap::updatePlayer() - Ogre::Vector3 pos(fpos[0], -fpos[2], fpos[1]); + Ogre::Vector3 pos(fpos[0], -fpos[2], -fpos[1]); Ogre::SceneNode *node = data.getBaseNode(); Ogre::Quaternion orient = node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); - mLocalMap->updatePlayer(mRendering.getCamera()->getRealPosition(), orient); + mLocalMap->updatePlayer(pos, orient); if (mWater) { Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f100fdd592..741ccc559c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -60,6 +60,18 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->toggleViewMode(); } + void togglePreviewMode(bool enable) { + mPlayer->togglePreviewMode(enable); + } + + virtual bool toggleVanityMode(bool enable, bool force) { + return mPlayer->toggleVanityMode(enable, force); + } + + virtual void allowVanityMode(bool allow) { + mPlayer->allowVanityMode(allow); + } + void attachCameraTo(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2e5f73702b..26824e7639 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -280,6 +280,18 @@ namespace MWWorld virtual void togglePOV() { mRendering->togglePOV(); } + + virtual void togglePreviewMode(bool enable) { + mRendering->togglePreviewMode(enable); + } + + virtual bool toggleVanityMode(bool enable, bool force) { + return mRendering->toggleVanityMode(enable, force); + } + + virtual void allowVanityMode(bool allow) { + mRendering->allowVanityMode(allow); + } }; } From 392e6efcb52e5de6f0acadcf9861f48f77c26590 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 20:33:29 +0400 Subject: [PATCH 079/143] initial player rendering --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwinput/inputmanagerimp.cpp | 1 + apps/openmw/mwrender/player.cpp | 21 ++++++++++++++++++++- apps/openmw/mwrender/player.hpp | 10 ++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 12 ++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 9 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b8421cc628..b818477e66 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -352,6 +352,8 @@ void OMW::Engine::go() pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.pos[2] = 0; + mEnvironment.getWorld()->renderPlayer(); + if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) { MWBase::Environment::get().getWorld()->indexToPosition (exterior->data.gridX, exterior->data.gridY, diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 819a242ec4..fbc57b4874 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -253,6 +253,8 @@ namespace MWBase virtual void togglePreviewMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; + + virtual void renderPlayer() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f0ca510ea5..9295233de6 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -411,6 +411,7 @@ private: if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += duration) > 0.5) { + mPreviewPOVDelay = 1.f; MWBase::Environment::get().getWorld()->togglePreviewMode(true); } } else { diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 2a1feacad5..c20ffad55d 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -10,6 +10,8 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/refdata.hpp" +#include "npcanimation.hpp" + namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) @@ -19,7 +21,7 @@ namespace MWRender mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mPreviewMode(false), - mHeight(40.f), + mHeight(128.f), mCameraDistance(400.f) { mVanity.enabled = false; @@ -111,6 +113,7 @@ namespace MWRender void Player::update(float duration) { + Ogre::Vector3 pos = mPlayerNode->getPosition(); if (!mVanity.enabled) { ++mUpdates; mTimeIdle += duration; @@ -118,6 +121,9 @@ namespace MWRender toggleVanityMode(true); } } + if (mAnimation) { + mAnimation->runAnimation(duration); + } if (mFirstPersonView && !mVanity.enabled) { return; } @@ -133,8 +139,10 @@ namespace MWRender mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); + mCameraNode->setPosition(0.f, 0.f, 128.f); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); + mCameraNode->setPosition(0.f, 0.f, 104.f); } } @@ -279,4 +287,15 @@ namespace MWRender } } } + + void Player::setHeight(float height) + { + mHeight = height; + mCameraNode->setPosition(0.f, 0.f, mHeight); + } + + float Player::getHeight() + { + return mHeight; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 1ca9bfc21f..82af243bcb 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -17,6 +17,7 @@ namespace MWWorld namespace MWRender { + class NpcAnimation; /// \brief Player character rendering and camera control class Player { @@ -30,6 +31,8 @@ namespace MWRender Ogre::SceneNode *mCameraNode; Ogre::SceneNode *mVanityNode; + NpcAnimation *mAnimation; + bool mFirstPersonView; bool mPreviewMode; @@ -82,6 +85,13 @@ namespace MWRender void update(float duration); void setCameraDistance(float dist, bool adjust = false); + + void setAnimation(MWRender::NpcAnimation *anim) { + mAnimation = anim; + } + + void setHeight(float height); + float getHeight(); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 05902d662d..8b6f483ca7 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -33,6 +33,7 @@ #include "localmap.hpp" #include "water.hpp" #include "compositors.hpp" +#include "npcanimation.hpp" using namespace MWRender; using namespace Ogre; @@ -830,4 +831,15 @@ void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) mPlayer->attachTo(ptr); } +void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) +{ + MWRender::NpcAnimation *anim = + new MWRender::NpcAnimation( + ptr, + mRendering, + MWWorld::Class::get(ptr).getInventoryStore(ptr) + ); + mPlayer->setAnimation(anim); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 741ccc559c..aa07bbc220 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -73,6 +73,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList } void attachCameraTo(const MWWorld::Ptr &ptr); + void renderPlayer(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); Compositors* getCompositors(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 20706438da..7a8f730345 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1151,4 +1151,9 @@ namespace MWWorld } return pos.z < cell.water; } + + void World::renderPlayer() + { + mRendering->renderPlayer(mPlayer->getPlayer()); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 26824e7639..717c43cfa2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -292,6 +292,8 @@ namespace MWWorld virtual void allowVanityMode(bool allow) { mRendering->allowVanityMode(allow); } + + virtual void renderPlayer(); }; } From 783e81afc3fcd296c8f9366a4ab3812479216c04 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 22:39:42 +0400 Subject: [PATCH 080/143] fix 1st/preview/1st change; 1st invisible model --- apps/openmw/mwrender/player.cpp | 14 +++++++++++++- apps/openmw/mwrender/renderingmanager.cpp | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index c20ffad55d..4a6ce03702 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -31,6 +31,8 @@ namespace MWRender mCameraNode->attachObject(mCamera); mCameraNode->setPosition(0.f, 0.f, mHeight); + mPlayerNode->setVisible(false); + mPreviewCam.yaw = 0.f; mPreviewCam.offset = 600.f; } @@ -140,9 +142,11 @@ namespace MWRender if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); mCameraNode->setPosition(0.f, 0.f, 128.f); + mPlayerNode->setVisible(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); mCameraNode->setPosition(0.f, 0.f, 104.f); + mPlayerNode->setVisible(true); } } @@ -169,6 +173,7 @@ namespace MWRender float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { + mPlayerNode->setVisible(true); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; @@ -179,6 +184,9 @@ namespace MWRender moveCameraNode(mPlayerNode); } + if (offset == 0.f) { + mPlayerNode->setVisible(false); + } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); @@ -194,6 +202,7 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { + mPlayerNode->setVisible(true); mMainCam.offset = offset; offset = mPreviewCam.offset; @@ -204,7 +213,10 @@ namespace MWRender moveCameraNode(mPlayerNode); } - mCamera->setPosition(0.f, 0.f, mPreviewCam.offset); + if (offset == 0.f) { + mPlayerNode->setVisible(false); + } + mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8b6f483ca7..87faba3e79 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -131,10 +131,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const SceneNode *rt = mRendering.getScene()->getRootSceneNode(); mMwRoot = rt->createChildSceneNode(); mMwRoot->pitch(Degree(-90)); + mObjects.setMwRoot(mMwRoot); mActors.setMwRoot(mMwRoot); - Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); From 575244bd35898c146c2e9cf81879504f8f8a4937 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 15 Aug 2012 15:17:35 +0400 Subject: [PATCH 081/143] fix SkaManager issue --- apps/openmw/mwrender/player.cpp | 23 +++++++++++------------ apps/openmw/mwrender/player.hpp | 4 +--- apps/openmw/mwrender/sky.cpp | 2 ++ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 4a6ce03702..6e0012d438 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -31,8 +31,6 @@ namespace MWRender mCameraNode->attachObject(mCamera); mCameraNode->setPosition(0.f, 0.f, mHeight); - mPlayerNode->setVisible(false); - mPreviewCam.yaw = 0.f; mPreviewCam.offset = 600.f; } @@ -142,12 +140,11 @@ namespace MWRender if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); mCameraNode->setPosition(0.f, 0.f, 128.f); - mPlayerNode->setVisible(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); mCameraNode->setPosition(0.f, 0.f, 104.f); - mPlayerNode->setVisible(true); } + mPlayerNode->setVisible(!mFirstPersonView, false); } void Player::allowVanityMode(bool allow) @@ -173,7 +170,7 @@ namespace MWRender float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { - mPlayerNode->setVisible(true); + mPlayerNode->setVisible(true, false); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; @@ -183,9 +180,7 @@ namespace MWRender offset = mMainCam.offset; moveCameraNode(mPlayerNode); - } - if (offset == 0.f) { - mPlayerNode->setVisible(false); + mPlayerNode->setVisible(!mFirstPersonView, false); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); @@ -202,7 +197,7 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { - mPlayerNode->setVisible(true); + mPlayerNode->setVisible(true, false); mMainCam.offset = offset; offset = mPreviewCam.offset; @@ -212,9 +207,7 @@ namespace MWRender offset = mMainCam.offset; moveCameraNode(mPlayerNode); - } - if (offset == 0.f) { - mPlayerNode->setVisible(false); + mPlayerNode->setVisible(!mFirstPersonView, false); } mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); @@ -300,6 +293,12 @@ namespace MWRender } } + void Player::setAnimation(NpcAnimation *anim) + { + mAnimation = anim; + mPlayerNode->setVisible(!mFirstPersonView, false); + } + void Player::setHeight(float height) { mHeight = height; diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 82af243bcb..8c1b3ed9ff 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -86,9 +86,7 @@ namespace MWRender void setCameraDistance(float dist, bool adjust = false); - void setAnimation(MWRender::NpcAnimation *anim) { - mAnimation = anim; - } + void setAnimation(MWRender::NpcAnimation *anim); void setHeight(float height); float getHeight(); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 9e551ba2a8..3eb42a05e5 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -405,6 +405,8 @@ void SkyManager::update(float duration) { if (!mEnabled) return; + mRootNode->setPosition(mCamera->getPosition()); + // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed * (MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", From b97b3d7b71b07538657edd924976e5b294c4bf01 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Aug 2012 17:56:24 +0200 Subject: [PATCH 082/143] fix sky position when reflection is enabled, fix delay in sky reflection --- apps/openmw/mwrender/sky.cpp | 10 ++++++---- apps/openmw/mwrender/water.cpp | 4 +--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 3eb42a05e5..eba605d0c2 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -280,7 +280,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mMoonRed(false) { mSceneMgr = pMwRoot->getCreator(); - mRootNode = mCamera->getParentSceneNode()->createChildSceneNode(); + mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates mRootNode->setInheritOrientation(false); } @@ -405,7 +405,8 @@ void SkyManager::update(float duration) { if (!mEnabled) return; - mRootNode->setPosition(mCamera->getPosition()); + mCamera->getParentSceneNode ()->needUpdate (); + mRootNode->setPosition(mCamera->getDerivedPosition()); // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed * (MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); @@ -668,12 +669,13 @@ Ogre::SceneNode* SkyManager::getSunNode() void SkyManager::setSkyPosition(const Ogre::Vector3& position) { - mRootNode->_setDerivedPosition(position); + mRootNode->setPosition(position); } void SkyManager::resetSkyPosition() { - mRootNode->setPosition(0,0,0); + mCamera->getParentSceneNode ()->needUpdate (); + mRootNode->setPosition(mCamera->getDerivedPosition()); } void SkyManager::scaleSky(float scale) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index d5b93b7cbe..46678f2bc5 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -207,6 +207,7 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) { if (evt.source == mReflectionTarget) { + mCamera->getParentSceneNode ()->needUpdate (); mReflectionCamera->setOrientation(mCamera->getDerivedOrientation()); mReflectionCamera->setPosition(mCamera->getDerivedPosition()); mReflectionCamera->setNearClipDistance(mCamera->getNearClipDistance()); @@ -215,11 +216,9 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) mReflectionCamera->setFOVy(mCamera->getFOVy()); mReflectionRenderActive = true; - /// \todo the reflection render (and probably all renderingmanager-updates) lag behind 1 camera frame for some reason Vector3 pos = mCamera->getRealPosition(); pos.y = mTop*2 - pos.y; mSky->setSkyPosition(pos); - mSky->scaleSky(mCamera->getFarClipDistance() / 50.f); mReflectionCamera->enableReflection(mWaterPlane); } } @@ -229,7 +228,6 @@ void Water::postRenderTargetUpdate(const RenderTargetEvent& evt) if (evt.source == mReflectionTarget) { mSky->resetSkyPosition(); - mSky->scaleSky(1); mReflectionCamera->disableReflection(); mReflectionCamera->disableCustomNearClipPlane(); mReflectionRenderActive = false; From 34f796c38e01d7701706179588d502c74326d998 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Aug 2012 18:29:21 +0200 Subject: [PATCH 083/143] removed the far clip distance hack which is not needed anymore --- apps/openmw/mwrender/water.cpp | 16 ---------------- apps/openmw/mwrender/water.hpp | 3 --- 2 files changed, 19 deletions(-) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 46678f2bc5..e79f308cd5 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -32,7 +32,6 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mIsUnderwater(false), mVisibilityFlags(0), mReflectionTarget(0), mActive(1), mToggled(1), mReflectionRenderActive(false), mRendering(rend), - mOldFarClip(0), mOldFarClip2(0), mWaterTimer(0.f) { mSky = rend->getSkyManager(); @@ -267,34 +266,19 @@ void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &in // We don't want the sky to get clipped by custom near clip plane (the water plane) if (queueGroupId < 20 && mReflectionRenderActive) { - mOldFarClip = mReflectionCamera->getFarClipDistance (); mReflectionCamera->disableCustomNearClipPlane(); - mReflectionCamera->setFarClipDistance (1000000000); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } - else if (queueGroupId == RQG_UnderWater) - {/* - mOldFarClip2 = mCamera->getFarClipDistance (); - mCamera->setFarClipDistance (1000000000); - Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); - */} } void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) { if (queueGroupId < 20 && mReflectionRenderActive) { - mReflectionCamera->setFarClipDistance (mOldFarClip); if (!mIsUnderwater) mReflectionCamera->enableCustomNearClipPlane(mErrorPlane); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } - if (queueGroupId == RQG_UnderWater) - { - /* - mCamera->setFarClipDistance (mOldFarClip2); - Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); - */} } void Water::update(float dt) diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index f56ba7410c..dcb76533b0 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -50,9 +50,6 @@ namespace MWRender { bool mToggled; int mTop; - int mOldFarClip; - int mOldFarClip2; - float mWaterTimer; bool mReflectionRenderActive; From a453a7f035fbd4e21a5790258fce123415309966 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 16 Aug 2012 13:15:38 +0400 Subject: [PATCH 084/143] camera adjustment, fix view mode on start --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +- apps/openmw/mwrender/player.cpp | 70 +++++++++++++++-------- apps/openmw/mwrender/player.hpp | 15 ++++- apps/openmw/mwrender/renderingmanager.cpp | 20 ++++++- 4 files changed, 80 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9295233de6..51c77e68e0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -242,7 +242,8 @@ private: player(_player), windows(_windows), mEngine (engine), - mDragDrop(false) + mDragDrop(false), + mPreviewPOVDelay(0.f) { using namespace OEngine::Input; using namespace OEngine::Render; diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 6e0012d438..68def64e81 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -22,7 +22,8 @@ namespace MWRender mFirstPersonView(true), mPreviewMode(false), mHeight(128.f), - mCameraDistance(400.f) + mCameraDistance(300.f), + mDistanceAdjusted(false) { mVanity.enabled = false; mVanity.allowed = true; @@ -32,7 +33,7 @@ namespace MWRender mCameraNode->setPosition(0.f, 0.f, mHeight); mPreviewCam.yaw = 0.f; - mPreviewCam.offset = 600.f; + mPreviewCam.offset = 400.f; } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) @@ -167,7 +168,7 @@ namespace MWRender mVanity.enabled = enable; mVanity.forced = force && enable; - float offset = 300.f; + float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { mPlayerNode->setVisible(true, false); @@ -267,32 +268,45 @@ namespace MWRender node->addChild(mCameraNode); } - void Player::setCameraDistance(float dist, bool adjust) + void Player::setCameraDistance(float dist, bool adjust, bool override) { - /// \note non-Morrowind feature: allow to change camera distance - /// int 3d-person mode - /// \todo review and simplify condition if possible - if (mPreviewMode || - mVanity.forced || - (!mVanity.enabled && !mFirstPersonView)) - { - Ogre::Vector3 v(0.f, 0.f, dist); - if (adjust) { - v += mCamera->getPosition(); - } - if (v.z > 800.f) { - v.z = 800.f; - } else if (v.z < 100.f) { - v.z = 100.f; - } - mCamera->setPosition(v); + if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { + return; + } + Ogre::Vector3 v(0.f, 0.f, dist); + if (adjust) { + v += mCamera->getPosition(); + } + if (v.z > 800.f) { + v.z = 800.f; + } else if (v.z < 10.f) { + v.z = 10.f; + } + mCamera->setPosition(v); - if (!mVanity.enabled && !mFirstPersonView) { + if (override) { + if (mVanity.enabled || mPreviewMode) { + mPreviewCam.offset = v.z; + } else if (!mFirstPersonView) { mCameraDistance = v.z; } + } else { + mDistanceAdjusted = true; } } + void Player::setCameraDistance() + { + if (mDistanceAdjusted) { + if (mVanity.enabled || mPreviewMode) { + mCamera->setPosition(0, 0, mPreviewCam.offset); + } else if (!mFirstPersonView) { + mCamera->setPosition(0, 0, mCameraDistance); + } + } + mDistanceAdjusted = false; + } + void Player::setAnimation(NpcAnimation *anim) { mAnimation = anim; @@ -307,6 +321,16 @@ namespace MWRender float Player::getHeight() { - return mHeight; + return mHeight * mPlayerNode->getScale().z; + } + + bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) + { + float xch; + camera = mCamera->getRealPosition(); + xch = camera.z, camera.z = camera.y, camera.y = -xch; + player = mPlayerNode->getPosition(); + + return mFirstPersonView && !mVanity.enabled && !mPreviewMode; } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 8c1b3ed9ff..c3585db9d0 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -43,6 +43,8 @@ namespace MWRender float mHeight, mCameraDistance; CamData mMainCam, mPreviewCam; + bool mDistanceAdjusted; + float mTimeIdle; int mUpdates; @@ -84,12 +86,23 @@ namespace MWRender void update(float duration); - void setCameraDistance(float dist, bool adjust = false); + /// Set camera distance for current mode. Don't work on 1st person view. + /// \param adjust Indicates should distance be adjusted or set. + /// \param override If true new distance will be used as default. + /// If false, default distance can be restored with setCameraDistance(). + void setCameraDistance(float dist, bool adjust = false, bool override = true); + + /// Restore default camera distance for current mode. + void setCameraDistance(); void setAnimation(MWRender::NpcAnimation *anim); void setHeight(float height); float getHeight(); + + /// Stores player and camera world positions in passed arguments + /// \return true if camera at the eye-place + bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 87faba3e79..f1d2029bb1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -41,7 +41,7 @@ using namespace Ogre; namespace MWRender { RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine) - :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0) + :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0), mPhysicsEngine(engine) { // select best shader mode bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); @@ -296,7 +296,22 @@ RenderingManager::moveObjectToCell( child->setPosition(pos); } -void RenderingManager::update (float duration){ +void RenderingManager::update (float duration) +{ + Ogre::Vector3 orig, dest; + mPlayer->setCameraDistance(); + if (!mPlayer->getPosition(orig, dest)) { + orig.z += mPlayer->getHeight() * mMwRoot->getScale().z; + + btVector3 btOrig(orig.x, orig.y, orig.z); + btVector3 btDest(dest.x, dest.y, dest.z); + std::pair test = + mPhysicsEngine->rayTest(btOrig, btDest); + if (!test.first.empty()) { + mPlayer->setCameraDistance(test.second * orig.distance(dest), false, false); + } + } + mPlayer->update(duration); mActors.update (duration); mObjects.update (duration); @@ -339,7 +354,6 @@ void RenderingManager::update (float duration){ ); mWater->update(duration); } - mPlayer->update(duration); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ From 40c7a850bf2e7e889d7d17b5733705f27494bbf9 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 16 Aug 2012 15:42:03 +0400 Subject: [PATCH 085/143] fix pmove BB shape origin --- libs/openengine/bullet/trace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 2d18aaa4da..89d654e6a5 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -97,8 +97,8 @@ const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& - const btVector3 btstart(start.x, start.y, start.z); - const btVector3 btend(end.x, end.y, end.z); + const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); + const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); const btQuaternion btrot(rotation.y, rotation.x, rotation.z); //y, x, z const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); From e8fc942bef4b530e31e02a631717097478a04da0 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 16 Aug 2012 16:24:59 +0400 Subject: [PATCH 086/143] reverse Z-axis rotation to make doors happy --- apps/openmw/mwinput/mouselookevent.cpp | 2 +- apps/openmw/mwrender/player.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index 4138c481c5..f318ce6660 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -24,5 +24,5 @@ void MouseLookEvent::event(Type type, int index, const void *p) float y = arg->state.Y.rel * sensY; MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 68def64e81..8ad7e04b03 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -82,7 +82,7 @@ namespace MWRender Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X ); - Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); pitchNode->setOrientation(xr); yawNode->setOrientation(zr); @@ -130,7 +130,7 @@ namespace MWRender } if (mVanity.enabled) { Ogre::Vector3 rot(0.f, 0.f, 0.f); - rot.z = Ogre::Degree(-3.f * duration).valueRadians(); + rot.z = Ogre::Degree(3.f * duration).valueRadians(); rotateCamera(rot, true); } } From 6961830efb5f4141d9ed97256672330d0a82109a Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 10:10:37 +0400 Subject: [PATCH 087/143] using real player eye direction when moving --- apps/openmw/mwrender/player.cpp | 12 ++++++++++++ apps/openmw/mwrender/player.hpp | 3 +++ apps/openmw/mwrender/renderingmanager.cpp | 7 +++++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/physicssystem.cpp | 24 ++++++++++++++--------- apps/openmw/mwworld/physicssystem.hpp | 8 +++++++- apps/openmw/mwworld/worldimp.cpp | 5 +++++ 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8ad7e04b03..c9db96eadb 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -11,6 +11,7 @@ #include "../mwworld/refdata.hpp" #include "npcanimation.hpp" +#include namespace MWRender { @@ -333,4 +334,15 @@ namespace MWRender return mFirstPersonView && !mVanity.enabled && !mPreviewMode; } + + Ogre::Vector3 Player::getPosition() + { + return mPlayerNode->getPosition(); + } + + void Player::getSightAngles(float &pitch, float &yaw) + { + pitch = mMainCam.pitch; + yaw = mMainCam.yaw; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index c3585db9d0..cc0fcac77b 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -103,6 +103,9 @@ namespace MWRender /// Stores player and camera world positions in passed arguments /// \return true if camera at the eye-place bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera); + Ogre::Vector3 getPosition(); + + void getSightAngles(float &pitch, float &yaw); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f1d2029bb1..2d29e8f556 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -856,4 +856,11 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) mPlayer->setAnimation(anim); } +void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) +{ + eyepos = mPlayer->getPosition(); + eyepos.z += mPlayer->getHeight(); + mPlayer->getSightAngles(pitch, yaw); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index aa07bbc220..1700835567 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->allowVanityMode(allow); } + void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); + void attachCameraTo(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1bbfe33fdc..24e2cdbb19 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -16,6 +16,8 @@ #include "ptr.hpp" #include "class.hpp" +#include + using namespace Ogre; namespace MWWorld { @@ -42,10 +44,8 @@ namespace MWWorld return mEngine; } - std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) - { - std::string handle = ""; - + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) + { //get a ray pointing to the center of the viewport Ray centerRay = mRender.getCamera()->getCameraToViewportRay( mRender.getViewport()->getWidth()/2, @@ -190,12 +190,11 @@ namespace MWWorld float pitch = 0.f; if (iter->first == "player") { - /// \fixme should not rely on player camera, real pitch needed - Ogre::SceneNode *node = mRender.getCamera()->getParentSceneNode(); - pitch = node->getOrientation().getPitch().valueDegrees(); + playerphysics->ps.viewangles.x = + Ogre::Radian(mPlayerData.pitch).valueDegrees(); - playerphysics->ps.viewangles.x = pitch - 90; - playerphysics->ps.viewangles.y = -yaw + 90; + playerphysics->ps.viewangles.y = + Ogre::Radian(mPlayerData.yaw).valueDegrees() + 90; pm_ref.rightmove = -iter->second.x; pm_ref.forwardmove = -iter->second.y; @@ -392,4 +391,11 @@ namespace MWWorld return true; } + + void PhysicsSystem::updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw) + { + mPlayerData.eyepos = eyepos; + mPlayerData.pitch = pitch; + mPlayerData.yaw = yaw; + } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index e42fa536b9..c6b8199fab 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -70,7 +70,14 @@ namespace MWWorld bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); + void updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw); + private: + struct { + Ogre::Vector3 eyepos; + float pitch, yaw; + } mPlayerData; + OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; bool mFreeFly; @@ -80,7 +87,6 @@ namespace MWWorld PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); }; - } #endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7a8f730345..655397c1f6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -806,6 +806,11 @@ namespace MWWorld /// \todo split this function up into subfunctions mWorldScene->update (duration); + + float pitch, yaw; + Ogre::Vector3 eyepos; + mRendering->getPlayerData(eyepos, pitch, yaw); + mPhysics->updatePlayerData(eyepos, pitch, yaw); mWeatherManager->update (duration); From 60fb1e8df6dd3117ee6f0102d08e195b14c4c8ba Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 12:08:31 +0400 Subject: [PATCH 088/143] fix sky in preview mode movement --- apps/openmw/mwrender/player.cpp | 50 ++++++++++++++++++--------------- apps/openmw/mwrender/player.hpp | 2 +- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index c9db96eadb..f85e949d0f 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -19,7 +19,6 @@ namespace MWRender : mCamera(camera), mPlayerNode(node), mCameraNode(mPlayerNode->createChildSceneNode()), - mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mPreviewMode(false), mHeight(128.f), @@ -50,14 +49,14 @@ namespace MWRender /// \note rotate player on forced vanity if (mVanity.forced) { - moveCameraNode(mPlayerNode); + float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; + mVanity.enabled = false; - rotateCamera(rot, adjust); - - moveCameraNode(mVanityNode); mVanity.enabled = true; + compensateYaw(diff); + trueRot.z = 0.f; } @@ -69,9 +68,6 @@ namespace MWRender void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) { - Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); - Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - if (adjust) { setYaw(getYaw() + rot.z); setPitch(getPitch() + rot.x); @@ -83,11 +79,16 @@ namespace MWRender Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X ); - Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); - - pitchNode->setOrientation(xr); - yawNode->setOrientation(zr); - + Ogre::Quaternion zr( + Ogre::Radian(getYaw()), + Ogre::Vector3::NEGATIVE_UNIT_Z + ); + if (!mVanity.enabled && !mPreviewMode) { + mPlayerNode->setOrientation(zr); + mCameraNode->setOrientation(xr); + } else { + mCameraNode->setOrientation(zr * xr); + } updateListener(); } @@ -176,12 +177,10 @@ namespace MWRender rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - moveCameraNode(mVanityNode); } else { rot.x = getPitch(); offset = mMainCam.offset; - moveCameraNode(mPlayerNode); mPlayerNode->setVisible(!mFirstPersonView, false); } rot.z = getYaw(); @@ -203,12 +202,10 @@ namespace MWRender mMainCam.offset = offset; offset = mPreviewCam.offset; - moveCameraNode(mVanityNode); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; - moveCameraNode(mPlayerNode); mPlayerNode->setVisible(!mFirstPersonView, false); } mCamera->setPosition(0.f, 0.f, offset); @@ -263,12 +260,6 @@ namespace MWRender } } - void Player::moveCameraNode(Ogre::SceneNode *node) - { - mCameraNode->getParentSceneNode()->removeChild(mCameraNode); - node->addChild(mCameraNode); - } - void Player::setCameraDistance(float dist, bool adjust, bool override) { if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { @@ -345,4 +336,17 @@ namespace MWRender pitch = mMainCam.pitch; yaw = mMainCam.yaw; } + + void Player::compensateYaw(float diff) + { + mPreviewCam.yaw -= diff; + Ogre::Quaternion zr( + Ogre::Radian(mPreviewCam.yaw), + Ogre::Vector3::NEGATIVE_UNIT_Z + ); + Ogre::Quaternion xr( + Ogre::Radian(mPreviewCam.pitch), + Ogre::Vector3::UNIT_X); + mCameraNode->setOrientation(zr * xr); + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index cc0fcac77b..3f419889af 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -59,7 +59,7 @@ namespace MWRender float getPitch(); void setPitch(float angle); - void moveCameraNode(Ogre::SceneNode *node); + void compensateYaw(float diff); public: From 0e6e141fd484609ea95c748661e5342ee5dfc8a5 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 13:23:02 +0400 Subject: [PATCH 089/143] camera control related script instructions --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 23 +++++++++++++--------- apps/openmw/mwinput/inputmanagerimp.hpp | 3 ++- apps/openmw/mwrender/player.cpp | 24 +++++++++++++++-------- apps/openmw/mwrender/player.hpp | 4 +++- apps/openmw/mwrender/renderingmanager.hpp | 4 ++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ 7 files changed, 44 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fbc57b4874..9c801cbff5 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -253,6 +253,7 @@ namespace MWBase virtual void togglePreviewMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; + virtual void togglePlayerLooking(bool enable) = 0; virtual void renderPlayer() = 0; }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 51c77e68e0..df0a42996f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -443,10 +443,7 @@ private: } else { - // Start mouse-looking again if allowed. - if (mControlSwitch["playerlooking"]) { - mouse->enable(); - } + mouse->enable(); // Disable GUI events guiEvents->enabled = false; @@ -467,16 +464,19 @@ private: } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time player.setUpDown(0); + } else if (sw == "vanitymode") { + MWBase::Environment::get().getWorld()->allowVanityMode(value); } else if (sw == "playerlooking") { - if (value) { - mouse->enable(); - } else { - mouse->disable(); - } + MWBase::Environment::get().getWorld()->togglePlayerLooking(value); } mControlSwitch[sw] = value; } + bool getControlSwitch(std::string sw) + { + return mControlSwitch[sw]; + } + void togglePOV() { MWBase::Environment::get().getWorld()->togglePOV(); @@ -535,4 +535,9 @@ private: { impl->toggleControlSwitch(sw, value); } + + bool MWInputManager::getControlSwitch(std::string sw) + { + return impl->getControlSwitch(sw); + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 70436e2078..eff2236d4d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -60,7 +60,8 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); - virtual void toggleControlSwitch (const std::string& sw, bool value); + void toggleControlSwitch(std::string sw, bool value); + bool getControlSwitch(std::string sw); }; } #endif diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index f85e949d0f..3bbb569076 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -11,7 +11,6 @@ #include "../mwworld/refdata.hpp" #include "npcanimation.hpp" -#include namespace MWRender { @@ -21,6 +20,7 @@ namespace MWRender mCameraNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mPreviewMode(false), + mFreeLook(true), mHeight(128.f), mCameraDistance(300.f), mDistanceAdjusted(false) @@ -49,18 +49,21 @@ namespace MWRender /// \note rotate player on forced vanity if (mVanity.forced) { - float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; + if (mFreeLook) { + float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; - mVanity.enabled = false; - rotateCamera(rot, adjust); - mVanity.enabled = true; - - compensateYaw(diff); + mVanity.enabled = false; + rotateCamera(rot, adjust); + mVanity.enabled = true; + compensateYaw(diff); + } trueRot.z = 0.f; } - rotateCamera(trueRot, adjust); + if (mFreeLook || mVanity.enabled || mPreviewMode) { + rotateCamera(trueRot, adjust); + } /// \note if vanity mode is forced by TVM then rotate player return (!mVanity.enabled && !mPreviewMode) || mVanity.forced; @@ -349,4 +352,9 @@ namespace MWRender Ogre::Vector3::UNIT_X); mCameraNode->setOrientation(zr * xr); } + + void Player::togglePlayerLooking(bool enable) + { + mFreeLook = enable; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 3f419889af..5bc32fffe0 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -29,12 +29,12 @@ namespace MWRender Ogre::SceneNode *mPlayerNode; Ogre::SceneNode *mCameraNode; - Ogre::SceneNode *mVanityNode; NpcAnimation *mAnimation; bool mFirstPersonView; bool mPreviewMode; + bool mFreeLook; struct { bool enabled, allowed, forced; @@ -106,6 +106,8 @@ namespace MWRender Ogre::Vector3 getPosition(); void getSightAngles(float &pitch, float &yaw); + + void togglePlayerLooking(bool enable); }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 1700835567..17a4d5280d 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->allowVanityMode(allow); } + virtual void togglePlayerLooking(bool enable) { + mPlayer->togglePlayerLooking(enable); + } + void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); void attachCameraTo(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 717c43cfa2..51216e3a5a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -293,6 +293,10 @@ namespace MWWorld mRendering->allowVanityMode(allow); } + virtual void togglePlayerLooking(bool enable) { + mRendering->togglePlayerLooking(enable); + } + virtual void renderPlayer(); }; } From 4f10138a04d966e3fc579288f42e9e7d4d7c1fd7 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 15:07:12 +0400 Subject: [PATCH 090/143] using real player sight angles on ray cast --- apps/openmw/mwworld/physicssystem.cpp | 42 +++++++++++++++------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 24e2cdbb19..dfba265894 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -16,8 +16,6 @@ #include "ptr.hpp" #include "class.hpp" -#include - using namespace Ogre; namespace MWWorld { @@ -46,28 +44,36 @@ namespace MWWorld std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) { - //get a ray pointing to the center of the viewport - Ray centerRay = mRender.getCamera()->getCameraToViewportRay( - mRender.getViewport()->getWidth()/2, - mRender.getViewport()->getHeight()/2); - //let's avoid the capsule shape of the player. - centerRay.setOrigin(centerRay.getOrigin() + 20*centerRay.getDirection()); - btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y); - btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y); + btVector3 dir(0, 1, 0); + dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir.setX(-dir.x()); - return mEngine->rayTest(from,to); + btVector3 origin( + mPlayerData.eyepos.x, + mPlayerData.eyepos.y, + mPlayerData.eyepos.z); + origin += dir * 5; + + btVector3 dest = origin + dir * 500; + return mEngine->rayTest(origin, dest); } std::vector < std::pair > PhysicsSystem::getFacedObjects () { - //get a ray pointing to the center of the viewport - Ray centerRay = mRender.getCamera()->getCameraToViewportRay( - mRender.getViewport()->getWidth()/2, - mRender.getViewport()->getHeight()/2); - btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y); - btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y); + btVector3 dir(0, 1, 0); + dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir.setX(-dir.x()); - return mEngine->rayTest2(from,to); + btVector3 origin( + mPlayerData.eyepos.x, + mPlayerData.eyepos.y, + mPlayerData.eyepos.z); + origin += dir * 5; + + btVector3 dest = origin + dir * 500; + return mEngine->rayTest2(origin, dest); } std::vector < std::pair > PhysicsSystem::getFacedObjects (float mouseX, float mouseY) From 7303d595dd0e342bab0bf567147918d4b336b5a1 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 15:36:51 +0400 Subject: [PATCH 091/143] resolving conflicts --- apps/openmw/mwbase/inputmanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++--- apps/openmw/mwinput/inputmanagerimp.hpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index d865bfb0ec..fdbab9fac0 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -22,7 +22,7 @@ namespace MWBase virtual ~InputManager() {} - virtual void update() = 0; + virtual void update(float duration) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index df0a42996f..0945842b2a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -450,7 +450,7 @@ private: } } - void toggleControlSwitch(std::string sw, bool value) + void toggleControlSwitch(const std::string &sw, bool value) { if (mControlSwitch[sw] == value) { return; @@ -472,7 +472,7 @@ private: mControlSwitch[sw] = value; } - bool getControlSwitch(std::string sw) + bool getControlSwitch(const std::string &sw) { return mControlSwitch[sw]; } @@ -536,7 +536,7 @@ private: impl->toggleControlSwitch(sw, value); } - bool MWInputManager::getControlSwitch(std::string sw) + bool MWInputManager::getControlSwitch(const std::string &sw) { return impl->getControlSwitch(sw); } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index eff2236d4d..d4ca32d2ff 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -60,8 +60,8 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); - void toggleControlSwitch(std::string sw, bool value); - bool getControlSwitch(std::string sw); + void toggleControlSwitch(const std::string &sw, bool value); + bool getControlSwitch(const std::string &sw); }; } #endif From 14f293882cf1f02b9bb0031a7929563a8852fc5b Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 16:42:42 +0400 Subject: [PATCH 092/143] still resolving --- apps/openmw/mwinput/inputmanagerimp.cpp | 14 +++++++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 91ef6722c6..292e4c2e95 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -45,6 +45,7 @@ namespace MWInput , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) + , mPreviewPOVDelay(0.f) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -243,12 +244,12 @@ namespace MWInput else if (actionIsActive(A_Crouch)) mPlayer.setUpDown (-1); else - player.setUpDown (0); + mPlayer.setUpDown (0); if (mControlSwitch["playerviewswitch"]) { - if (poller.isDown(A_TogglePOV)) { + if (actionIsActive(A_TogglePOV)) { if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += duration) > 0.5) + (mPreviewPOVDelay += dt) > 0.5) { mPreviewPOVDelay = 1.f; MWBase::Environment::get().getWorld()->togglePreviewMode(true); @@ -258,7 +259,7 @@ namespace MWInput //disable preview mode MWBase::Environment::get().getWorld()->togglePreviewMode(false); } else if (mPreviewPOVDelay > 0.f) { - togglePOV(); + MWBase::Environment::get().getWorld()->togglePOV(); } mPreviewPOVDelay = 0.f; } @@ -329,7 +330,7 @@ namespace MWInput mPlayer.setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time - player.setUpDown(0); + mPlayer.setUpDown(0); } else if (sw == "vanitymode") { MWBase::Environment::get().getWorld()->allowVanityMode(value); } else if (sw == "playerlooking") { @@ -552,6 +553,7 @@ namespace MWInput defaultKeyBindings[A_Journal] = OIS::KC_J; defaultKeyBindings[A_Rest] = OIS::KC_T; defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; + defaultKeyBindings[A_TogglePOV] = OIS::KC_TAB; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; @@ -601,6 +603,7 @@ namespace MWInput descriptions[A_Journal] = "sJournal"; descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; + descriptions[A_TogglePOV] = "Toggle POV"; if (descriptions[action] == "") return ""; // not configurable @@ -631,6 +634,7 @@ namespace MWInput ret.push_back(A_MoveLeft); ret.push_back(A_MoveRight); ret.push_back(A_Crouch); + ret.push_back(A_TogglePOV); ret.push_back(A_Activate); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 72d0b5d678..d199344a33 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -139,6 +139,7 @@ namespace MWInput float mUISensitivity; float mCameraYMultiplier; float mUIYMultiplier; + float mPreviewPOVDelay; bool mMouseLookEnabled; bool mGuiCursorEnabled; @@ -207,6 +208,8 @@ namespace MWInput A_ToggleWalk, //Toggle Walking/Running A_Crouch, + A_TogglePOV, + A_QuickSave, A_QuickLoad, A_QuickMenu, From 1da56e28325d1371648b1e46fcd27fd3f4095b8f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 15:37:14 +0200 Subject: [PATCH 093/143] removed some unused variables --- apps/openmw/mwrender/player.cpp | 1 - apps/openmw/mwworld/physicssystem.cpp | 6 ------ components/nifogre/ogre_nif_loader.cpp | 4 ++-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3bbb569076..953543d991 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -119,7 +119,6 @@ namespace MWRender void Player::update(float duration) { - Ogre::Vector3 pos = mPlayerNode->getPosition(); if (!mVanity.enabled) { ++mUpdates; mTimeIdle += duration; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index dfba265894..b7cb2778dd 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -189,12 +189,6 @@ namespace MWWorld iter!=actors.end(); ++iter) { //dirty stuff to get the camera orientation. Must be changed! - Ogre::Quaternion orient = - mRender.getScene()->getSceneNode(iter->first)->getOrientation(); - - float yaw = orient.getRoll().valueDegrees(); - float pitch = 0.f; - if (iter->first == "player") { playerphysics->ps.viewangles.x = Ogre::Radian(mPlayerData.pitch).valueDegrees(); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 4393749fbb..0fdeaae3d9 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -471,7 +471,7 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam float glossiness = 0.0f; float alpha = 1.0f; int alphaFlags = -1; - ubyte alphaTest = 0; +// ubyte alphaTest = 0; Ogre::String texName; bool vertexColour = (shape->data->colors.size() != 0); @@ -523,7 +523,7 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam if (a) { alphaFlags = a->flags; - alphaTest = a->data.threshold; +// alphaTest = a->data.threshold; } // Material From 3112bfb54f105a4d571f790f06f1499b99e7142d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 15:37:54 +0200 Subject: [PATCH 094/143] incremented version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb983..77efa186cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 16) +set (OPENMW_VERSION_MINOR 17) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") diff --git a/readme.txt b/readme.txt index ce3d115e37..f146de71b7 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.16.0 +Version: 0.17.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 296b61f476188925e19620c0add7e38809d66ecd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 15:40:46 +0200 Subject: [PATCH 095/143] updated changelog --- readme.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/readme.txt b/readme.txt index f146de71b7..10a0a6f5de 100644 --- a/readme.txt +++ b/readme.txt @@ -97,6 +97,37 @@ Allowed options: CHANGELOG +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 +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 From 7952d38e6cb154e02203a61cedf178e49374065e Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Tue, 14 Aug 2012 14:45:16 -0400 Subject: [PATCH 096/143] Use debug dlls when debugging in vs2010 (try 2) Using the Debug build in vs2010 is not working because the debug dlls are not loaded when debugging. The reason they are not loaded is that CMAKE_BUILD_TYPE is not defined when doing multiple builds. This in turns causes OGRE_PLUGIN_DEBUG_SUFFIX not to be set. This patch makes sure that OGRE_PLUGIN_DEBUG_SUFFIX is always set but only used when debugging. It also defines DEBUG to make it easier turn things on and off when debugging. There are still other bugs that have broken Debug mode in vs2010 but those will be addressed in other patches. --- CMakeLists.txt | 10 ++++++---- components/files/ogreplugin.cpp | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb983..d611e53d7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,13 +257,15 @@ if (APPLE) "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) endif (APPLE) +# Set up DEBUG define +set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) # Set up Ogre plugin folder & debug suffix -# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) -if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") -else() +if (APPLE) + # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") +else () + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index c434114b37..ca90fd30e1 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,7 +6,11 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { + // Append plugin suffix if debugging. +#if defined(DEBUG) pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; +#endif + #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; From 8817a9634ccbab8bae0241b064a646304f24b115 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Aug 2012 16:55:21 +0200 Subject: [PATCH 097/143] don't switch to vanity mode as a result of idle time when the gui is opened (e.g. when talking to someone) --- apps/openmw/mwrender/player.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 953543d991..fb8109a1d5 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -5,6 +5,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" @@ -119,7 +120,8 @@ namespace MWRender void Player::update(float duration) { - if (!mVanity.enabled) { + bool isGuiMode = MWBase::Environment::get().getWindowManager ()->isGuiMode(); + if (!mVanity.enabled && !isGuiMode) { ++mUpdates; mTimeIdle += duration; if (mTimeIdle > 30.f) { From b6954a4e8d251c7152ddc6278407f4d9c204fd18 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 22:08:16 +0400 Subject: [PATCH 098/143] use the same height on non-1st person modes --- apps/openmw/mwrender/player.cpp | 32 +++++++++++++++++--------------- apps/openmw/mwrender/player.hpp | 5 ++--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3bbb569076..98cb1695e2 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -38,9 +37,6 @@ namespace MWRender bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - mUpdates = 0; - mTimeIdle = 0.f; - if (mVanity.enabled) { toggleVanityMode(false); } @@ -120,13 +116,6 @@ namespace MWRender void Player::update(float duration) { Ogre::Vector3 pos = mPlayerNode->getPosition(); - if (!mVanity.enabled) { - ++mUpdates; - mTimeIdle += duration; - if (mTimeIdle > 30.f) { - toggleVanityMode(true); - } - } if (mAnimation) { mAnimation->runAnimation(duration); } @@ -145,10 +134,10 @@ namespace MWRender mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); - mCameraNode->setPosition(0.f, 0.f, 128.f); + setLowHeight(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); - mCameraNode->setPosition(0.f, 0.f, 104.f); + setLowHeight(true); } mPlayerNode->setVisible(!mFirstPersonView, false); } @@ -176,15 +165,17 @@ namespace MWRender float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { - mPlayerNode->setVisible(true, false); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); @@ -201,15 +192,17 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { - mPlayerNode->setVisible(true, false); mMainCam.offset = offset; offset = mPreviewCam.offset; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); @@ -357,4 +350,13 @@ namespace MWRender { mFreeLook = enable; } + + void Player::setLowHeight(bool low) + { + if (low) { + mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); + } else { + mCameraNode->setPosition(0.f, 0.f, mHeight); + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 5bc32fffe0..8dd313b7fa 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -45,9 +45,6 @@ namespace MWRender bool mDistanceAdjusted; - float mTimeIdle; - int mUpdates; - /// Updates sound manager listener data void updateListener(); @@ -61,6 +58,8 @@ namespace MWRender void compensateYaw(float diff); + void setLowHeight(bool low = true); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); From d2b451eb7d7d9374f36c160e043776808205d2c0 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 23:25:29 +0400 Subject: [PATCH 099/143] entering vanity mode --- apps/openmw/mwbase/inputmanager.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 59 +++++++++++++++++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 3 +- apps/openmw/mwinput/mouselookevent.cpp | 2 + 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index fdbab9fac0..00d7c8125a 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -31,6 +31,8 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + + virtual void resetIdleTime() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 0945842b2a..c78643f599 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -92,6 +92,7 @@ namespace MWInput std::map mControlSwitch; float mPreviewPOVDelay; + float mTimeIdle; /* InputImpl Methods */ public: @@ -99,11 +100,34 @@ public: { input.adjustMouseClippingSize(width, height); } + + void resetIdleTime() + { + if (mTimeIdle < 0) { + MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); + } + mTimeIdle = 0.f; + } + private: + + void updateIdleTime(float dt) + { + if (mTimeIdle >= 0.f) { + mTimeIdle += dt; + } + if (mTimeIdle > 30.f) { + MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); + mTimeIdle = -1.f; + } + } + void toggleSpell() { if (windows.isGuiMode()) return; + resetIdleTime(); + MWMechanics::DrawState_ state = player.getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) { @@ -121,6 +145,8 @@ private: { if (windows.isGuiMode()) return; + resetIdleTime(); + MWMechanics::DrawState_ state = player.getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) { @@ -200,18 +226,26 @@ private: void activate() { + resetIdleTime(); + mEngine.activate(); } void toggleAutoMove() { if (windows.isGuiMode()) return; + + resetIdleTime(); + player.setAutoMove (!player.getAutoMove()); } void toggleWalking() { if (windows.isGuiMode()) return; + + resetIdleTime(); + player.toggleRunning(); } @@ -243,7 +277,8 @@ private: windows(_windows), mEngine (engine), mDragDrop(false), - mPreviewPOVDelay(0.f) + mPreviewPOVDelay(0.f), + mTimeIdle(0.f) { using namespace OEngine::Input; using namespace OEngine::Render; @@ -426,6 +461,19 @@ private: } } } + // Idle time update despite of control switches + if (poller.isDown(A_MoveLeft) || + poller.isDown(A_MoveRight) || + poller.isDown(A_MoveForward) || + poller.isDown(A_MoveBackward) || + poller.isDown(A_Jump) || + poller.isDown(A_Crouch) || + poller.isDown(A_TogglePOV)) + { + resetIdleTime(); + } else { + updateIdleTime(duration); + } } // Switch between gui modes. Besides controlling the Gui windows @@ -472,11 +520,6 @@ private: mControlSwitch[sw] = value; } - bool getControlSwitch(const std::string &sw) - { - return mControlSwitch[sw]; - } - void togglePOV() { MWBase::Environment::get().getWorld()->togglePOV(); @@ -536,8 +579,8 @@ private: impl->toggleControlSwitch(sw, value); } - bool MWInputManager::getControlSwitch(const std::string &sw) + void MWInputManager::resetIdleTime() { - return impl->getControlSwitch(sw); + impl->resetIdleTime(); } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d4ca32d2ff..868565dbbc 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -61,7 +61,8 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); void toggleControlSwitch(const std::string &sw, bool value); - bool getControlSwitch(const std::string &sw); + + void resetIdleTime(); }; } #endif diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index f318ce6660..e6852d9234 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -1,6 +1,7 @@ #include "mouselookevent.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" @@ -17,6 +18,7 @@ void MouseLookEvent::event(Type type, int index, const void *p) if (type != EV_MouseMove || mDisabled) { return; } + MWBase::Environment::get().getInputManager()->resetIdleTime(); MouseEvent *arg = (MouseEvent*)(p); From 3f3972eb3b0e73417de662245a07315b2dc18c19 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 23:44:28 +0400 Subject: [PATCH 100/143] no vanity in gui mode (thanks scrawl) --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c78643f599..638bc6047c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -116,7 +116,7 @@ private: if (mTimeIdle >= 0.f) { mTimeIdle += dt; } - if (mTimeIdle > 30.f) { + if (mTimeIdle > 30.f && !windows.isGuiMode()) { MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); mTimeIdle = -1.f; } From b8e56d61d3592d8304699dc7d1ded378eeb76220 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 18 Aug 2012 01:31:57 +0400 Subject: [PATCH 101/143] update camera code, add idle time --- apps/openmw/mwinput/inputmanagerimp.cpp | 40 +++++++++++++++++++++++++ apps/openmw/mwinput/inputmanagerimp.hpp | 6 ++-- apps/openmw/mwrender/player.cpp | 32 ++++++++++---------- apps/openmw/mwrender/player.hpp | 5 ++-- 4 files changed, 63 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 292e4c2e95..e4522e593e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -46,6 +46,7 @@ namespace MWInput , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) , mPreviewPOVDelay(0.f) + , mTimeIdle(0.f) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -164,24 +165,30 @@ namespace MWInput toggleConsole (); break; case A_Activate: + resetIdleTime(); activate(); break; case A_Journal: toggleJournal (); break; case A_AutoMove: + resetIdleTime(); toggleAutoMove (); break; case A_ToggleSneak: /// \todo implement + resetIdleTime(); break; case A_ToggleWalk: + resetIdleTime(); toggleWalking (); break; case A_ToggleWeapon: + resetIdleTime(); toggleWeapon (); break; case A_ToggleSpell: + resetIdleTime(); toggleSpell (); break; } @@ -265,6 +272,18 @@ namespace MWInput } } } + if (actionIsActive(A_MoveForward) || + actionIsActive(A_MoveBackward) || + actionIsActive(A_MoveLeft) || + actionIsActive(A_MoveRight) || + actionIsActive(A_Jump) || + actionIsActive(A_Crouch) || + actionIsActive(A_TogglePOV)) + { + resetIdleTime(); + } else { + updateIdleTime(dt); + } } void InputManager::setDragDrop(bool dragDrop) @@ -402,6 +421,8 @@ namespace MWInput if (mMouseLookEnabled) { + resetIdleTime(); + float x = arg.state.X.rel * mCameraSensitivity * 0.2; float y = arg.state.Y.rel * mCameraSensitivity * 0.2 * (mInvertY ? -1 : 1) * mUIYMultiplier; @@ -528,6 +549,25 @@ namespace MWInput Ogre::Root::getSingleton().queueEndRendering (); } + void InputManager::resetIdleTime() + { + if (mTimeIdle < 0) { + MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); + } + mTimeIdle = 0.f; + } + + void InputManager::updateIdleTime(float dt) + { + if (mTimeIdle >= 0.f) { + mTimeIdle += dt; + } + if (mTimeIdle > 30.f) { + MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); + mTimeIdle = -1.f; + } + } + bool InputManager::actionIsActive (int id) { return mInputCtrl->getChannel (id)->getValue () == 1; diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d199344a33..d1fb472c94 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -83,7 +83,6 @@ namespace MWInput virtual void enableDetectingBindingMode (int action); virtual void resetToDefaultBindings(); - public: virtual bool keyPressed( const OIS::KeyEvent &arg ); virtual bool keyReleased( const OIS::KeyEvent &arg ); @@ -140,6 +139,7 @@ namespace MWInput float mCameraYMultiplier; float mUIYMultiplier; float mPreviewPOVDelay; + float mTimeIdle; bool mMouseLookEnabled; bool mGuiCursorEnabled; @@ -149,10 +149,12 @@ namespace MWInput std::map mControlSwitch; - private: void adjustMouseRegion(int width, int height); + void resetIdleTime(); + void updateIdleTime(float dt); + private: void toggleMainMenu(); void toggleSpell(); diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3bbb569076..98cb1695e2 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -38,9 +37,6 @@ namespace MWRender bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - mUpdates = 0; - mTimeIdle = 0.f; - if (mVanity.enabled) { toggleVanityMode(false); } @@ -120,13 +116,6 @@ namespace MWRender void Player::update(float duration) { Ogre::Vector3 pos = mPlayerNode->getPosition(); - if (!mVanity.enabled) { - ++mUpdates; - mTimeIdle += duration; - if (mTimeIdle > 30.f) { - toggleVanityMode(true); - } - } if (mAnimation) { mAnimation->runAnimation(duration); } @@ -145,10 +134,10 @@ namespace MWRender mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); - mCameraNode->setPosition(0.f, 0.f, 128.f); + setLowHeight(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); - mCameraNode->setPosition(0.f, 0.f, 104.f); + setLowHeight(true); } mPlayerNode->setVisible(!mFirstPersonView, false); } @@ -176,15 +165,17 @@ namespace MWRender float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { - mPlayerNode->setVisible(true, false); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); @@ -201,15 +192,17 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { - mPlayerNode->setVisible(true, false); mMainCam.offset = offset; offset = mPreviewCam.offset; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); @@ -357,4 +350,13 @@ namespace MWRender { mFreeLook = enable; } + + void Player::setLowHeight(bool low) + { + if (low) { + mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); + } else { + mCameraNode->setPosition(0.f, 0.f, mHeight); + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 5bc32fffe0..8dd313b7fa 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -45,9 +45,6 @@ namespace MWRender bool mDistanceAdjusted; - float mTimeIdle; - int mUpdates; - /// Updates sound manager listener data void updateListener(); @@ -61,6 +58,8 @@ namespace MWRender void compensateYaw(float diff); + void setLowHeight(bool low = true); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); From 5b2b378f25f4c94d7885879899508cea70df140e Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 18 Aug 2012 18:05:10 +0400 Subject: [PATCH 102/143] tvm script instruction --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwrender/player.hpp | 4 ++++ apps/openmw/mwrender/renderingmanager.hpp | 10 ++++++--- apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 27 +++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9c801cbff5..5e97b4922b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -254,6 +254,7 @@ namespace MWBase virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; + virtual bool isVanityEnabled() = 0; virtual void renderPlayer() = 0; }; diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 8dd313b7fa..e56abc17d8 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -107,6 +107,10 @@ namespace MWRender void getSightAngles(float &pitch, float &yaw); void togglePlayerLooking(bool enable); + + bool isVanityEnabled() { + return mVanity.enabled; + } }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 17a4d5280d..003a61a391 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -64,15 +64,19 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->togglePreviewMode(enable); } - virtual bool toggleVanityMode(bool enable, bool force) { + bool toggleVanityMode(bool enable, bool force) { return mPlayer->toggleVanityMode(enable, force); } - virtual void allowVanityMode(bool allow) { + bool isVanityEnabled() { + return mPlayer->isVanityEnabled(); + } + + void allowVanityMode(bool allow) { mPlayer->allowVanityMode(allow); } - virtual void togglePlayerLooking(bool enable) { + void togglePlayerLooking(bool enable) { mPlayer->togglePlayerLooking(enable); } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 1ea5f8e3ec..dbc8159d18 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -181,4 +181,5 @@ op 0x2000170: user4, explicit reference (console only, requires --script-console op 0x2000171: user4 (implicit reference, console only, requires --script-console switch) op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference -opcodes 0x2000174-0x3ffffff unused +op 0x2000174: ToggleVanityMode +opcodes 0x2000175-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 0c328e0da9..e9e29bdae9 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -207,6 +207,29 @@ namespace MWScript } }; + class OpToggleVanityMode : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime &runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + MWBase::World *world = + MWBase::Environment::get().getWorld(); + + bool value = !world->isVanityEnabled(); + if (world->toggleVanityMode(value, true)) { + context.report( + (value) ? "Vanity Mode -> On" : "Vanity Mode -> Off" + ); + } else { + context.report("Vanity Mode -> No"); + } + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -222,6 +245,7 @@ namespace MWScript const int opcodeToggleWater = 0x2000144; const int opcodeTogglePathgrid = 0x2000146; const int opcodeDontSaveObject = 0x2000153; + const int opcodeToggleVanityMode = 0x2000174; void registerExtensions (Compiler::Extensions& extensions) { @@ -244,6 +268,8 @@ namespace MWScript extensions.registerInstruction ("togglepathgrid", "", opcodeTogglePathgrid); extensions.registerInstruction ("tpg", "", opcodeTogglePathgrid); extensions.registerInstruction ("dontsaveobject", "", opcodeDontSaveObject); + extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); + extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -263,6 +289,7 @@ namespace MWScript interpreter.installSegment5 (opcodeTogglePathgrid, new OpTogglePathgrid); interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater); interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject); + interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); } } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 51216e3a5a..889741989b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -298,6 +298,10 @@ namespace MWWorld } virtual void renderPlayer(); + + virtual bool isVanityEnabled() { + return mRendering->isVanityEnabled(); + } }; } From 609a45258bf6d89a8c90dbfc5aafb0a4eeb385cd Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 18 Aug 2012 21:26:57 +0400 Subject: [PATCH 103/143] updated changelog with newly created bug for laggy input on OS X --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 10a0a6f5de..a254e2672b 100644 --- a/readme.txt +++ b/readme.txt @@ -112,6 +112,7 @@ Bug #341: Building with Ogre Debug libraries does not use debug version of plugi 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 From e6c3e0744efe94086ffed6515f75bf4c4dcfa8a9 Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 19 Aug 2012 10:37:51 +0400 Subject: [PATCH 104/143] tvm update and fix --- apps/openmw/mwbase/world.hpp | 1 - apps/openmw/mwrender/player.hpp | 4 ---- apps/openmw/mwrender/renderingmanager.hpp | 4 ---- apps/openmw/mwscript/miscextensions.cpp | 9 ++++++--- apps/openmw/mwworld/worldimp.hpp | 4 ---- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 5e97b4922b..9c801cbff5 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -254,7 +254,6 @@ namespace MWBase virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; - virtual bool isVanityEnabled() = 0; virtual void renderPlayer() = 0; }; diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index e56abc17d8..8dd313b7fa 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -107,10 +107,6 @@ namespace MWRender void getSightAngles(float &pitch, float &yaw); void togglePlayerLooking(bool enable); - - bool isVanityEnabled() { - return mVanity.enabled; - } }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 003a61a391..b6bfcbf974 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -68,10 +68,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList return mPlayer->toggleVanityMode(enable, force); } - bool isVanityEnabled() { - return mPlayer->isVanityEnabled(); - } - void allowVanityMode(bool allow) { mPlayer->allowVanityMode(allow); } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index e9e29bdae9..864c1a1eed 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -209,6 +209,8 @@ namespace MWScript class OpToggleVanityMode : public Interpreter::Opcode0 { + static bool sActivate; + public: virtual void execute(Interpreter::Runtime &runtime) @@ -219,16 +221,17 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); - bool value = !world->isVanityEnabled(); - if (world->toggleVanityMode(value, true)) { + if (world->toggleVanityMode(sActivate, true)) { context.report( - (value) ? "Vanity Mode -> On" : "Vanity Mode -> Off" + (sActivate) ? "Vanity Mode -> On" : "Vanity Mode -> Off" ); + sActivate = !sActivate; } else { context.report("Vanity Mode -> No"); } } }; + bool OpToggleVanityMode::sActivate = true; const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 889741989b..51216e3a5a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -298,10 +298,6 @@ namespace MWWorld } virtual void renderPlayer(); - - virtual bool isVanityEnabled() { - return mRendering->isVanityEnabled(); - } }; } From f7d537cb2838772c67724a20e64ed30ca7091f8a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Aug 2012 22:15:31 +0200 Subject: [PATCH 105/143] very small correction --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 177e90b956..a7cce25c06 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -275,7 +275,8 @@ namespace MWInput || actionIsActive(A_MoveForward) || actionIsActive(A_MoveBackward) || actionIsActive(A_Jump) - || actionIsActive(A_Crouch)) + || actionIsActive(A_Crouch) + || actionIsActive(A_TogglePOV)) { resetIdleTime (); From 82e7c04c0a267549aac3a380e4d45d81d3d8f8ab Mon Sep 17 00:00:00 2001 From: Douglas Diniz Date: Sun, 19 Aug 2012 20:11:50 -0300 Subject: [PATCH 106/143] Changing sounds to Action. Adding onActor to Action to choose between playSound and playSound3D. --- apps/openmw/mwclass/apparatus.cpp | 9 ++++---- apps/openmw/mwclass/armor.cpp | 14 +++++++----- apps/openmw/mwclass/clothing.cpp | 16 +++++++------ apps/openmw/mwclass/container.cpp | 11 ++++----- apps/openmw/mwclass/door.cpp | 36 +++++++++++++++++++++--------- apps/openmw/mwclass/ingredient.cpp | 8 +++---- apps/openmw/mwclass/light.cpp | 13 ++++++----- apps/openmw/mwclass/lockpick.cpp | 14 +++++++----- apps/openmw/mwclass/misc.cpp | 8 +++---- apps/openmw/mwclass/potion.cpp | 9 ++++---- apps/openmw/mwclass/probe.cpp | 14 +++++++----- apps/openmw/mwclass/repair.cpp | 8 +++---- apps/openmw/mwclass/weapon.cpp | 14 +++++++----- apps/openmw/mwworld/action.cpp | 19 +++++++++++++--- apps/openmw/mwworld/action.hpp | 3 ++- 15 files changed, 121 insertions(+), 75 deletions(-) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 8a117af5dc..3d362e8c8d 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -63,10 +62,12 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action( + new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index c48d7ff4db..f1400dc010 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -66,10 +65,11 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } bool Armor::hasItemHealth (const MWWorld::Ptr& ptr) const @@ -271,9 +271,11 @@ namespace MWClass boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 597eb22fec..21069e667e 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -62,12 +61,13 @@ namespace MWClass } boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, - const MWWorld::Ptr& actor) const + const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Clothing::getScript (const MWWorld::Ptr& ptr) const @@ -222,9 +222,11 @@ namespace MWClass boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index ad6da90dd3..608fc61227 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -93,8 +92,9 @@ namespace MWClass { // TODO check for key std::cout << "Locked container" << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, lockedSound, 1.0, 1.0); - return boost::shared_ptr (new MWWorld::NullAction); + boost::shared_ptr action(new MWWorld::NullAction); + action->setSound(lockedSound); + return action; } else { @@ -109,9 +109,10 @@ namespace MWClass { // Trap activation goes here std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, trapActivationSound, 1.0, 1.0); + boost::shared_ptr action(new MWWorld::NullAction); + action->setSound(trapActivationSound); ptr.getCellRef().trap = ""; - return boost::shared_ptr (new MWWorld::NullAction); + return action; } } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 3b283a9d1b..bf5dd4bba1 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -5,8 +5,9 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -80,17 +81,25 @@ namespace MWClass // TODO check for key // TODO report failure to player (message, sound?). Look up behaviour of original MW. std::cout << "Locked!" << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, lockedSound, 1.0, 1.0); - return boost::shared_ptr (new MWWorld::NullAction); + + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(lockedSound); + + return action; } if(!ptr.getCellRef().trap.empty()) { // Trap activation std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D(ptr, trapActivationSound, 1.0, 1.0); + + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(trapActivationSound); ptr.getCellRef().trap = ""; - return boost::shared_ptr (new MWWorld::NullAction); + + return action; } if (ref->ref.teleport) @@ -101,9 +110,13 @@ namespace MWClass { // the player is using the door // The reason this is not 3D is that it would get interrupted when you teleport - MWBase::Environment::get().getSoundManager()->playSound(openSound, 1.0, 1.0); - return boost::shared_ptr ( - new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); + //MWBase::Environment::get().getSoundManager()->playSound3D(ptr,openSound, 1.0, 1.0); + + boost::shared_ptr action(new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); + + action->setSound(openSound, true); + + return action; } else { @@ -117,8 +130,11 @@ namespace MWClass // TODO return action for rotating the door // This is a little pointless, but helps with testing - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, openSound, 1.0, 1.0); - return boost::shared_ptr (new MWWorld::NullAction); + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(openSound); + + return action; } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 8f9f8a315c..f0354de76a 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -62,10 +61,11 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 40ecf0a0f2..5702215038 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -94,10 +94,11 @@ namespace MWClass if (!(ref->base->data.flags & ESM::Light::Carry)) return boost::shared_ptr (new MWWorld::NullAction); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Light::getScript (const MWWorld::Ptr& ptr) const @@ -191,9 +192,11 @@ namespace MWClass boost::shared_ptr Light::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 8fdd95760d..44498e4797 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -64,10 +63,11 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const @@ -160,9 +160,11 @@ namespace MWClass boost::shared_ptr Lockpick::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 2cd0f63a14..eb44b81037 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -67,10 +66,11 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 6bff855536..8fef163b1d 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -64,10 +63,12 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action( + new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound (getUpSoundId(ptr)); + + return action; } std::string Potion::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 25aae445bd..73258e5286 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -63,10 +62,11 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Probe::getScript (const MWWorld::Ptr& ptr) const @@ -159,9 +159,11 @@ namespace MWClass boost::shared_ptr Probe::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index ebba8c44e0..a4240d0c49 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -62,10 +61,11 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Repair::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 2a9863cdf3..b2397f4aff 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -64,10 +63,11 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } bool Weapon::hasItemHealth (const MWWorld::Ptr& ptr) const @@ -360,9 +360,11 @@ namespace MWClass boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index c142294bb4..15bae4cd46 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -11,13 +11,26 @@ MWWorld::Action::~Action() {} void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) - MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); + { + if (onActor) + { + std::cout << "Douglas - Som Normal" << std::endl; + MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } + else + { + std::cout << "Douglas - Som 3D" << std::endl; + MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } + } executeImp (actor); } -void MWWorld::Action::setSound (const std::string& id) +void MWWorld::Action::setSound (const std::string& id, const bool onActorValue) { mSoundId = id; + onActor = onActorValue; } diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index a00f679517..f9ad975d39 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -11,6 +11,7 @@ namespace MWWorld class Action { std::string mSoundId; + bool onActor; // not implemented Action (const Action& action); @@ -26,7 +27,7 @@ namespace MWWorld void execute (const Ptr& actor); - void setSound (const std::string& id); + void setSound (const std::string& id, const bool onActor = false); }; } From 5395721c264c0179ec29c6d8b2678d4ef3ce72d0 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Mon, 20 Aug 2012 21:05:02 +0400 Subject: [PATCH 107/143] more appropriate fix for #189 --- apps/openmw/CMakeLists.txt | 4 +++- apps/openmw/mwinput/inputmanagerimp.cpp | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 1b4ae209d9..4741ba5ddd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -114,7 +114,9 @@ endif() if(APPLE) find_library(CARBON_FRAMEWORK Carbon) - target_link_libraries(openmw ${CARBON_FRAMEWORK}) + find_library(COCOA_FRAMEWORK Cocoa) + find_library(IOKIT_FRAMEWORK IOKit) + target_link_libraries(openmw ${CARBON_FRAMEWORK} ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) endif(APPLE) if(DPKG_PROGRAM) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index a7cce25c06..1aa4ceeff5 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -387,9 +387,18 @@ namespace MWInput bool InputManager::keyPressed( const OIS::KeyEvent &arg ) { - mInputCtrl->keyPressed (arg); + std::cout << "text received: " << arg.text << std::endl; - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + mInputCtrl->keyPressed (arg); + unsigned int text = arg.text; + #ifdef __APPLE__ // filter \016 symbol for F-keys on OS X + if ((arg.key >= OIS::KC_F1 && arg.key <= OIS::KC_F10) || + (arg.key >= OIS::KC_F11 && arg.key <= OIS::KC_F15)) { + text = 0; + } + #endif + + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), text); return true; } From 71a6ce2202b395dd08c87888d11ce7cad33dd3e2 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Mon, 20 Aug 2012 21:14:29 +0400 Subject: [PATCH 108/143] removed cout spam --- apps/openmw/mwinput/inputmanagerimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 1aa4ceeff5..d1f02aaf07 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -387,16 +387,14 @@ namespace MWInput bool InputManager::keyPressed( const OIS::KeyEvent &arg ) { - std::cout << "text received: " << arg.text << std::endl; - mInputCtrl->keyPressed (arg); unsigned int text = arg.text; - #ifdef __APPLE__ // filter \016 symbol for F-keys on OS X +#ifdef __APPLE__ // filter \016 symbol for F-keys on OS X if ((arg.key >= OIS::KC_F1 && arg.key <= OIS::KC_F10) || (arg.key >= OIS::KC_F11 && arg.key <= OIS::KC_F15)) { text = 0; } - #endif +#endif MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), text); From 5fa8165f97ae19a461c904d60e3e9c23b5ff37b2 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Wed, 22 Aug 2012 15:00:07 -0400 Subject: [PATCH 109/143] Use char literals in UTF 8 conversion to fix 798 warnings The data type is specified as char but the literals are unsigned char. This results in 798 truncation warnings in vs2010. The literals were converted with a simple python script to signed char while taking two's complement and the overflow into account. Also tested on Ubuntu 12.04 with gcc 4.6. --- components/to_utf8/tables_gen.hpp | 1536 ++++++++++++++--------------- 1 file changed, 768 insertions(+), 768 deletions(-) diff --git a/components/to_utf8/tables_gen.hpp b/components/to_utf8/tables_gen.hpp index 1084ca28f9..9c32f0427d 100644 --- a/components/to_utf8/tables_gen.hpp +++ b/components/to_utf8/tables_gen.hpp @@ -10,785 +10,785 @@ namespace ToUTF8 /// Serbian (Latin script), Romanian and Albanian. static char windows_1250[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x99, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 3, -30, -126, -84, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -102, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -80, 0, 0, + 2, -59, -96, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -59, -102, 0, 0, 0, + 2, -59, -92, 0, 0, 0, + 2, -59, -67, 0, 0, 0, + 2, -59, -71, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -124, -94, 0, 0, + 2, -59, -95, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -59, -101, 0, 0, 0, + 2, -59, -91, 0, 0, 0, + 2, -59, -66, 0, 0, 0, + 2, -59, -70, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -53, -121, 0, 0, 0, + 2, -53, -104, 0, 0, 0, + 2, -59, -127, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -60, -124, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -62, -88, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -59, -98, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -59, -69, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -53, -101, 0, 0, 0, + 2, -59, -126, 0, 0, 0, + 2, -62, -76, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -62, -72, 0, 0, 0, + 2, -60, -123, 0, 0, 0, + 2, -59, -97, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -60, -67, 0, 0, 0, + 2, -53, -99, 0, 0, 0, + 2, -60, -66, 0, 0, 0, + 2, -59, -68, 0, 0, 0, + 2, -59, -108, 0, 0, 0, + 2, -61, -127, 0, 0, 0, + 2, -61, -126, 0, 0, 0, + 2, -60, -126, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -60, -71, 0, 0, 0, + 2, -60, -122, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -60, -116, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -60, -104, 0, 0, 0, + 2, -61, -117, 0, 0, 0, + 2, -60, -102, 0, 0, 0, + 2, -61, -115, 0, 0, 0, + 2, -61, -114, 0, 0, 0, + 2, -60, -114, 0, 0, 0, + 2, -60, -112, 0, 0, 0, + 2, -59, -125, 0, 0, 0, + 2, -59, -121, 0, 0, 0, + 2, -61, -109, 0, 0, 0, + 2, -61, -108, 0, 0, 0, + 2, -59, -112, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -105, 0, 0, 0, + 2, -59, -104, 0, 0, 0, + 2, -59, -82, 0, 0, 0, + 2, -61, -102, 0, 0, 0, + 2, -59, -80, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -61, -99, 0, 0, 0, + 2, -59, -94, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -59, -107, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -60, -125, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -60, -70, 0, 0, 0, + 2, -60, -121, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -60, -115, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -60, -103, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -60, -101, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -60, -113, 0, 0, 0, + 2, -60, -111, 0, 0, 0, + 2, -59, -124, 0, 0, 0, + 2, -59, -120, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -59, -111, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -73, 0, 0, 0, + 2, -59, -103, 0, 0, 0, + 2, -59, -81, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -59, -79, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -67, 0, 0, 0, + 2, -59, -93, 0, 0, 0, + 2, -53, -103, 0, 0, 0 }; /// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic /// and other languages static char windows_1251[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd2, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd2, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x84, (char)0x96, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x80, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8f, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 2, -48, -126, 0, 0, 0, + 2, -48, -125, 0, 0, 0, + 3, -30, -128, -102, 0, 0, + 2, -47, -109, 0, 0, 0, + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 3, -30, -126, -84, 0, 0, + 3, -30, -128, -80, 0, 0, + 2, -48, -119, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -48, -118, 0, 0, 0, + 2, -48, -116, 0, 0, 0, + 2, -48, -117, 0, 0, 0, + 2, -48, -113, 0, 0, 0, + 2, -47, -110, 0, 0, 0, + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -124, -94, 0, 0, + 2, -47, -103, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -47, -102, 0, 0, 0, + 2, -47, -100, 0, 0, 0, + 2, -47, -101, 0, 0, 0, + 2, -47, -97, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -48, -114, 0, 0, 0, + 2, -47, -98, 0, 0, 0, + 2, -48, -120, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -46, -112, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -48, -127, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -48, -124, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -48, -121, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -48, -122, 0, 0, 0, + 2, -47, -106, 0, 0, 0, + 2, -46, -111, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -47, -111, 0, 0, 0, + 3, -30, -124, -106, 0, 0, + 2, -47, -108, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -47, -104, 0, 0, 0, + 2, -48, -123, 0, 0, 0, + 2, -47, -107, 0, 0, 0, + 2, -47, -105, 0, 0, 0, + 2, -48, -112, 0, 0, 0, + 2, -48, -111, 0, 0, 0, + 2, -48, -110, 0, 0, 0, + 2, -48, -109, 0, 0, 0, + 2, -48, -108, 0, 0, 0, + 2, -48, -107, 0, 0, 0, + 2, -48, -106, 0, 0, 0, + 2, -48, -105, 0, 0, 0, + 2, -48, -104, 0, 0, 0, + 2, -48, -103, 0, 0, 0, + 2, -48, -102, 0, 0, 0, + 2, -48, -101, 0, 0, 0, + 2, -48, -100, 0, 0, 0, + 2, -48, -99, 0, 0, 0, + 2, -48, -98, 0, 0, 0, + 2, -48, -97, 0, 0, 0, + 2, -48, -96, 0, 0, 0, + 2, -48, -95, 0, 0, 0, + 2, -48, -94, 0, 0, 0, + 2, -48, -93, 0, 0, 0, + 2, -48, -92, 0, 0, 0, + 2, -48, -91, 0, 0, 0, + 2, -48, -90, 0, 0, 0, + 2, -48, -89, 0, 0, 0, + 2, -48, -88, 0, 0, 0, + 2, -48, -87, 0, 0, 0, + 2, -48, -86, 0, 0, 0, + 2, -48, -85, 0, 0, 0, + 2, -48, -84, 0, 0, 0, + 2, -48, -83, 0, 0, 0, + 2, -48, -82, 0, 0, 0, + 2, -48, -81, 0, 0, 0, + 2, -48, -80, 0, 0, 0, + 2, -48, -79, 0, 0, 0, + 2, -48, -78, 0, 0, 0, + 2, -48, -77, 0, 0, 0, + 2, -48, -76, 0, 0, 0, + 2, -48, -75, 0, 0, 0, + 2, -48, -74, 0, 0, 0, + 2, -48, -73, 0, 0, 0, + 2, -48, -72, 0, 0, 0, + 2, -48, -71, 0, 0, 0, + 2, -48, -70, 0, 0, 0, + 2, -48, -69, 0, 0, 0, + 2, -48, -68, 0, 0, 0, + 2, -48, -67, 0, 0, 0, + 2, -48, -66, 0, 0, 0, + 2, -48, -65, 0, 0, 0, + 2, -47, -128, 0, 0, 0, + 2, -47, -127, 0, 0, 0, + 2, -47, -126, 0, 0, 0, + 2, -47, -125, 0, 0, 0, + 2, -47, -124, 0, 0, 0, + 2, -47, -123, 0, 0, 0, + 2, -47, -122, 0, 0, 0, + 2, -47, -121, 0, 0, 0, + 2, -47, -120, 0, 0, 0, + 2, -47, -119, 0, 0, 0, + 2, -47, -118, 0, 0, 0, + 2, -47, -117, 0, 0, 0, + 2, -47, -116, 0, 0, 0, + 2, -47, -115, 0, 0, 0, + 2, -47, -114, 0, 0, 0, + 2, -47, -113, 0, 0, 0 }; /// Latin alphabet used by English and some other Western languages static char windows_1252[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x2, (char)0xc6, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x2, (char)0xc5, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x2, (char)0xc5, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x80, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbf, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 3, -30, -126, -84, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -102, 0, 0, + 2, -58, -110, 0, 0, 0, + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 2, -53, -122, 0, 0, 0, + 3, -30, -128, -80, 0, 0, + 2, -59, -96, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -59, -110, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 2, -59, -67, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 2, -53, -100, 0, 0, 0, + 3, -30, -124, -94, 0, 0, + 2, -59, -95, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -59, -109, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 2, -59, -66, 0, 0, 0, + 2, -59, -72, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -62, -95, 0, 0, 0, + 2, -62, -94, 0, 0, 0, + 2, -62, -93, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -62, -91, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -62, -88, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -62, -86, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -62, -81, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -62, -78, 0, 0, 0, + 2, -62, -77, 0, 0, 0, + 2, -62, -76, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -62, -72, 0, 0, 0, + 2, -62, -71, 0, 0, 0, + 2, -62, -70, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -62, -68, 0, 0, 0, + 2, -62, -67, 0, 0, 0, + 2, -62, -66, 0, 0, 0, + 2, -62, -65, 0, 0, 0, + 2, -61, -128, 0, 0, 0, + 2, -61, -127, 0, 0, 0, + 2, -61, -126, 0, 0, 0, + 2, -61, -125, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -61, -123, 0, 0, 0, + 2, -61, -122, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -61, -120, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -61, -118, 0, 0, 0, + 2, -61, -117, 0, 0, 0, + 2, -61, -116, 0, 0, 0, + 2, -61, -115, 0, 0, 0, + 2, -61, -114, 0, 0, 0, + 2, -61, -113, 0, 0, 0, + 2, -61, -112, 0, 0, 0, + 2, -61, -111, 0, 0, 0, + 2, -61, -110, 0, 0, 0, + 2, -61, -109, 0, 0, 0, + 2, -61, -108, 0, 0, 0, + 2, -61, -107, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -105, 0, 0, 0, + 2, -61, -104, 0, 0, 0, + 2, -61, -103, 0, 0, 0, + 2, -61, -102, 0, 0, 0, + 2, -61, -101, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -61, -99, 0, 0, 0, + 2, -61, -98, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -61, -96, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -61, -93, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -61, -91, 0, 0, 0, + 2, -61, -90, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -61, -88, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -61, -86, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -61, -84, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -61, -81, 0, 0, 0, + 2, -61, -80, 0, 0, 0, + 2, -61, -79, 0, 0, 0, + 2, -61, -78, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -61, -75, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -73, 0, 0, 0, + 2, -61, -72, 0, 0, 0, + 2, -61, -71, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -61, -69, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -67, 0, 0, 0, + 2, -61, -66, 0, 0, 0, + 2, -61, -65, 0, 0, 0 }; } From fab4cfecb13d90e1ac8a8cf7278f82ca78628d48 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Aug 2012 10:52:06 +0200 Subject: [PATCH 110/143] some unfinished work on quick keys window --- apps/openmw/CMakeLists.txt | 3 +- apps/openmw/mwgui/class.cpp | 19 +- apps/openmw/mwgui/class.hpp | 8 +- apps/openmw/mwgui/confirmationdialog.cpp | 13 +- apps/openmw/mwgui/confirmationdialog.hpp | 4 +- apps/openmw/mwgui/countdialog.cpp | 15 +- apps/openmw/mwgui/countdialog.hpp | 4 +- apps/openmw/mwgui/itemselection.cpp | 39 +++ apps/openmw/mwgui/itemselection.hpp | 28 +++ apps/openmw/mwgui/mode.hpp | 4 +- apps/openmw/mwgui/quickkeysmenu.cpp | 237 ++++++++++++++++++ apps/openmw/mwgui/quickkeysmenu.hpp | 84 +++++++ apps/openmw/mwgui/window_base.cpp | 23 +- apps/openmw/mwgui/window_base.hpp | 17 +- apps/openmw/mwgui/windowmanagerimp.cpp | 7 + apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 82 +++++- apps/openmw/mwinput/inputmanagerimp.hpp | 16 ++ files/mygui/CMakeLists.txt | 4 + .../mygui/openmw_itemselection_dialog.layout | 20 ++ .../mygui/openmw_magicselection_dialog.layout | 19 ++ files/mygui/openmw_quickkeys_menu.layout | 38 +++ .../mygui/openmw_quickkeys_menu_assign.layout | 28 +++ 23 files changed, 655 insertions(+), 59 deletions(-) create mode 100644 apps/openmw/mwgui/itemselection.cpp create mode 100644 apps/openmw/mwgui/itemselection.hpp create mode 100644 apps/openmw/mwgui/quickkeysmenu.cpp create mode 100644 apps/openmw/mwgui/quickkeysmenu.hpp create mode 100644 files/mygui/openmw_itemselection_dialog.layout create mode 100644 files/mygui/openmw_magicselection_dialog.layout create mode 100644 files/mygui/openmw_quickkeys_menu.layout create mode 100644 files/mygui/openmw_quickkeys_menu_assign.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 1b4ae209d9..fae76dd648 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -28,7 +28,8 @@ add_openmw_dir (mwgui dialogue_history window_base stats_window messagebox journalwindow charactercreation map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow - confirmationdialog alchemywindow referenceinterface spellwindow mainmenu + confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu + itemselection ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index eaf1918198..971740b5ed 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -695,7 +695,7 @@ void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) /* SelectSpecializationDialog */ SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_specialization.layout", parWindowManager) + : WindowModal("openmw_chargen_select_specialization.layout", parWindowManager) { // Centre dialog center(); @@ -727,13 +727,10 @@ SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& pa cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); int buttonWidth = cancelButton->getTextSize().width + 24; cancelButton->setCoord(216 - buttonWidth, 90, buttonWidth, 21); - - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } SelectSpecializationDialog::~SelectSpecializationDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls @@ -760,7 +757,7 @@ void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectAttributeDialog */ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_attribute.layout", parWindowManager) + : WindowModal("openmw_chargen_select_attribute.layout", parWindowManager) { // Centre dialog center(); @@ -785,13 +782,10 @@ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowMan cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); int buttonWidth = cancelButton->getTextSize().width + 24; cancelButton->setCoord(186 - buttonWidth, 180, buttonWidth, 21); - - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } SelectAttributeDialog::~SelectAttributeDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls @@ -812,7 +806,7 @@ void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectSkillDialog */ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_skill.layout", parWindowManager) + : WindowModal("openmw_chargen_select_skill.layout", parWindowManager) { // Centre dialog center(); @@ -884,12 +878,10 @@ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) int buttonWidth = cancelButton->getTextSize().width + 24; cancelButton->setCoord(447 - buttonWidth, 218, buttonWidth, 21); - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } SelectSkillDialog::~SelectSkillDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls @@ -908,7 +900,7 @@ void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) /* DescriptionDialog */ DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_class_description.layout", parWindowManager) + : WindowModal("openmw_chargen_class_description.layout", parWindowManager) { // Centre dialog center(); @@ -924,13 +916,10 @@ DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) // Make sure the edit box has focus MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); - - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } DescriptionDialog::~DescriptionDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 4baceed1ef..bcb5c26277 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -134,7 +134,7 @@ namespace MWGui std::string mCurrentClassId; }; - class SelectSpecializationDialog : public WindowBase + class SelectSpecializationDialog : public WindowModal { public: SelectSpecializationDialog(MWBase::WindowManager& parWindowManager); @@ -165,7 +165,7 @@ namespace MWGui ESM::Class::Specialization mSpecializationId; }; - class SelectAttributeDialog : public WindowBase + class SelectAttributeDialog : public WindowModal { public: SelectAttributeDialog(MWBase::WindowManager& parWindowManager); @@ -198,7 +198,7 @@ namespace MWGui ESM::Attribute::AttributeID mAttributeId; }; - class SelectSkillDialog : public WindowBase + class SelectSkillDialog : public WindowModal { public: SelectSkillDialog(MWBase::WindowManager& parWindowManager); @@ -234,7 +234,7 @@ namespace MWGui ESM::Skill::SkillEnum mSkillId; }; - class DescriptionDialog : public WindowBase + class DescriptionDialog : public WindowModal { public: DescriptionDialog(MWBase::WindowManager& parWindowManager); diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 1c68da9e57..d22181d60c 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -8,7 +8,7 @@ namespace MWGui { ConfirmationDialog::ConfirmationDialog(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_confirmation_dialog.layout", parWindowManager) + WindowModal("openmw_confirmation_dialog.layout", parWindowManager) { getWidget(mMessage, "Message"); getWidget(mOkButton, "OkButton"); @@ -32,9 +32,6 @@ namespace MWGui center(); - // make other gui elements inaccessible while this dialog is open - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); - int okButtonWidth = mOkButton->getTextSize().width + 24; mOkButton->setCoord(mMainWidget->getWidth() - 30 - okButtonWidth, mOkButton->getTop(), @@ -52,19 +49,13 @@ namespace MWGui { eventCancelClicked(); - close(); + setVisible(false); } void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender) { eventOkClicked(); - close(); - } - - void ConfirmationDialog::close() - { setVisible(false); - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } } diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index a78028b1c4..45941f2ad0 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -5,7 +5,7 @@ namespace MWGui { - class ConfirmationDialog : public WindowBase + class ConfirmationDialog : public WindowModal { public: ConfirmationDialog(MWBase::WindowManager& parWindowManager); @@ -26,8 +26,6 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); - - void close(); }; } diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 6d94153540..0ed9cf8cac 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -8,7 +8,7 @@ namespace MWGui { CountDialog::CountDialog(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_count_window.layout", parWindowManager) + WindowModal("openmw_count_window.layout", parWindowManager) { getWidget(mSlider, "CountSlider"); getWidget(mItemEdit, "ItemEdit"); @@ -40,9 +40,6 @@ namespace MWGui width, mMainWidget->getHeight()); - // make other gui elements inaccessible while this dialog is open - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); @@ -63,14 +60,14 @@ namespace MWGui void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) { - close(); + setVisible(false); } void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); - close(); + setVisible(false); } void CountDialog::onEditTextChange(MyGUI::EditBox* _sender) @@ -99,10 +96,4 @@ namespace MWGui { mItemEdit->setCaption(boost::lexical_cast(_position+1)); } - - void CountDialog::close() - { - setVisible(false); - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); - } } diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 6002dadfed..80da6eea01 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -5,7 +5,7 @@ namespace MWGui { - class CountDialog : public WindowBase + class CountDialog : public WindowModal { public: CountDialog(MWBase::WindowManager& parWindowManager); @@ -30,8 +30,6 @@ namespace MWGui void onOkButtonClicked(MyGUI::Widget* _sender); void onEditTextChange(MyGUI::EditBox* _sender); void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position); - - void close(); }; } diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp new file mode 100644 index 0000000000..14b1cf8ee5 --- /dev/null +++ b/apps/openmw/mwgui/itemselection.cpp @@ -0,0 +1,39 @@ +#include "itemselection.hpp" + +namespace MWGui +{ + + ItemSelectionDialog::ItemSelectionDialog(const std::string &label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager) + : ContainerBase(NULL) + , WindowModal("openmw_itemselection_dialog.layout", parWindowManager) + { + mFilter = filter; + + MyGUI::ScrollView* itemView; + MyGUI::Widget* containerWidget; + getWidget(containerWidget, "Items"); + getWidget(itemView, "ItemView"); + setWidgets(containerWidget, itemView); + + MyGUI::TextBox* l; + getWidget(l, "Label"); + l->setCaptionWithReplacing (label); + + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked); + + center(); + } + + void ItemSelectionDialog::onSelectedItemImpl(MWWorld::Ptr item) + { + eventItemSelected(item); + } + + void ItemSelectionDialog::onCancelButtonClicked(MyGUI::Widget* sender) + { + eventDialogCanceled(); + } + +} diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp new file mode 100644 index 0000000000..cab125f1f8 --- /dev/null +++ b/apps/openmw/mwgui/itemselection.hpp @@ -0,0 +1,28 @@ +#include "container.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + + class ItemSelectionDialog : public ContainerBase, public WindowModal + { + public: + ItemSelectionDialog(const std::string& label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager); + + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Item; + + EventHandle_Item eventItemSelected; + EventHandle_Void eventDialogCanceled; + + + private: + virtual void onReferenceUnavailable() { ; } + + virtual void onSelectedItemImpl(MWWorld::Ptr item); + + void onCancelButtonClicked(MyGUI::Widget* sender); + }; + +} diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 8b502ce7c9..2d6f69871b 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -32,7 +32,9 @@ namespace MWGui GM_Review, // interactive MessageBox - GM_InterMessageBox + GM_InterMessageBox, + + GM_QuickKeysMenu }; // Windows shown in inventory mode diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp new file mode 100644 index 0000000000..a9aa3e6169 --- /dev/null +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -0,0 +1,237 @@ +#include "quickkeysmenu.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" + +#include "windowmanagerimp.hpp" +#include "itemselection.hpp" + +namespace MWGui +{ + + QuickKeysMenu::QuickKeysMenu(MWBase::WindowManager& parWindowManager) + : WindowBase("openmw_quickkeys_menu.layout", parWindowManager) + , mAssignDialog(0) + , mItemSelectionDialog(0) + , mMagicSelectionDialog(0) + { + getWidget(mOkButton, "OKButton"); + getWidget(mInstructionLabel, "InstructionLabel"); + + mMainWidget->setSize(mMainWidget->getWidth(), + mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight())); + + int okButtonWidth = mOkButton->getTextSize ().width + 24; + mOkButton->setCoord(mOkButton->getLeft() - (okButtonWidth - mOkButton->getWidth()), + mOkButton->getTop(), + okButtonWidth, + mOkButton->getHeight()); + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked); + + center(); + + + for (int i = 0; i < 10; ++i) + { + MyGUI::Button* button; + getWidget(button, "QuickKey" + boost::lexical_cast(i+1)); + + if (i != 9) // 10th quick key is always set to hand-to-hand + button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + unassign(button, i); + + mQuickKeyButtons.push_back(button); + + } + } + + QuickKeysMenu::~QuickKeysMenu() + { + delete mAssignDialog; + } + + void QuickKeysMenu::unassign(MyGUI::Widget* key, int index) + { + while (key->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (key->getChildAt(0)); + + MyGUI::TextBox* textBox = key->createWidgetReal("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); + textBox->setTextAlign (MyGUI::Align::Center); + textBox->setCaption (boost::lexical_cast(index+1)); + textBox->setNeedMouseFocus (false); + } + + void QuickKeysMenu::onQuickKeyButtonClicked(MyGUI::Widget* sender) + { + int index = -1; + for (int i = 0; i < 10; ++i) + { + if (sender == mQuickKeyButtons[i] || sender->getParent () == mQuickKeyButtons[i]) + { + index = i; + break; + } + } + assert(index != -1); + mSelectedIndex = index; + + { + // open assign dialog + if (!mAssignDialog) + mAssignDialog = new QuickKeysMenuAssign(mWindowManager, this); + mAssignDialog->setVisible (true); + } + } + + void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender) + { + mWindowManager.removeGuiMode(GM_QuickKeysMenu); + } + + + void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender) + { + if (!mItemSelectionDialog ) + { + mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}", ContainerBase::Filter_All, mWindowManager); + mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem); + mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); + } + mItemSelectionDialog->setVisible(true); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->drawItems (); + + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender) + { + if (!mMagicSelectionDialog ) + { + mMagicSelectionDialog = new MagicSelectionDialog(mWindowManager, this); + } + mMagicSelectionDialog->setVisible(true); + + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender) + { + unassign(mQuickKeyButtons[mSelectedIndex], mSelectedIndex); + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender) + { + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onAssignItem(MWWorld::Ptr item) + { + MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; + while (button->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); + + MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(item); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + mItemSelectionDialog->setVisible(false); + } + + void QuickKeysMenu::onAssignItemCancel() + { + mItemSelectionDialog->setVisible(false); + } + + void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) + { + + } + + void QuickKeysMenu::onAssignMagic (const std::string& spellId) + { + + } + + void QuickKeysMenu::onAssignMagicCancel () + { + mMagicSelectionDialog->setVisible(false); + } + + // --------------------------------------------------------------------------------------------------------- + + QuickKeysMenuAssign::QuickKeysMenuAssign (MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) + : WindowModal("openmw_quickkeys_menu_assign.layout", parWindowManager) + , mParent(parent) + { + getWidget(mLabel, "Label"); + getWidget(mItemButton, "ItemButton"); + getWidget(mMagicButton, "MagicButton"); + getWidget(mUnassignButton, "UnassignButton"); + getWidget(mCancelButton, "CancelButton"); + + mItemButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onItemButtonClicked); + mMagicButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onMagicButtonClicked); + mUnassignButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onUnassignButtonClicked); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onCancelButtonClicked); + + + int maxWidth = mItemButton->getTextSize ().width + 24; + maxWidth = std::max(maxWidth, mMagicButton->getTextSize ().width + 24); + maxWidth = std::max(maxWidth, mUnassignButton->getTextSize ().width + 24); + maxWidth = std::max(maxWidth, mCancelButton->getTextSize ().width + 24); + + mMainWidget->setSize(maxWidth + 24, mMainWidget->getHeight()); + mLabel->setSize(maxWidth, mLabel->getHeight()); + + mItemButton->setCoord((maxWidth - mItemButton->getTextSize().width-24)/2 + 8, + mItemButton->getTop(), + mItemButton->getTextSize().width + 24, + mItemButton->getHeight()); + mMagicButton->setCoord((maxWidth - mMagicButton->getTextSize().width-24)/2 + 8, + mMagicButton->getTop(), + mMagicButton->getTextSize().width + 24, + mMagicButton->getHeight()); + mUnassignButton->setCoord((maxWidth - mUnassignButton->getTextSize().width-24)/2 + 8, + mUnassignButton->getTop(), + mUnassignButton->getTextSize().width + 24, + mUnassignButton->getHeight()); + mCancelButton->setCoord((maxWidth - mCancelButton->getTextSize().width-24)/2 + 8, + mCancelButton->getTop(), + mCancelButton->getTextSize().width + 24, + mCancelButton->getHeight()); + + center(); + } + + + // --------------------------------------------------------------------------------------------------------- + + MagicSelectionDialog::MagicSelectionDialog(MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) + : WindowModal("openmw_magicselection_dialog.layout", parWindowManager) + , mParent(parent) + { + getWidget(mCancelButton, "CancelButton"); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); + + center(); + } + + void MagicSelectionDialog::onCancelButtonClicked (MyGUI::Widget *sender) + { + mParent->onAssignMagicCancel (); + } + +} diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp new file mode 100644 index 0000000000..4968061099 --- /dev/null +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -0,0 +1,84 @@ +#ifndef MWGUI_QUICKKEYS_H +#define MWGUI_QUICKKEYS_H + + +#include "../mwworld/ptr.hpp" + +#include "window_base.hpp" + +namespace MWGui +{ + + class QuickKeysMenuAssign; + class ItemSelectionDialog; + class MagicSelectionDialog; + + class QuickKeysMenu : public WindowBase + { + public: + QuickKeysMenu(MWBase::WindowManager& parWindowManager); + ~QuickKeysMenu(); + + + void onItemButtonClicked(MyGUI::Widget* sender); + void onMagicButtonClicked(MyGUI::Widget* sender); + void onUnassignButtonClicked(MyGUI::Widget* sender); + void onCancelButtonClicked(MyGUI::Widget* sender); + + void onAssignItem (MWWorld::Ptr item); + void onAssignItemCancel (); + void onAssignMagicItem (MWWorld::Ptr item); + void onAssignMagic (const std::string& spellId); + void onAssignMagicCancel (); + + + private: + MyGUI::EditBox* mInstructionLabel; + MyGUI::Button* mOkButton; + + std::vector mQuickKeyButtons; + + QuickKeysMenuAssign* mAssignDialog; + ItemSelectionDialog* mItemSelectionDialog; + MagicSelectionDialog* mMagicSelectionDialog; + + int mSelectedIndex; + + + void onQuickKeyButtonClicked(MyGUI::Widget* sender); + void onOkButtonClicked(MyGUI::Widget* sender); + + void unassign(MyGUI::Widget* key, int index); + }; + + class QuickKeysMenuAssign : public WindowModal + { + public: + QuickKeysMenuAssign(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + + private: + MyGUI::TextBox* mLabel; + MyGUI::Button* mItemButton; + MyGUI::Button* mMagicButton; + MyGUI::Button* mUnassignButton; + MyGUI::Button* mCancelButton; + + QuickKeysMenu* mParent; + }; + + class MagicSelectionDialog : public WindowModal + { + public: + MagicSelectionDialog(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + + private: + MyGUI::Button* mCancelButton; + + QuickKeysMenu* mParent; + + void onCancelButtonClicked (MyGUI::Widget* sender); + }; +} + + +#endif diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/window_base.cpp index dbb37efbb0..38bee9ea30 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/window_base.cpp @@ -12,17 +12,15 @@ WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parW { } -void WindowBase::open() -{ -} - void WindowBase::setVisible(bool visible) { bool wasVisible = mMainWidget->getVisible(); mMainWidget->setVisible(visible); - if (!wasVisible && visible) + if (visible) open(); + else if (wasVisible && !visible) + close(); } void WindowBase::center() @@ -40,3 +38,18 @@ void WindowBase::center() coord.top = (gameWindowSize.height - coord.height)/2; mMainWidget->setCoord(coord); } + +WindowModal::WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager) + : WindowBase(parLayout, parWindowManager) +{ +} + +void WindowModal::open() +{ + MyGUI::InputManager::getInstance ().addWidgetModal (mMainWidget); +} + +void WindowModal::close() +{ + MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); +} diff --git a/apps/openmw/mwgui/window_base.hpp b/apps/openmw/mwgui/window_base.hpp index 74d874bb82..afdf4d065b 100644 --- a/apps/openmw/mwgui/window_base.hpp +++ b/apps/openmw/mwgui/window_base.hpp @@ -20,8 +20,9 @@ namespace MWGui // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; - virtual void open(); - virtual void setVisible(bool visible); // calls open() if visible is true and was false before + virtual void open() {} + virtual void close () {} + virtual void setVisible(bool visible); void center(); /** Event : Dialog finished, OK button clicked.\n @@ -33,6 +34,18 @@ namespace MWGui /// \todo remove MWBase::WindowManager& mWindowManager; }; + + + /* + * "Modal" windows cause the rest of the interface to be unaccessible while they are visible + */ + class WindowModal : public WindowBase + { + public: + WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + virtual void open(); + virtual void close(); + }; } #endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 676eb2046e..f1ee01c4d0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -41,6 +41,7 @@ #include "confirmationdialog.hpp" #include "alchemywindow.hpp" #include "spellwindow.hpp" +#include "quickkeysmenu.hpp" using namespace MWGui; @@ -133,6 +134,7 @@ WindowManager::WindowManager( mConfirmationDialog = new ConfirmationDialog(*this); mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); + mQuickKeysMenu = new QuickKeysMenu(*this); mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); @@ -226,6 +228,7 @@ void WindowManager::updateVisible() mSettingsWindow->setVisible(false); mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); + mQuickKeysMenu->setVisible(false); // Mouse is visible whenever we're not in game mode MyGUI::PointerManager::getInstance().setVisible(isGuiMode()); @@ -254,6 +257,9 @@ void WindowManager::updateVisible() GuiMode mode = mGuiModes.back(); switch(mode) { + case GM_QuickKeysMenu: + mQuickKeysMenu->setVisible (true); + break; case GM_MainMenu: mMenu->setVisible(true); break; @@ -652,6 +658,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mAlchemyWindow->center(); mScrollWindow->center(); mBookWindow->center(); + mQuickKeysMenu->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); mInputBlocker->setSize(MyGUI::IntSize(x,y)); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 3913055942..f27522986a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -60,6 +60,7 @@ namespace MWGui class MessageBoxManager; class SettingsWindow; class AlchemyWindow; + class QuickKeysMenu; class WindowManager : public MWBase::WindowManager { @@ -209,6 +210,7 @@ namespace MWGui ConfirmationDialog* mConfirmationDialog; AlchemyWindow* mAlchemyWindow; SpellWindow* mSpellWindow; + QuickKeysMenu* mQuickKeysMenu; CharacterCreation* mCharGen; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index a7cce25c06..8f4fde7a4f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -188,7 +188,40 @@ namespace MWInput case A_ToggleSpell: toggleSpell (); break; - } + case A_QuickKey1: + quickKey(1); + break; + case A_QuickKey2: + quickKey(2); + break; + case A_QuickKey3: + quickKey(3); + break; + case A_QuickKey4: + quickKey(4); + break; + case A_QuickKey5: + quickKey(5); + break; + case A_QuickKey6: + quickKey(6); + break; + case A_QuickKey7: + quickKey(7); + break; + case A_QuickKey8: + quickKey(8); + break; + case A_QuickKey9: + quickKey(9); + break; + case A_QuickKey10: + quickKey(10); + break; + case A_QuickKeysMenu: + showQuickKeysMenu(); + break; + } } } @@ -545,6 +578,17 @@ namespace MWInput // .. but don't touch any other mode. } + void InputManager::quickKey (int index) + { + std::cout << "quick key " << index << std::endl; + } + + void InputManager::showQuickKeysMenu() + { + if (!mWindows.isGuiMode ()) + mWindows.pushGuiMode (MWGui::GM_QuickKeysMenu); + } + void InputManager::activate() { mEngine.activate(); @@ -587,7 +631,8 @@ namespace MWInput defaultKeyBindings[A_MoveRight] = OIS::KC_D; defaultKeyBindings[A_ToggleWeapon] = OIS::KC_F; defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; - defaultKeyBindings[A_Console] = OIS::KC_F1; + defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1; + defaultKeyBindings[A_Console] = OIS::KC_F2; defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL; defaultKeyBindings[A_AutoMove] = OIS::KC_Q; defaultKeyBindings[A_Jump] = OIS::KC_E; @@ -595,9 +640,20 @@ namespace MWInput defaultKeyBindings[A_Rest] = OIS::KC_T; defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; defaultKeyBindings[A_TogglePOV] = OIS::KC_TAB; + defaultKeyBindings[A_QuickKey1] = OIS::KC_1; + defaultKeyBindings[A_QuickKey2] = OIS::KC_2; + defaultKeyBindings[A_QuickKey3] = OIS::KC_3; + defaultKeyBindings[A_QuickKey4] = OIS::KC_4; + defaultKeyBindings[A_QuickKey5] = OIS::KC_5; + defaultKeyBindings[A_QuickKey6] = OIS::KC_6; + defaultKeyBindings[A_QuickKey7] = OIS::KC_7; + defaultKeyBindings[A_QuickKey8] = OIS::KC_8; + defaultKeyBindings[A_QuickKey9] = OIS::KC_9; + defaultKeyBindings[A_QuickKey10] = OIS::KC_0; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; + defaultMouseButtonBindings[A_Use] = OIS::MB_Left; for (int i = 0; i < A_Last; ++i) { @@ -645,6 +701,17 @@ namespace MWInput descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; descriptions[A_TogglePOV] = "sTogglePOVCmd"; + descriptions[A_QuickKeysMenu] = "sQuickMenu"; + descriptions[A_QuickKey1] = "sQuick1Cmd"; + descriptions[A_QuickKey2] = "sQuick2Cmd"; + descriptions[A_QuickKey3] = "sQuick3Cmd"; + descriptions[A_QuickKey4] = "sQuick4Cmd"; + descriptions[A_QuickKey5] = "sQuick5Cmd"; + descriptions[A_QuickKey6] = "sQuick6Cmd"; + descriptions[A_QuickKey7] = "sQuick7Cmd"; + descriptions[A_QuickKey8] = "sQuick8Cmd"; + descriptions[A_QuickKey9] = "sQuick9Cmd"; + descriptions[A_QuickKey10] = "sQuick10Cmd"; if (descriptions[action] == "") return ""; // not configurable @@ -685,6 +752,17 @@ namespace MWInput ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); + ret.push_back(A_QuickKeysMenu); + ret.push_back(A_QuickKey1); + ret.push_back(A_QuickKey2); + ret.push_back(A_QuickKey3); + ret.push_back(A_QuickKey4); + ret.push_back(A_QuickKey5); + ret.push_back(A_QuickKey6); + ret.push_back(A_QuickKey7); + ret.push_back(A_QuickKey8); + ret.push_back(A_QuickKey9); + ret.push_back(A_QuickKey10); return ret; } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 71d43b8d5c..a7a2df8521 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -168,6 +168,9 @@ namespace MWInput void toggleAutoMove(); void exitNow(); + void quickKey (int index); + void showQuickKeysMenu(); + bool actionIsActive (int id); void loadKeyDefaults(bool force = false); @@ -220,6 +223,19 @@ namespace MWInput A_TogglePOV, + A_QuickKey1, + A_QuickKey2, + A_QuickKey3, + A_QuickKey4, + A_QuickKey5, + A_QuickKey6, + A_QuickKey7, + A_QuickKey8, + A_QuickKey9, + A_QuickKey10, + + A_QuickKeysMenu, + A_Last // Marker for the last item }; }; diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 3a5430c6f4..2e448a3691 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -65,6 +65,10 @@ set(MYGUI_FILES openmw_tooltips.layout openmw_trade_window.layout openmw_windows.skin.xml + openmw_quickkeys_menu.layout + openmw_quickkeys_menu_assign.layout + openmw_itemselection_dialog.layout + openmw_magicselection_dialog.layout smallbars.png VeraMono.ttf ) diff --git a/files/mygui/openmw_itemselection_dialog.layout b/files/mygui/openmw_itemselection_dialog.layout new file mode 100644 index 0000000000..93681e69e1 --- /dev/null +++ b/files/mygui/openmw_itemselection_dialog.layout @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout new file mode 100644 index 0000000000..7834b0ed9d --- /dev/null +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_quickkeys_menu.layout b/files/mygui/openmw_quickkeys_menu.layout new file mode 100644 index 0000000000..a45f2896fd --- /dev/null +++ b/files/mygui/openmw_quickkeys_menu.layout @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_quickkeys_menu_assign.layout b/files/mygui/openmw_quickkeys_menu_assign.layout new file mode 100644 index 0000000000..4bbd9f1fa5 --- /dev/null +++ b/files/mygui/openmw_quickkeys_menu_assign.layout @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c0f27bd5ef3f1420dd88aef367bc49d4788c9879 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Aug 2012 11:37:33 +0200 Subject: [PATCH 111/143] magic selection window --- apps/openmw/mwgui/quickkeysmenu.cpp | 257 +++++++++++++++++- apps/openmw/mwgui/quickkeysmenu.hpp | 13 + .../mygui/openmw_magicselection_dialog.layout | 2 +- 3 files changed, 270 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index a9aa3e6169..6739b3d1b4 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -5,10 +5,33 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "windowmanagerimp.hpp" #include "itemselection.hpp" + +namespace +{ + bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) + { + int cmp = MWWorld::Class::get(left).getName(left).compare( + MWWorld::Class::get(right).getName(right)); + return cmp < 0; + } + + bool sortSpells(const std::string& left, const std::string& right) + { + const ESM::Spell* a = MWBase::Environment::get().getWorld()->getStore().spells.find(left); + const ESM::Spell* b = MWBase::Environment::get().getWorld()->getStore().spells.find(right); + + int cmp = a->name.compare(b->name); + return cmp < 0; + } +} + namespace MWGui { @@ -157,12 +180,35 @@ namespace MWGui void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) { - + onAssignItem(item); + mMagicSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignMagic (const std::string& spellId) { + MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; + while (button->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); + MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + image->setUserString ("ToolTipType", "Spell"); + image->setUserString ("Spell", spellId); + + // use the icon of the first effect + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); + const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().magicEffects.find(spell->effects.list.front().effectID); + std::string path = effect->icon; + int slashPos = path.find("\\"); + path.insert(slashPos+1, "b_"); + path = std::string("icons\\") + path; + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + + image->setImageTexture (path); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + mMagicSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignMagicCancel () @@ -222,8 +268,11 @@ namespace MWGui MagicSelectionDialog::MagicSelectionDialog(MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) : WindowModal("openmw_magicselection_dialog.layout", parWindowManager) , mParent(parent) + , mWidth(0) + , mHeight(0) { getWidget(mCancelButton, "CancelButton"); + getWidget(mMagicList, "MagicList"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); center(); @@ -234,4 +283,210 @@ namespace MWGui mParent->onAssignMagicCancel (); } + void MagicSelectionDialog::open () + { + WindowModal::open(); + + while (mMagicList->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mMagicList->getChildAt (0)); + + mHeight = 0; + + const int spellHeight = 18; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + + /// \todo lots of copy&pasted code from SpellWindow + + // retrieve powers & spells, sort by name + std::vector spellList; + + for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + spellList.push_back(*it); + } + + std::vector powers; + std::vector::iterator it = spellList.begin(); + while (it != spellList.end()) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it); + if (spell->data.type == ESM::Spell::ST_Power) + { + powers.push_back(*it); + it = spellList.erase(it); + } + else if (spell->data.type == ESM::Spell::ST_Ability + || spell->data.type == ESM::Spell::ST_Blight + || spell->data.type == ESM::Spell::ST_Curse + || spell->data.type == ESM::Spell::ST_Disease) + { + it = spellList.erase(it); + } + else + ++it; + } + std::sort(powers.begin(), powers.end(), sortSpells); + std::sort(spellList.begin(), spellList.end(), sortSpells); + + // retrieve usable magic items & sort + std::vector items; + for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it) + { + std::string enchantId = MWWorld::Class::get(*it).getEnchantment(*it); + if (enchantId != "") + { + // only add items with "Cast once" or "Cast on use" + const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().enchants.find(enchantId); + int type = enchant->data.type; + if (type != ESM::Enchantment::CastOnce + && type != ESM::Enchantment::WhenUsed) + continue; + + items.push_back(*it); + } + } + std::sort(items.begin(), items.end(), sortItems); + + + int height = estimateHeight(items.size() + powers.size() + spellList.size()); + bool scrollVisible = height > mMagicList->getHeight(); + mWidth = mMagicList->getWidth() - scrollVisible * 18; + + + // powers + addGroup("#{sPowers}", ""); + + for (std::vector::const_iterator it = powers.begin(); it != powers.end(); ++it) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it); + MyGUI::Button* t = mMagicList->createWidget("SpellText", + MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setCaption(spell->name); + t->setTextAlign(MyGUI::Align::Left); + t->setUserString("ToolTipType", "Spell"); + t->setUserString("Spell", *it); + t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); + t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onSpellSelected); + + mHeight += spellHeight; + } + + // other spells + addGroup("#{sSpells}", ""); + for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it); + MyGUI::Button* t = mMagicList->createWidget("SpellText", + MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setCaption(spell->name); + t->setTextAlign(MyGUI::Align::Left); + t->setUserString("ToolTipType", "Spell"); + t->setUserString("Spell", *it); + t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); + t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onSpellSelected); + + mHeight += spellHeight; + } + + + // enchanted items + addGroup("#{sMagicItem}", ""); + + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + MWWorld::Ptr item = *it; + + // check if the item is currently equipped (will display in a different color) + bool equipped = false; + for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) + { + if (store.getSlot(i) != store.end() && *store.getSlot(i) == item) + { + equipped = true; + break; + } + } + + MyGUI::Button* t = mMagicList->createWidget(equipped ? "SpellText" : "SpellTextUnequipped", + MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setCaption(MWWorld::Class::get(item).getName(item)); + t->setTextAlign(MyGUI::Align::Left); + t->setUserData(item); + t->setUserString("ToolTipType", "ItemPtr"); + t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onEnchantedItemSelected); + t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); + + mHeight += spellHeight; + } + + + mMagicList->setCanvasSize (mWidth, std::max(mMagicList->getHeight(), mHeight)); + + } + + void MagicSelectionDialog::addGroup(const std::string &label, const std::string& label2) + { + if (mMagicList->getChildCount() > 0) + { + MyGUI::ImageBox* separator = mMagicList->createWidget("MW_HLine", + MyGUI::IntCoord(4, mHeight, mWidth-8, 18), + MyGUI::Align::Left | MyGUI::Align::Top); + separator->setNeedMouseFocus(false); + mHeight += 18; + } + + MyGUI::TextBox* groupWidget = mMagicList->createWidget("SandBrightText", + MyGUI::IntCoord(0, mHeight, mWidth, 24), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + groupWidget->setCaptionWithReplacing(label); + groupWidget->setTextAlign(MyGUI::Align::Left); + groupWidget->setNeedMouseFocus(false); + + if (label2 != "") + { + MyGUI::TextBox* groupWidget2 = mMagicList->createWidget("SandBrightText", + MyGUI::IntCoord(0, mHeight, mWidth-4, 24), + MyGUI::Align::Left | MyGUI::Align::Top); + groupWidget2->setCaptionWithReplacing(label2); + groupWidget2->setTextAlign(MyGUI::Align::Right); + groupWidget2->setNeedMouseFocus(false); + } + + mHeight += 24; + } + + + void MagicSelectionDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mMagicList->getViewOffset().top + _rel*0.3 > 0) + mMagicList->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mMagicList->setViewOffset(MyGUI::IntPoint(0, mMagicList->getViewOffset().top + _rel*0.3)); + } + + void MagicSelectionDialog::onEnchantedItemSelected(MyGUI::Widget* _sender) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr item = *_sender->getUserData(); + + mParent->onAssignMagicItem (item); + } + + void MagicSelectionDialog::onSpellSelected(MyGUI::Widget* _sender) + { + mParent->onAssignMagic (_sender->getUserString("Spell")); + } + + int MagicSelectionDialog::estimateHeight(int numSpells) const + { + int height = 0; + height += 24 * 3 + 18 * 2; // group headings + height += numSpells * 18; + return height; + } + } diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 4968061099..44e48d3b0c 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -71,12 +71,25 @@ namespace MWGui public: MagicSelectionDialog(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + virtual void open(); + private: MyGUI::Button* mCancelButton; + MyGUI::ScrollView* mMagicList; + + int mWidth; + int mHeight; QuickKeysMenu* mParent; void onCancelButtonClicked (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void onEnchantedItemSelected(MyGUI::Widget* _sender); + void onSpellSelected(MyGUI::Widget* _sender); + int estimateHeight(int numSpells) const; + + + void addGroup(const std::string& label, const std::string& label2); }; } diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout index 7834b0ed9d..bf942b32f4 100644 --- a/files/mygui/openmw_magicselection_dialog.layout +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -7,7 +7,7 @@ - + From 5cbb08fee16cae95325f957bcd26f8f3f4116636 Mon Sep 17 00:00:00 2001 From: Douglas Diniz Date: Sun, 26 Aug 2012 11:47:45 -0300 Subject: [PATCH 112/143] Task 339 - Moving all sounds to actions --- apps/openmw/mwclass/door.cpp | 8 +------- apps/openmw/mwworld/action.cpp | 14 ++++++++------ apps/openmw/mwworld/action.hpp | 8 +++++--- apps/openmw/mwworld/actionteleport.cpp | 6 +++++- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index bf5dd4bba1..f483aa3a88 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -6,8 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/soundmanager.hpp" - #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -108,13 +106,9 @@ namespace MWClass /// \todo remove this if clause once ActionTeleport can also support other actors if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) { - // the player is using the door - // The reason this is not 3D is that it would get interrupted when you teleport - //MWBase::Environment::get().getSoundManager()->playSound3D(ptr,openSound, 1.0, 1.0); - boost::shared_ptr action(new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); - action->setSound(openSound, true); + action->setSound(openSound); return action; } diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 15bae4cd46..90e30a5bfc 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -2,9 +2,13 @@ #include "action.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwbase/soundmanager.hpp" -MWWorld::Action::Action() {} +MWWorld::Action::Action() { + teleport = false; +} MWWorld::Action::~Action() {} @@ -12,15 +16,14 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) { - if (onActor) + if (teleport == true) { - std::cout << "Douglas - Som Normal" << std::endl; + //this is a teleport action, so we need to call playSound MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); } else { - std::cout << "Douglas - Som 3D" << std::endl; MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); } @@ -29,8 +32,7 @@ void MWWorld::Action::execute (const Ptr& actor) executeImp (actor); } -void MWWorld::Action::setSound (const std::string& id, const bool onActorValue) +void MWWorld::Action::setSound (const std::string& id) { mSoundId = id; - onActor = onActorValue; } diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index f9ad975d39..01180a7784 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -11,7 +11,6 @@ namespace MWWorld class Action { std::string mSoundId; - bool onActor; // not implemented Action (const Action& action); @@ -19,7 +18,10 @@ namespace MWWorld virtual void executeImp (const Ptr& actor) = 0; - public: + protected: + bool teleport; //True if the action will teleport the actor + + public: Action(); @@ -27,7 +29,7 @@ namespace MWWorld void execute (const Ptr& actor); - void setSound (const std::string& id, const bool onActor = false); + void setSound (const std::string& id); }; } diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 9c87d37ae5..e658732236 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -9,10 +9,14 @@ namespace MWWorld ActionTeleport::ActionTeleport (const std::string& cellName, const ESM::Position& position) : mCellName (cellName), mPosition (position) - {} + { + teleport = true; + } void ActionTeleport::executeImp (const Ptr& actor) { + teleport = true; + if (mCellName.empty()) MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); else From d0cebea580b6148f7b175dcc36fc8f2516173df0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 26 Aug 2012 18:50:47 +0200 Subject: [PATCH 113/143] some cleanup --- apps/openmw/mwworld/action.cpp | 27 +++++++++++++------------- apps/openmw/mwworld/action.hpp | 7 +++---- apps/openmw/mwworld/actionteleport.cpp | 5 +---- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 90e30a5bfc..748f6aff78 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -6,9 +6,8 @@ #include "../mwbase/soundmanager.hpp" -MWWorld::Action::Action() { - teleport = false; -} +MWWorld::Action::Action (bool teleport) : mTeleport (teleport) +{} MWWorld::Action::~Action() {} @@ -16,17 +15,17 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) { - if (teleport == true) - { - //this is a teleport action, so we need to call playSound - MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); - } - else - { - MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); - } + if (mTeleport == true) + { + //this is a teleport action, so we need to call playSound + MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } + else + { + MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } } executeImp (actor); diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index 01180a7784..511a7002da 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -11,6 +11,7 @@ namespace MWWorld class Action { std::string mSoundId; + bool mTeleport; // not implemented Action (const Action& action); @@ -18,12 +19,10 @@ namespace MWWorld virtual void executeImp (const Ptr& actor) = 0; - protected: - bool teleport; //True if the action will teleport the actor - public: - Action(); + Action (bool teleport = false); + ///< \param teleport action will teleport the actor virtual ~Action(); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index e658732236..ae5ffc3b90 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -8,15 +8,12 @@ namespace MWWorld { ActionTeleport::ActionTeleport (const std::string& cellName, const ESM::Position& position) - : mCellName (cellName), mPosition (position) + : Action (true), mCellName (cellName), mPosition (position) { - teleport = true; } void ActionTeleport::executeImp (const Ptr& actor) { - teleport = true; - if (mCellName.empty()) MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); else From cc4d55a7557a9976a8a5ea25a6e0e66191d1555c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 26 Aug 2012 20:59:19 +0200 Subject: [PATCH 114/143] updated credits.txt --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index c7ec4936e7..62e41ff0bf 100644 --- a/credits.txt +++ b/credits.txt @@ -16,6 +16,7 @@ Artem Kotsynyak (greye) athile BrotherBrick Cris Mihalache (Mirceam) +Douglas Diniz (Dgdiniz) Eli2 gugus / gus Jacob Essex (Yacoby) From b1a394552dd6c2f5b406a68514591d41340ab343 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 10:01:53 +0200 Subject: [PATCH 115/143] mouse click sounds --- apps/openmw/mwinput/inputmanagerimp.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8f4fde7a4f..40091f3582 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include #include @@ -21,6 +23,7 @@ #include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" namespace MWInput { @@ -442,6 +445,15 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) + { + MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); + if (b) + { + MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); + } + } + return true; } From c5e55d3cac140872de52455bdf1b9f658632d01d Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 15:51:01 +0200 Subject: [PATCH 116/143] fix screenshot function --- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwgui/quickkeysmenu.cpp | 82 +++++++++++++++++-- apps/openmw/mwgui/quickkeysmenu.hpp | 10 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 8 +- .../mygui/openmw_itemselection_dialog.layout | 8 +- 7 files changed, 102 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fde965256c..589ab4a4b6 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -171,6 +171,8 @@ namespace MWBase virtual void setWeaponVisibility(bool visible) = 0; virtual void setSpellVisibility(bool visible) = 0; + virtual void activateQuickKey (int index) = 0; + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) = 0; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 6739b3d1b4..853e2ed14e 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -82,6 +82,8 @@ namespace MWGui while (key->getChildCount ()) MyGUI::Gui::getInstance ().destroyWidget (key->getChildAt(0)); + key->setUserData(Type_Unassigned); + MyGUI::TextBox* textBox = key->createWidgetReal("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); textBox->setTextAlign (MyGUI::Align::Center); textBox->setCaption (boost::lexical_cast(index+1)); @@ -159,16 +161,25 @@ namespace MWGui while (button->getChildCount ()) MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); - MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); - image->setUserString ("ToolTipType", "ItemPtr"); - image->setUserData(item); + button->setUserData(Type_Item); + + MyGUI::ImageBox* frame = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + std::string backgroundTex = "textures\\menu_icon_barter.dds"; + frame->setImageTexture (backgroundTex); + frame->setImageCoord (MyGUI::IntCoord(4, 4, 40, 40)); + frame->setUserString ("ToolTipType", "ItemPtr"); + frame->setUserData(item); + frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + + MyGUI::ImageBox* image = frame->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); std::string path = std::string("icons\\"); path += MWWorld::Class::get(item).getInventoryIcon(item); int pos = path.rfind("."); path.erase(pos); path.append(".dds"); image->setImageTexture (path); - image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + image->setNeedMouseFocus (false); mItemSelectionDialog->setVisible(false); } @@ -180,7 +191,29 @@ namespace MWGui void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) { - onAssignItem(item); + MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; + while (button->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); + + button->setUserData(Type_MagicItem); + + MyGUI::ImageBox* frame = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + std::string backgroundTex = "textures\\menu_icon_select_magic_magic.dds"; + frame->setImageTexture (backgroundTex); + frame->setImageCoord (MyGUI::IntCoord(2, 2, 40, 40)); + frame->setUserString ("ToolTipType", "ItemPtr"); + frame->setUserData(item); + frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + MyGUI::ImageBox* image = frame->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setNeedMouseFocus (false); + mMagicSelectionDialog->setVisible(false); } @@ -190,9 +223,17 @@ namespace MWGui while (button->getChildCount ()) MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); - MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); - image->setUserString ("ToolTipType", "Spell"); - image->setUserString ("Spell", spellId); + button->setUserData(Type_Magic); + + MyGUI::ImageBox* frame = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + std::string backgroundTex = "textures\\menu_icon_select_magic.dds"; + frame->setImageTexture (backgroundTex); + frame->setImageCoord (MyGUI::IntCoord(2, 2, 40, 40)); + frame->setUserString ("ToolTipType", "Spell"); + frame->setUserString ("Spell", spellId); + frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + MyGUI::ImageBox* image = frame->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); // use the icon of the first effect const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); @@ -206,7 +247,7 @@ namespace MWGui path.append(".dds"); image->setImageTexture (path); - image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + image->setNeedMouseFocus (false); mMagicSelectionDialog->setVisible(false); } @@ -216,6 +257,29 @@ namespace MWGui mMagicSelectionDialog->setVisible(false); } + void QuickKeysMenu::activateQuickKey(int index) + { + MyGUI::Button* button = mQuickKeyButtons[index-1]; + + QuickKeyType type = *button->getUserData(); + + if (type == Type_Magic) + { + std::string spellId = button->getChildAt(0)->getUserString("Spell"); + MWBase::Environment::get().getWindowManager ()->setSelectedSpell (spellId, 100); + } + else if (type == Type_Item) + { + MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); + MWBase::Environment::get().getWindowManager ()->setSelectedWeapon(item, 100); + } + else if (type == Type_MagicItem) + { + MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); + MWBase::Environment::get().getWindowManager ()->setSelectedEnchantItem (item, 100); + } + } + // --------------------------------------------------------------------------------------------------------- QuickKeysMenuAssign::QuickKeysMenuAssign (MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 44e48d3b0c..345ffa0c87 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -31,6 +31,16 @@ namespace MWGui void onAssignMagic (const std::string& spellId); void onAssignMagicCancel (); + void activateQuickKey(int index); + + enum QuickKeyType + { + Type_Item, + Type_Magic, + Type_MagicItem, + Type_Unassigned + }; + private: MyGUI::EditBox* mInstructionLabel; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f1ee01c4d0..138349c2ec 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -854,3 +854,8 @@ void WindowManager::notifyInputActionBound () mSettingsWindow->updateControlsBox (); allowMouse(); } + +void WindowManager::activateQuickKey (int index) +{ + mQuickKeysMenu->activateQuickKey(index); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index f27522986a..8b011e259a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -152,6 +152,8 @@ namespace MWGui virtual void setWeaponVisibility(bool visible); virtual void setSpellVisibility(bool visible); + virtual void activateQuickKey (int index); + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 49b9a08a8d..7daafc726c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -599,7 +599,7 @@ namespace MWInput void InputManager::quickKey (int index) { - std::cout << "quick key " << index << std::endl; + mWindows.activateQuickKey (index); } void InputManager::showQuickKeysMenu() @@ -669,6 +669,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey8] = OIS::KC_8; defaultKeyBindings[A_QuickKey9] = OIS::KC_9; defaultKeyBindings[A_QuickKey10] = OIS::KC_0; + defaultKeyBindings[A_Screenshot] = OIS::KC_SYSRQ; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; @@ -689,7 +690,10 @@ namespace MWInput control = mInputCtrl->getChannel(i)->getAttachedControls ().front().control; } - if (!controlExists || force) + if (!controlExists || force || + ( mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) == OIS::KC_UNASSIGNED + && mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS + )) { clearAllBindings (control); diff --git a/files/mygui/openmw_itemselection_dialog.layout b/files/mygui/openmw_itemselection_dialog.layout index 93681e69e1..81376d6d5c 100644 --- a/files/mygui/openmw_itemselection_dialog.layout +++ b/files/mygui/openmw_itemselection_dialog.layout @@ -4,14 +4,14 @@ - - + + - + - + From eff2799c1bc1e8374c60699a9f07bfc51619bfda Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Mon, 27 Aug 2012 10:55:39 -0400 Subject: [PATCH 117/143] Update UTF 8 table generator to print char values This patch is in relation to commit 25fa8165f97 (Use char literals in UTF 8 conversion to fix 798 warnings), which changed the UTF 8 table to have char integer values instead of unsigned chars. Those values were converted using a custom Python script. This patch changes the original table generator so it can now output the same format. --- components/to_utf8/gen_iconv.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/components/to_utf8/gen_iconv.cpp b/components/to_utf8/gen_iconv.cpp index cc7cc191ab..dea68c1fa2 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/to_utf8/gen_iconv.cpp @@ -1,7 +1,6 @@ // This program generates the file tables_gen.hpp #include -#include using namespace std; #include @@ -10,9 +9,11 @@ using namespace std; void tab() { cout << " "; } // write one number with a space in front of it and a comma after it -void num(unsigned char i, bool last) +void num(char i, bool last) { - cout << " (char)0x" << (unsigned)i; + // Convert i to its integer value, i.e. -128 to 127. Printing it directly + // would result in non-printable characters in the source code, which is bad. + cout << " " << static_cast(i); if(!last) cout << ","; } @@ -80,8 +81,6 @@ int write_table(const std::string &charset, const std::string &tableName) int main() { - cout << hex; - // Write header guard cout << "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\n"; From 9af0b48ad6affe3aa1d95e5ff98b1b86ea6b3238 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 19:18:55 +0200 Subject: [PATCH 118/143] show/hide crosshair correctly --- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwgui/hud.cpp | 161 ++++++++++++++---------- apps/openmw/mwgui/hud.hpp | 26 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 15 ++- apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 9 +- apps/openmw/mwrender/player.cpp | 4 + 7 files changed, 134 insertions(+), 85 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fde965256c..a65c2ff4d7 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -177,6 +177,8 @@ namespace MWBase virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; + virtual void showCrosshair(bool show) = 0; + virtual void disallowMouse() = 0; virtual void allowMouse() = 0; virtual void notifyInputActionBound() = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 92dc4e495f..20b9765d0e 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -24,9 +24,9 @@ using namespace MWGui; HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) : Layout("openmw_hud.layout") - , health(NULL) - , magicka(NULL) - , stamina(NULL) + , mHealth(NULL) + , mMagicka(NULL) + , mStamina(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) @@ -36,10 +36,10 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) , mMinimap(NULL) , mCompass(NULL) , mCrosshair(NULL) - , fpsbox(NULL) - , fpscounter(NULL) - , trianglecounter(NULL) - , batchcounter(NULL) + , mFpsBox(NULL) + , mFpsCounter(NULL) + , mTriangleCounter(NULL) + , mBatchCounter(NULL) , mHealthManaStaminaBaseLeft(0) , mWeapBoxBaseLeft(0) , mSpellBoxBaseLeft(0) @@ -57,9 +57,9 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) // Energy bars getWidget(mHealthFrame, "HealthFrame"); - getWidget(health, "Health"); - getWidget(magicka, "Magicka"); - getWidget(stamina, "Stamina"); + getWidget(mHealth, "Health"); + getWidget(mMagicka, "Magicka"); + getWidget(mStamina, "Stamina"); mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); @@ -104,8 +104,8 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) setFpsLevel(fpsLevel); - getWidget(trianglecounter, "TriangleCounter"); - getWidget(batchcounter, "BatchCounter"); + getWidget(mTriangleCounter, "TriangleCounter"); + getWidget(mBatchCounter, "BatchCounter"); setEffect("icons\\s\\tx_s_chameleon.dds"); @@ -118,7 +118,7 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) void HUD::setFpsLevel(int level) { - fpscounter = 0; + mFpsCounter = 0; MyGUI::Widget* fps; getWidget(fps, "FPSBoxAdv"); @@ -128,32 +128,32 @@ void HUD::setFpsLevel(int level) if (level == 2) { - getWidget(fpsbox, "FPSBoxAdv"); - fpsbox->setVisible(true); - getWidget(fpscounter, "FPSCounterAdv"); + getWidget(mFpsBox, "FPSBoxAdv"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounterAdv"); } else if (level == 1) { - getWidget(fpsbox, "FPSBox"); - fpsbox->setVisible(true); - getWidget(fpscounter, "FPSCounter"); + getWidget(mFpsBox, "FPSBox"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounter"); } } void HUD::setFPS(float fps) { - if (fpscounter) - fpscounter->setCaption(boost::lexical_cast((int)fps)); + if (mFpsCounter) + mFpsCounter->setCaption(boost::lexical_cast((int)fps)); } void HUD::setTriangleCount(unsigned int count) { - trianglecounter->setCaption(boost::lexical_cast(count)); + mTriangleCounter->setCaption(boost::lexical_cast(count)); } void HUD::setBatchCount(unsigned int count) { - batchcounter->setCaption(boost::lexical_cast(count)); + mBatchCounter->setCaption(boost::lexical_cast(count)); } void HUD::setEffect(const char *img) @@ -176,20 +176,20 @@ void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& v switch (i) { case 0: - health->setProgressRange (value.getModified()); - health->setProgressPosition (value.getCurrent()); + mHealth->setProgressRange (value.getModified()); + mHealth->setProgressPosition (value.getCurrent()); getWidget(w, "HealthFrame"); w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); break; case 1: - magicka->setProgressRange (value.getModified()); - magicka->setProgressPosition (value.getCurrent()); + mMagicka->setProgressRange (value.getModified()); + mMagicka->setProgressPosition (value.getCurrent()); getWidget(w, "MagickaFrame"); w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); break; case 2: - stamina->setProgressRange (value.getModified()); - stamina->setProgressPosition (value.getCurrent()); + mStamina->setProgressRange (value.getModified()); + mStamina->setProgressPosition (value.getCurrent()); getWidget(w, "FatigueFrame"); w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); break; @@ -197,44 +197,6 @@ void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& v } } -void HUD::setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellVisible) -{ - int weapDx = 0, spellDx = 0; - if (!hmsVisible) - spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; - - if (!weapVisible) - spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; - - mWeaponVisible = weapVisible; - mSpellVisible = spellVisible; - if (!mWeaponVisible && !mSpellVisible) - mWeaponSpellBox->setVisible(false); - - health->setVisible(hmsVisible); - stamina->setVisible(hmsVisible); - magicka->setVisible(hmsVisible); - mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); - mWeapBox->setVisible(weapVisible); - mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); - mSpellBox->setVisible(spellVisible); -} - -void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible) -{ - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // effect box can have variable width -> variable left coordinate - int effectsDx = 0; - if (!minimapBoxVisible) - effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); - - mMapVisible = minimapBoxVisible; - mMinimapBox->setVisible(minimapBoxVisible); - mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); - mEffectBox->setVisible(effectBoxVisible); -} - void HUD::onWorldClicked(MyGUI::Widget* _sender) { if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) @@ -520,3 +482,68 @@ void HUD::unsetSelectedWeapon() mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); mWeapBox->clearUserStrings(); } + +void HUD::setCrosshairVisible(bool visible) +{ + mCrosshair->setVisible (visible); +} + +void HUD::setHmsVisible(bool visible) +{ + mHealth->setVisible(visible); + mMagicka->setVisible(visible); + mStamina->setVisible(visible); + updatePositions(); +} + +void HUD::setWeapVisible(bool visible) +{ + mWeapBox->setVisible(visible); + updatePositions(); +} + +void HUD::setSpellVisible(bool visible) +{ + mSpellBox->setVisible(visible); + updatePositions(); +} + +void HUD::setEffectVisible(bool visible) +{ + mEffectBox->setVisible (visible); + updatePositions(); +} + +void HUD::setMinimapVisible(bool visible) +{ + mMinimapBox->setVisible (visible); + updatePositions(); +} + +void HUD::updatePositions() +{ + int weapDx = 0, spellDx = 0; + if (!mHealth->getVisible()) + spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; + + if (!mWeapBox->getVisible()) + spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; + + mWeaponVisible = mWeapBox->getVisible(); + mSpellVisible = mSpellBox->getVisible(); + if (!mWeaponVisible && !mSpellVisible) + mWeaponSpellBox->setVisible(false); + + mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); + mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // effect box can have variable width -> variable left coordinate + int effectsDx = 0; + if (!mMinimapBox->getVisible ()) + effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); + + mMapVisible = mMinimapBox->getVisible (); + mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); +} diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index baa350a10c..49ed6a698a 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -18,8 +18,14 @@ namespace MWGui void setFPS(float fps); void setTriangleCount(unsigned int count); void setBatchCount(unsigned int count); - void setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellVisible); - void setBottomRightVisibility(bool effectBoxVisible, bool minimapVisible); + + void setHmsVisible(bool visible); + void setWeapVisible(bool visible); + void setSpellVisible(bool visible); + + void setEffectVisible(bool visible); + void setMinimapVisible(bool visible); + void setFpsLevel(const int level); void setSelectedSpell(const std::string& spellId, int successChancePercent); @@ -28,6 +34,8 @@ namespace MWGui void unsetSelectedSpell(); void unsetSelectedWeapon(); + void setCrosshairVisible(bool visible); + void onFrame(float dt); void onResChange(int width, int height); @@ -35,7 +43,8 @@ namespace MWGui bool getWorldMouseOver() { return mWorldMouseOver; } - MyGUI::ProgressPtr health, magicka, stamina; + private: + MyGUI::ProgressPtr mHealth, mMagicka, mStamina; MyGUI::Widget* mHealthFrame; MyGUI::Widget *mWeapBox, *mSpellBox; MyGUI::ImageBox *mWeapImage, *mSpellImage; @@ -50,12 +59,11 @@ namespace MWGui MyGUI::Widget* mDummy; - MyGUI::WidgetPtr fpsbox; - MyGUI::TextBox* fpscounter; - MyGUI::TextBox* trianglecounter; - MyGUI::TextBox* batchcounter; + MyGUI::WidgetPtr mFpsBox; + MyGUI::TextBox* mFpsCounter; + MyGUI::TextBox* mTriangleCounter; + MyGUI::TextBox* mBatchCounter; - private: // bottom left elements int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft; // bottom right elements @@ -83,5 +91,7 @@ namespace MWGui void onWeaponClicked(MyGUI::Widget* _sender); void onMagicClicked(MyGUI::Widget* _sender); void onMapClicked(MyGUI::Widget* _sender); + + void updatePositions(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 676eb2046e..c458ac6c6d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -559,12 +559,12 @@ void WindowManager::setPlayerDir(const float x, const float y) void WindowManager::setHMSVisibility(bool visible) { - mHud->setBottomLeftVisibility(visible, mHud->mWeapBox->getVisible(), mHud->mSpellBox->getVisible()); + mHud->setHmsVisible (visible); } void WindowManager::setMinimapVisibility(bool visible) { - mHud->setBottomRightVisibility(mHud->mEffectBox->getVisible(), visible); + mHud->setMinimapVisible (visible); } void WindowManager::toggleFogOfWar() @@ -595,13 +595,13 @@ bool WindowManager::getFullHelp() const void WindowManager::setWeaponVisibility(bool visible) { - mHud->setBottomLeftVisibility(mHud->health->getVisible(), visible, mHud->mSpellBox->getVisible()); + mHud->setWeapVisible (visible); } void WindowManager::setSpellVisibility(bool visible) { - mHud->setBottomLeftVisibility(mHud->health->getVisible(), mHud->mWeapBox->getVisible(), visible); - mHud->setBottomRightVisibility(visible, mHud->mMinimapBox->getVisible()); + mHud->setSpellVisible (visible); + mHud->setEffectVisible (visible); } void WindowManager::setMouseVisible(bool visible) @@ -847,3 +847,8 @@ void WindowManager::notifyInputActionBound () mSettingsWindow->updateControlsBox (); allowMouse(); } + +void WindowManager::showCrosshair (bool show) +{ + mHud->setCrosshairVisible (show); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 3913055942..daff81f8c2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -157,6 +157,8 @@ namespace MWGui virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); + virtual void showCrosshair(bool show); + virtual void disallowMouse(); virtual void allowMouse(); virtual void notifyInputActionBound(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index caf4d79c90..c0e678a10e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -176,23 +176,18 @@ namespace MWInput toggleJournal (); break; case A_AutoMove: - resetIdleTime(); toggleAutoMove (); break; case A_ToggleSneak: /// \todo implement - resetIdleTime(); break; case A_ToggleWalk: - resetIdleTime(); toggleWalking (); break; case A_ToggleWeapon: - resetIdleTime(); toggleWeapon (); break; case A_ToggleSpell: - resetIdleTime(); toggleSpell (); break; } @@ -303,6 +298,8 @@ namespace MWInput // Disable mouse look mMouseLookEnabled = false; + mWindows.showCrosshair (false); + // Enable GUI events mGuiCursorEnabled = true; } @@ -311,6 +308,8 @@ namespace MWInput // Enable mouse look mMouseLookEnabled = true; + mWindows.showCrosshair (false); + // Disable GUI events mGuiCursorEnabled = false; } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8b99e001a2..b9ab0da792 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -116,6 +116,10 @@ namespace MWRender void Player::update(float duration) { + // only show the crosshair in game mode and in first person mode. + MWBase::Environment::get().getWindowManager ()->showCrosshair + (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); + if (mAnimation) { mAnimation->runAnimation(duration); } From dc8f9bd92360b0a0d2384648efee0f64e71beae9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 19:25:30 +0200 Subject: [PATCH 119/143] workaround for alt tab view mode toggle --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index caf4d79c90..5ead446f13 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -258,7 +258,9 @@ namespace MWInput mPlayer.setUpDown (0); if (mControlSwitch["playerviewswitch"]) { - if (actionIsActive(A_TogglePOV)) { + + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mKeyboard->isModifierDown (OIS::Keyboard::Alt)) { if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += dt) > 0.5) { From 6161f81c2475550feea994b405b6adb8df0ea762 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:44:14 +0200 Subject: [PATCH 120/143] last bits of the quick key menu. --- apps/openmw/mwgui/itemselection.cpp | 6 ++ apps/openmw/mwgui/quickkeysmenu.cpp | 79 ++++++++++++++++++- apps/openmw/mwgui/spellwindow.cpp | 29 +------ apps/openmw/mwmechanics/spellsuccess.hpp | 2 + .../mygui/openmw_magicselection_dialog.layout | 8 +- 5 files changed, 91 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 14b1cf8ee5..d93a621ac2 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -23,6 +23,12 @@ namespace MWGui getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked); + int dx = (cancelButton->getTextSize().width + 24) - cancelButton->getWidth(); + cancelButton->setCoord(cancelButton->getLeft() - dx, + cancelButton->getTop(), + cancelButton->getTextSize ().width + 24, + cancelButton->getHeight()); + center(); } diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 853e2ed14e..fcfac8d5d9 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -6,8 +6,13 @@ #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/actionequip.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellsuccess.hpp" +#include "../mwgui/inventorywindow.hpp" +#include "../mwgui/bookwindow.hpp" +#include "../mwgui/scrollwindow.hpp" #include "windowmanagerimp.hpp" #include "itemselection.hpp" @@ -263,20 +268,82 @@ namespace MWGui QuickKeyType type = *button->getUserData(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + if (type == Type_Magic) { std::string spellId = button->getChildAt(0)->getUserString("Spell"); - MWBase::Environment::get().getWindowManager ()->setSelectedSpell (spellId, 100); + spells.setSelectedSpell(spellId); + store.setSelectedEnchantItem(store.end()); + mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } else if (type == Type_Item) { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - MWBase::Environment::get().getWindowManager ()->setSelectedWeapon(item, 100); + + // make sure the item is available + if (item.getRefData ().getCount() == 0) + { + MWBase::Environment::get().getWindowManager ()->messageBox ( + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + return; + } + + boost::shared_ptr action = MWWorld::Class::get(item).use(item); + + action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + // this is necessary for books/scrolls: if they are already in the player's inventory, + // the "Take" button should not be visible. + // NOTE: the take button is "reset" when the window opens, so we can safely do the following + // without screwing up future book windows + mWindowManager.getBookWindow()->setTakeButtonShow(false); + mWindowManager.getScrollWindow()->setTakeButtonShow(false); + + // since we changed equipping status, update the inventory window + mWindowManager.getInventoryWindow()->drawItems(); } else if (type == Type_MagicItem) { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - MWBase::Environment::get().getWindowManager ()->setSelectedEnchantItem (item, 100); + + // make sure the item is available + if (item.getRefData ().getCount() == 0) + { + MWBase::Environment::get().getWindowManager ()->messageBox ( + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + return; + } + + // retrieve ContainerStoreIterator to the item + MWWorld::ContainerStoreIterator it = store.begin(); + for (; it != store.end(); ++it) + { + if (*it == item) + { + break; + } + } + assert(it != store.end()); + + // equip, if it can be equipped + if (!MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) + { + // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping + + MWWorld::ActionEquip action(item); + action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); + + // since we changed equipping status, update the inventory window + mWindowManager.getInventoryWindow()->drawItems(); + } + + store.setSelectedEnchantItem(it); + spells.setSelectedSpell(""); + mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge % } } @@ -339,6 +406,12 @@ namespace MWGui getWidget(mMagicList, "MagicList"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); + int dx = (mCancelButton->getTextSize().width + 24) - mCancelButton->getWidth(); + mCancelButton->setCoord(mCancelButton->getLeft() - dx, + mCancelButton->getTop(), + mCancelButton->getTextSize ().width + 24, + mCancelButton->getHeight()); + center(); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 8754f5d105..bd671cab62 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -13,6 +13,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/actionequip.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -350,34 +351,10 @@ namespace MWGui if (_sender->getUserString("Equipped") == "false" && !MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) { - // sound - MWBase::Environment::get().getSoundManager()->playSound(MWWorld::Class::get(item).getUpSoundId(item), 1.0, 1.0); - // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping - /// \todo the following code is pretty much copy&paste from ActionEquip, put it in a function? - // slots that this item can be equipped in - std::pair, bool> slots = MWWorld::Class::get(item).getEquipmentSlots(item); - - // equip the item in the first free slot - for (std::vector::const_iterator slot=slots.first.begin(); - slot!=slots.first.end(); ++slot) - { - // if all slots are occupied, replace the last slot - if (slot == --slots.first.end()) - { - store.equip(*slot, it); - break; - } - - if (store.getSlot(*slot) == store.end()) - { - // slot is not occupied - store.equip(*slot, it); - break; - } - } - /// \todo scripts? + MWWorld::ActionEquip action(item); + action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); // since we changed equipping status, update the inventory window mWindowManager.getInventoryWindow()->drawItems(); diff --git a/apps/openmw/mwmechanics/spellsuccess.hpp b/apps/openmw/mwmechanics/spellsuccess.hpp index 1ab1bb11fd..e4778be693 100644 --- a/apps/openmw/mwmechanics/spellsuccess.hpp +++ b/apps/openmw/mwmechanics/spellsuccess.hpp @@ -8,6 +8,8 @@ #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" +#include + #include "npcstats.hpp" namespace MWMechanics diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout index bf942b32f4..31ad7b4c9f 100644 --- a/files/mygui/openmw_magicselection_dialog.layout +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -2,16 +2,16 @@ - + - - + + - + From bc36b0e2bda5e2aba63bd634a055321d3201e48c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:51:13 +0200 Subject: [PATCH 121/143] remove the restriction on the last quick key --- apps/openmw/mwgui/quickkeysmenu.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index fcfac8d5d9..8bc441c653 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -67,9 +67,6 @@ namespace MWGui MyGUI::Button* button; getWidget(button, "QuickKey" + boost::lexical_cast(i+1)); - if (i != 9) // 10th quick key is always set to hand-to-hand - button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); - unassign(button, i); mQuickKeyButtons.push_back(button); From 4f19b7fb9b0fb2baaae45cda5196e420e173610a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:52:32 +0200 Subject: [PATCH 122/143] fix leaks. --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 8bc441c653..3c05e63277 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -77,6 +77,8 @@ namespace MWGui QuickKeysMenu::~QuickKeysMenu() { delete mAssignDialog; + delete mItemSelectionDialog; + delete mMagicSelectionDialog; } void QuickKeysMenu::unassign(MyGUI::Widget* key, int index) From 5c28a6720307cfa2fdab09293d043e93d3099522 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:55:39 +0200 Subject: [PATCH 123/143] broke something in the last commit. --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 3c05e63277..5b7115dbea 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -67,6 +67,8 @@ namespace MWGui MyGUI::Button* button; getWidget(button, "QuickKey" + boost::lexical_cast(i+1)); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + unassign(button, i); mQuickKeyButtons.push_back(button); From 321f7c3419099d824c18a6b2b82b7e69bd60716e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 21:29:04 +0200 Subject: [PATCH 124/143] fix an infinite recursion regression on several dialogs. --- apps/openmw/mwgui/birth.cpp | 2 +- apps/openmw/mwgui/birth.hpp | 2 +- apps/openmw/mwgui/charactercreation.cpp | 18 +++++++++--------- apps/openmw/mwgui/class.cpp | 12 ------------ apps/openmw/mwgui/class.hpp | 7 ++----- apps/openmw/mwgui/journalwindow.hpp | 2 +- apps/openmw/mwgui/race.cpp | 1 - apps/openmw/mwgui/race.hpp | 2 +- apps/openmw/mwgui/review.cpp | 1 - apps/openmw/mwgui/review.hpp | 2 +- apps/openmw/mwgui/text_input.cpp | 1 - apps/openmw/mwgui/text_input.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 1 - 13 files changed, 17 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 05a337cbce..1a5c2d0f6e 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -65,9 +65,9 @@ void BirthDialog::setNextButtonShow(bool shown) void BirthDialog::open() { + WindowBase::open(); updateBirths(); updateSpells(); - setVisible(true); } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 92665081db..5bd36e8289 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -28,7 +28,7 @@ namespace MWGui void setBirthId(const std::string &raceId); void setNextButtonShow(bool shown); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 8c82b3e43d..7d63f69220 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -194,7 +194,7 @@ void CharacterCreation::spawnDialog(const char id) mNameDialog->setTextInput(mPlayerName); mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); - mNameDialog->open(); + mNameDialog->setVisible(true); break; case GM_Race: @@ -205,7 +205,7 @@ void CharacterCreation::spawnDialog(const char id) mRaceDialog->setRaceId(mPlayerRaceId); mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); - mRaceDialog->open(); + mRaceDialog->setVisible(true);; break; case GM_Class: @@ -213,7 +213,7 @@ void CharacterCreation::spawnDialog(const char id) mClassChoiceDialog = 0; mClassChoiceDialog = new ClassChoiceDialog(*mWM); mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); - mClassChoiceDialog->open(); + mClassChoiceDialog->setVisible(true); break; case GM_ClassPick: @@ -224,7 +224,7 @@ void CharacterCreation::spawnDialog(const char id) mPickClassDialog->setClassId(mPlayerClass.name); mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); - mPickClassDialog->open(); + mPickClassDialog->setVisible(true); break; case GM_Birth: @@ -234,7 +234,7 @@ void CharacterCreation::spawnDialog(const char id) mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); - mBirthSignDialog->open(); + mBirthSignDialog->setVisible(true); break; case GM_ClassCreate: @@ -244,7 +244,7 @@ void CharacterCreation::spawnDialog(const char id) mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); - mCreateClassDialog->open(); + mCreateClassDialog->setVisible(true); break; case GM_ClassGenerate: mGenerateClassStep = 0; @@ -289,7 +289,7 @@ void CharacterCreation::spawnDialog(const char id) mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); - mReviewDialog->open(); + mReviewDialog->setVisible(true); break; } } @@ -680,7 +680,7 @@ void CharacterCreation::showClassQuestionDialog() mGenerateClassResultDialog->setClassId(mGenerateClass); mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); - mGenerateClassResultDialog->open(); + mGenerateClassResultDialog->setVisible(true); return; } @@ -703,7 +703,7 @@ void CharacterCreation::showClassQuestionDialog() buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[2]); mGenerateClassQuestionDialog->setButtons(buttons); mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); - mGenerateClassQuestionDialog->open(); + mGenerateClassQuestionDialog->setVisible(true); MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps[mGenerateClassStep].mSound); } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 971740b5ed..9ea7b8052e 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -46,11 +46,6 @@ GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parW backButton->setCoord(315 - okButtonWidth - backButtonWidth - 6, 219, backButtonWidth, 23); } -void GenerateClassResultDialog::open() -{ - setVisible(true); -} - std::string GenerateClassResultDialog::getClassId() const { return mClassName->getCaption(); @@ -143,7 +138,6 @@ void PickClassDialog::open() { updateClasses(); updateStats(); - setVisible(true); } @@ -341,7 +335,6 @@ void InfoBoxDialog::open() layoutVertically(mMainWidget, 4 + 6); center(); - setVisible(true); } int InfoBoxDialog::getChosenButton() const @@ -549,11 +542,6 @@ void CreateClassDialog::setNextButtonShow(bool shown) descriptionButton->setCoord(459 - okButtonWidth - backButtonWidth - descriptionButtonWidth - 12, 158, descriptionButtonWidth, 23); } -void CreateClassDialog::open() -{ - setVisible(true); -} - // widget controls void CreateClassDialog::onDialogCancel() diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index bcb5c26277..43511cccaa 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -26,7 +26,7 @@ namespace MWGui std::string getText() const; void setButtons(ButtonList &buttons); - void open(); + virtual void open(); int getChosenButton() const; // Events @@ -74,8 +74,6 @@ namespace MWGui std::string getClassId() const; void setClassId(const std::string &classId); - void open(); - // Events typedef delegates::CMultiDelegate0 EventHandle_Void; @@ -104,7 +102,7 @@ namespace MWGui void setClassId(const std::string &classId); void setNextButtonShow(bool shown); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; @@ -264,7 +262,6 @@ namespace MWGui std::vector getMinorSkills() const; void setNextButtonShow(bool shown); - void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index fc05bbdbcd..a62e488030 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -14,7 +14,7 @@ namespace MWGui { public: JournalWindow(MWBase::WindowManager& parWindowManager); - void open(); + virtual void open(); virtual void setVisible(bool visible); // only used to play close sound diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index ceb0452fba..a597dcadb7 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -113,7 +113,6 @@ void RaceDialog::open() updateRaces(); updateSkills(); updateSpellPowers(); - setVisible(true); } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 3da6b0ace8..2e9e09a3e4 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -43,7 +43,7 @@ namespace MWGui // setHair() void setNextButtonShow(bool shown); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 8dd894c258..4a9cc778a6 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -110,7 +110,6 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) void ReviewDialog::open() { updateSkillArea(); - setVisible(true); } void ReviewDialog::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index f0bde6ecde..f6d3732323 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -46,7 +46,7 @@ namespace MWGui void configureSkills(const SkillList& major, const SkillList& minor); void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 802cd30637..b724bab15f 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -45,7 +45,6 @@ void TextInputDialog::open() { // Make sure the edit box has focus MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); - setVisible(true); } // widget controls diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index 7a33257224..8fb0e58194 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -25,7 +25,7 @@ namespace MWGui void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); - void open(); + virtual void open(); protected: void onOkClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 138349c2ec..60a574b3c6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -318,7 +318,6 @@ void WindowManager::updateVisible() break; case GM_Journal: mJournal->setVisible(true); - mJournal->open(); break; default: // Unsupported mode, switch back to game From 40d4dad15e42ad882caa68e5a97821d48854bbbe Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Aug 2012 09:32:38 +0200 Subject: [PATCH 125/143] Issue #378: Fix (record wasn't build fully) --- apps/openmw/mwworld/worldimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 655397c1f6..0ad733e703 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -745,8 +745,11 @@ namespace MWWorld std::ostringstream stream; stream << "$dynamic" << mNextDynamicRecord++; + ESM::Potion record2 (record); + record2.mId = stream.str(); + const ESM::Potion *created = - &mStore.potions.list.insert (std::make_pair (stream.str(), record)).first->second; + &mStore.potions.list.insert (std::make_pair (stream.str(), record2)).first->second; mStore.all.insert (std::make_pair (stream.str(), ESM::REC_ALCH)); From 34c30b132c58ca582c064efa128b120b248f6c3b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Aug 2012 10:12:22 +0200 Subject: [PATCH 126/143] Issue #378: workaround for bad ingredient records in Morrowind.esm --- components/esm/loadingr.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index a745ff6695..ddc9de0d53 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -12,6 +12,18 @@ void Ingredient::load(ESMReader &esm, const std::string& id) esm.getHNT(data, "IRDT", 56); script = esm.getHNOString("SCRI"); icon = esm.getHNOString("ITEX"); + + // horrible hack to fix broken data in records + for (int i=0; i<4; ++i) + { + if (data.effectID[i]!=85 && data.effectID[i]!=22 && data.effectID[i]!=17 && data.effectID[i]!=79 && + data.effectID[i]!=74) + data.attributes[i] = -1; + + if (data.effectID[i]!=89 && data.effectID[i]!=26 && data.effectID[i]!=21 && data.effectID[i]!=83 && + data.effectID[i]!=78) + data.skills[i] = -1; + } } } From 5834b4baa55416856a395a0d1f462b3a921de81d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 17:30:34 +0200 Subject: [PATCH 127/143] door markers on the local map --- apps/openmw/mwbase/world.hpp | 15 ++++ apps/openmw/mwgui/hud.cpp | 4 +- apps/openmw/mwgui/hud.hpp | 1 + apps/openmw/mwgui/map_window.cpp | 86 ++++++++++++++++++++++- apps/openmw/mwgui/map_window.hpp | 9 +++ apps/openmw/mwgui/tooltips.cpp | 18 ++++- apps/openmw/mwrender/localmap.cpp | 45 ++++++++---- apps/openmw/mwrender/localmap.hpp | 12 ++++ apps/openmw/mwrender/renderingmanager.cpp | 10 +++ apps/openmw/mwrender/renderingmanager.hpp | 6 ++ apps/openmw/mwworld/worldimp.cpp | 58 +++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 9 +++ files/mygui/openmw_hud.layout | 9 ++- 13 files changed, 260 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9c801cbff5..19405fb7a1 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -65,6 +65,12 @@ namespace MWBase Render_Compositors }; + struct DoorMarker + { + std::string name; + float x, y; // world position + }; + World() {} virtual ~World() {} @@ -108,6 +114,15 @@ namespace MWBase virtual Ogre::Vector2 getNorthVector (MWWorld::CellStore* cell) = 0; ///< get north vector (OGRE coordinates) for given interior cell + virtual std::vector getDoorMarkers (MWWorld::CellStore* cell) = 0; + ///< get a list of teleport door markers for a given cell, to be displayed on the local map + + virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) = 0; + ///< see MWRender::LocalMap::getInteriorMapPosition + + virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0; + ///< see MWRender::LocalMap::isPositionExplored + virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0; virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 20b9765d0e..b148335537 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -60,7 +60,6 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) getWidget(mHealth, "Health"); getWidget(mMagicka, "Magicka"); getWidget(mStamina, "Stamina"); - mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; @@ -93,9 +92,10 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) getWidget(mMinimapBox, "MiniMapBox"); mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); - mMinimapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); getWidget(mMinimap, "MiniMap"); getWidget(mCompass, "Compass"); + getWidget(mMinimapButton, "MiniMapButton"); + mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); getWidget(mCellNameBox, "CellName"); getWidget(mWeaponSpellBox, "WeaponSpellName"); diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 49ed6a698a..de4228e87c 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -50,6 +50,7 @@ namespace MWGui MyGUI::ImageBox *mWeapImage, *mSpellImage; MyGUI::ProgressPtr mWeapStatus, mSpellStatus; MyGUI::Widget *mEffectBox, *mMinimapBox; + MyGUI::Button* mMinimapButton; MyGUI::ImageBox* mEffect1; MyGUI::ScrollView* mMinimap; MyGUI::ImageBox* mCompass; diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 1ffedaac48..fb9d781476 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -1,9 +1,13 @@ #include "map_window.hpp" -#include "../mwbase/windowmanager.hpp" - #include +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + using namespace MWGui; LocalMapBase::LocalMapBase() @@ -92,10 +96,21 @@ void LocalMapBase::applyFogOfWar() void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell + + // clear all previous markers + for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) + { + if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + { + MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); + } + } + for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) { + // map std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" + boost::lexical_cast(y + (interior ? (my-1) : -1*(my-1))); @@ -108,12 +123,79 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) box->setImageTexture(image); else box->setImageTexture("black.png"); + + + // door markers + + // interior map only consists of one cell, so handle the markers only once + if (interior && (mx != 2 || my != 2)) + continue; + + MWWorld::CellStore* cell; + if (interior) + cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); + else + cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); + + std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); + + for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + { + MWBase::World::DoorMarker marker = *it; + + // convert world coordinates to normalized cell coordinates + MyGUI::IntCoord widgetCoord; + float nX,nY; + int cellDx, cellDy; + if (!interior) + { + const int cellSize = 8192; + + nX = (marker.x - cellSize * (x+mx-1)) / cellSize; + nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; + + widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + mx * 512, nY * 512 - 3 + my * 512, 7, 7); + } + else + { + Ogre::Vector2 position (marker.x, -marker.y); + MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); + + widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + (1+cellDx-x) * 512, nY * 512 - 3 + (1+cellDy-y) * 512, 7, 7); + } + + std::cout << "widgetCoord " << widgetCoord.left << " " << widgetCoord.top << " nX " << nX << " nY " << nY << " xy " << x << " " << y << std::endl; + + static int counter = 0; + ++counter; + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); + markerWidget->setImageTexture ("textures\\door_icon.dds"); + markerWidget->setImageCoord (MyGUI::IntCoord(0,0,7,7)); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTip"); + markerWidget->setUserString("Caption_Text", marker.name); + markerWidget->setUserString("IsMarker", "true"); + + MarkerPosition markerPos; + markerPos.interior = interior; + markerPos.cellX = interior ? cellDx : x + mx - 1; + markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); + markerPos.nX = nX; + markerPos.nY = nY; + + markerWidget->setUserData(markerPos); + } + + } } mInterior = interior; mCurX = x; mCurY = y; mChanged = false; + + // fog of war applyFogOfWar(); // set the compass texture again, because MyGUI determines sorting of ImageBox widgets diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 447c16901a..c69d8986c7 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -18,6 +18,15 @@ namespace MWGui void toggleFogOfWar(); + struct MarkerPosition + { + bool interior; + int cellX; + int cellY; + float nX; + float nY; + }; + protected: int mCurX, mCurY; bool mInterior; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index edc787cef7..16ee279230 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -12,6 +12,7 @@ #include "../mwworld/class.hpp" +#include "map_window.hpp" #include "widgets.hpp" using namespace MWGui; @@ -150,7 +151,19 @@ void ToolTips::onFrame(float frameDuration) { return; } - else if (type == "ItemPtr") + + // special handling for markers on the local map: the tooltip should only be visible + // if the marker is not hidden due to the fog of war. + if (focus->getUserString ("IsMarker") == "true") + { + LocalMapBase::MarkerPosition pos = *focus->getUserData(); + + if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) + return; + } + + + if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); tooltipSize = getToolTipViaPtr(false); @@ -199,7 +212,8 @@ void ToolTips::onFrame(float frameDuration) it != userStrings.end(); ++it) { if (it->first == "ToolTipType" - || it->first == "ToolTipLayout") + || it->first == "ToolTipLayout" + || it->first == "IsMarker") continue; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0e85d32e07..e254a2f0a9 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -270,6 +270,34 @@ void LocalMap::render(const float x, const float y, mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, fStart, fEnd); } +void LocalMap::getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y) +{ + pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle); + + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + + x = std::ceil((pos.x - min.x)/sSize)-1; + y = std::ceil((pos.y - min.y)/sSize)-1; + + nX = (pos.x - min.x - sSize*x)/sSize; + nY = (pos.y - min.y - sSize*y)/sSize; +} + +bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interior) +{ + std::string texName = (interior ? mInteriorName + "_" : "Cell_") + coordStr(x, y); + + if (mBuffers.find(texName) == mBuffers.end()) + return false; + + int texU = (sFogOfWarResolution-1) * nX; + int texV = (sFogOfWarResolution-1) * nY; + + Ogre::uint32 clr = mBuffers[texName][texV * sFogOfWarResolution + texU]; + uint8 alpha = (clr >> 24); + return alpha < 200; +} + void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation) { if (sFogOfWarSkip != 0) @@ -281,14 +309,12 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni // retrieve the x,y grid coordinates the player is in int x,y; - Vector3 _pos(position.x, 0, position.z); - Vector2 pos(_pos.x, _pos.z); + float u,v; + + Vector2 pos(position.x, position.z); if (mInterior) - { - pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle); - } - + getInteriorMapPosition(pos, u,v, x,y); Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); @@ -303,14 +329,10 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } else { - x = std::ceil((pos.x - min.x)/sSize)-1; - y = std::ceil((pos.y - min.y)/sSize)-1; - MWBase::Environment::get().getWindowManager()->setInteriorMapTexture(x,y); } // convert from world coordinates to texture UV coordinates - float u,v; std::string texBaseName; if (!mInterior) { @@ -320,9 +342,6 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } else { - u = (pos.x - min.x - sSize*x)/sSize; - v = (pos.y - min.y - sSize*y)/sSize; - texBaseName = mInteriorName + "_"; } diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index c5cd908fc3..056d2498a3 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -58,6 +58,18 @@ namespace MWRender */ void saveFogOfWar(MWWorld::CellStore* cell); + + /** + * Get the interior map texture index and normalized position + * on this texture, given a world position (in ogre coordinates) + */ + void getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y); + + /** + * Check if a given position is explored by the player (i.e. not obscured by fog of war) + */ + bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + private: OEngine::Render::OgreRenderer* mRendering; MWRender::RenderingManager* mRenderingManager; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2d29e8f556..edeb0fe127 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -863,4 +863,14 @@ void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float mPlayer->getSightAngles(pitch, yaw); } +void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) +{ + return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y); +} + +bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, bool interior) +{ + return mLocalMap->isPositionExplored(nX, nY, x, y, interior); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b6bfcbf974..de24491504 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -184,6 +184,12 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList static bool waterShaderSupported(); + virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + ///< see MWRender::LocalMap::getInteriorMapPosition + + virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + ///< see MWRender::LocalMap::isPositionExplored + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0ad733e703..8e9d981c12 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1031,6 +1031,64 @@ namespace MWWorld return d; } + std::vector World::getDoorMarkers (CellStore* cell) + { + std::vector result; + + MWWorld::CellRefList doors = cell->doors; + std::list< MWWorld::LiveCellRef > refList = doors.list; + for (std::list< MWWorld::LiveCellRef >::iterator it = refList.begin(); it != refList.end(); ++it) + { + MWWorld::LiveCellRef ref = *it; + + if (ref.ref.teleport) + { + World::DoorMarker newMarker; + + std::string dest; + if (ref.ref.destCell != "") + { + // door leads to an interior, use interior name + dest = ref.ref.destCell; + } + else + { + // door leads to exterior, use cell name (if any), otherwise translated region name + int x,y; + positionToIndex (ref.ref.doorDest.pos[0], ref.ref.doorDest.pos[1], x, y); + const ESM::Cell* cell = mStore.cells.findExt(x,y); + if (cell->name != "") + dest = cell->name; + else + { + const ESM::Region* region = mStore.regions.search(cell->region); + dest = region->name; + } + } + + newMarker.name = dest; + + ESM::Position pos = ref.mData.getPosition (); + + newMarker.x = pos.pos[0]; + newMarker.y = pos.pos[1]; + result.push_back(newMarker); + } + } + + return result; + } + + void World::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) + { + mRendering->getInteriorMapPosition(position, nX, nY, x, y); + } + + bool World::isPositionExplored (float nX, float nY, int x, int y, bool interior) + { + return mRendering->isPositionExplored(nX, nY, x, y, interior); + } + void World::setWaterHeight(const float height) { mRendering->setWaterHeight(height); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 51216e3a5a..1d932bce2c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -140,6 +140,15 @@ namespace MWWorld virtual Ogre::Vector2 getNorthVector (CellStore* cell); ///< get north vector (OGRE coordinates) for given interior cell + virtual std::vector getDoorMarkers (MWWorld::CellStore* cell); + ///< get a list of teleport door markers for a given cell, to be displayed on the local map + + virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + ///< see MWRender::LocalMap::getInteriorMapPosition + + virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + ///< see MWRender::LocalMap::isPositionExplored + virtual Globals::Data& getGlobalVariable (const std::string& name); virtual Globals::Data getGlobalVariable (const std::string& name) const; diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index cf353a205e..e4c6f0765e 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -76,18 +76,21 @@ - + - - + + + + + From 571d5095d45bcd929050195115d38cbc772b85b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 17:32:50 +0200 Subject: [PATCH 128/143] removed a cout --- apps/openmw/mwgui/map_window.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index fb9d781476..44baeddcb9 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -164,8 +164,6 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + (1+cellDx-x) * 512, nY * 512 - 3 + (1+cellDy-y) * 512, 7, 7); } - std::cout << "widgetCoord " << widgetCoord.left << " " << widgetCoord.top << " nX " << nX << " nY " << nY << " xy " << x << " " << y << std::endl; - static int counter = 0; ++counter; MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", From 3c39c47e2fb5a003bb3bc940ecd8e0d830b11cf6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:23:01 +0200 Subject: [PATCH 129/143] don't make copies of CellRefList & LiveCellRef --- apps/openmw/mwworld/worldimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8e9d981c12..a099f71cc8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1035,11 +1035,11 @@ namespace MWWorld { std::vector result; - MWWorld::CellRefList doors = cell->doors; - std::list< MWWorld::LiveCellRef > refList = doors.list; + MWWorld::CellRefList& doors = cell->doors; + std::list< MWWorld::LiveCellRef >& refList = doors.list; for (std::list< MWWorld::LiveCellRef >::iterator it = refList.begin(); it != refList.end(); ++it) { - MWWorld::LiveCellRef ref = *it; + MWWorld::LiveCellRef& ref = *it; if (ref.ref.teleport) { From d9276ca09dd1efd213fb3180cac60a2ab452c533 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:23:59 +0200 Subject: [PATCH 130/143] don't make copy of CellRefList in World::getNorthVector --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a099f71cc8..968aa8ceea 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1021,7 +1021,7 @@ namespace MWWorld Ogre::Vector2 World::getNorthVector (CellStore* cell) { - MWWorld::CellRefList statics = cell->statics; + MWWorld::CellRefList& statics = cell->statics; MWWorld::LiveCellRef* ref = statics.find("northmarker"); if (!ref) return Vector2(0, 1); From 7eaf5e7f0f3ebb4f4fed3f383eec2781b55738ff Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:40:11 +0200 Subject: [PATCH 131/143] changed the markers to look more like morrowind's (hover effect) --- apps/openmw/mwgui/map_window.cpp | 9 ++++----- files/mygui/CMakeLists.txt | 1 + files/mygui/markers.png | Bin 0 -> 245 bytes files/mygui/openmw_resources.xml | 18 ++++++++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 files/mygui/markers.png diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 44baeddcb9..c5c3dff920 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -154,22 +154,21 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) nX = (marker.x - cellSize * (x+mx-1)) / cellSize; nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; - widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + mx * 512, nY * 512 - 3 + my * 512, 7, 7); + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); } else { Ogre::Vector2 position (marker.x, -marker.y); MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); - widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + (1+cellDx-x) * 512, nY * 512 - 3 + (1+cellDy-y) * 512, 7, 7); + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); } static int counter = 0; ++counter; - MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); - markerWidget->setImageTexture ("textures\\door_icon.dds"); - markerWidget->setImageCoord (MyGUI::IntCoord(0,0,7,7)); + markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTip"); markerWidget->setUserString("Caption_Text", marker.name); diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 2e448a3691..70f7282e52 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -71,6 +71,7 @@ set(MYGUI_FILES openmw_magicselection_dialog.layout smallbars.png VeraMono.ttf + markers.png ) diff --git a/files/mygui/markers.png b/files/mygui/markers.png new file mode 100644 index 0000000000000000000000000000000000000000..76b6b9913b01cd9f805d1a9d7ff7693cf49c99fe GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~c!3HEhl+{lMQfx`y?k)`fL2$v|<&%LToCO|{ z#S9GG!XV7ZFl&wkP>{XE)7O>#4u_0@5~t(+j%PrjCQlc~5RLQ6JiQ!qPd2bbNJdQD z$+KUw&iKLP^u&aOgov#*p;F9Z0BR06 zaSc8`saxaIQNyTA9w6{J(G|8nV(z}2qnk?DWEf`K2s5`fu+9{g60{PwoxpMOFz*4; i2?h + + + + + + + + + + + + + + + + + + From 1c5055c8ac41c5a7ea3c08b0248842a754f80c41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:49:44 +0200 Subject: [PATCH 132/143] fix a problem where hovering a marker would render it on top of the fog of war instead of below --- apps/openmw/mwgui/map_window.cpp | 12 ++++++++++++ apps/openmw/mwgui/map_window.hpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index c5c3dff920..0d6300baad 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -93,6 +93,16 @@ void LocalMapBase::applyFogOfWar() } } +void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) +{ + applyFogOfWar (); +} + +void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) +{ + applyFogOfWar (); +} + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell @@ -173,6 +183,8 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) markerWidget->setUserString("ToolTipLayout", "TextToolTip"); markerWidget->setUserString("Caption_Text", marker.name); markerWidget->setUserString("IsMarker", "true"); + markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); + markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); MarkerPosition markerPos; markerPos.interior = interior; diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index c69d8986c7..1203233bf9 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -41,6 +41,9 @@ namespace MWGui void applyFogOfWar(); + void onMarkerFocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + OEngine::GUI::Layout* mLayout; bool mMapDragAndDrop; From e9b2f4ee74bdd346cfd4434edb6352aae30db16b Mon Sep 17 00:00:00 2001 From: Artem Kotsynyak Date: Tue, 28 Aug 2012 22:59:44 +0400 Subject: [PATCH 133/143] fix equipment visibility update --- apps/openmw/mwrender/player.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index b9ab0da792..3ccc080221 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -123,6 +123,11 @@ namespace MWRender if (mAnimation) { mAnimation->runAnimation(duration); } + mPlayerNode->setVisible( + mVanity.enabled || mPreviewMode || !mFirstPersonView, + false + ); + if (mFirstPersonView && !mVanity.enabled) { return; } @@ -143,7 +148,6 @@ namespace MWRender mCamera->setPosition(0.f, 0.f, mCameraDistance); setLowHeight(true); } - mPlayerNode->setVisible(!mFirstPersonView, false); } void Player::allowVanityMode(bool allow) @@ -172,13 +176,11 @@ namespace MWRender rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - mPlayerNode->setVisible(true, false); setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; - mPlayerNode->setVisible(!mFirstPersonView, false); setLowHeight(!mFirstPersonView); } rot.z = getYaw(); @@ -199,13 +201,11 @@ namespace MWRender mMainCam.offset = offset; offset = mPreviewCam.offset; - mPlayerNode->setVisible(true, false); setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; - mPlayerNode->setVisible(!mFirstPersonView, false); setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); @@ -302,7 +302,6 @@ namespace MWRender void Player::setAnimation(NpcAnimation *anim) { mAnimation = anim; - mPlayerNode->setVisible(!mFirstPersonView, false); } void Player::setHeight(float height) From d7572d815e64737404c3c38da1fa1f3b05127a01 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Tue, 28 Aug 2012 15:32:12 -0400 Subject: [PATCH 134/143] Do not define OIS_DYNAMIC_LIB in ICSPrerequisites.h Fixes http://bugs.openmw.org/issues/381 --- extern/oics/ICSPrerequisites.h | 1 - 1 file changed, 1 deletion(-) diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h index 864dad15fd..3b5d1935b4 100644 --- a/extern/oics/ICSPrerequisites.h +++ b/extern/oics/ICSPrerequisites.h @@ -39,7 +39,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "tinyxml.h" -#define OIS_DYNAMIC_LIB #include #include #include From 5370fecb6f25e942ec3fbaa3ce9e658c4f60561a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Aug 2012 10:51:36 +0200 Subject: [PATCH 135/143] fix a small input focus issue with the map --- files/mygui/openmw_map_window_skin.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/mygui/openmw_map_window_skin.xml b/files/mygui/openmw_map_window_skin.xml index fbb7af7aef..0c6050969d 100644 --- a/files/mygui/openmw_map_window_skin.xml +++ b/files/mygui/openmw_map_window_skin.xml @@ -5,7 +5,7 @@ - - + + From 6ad08dfd338eb7fba55e76a22ce51c0d54f20be8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Aug 2012 11:15:17 +0200 Subject: [PATCH 136/143] added settings for crosshair & subtitles --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/settingswindow.cpp | 11 +++++++++++ apps/openmw/mwgui/settingswindow.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 13 ++++++++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++++ apps/openmw/mwscript/soundextensions.cpp | 5 ++++- files/mygui/openmw_settings_window.layout | 12 ++++++++++++ files/settings-default.cfg | 6 +++++- 8 files changed, 51 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 0e1ad9c6ea..388dc4af93 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -180,6 +180,7 @@ namespace MWBase virtual void unsetSelectedWeapon() = 0; virtual void showCrosshair(bool show) = 0; + virtual bool getSubtitlesEnabled() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 4039136a17..2599ea0b26 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -85,6 +85,8 @@ namespace MWGui WindowBase("openmw_settings_window.layout", parWindowManager) { getWidget(mOkButton, "OkButton"); + getWidget(mSubtitlesButton, "SubtitlesButton"); + getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mResolutionList, "ResolutionList"); getWidget(mMenuTransparencySlider, "MenuTransparencySlider"); getWidget(mToolTipDelaySlider, "ToolTipDelaySlider"); @@ -121,6 +123,8 @@ namespace MWGui getWidget(mUISensitivitySlider, "UISensitivitySlider"); getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); + mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -192,6 +196,9 @@ namespace MWGui int tooltip_delay = (mToolTipDelaySlider->getScrollRange()-1) * Settings::Manager::getFloat("tooltip delay", "GUI"); mToolTipDelaySlider->setScrollPosition(tooltip_delay); + mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); + mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}"); + float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin); mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); MyGUI::TextBox* fovText; @@ -403,6 +410,10 @@ namespace MWGui Settings::Manager::setBool("debug", "Shadows", newState); else if (_sender == mInvertYButton) Settings::Manager::setBool("invert y axis", "Input", newState); + else if (_sender == mCrosshairButton) + Settings::Manager::setBool("crosshair", "HUD", newState); + else if (_sender == mSubtitlesButton) + Settings::Manager::setBool("subtitles", "GUI", newState); apply(); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 159d52bdcc..d1f35ed710 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -30,6 +30,8 @@ namespace MWGui MyGUI::ScrollBar* mMenuTransparencySlider; MyGUI::ScrollBar* mToolTipDelaySlider; + MyGUI::Button* mSubtitlesButton; + MyGUI::Button* mCrosshairButton; // graphics MyGUI::ListBox* mResolutionList; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 091d31bfe3..a3d20ac9e5 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -84,6 +84,8 @@ WindowManager::WindowManager( , mFPS(0.0f) , mTriangleCount(0) , mBatchCount(0) + , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) + , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) { // Set up the GUI system @@ -644,6 +646,10 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector { changeRes = true; } + else if (it->first == "HUD" && it->second == "crosshair") + mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); + else if (it->first == "GUI" && it->second == "subtitles") + mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); } if (changeRes) @@ -857,10 +863,15 @@ void WindowManager::notifyInputActionBound () void WindowManager::showCrosshair (bool show) { - mHud->setCrosshairVisible (show); + mHud->setCrosshairVisible (show && mCrosshairEnabled); } void WindowManager::activateQuickKey (int index) { mQuickKeysMenu->activateQuickKey(index); } + +bool WindowManager::getSubtitlesEnabled () +{ + return mSubtitlesEnabled; +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 91e9d44b56..dd8a3b0067 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -161,6 +161,7 @@ namespace MWGui virtual void unsetSelectedWeapon(); virtual void showCrosshair(bool show); + virtual bool getSubtitlesEnabled(); virtual void disallowMouse(); virtual void allowMouse(); @@ -220,6 +221,9 @@ namespace MWGui MyGUI::Widget* mInputBlocker; + bool mCrosshairEnabled; + bool mSubtitlesEnabled; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 6dc8f39197..35e66241eb 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -37,7 +38,9 @@ namespace MWScript runtime.pop(); MWBase::Environment::get().getSoundManager()->say (ptr, file); - context.messageBox (text); + + if (MWBase::Environment::get().getWindowManager ()->getSubtitlesEnabled()) + context.messageBox (text); } }; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ad906d2a13..c8eae518f7 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -41,6 +41,18 @@ + + + + + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index effd9f3da5..8fab98da24 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -29,7 +29,9 @@ vsync = false menu transparency = 0.84 # 0 - instantly, 1 - max. delay -tooltip delay = 0.2 +tooltip delay = 0 + +subtitles = false [General] # Camera field of view @@ -85,6 +87,8 @@ debug = false # 2: advanced FPS display (batches, triangles) fps = 0 +crosshair = true + [Objects] shaders = true From 02d7aa41355a9cfc68061d583f9aa592fcd935f9 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 29 Aug 2012 14:01:10 +0400 Subject: [PATCH 137/143] npc ai packages --- components/esm/defs_ai.hpp | 128 ++++++++++++++++++++++++++++++++++ components/esm/esm_reader.hpp | 12 ++-- components/esm/loadcrea.hpp | 10 +-- components/esm/loadnpc.cpp | 10 +++ components/esm/loadnpc.hpp | 20 +++--- 5 files changed, 155 insertions(+), 25 deletions(-) create mode 100644 components/esm/defs_ai.hpp diff --git a/components/esm/defs_ai.hpp b/components/esm/defs_ai.hpp new file mode 100644 index 0000000000..a7f2a26ea4 --- /dev/null +++ b/components/esm/defs_ai.hpp @@ -0,0 +1,128 @@ +#ifndef _ESM_DEFS_AI_H +#define _ESM_DEFS_AI_H + +#include "esm_reader.hpp" + +#include + +namespace ESM +{ + #pragma pack(push) + #pragma pack(1) + + struct AIDTstruct + { + // These are probabilities + char hello, u1, fight, flee, alarm, u2, u3, u4; + // The last u's might be the skills that this NPC can train you + // in? + int services; // See the Services enum + }; // 12 bytes + + struct AIWander + { + short distance; + short duration; + char timeOfDay; + char idle[8]; + char unk; + }; + + struct AITravel + { + float x, y, z; + long unk; + }; + + struct AITarget + { + float x, y, z; + short duration; + NAME32 id; + short unk; + }; + + struct AIActivate + { + NAME32 name; + char unk; + }; + + #pragma pack(pop) + + enum + { + AI_Wander = 0x575f4941, + AI_Travel = 0x545f4941, + AI_Follow = 0x465f4941, + AI_Escort = 0x455f4941, + AI_Activate = 0x415f4941, + }; + + /// \note Used for storaging packages in a single container + /// w/o manual memory allocation accordingly to policy standards + struct AIPackage + { + int type; + + // Anonymous union + union + { + AIWander wander; + AITravel travel; + AITarget target; + AIActivate activate; + }; + + /// \note for AITarget only, placed here to stick with union, + /// overhead should be not so awful + std::string cellName; + }; + + struct AIPackageList + { + std::vector list; + + /// \note This breaks consistency of subrecords reading: + /// after calling it subrecord name is already read, so + /// it needs to use retSubName() if needed. But, hey, there + /// is only one field left (XSCL) and only two records uses AI + void load(ESMReader &esm) { + while (esm.hasMoreSubs()) { + // initialize every iteration + AIPackage pack; + + esm.getSubName(); + std::cout << esm.retSubName().toString() << std::endl; + if (esm.retSubName() == 0x54444e43) { // CNDT + list.back().cellName = esm.getHString(); + } else if (esm.retSubName() == AI_Wander) { + pack.type = AI_Wander; + esm.getHExact(&pack.wander, 14); + list.push_back(pack); + } else if (esm.retSubName() == AI_Travel) { + pack.type = AI_Travel; + esm.getHExact(&pack.travel, 16); + list.push_back(pack); + } else if (esm.retSubName() == AI_Escort || + esm.retSubName() == AI_Follow) + { + pack.type = + (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow; + + esm.getHExact(&pack.target, 48); + list.push_back(pack); + } else if (esm.retSubName() == AI_Activate) { + pack.type = AI_Activate; + esm.getHExact(&pack.activate, 33); + list.push_back(pack); + } else { // not AI package related data, so leave + return; + } + } + } + }; +} + +#endif + diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 13f1f4a019..66c0710a68 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -49,23 +49,23 @@ union NAME_T char name[LEN]; int32_t val; - bool operator==(const char *str) + bool operator==(const char *str) const { for(int i=0; i dest; + AIPackageList aiPack; + std::string name, model, race, cls, faction, script, hair, head; // body parts From 75fa0288a34b8ae5e08b44d8859c5b5404d0c9aa Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 29 Aug 2012 14:34:27 +0400 Subject: [PATCH 138/143] fix creatures has spells, creatures ai data --- components/esm/defs_ai.hpp | 1 - components/esm/loadcrea.cpp | 15 ++------------- components/esm/loadcrea.hpp | 3 +++ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/components/esm/defs_ai.hpp b/components/esm/defs_ai.hpp index a7f2a26ea4..b14cc7d650 100644 --- a/components/esm/defs_ai.hpp +++ b/components/esm/defs_ai.hpp @@ -93,7 +93,6 @@ namespace ESM AIPackage pack; esm.getSubName(); - std::cout << esm.retSubName().toString() << std::endl; if (esm.retSubName() == 0x54444e43) { // CNDT list.back().cellName = esm.getHString(); } else if (esm.retSubName() == AI_Wander) { diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index 8af3526a0f..fcef9ccd46 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -18,6 +18,7 @@ void Creature::load(ESMReader &esm, const std::string& id) esm.getHNOT(scale, "XSCL"); inventory.load(esm); + spells.load(esm); if (esm.isNextSub("AIDT")) { @@ -27,20 +28,8 @@ void Creature::load(ESMReader &esm, const std::string& id) else hasAI = false; - // More subrecords: - // AI_W - wander (14 bytes, i don't understand it) - // short distance - // byte duration - // byte timeOfDay - // byte idle[10] - // - // Rest is optional: - // AI_T - travel? - // AI_F - follow? - // AI_E - escort? - // AI_A - activate? + aiPack.load(esm); esm.skipRecord(); - } } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 364b10eebb..9db1f3fff2 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -3,6 +3,7 @@ #include "esm_reader.hpp" #include "loadcont.hpp" +#include "defs.hpp" #include "defs_ai.hpp" namespace ESM @@ -61,9 +62,11 @@ struct Creature // Defined in loadcont.hpp InventoryList inventory; + SpellList spells; bool hasAI; AIDTstruct AI; + AIPackageList aiPack; std::string mId; From dbe3cca828587eff84585ef4e44acb163f55b7b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Aug 2012 13:11:06 +0200 Subject: [PATCH 139/143] added a separate core.xml file that works with the mygui layout editor --- files/mygui/core_layouteditor.xml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 files/mygui/core_layouteditor.xml diff --git a/files/mygui/core_layouteditor.xml b/files/mygui/core_layouteditor.xml new file mode 100644 index 0000000000..740f129cc6 --- /dev/null +++ b/files/mygui/core_layouteditor.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + From 81463fa23db4643a35bfb8811b1bb06426923044 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 29 Aug 2012 18:48:20 +0200 Subject: [PATCH 140/143] Issue #372: fixed positions in SoundManager --- apps/openmw/mwsound/soundmanagerimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index e1720453ba..4b10624a5c 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -215,7 +215,7 @@ namespace MWSound // The range values are not tested float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - const ESM::Position &pos = ptr.getCellRef().pos; + const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, @@ -313,7 +313,7 @@ namespace MWSound float basevol = mMasterVolume * mSFXVolume; float min, max; std::string file = lookup(soundId, basevol, min, max); - const ESM::Position &pos = ptr.getCellRef().pos; + const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode); @@ -406,7 +406,7 @@ namespace MWSound void SoundManager::updateObject(MWWorld::Ptr ptr) { - const ESM::Position &pos = ptr.getCellRef().pos; + const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) From a90547bbbef6ff431ac5e5047f25aefc018531b9 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 29 Aug 2012 21:35:06 +0400 Subject: [PATCH 141/143] code formatting --- apps/openmw/mwclass/creature.cpp | 8 +- apps/openmw/mwclass/npc.cpp | 8 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 8 +- apps/openmw/mwgui/tradewindow.cpp | 8 +- components/CMakeLists.txt | 2 +- components/esm/aipackage.cpp | 37 +++++ components/esm/aipackage.hpp | 95 +++++++++++++ components/esm/defs_ai.hpp | 127 ------------------ components/esm/loadcrea.cpp | 10 +- components/esm/loadcrea.hpp | 10 +- components/esm/loadnpc.cpp | 14 +- components/esm/loadnpc.hpp | 34 ++--- 12 files changed, 184 insertions(+), 177 deletions(-) create mode 100644 components/esm/aipackage.cpp create mode 100644 components/esm/aipackage.hpp delete mode 100644 components/esm/defs_ai.hpp diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a22c2d661d..7c60216528 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -61,10 +61,10 @@ namespace MWClass data->mCreatureStats.setLevel(ref->base->data.level); - data->mCreatureStats.setHello(ref->base->AI.hello); - data->mCreatureStats.setFight(ref->base->AI.fight); - data->mCreatureStats.setFlee(ref->base->AI.flee); - data->mCreatureStats.setAlarm(ref->base->AI.alarm); + data->mCreatureStats.setHello(ref->base->mAiData.mHello); + data->mCreatureStats.setFight(ref->base->mAiData.mFight); + data->mCreatureStats.setFlee(ref->base->mAiData.mFlee); + data->mCreatureStats.setAlarm(ref->base->mAiData.mAlarm); // store ptr.getRefData().setCustomData (data.release()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 94c98ba425..909c681a57 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -100,10 +100,10 @@ namespace MWClass /// \todo do something with npdt12 maybe:p } - data->mCreatureStats.setHello(ref->base->AI.hello); - data->mCreatureStats.setFight(ref->base->AI.fight); - data->mCreatureStats.setFlee(ref->base->AI.flee); - data->mCreatureStats.setAlarm(ref->base->AI.alarm); + data->mCreatureStats.setHello(ref->base->mAiData.mHello); + data->mCreatureStats.setFight(ref->base->mAiData.mFight); + data->mCreatureStats.setFlee(ref->base->mAiData.mFlee); + data->mCreatureStats.setAlarm(ref->base->mAiData.mAlarm); // store ptr.getRefData().setCustomData (data.release()); diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index acd7868921..841e36bf46 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -769,14 +769,14 @@ namespace MWDialogue if (mActor.getTypeName() == typeid(ESM::NPC).name()) { MWWorld::LiveCellRef* ref = mActor.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } else if (mActor.getTypeName() == typeid(ESM::Creature).name()) { MWWorld::LiveCellRef* ref = mActor.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } if (services & ESM::NPC::Weapon diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 8471367c68..9abd97bb7e 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -291,14 +291,14 @@ namespace MWGui if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) { MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } /// \todo what about potions, there doesn't seem to be a flag for them?? diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index c0585d5ee5..1bc5e65f8b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -43,7 +43,7 @@ add_component_dir (esm loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst loadinfo loadingr loadland loadlevlist loadligh loadlocks loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat - loadweap records + loadweap records aipackage ) add_component_dir (misc diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp new file mode 100644 index 0000000000..a2fd8beb19 --- /dev/null +++ b/components/esm/aipackage.cpp @@ -0,0 +1,37 @@ +#include "aipackage.hpp" + +namespace ESM +{ + void AIPackageList::load(ESMReader &esm) + { + while (esm.hasMoreSubs()) { + // initialize every iteration + AIPackage pack; + esm.getSubName(); + if (esm.retSubName() == 0x54444e43) { // CNDT + mList.back().mCellName = esm.getHString(); + } else if (esm.retSubName() == AI_Wander) { + pack.mType = AI_Wander; + esm.getHExact(&pack.mWander, 14); + mList.push_back(pack); + } else if (esm.retSubName() == AI_Travel) { + pack.mType = AI_Travel; + esm.getHExact(&pack.mTravel, 16); + mList.push_back(pack); + } else if (esm.retSubName() == AI_Escort || + esm.retSubName() == AI_Follow) + { + pack.mType = + (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow; + esm.getHExact(&pack.mTarget, 48); + mList.push_back(pack); + } else if (esm.retSubName() == AI_Activate) { + pack.mType = AI_Activate; + esm.getHExact(&pack.mActivate, 33); + mList.push_back(pack); + } else { // not AI package related data, so leave + return; + } + } + } +} diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp new file mode 100644 index 0000000000..3a8547402a --- /dev/null +++ b/components/esm/aipackage.hpp @@ -0,0 +1,95 @@ +#ifndef OPENMW_ESM_AIPACKAGE_H +#define OPENMW_ESM_AIPACKAGE_H + +#include "esm_reader.hpp" + +#include + +namespace ESM +{ + #pragma pack(push) + #pragma pack(1) + + struct AIData + { + // These are probabilities + char mHello, mU1, mFight, mFlee, mAlarm, mU2, mU3, mU4; + // The last u's might be the skills that this NPC can train you + // in? + int mServices; // See the Services enum + }; // 12 bytes + + struct AIWander + { + short mDistance; + short mDuration; + unsigned char mTimeOfDay; + unsigned char mIdle[8]; + unsigned char mUnk; + }; + + struct AITravel + { + float mX, mY, mZ; + long mUnk; + }; + + struct AITarget + { + float mX, mY, mZ; + short mDuration; + NAME32 mId; + short mUnk; + }; + + struct AIActivate + { + NAME32 mName; + unsigned char mUnk; + }; + + #pragma pack(pop) + + enum + { + AI_Wander = 0x575f4941, + AI_Travel = 0x545f4941, + AI_Follow = 0x465f4941, + AI_Escort = 0x455f4941, + AI_Activate = 0x415f4941, + }; + + /// \note Used for storaging packages in a single container + /// w/o manual memory allocation accordingly to policy standards + struct AIPackage + { + int mType; + + // Anonymous union + union + { + AIWander mWander; + AITravel mTravel; + AITarget mTarget; + AIActivate mActivate; + }; + + /// \note for AITarget only, placed here to stick with union, + /// overhead should be not so awful + std::string mCellName; + }; + + struct AIPackageList + { + std::vector mList; + + /// \note This breaks consistency of subrecords reading: + /// after calling it subrecord name is already read, so + /// it needs to use retSubName() if needed. But, hey, there + /// is only one field left (XSCL) and only two records uses AI + void load(ESMReader &esm); + }; +} + +#endif + diff --git a/components/esm/defs_ai.hpp b/components/esm/defs_ai.hpp deleted file mode 100644 index b14cc7d650..0000000000 --- a/components/esm/defs_ai.hpp +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef _ESM_DEFS_AI_H -#define _ESM_DEFS_AI_H - -#include "esm_reader.hpp" - -#include - -namespace ESM -{ - #pragma pack(push) - #pragma pack(1) - - struct AIDTstruct - { - // These are probabilities - char hello, u1, fight, flee, alarm, u2, u3, u4; - // The last u's might be the skills that this NPC can train you - // in? - int services; // See the Services enum - }; // 12 bytes - - struct AIWander - { - short distance; - short duration; - char timeOfDay; - char idle[8]; - char unk; - }; - - struct AITravel - { - float x, y, z; - long unk; - }; - - struct AITarget - { - float x, y, z; - short duration; - NAME32 id; - short unk; - }; - - struct AIActivate - { - NAME32 name; - char unk; - }; - - #pragma pack(pop) - - enum - { - AI_Wander = 0x575f4941, - AI_Travel = 0x545f4941, - AI_Follow = 0x465f4941, - AI_Escort = 0x455f4941, - AI_Activate = 0x415f4941, - }; - - /// \note Used for storaging packages in a single container - /// w/o manual memory allocation accordingly to policy standards - struct AIPackage - { - int type; - - // Anonymous union - union - { - AIWander wander; - AITravel travel; - AITarget target; - AIActivate activate; - }; - - /// \note for AITarget only, placed here to stick with union, - /// overhead should be not so awful - std::string cellName; - }; - - struct AIPackageList - { - std::vector list; - - /// \note This breaks consistency of subrecords reading: - /// after calling it subrecord name is already read, so - /// it needs to use retSubName() if needed. But, hey, there - /// is only one field left (XSCL) and only two records uses AI - void load(ESMReader &esm) { - while (esm.hasMoreSubs()) { - // initialize every iteration - AIPackage pack; - - esm.getSubName(); - if (esm.retSubName() == 0x54444e43) { // CNDT - list.back().cellName = esm.getHString(); - } else if (esm.retSubName() == AI_Wander) { - pack.type = AI_Wander; - esm.getHExact(&pack.wander, 14); - list.push_back(pack); - } else if (esm.retSubName() == AI_Travel) { - pack.type = AI_Travel; - esm.getHExact(&pack.travel, 16); - list.push_back(pack); - } else if (esm.retSubName() == AI_Escort || - esm.retSubName() == AI_Follow) - { - pack.type = - (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow; - - esm.getHExact(&pack.target, 48); - list.push_back(pack); - } else if (esm.retSubName() == AI_Activate) { - pack.type = AI_Activate; - esm.getHExact(&pack.activate, 33); - list.push_back(pack); - } else { // not AI package related data, so leave - return; - } - } - } - }; -} - -#endif - diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index fcef9ccd46..e729447877 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -18,17 +18,17 @@ void Creature::load(ESMReader &esm, const std::string& id) esm.getHNOT(scale, "XSCL"); inventory.load(esm); - spells.load(esm); + mSpells.load(esm); if (esm.isNextSub("AIDT")) { - esm.getHExact(&AI, sizeof(AI)); - hasAI = true; + esm.getHExact(&mAiData, sizeof(mAiData)); + mHasAI = true; } else - hasAI = false; + mHasAI = false; - aiPack.load(esm); + mAiPackage.load(esm); esm.skipRecord(); } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 9db1f3fff2..59e9264464 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -4,7 +4,7 @@ #include "esm_reader.hpp" #include "loadcont.hpp" #include "defs.hpp" -#include "defs_ai.hpp" +#include "aipackage.hpp" namespace ESM { @@ -62,11 +62,11 @@ struct Creature // Defined in loadcont.hpp InventoryList inventory; - SpellList spells; + SpellList mSpells; - bool hasAI; - AIDTstruct AI; - AIPackageList aiPack; + bool mHasAI; + AIData mAiData; + AIPackageList mAiPackage; std::string mId; diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 9fba482469..637aa0e622 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -36,22 +36,22 @@ void NPC::load(ESMReader &esm, const std::string& id) if (esm.isNextSub("AIDT")) { - esm.getHExact(&AI, sizeof(AI)); - hasAI = true; + esm.getHExact(&mAiData, sizeof(mAiData)); + mHasAI= true; } else - hasAI = false; + mHasAI = false; while (esm.isNextSub("DODT") || esm.isNextSub("DNAM")) { if (esm.retSubName() == 0x54444f44) { // DODT struct Dest dodt; - esm.getHExact(&dodt.pos, 24); - dest.push_back(dodt); + esm.getHExact(&dodt.mPos, 24); + mTransport.push_back(dodt); } else if (esm.retSubName() == 0x4d414e44) { // DNAM struct - dest.back().cellName = esm.getHString(); + mTransport.back().mCellName = esm.getHString(); } } - aiPack.load(esm); + mAiPackage.load(esm); esm.skipRecord(); } diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index c636d1f319..eec978e9f2 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -4,7 +4,7 @@ #include "esm_reader.hpp" #include "loadcont.hpp" #include "defs.hpp" -#include "defs_ai.hpp" +#include "aipackage.hpp" namespace ESM { @@ -19,16 +19,16 @@ struct NPC { // This merchant buys: Weapon = 0x00001, - Armor = 0x00002, + Armor = 0x00002, Clothing = 0x00004, - Books = 0x00008, + Books = 0x00008, Ingredients = 0x00010, - Picks = 0x00020, + Picks = 0x00020, Probes = 0x00040, Lights = 0x00080, Apparatus = 0x00100, RepairItem = 0x00200, - Misc = 0x00400, + Misc = 0x00400, // Other services Spells = 0x00800, @@ -47,7 +47,7 @@ struct NPC Respawn = 0x0004, Autocalc = 0x0008, Skeleton = 0x0400, // Skeleton blood effect (white) - Metal = 0x0800 // Metal blood effect (golden?) + Metal = 0x0800 // Metal blood effect (golden?) }; #pragma pack(push) @@ -76,8 +76,8 @@ struct NPC struct Dest { - Position pos; - std::string cellName; + Position mPos; + std::string mCellName; }; NPDTstruct52 npdt52; @@ -88,19 +88,21 @@ struct NPC InventoryList inventory; SpellList spells; - AIDTstruct AI; - bool hasAI; + AIData mAiData; + bool mHasAI; - std::vector dest; - AIPackageList aiPack; + std::vector mTransport; + AIPackageList mAiPackage; - std::string name, model, race, cls, faction, script, - hair, head; // body parts + std::string name, model, race, cls, faction, script; + + // body parts + std::string hair, head; std::string mId; - // Implementation moved to load_impl.cpp - void load(ESMReader &esm, const std::string& id); + // Implementation moved to load_impl.cpp + void load(ESMReader &esm, const std::string& id); }; } #endif From 760f05c4542e8406c98455a04900fab179b5ca0f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Aug 2012 20:47:39 +0200 Subject: [PATCH 142/143] the HUD can be toggled with F12 key; useful for screenshots --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 7 +++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++++ apps/openmw/mwinput/inputmanagerimp.hpp | 2 ++ 5 files changed, 16 insertions(+) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 388dc4af93..389f816dcb 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -181,6 +181,7 @@ namespace MWBase virtual void showCrosshair(bool show) = 0; virtual bool getSubtitlesEnabled() = 0; + virtual void toggleHud() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a3d20ac9e5..faaa41783b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -86,6 +86,7 @@ WindowManager::WindowManager( , mBatchCount(0) , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) + , mHudEnabled(true) { // Set up the GUI system @@ -875,3 +876,9 @@ bool WindowManager::getSubtitlesEnabled () { return mSubtitlesEnabled; } + +void WindowManager::toggleHud () +{ + mHudEnabled = !mHudEnabled; + mHud->setVisible (mHudEnabled); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index dd8a3b0067..38411bb18c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -162,6 +162,7 @@ namespace MWGui virtual void showCrosshair(bool show); virtual bool getSubtitlesEnabled(); + virtual void toggleHud(); virtual void disallowMouse(); virtual void allowMouse(); @@ -223,6 +224,7 @@ namespace MWGui bool mCrosshairEnabled; bool mSubtitlesEnabled; + bool mHudEnabled; /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 852f90ae93..f47ea8bb16 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -226,6 +226,9 @@ namespace MWInput case A_QuickKeysMenu: showQuickKeysMenu(); break; + case A_ToggleHUD: + mWindows.toggleHud(); + break; } } } @@ -676,6 +679,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey9] = OIS::KC_9; defaultKeyBindings[A_QuickKey10] = OIS::KC_0; defaultKeyBindings[A_Screenshot] = OIS::KC_SYSRQ; + defaultKeyBindings[A_ToggleHUD] = OIS::KC_F12; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index ffa222f449..2572ec1152 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -234,6 +234,8 @@ namespace MWInput A_QuickKeysMenu, + A_ToggleHUD, + A_Last // Marker for the last item }; }; From ba43f2997f17d1833b04f5d5f74371f82a6efef9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Aug 2012 22:08:30 +0200 Subject: [PATCH 143/143] fix missing character in font for french morrowind version, thanks to jvoisin --- files/mygui/openmw.font.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/files/mygui/openmw.font.xml b/files/mygui/openmw.font.xml index 73d491e04e..cb5dd648f8 100644 --- a/files/mygui/openmw.font.xml +++ b/files/mygui/openmw.font.xml @@ -12,6 +12,7 @@ +