Skip to content

Commit

Permalink
Rework editor instrument groups: add new buttons, rework instrument p…
Browse files Browse the repository at this point in the history
…anel, add evil inerface (#8150)
  • Loading branch information
Districh-ru authored Dec 24, 2023
1 parent 79c2347 commit 05b1131
Show file tree
Hide file tree
Showing 14 changed files with 890 additions and 332 deletions.
24 changes: 24 additions & 0 deletions src/engine/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,11 @@ namespace fheroes2
Copy( in, 0, 0, out, 0, 0, in.width(), in.height() );
}

void Copy( const Image & in, int32_t inX, int32_t inY, Image & out, const Rect & outRoi )
{
Copy( in, inX, inY, out, outRoi.x, outRoi.y, outRoi.width, outRoi.height );
}

void Copy( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height )
{
if ( !Verify( in, inX, inY, out, outX, outY, width, height ) ) {
Expand Down Expand Up @@ -2645,6 +2650,25 @@ namespace fheroes2
}
}

void ReplaceTransformIdByColorId( Image & image, const uint8_t transformId, const uint8_t colorId )
{
if ( image.empty() || image.singleLayer() ) {
return;
}

const int32_t size = image.width() * image.height();

uint8_t * imageIn = image.image();
uint8_t * transformIn = image.transform();
const uint8_t * imageInEnd = imageIn + size;
for ( ; imageIn != imageInEnd; ++imageIn, ++transformIn ) {
if ( *transformIn == transformId ) {
*transformIn = 0U;
*imageIn = colorId;
}
}
}

void Resize( const Image & in, Image & out, const bool isSubpixelAccuracy )
{
if ( in.empty() || out.empty() ) {
Expand Down
4 changes: 4 additions & 0 deletions src/engine/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ namespace fheroes2
void Blit( const Image & in, const Point & inPos, Image & out, const Point & outPos, const Size & size, bool flip = false );

void Copy( const Image & in, Image & out );
void Copy( const Image & in, int32_t inX, int32_t inY, Image & out, const Rect & outRoi );
void Copy( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height );

// Copies transform the layer from in to out. Both images must be of the same size.
Expand Down Expand Up @@ -295,6 +296,9 @@ namespace fheroes2
// Use this function only when you need to convert pixel value into transform layer
void ReplaceColorIdByTransformId( Image & image, const uint8_t colorId, const uint8_t transformId );

// Use this function only when you need to convert transform value into non-transparent pixel with the given color.
void ReplaceTransformIdByColorId( Image & image, const uint8_t transformId, const uint8_t colorId );

// Please remember that subpixel accuracy resizing is extremely slow!
void Resize( const Image & in, Image & out, const bool isSubpixelAccuracy = false );

Expand Down
177 changes: 143 additions & 34 deletions src/fheroes2/agg/agg_image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <initializer_list>
#include <map>
#include <memory>
#include <numeric>
#include <random>
#include <set>
#include <stdexcept>
Expand Down Expand Up @@ -547,6 +548,45 @@ namespace
std::fill( imageTransform + ( imageHeight - 1 ) * imageWidth - 3, imageTransform + ( imageHeight - 1 ) * imageWidth, transparencyValue );
std::fill( imageTransform + imageHeight * imageWidth - 4, imageTransform + imageHeight * imageWidth, transparencyValue );
}

// Remove all shadows from the image and make them fully transparent.
void setTransformLayerTransparent( fheroes2::Image & image )
{
assert( !image.empty() && !image.singleLayer() );

uint8_t * transform = image.transform();
const uint8_t * transformEnd = transform + static_cast<ptrdiff_t>( image.width() ) * image.height();

for ( ; transform != transformEnd; ++transform ) {
if ( *transform > 1 ) {
*transform = 1U;
}
}
}

// Draw the given image in the center of the button images (released and pressed states) and add extra shading and brightening at the edges of the image.
void drawImageOnButton( const fheroes2::Image & image, const int32_t maxImageWidth, const int32_t maxImageHeight, fheroes2::Image & releasedSprite,
fheroes2::Image & pressedSprite )
{
assert( !image.empty() && !releasedSprite.empty() && !pressedSprite.empty() );

fheroes2::Image newImage( std::min( maxImageWidth, image.width() + 4 ), std::min( maxImageHeight, image.height() + 4 ) );
newImage.reset();
fheroes2::Blit( image, 0, 0, newImage, 2, 2, 35, 25 );
// Remove shadow from the image.
setTransformLayerTransparent( newImage );
// Add extra shading and brightening at the edges of the image.
fheroes2::updateShadow( newImage, { 1, -1 }, 2U, false );
fheroes2::updateShadow( newImage, { 2, -2 }, 5U, false );
fheroes2::updateShadow( newImage, { -1, 1 }, 6U, false );
fheroes2::updateShadow( newImage, { -2, 2 }, 9U, false );
// Draw the image on the button.
const int32_t offsetX = ( pressedSprite.width() - newImage.width() ) / 2;
const int32_t offsetY = ( pressedSprite.height() - newImage.height() ) / 2;
fheroes2::Blit( newImage, pressedSprite, offsetX + 2, offsetY + 1 );
fheroes2::ReplaceTransformIdByColorId( newImage, 6U, 10U );
fheroes2::Blit( newImage, releasedSprite, offsetX + 3, offsetY );
}
}

namespace fheroes2
Expand Down Expand Up @@ -2507,7 +2547,7 @@ namespace fheroes2
// Generate random spell image for Editor.
{
const Sprite & randomSpellImage = _icnVsSprite[id][2];
int32_t imageWidth = randomSpellImage.width();
const int32_t imageWidth = randomSpellImage.width();

Copy( randomSpellImage, _icnVsSprite[id][67] );

Expand Down Expand Up @@ -2667,9 +2707,7 @@ namespace fheroes2
Sprite & out = _icnVsSprite[id][23];

std::vector<uint8_t> indexes( 256 );
for ( uint32_t i = 0; i < 256; ++i ) {
indexes[i] = static_cast<uint8_t>( i );
}
std::iota( indexes.begin(), indexes.end(), static_cast<uint8_t>( 0 ) );

indexes[69] = 187;
indexes[71] = 195;
Expand Down Expand Up @@ -2732,9 +2770,7 @@ namespace fheroes2
}

std::vector<uint8_t> indexes( 256 );
for ( uint32_t i = 0; i < 256; ++i ) {
indexes[i] = static_cast<uint8_t>( i );
}
std::iota( indexes.begin(), indexes.end(), static_cast<uint8_t>( 0 ) );

indexes[10] = 152;
indexes[11] = 153;
Expand Down Expand Up @@ -3029,35 +3065,108 @@ namespace fheroes2

return true;
}
case ICN::EDITBTNS:
LoadOriginalICN( id );
if ( _icnVsSprite[id].size() == 35 ) {
// We add three buttons for new object groups: Adventure, Kingdom, Monsters.
_icnVsSprite[id].resize( 41 );

// First make clean button sprites (pressed and released).
Sprite released = GetICN( ICN::EDITBTNS, 4 );
Sprite pressed = GetICN( ICN::EDITBTNS, 5 );
// Clean the image from the button.
Fill( released, 16, 6, 18, 24, 41U );
Fill( pressed, 16, 7, 17, 23, 46U );

for ( size_t i = 0; i < 4; i += 2 ) {
_icnVsSprite[id][35 + i] = released;
_icnVsSprite[id][35 + 1 + i] = pressed;
}
_icnVsSprite[id][39] = std::move( released );
_icnVsSprite[id][40] = std::move( pressed );

// Adventure objects button.
drawImageOnButton( GetICN( ICN::X_LOC1, 0 ), 39, 29, _icnVsSprite[id][35], _icnVsSprite[id][36] );

// Kingdom objects button.
drawImageOnButton( GetICN( ICN::OBJNARTI, 13 ), 39, 29, _icnVsSprite[id][37], _icnVsSprite[id][38] );

// Monsters objects button.
drawImageOnButton( GetICN( ICN::MONS32, 11 ), 39, 29, _icnVsSprite[id][39], _icnVsSprite[id][40] );
}
return true;
case ICN::EDITBTNS_EVIL: {
loadICN( ICN::EDITBTNS );
_icnVsSprite[id] = _icnVsSprite[ICN::EDITBTNS];
for ( auto & image : _icnVsSprite[id] ) {
convertToEvilInterface( image, { 0, 0, image.width(), image.height() } );
}
return true;
}
case ICN::EDITPANL:
LoadOriginalICN( id );
if ( _icnVsSprite[id].size() > 5 ) {
Sprite & erasePanel = _icnVsSprite[id][5];
// To select object types for erasure we copy object buttons.
Copy( _icnVsSprite[id][1], 15, 68, erasePanel, 15, 68, 114, 55 );

// Make 3 empty buttons for terrain objects, roads and streams.
Fill( erasePanel, 16, 69, 24, 24, 65U );
Copy( erasePanel, 15, 68, erasePanel, 44, 96, 27, 27 );
Copy( erasePanel, 15, 68, erasePanel, 73, 96, 27, 27 );

// Make erase terrain objects button image.
Image objectsImage( 24, 24 );
Copy( GetICN( ICN::EDITBTNS, 2 ), 13, 4, objectsImage, 0, 0, 24, 24 );
AddTransparency( objectsImage, 10U );
AddTransparency( objectsImage, 38U );
AddTransparency( objectsImage, 39U );
AddTransparency( objectsImage, 40U );
AddTransparency( objectsImage, 41U );
AddTransparency( objectsImage, 46U );
Blit( objectsImage, 0, 0, erasePanel, 16, 69, 24, 24 );

// Make erase roads button image.
Blit( GetICN( ICN::ROAD, 2 ), 0, 0, erasePanel, 45, 104, 24, 5 );
Blit( GetICN( ICN::ROAD, 1 ), 1, 0, erasePanel, 45, 109, 24, 5 );

// Make erase streams button image.
Blit( GetICN( ICN::STREAM, 2 ), 0, 0, erasePanel, 74, 104, 24, 11 );
if ( _icnVsSprite[id].size() == 6 ) {
_icnVsSprite[id].resize( 18 );

// Make empty buttons for object types.
_icnVsSprite[id][6].resize( 27, 27 );
_icnVsSprite[id][6]._disableTransformLayer();
_icnVsSprite[id][6].reset();
Fill( _icnVsSprite[id][6], 1, 1, 24, 24, 65U );
for ( size_t i = 7; i < _icnVsSprite[id].size(); ++i ) {
_icnVsSprite[id][i] = _icnVsSprite[id][6];
}

// Make Mountains objects button.
Blit( GetICN( ICN::MTNCRCK, 3 ), 4, 0, _icnVsSprite[id][6], 1, 1, 24, 24 );

// Make Rocks objects button.
Blit( GetICN( ICN::OBJNGRAS, 41 ), 1, 0, _icnVsSprite[id][7], 1, 9, 23, 12 );

// Make Trees object button. Also used as erase terrain objects button image.
Copy( GetICN( ICN::EDITBTNS, 2 ), 13, 4, _icnVsSprite[id][8], 1, 1, 24, 24 );

// Replace image contour colors with the background color.
std::vector<uint8_t> indexes( 256 );
std::iota( indexes.begin(), indexes.end(), static_cast<uint8_t>( 0 ) );

indexes[10] = 65U;
indexes[38] = 65U;
indexes[39] = 65U;
indexes[40] = 65U;
indexes[41] = 65U;
indexes[46] = 65U;

ApplyPalette( _icnVsSprite[id][8], indexes );

// Make Landscape Water objects button.
Blit( GetICN( ICN::OBJNWAT2, 0 ), 0, 3, _icnVsSprite[id][9], 5, 1, 13, 3 );
Blit( GetICN( ICN::OBJNWAT2, 2 ), 5, 0, _icnVsSprite[id][9], 1, 4, 24, 21 );

// Make Landscape Miscellaneous objects button.
Blit( GetICN( ICN::OBJNMUL2, 16 ), 3, 0, _icnVsSprite[id][10], 1, 4, 24, 19 );

// Make erase Dwellings button image.
Blit( GetICN( ICN::OBJNMULT, 114 ), 7, 0, _icnVsSprite[id][11], 1, 1, 24, 24 );

// Make erase Mines button image.
Blit( GetICN( ICN::MTNMULT, 82 ), 8, 4, _icnVsSprite[id][12], 1, 1, 24, 24 );

// Make erase Power-ups button image.
Blit( GetICN( ICN::OBJNMULT, 72 ), 0, 6, _icnVsSprite[id][13], 1, 1, 24, 24 );

// Make Adventure Water objects button.
Blit( GetICN( ICN::OBJNWATR, 24 ), 3, 0, _icnVsSprite[id][14], 1, 1, 24, 24 );

// Make Adventure Miscellaneous objects button.
Blit( GetICN( ICN::OBJNMUL2, 198 ), 2, 0, _icnVsSprite[id][15], 1, 1, 24, 24 );

// Make erase Roads button image.
Blit( GetICN( ICN::ROAD, 2 ), 0, 0, _icnVsSprite[id][16], 1, 8, 24, 5 );
Blit( GetICN( ICN::ROAD, 1 ), 1, 0, _icnVsSprite[id][16], 1, 13, 24, 5 );

// Make erase Streams button image.
Blit( GetICN( ICN::STREAM, 2 ), 0, 0, _icnVsSprite[id][17], 1, 8, 24, 11 );
}
return true;
case ICN::EDITOR:
Expand Down
1 change: 1 addition & 0 deletions src/fheroes2/agg/icn.h
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ namespace ICN
CASLXTRA_EVIL,
RECRBKG_EVIL,
STRIP_BACKGROUND_EVIL,
EDITBTNS_EVIL,

GOOD_CAMPAIGN_BUTTONS,
EVIL_CAMPAIGN_BUTTONS,
Expand Down
13 changes: 11 additions & 2 deletions src/fheroes2/dialog/dialog_selectitems.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -866,13 +866,22 @@ int Dialog::selectTreasureType( const int resourceType )
return selectObjectType( resourceType, objectInfo.size(), listbox );
}

int Dialog::selectOceanObjectType( const int resourceType )
int Dialog::selectOceanObjectType( const int objectType )
{
const auto & objectInfo = Maps::getObjectsByGroup( Maps::ObjectGroup::ADVENTURE_WATER );

OceanObjectTypeSelection listbox( objectInfo, { 350, fheroes2::Display::instance().height() - 200 }, _( "Select Ocean Object:" ) );

return selectObjectType( resourceType, objectInfo.size(), listbox );
return selectObjectType( objectType, objectInfo.size(), listbox );
}

int Dialog::selectLandscapeOceanObjectType( const int objectType )
{
const auto & objectInfo = Maps::getObjectsByGroup( Maps::ObjectGroup::LANDSCAPE_WATER );

OceanObjectTypeSelection listbox( objectInfo, { 350, fheroes2::Display::instance().height() - 200 }, _( "Select Ocean Object:" ) );

return selectObjectType( objectType, objectInfo.size(), listbox );
}

void Dialog::selectTownType( int & type, int & color )
Expand Down
4 changes: 3 additions & 1 deletion src/fheroes2/dialog/dialog_selectitems.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ namespace Dialog

int selectTreasureType( const int resourceType );

int selectOceanObjectType( const int resourceType );
int selectOceanObjectType( const int objectType );

int selectLandscapeOceanObjectType( const int objectType );

void selectTownType( int & type, int & color );
}
Expand Down
18 changes: 2 additions & 16 deletions src/fheroes2/editor/editor_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
#include "interface_border.h"
#include "interface_gamearea.h"
#include "interface_radar.h"
#include "interface_status.h"
#include "localevent.h"
#include "map_format_helper.h"
#include "map_format_info.h"
Expand Down Expand Up @@ -164,13 +163,7 @@ namespace Interface
const int32_t xOffset = display.width() - BORDERWIDTH - RADARWIDTH;
_radar.SetPos( xOffset, BORDERWIDTH );

if ( display.height() > display.DEFAULT_HEIGHT + BORDERWIDTH ) {
_editorPanel.setPos( xOffset, _radar.GetArea().y + _radar.GetArea().height + BORDERWIDTH );
_statusWindow.SetPos( xOffset, _editorPanel.getRect().y + _editorPanel.getRect().height );
}
else {
_editorPanel.setPos( xOffset, _radar.GetArea().y + _radar.GetArea().height );
}
_editorPanel.setPos( xOffset, _radar.GetArea().y + _radar.GetArea().height + ( ( display.height() > display.DEFAULT_HEIGHT + BORDERWIDTH ) ? BORDERWIDTH : 0 ) );

const fheroes2::Point prevCenter = _gameArea.getCurrentCenterInPixels();
const fheroes2::Rect prevRoi = _gameArea.GetROI();
Expand Down Expand Up @@ -227,13 +220,6 @@ namespace Interface
_editorPanel._redraw();
}

if ( ( combinedRedraw & REDRAW_STATUS ) && ( display.height() > display.DEFAULT_HEIGHT + BORDERWIDTH ) ) {
// Currently the Adventure Map status is rendered to fill the space under the Editor buttons on high resolutions.
// TODO: Make special status for Editor to display some map info, e.g. object properties under the cursor (castle garrison, amount of resources, etc.)
// TODO: Decide where to output the status for low resolutions (reduce the number of displayed buttons - put some into sub-menu).
_statusWindow._redraw();
}

_redraw = 0;
}

Expand Down Expand Up @@ -832,7 +818,7 @@ namespace Interface
setObjectOnTile( tile, objectInfo );
}
}
else if ( groupType == Maps::ObjectGroup::ADVENTURE_WATER ) {
else if ( groupType == Maps::ObjectGroup::ADVENTURE_WATER || groupType == Maps::ObjectGroup::LANDSCAPE_WATER ) {
const auto & objectInfo = getObjectInfo( groupType, _editorPanel.getSelectedObjectType() );

if ( !isObjectPlacementAllowed( objectInfo, tilePos ) ) {
Expand Down
Loading

0 comments on commit 05b1131

Please sign in to comment.