Allow user-created markers on local map (Fixes #1571)

pull/229/head
scrawl 11 years ago
parent 17dc01ec44
commit 4f92044d71

@ -148,8 +148,11 @@ namespace MWBase
virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector<DoorMarker>& 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

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

@ -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<float>& value);
void setFPS(float fps);

@ -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<CustomMarker>::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<CustomMarker>::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<CustomMarker>::const_iterator CustomMarkerCollection::begin() const
{
return mMarkers.begin();
}
std::vector<CustomMarker>::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<MyGUI::Widget*>::iterator it = mDoorMarkerWidgets.begin(); it != mDoorMarkerWidgets.end(); ++it)
(*it)->eventMouseLostFocus.clear();
for (std::vector<MyGUI::Widget*>::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<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize),
MyGUI::Align::Top | MyGUI::Align::Left);
map->setDepth(MapLayer);
MyGUI::ImageBox* fog = map->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(0, 0, widgetSize, widgetSize),
MyGUI::ImageBox* fog = mLocalMap->createWidget<MyGUI::ImageBox>("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<MyGUI::Widget*>::iterator it = mCustomMarkerWidgets.begin(); it != mCustomMarkerWidgets.end(); ++it)
MyGUI::Gui::getInstance().destroyWidget(*it);
mCustomMarkerWidgets.clear();
for (std::vector<CustomMarker>::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<MyGUI::Button>("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<MyGUI::Widget*>::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<MyGUI::Button>("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<MyGUI::ImageBox>("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<MyGUI::Widget*>::iterator it = mMarkerWidgets.begin(); it != mMarkerWidgets.end(); ++it)
for (std::vector<MyGUI::Widget*>::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<MyGUI::ImageBox>("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<CustomMarker>();
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<MWGui::Widgets::AutoSizedButton>("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();
}
}

@ -5,6 +5,8 @@
#include "windowpinnablebase.hpp"
#include <components/esm/cellid.hpp>
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<CustomMarker>::const_iterator begin() const;
std::vector<CustomMarker>::const_iterator end() const;
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
EventHandle_Void eventMarkersChanged;
private:
std::vector<CustomMarker> 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<MyGUI::ImageBox*> mMapWidgets;
std::vector<MyGUI::ImageBox*> mFogWidgets;
// Keep track of created marker widgets, just to easily remove them later.
std::vector<MyGUI::Widget*> mDoorMarkerWidgets; // Doors
std::vector<MyGUI::Widget*> mMarkerWidgets; // Other markers
std::vector<MyGUI::Widget*> mDoorMarkerWidgets;
std::vector<MyGUI::Widget*> mMagicMarkerWidgets;
std::vector<MyGUI::Widget*> 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();
};
}

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

@ -12,6 +12,8 @@
#include "../mwbase/windowmanager.hpp"
#include "mapwindow.hpp"
#include <MyGUI_KeyCode.h>
#include <MyGUI_Types.h>
@ -345,6 +347,9 @@ namespace MWGui
std::stack<WindowModal*> 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;

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

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

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

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

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

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

@ -210,8 +210,11 @@ namespace MWWorld
virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector<DoorMarker>& 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

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

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

@ -40,4 +40,21 @@
</Skin>
<Skin name="MW_TextBoxEditWithBorder" size="512 20">
<!-- Borders -->
<Child type="Widget" skin="MW_Box" offset="0 0 512 20" align="Stretch"/>
<Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Left Top"/>
<Property key="TextColour" value="0.75 0.6 0.35"/>
<Child type="TextBox" skin="MW_TextEditClient" offset="2 2 490 18" align="Stretch" name="Client"/>
<Child type="MWScrollBar" skin="MW_VScroll" offset="494 3 14 14" align="Right VStretch" name="VScroll"/>
</Skin>
</MyGUI>

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 336 242" name="_Main">
<Widget type="AutoSizedTextBox" skin="SandText" position="13 13 200 16">
<Property key="Caption" value="#{sEditNote}"/>
</Widget>
<Widget type="EditBox" skin="MW_TextBoxEditWithBorder" position="13 38 303 150" name="TextEdit" align="Left Top Stretch">
<Property key="MultiLine" value="true"/>
<Property key="VisibleVScroll" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
</Widget>
<Widget type="HBox" position="13 200 303 24">
<Property key="Spacing" value="6"/>
<Widget type="Widget">
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="DeleteButton">
<Property key="Caption" value="#{sDelete}"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="CancelButton">
<Property key="Caption" value="#{sCancel}"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="OkButton">
<Property key="Caption" value="#{sOk}"/>
</Widget>
</Widget>
</Widget>
</MyGUI>

@ -12,7 +12,9 @@
<Property key="ImageTexture" value="textures\compass.dds"/>
</Widget>
<Widget type="Button" skin="" position_real="0 0 1 1" name="EventBoxLocal" align="Stretch"/>
<Widget type="Button" skin="" position_real="0 0 1 1" name="EventBoxLocal" align="Stretch">
<Property key="Depth" value="10"/>
</Widget>
</Widget>
<!-- Global map -->
@ -28,7 +30,6 @@
<Widget type="Button" skin="" position_real="0 0 1 1" name="EventBoxGlobal" align="Stretch"/>
</Widget>
<!-- World button -->
<Widget type="AutoSizedButton" skin="MW_Button" position="213 233 61 22" align="Bottom Right" name="WorldButton">
<Property key="ExpandDirection" value="Left"/>

Loading…
Cancel
Save