mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 13:09:40 +00:00
Fixes #1297: Store global map markers in savegame
This commit is contained in:
parent
f921f2e7db
commit
30666f2cce
7 changed files with 104 additions and 91 deletions
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include "../mwrender/globalmap.hpp"
|
#include "../mwrender/globalmap.hpp"
|
||||||
|
|
||||||
|
#include "../components/esm/globalmap.hpp"
|
||||||
|
|
||||||
#include "widgets.hpp"
|
#include "widgets.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
|
@ -436,7 +438,6 @@ namespace MWGui
|
||||||
worldY * mGlobalMapRender->getHeight()+6,
|
worldY * mGlobalMapRender->getHeight()+6,
|
||||||
12, 12);
|
12, 12);
|
||||||
|
|
||||||
|
|
||||||
static int _counter=0;
|
static int _counter=0;
|
||||||
MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage",
|
MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage",
|
||||||
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter));
|
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter));
|
||||||
|
@ -452,6 +453,11 @@ namespace MWGui
|
||||||
markerWidget->setUserString("ToolTipType", "Layout");
|
markerWidget->setUserString("ToolTipType", "Layout");
|
||||||
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
||||||
markerWidget->setUserString("Caption_TextOneLine", name);
|
markerWidget->setUserString("Caption_TextOneLine", name);
|
||||||
|
|
||||||
|
CellId cell;
|
||||||
|
cell.first = x;
|
||||||
|
cell.second = y;
|
||||||
|
mMarkers.push_back(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapWindow::cellExplored(int x, int y)
|
void MapWindow::cellExplored(int x, int y)
|
||||||
|
@ -580,6 +586,7 @@ namespace MWGui
|
||||||
|
|
||||||
void MapWindow::clear()
|
void MapWindow::clear()
|
||||||
{
|
{
|
||||||
|
mMarkers.clear();
|
||||||
mGlobalMapRender->clear();
|
mGlobalMapRender->clear();
|
||||||
|
|
||||||
while (mEventBoxGlobal->getChildCount())
|
while (mEventBoxGlobal->getChildCount())
|
||||||
|
@ -590,19 +597,31 @@ namespace MWGui
|
||||||
|
|
||||||
void MapWindow::write(ESM::ESMWriter &writer)
|
void MapWindow::write(ESM::ESMWriter &writer)
|
||||||
{
|
{
|
||||||
mGlobalMapRender->write(writer);
|
ESM::GlobalMap map;
|
||||||
|
mGlobalMapRender->write(map);
|
||||||
|
|
||||||
|
map.mMarkers = mMarkers;
|
||||||
|
|
||||||
|
writer.startRecord(ESM::REC_GMAP);
|
||||||
|
map.save(writer);
|
||||||
|
writer.endRecord(ESM::REC_GMAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
|
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<int, int> > exploredCells;
|
if (type == ESM::REC_GMAP)
|
||||||
mGlobalMapRender->readRecord(reader, type, exploredCells);
|
|
||||||
|
|
||||||
for (std::vector<std::pair<int, int> >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it)
|
|
||||||
{
|
{
|
||||||
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(it->first, it->second);
|
ESM::GlobalMap map;
|
||||||
if (cell && !cell->mName.empty())
|
map.load(reader);
|
||||||
addVisitedLocation(cell->mName, it->first, it->second);
|
|
||||||
|
mGlobalMapRender->read(map);
|
||||||
|
|
||||||
|
for (std::vector<ESM::GlobalMap::CellId>::iterator it = map.mMarkers.begin(); it != map.mMarkers.end(); ++it)
|
||||||
|
{
|
||||||
|
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(it->first, it->second);
|
||||||
|
if (cell && !cell->mName.empty())
|
||||||
|
addVisitedLocation(cell->mName, it->first, it->second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,9 @@ namespace MWGui
|
||||||
bool mChanged;
|
bool mChanged;
|
||||||
bool mFogOfWar;
|
bool mFogOfWar;
|
||||||
|
|
||||||
|
typedef std::pair<int, int> CellId;
|
||||||
|
std::vector<CellId> mMarkers;
|
||||||
|
|
||||||
std::vector<MyGUI::ImageBox*> mMapWidgets;
|
std::vector<MyGUI::ImageBox*> mMapWidgets;
|
||||||
std::vector<MyGUI::ImageBox*> mFogWidgets;
|
std::vector<MyGUI::ImageBox*> mFogWidgets;
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,7 @@ namespace MWGui
|
||||||
MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
|
MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
|
||||||
|
|
||||||
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
|
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
|
||||||
SDL_ShowCursor(false);
|
//SDL_ShowCursor(false);
|
||||||
|
|
||||||
mCursorManager->setEnabled(true);
|
mCursorManager->setEnabled(true);
|
||||||
|
|
||||||
|
|
|
@ -231,9 +231,8 @@ namespace MWRender
|
||||||
mOverlayTexture->getBuffer()->blitFromMemory(pb);
|
mOverlayTexture->getBuffer()->blitFromMemory(pb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalMap::write(ESM::ESMWriter &writer)
|
void GlobalMap::write(ESM::GlobalMap& map)
|
||||||
{
|
{
|
||||||
ESM::GlobalMap map;
|
|
||||||
map.mBounds.mMinX = mMinX;
|
map.mBounds.mMinX = mMinX;
|
||||||
map.mBounds.mMaxX = mMaxX;
|
map.mBounds.mMaxX = mMaxX;
|
||||||
map.mBounds.mMinY = mMinY;
|
map.mBounds.mMinY = mMinY;
|
||||||
|
@ -244,95 +243,68 @@ namespace MWRender
|
||||||
Ogre::DataStreamPtr encoded = image.encode("png");
|
Ogre::DataStreamPtr encoded = image.encode("png");
|
||||||
map.mImageData.resize(encoded->size());
|
map.mImageData.resize(encoded->size());
|
||||||
encoded->read(&map.mImageData[0], encoded->size());
|
encoded->read(&map.mImageData[0], encoded->size());
|
||||||
|
|
||||||
writer.startRecord(ESM::REC_GMAP);
|
|
||||||
map.save(writer);
|
|
||||||
writer.endRecord(ESM::REC_GMAP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalMap::readRecord(ESM::ESMReader &reader, int32_t type, std::vector<std::pair<int, int> >& exploredCells)
|
void GlobalMap::read(ESM::GlobalMap& map)
|
||||||
{
|
{
|
||||||
if (type == ESM::REC_GMAP)
|
const ESM::GlobalMap::Bounds& bounds = map.mBounds;
|
||||||
{
|
|
||||||
ESM::GlobalMap map;
|
|
||||||
map.load(reader);
|
|
||||||
|
|
||||||
const ESM::GlobalMap::Bounds& bounds = map.mBounds;
|
if (bounds.mMaxX-bounds.mMinX <= 0)
|
||||||
|
return;
|
||||||
|
if (bounds.mMaxY-bounds.mMinY <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (bounds.mMaxX-bounds.mMinX <= 0)
|
if (bounds.mMinX > bounds.mMaxX
|
||||||
return;
|
|| bounds.mMinY > bounds.mMaxY)
|
||||||
if (bounds.mMaxY-bounds.mMinY <= 0)
|
throw std::runtime_error("invalid map bounds");
|
||||||
return;
|
|
||||||
|
|
||||||
if (bounds.mMinX > bounds.mMaxX
|
Ogre::Image image;
|
||||||
|| bounds.mMinY > bounds.mMaxY)
|
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size()));
|
||||||
throw std::runtime_error("invalid map bounds");
|
image.load(stream, "png");
|
||||||
|
|
||||||
Ogre::Image image;
|
int xLength = (bounds.mMaxX-bounds.mMinX+1);
|
||||||
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size()));
|
int yLength = (bounds.mMaxY-bounds.mMinY+1);
|
||||||
image.load(stream, "png");
|
|
||||||
|
|
||||||
int xLength = (bounds.mMaxX-bounds.mMinX+1);
|
// Size of one cell in image space
|
||||||
int yLength = (bounds.mMaxY-bounds.mMinY+1);
|
int cellImageSizeSrc = image.getWidth() / xLength;
|
||||||
|
if (int(image.getHeight() / yLength) != cellImageSizeSrc)
|
||||||
|
throw std::runtime_error("cell size must be quadratic");
|
||||||
|
|
||||||
// Size of one cell in image space
|
// If cell bounds of the currently loaded content and the loaded savegame do not match,
|
||||||
int cellImageSizeSrc = image.getWidth() / xLength;
|
// we need to resize source/dest boxes to accommodate
|
||||||
if (int(image.getHeight() / yLength) != cellImageSizeSrc)
|
// This means nonexisting cells will be dropped silently
|
||||||
throw std::runtime_error("cell size must be quadratic");
|
int cellImageSizeDst = 24;
|
||||||
|
|
||||||
// Determine which cells were explored by reading the image data
|
// Completely off-screen? -> no need to blit anything
|
||||||
for (int x=0; x < xLength; ++x)
|
if (bounds.mMaxX < mMinX
|
||||||
{
|
|| bounds.mMaxY < mMinY
|
||||||
for (int y=0; y < yLength; ++y)
|
|| bounds.mMinX > mMaxX
|
||||||
{
|
|| bounds.mMinY > mMaxY)
|
||||||
unsigned int imageX = (x) * cellImageSizeSrc;
|
return;
|
||||||
// NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is
|
|
||||||
unsigned int imageY = (yLength - (y + 1)) * cellImageSizeSrc;
|
|
||||||
|
|
||||||
assert(imageX < image.getWidth());
|
int leftDiff = (mMinX - bounds.mMinX);
|
||||||
assert(imageY < image.getHeight());
|
int topDiff = (bounds.mMaxY - mMaxY);
|
||||||
|
int rightDiff = (bounds.mMaxX - mMaxX);
|
||||||
|
int bottomDiff = (mMinY - bounds.mMinY);
|
||||||
|
Ogre::Image::Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc),
|
||||||
|
std::max(0, topDiff * cellImageSizeSrc),
|
||||||
|
std::min(image.getWidth(), image.getWidth() - rightDiff * cellImageSizeSrc),
|
||||||
|
std::min(image.getHeight(), image.getHeight() - bottomDiff * cellImageSizeSrc));
|
||||||
|
|
||||||
if (image.getColourAt(imageX, imageY, 0).a > 0)
|
Ogre::Image::Box destBox ( std::max(0, -leftDiff * cellImageSizeDst),
|
||||||
exploredCells.push_back(std::make_pair(x+bounds.mMinX,y+bounds.mMinY));
|
std::max(0, -topDiff * cellImageSizeDst),
|
||||||
}
|
std::min(mOverlayTexture->getWidth(), mOverlayTexture->getWidth() + rightDiff * cellImageSizeDst),
|
||||||
}
|
std::min(mOverlayTexture->getHeight(), mOverlayTexture->getHeight() + bottomDiff * cellImageSizeDst));
|
||||||
|
|
||||||
// If cell bounds of the currently loaded content and the loaded savegame do not match,
|
// Looks like there is no interface for blitting from memory with src/dst boxes.
|
||||||
// we need to resize source/dest boxes to accommodate
|
// So we create a temporary texture for blitting.
|
||||||
// This means nonexisting cells will be dropped silently
|
Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().createManual("@temp",
|
||||||
int cellImageSizeDst = 24;
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, image.getWidth(),
|
||||||
|
image.getHeight(), 0, Ogre::PF_A8B8G8R8);
|
||||||
|
tex->loadImage(image);
|
||||||
|
|
||||||
// Completely off-screen? -> no need to blit anything
|
mOverlayTexture->getBuffer()->blit(tex->getBuffer(), srcBox, destBox);
|
||||||
if (bounds.mMaxX < mMinX
|
|
||||||
|| bounds.mMaxY < mMinY
|
|
||||||
|| bounds.mMinX > mMaxX
|
|
||||||
|| bounds.mMinY > mMaxY)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int leftDiff = (mMinX - bounds.mMinX);
|
Ogre::TextureManager::getSingleton().remove("@temp");
|
||||||
int topDiff = (bounds.mMaxY - mMaxY);
|
|
||||||
int rightDiff = (bounds.mMaxX - mMaxX);
|
|
||||||
int bottomDiff = (mMinY - bounds.mMinY);
|
|
||||||
Ogre::Image::Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc),
|
|
||||||
std::max(0, topDiff * cellImageSizeSrc),
|
|
||||||
std::min(image.getWidth(), image.getWidth() - rightDiff * cellImageSizeSrc),
|
|
||||||
std::min(image.getHeight(), image.getHeight() - bottomDiff * cellImageSizeSrc));
|
|
||||||
|
|
||||||
Ogre::Image::Box destBox ( std::max(0, -leftDiff * cellImageSizeDst),
|
|
||||||
std::max(0, -topDiff * cellImageSizeDst),
|
|
||||||
std::min(mOverlayTexture->getWidth(), mOverlayTexture->getWidth() + rightDiff * cellImageSizeDst),
|
|
||||||
std::min(mOverlayTexture->getHeight(), mOverlayTexture->getHeight() + bottomDiff * cellImageSizeDst));
|
|
||||||
|
|
||||||
// Looks like there is no interface for blitting from memory with src/dst boxes.
|
|
||||||
// So we create a temporary texture for blitting.
|
|
||||||
Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().createManual("@temp",
|
|
||||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, image.getWidth(),
|
|
||||||
image.getHeight(), 0, Ogre::PF_A8B8G8R8);
|
|
||||||
tex->loadImage(image);
|
|
||||||
|
|
||||||
mOverlayTexture->getBuffer()->blit(tex->getBuffer(), srcBox, destBox);
|
|
||||||
|
|
||||||
Ogre::TextureManager::getSingleton().remove("@temp");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,7 @@ namespace Loading
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
class ESMWriter;
|
class GlobalMap;
|
||||||
class ESMReader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
|
@ -40,8 +39,8 @@ namespace MWRender
|
||||||
/// Clears the overlay
|
/// Clears the overlay
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void write (ESM::ESMWriter& writer);
|
void write (ESM::GlobalMap& map);
|
||||||
void readRecord (ESM::ESMReader& reader, int32_t type, std::vector<std::pair<int, int> >& exploredCells);
|
void read (ESM::GlobalMap& map);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mCacheDir;
|
std::string mCacheDir;
|
||||||
|
|
|
@ -14,6 +14,15 @@ void ESM::GlobalMap::load (ESMReader &esm)
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
mImageData.resize(esm.getSubSize());
|
mImageData.resize(esm.getSubSize());
|
||||||
esm.getExact(&mImageData[0], mImageData.size());
|
esm.getExact(&mImageData[0], mImageData.size());
|
||||||
|
|
||||||
|
while (esm.isNextSub("MRK_"))
|
||||||
|
{
|
||||||
|
esm.getSubHeader();
|
||||||
|
CellId cell;
|
||||||
|
esm.getT(cell.first);
|
||||||
|
esm.getT(cell.second);
|
||||||
|
mMarkers.push_back(cell);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::GlobalMap::save (ESMWriter &esm) const
|
void ESM::GlobalMap::save (ESMWriter &esm) const
|
||||||
|
@ -23,4 +32,12 @@ void ESM::GlobalMap::save (ESMWriter &esm) const
|
||||||
esm.startSubRecord("DATA");
|
esm.startSubRecord("DATA");
|
||||||
esm.write(&mImageData[0], mImageData.size());
|
esm.write(&mImageData[0], mImageData.size());
|
||||||
esm.endRecord("DATA");
|
esm.endRecord("DATA");
|
||||||
|
|
||||||
|
for (std::vector<CellId>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it)
|
||||||
|
{
|
||||||
|
esm.startSubRecord("MRK_");
|
||||||
|
esm.writeT(it->first);
|
||||||
|
esm.writeT(it->second);
|
||||||
|
esm.endRecord("MRK_");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ namespace ESM
|
||||||
|
|
||||||
std::vector<char> mImageData;
|
std::vector<char> mImageData;
|
||||||
|
|
||||||
|
typedef std::pair<int, int> CellId;
|
||||||
|
std::vector<CellId> mMarkers;
|
||||||
|
|
||||||
void load (ESMReader &esm);
|
void load (ESMReader &esm);
|
||||||
void save (ESMWriter &esm) const;
|
void save (ESMWriter &esm) const;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue