diff --git a/src/fheroes2/editor/editor_interface.cpp b/src/fheroes2/editor/editor_interface.cpp index 9c8147480f3..7820c54a67f 100644 --- a/src/fheroes2/editor/editor_interface.cpp +++ b/src/fheroes2/editor/editor_interface.cpp @@ -48,6 +48,7 @@ #include "maps_tiles.h" #include "maps_tiles_helper.h" #include "math_base.h" +#include "monster.h" #include "mp2.h" #include "screen.h" #include "settings.h" @@ -586,6 +587,20 @@ namespace Interface } } } + else if ( _editorPanel.isMonsterSettingMode() ) { + if ( tile.isWater() ) { + fheroes2::showStandardTextMessage( _( "Monster" ), _( "Monsters cannot be placed on water." ), Dialog::OK ); + } + else if ( !Maps::isClearGround( tile ) ) { + fheroes2::showStandardTextMessage( _( "Monster" ), _( "Choose a tile which does not contain any objects." ), Dialog::OK ); + } + else if ( Monster{ _editorPanel.getMonsterId() }.isValid() ) { + const fheroes2::ActionCreator action( _historyManager ); + + Maps::setMonsterOnTile( tile, _editorPanel.getMonsterId(), 0 ); + _redraw |= mapUpdateFlags; + } + } } void EditorInterface::mouseCursorAreaPressRight( const int32_t tileIndex ) const @@ -623,8 +638,13 @@ namespace Interface } } - void EditorInterface::updateCursor( const int32_t /*tileIndex*/ ) + void EditorInterface::updateCursor( const int32_t tileIndex ) { - Cursor::Get().SetThemes( Cursor::POINTER ); + if ( _cursorUpdater && tileIndex >= 0 ) { + _cursorUpdater( tileIndex ); + } + else { + Cursor::Get().SetThemes( Cursor::POINTER ); + } } } diff --git a/src/fheroes2/editor/editor_interface.h b/src/fheroes2/editor/editor_interface.h index 8a904dfb5d2..669058513b0 100644 --- a/src/fheroes2/editor/editor_interface.h +++ b/src/fheroes2/editor/editor_interface.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include "editor_interface_panel.h" #include "game_mode.h" @@ -71,6 +72,11 @@ namespace Interface void updateCursor( const int32_t tileIndex ) override; + void setCursorUpdater( const std::function & cursorUpdater ) + { + _cursorUpdater = cursorUpdater; + } + private: EditorInterface() : _editorPanel( *this ) @@ -83,6 +89,8 @@ namespace Interface int32_t _selectedTile{ -1 }; int32_t _tileUnderCursor{ -1 }; + std::function _cursorUpdater; + fheroes2::HistoryManager _historyManager; }; } diff --git a/src/fheroes2/editor/editor_interface_panel.cpp b/src/fheroes2/editor/editor_interface_panel.cpp index 288afa4881e..6c96debadcb 100644 --- a/src/fheroes2/editor/editor_interface_panel.cpp +++ b/src/fheroes2/editor/editor_interface_panel.cpp @@ -25,7 +25,9 @@ #include #include "agg_image.h" +#include "cursor.h" #include "dialog.h" +#include "dialog_selectitems.h" #include "dialog_system_options.h" #include "editor_interface.h" #include "ground.h" @@ -74,7 +76,7 @@ namespace Interface int32_t EditorPanel::getBrushSize() const { // Roads and streams are placed using only 1x1 brush. - if ( _selectedInstrument == Instrument::STREAM || _selectedInstrument == Instrument::ROAD ) { + if ( _selectedInstrument == Instrument::STREAM || _selectedInstrument == Instrument::ROAD || isMonsterSettingMode() ) { return 1; } @@ -301,6 +303,10 @@ namespace Interface if ( le.MousePressLeft( _instrumentButtonsRect[i] ) ) { if ( _instrumentButtons[i].drawOnPress() ) { _selectedInstrument = static_cast( i ); + + // Reset cursor updater since this UI element was clicked. + _interface.setCursorUpdater( {} ); + setRedraw(); } } @@ -399,6 +405,10 @@ namespace Interface for ( size_t i = 0; i < _objectButtonsRect.size(); ++i ) { if ( ( _selectedObject != i ) && le.MousePressLeft( _objectButtonsRect[i] ) ) { _selectedObject = static_cast( i ); + + // Reset cursor updater since this UI element was clicked. + _interface.setCursorUpdater( {} ); + setRedraw(); // There is no need to continue the loop as only one button can be pressed at one moment. @@ -432,6 +442,23 @@ namespace Interface else if ( le.MousePressRight( _objectButtonsRect[Brush::TREASURES] ) ) { fheroes2::showStandardTextMessage( _getObjectTypeName( Brush::TREASURES ), _( "Used to place\na resource or treasure." ), Dialog::ZERO ); } + else if ( le.MouseClickLeft( _objectButtonsRect[Brush::MONSTERS] ) ) { + _monsterId = Monster::UNKNOWN; + + const Monster monster = Dialog::selectMonster(); + if ( monster.GetID() != Monster::UNKNOWN ) { + _monsterId = monster.GetID(); + + _interface.setCursorUpdater( [monster = monster]( const int32_t /*tileIndex*/ ) { + const fheroes2::Sprite & image = fheroes2::AGG::GetICN( ICN::MONS32, monster.GetSpriteIndex() ); + + Cursor::Get().setCustomImage( image, { -image.width() / 2, -image.height() / 2 } ); + } ); + + _interface.updateCursor( 0 ); + return res; + } + } } le.MousePressLeft( _rectMagnify ) ? _buttonMagnify.drawOnPress() : _buttonMagnify.drawOnRelease(); diff --git a/src/fheroes2/editor/editor_interface_panel.h b/src/fheroes2/editor/editor_interface_panel.h index 7150f43cc48..56527ecbdf8 100644 --- a/src/fheroes2/editor/editor_interface_panel.h +++ b/src/fheroes2/editor/editor_interface_panel.h @@ -26,6 +26,7 @@ #include "game_mode.h" #include "ground.h" #include "math_base.h" +#include "monster.h" #include "ui_button.h" namespace Interface @@ -66,10 +67,20 @@ namespace Interface return _selectedInstrument == Instrument::ERASE; } + bool isMonsterSettingMode() const + { + return ( _selectedInstrument == OBJECT ) && ( _selectedObject == MONSTERS ); + } + + int32_t getMonsterId() const + { + return _monsterId; + } + bool showAreaSelectRect() const { return _selectedInstrument == Instrument::TERRAIN || _selectedInstrument == Instrument::STREAM || _selectedInstrument == Instrument::ROAD - || _selectedInstrument == Instrument::ERASE; + || _selectedInstrument == Instrument::ERASE || isMonsterSettingMode(); } bool useMouseDragMovement() const @@ -184,5 +195,7 @@ namespace Interface uint8_t _selectedTerrain{ Brush::WATER }; uint8_t _selectedObject{ Brush::WATER }; uint8_t _selectedBrushSize{ BrushSize::MEDIUM }; + + int32_t _monsterId{ Monster::UNKNOWN }; }; } diff --git a/src/fheroes2/gui/cursor.cpp b/src/fheroes2/gui/cursor.cpp index c7a5b9e6ab2..b911ec4eb37 100644 --- a/src/fheroes2/gui/cursor.cpp +++ b/src/fheroes2/gui/cursor.cpp @@ -87,7 +87,7 @@ void Cursor::setCustomImage( const fheroes2::Image & image, const fheroes2::Poin { theme = NONE; - fheroes2::cursor().update( image, 0, 0 ); + fheroes2::cursor().update( image, -offset.x, -offset.y ); // Immediately apply new mouse offset. const fheroes2::Point & currentPos = LocalEvent::Get().GetMouseCursor(); diff --git a/src/fheroes2/maps/maps_tiles_helper.cpp b/src/fheroes2/maps/maps_tiles_helper.cpp index ab72ca9f28a..8e320ed40ce 100644 --- a/src/fheroes2/maps/maps_tiles_helper.cpp +++ b/src/fheroes2/maps/maps_tiles_helper.cpp @@ -2423,10 +2423,13 @@ namespace Maps { tile.SetObject( MP2::OBJ_MONSTER ); - // If there was another object sprite here (shadow for example) push it down to Addons, - // except when there is already MONS32.ICN here. - if ( tile.getObjectIcnType() != MP2::OBJ_ICN_TYPE_UNKNOWN && tile.getObjectIcnType() != MP2::OBJ_ICN_TYPE_MONS32 && tile.GetObjectSpriteIndex() != 255 ) { - // Push object sprite to Level 1 Addons preserving the Layer Type. + if ( tile.getObjectIcnType() == MP2::OBJ_ICN_TYPE_UNKNOWN ) { + // No object exists on this tile. Add one. + tile.setObjectUID( getNewObjectUID() ); + tile.setObjectIcnType( MP2::OBJ_ICN_TYPE_MONS32 ); + } + else if ( tile.getObjectIcnType() != MP2::OBJ_ICN_TYPE_MONS32 ) { + // If there is another object sprite here (shadow for example) push it down to add-ons. tile.pushBottomLayerAddon( TilesAddon( tile.getLayerType(), tile.GetObjectUID(), tile.getObjectIcnType(), tile.GetObjectSpriteIndex() ) ); // Set unique UID for placed monster.