From 4f92044d71f8e40cdb1e876cc509d5239eed5bf0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 17 Aug 2014 05:45:35 +0200 Subject: [PATCH] Allow user-created markers on local map (Fixes #1571) --- apps/openmw/mwbase/world.hpp | 7 +- apps/openmw/mwgui/hud.cpp | 5 +- apps/openmw/mwgui/hud.hpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 398 +++++++++++++++++----- apps/openmw/mwgui/mapwindow.hpp | 111 +++++- apps/openmw/mwgui/windowmanagerimp.cpp | 21 +- apps/openmw/mwgui/windowmanagerimp.hpp | 5 + apps/openmw/mwrender/localmap.cpp | 16 +- apps/openmw/mwrender/localmap.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 9 +- apps/openmw/mwrender/renderingmanager.hpp | 7 +- apps/openmw/mwstate/statemanagerimp.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 9 +- apps/openmw/mwworld/worldimp.hpp | 7 +- components/esm/defs.hpp | 1 + files/mygui/CMakeLists.txt | 1 + files/mygui/openmw_edit.skin.xml | 17 + files/mygui/openmw_edit_note.layout | 34 ++ files/mygui/openmw_map_window.layout | 5 +- 19 files changed, 546 insertions(+), 116 deletions(-) create mode 100644 files/mygui/openmw_edit_note.layout diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index bf64ee44e..02e143286 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -148,8 +148,11 @@ namespace MWBase virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out) = 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 void worldToInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) = 0; + ///< see MWRender::LocalMap::worldToInteriorMapPosition + + virtual Ogre::Vector2 interiorMapToWorldPosition (float nX, float nY, int x, int y) = 0; + ///< see MWRender::LocalMap::interiorMapToWorldPosition virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0; ///< see MWRender::LocalMap::isPositionExplored diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index a541901ea..2593e6e77 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -61,8 +61,9 @@ namespace MWGui }; - HUD::HUD(int fpsLevel, DragAndDrop* dragAndDrop) + HUD::HUD(CustomMarkerCollection &customMarkers, int fpsLevel, DragAndDrop* dragAndDrop) : Layout("openmw_hud.layout") + , LocalMapBase(customMarkers) , mHealth(NULL) , mMagicka(NULL) , mStamina(NULL) @@ -161,7 +162,7 @@ namespace MWGui getWidget(mTriangleCounter, "TriangleCounter"); getWidget(mBatchCounter, "BatchCounter"); - LocalMapBase::init(mMinimap, mCompass, this); + LocalMapBase::init(mMinimap, mCompass); mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index bf0419aae..b41a374b6 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -15,7 +15,7 @@ namespace MWGui class HUD : public OEngine::GUI::Layout, public LocalMapBase { public: - HUD(int fpsLevel, DragAndDrop* dragAndDrop); + HUD(CustomMarkerCollection& customMarkers, int fpsLevel, DragAndDrop* dragAndDrop); virtual ~HUD(); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setFPS(float fps); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 1f2794be3..2271fc2e5 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -18,48 +18,131 @@ #include "widgets.hpp" +namespace +{ + + const int widgetSize = 512; + + const int cellSize = 8192; + + enum WidgetDepth + { + CompassLayer = 0, + MarkerAboveFogLayer = 1, + FogLayer = 2, + MarkerLayer = 3, + MapLayer = 4 + }; + +} + namespace MWGui { - LocalMapBase::LocalMapBase() + void CustomMarker::save(ESM::ESMWriter &esm) const + { + esm.writeHNT("POSX", mWorldX); + esm.writeHNT("POSY", mWorldY); + mCell.save(esm); + if (!mNote.empty()) + esm.writeHNString("NOTE", mNote); + } + + void CustomMarker::load(ESM::ESMReader &esm) + { + esm.getHNT(mWorldX, "POSX"); + esm.getHNT(mWorldY, "POSY"); + mCell.load(esm); + mNote = esm.getHNOString("NOTE"); + } + + // ------------------------------------------------------ + + void CustomMarkerCollection::addMarker(const CustomMarker &marker, bool triggerEvent) + { + mMarkers.push_back(marker); + if (triggerEvent) + eventMarkersChanged(); + } + + void CustomMarkerCollection::deleteMarker(const CustomMarker &marker) + { + std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); + if (it != mMarkers.end()) + mMarkers.erase(it); + else + throw std::runtime_error("can't find marker to delete"); + + eventMarkersChanged(); + } + + void CustomMarkerCollection::updateMarker(const CustomMarker &marker, const std::string &newNote) + { + std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); + if (it != mMarkers.end()) + it->mNote = newNote; + else + throw std::runtime_error("can't find marker to update"); + + eventMarkersChanged(); + } + + void CustomMarkerCollection::clear() + { + mMarkers.clear(); + eventMarkersChanged(); + } + + std::vector::const_iterator CustomMarkerCollection::begin() const + { + return mMarkers.begin(); + } + + std::vector::const_iterator CustomMarkerCollection::end() const + { + return mMarkers.end(); + } + + size_t CustomMarkerCollection::size() const + { + return mMarkers.size(); + } + + // ------------------------------------------------------ + + LocalMapBase::LocalMapBase(CustomMarkerCollection &markers) : mCurX(0) , mCurY(0) , mInterior(false) , mFogOfWar(true) , mLocalMap(NULL) - , mMapDragAndDrop(false) , mPrefix() , mChanged(true) - , mLayout(NULL) , mLastPositionX(0.0f) , mLastPositionY(0.0f) , mLastDirectionX(0.0f) , mLastDirectionY(0.0f) , mCompass(NULL) , mMarkerUpdateTimer(0.0f) + , mCustomMarkers(markers) { + mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } LocalMapBase::~LocalMapBase() { - // Clear our "lost focus" delegate for marker widgets first, otherwise it will - // fire when the widget is about to be destroyed and the mouse cursor is over it. - // At that point, other widgets may already be destroyed, so applyFogOfWar (which is called by the delegate) would crash. - for (std::vector::iterator it = mDoorMarkerWidgets.begin(); it != mDoorMarkerWidgets.end(); ++it) - (*it)->eventMouseLostFocus.clear(); - for (std::vector::iterator it = mMarkerWidgets.begin(); it != mMarkerWidgets.end(); ++it) - (*it)->eventMouseLostFocus.clear(); + mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } - void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass) { mLocalMap = widget; - mLayout = layout; - mMapDragAndDrop = mapDragAndDrop; mCompass = compass; + mCompass->setDepth(CompassLayer); + mCompass->setNeedMouseFocus(false); + // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each - const int widgetSize = 512; for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) @@ -67,16 +150,15 @@ namespace MWGui MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), MyGUI::Align::Top | MyGUI::Align::Left); + map->setDepth(MapLayer); - MyGUI::ImageBox* fog = map->createWidget("ImageBox", - MyGUI::IntCoord(0, 0, widgetSize, widgetSize), + MyGUI::ImageBox* fog = mLocalMap->createWidget("ImageBox", + MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), MyGUI::Align::Top | MyGUI::Align::Left); + fog->setDepth(FogLayer); - if (!mMapDragAndDrop) - { - map->setNeedMouseFocus(false); - fog->setNeedMouseFocus(false); - } + map->setNeedMouseFocus(false); + fog->setNeedMouseFocus(false); mMapWidgets.push_back(map); mFogWidgets.push_back(fog); @@ -112,19 +194,7 @@ namespace MWGui : ""); } } - notifyMapChanged (); - } - - void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) - { - // Workaround to not make the marker visible if it's under fog of war - applyFogOfWar (); - } - - void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) - { - // Workaround to not make the marker visible if it's under fog of war - applyFogOfWar (); + redraw(); } MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerPosition& markerPos) @@ -139,7 +209,6 @@ namespace MWGui { int cellX, cellY; MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY); - const int cellSize = 8192; nX = (worldX - cellSize * cellX) / cellSize; // Image space is -Y up, cells are Y up nY = 1 - (worldY - cellSize * cellY) / cellSize; @@ -150,21 +219,21 @@ namespace MWGui markerPos.cellX = cellX; markerPos.cellY = cellY; - widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellDx) * 512, - nY * 512 - (cellDy-1) * 512); + widgetPos = MyGUI::IntPoint(nX * widgetSize + (1+cellDx) * widgetSize, + nY * widgetSize - (cellDy-1) * widgetSize); } else { int cellX, cellY; Ogre::Vector2 worldPos (worldX, worldY); - MWBase::Environment::get().getWorld ()->getInteriorMapPosition (worldPos, nX, nY, cellX, cellY); + MWBase::Environment::get().getWorld ()->worldToInteriorMapPosition (worldPos, nX, nY, cellX, cellY); markerPos.cellX = cellX; markerPos.cellY = cellY; // Image space is -Y up, cells are Y up - widgetPos = MyGUI::IntPoint(nX * 512 + (1+(cellX-mCurX)) * 512, - nY * 512 + (1-(cellY-mCurY)) * 512); + widgetPos = MyGUI::IntPoint(nX * widgetSize + (1+(cellX-mCurX)) * widgetSize, + nY * widgetSize + (1-(cellY-mCurY)) * widgetSize); } markerPos.nX = nX; @@ -172,6 +241,52 @@ namespace MWGui return widgetPos; } + void LocalMapBase::updateCustomMarkers() + { + for (std::vector::iterator it = mCustomMarkerWidgets.begin(); it != mCustomMarkerWidgets.end(); ++it) + MyGUI::Gui::getInstance().destroyWidget(*it); + mCustomMarkerWidgets.clear(); + + for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) + { + const CustomMarker& marker = *it; + + if (marker.mCell.mPaged != !mInterior) + continue; + if (mInterior) + { + if (marker.mCell.mWorldspace != mPrefix) + continue; + } + else + { + if (std::abs(marker.mCell.mIndex.mX - mCurX) > 1) + continue; + if (std::abs(marker.mCell.mIndex.mY - mCurY) > 1) + continue; + } + + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(marker.mWorldX, marker.mWorldY, markerPos); + + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default); + markerWidget->setDepth(MarkerAboveFogLayer); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", MyGUI::TextIterator::toTagsString(marker.mNote)); + markerWidget->setColour(MyGUI::Colour(1.0,0.3,0.3)); + markerWidget->setUserData(marker); + markerWidget->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &LocalMapBase::onCustomMarkerDoubleClicked); + mCustomMarkerWidgets.push_back(markerWidget); + } + redraw(); + } + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) @@ -182,6 +297,9 @@ namespace MWGui mInterior = interior; mChanged = false; + applyFogOfWar(); + + // clear all previous door markers for (std::vector::iterator it = mDoorMarkerWidgets.begin(); it != mDoorMarkerWidgets.end(); ++it) MyGUI::Gui::getInstance().destroyWidget(*it); @@ -240,12 +358,11 @@ namespace MWGui ++counter; MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", widgetCoord, MyGUI::Align::Default); + markerWidget->setDepth(MarkerLayer); markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); markerWidget->setUserString("Caption_TextOneLine", marker.name); - markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); - markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); // Used by tooltips to not show the tooltip if marker is hidden by fog of war markerWidget->setUserString("IsMarker", "true"); markerWidget->setUserData(markerPos); @@ -253,21 +370,19 @@ namespace MWGui mDoorMarkerWidgets.push_back(markerWidget); } - updateMarkers(); - - applyFogOfWar(); - - // set the compass texture again, because MyGUI determines sorting of ImageBox widgets - // based on the last setImageTexture call - std::string tex = "textures\\compass.dds"; - mCompass->setImageTexture(""); - mCompass->setImageTexture(tex); + updateMagicMarkers(); + updateCustomMarkers(); } + void LocalMapBase::redraw() + { + // Redraw children in proper order + mLocalMap->getParent()->_updateChilds(); + } void LocalMapBase::setPlayerPos(const float x, const float y) { - updateMarkers(); + updateMagicMarkers(); if (x == mLastPositionX && y == mLastPositionY) return; @@ -280,7 +395,7 @@ namespace MWGui MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); mLocalMap->setViewOffset(pos); - mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16)); + mCompass->setPosition(MyGUI::IntPoint(widgetSize+x*widgetSize-16, widgetSize+y*widgetSize-16)); mLastPositionX = x; mLastPositionY = y; } @@ -342,11 +457,12 @@ namespace MWGui ++counter; MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", widgetCoord, MyGUI::Align::Default); + markerWidget->setDepth(MarkerAboveFogLayer); markerWidget->setImageTexture(markerTexture); markerWidget->setUserString("IsMarker", "true"); markerWidget->setUserData(markerPos); markerWidget->setColour(markerColour); - mMarkerWidgets.push_back(markerWidget); + mMagicMarkerWidgets.push_back(markerWidget); } } @@ -357,16 +473,16 @@ namespace MWGui if (mMarkerUpdateTimer >= 0.25) { mMarkerUpdateTimer = 0; - updateMarkers(); + updateMagicMarkers(); } } - void LocalMapBase::updateMarkers() + void LocalMapBase::updateMagicMarkers() { // clear all previous markers - for (std::vector::iterator it = mMarkerWidgets.begin(); it != mMarkerWidgets.end(); ++it) + for (std::vector::iterator it = mMagicMarkerWidgets.begin(); it != mMagicMarkerWidgets.end(); ++it) MyGUI::Gui::getInstance().destroyWidget(*it); - mMarkerWidgets.clear(); + mMagicMarkerWidgets.clear(); addDetectionMarkers(MWBase::World::Detect_Creature); addDetectionMarkers(MWBase::World::Detect_Key); @@ -386,22 +502,31 @@ namespace MWGui 8, 8); MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", widgetCoord, MyGUI::Align::Default); + markerWidget->setDepth(MarkerAboveFogLayer); markerWidget->setImageTexture("textures\\menu_map_smark.dds"); markerWidget->setUserString("IsMarker", "true"); markerWidget->setUserData(markerPos); - mMarkerWidgets.push_back(markerWidget); + mMagicMarkerWidgets.push_back(markerWidget); } + + redraw(); } // ------------------------------------------------------------------------------------------ - MapWindow::MapWindow(DragAndDrop* drag, const std::string& cacheDir) + MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, const std::string& cacheDir) : WindowPinnableBase("openmw_map_window.layout") , NoDrop(drag, mMainWidget) + , LocalMapBase(customMarkers) , mGlobal(false) , mGlobalMap(0) , mGlobalMapRender(0) + , mEditNoteDialog() { + mEditNoteDialog.setVisible(false); + mEditNoteDialog.eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditOk); + mEditNoteDialog.eventDeleteClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditDelete); + setCoord(500,0,320,300); getWidget(mLocalMap, "LocalMap"); @@ -423,8 +548,78 @@ namespace MWGui getWidget(mEventBoxLocal, "EventBoxLocal"); mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked); + + LocalMapBase::init(mLocalMap, mPlayerArrowLocal); + } + + void MapWindow::onNoteEditOk() + { + if (mEditNoteDialog.getDeleteButtonShown()) + mCustomMarkers.updateMarker(mEditingMarker, mEditNoteDialog.getText()); + else + { + mEditingMarker.mNote = mEditNoteDialog.getText(); + mCustomMarkers.addMarker(mEditingMarker); + } + + mEditNoteDialog.setVisible(false); + } + + void MapWindow::onNoteEditDelete() + { + mCustomMarkers.deleteMarker(mEditingMarker); + + mEditNoteDialog.setVisible(false); + } - LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); + void MapWindow::onCustomMarkerDoubleClicked(MyGUI::Widget *sender) + { + mEditingMarker = *sender->getUserData(); + mEditNoteDialog.setText(mEditingMarker.mNote); + mEditNoteDialog.showDeleteButton(true); + mEditNoteDialog.setVisible(true); + } + + void MapWindow::onMapDoubleClicked(MyGUI::Widget *sender) + { + MyGUI::IntPoint clickedPos = MyGUI::InputManager::getInstance().getMousePosition(); + + MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition(); + int x = int(widgetPos.left/float(widgetSize))-1; + int y = (int(widgetPos.top/float(widgetSize))-1)*-1; + float nX = widgetPos.left/float(widgetSize) - int(widgetPos.left/float(widgetSize)); + float nY = widgetPos.top/float(widgetSize) - int(widgetPos.top/float(widgetSize)); + x += mCurX; + y += mCurY; + + Ogre::Vector2 worldPos; + if (mInterior) + { + worldPos = MWBase::Environment::get().getWorld()->interiorMapToWorldPosition(nX, nY, x, y); + } + else + { + worldPos.x = (x + nX) * cellSize; + worldPos.y = (y + (1.0-nY)) * cellSize; + } + + mEditingMarker.mWorldX = worldPos.x; + mEditingMarker.mWorldY = worldPos.y; + + mEditingMarker.mCell.mPaged = !mInterior; + if (mInterior) + mEditingMarker.mCell.mWorldspace = LocalMapBase::mPrefix; + else + { + mEditingMarker.mCell.mWorldspace = "sys::default"; + mEditingMarker.mCell.mIndex.mX = x; + mEditingMarker.mCell.mIndex.mY = y; + } + + mEditNoteDialog.setVisible(true); + mEditNoteDialog.showDeleteButton(false); + mEditNoteDialog.setText(""); } void MapWindow::renderGlobalMap(Loading::Listener* loadingListener) @@ -545,9 +740,6 @@ namespace MWGui void MapWindow::open() { globalMapUpdatePlayer(); - - mPlayerArrowGlobal->setImageTexture (""); - mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); } void MapWindow::globalMapUpdatePlayer () @@ -584,20 +776,6 @@ namespace MWGui globalMapUpdatePlayer (); } - void MapWindow::notifyMapChanged () - { - // workaround to prevent the map from drawing on top of the button - MyGUI::IntCoord oldCoord = mButton->getCoord (); - MyGUI::Gui::getInstance().destroyWidget (mButton); - mButton = mMainWidget->createWidget("MW_Button", - oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right); - mButton->setProperty ("ExpandDirection", "Left"); - - mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); - mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : - "#{sWorld}"); - } - void MapWindow::setGlobalMapPlayerPosition(float worldX, float worldY) { float x, y; @@ -617,6 +795,7 @@ namespace MWGui { mMarkers.clear(); mGlobalMapRender->clear(); + mChanged = true; while (mEventBoxGlobal->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mEventBoxGlobal->getChildAt(0)); @@ -654,4 +833,67 @@ namespace MWGui } } } + + // ------------------------------------------------------------------- + + EditNoteDialog::EditNoteDialog() + : WindowModal("openmw_edit_note.layout") + { + getWidget(mOkButton, "OkButton"); + getWidget(mCancelButton, "CancelButton"); + getWidget(mDeleteButton, "DeleteButton"); + getWidget(mTextEdit, "TextEdit"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onCancelButtonClicked); + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onOkButtonClicked); + mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onDeleteButtonClicked); + } + + void EditNoteDialog::showDeleteButton(bool show) + { + mDeleteButton->setVisible(show); + } + + bool EditNoteDialog::getDeleteButtonShown() + { + return mDeleteButton->getVisible(); + } + + void EditNoteDialog::setText(const std::string &text) + { + mTextEdit->setCaption(MyGUI::TextIterator::toTagsString(text)); + } + + std::string EditNoteDialog::getText() + { + return MyGUI::TextIterator::getOnlyText(mTextEdit->getCaption()); + } + + void EditNoteDialog::open() + { + WindowModal::open(); + center(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); + } + + void EditNoteDialog::exit() + { + setVisible(false); + } + + void EditNoteDialog::onCancelButtonClicked(MyGUI::Widget *sender) + { + setVisible(false); + } + + void EditNoteDialog::onOkButtonClicked(MyGUI::Widget *sender) + { + eventOkClicked(); + } + + void EditNoteDialog::onDeleteButtonClicked(MyGUI::Widget *sender) + { + eventDeleteClicked(); + } + } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 7021a5d62..1c7ba34ed 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -5,6 +5,8 @@ #include "windowpinnablebase.hpp" +#include + namespace MWRender { class GlobalMap; @@ -23,12 +25,52 @@ namespace Loading namespace MWGui { + + struct CustomMarker + { + float mWorldX; + float mWorldY; + + ESM::CellId mCell; + + std::string mNote; + + bool operator == (const CustomMarker& other) + { + return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY; + } + + void load (ESM::ESMReader& reader); + void save (ESM::ESMWriter& writer) const; + }; + + class CustomMarkerCollection + { + public: + void addMarker(const CustomMarker& marker, bool triggerEvent=true); + void deleteMarker (const CustomMarker& marker); + void updateMarker(const CustomMarker& marker, const std::string& newNote); + + void clear(); + + size_t size() const; + + std::vector::const_iterator begin() const; + std::vector::const_iterator end() const; + + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; + EventHandle_Void eventMarkersChanged; + + private: + std::vector mMarkers; + }; + class LocalMapBase { public: - LocalMapBase(); + LocalMapBase(CustomMarkerCollection& markers); virtual ~LocalMapBase(); - void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop=false); + void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass); void setCellPrefix(const std::string& prefix); void setActiveCell(const int x, const int y, bool interior=false); @@ -57,44 +99,74 @@ namespace MWGui bool mChanged; bool mFogOfWar; + // Stores markers that were placed by a player. May be shared between multiple map views. + CustomMarkerCollection& mCustomMarkers; + std::vector mMapWidgets; std::vector mFogWidgets; // Keep track of created marker widgets, just to easily remove them later. - std::vector mDoorMarkerWidgets; // Doors - std::vector mMarkerWidgets; // Other markers + std::vector mDoorMarkerWidgets; + std::vector mMagicMarkerWidgets; + std::vector mCustomMarkerWidgets; - void applyFogOfWar(); + void updateCustomMarkers(); - void onMarkerFocused(MyGUI::Widget* w1, MyGUI::Widget* w2); - void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + void applyFogOfWar(); MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerPosition& markerPos); virtual void notifyPlayerUpdate() {} virtual void notifyMapChanged() {} - // Update markers (Detect X effects, Mark/Recall effects) - // Note, door markers are handled in setActiveCell - void updateMarkers(); + virtual void onCustomMarkerDoubleClicked(MyGUI::Widget* sender) {} + + void updateMagicMarkers(); void addDetectionMarkers(int type); - OEngine::GUI::Layout* mLayout; + void redraw(); float mMarkerUpdateTimer; - bool mMapDragAndDrop; - float mLastPositionX; float mLastPositionY; float mLastDirectionX; float mLastDirectionY; }; + class EditNoteDialog : public MWGui::WindowModal + { + public: + EditNoteDialog(); + + virtual void open(); + virtual void exit(); + + void showDeleteButton(bool show); + bool getDeleteButtonShown(); + void setText(const std::string& text); + std::string getText(); + + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; + + EventHandle_Void eventDeleteClicked; + EventHandle_Void eventOkClicked; + + private: + void onCancelButtonClicked(MyGUI::Widget* sender); + void onOkButtonClicked(MyGUI::Widget* sender); + void onDeleteButtonClicked(MyGUI::Widget* sender); + + MyGUI::TextBox* mTextEdit; + MyGUI::Button* mOkButton; + MyGUI::Button* mCancelButton; + MyGUI::Button* mDeleteButton; + }; + class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop { public: - MapWindow(DragAndDrop* drag, const std::string& cacheDir); + MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, const std::string& cacheDir); virtual ~MapWindow(); void setCellName(const std::string& cellName); @@ -123,7 +195,10 @@ namespace MWGui void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onWorldButtonClicked(MyGUI::Widget* _sender); - + void onMapDoubleClicked(MyGUI::Widget* sender); + void onNoteEditOk(); + void onNoteEditDelete(); + void onNoteDoubleClicked(MyGUI::Widget* sender); void globalMapUpdatePlayer(); MyGUI::ScrollView* mGlobalMap; @@ -148,12 +223,14 @@ namespace MWGui MWRender::GlobalMap* mGlobalMapRender; - protected: + EditNoteDialog mEditNoteDialog; + CustomMarker mEditingMarker; + virtual void onPinToggled(); virtual void onTitleDoubleClicked(); + virtual void onCustomMarkerDoubleClicked(MyGUI::Widget* sender); virtual void notifyPlayerUpdate(); - virtual void notifyMapChanged(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7096f3d89..780580e2a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -225,7 +225,7 @@ namespace MWGui mRecharge = new Recharge(); mMenu = new MainMenu(w,h); - mMap = new MapWindow(mDragAndDrop, ""); + mMap = new MapWindow(mCustomMarkers, mDragAndDrop, ""); trackWindow(mMap, "map"); mStatsWindow = new StatsWindow(mDragAndDrop); trackWindow(mStatsWindow, "stats"); @@ -243,7 +243,7 @@ namespace MWGui trackWindow(mDialogueWindow, "dialogue"); mContainerWindow = new ContainerWindow(mDragAndDrop); trackWindow(mContainerWindow, "container"); - mHud = new HUD(mShowFPSLevel, mDragAndDrop); + mHud = new HUD(mCustomMarkers, mShowFPSLevel, mDragAndDrop); mToolTips = new ToolTips(); mScrollWindow = new ScrollWindow(); mBookWindow = new BookWindow(); @@ -1530,6 +1530,8 @@ namespace MWGui mSelectedSpell.clear(); + mCustomMarkers.clear(); + mGuiModes.clear(); MWBase::Environment::get().getInputManager()->changeInputMode(false); updateVisible(); @@ -1549,6 +1551,14 @@ namespace MWGui writer.endRecord(ESM::REC_ASPL); progress.increaseProgress(); } + + for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) + { + writer.startRecord(ESM::REC_MARK); + (*it).save(writer); + writer.endRecord(ESM::REC_MARK); + progress.increaseProgress(); + } } void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type) @@ -1562,12 +1572,19 @@ namespace MWGui reader.getSubNameIs("ID__"); mSelectedSpell = reader.getHString(); } + else if (type == ESM::REC_MARK) + { + CustomMarker marker; + marker.load(reader); + mCustomMarkers.addMarker(marker, false); + } } int WindowManager::countSavedGameRecords() const { return 1 // Global map + 1 // QuickKeysMenu + + mCustomMarkers.size() + (!mSelectedSpell.empty() ? 1 : 0); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 76bab8fc8..00a5ce80e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -12,6 +12,8 @@ #include "../mwbase/windowmanager.hpp" +#include "mapwindow.hpp" + #include #include @@ -345,6 +347,9 @@ namespace MWGui std::stack mCurrentModals; + // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). + CustomMarkerCollection mCustomMarkers; + OEngine::GUI::MyGUIManager *mGuiManager; OEngine::Render::OgreRenderer *mRendering; HUD *mHud; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index ace105b2d..85f73ab7e 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -431,7 +431,7 @@ void LocalMap::render(const float x, const float y, mRendering->getScene()->setAmbientLight(oldAmbient); } -void LocalMap::getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y) +void LocalMap::worldToInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y) { pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().y), mAngle); @@ -444,6 +444,18 @@ void LocalMap::getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, nY = 1.0-(pos.y - min.y - sSize*y)/sSize; } +Ogre::Vector2 LocalMap::interiorMapToWorldPosition (float nX, float nY, int x, int y) +{ + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); + Ogre::Vector2 pos; + + pos.x = sSize * (nX + x) + min.x; + pos.y = sSize * (1.0-nY + y) + min.y; + + pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().y), -mAngle); + return pos; +} + bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interior) { std::string texName = (interior ? mInteriorName + "_" : "Cell_") + coordStr(x, y); @@ -502,7 +514,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni Vector2 pos(position.x, position.y); if (mInterior) - getInteriorMapPosition(pos, u,v, x,y); + worldToInteriorMapPosition(pos, u,v, x,y); Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).yAxis(); diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index aed7e0637..b531c3e29 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -74,7 +74,9 @@ namespace MWRender * Get the interior map texture index and normalized position * on this texture, given a world position */ - void getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y); + void worldToInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y); + + Ogre::Vector2 interiorMapToWorldPosition (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) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 46ff57cb0..15bac7454 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -945,9 +945,14 @@ void RenderingManager::setCameraDistance(float dist, bool adjust, bool override) } } -void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) +void RenderingManager::worldToInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) { - return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y); + return mLocalMap->worldToInteriorMapPosition (position, nX, nY, x, y); +} + +Ogre::Vector2 RenderingManager::interiorMapToWorldPosition(float nX, float nY, int x, int y) +{ + return mLocalMap->interiorMapToWorldPosition(nX, nY, x, y); } bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, bool interior) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ef436931d..c5a77afc7 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -198,8 +198,11 @@ public: Ogre::Viewport* getViewport() { return mRendering.getViewport(); } - void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); - ///< see MWRender::LocalMap::getInteriorMapPosition + void worldToInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + ///< see MWRender::LocalMap::worldToInteriorMapPosition + + Ogre::Vector2 interiorMapToWorldPosition (float nX, float nY, int x, int y); + ///< see MWRender::LocalMap::interiorMapToWorldPosition bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 96f14d7e9..80851d573 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -363,6 +363,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_GMAP: case ESM::REC_KEYS: case ESM::REC_ASPL: + case ESM::REC_MARK: MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); break; @@ -375,7 +376,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl default: // ignore invalid records - /// \todo log error + std::cerr << "Ignoring unknown record: " << n.name << std::endl; reader.skipRecord(); } listener.increaseProgress(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 89741dc74..f19a40313 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1627,9 +1627,14 @@ namespace MWWorld } } - void World::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) + void World::worldToInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) { - mRendering->getInteriorMapPosition(position, nX, nY, x, y); + mRendering->worldToInteriorMapPosition(position, nX, nY, x, y); + } + + Ogre::Vector2 World::interiorMapToWorldPosition(float nX, float nY, int x, int y) + { + return mRendering->interiorMapToWorldPosition(nX, nY, x, y); } bool World::isPositionExplored (float nX, float nY, int x, int y, bool interior) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ad166c91a..4dade0f21 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -210,8 +210,11 @@ namespace MWWorld virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out); ///< 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 void worldToInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + ///< see MWRender::LocalMap::worldToInteriorMapPosition + + virtual Ogre::Vector2 interiorMapToWorldPosition (float nX, float nY, int x, int y); + ///< see MWRender::LocalMap::interiorMapToWorldPosition virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index f967af274..6ef0f77fb 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -112,6 +112,7 @@ enum RecNameInts REC_MPRJ = FourCC<'M','P','R','J'>::value, REC_PROJ = FourCC<'P','R','O','J'>::value, REC_DCOU = FourCC<'D','C','O','U'>::value, + REC_MARK = FourCC<'M','A','R','K'>::value, // format 1 REC_FILT = 0x544C4946 diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 54a4d0b63..a59535090 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -81,6 +81,7 @@ set(MYGUI_FILES openmw_savegame_dialog.layout openmw_recharge_dialog.layout openmw_screen_fader.layout + openmw_edit_note.layout DejaVuLGCSansMono.ttf markers.png ../launcher/images/openmw.png diff --git a/files/mygui/openmw_edit.skin.xml b/files/mygui/openmw_edit.skin.xml index 50f37dbfc..9a872c576 100644 --- a/files/mygui/openmw_edit.skin.xml +++ b/files/mygui/openmw_edit.skin.xml @@ -40,4 +40,21 @@ + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_edit_note.layout b/files/mygui/openmw_edit_note.layout new file mode 100644 index 000000000..4985b5187 --- /dev/null +++ b/files/mygui/openmw_edit_note.layout @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index b842888a1..605c3d6ff 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -12,7 +12,9 @@ - + + + @@ -28,7 +30,6 @@ -