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:
parent
e62bf8fca9
commit
e0de76a6f7
12 changed files with 219 additions and 4 deletions
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -91,6 +91,7 @@ enum RecNameInts
|
|||
REC_PLAY = 0x59414c50,
|
||||
REC_CSTA = 0x41545343,
|
||||
REC_OBJE = 0x454a424f,
|
||||
REC_GMAP = 0x50414d47,
|
||||
|
||||
// format 1
|
||||
REC_FILT = 0x544C4946
|
||||
|
|
26
components/esm/globalmap.cpp
Normal file
26
components/esm/globalmap.cpp
Normal 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");
|
||||
}
|
34
components/esm/globalmap.hpp
Normal file
34
components/esm/globalmap.hpp
Normal 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
|
Loading…
Reference in a new issue