mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-07-21 08:44:06 +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
|
namespace ESM
|
||||||
{
|
{
|
||||||
struct Class;
|
struct Class;
|
||||||
|
class ESMReader;
|
||||||
|
class ESMWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
|
@ -288,6 +290,9 @@ namespace MWBase
|
||||||
|
|
||||||
/// Clear all savegame-specific data
|
/// Clear all savegame-specific data
|
||||||
virtual void clear() = 0;
|
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));
|
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;
|
class GlobalMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
class ESMWriter;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Loading
|
namespace Loading
|
||||||
{
|
{
|
||||||
class Listener;
|
class Listener;
|
||||||
|
@ -95,6 +101,9 @@ namespace MWGui
|
||||||
/// Clear all savegame-specific data
|
/// Clear all savegame-specific data
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
void write (ESM::ESMWriter& writer);
|
||||||
|
void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
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 onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||||
|
|
|
@ -1386,4 +1386,14 @@ namespace MWGui
|
||||||
mMap->clear();
|
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
|
/// Clear all savegame-specific data
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
||||||
|
virtual void write (ESM::ESMWriter& writer);
|
||||||
|
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mConsoleOnlyScripts;
|
bool mConsoleOnlyScripts;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#include <components/loadinglistener/loadinglistener.hpp>
|
#include <components/loadinglistener/loadinglistener.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/globalmap.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
@ -60,8 +62,6 @@ namespace MWRender
|
||||||
loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1));
|
loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1));
|
||||||
loadingListener->setProgress(0);
|
loadingListener->setProgress(0);
|
||||||
|
|
||||||
mExploredBuffer.resize((mMaxX-mMinX+1) * (mMaxY-mMinY+1) * 4);
|
|
||||||
|
|
||||||
//if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png"))
|
//if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png"))
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
|
@ -231,4 +231,99 @@ namespace MWRender
|
||||||
|
|
||||||
mOverlayTexture->getBuffer()->blitFromMemory(pb);
|
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;
|
class Listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMWriter;
|
||||||
|
class ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -34,13 +40,15 @@ namespace MWRender
|
||||||
/// Clears the overlay
|
/// Clears the overlay
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
void write (ESM::ESMWriter& writer);
|
||||||
|
void readRecord (ESM::ESMReader& reader, int32_t type, std::vector<std::pair<int, int> >& exploredCells);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mCacheDir;
|
std::string mCacheDir;
|
||||||
|
|
||||||
std::vector< std::pair<int,int> > mExploredCells;
|
std::vector< std::pair<int,int> > mExploredCells;
|
||||||
|
|
||||||
Ogre::TexturePtr mOverlayTexture;
|
Ogre::TexturePtr mOverlayTexture;
|
||||||
std::vector<Ogre::uchar> mExploredBuffer;
|
|
||||||
|
|
||||||
int mWidth;
|
int mWidth;
|
||||||
int mHeight;
|
int mHeight;
|
||||||
|
|
|
@ -174,6 +174,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
||||||
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
||||||
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
||||||
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
||||||
|
+ 1 // global map
|
||||||
);
|
);
|
||||||
|
|
||||||
writer.save (stream);
|
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().getJournal()->write (writer);
|
||||||
MWBase::Environment::get().getWorld()->write (writer);
|
MWBase::Environment::get().getWorld()->write (writer);
|
||||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer);
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer);
|
||||||
|
MWBase::Environment::get().getWindowManager()->write(writer);
|
||||||
|
|
||||||
writer.close();
|
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);
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ESM::REC_GMAP:
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
// ignore invalid records
|
// ignore invalid records
|
||||||
|
|
|
@ -40,7 +40,7 @@ add_component_dir (esm
|
||||||
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
|
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
|
||||||
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
|
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
|
||||||
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
|
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
|
add_component_dir (misc
|
||||||
|
|
|
@ -91,6 +91,7 @@ enum RecNameInts
|
||||||
REC_PLAY = 0x59414c50,
|
REC_PLAY = 0x59414c50,
|
||||||
REC_CSTA = 0x41545343,
|
REC_CSTA = 0x41545343,
|
||||||
REC_OBJE = 0x454a424f,
|
REC_OBJE = 0x454a424f,
|
||||||
|
REC_GMAP = 0x50414d47,
|
||||||
|
|
||||||
// format 1
|
// format 1
|
||||||
REC_FILT = 0x544C4946
|
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