Skip to content

Commit

Permalink
Store Map Format within Editor to allow any changes to be done direct…
Browse files Browse the repository at this point in the history
…ly within the format (ihhub#8180)
  • Loading branch information
ihhub authored Dec 26, 2023
1 parent 945f500 commit c155678
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 80 deletions.
44 changes: 26 additions & 18 deletions src/fheroes2/editor/editor_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
#include "interface_radar.h"
#include "localevent.h"
#include "map_format_helper.h"
#include "map_format_info.h"
#include "map_object_info.h"
#include "maps.h"
#include "maps_tiles.h"
Expand Down Expand Up @@ -294,16 +293,10 @@ namespace Interface
continue;
}

Maps::Map_Format::MapFormat map;
if ( !Maps::saveMapInEditor( map ) ) {
fheroes2::showStandardTextMessage( _( "Warning!" ), "Cannot convert a map into the required map format.", Dialog::OK );
continue;
}

map.name = fileName;
map.description = "Put a real description here.";
_mapFormat.name = fileName;
_mapFormat.description = "Put a real description here.";

if ( !Maps::Map_Format::saveMap( System::concatPath( mapDirectory, fileName + ".fh2m" ), map ) ) {
if ( !Maps::Map_Format::saveMap( System::concatPath( mapDirectory, fileName + ".fh2m" ), _mapFormat ) ) {
fheroes2::showStandardTextMessage( _( "Warning!" ), "Failed to save the map.", Dialog::OK );
}
}
Expand Down Expand Up @@ -451,14 +444,14 @@ namespace Interface
if ( isCursorOverGamearea && _editorPanel.getBrushArea().width == 0 ) {
if ( _editorPanel.isTerrainEdit() ) {
// Fill the selected area in terrain edit mode.
const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

const int groundId = _editorPanel.selectedGroundType();
Maps::setTerrainOnTiles( _selectedTile, _tileUnderCursor, groundId );
}
else if ( _editorPanel.isEraseMode() ) {
// Erase objects in the selected area.
const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

Maps::eraseObjectsOnTiles( _selectedTile, _tileUnderCursor, _editorPanel.getEraseTypes() );
}
Expand Down Expand Up @@ -646,7 +639,7 @@ namespace Interface

const int groundId = _editorPanel.selectedGroundType();

const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

if ( brushSize.width > 0 ) {
const fheroes2::Point indices = getBrushAreaIndicies( brushSize, tileIndex );
Expand All @@ -665,14 +658,14 @@ namespace Interface
_redraw |= mapUpdateFlags;
}
else if ( _editorPanel.isRoadDraw() ) {
const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

if ( Maps::updateRoadOnTile( tile, true ) ) {
_redraw |= mapUpdateFlags;
}
}
else if ( _editorPanel.isStreamDraw() ) {
const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

if ( Maps::updateStreamOnTile( tile, true ) ) {
_redraw |= mapUpdateFlags;
Expand All @@ -682,7 +675,7 @@ namespace Interface
const fheroes2::Rect brushSize = _editorPanel.getBrushArea();
assert( brushSize.width == brushSize.height );

const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

if ( brushSize.width > 1 ) {
const fheroes2::Point indices = getBrushAreaIndicies( brushSize, tileIndex );
Expand Down Expand Up @@ -889,7 +882,7 @@ namespace Interface
return;
}

const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

Maps::setObjectOnTile( tile, basementObjectInfo );

Expand Down Expand Up @@ -931,10 +924,25 @@ namespace Interface

void EditorInterface::setObjectOnTile( Maps::Tiles & tile, const Maps::ObjectInfo & objectInfo )
{
const fheroes2::ActionCreator action( _historyManager );
const fheroes2::ActionCreator action( _historyManager, _mapFormat );

Maps::setObjectOnTile( tile, objectInfo );

_redraw |= mapUpdateFlags;
}

bool EditorInterface::loadMap( const std::string & filePath )
{
if ( !Maps::Map_Format::loadMap( filePath, _mapFormat ) ) {
fheroes2::showStandardTextMessage( _( "Warning!" ), "Failed to load the map.", Dialog::OK );
return false;
}

if ( !Maps::readMapInEditor( _mapFormat ) ) {
fheroes2::showStandardTextMessage( _( "Warning!" ), "Failed to read the map.", Dialog::OK );
return false;
}

return true;
}
}
6 changes: 6 additions & 0 deletions src/fheroes2/editor/editor_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@

#include <cstdint>
#include <functional>
#include <string>

#include "editor_interface_panel.h"
#include "game_mode.h"
#include "history_manager.h"
#include "interface_base.h"
#include "map_format_info.h"

namespace Maps
{
Expand Down Expand Up @@ -83,6 +85,8 @@ namespace Interface
_cursorUpdater = cursorUpdater;
}

bool loadMap( const std::string & filePath );

private:
EditorInterface()
: BaseInterface( true )
Expand All @@ -103,5 +107,7 @@ namespace Interface
std::function<void( const int32_t )> _cursorUpdater;

fheroes2::HistoryManager _historyManager;

Maps::Map_Format::MapFormat _mapFormat;
};
}
12 changes: 2 additions & 10 deletions src/fheroes2/editor/editor_mainmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
#include "icn.h"
#include "localevent.h"
#include "logging.h"
#include "map_format_helper.h"
#include "map_format_info.h"
#include "maps.h"
#include "maps_fileinfo.h"
#include "math_base.h"
Expand Down Expand Up @@ -311,14 +309,8 @@ namespace Editor
return fheroes2::GameMode::EDITOR_MAIN_MENU;
}

Maps::Map_Format::MapFormat map;
if ( !Maps::Map_Format::loadMap( fileInfo->filename, map ) ) {
fheroes2::showStandardTextMessage( _( "Warning!" ), "Failed to load the map.", Dialog::OK );
return fheroes2::GameMode::CANCEL;
}

if ( !Maps::readMapInEditor( map ) ) {
fheroes2::showStandardTextMessage( _( "Warning!" ), "Failed to read the map.", Dialog::OK );
Interface::EditorInterface & editorInterface = Interface::EditorInterface::Get();
if ( !editorInterface.loadMap( fileInfo->filename ) ) {
return fheroes2::GameMode::CANCEL;
}

Expand Down
89 changes: 38 additions & 51 deletions src/fheroes2/editor/history_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,93 +22,80 @@

#include <cassert>
#include <cstdint>
#include <vector>

#include "maps_tiles.h"
#include "world.h"
#include "map_format_helper.h"
#include "map_format_info.h"
#include "world_object_uid.h"

namespace
{
// This class holds 2 copies of MapFormat objects:
// - one copy before the action
// - one copy after the action
// Ideally, we need to store only difference between before and after an action
// but for simplification purposes we chose this way.
// TODO: optimize this class (if possible) to store less data.
class MapAction : public fheroes2::Action
{
public:
MapAction()
: _latestObjectUIDBefore( Maps::getLastObjectUID() )
explicit MapAction( Maps::Map_Format::MapFormat & mapFormat )
: _mapFormat( mapFormat )
, _latestObjectUIDBefore( Maps::getLastObjectUID() )
{
const int32_t size = world.w() * world.h();
_before.reserve( size );

for ( int32_t i = 0; i < size; ++i ) {
_before.push_back( world.GetTiles( i ) );
if ( !Maps::saveMapInEditor( _mapFormat ) ) {
// If this assertion blows up then something is really wrong with the Editor.
assert( 0 );
}

_beforeMapFormat = _mapFormat;
}

bool prepare()
{
std::vector<Maps::Tiles> temp;
std::swap( temp, _before );

const int32_t size = world.w() * world.h();
if ( size != static_cast<int32_t>( temp.size() ) ) {
if ( !Maps::saveMapInEditor( _mapFormat ) ) {
// If this assertion blows up then something is really wrong with the Editor.
assert( 0 );
return false;
}

_latestObjectUIDAfter = Maps::getLastObjectUID();

bool foundDifference = false;

for ( int32_t i = 0; i < size; ++i ) {
if ( temp[i] != world.GetTiles( i ) ) {
_before.push_back( std::move( temp[i] ) );
_after.push_back( world.GetTiles( i ) );
foundDifference = true;
}
}

assert( _before.size() == _after.size() );
_afterMapFormat = _mapFormat;

// TODO: logically if the last object UID has been changed then we should mark the difference.

return foundDifference;
return true;
}

bool redo() override
{
for ( const Maps::Tiles & tile : _after ) {
// Copy operator is disabled for Maps::Tiles class.
// This is done to avoid any tile copy made by a developer during the gameplay like map initialization.
// Therefore, such a trick is required to assign a new value.
Maps::Tiles temp{ tile };

world.GetTiles( tile.GetIndex() ) = std::move( temp );
_mapFormat = _afterMapFormat;
if ( !Maps::readMapInEditor( _mapFormat ) ) {
// If this assertion blows up then something is really wrong with the Editor.
assert( 0 );
return false;
}

Maps::setLastObjectUID( _latestObjectUIDAfter );

return ( !_after.empty() );
return true;
}

bool undo() override
{
for ( const Maps::Tiles & tile : _before ) {
// Copy operator is disabled for Maps::Tiles class.
// This is done to avoid any tile copy made by a developer during the gameplay like map initialization.
// Therefore, such a trick is required to assign a new value.
Maps::Tiles temp{ tile };

world.GetTiles( tile.GetIndex() ) = std::move( temp );
_mapFormat = _beforeMapFormat;
if ( !Maps::readMapInEditor( _mapFormat ) ) {
// If this assertion blows up then something is really wrong with the Editor.
assert( 0 );
return false;
}

Maps::setLastObjectUID( _latestObjectUIDBefore );

return ( !_before.empty() );
return true;
}

private:
std::vector<Maps::Tiles> _before;
std::vector<Maps::Tiles> _after;
Maps::Map_Format::MapFormat & _mapFormat;

Maps::Map_Format::MapFormat _beforeMapFormat;
Maps::Map_Format::MapFormat _afterMapFormat;

const uint32_t _latestObjectUIDBefore{ 0 };
uint32_t _latestObjectUIDAfter{ 0 };
Expand All @@ -117,10 +104,10 @@ namespace

namespace fheroes2
{
ActionCreator::ActionCreator( HistoryManager & manager )
ActionCreator::ActionCreator( HistoryManager & manager, Maps::Map_Format::MapFormat & mapFormat )
: _manager( manager )
{
_action = std::make_unique<MapAction>();
_action = std::make_unique<MapAction>( mapFormat );
}

ActionCreator::~ActionCreator()
Expand Down
7 changes: 6 additions & 1 deletion src/fheroes2/editor/history_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
#include <memory>
#include <utility>

namespace Maps::Map_Format
{
struct MapFormat;
}

namespace fheroes2
{
class HistoryManager;
Expand All @@ -45,7 +50,7 @@ namespace fheroes2
class ActionCreator
{
public:
explicit ActionCreator( HistoryManager & manager );
explicit ActionCreator( HistoryManager & manager, Maps::Map_Format::MapFormat & mapFormat );

~ActionCreator();

Expand Down
11 changes: 11 additions & 0 deletions src/fheroes2/maps/map_format_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ namespace Maps

assert( static_cast<size_t>( world.w() ) * world.h() == map.tiles.size() );

// We must clear all tiles before writing something on them.
for ( size_t i = 0; i < map.tiles.size(); ++i ) {
auto & tile = world.GetTiles( static_cast<int32_t>( i ) );
tile = {};

tile.setIndex( static_cast<int32_t>( i ) );
}

for ( size_t i = 0; i < map.tiles.size(); ++i ) {
readTile( world.GetTiles( static_cast<int32_t>( i ) ), map.tiles[i] );
}
Expand Down Expand Up @@ -93,6 +101,9 @@ namespace Maps

void writeTile( const Tiles & tile, Map_Format::TileInfo & info )
{
// Clear all data before writing new one.
info = {};

// Only bottom layer addons / objects parts can be stored within the map format.
ObjectGroup group;
uint32_t index;
Expand Down

0 comments on commit c155678

Please sign in to comment.