1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-19 21:53:51 +00:00

Save/load global map

This commit is contained in:
scrawl 2014-01-25 18:20:17 +01:00
parent e62bf8fca9
commit e0de76a6f7
12 changed files with 219 additions and 4 deletions

View file

@ -33,6 +33,8 @@ namespace OEngine
namespace ESM
{
struct Class;
class ESMReader;
class ESMWriter;
}
namespace MWWorld
@ -288,6 +290,9 @@ namespace MWBase
/// Clear all savegame-specific data
virtual void clear() = 0;
virtual void write (ESM::ESMWriter& writer) = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
};
}

View file

@ -584,4 +584,21 @@ namespace MWGui
MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0));
}
void MapWindow::write(ESM::ESMWriter &writer)
{
mGlobalMapRender->write(writer);
}
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
{
std::vector<std::pair<int, int> > exploredCells;
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);
if (cell && !cell->mName.empty())
addVisitedLocation(cell->mName, it->first, it->second);
}
}
}

View file

@ -8,6 +8,12 @@ namespace MWRender
class GlobalMap;
}
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace Loading
{
class Listener;
@ -95,6 +101,9 @@ namespace MWGui
/// Clear all savegame-specific data
void clear();
void write (ESM::ESMWriter& writer);
void readRecord (ESM::ESMReader& reader, int32_t type);
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);

View file

@ -1386,4 +1386,14 @@ namespace MWGui
mMap->clear();
}
void WindowManager::write(ESM::ESMWriter &writer)
{
mMap->write(writer);
}
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
{
mMap->readRecord(reader, type);
}
}

View file

@ -283,6 +283,9 @@ namespace MWGui
/// Clear all savegame-specific data
virtual void clear();
virtual void write (ESM::ESMWriter& writer);
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
private:
bool mConsoleOnlyScripts;

View file

@ -12,6 +12,8 @@
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/esm/globalmap.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -60,8 +62,6 @@ namespace MWRender
loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1));
loadingListener->setProgress(0);
mExploredBuffer.resize((mMaxX-mMinX+1) * (mMaxY-mMinY+1) * 4);
//if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png"))
if (1)
{
@ -231,4 +231,99 @@ namespace MWRender
mOverlayTexture->getBuffer()->blitFromMemory(pb);
}
void GlobalMap::write(ESM::ESMWriter &writer)
{
ESM::GlobalMap map;
map.mBounds.mMinX = mMinX;
map.mBounds.mMaxX = mMaxX;
map.mBounds.mMinY = mMinY;
map.mBounds.mMaxY = mMaxY;
Ogre::Image image;
mOverlayTexture->convertToImage(image);
Ogre::DataStreamPtr encoded = image.encode("png");
map.mImageData.resize(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)
{
if (type == ESM::REC_GMAP)
{
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;
Ogre::Image image;
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size()));
image.load(stream, "png");
int xLength = (bounds.mMaxX-bounds.mMinX+1);
int yLength = (bounds.mMaxY-bounds.mMinY+1);
// Size of one cell in image space
int cellImageSizeSrc = image.getWidth() / xLength;
if (int(image.getHeight() / yLength) != cellImageSizeSrc)
throw std::runtime_error("cell size must be quadratic");
// Determine which cells were explored by reading the image data
for (int x=0; x < xLength; ++x)
{
for (int y=0; y < yLength; ++y)
{
unsigned int imageX = (x) * cellImageSizeSrc;
// 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());
assert(imageY < image.getWidth());
if (image.getColourAt(imageX, imageY, 0).a > 0)
exploredCells.push_back(std::make_pair(x+bounds.mMinX,y+bounds.mMinY));
}
}
// If cell bounds of the currently loaded content and the loaded savegame do not match,
// we need to resize source/dest boxes to accommodate
// This means nonexisting cells will be dropped silently
int cellImageSizeDst = 24;
int leftDiff = (mMinX - bounds.mMinX);
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");
}
}
}

View file

@ -10,6 +10,12 @@ namespace Loading
class Listener;
}
namespace ESM
{
class ESMWriter;
class ESMReader;
}
namespace MWRender
{
@ -34,13 +40,15 @@ namespace MWRender
/// Clears the overlay
void clear();
void write (ESM::ESMWriter& writer);
void readRecord (ESM::ESMReader& reader, int32_t type, std::vector<std::pair<int, int> >& exploredCells);
private:
std::string mCacheDir;
std::vector< std::pair<int,int> > mExploredCells;
Ogre::TexturePtr mOverlayTexture;
std::vector<Ogre::uchar> mExploredBuffer;
int mWidth;
int mHeight;

View file

@ -174,6 +174,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
+ 1 // global map
);
writer.save (stream);
@ -185,6 +186,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
MWBase::Environment::get().getJournal()->write (writer);
MWBase::Environment::get().getWorld()->write (writer);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer);
MWBase::Environment::get().getWindowManager()->write(writer);
writer.close();
@ -243,6 +245,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val);
break;
case ESM::REC_GMAP:
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
break;
default:
// ignore invalid records

View file

@ -40,7 +40,7 @@ add_component_dir (esm
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap
)
add_component_dir (misc

View file

@ -91,6 +91,7 @@ enum RecNameInts
REC_PLAY = 0x59414c50,
REC_CSTA = 0x41545343,
REC_OBJE = 0x454a424f,
REC_GMAP = 0x50414d47,
// format 1
REC_FILT = 0x544C4946

View file

@ -0,0 +1,26 @@
#include "globalmap.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
unsigned int ESM::GlobalMap::sRecordId = ESM::REC_GMAP;
void ESM::GlobalMap::load (ESMReader &esm)
{
esm.getHNT(mBounds, "BNDS");
esm.getSubNameIs("DATA");
esm.getSubHeader();
mImageData.resize(esm.getSubSize());
esm.getExact(&mImageData[0], mImageData.size());
}
void ESM::GlobalMap::save (ESMWriter &esm) const
{
esm.writeHNT("BNDS", mBounds);
esm.startSubRecord("DATA");
esm.write(&mImageData[0], mImageData.size());
esm.endRecord("DATA");
}

View file

@ -0,0 +1,34 @@
#ifndef OPENMW_COMPONENTS_ESM_GLOBALMAP_H
#define OPENMW_COMPONENTS_ESM_GLOBALMAP_H
#include <vector>
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
///< \brief An image containing the explored areas on the global map.
struct GlobalMap
{
static unsigned int sRecordId;
// The minimum and maximum cell coordinates
struct Bounds
{
int mMinX, mMaxX, mMinY, mMaxY;
};
Bounds mBounds;
std::vector<char> mImageData;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif