Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to select instrument brush and brush size in Editor UI #7399

Merged
merged 19 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 265 additions & 20 deletions src/fheroes2/editor/editor_interface_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,24 @@

#include "editor_interface_panel.h"

#include <memory>
#include <cassert>
#include <string>

#include "agg_image.h"
#include "dialog.h"
#include "dialog_system_options.h"
#include "editor_interface.h"
#include "ground.h"
#include "icn.h"
#include "image.h"
#include "interface_base.h"
#include "localevent.h"
#include "screen.h"
#include "tools.h"
#include "translations.h"
#include "ui_button.h"
#include "ui_dialog.h"
#include "ui_text.h"

namespace Interface
{
Expand All @@ -42,6 +46,7 @@ namespace Interface
{
int32_t icnIndex = 0;

// Editor Instruments go in this order in ICN: TERRAIN, OBJECT, DETAIL, STREAM, ROAD, ERASE.
for ( fheroes2::Button & button : _instrumentButtons ) {
button.setICNInfo( ICN::EDITBTNS, icnIndex, icnIndex + 1 );
icnIndex += 2;
Expand All @@ -54,14 +59,22 @@ namespace Interface
_buttonFile.setICNInfo( ICN::EDITBTNS, 20, 21 );
_buttonSystem.setICNInfo( ICN::EDITBTNS, 22, 23 );

_instrumentButtons.front().press();
// Brush Size buttons go in this order in ICN: SMALL (1x), MEDIUM (2x), LARGE (4x), AREA.
icnIndex = 24;
for ( fheroes2::Button & button : _brushSizeButtons ) {
button.setICNInfo( ICN::EDITBTNS, icnIndex, icnIndex + 1 );
icnIndex += 2;
}

_instrumentButtons[_selectedInstrument].press();
_brushSizeButtons[_selectedBrushSize].press();
}

void EditorPanel::setPos( const int32_t displayX, int32_t displayY )
{
int32_t offsetX = displayX;

for ( uint8_t i = 0; i < instrumentsCount; ++i ) {
for ( uint8_t i = 0; i < static_cast<uint8_t>( _instrumentButtonsRect.size() ); ++i ) {
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
_instrumentButtons[i].setPosition( offsetX, displayY );
_instrumentButtonsRect[i] = _instrumentButtons[i].area();

Expand All @@ -83,8 +96,31 @@ namespace Interface
const fheroes2::Sprite & instrumentPanel = fheroes2::AGG::GetICN( ICN::EDITPANL, _selectedInstrument );
_rectInstrumentPanel = { displayX, displayY, instrumentPanel.width(), instrumentPanel.height() };

// System buttons top row.
offsetX = displayX + 14;
int32_t offsetY = displayY + 128;
for ( uint8_t i = 0; i < static_cast<uint8_t>( _brushSizeButtonsRect.size() ); ++i ) {
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
_brushSizeButtons[i].setPosition( offsetX, offsetY );
_brushSizeButtonsRect[i] = _brushSizeButtons[i].area();
offsetX += 30;
}

offsetX = displayX + 30;
offsetY = displayY + 11;
for ( uint8_t i = 0; i < static_cast<uint8_t>( _terrainButtonsRect.size() ); ++i ) {
_terrainButtonsRect[i] = { offsetX + ( i % 3 ) * 29, offsetY + ( i / 3 ) * 29, 27, 27 };
}

offsetX = displayX + 15;
++offsetY;
for ( uint8_t i = 0; i < static_cast<uint8_t>( _objectButtonsRect.size() ); ++i ) {
_objectButtonsRect[i] = { offsetX + ( i % 4 ) * 29, offsetY + ( i / 4 ) * 28, 27, 27 };
}
// The last object button is located not next to previous one and needs to be shifted to the right.
_objectButtonsRect[Brush::TREASURES].x += 29 * 2;

displayY += _rectInstrumentPanel.height;

// System buttons top row.
_buttonMagnify.setPosition( displayX, displayY );
_rectMagnify = _buttonMagnify.area();

Expand Down Expand Up @@ -121,16 +157,124 @@ namespace Interface
button.draw();
}

fheroes2::Display & display = fheroes2::Display::instance();

const fheroes2::Sprite & instrumentPanel = fheroes2::AGG::GetICN( ICN::EDITPANL, _selectedInstrument );
fheroes2::Copy( instrumentPanel, 0, 0, fheroes2::Display::instance(), _rectEditorPanel.x, _rectInstruments.y + _rectInstruments.height, instrumentPanel.width(),
fheroes2::Copy( instrumentPanel, 0, 0, display, _rectEditorPanel.x, _rectInstruments.y + _rectInstruments.height, instrumentPanel.width(),
instrumentPanel.height() );

if ( _selectedInstrument == Instrument::TERRAIN || _selectedInstrument == Instrument::ERASE ) {
for ( const fheroes2::Button & button : _brushSizeButtons ) {
button.draw();
}
}

if ( _selectedInstrument == Instrument::TERRAIN ) {
const fheroes2::Sprite & selection = fheroes2::AGG::GetICN( ICN::TERRAINS, 9 );
fheroes2::Blit( selection, 0, 0, display, _terrainButtonsRect[_selectedTerrain].x - 2, _terrainButtonsRect[_selectedTerrain].y - 2, selection.width(),
selection.height() );

const fheroes2::Text terrainText( _getTerrainTypeName( _selectedTerrain ), fheroes2::FontType::smallWhite() );
terrainText.draw( _rectInstrumentPanel.x + 72 - terrainText.width( 118 ) / 2, _rectInstrumentPanel.y + 107, display );
}

Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
else if ( _selectedInstrument == Instrument::OBJECT ) {
const fheroes2::Sprite & selection = fheroes2::AGG::GetICN( ICN::TERRAINS, 9 );
fheroes2::Blit( selection, 0, 0, display, _objectButtonsRect[_selectedObject].x - 2, _objectButtonsRect[_selectedObject].y - 2, selection.width(),
selection.height() );

const fheroes2::Text terrainText( _getObjectTypeName( _selectedObject ), fheroes2::FontType::smallWhite() );
terrainText.draw( _rectInstrumentPanel.x + 72 - terrainText.width( 118 ) / 2, _rectInstrumentPanel.y + 135, display );
}

_buttonMagnify.draw();
_buttonUndo.draw();
_buttonNew.draw();
_buttonSpecs.draw();
_buttonFile.draw();
_buttonSystem.draw();

display.render( _rectInstrumentPanel );
}

const char * EditorPanel::_getTerrainTypeName( const uint8_t brushId )
{
int groundId = Maps::Ground::UNKNOWN;
switch ( brushId ) {
case Brush::WATER:
groundId = Maps::Ground::WATER;
break;
case Brush::GRASS:
groundId = Maps::Ground::GRASS;
break;
case Brush::SNOW:
groundId = Maps::Ground::SNOW;
break;
case Brush::SWAMP:
groundId = Maps::Ground::SWAMP;
break;
case Brush::LAVA:
groundId = Maps::Ground::LAVA;
break;
case Brush::DESERT:
groundId = Maps::Ground::DESERT;
break;
case Brush::DIRT:
groundId = Maps::Ground::DIRT;
break;
case Brush::WASTELAND:
groundId = Maps::Ground::WASTELAND;
break;
case Brush::BEACH:
groundId = Maps::Ground::BEACH;
break;
default:
// Have you added a new terrain type?
assert( 0 );
break;
ihhub marked this conversation as resolved.
Show resolved Hide resolved
}

return Maps::Ground::String( groundId );
}

const char * EditorPanel::_getObjectTypeName( const uint8_t brushId )
{
switch ( brushId ) {
case Brush::WATER:
return _( "Ocean Objects" );
case Brush::GRASS:
return _( "Grass Objects" );
case Brush::SNOW:
return _( "Snow Objects" );
case Brush::SWAMP:
return _( "Swamp Objects" );
case Brush::LAVA:
return _( "Lava Objects" );
case Brush::DESERT:
return _( "Desert Objects" );
case Brush::DIRT:
return _( "Dirt Objects" );
case Brush::WASTELAND:
return _( "Wasteland Objects" );
case Brush::BEACH:
return _( "Beach Objects" );
case Brush::TOWNS:
return _( "Towns" );
case Brush::MONSTERS:
return _( "Monsters" );
case Brush::HEROES:
return _( "Heroes" );
case Brush::ARTIFACTS:
return _( "Artifacts" );
case Brush::TREASURES:
return _( "Treasures" );
default:
// Have you added a new object type?
assert( 0 );
break;
}

return {};
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
}

fheroes2::GameMode EditorPanel::queueEventProcessing()
Expand All @@ -139,7 +283,7 @@ namespace Interface
fheroes2::GameMode res = fheroes2::GameMode::CANCEL;

if ( le.MousePressLeft( _rectInstruments ) ) {
for ( uint8_t i = 0; i < instrumentsCount; ++i ) {
for ( uint8_t i = 0; i < static_cast<uint8_t>( _instrumentButtonsRect.size() ); ++i ) {
if ( le.MousePressLeft( _instrumentButtonsRect[i] ) ) {
if ( _instrumentButtons[i].drawOnPress() ) {
_selectedInstrument = i;
Expand All @@ -152,6 +296,114 @@ namespace Interface
}
}

if ( _selectedInstrument == Instrument::TERRAIN || _selectedInstrument == Instrument::ERASE ) {
for ( uint8_t i = 0; i < static_cast<uint8_t>( _brushSizeButtonsRect.size() ); ++i ) {
if ( le.MousePressLeft( _brushSizeButtonsRect[i] ) ) {
if ( _brushSizeButtons[i].drawOnPress() ) {
_selectedBrushSize = i;
}
}
else if ( i != _selectedBrushSize ) {
_brushSizeButtons[i].drawOnRelease();
}
}

const auto brushSizeText = []( const int & brushSize ) {
std::string text = _( "Draws objects in %{size} by %{size} square increments." );
StringReplace( text, "%{size}", brushSize );
return text;
};

if ( le.MousePressRight( _brushSizeButtonsRect[BrushSize::SMALL] ) ) {
fheroes2::showStandardTextMessage( _( "Small Brush" ), brushSizeText( 1 ), Dialog::ZERO );
}
else if ( le.MousePressRight( _brushSizeButtonsRect[BrushSize::MEDIUM] ) ) {
fheroes2::showStandardTextMessage( _( "Medium Brush" ), brushSizeText( 2 ), Dialog::ZERO );
}
else if ( le.MousePressRight( _brushSizeButtonsRect[BrushSize::LARGE] ) ) {
fheroes2::showStandardTextMessage( _( "Large Brush" ), brushSizeText( 4 ), Dialog::ZERO );
}
else if ( le.MousePressRight( _brushSizeButtonsRect[BrushSize::AREA] ) ) {
fheroes2::showStandardTextMessage( _( "Area Fill" ), _( "Used to click and drag for clearing large areas." ), Dialog::ZERO );
}
}

if ( _selectedInstrument == Instrument::TERRAIN ) {
for ( uint8_t i = 0; i < static_cast<uint8_t>( _terrainButtonsRect.size() ); ++i ) {
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
if ( ( _selectedTerrain != i ) && le.MousePressLeft( _terrainButtonsRect[i] ) ) {
_selectedTerrain = i;
_redraw();
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
}
}

const auto movePenaltyText = []( const std::string & rate ) {
std::string text = _( "Costs %{rate} times normal movement for all heroes. (Pathfinding reduces or eliminates the penalty.)" );
StringReplace( text, "%{rate}", rate );
return text;
};

if ( le.MousePressRight( _terrainButtonsRect[Brush::WATER] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::WATER ), _( "Traversable only by boat." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::GRASS] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::GRASS ), _( "No special modifiers." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::SNOW] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::SNOW ), movePenaltyText( "1.5" ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::SWAMP] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::SWAMP ), movePenaltyText( "1.75" ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::LAVA] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::LAVA ), _( "No special modifiers." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::DESERT] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::DESERT ), movePenaltyText( "2" ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::DIRT] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::DIRT ), _( "No special modifiers." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::WASTELAND] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::WASTELAND ), movePenaltyText( "1.25" ), Dialog::ZERO );
}
else if ( le.MousePressRight( _terrainButtonsRect[Brush::BEACH] ) ) {
fheroes2::showStandardTextMessage( _getTerrainTypeName( Brush::BEACH ), movePenaltyText( "1.25" ), Dialog::ZERO );
}
}

if ( _selectedInstrument == Instrument::OBJECT ) {
for ( uint8_t i = 0; i < static_cast<uint8_t>( _objectButtonsRect.size() ); ++i ) {
if ( ( _selectedObject != i ) && le.MousePressLeft( _objectButtonsRect[i] ) ) {
_selectedObject = i;
_redraw();
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
}
}

for ( uint8_t objectId = Brush::WATER; objectId < Brush::TOWNS; ++objectId ) {
if ( le.MousePressRight( _objectButtonsRect[objectId] ) ) {
std::string text = _( "Used to select objects most appropriate for use on %{terrain}." );
StringReplaceWithLowercase( text, "%{terrain}", _getTerrainTypeName( objectId ) );
fheroes2::showStandardTextMessage( _getObjectTypeName( objectId ), text, Dialog::ZERO );
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
}
}

if ( le.MousePressRight( _objectButtonsRect[Brush::TOWNS] ) ) {
fheroes2::showStandardTextMessage( _getObjectTypeName( Brush::TOWNS ), _( "Used to place a town or castle." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _objectButtonsRect[Brush::MONSTERS] ) ) {
fheroes2::showStandardTextMessage( _getObjectTypeName( Brush::MONSTERS ), _( "Used to place a monster group." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _objectButtonsRect[Brush::HEROES] ) ) {
fheroes2::showStandardTextMessage( _getObjectTypeName( Brush::HEROES ), _( "Used to place a hero." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _objectButtonsRect[Brush::ARTIFACTS] ) ) {
fheroes2::showStandardTextMessage( _getObjectTypeName( Brush::ARTIFACTS ), _( "Used to place an artifact." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _objectButtonsRect[Brush::TREASURES] ) ) {
fheroes2::showStandardTextMessage( _getObjectTypeName( Brush::TREASURES ), _( "Used to place a resource or treasure." ), Dialog::ZERO );
}
}

le.MousePressLeft( _rectMagnify ) ? _buttonMagnify.drawOnPress() : _buttonMagnify.drawOnRelease();
le.MousePressLeft( _rectUndo ) ? _buttonUndo.drawOnPress() : _buttonUndo.drawOnRelease();
le.MousePressLeft( _rectNew ) ? _buttonNew.drawOnPress() : _buttonNew.drawOnRelease();
Expand Down Expand Up @@ -179,36 +431,29 @@ namespace Interface
// Replace this with Editor options dialog.
fheroes2::showSystemOptionsDialog();
}

if ( le.MousePressRight( _rectInstrumentPanel ) || le.MouseClickLeft( _rectInstrumentPanel ) ) {
// TODO: Implement instrument and brush select.
fheroes2::showStandardTextMessage( _( "Warning!" ), "The Map Editor is still in development. This panel is currently not functional.",
le.MousePressRight() ? Dialog::ZERO : Dialog::OK );
}

if ( le.MousePressRight( _instrumentButtonsRect[0] ) ) {
if ( le.MousePressRight( _instrumentButtonsRect[Instrument::TERRAIN] ) ) {
fheroes2::showStandardTextMessage( _( "Terrain Mode" ), _( "Used to draw the underlying grass, dirt, water, etc. on the map." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _instrumentButtonsRect[1] ) ) {
else if ( le.MousePressRight( _instrumentButtonsRect[Instrument::OBJECT] ) ) {
fheroes2::showStandardTextMessage( _( "Object Mode" ), _( "Used to place objects (mountains, trees, treasure, etc.) on the map." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _instrumentButtonsRect[2] ) ) {
else if ( le.MousePressRight( _instrumentButtonsRect[Instrument::DETAIL] ) ) {
fheroes2::showStandardTextMessage( _( "Detail Mode" ), _( "Used for special editing of monsters, heroes and towns." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _instrumentButtonsRect[3] ) ) {
else if ( le.MousePressRight( _instrumentButtonsRect[Instrument::STREAM] ) ) {
fheroes2::showStandardTextMessage( _( "Stream Mode" ), _( "Allows you to draw streams by clicking and dragging." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _instrumentButtonsRect[4] ) ) {
else if ( le.MousePressRight( _instrumentButtonsRect[Instrument::ROAD] ) ) {
fheroes2::showStandardTextMessage( _( "Road Mode" ), _( "Allows you to draw roads by clicking and dragging." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _instrumentButtonsRect[5] ) ) {
else if ( le.MousePressRight( _instrumentButtonsRect[Instrument::ERASE] ) ) {
fheroes2::showStandardTextMessage( _( "Erase Mode" ), _( "Used to erase objects off the map." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _rectMagnify ) ) {
fheroes2::showStandardTextMessage( _( "Magnify" ), _( "Change between zoom and normal view." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _rectUndo ) ) {
fheroes2::showStandardTextMessage( _( "Undo" ), _( "Undo your last action. Press again to redo the action." ), Dialog::ZERO );
fheroes2::showStandardTextMessage( _( "Undo" ), _( "Undo your last action. Press again to redo the action." ), Dialog::ZERO );
}
else if ( le.MousePressRight( _rectNew ) ) {
fheroes2::showStandardTextMessage( _( "New Map" ), _( "Create a new map either from scratch or using the random map generator." ), Dialog::ZERO );
Expand Down
Loading