2022-10-13 01:51:55 +04:00
/*
* scenelayer . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2022-09-18 03:23:17 +04:00
# include "StdInc.h"
# include "scenelayer.h"
# include "mainwindow.h"
# include "../lib/mapping/CMapEditManager.h"
2023-05-24 02:05:59 +03:00
# include "../lib/mapping/CMap.h"
2022-09-18 03:23:17 +04:00
# include "inspector/inspector.h"
# include "mapview.h"
# include "mapcontroller.h"
AbstractLayer : : AbstractLayer ( MapSceneBase * s ) : scene ( s )
{
}
void AbstractLayer : : initialize ( MapController & controller )
{
map = controller . map ( ) ;
handler = controller . mapHandler ( ) ;
}
void AbstractLayer : : show ( bool show )
{
isShown = show ;
2025-10-06 20:33:09 +02:00
2022-09-18 03:23:17 +04:00
redraw ( ) ;
}
2025-10-06 20:33:09 +02:00
int AbstractLayer : : mapWidthPx ( ) const
{
return map ? map - > width * tileSize : 0 ;
}
int AbstractLayer : : mapHeightPx ( ) const
{
return map ? map - > height * tileSize : 0 ;
}
int AbstractLayer : : toInt ( double value ) const
{
return static_cast < int > ( std : : round ( value ) ) ; // is rounded explicitly in order to avoid rounding down unprecise double values
}
AbstractFixedLayer : : AbstractFixedLayer ( MapSceneBase * s ) : AbstractLayer ( s )
{
}
void AbstractFixedLayer : : redraw ( )
2022-09-18 03:23:17 +04:00
{
if ( item )
{
if ( pixmap & & isShown )
item - > setPixmap ( * pixmap ) ;
else
item - > setPixmap ( emptyPixmap ) ;
}
else
{
if ( pixmap & & isShown )
item . reset ( scene - > addPixmap ( * pixmap ) ) ;
else
item . reset ( scene - > addPixmap ( emptyPixmap ) ) ;
}
}
2025-10-06 20:33:09 +02:00
AbstractViewportLayer : : AbstractViewportLayer ( MapSceneBase * s ) : AbstractLayer ( s )
{
}
void AbstractViewportLayer : : createLayer ( )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
QList < QGraphicsItem * > emptyList ;
items . reset ( scene - > createItemGroup ( emptyList ) ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
void AbstractViewportLayer : : setViewport ( const QRectF & viewPort )
2022-09-18 03:23:17 +04:00
{
if ( ! map )
return ;
2025-10-06 20:33:09 +02:00
if ( items - > boundingRect ( ) . contains ( viewPort ) )
return ;
std : : vector < QGraphicsItem * > outOfScreenSectors ;
for ( QGraphicsItem * sector : getAllSectors ( ) )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
if ( ! viewPort . intersects ( sector - > sceneBoundingRect ( ) ) )
outOfScreenSectors . push_back ( sector ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
for ( QGraphicsItem * sector : outOfScreenSectors )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
removeSector ( sector ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
std : : vector < QRectF > newAreas ;
int left = toInt ( viewPort . left ( ) ) ;
int right = toInt ( viewPort . right ( ) ) ;
int top = toInt ( viewPort . top ( ) ) ;
int bottom = toInt ( viewPort . bottom ( ) ) ;
int startX = left - ( left % sectorSize ) ;
int limitX = std : : min ( right + ( sectorSize - right % sectorSize ) , mapWidthPx ( ) ) ;
int startY = top - ( top % sectorSize ) ;
int limitY = std : : min ( bottom + ( sectorSize - bottom % sectorSize ) , mapHeightPx ( ) ) ;
for ( int x = startX ; x < limitX ; x + = sectorSize )
{
for ( int y = startY ; y < limitY ; y + = sectorSize )
{
int width = x + sectorSize < limitX ? sectorSize : limitX - x ;
int height = y + sectorSize < limitY ? sectorSize : limitY - y ;
QRectF area ( x , y , width , height ) ;
if ( ! items - > boundingRect ( ) . intersects ( area ) )
newAreas . emplace_back ( area ) ;
}
}
for ( QRectF newSection : newAreas )
{
QGraphicsItem * sector = draw ( newSection ) ;
if ( sector )
addSector ( sector ) ;
}
}
void AbstractViewportLayer : : update ( )
{
2022-09-18 03:23:17 +04:00
redraw ( ) ;
}
2025-10-06 20:33:09 +02:00
void AbstractViewportLayer : : redraw ( )
{
std : : set < QGraphicsItem * > allSectors ;
for ( auto * sector : getAllSectors ( ) )
allSectors . insert ( sector ) ;
redrawSectors ( allSectors ) ;
}
void AbstractViewportLayer : : redraw ( const std : : vector < int3 > & tiles )
{
std : : set < QGraphicsItem * > sectorsToRedraw = getContainingSectors ( tiles ) ;
redrawSectors ( sectorsToRedraw ) ;
}
void AbstractViewportLayer : : redrawWithSurroundingTiles ( const std : : vector < int3 > & tiles )
{
int maxX = 0 ;
int maxY = 0 ;
int minX = INT_MAX ;
int minY = INT_MAX ;
for ( const int3 tile : tiles )
{
maxX = std : : max ( tile . x , maxX ) ;
maxY = std : : max ( tile . y , maxY ) ;
minX = std : : min ( tile . x , minX ) ;
minY = std : : min ( tile . y , minY ) ;
}
QRectF bounds ( ( minX - 2 ) * tileSize , ( minY - 2 ) * tileSize , ( maxX - minX + 4 ) * tileSize , ( maxY - minY + 4 ) * tileSize ) ; //tiles start with 1, QRectF from 0
redraw ( { bounds } ) ;
}
void AbstractViewportLayer : : redraw ( const std : : set < CGObjectInstance * > & objects )
2022-09-18 03:23:17 +04:00
{
2025-10-27 21:42:56 +01:00
std : : vector < QRectF > areas ;
areas . reserve ( objects . size ( ) ) ;
2025-10-06 20:33:09 +02:00
for ( const CGObjectInstance * object : objects )
{
areas . push_back ( getObjectArea ( object ) ) ;
}
redraw ( areas ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
void AbstractViewportLayer : : redraw ( const std : : vector < QRectF > & areas )
{
std : : set < QGraphicsItem * > intersectingSectors ;
for ( QGraphicsItem * existingSector : getAllSectors ( ) )
{
for ( auto area : areas )
{
if ( existingSector - > sceneBoundingRect ( ) . intersects ( area ) )
{
intersectingSectors . insert ( existingSector ) ;
}
}
}
redrawSectors ( intersectingSectors ) ;
}
2025-10-06 20:52:25 +02:00
QRectF AbstractViewportLayer : : getObjectArea ( const CGObjectInstance * object ) const
2025-10-06 20:33:09 +02:00
{
auto pos = object - > pos ;
int x = ( ( pos . x + 1 ) * tileSize ) - ( object - > getWidth ( ) * tileSize ) ; //Qt set 0,0 point on the top right corner, CGObjectInstance on the bottom left
int y = ( ( pos . y + 1 ) * tileSize ) - ( object - > getHeight ( ) * tileSize ) ;
QRectF objectArea ( x , y , object - > getWidth ( ) * tileSize , object - > getHeight ( ) * tileSize ) ;
return objectArea ;
}
void AbstractViewportLayer : : addSector ( QGraphicsItem * sector )
{
items - > addToGroup ( sector ) ;
}
void AbstractViewportLayer : : removeSector ( QGraphicsItem * sector )
{
items - > removeFromGroup ( sector ) ;
delete sector ;
}
void AbstractViewportLayer : : redrawSectors ( std : : set < QGraphicsItem * > & sectors )
{
std : : set < QGraphicsItem * > sectorsToRemove ;
for ( QGraphicsItem * existingSectors : getAllSectors ( ) )
{
for ( QGraphicsItem * sector : sectors )
{
if ( existingSectors - > sceneBoundingRect ( ) . contains ( sector - > sceneBoundingRect ( ) ) )
sectorsToRemove . insert ( existingSectors ) ;
}
}
for ( QGraphicsItem * sectorToRemove : sectorsToRemove )
{
addSector ( draw ( sectorToRemove - > sceneBoundingRect ( ) ) ) ;
removeSector ( sectorToRemove ) ;
}
}
2025-10-06 20:52:25 +02:00
const QList < QGraphicsItem * > AbstractViewportLayer : : getAllSectors ( ) const //returning const is necessary to avoid "range-loop might detach Qt container" problem
2025-10-06 20:33:09 +02:00
{
QList < QGraphicsItem * > emptyList ;
return items ? items - > childItems ( ) : emptyList ;
}
2025-10-06 20:52:25 +02:00
std : : set < QGraphicsItem * > AbstractViewportLayer : : getContainingSectors ( const std : : vector < int3 > & tiles ) const
2025-10-06 20:33:09 +02:00
{
std : : set < QGraphicsItem * > result ;
for ( QGraphicsItem * existingSector : getAllSectors ( ) ) {
for ( const int3 tile : tiles )
{
if ( existingSector - > sceneBoundingRect ( ) . contains ( QPointF ( tile . x * tileSize , tile . y * tileSize ) ) )
{
result . insert ( existingSector ) ;
break ;
}
}
}
return result ;
}
2025-10-06 20:52:25 +02:00
std : : set < QGraphicsItem * > AbstractViewportLayer : : getIntersectingSectors ( const std : : vector < QRectF > & areas ) const
2025-10-06 20:33:09 +02:00
{
std : : set < QGraphicsItem * > result ;
for ( QGraphicsItem * existingSector : getAllSectors ( ) ) {
for ( QRectF area : areas )
{
if ( existingSector - > sceneBoundingRect ( ) . intersects ( area ) )
{
result . insert ( existingSector ) ;
}
}
}
return result ;
}
EmptyLayer : : EmptyLayer ( MapSceneBase * s ) : AbstractFixedLayer ( s )
{
isShown = true ;
}
void EmptyLayer : : update ( )
2022-09-18 03:23:17 +04:00
{
if ( ! map )
return ;
2025-10-06 20:33:09 +02:00
pixmap = std : : make_unique < QPixmap > ( map - > width * 32 , map - > height * 32 ) ;
redraw ( ) ;
}
GridLayer : : GridLayer ( MapSceneBase * s ) : AbstractViewportLayer ( s )
{
}
QGraphicsItem * GridLayer : : draw ( const QRectF & section )
{
QPixmap pixmap ( toInt ( section . width ( ) ) , toInt ( section . height ( ) ) ) ;
pixmap . fill ( Qt : : transparent ) ;
if ( isShown )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
QPainter painter ( & pixmap ) ;
painter . setPen ( QColor ( 0 , 0 , 0 , 190 ) ) ;
for ( int j = 0 ; j < = pixmap . height ( ) ; j + = tileSize )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
painter . drawLine ( 0 , j , pixmap . width ( ) , j ) ;
}
for ( int i = 0 ; i < = pixmap . width ( ) ; i + = tileSize )
{
painter . drawLine ( i , 0 , i , pixmap . height ( ) ) ;
}
}
QGraphicsItem * result = scene - > addPixmap ( pixmap ) ;
result - > setPos ( section . x ( ) , section . y ( ) ) ;
return result ;
}
PassabilityLayer : : PassabilityLayer ( MapSceneBase * s ) : AbstractViewportLayer ( s )
{
}
QGraphicsItem * PassabilityLayer : : draw ( const QRectF & section )
{
QPixmap pixmap ( toInt ( section . width ( ) ) , toInt ( section . height ( ) ) ) ;
pixmap . fill ( Qt : : transparent ) ;
if ( isShown )
{
QPainter painter ( & pixmap ) ;
for ( int j = 0 ; j < = pixmap . height ( ) ; j + = tileSize )
{
for ( int i = 0 ; i < pixmap . width ( ) ; i + = tileSize )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
auto tl = map - > getTile ( int3 ( toInt ( section . x ( ) ) / tileSize + i / tileSize , toInt ( section . y ( ) ) / tileSize + j / tileSize , scene - > level ) ) ;
if ( tl . blocked ( ) | | tl . visitable ( ) )
{
painter . fillRect ( i , j , 31 , 31 , tl . visitable ( ) ? QColor ( 200 , 200 , 0 , 64 ) : QColor ( 255 , 0 , 0 , 64 ) ) ;
}
2022-09-18 03:23:17 +04:00
}
}
}
2025-10-06 20:33:09 +02:00
QGraphicsItem * result = scene - > addPixmap ( pixmap ) ;
result - > setPos ( section . x ( ) , section . y ( ) ) ;
return result ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
ObjectPickerLayer : : ObjectPickerLayer ( MapSceneBase * s ) : AbstractViewportLayer ( s )
2023-09-11 18:01:53 +02:00
{
}
2025-10-06 20:33:09 +02:00
void ObjectPickerLayer : : highlight ( const std : : function < bool ( const CGObjectInstance * ) > & predicate )
2023-09-11 18:01:53 +02:00
{
if ( ! map )
return ;
2025-08-02 02:33:47 +02:00
for ( int j = 0 ; j < map - > height ; + + j )
2023-09-11 18:01:53 +02:00
{
2025-08-02 02:33:47 +02:00
for ( int i = 0 ; i < map - > width ; + + i )
2023-09-11 18:01:53 +02:00
{
2025-08-02 02:33:47 +02:00
auto tl = map - > getTile ( int3 ( i , j , scene - > level ) ) ;
ObjectInstanceID objID = tl . topVisitableObj ( ) ;
if ( ! objID . hasValue ( ) & & ! tl . blockingObjects . empty ( ) )
objID = tl . blockingObjects . front ( ) ;
2025-03-19 14:40:45 +00:00
2025-08-02 02:33:47 +02:00
if ( objID . hasValue ( ) )
{
const CGObjectInstance * obj = map - > getObject ( objID ) ;
if ( obj & & predicate ( obj ) )
possibleObjects . insert ( obj ) ;
2023-09-11 18:44:55 +02:00
}
2023-09-11 18:01:53 +02:00
}
}
isActive = true ;
}
bool ObjectPickerLayer : : isVisible ( ) const
{
return isShown & & isActive ;
}
void ObjectPickerLayer : : clear ( )
{
possibleObjects . clear ( ) ;
isActive = false ;
}
2025-10-06 20:33:09 +02:00
QGraphicsItem * ObjectPickerLayer : : draw ( const QRectF & section )
2023-09-11 18:01:53 +02:00
{
2025-10-06 20:33:09 +02:00
int offsetX = toInt ( section . x ( ) ) ;
int offsetY = toInt ( section . y ( ) ) ;
QPixmap pixmap ( toInt ( section . width ( ) ) , toInt ( section . height ( ) ) ) ;
pixmap . fill ( Qt : : transparent ) ;
if ( isVisible ( ) )
pixmap . fill ( QColor ( 255 , 255 , 255 , 128 ) ) ;
QPainter painter ( & pixmap ) ;
2023-09-11 18:01:53 +02:00
painter . setCompositionMode ( QPainter : : CompositionMode_Source ) ;
2025-10-06 20:33:09 +02:00
for ( const auto * obj : possibleObjects )
2023-09-11 18:01:53 +02:00
{
if ( obj - > pos . z ! = scene - > level )
continue ;
2025-10-06 20:33:09 +02:00
for ( const auto & pos : obj - > getBlockedPos ( ) )
painter . fillRect ( pos . x * tileSize - offsetX , pos . y * tileSize - offsetY , tileSize , tileSize , QColor ( 255 , 211 , 0 , 64 ) ) ;
2023-09-11 18:01:53 +02:00
}
2025-10-06 20:33:09 +02:00
QGraphicsItem * result = scene - > addPixmap ( pixmap ) ;
result - > setPos ( section . x ( ) , section . y ( ) ) ;
return result ;
2023-09-11 18:01:53 +02:00
}
void ObjectPickerLayer : : select ( const CGObjectInstance * obj )
{
if ( obj & & possibleObjects . count ( obj ) )
{
clear ( ) ;
2025-10-06 20:33:09 +02:00
Q_EMIT selectionMade ( obj ) ;
2023-09-11 18:01:53 +02:00
}
}
void ObjectPickerLayer : : discard ( )
{
clear ( ) ;
2025-10-06 20:33:09 +02:00
Q_EMIT selectionMade ( nullptr ) ;
2023-09-11 18:01:53 +02:00
}
2025-10-06 20:33:09 +02:00
SelectionTerrainLayer : : SelectionTerrainLayer ( MapSceneBase * s ) : AbstractViewportLayer ( s )
2022-09-18 03:23:17 +04:00
{
}
2025-10-06 20:33:09 +02:00
QGraphicsItem * SelectionTerrainLayer : : draw ( const QRectF & section )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
int offsetX = toInt ( section . x ( ) ) ;
int offsetY = toInt ( section . y ( ) ) ;
QPixmap pixmap ( toInt ( section . width ( ) ) , toInt ( section . height ( ) ) ) ;
pixmap . fill ( Qt : : transparent ) ;
QPainter painter ( & pixmap ) ;
2022-09-18 03:23:17 +04:00
painter . setCompositionMode ( QPainter : : CompositionMode_Source ) ;
2025-10-06 20:33:09 +02:00
for ( const auto & t : area )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
if ( section . contains ( t . x * tileSize , t . y * tileSize ) )
painter . fillRect ( t . x * tileSize - offsetX , t . y * tileSize - offsetY , 31 , 31 , QColor ( 128 , 128 , 128 , 96 ) ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
QGraphicsPixmapItem * result = scene - > addPixmap ( pixmap ) ;
result - > setPos ( section . x ( ) , section . y ( ) ) ;
return result ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
void SelectionTerrainLayer : : select ( const std : : vector < int3 > & tiles )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
for ( int3 tile : tiles )
2022-09-18 03:23:17 +04:00
{
2025-10-27 21:42:19 +01:00
if ( ! area . count ( tile ) & & map - > isInTheMap ( tile ) )
2025-10-06 20:33:09 +02:00
area . insert ( tile ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
redraw ( tiles ) ;
2022-09-18 03:23:17 +04:00
onSelection ( ) ;
}
2025-10-06 20:33:09 +02:00
void SelectionTerrainLayer : : erase ( const std : : vector < int3 > & tiles )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
for ( int3 tile : tiles )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
if ( area . count ( tile ) )
{
area . erase ( tile ) ;
}
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
redraw ( tiles ) ;
2022-09-18 03:23:17 +04:00
onSelection ( ) ;
}
void SelectionTerrainLayer : : clear ( )
{
area . clear ( ) ;
onSelection ( ) ;
2025-10-06 20:33:09 +02:00
redraw ( ) ;
2022-09-18 03:23:17 +04:00
}
const std : : set < int3 > & SelectionTerrainLayer : : selection ( ) const
{
return area ;
}
void SelectionTerrainLayer : : onSelection ( )
{
2025-10-06 20:33:09 +02:00
Q_EMIT selectionMade ( ! area . empty ( ) ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
TerrainLayer : : TerrainLayer ( MapSceneBase * s ) : AbstractViewportLayer ( s )
2022-09-18 03:23:17 +04:00
{
}
2025-10-06 20:33:09 +02:00
void TerrainLayer : : redrawTerrain ( const std : : vector < int3 > & tiles )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
redrawWithSurroundingTiles ( tiles ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
QGraphicsItem * TerrainLayer : : draw ( const QRectF & section )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
int left = toInt ( section . left ( ) ) ;
int right = toInt ( section . right ( ) ) ;
int top = toInt ( section . top ( ) ) ;
int bottom = toInt ( section . bottom ( ) ) ;
QPixmap pixmap ( toInt ( section . width ( ) ) , toInt ( section . height ( ) ) ) ;
pixmap . fill ( Qt : : transparent ) ;
2022-09-18 03:23:17 +04:00
2025-10-06 20:33:09 +02:00
QPainter painter ( & pixmap ) ;
QPointF offset = section . topLeft ( ) ;
for ( int x = left / tileSize ; x < right / tileSize ; + + x )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
for ( int y = top / tileSize ; y < bottom / tileSize ; + + y )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
handler - > drawTerrainTile ( painter , x , y , scene - > level , offset ) ;
handler - > drawRiver ( painter , x , y , scene - > level , offset ) ;
handler - > drawRoad ( painter , x , y , scene - > level , offset ) ;
2022-09-18 03:23:17 +04:00
}
}
2025-10-06 20:33:09 +02:00
QGraphicsPixmapItem * result = scene - > addPixmap ( pixmap ) ;
result - > setPos ( section . x ( ) , section . y ( ) ) ;
return result ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
ObjectsLayer : : ObjectsLayer ( MapSceneBase * s ) : AbstractViewportLayer ( s )
2022-09-18 03:23:17 +04:00
{
}
2025-10-06 20:33:09 +02:00
QGraphicsItem * ObjectsLayer : : draw ( const QRectF & section )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
QPixmap pixmap ( toInt ( section . width ( ) ) , toInt ( section . height ( ) ) ) ;
pixmap . fill ( Qt : : transparent ) ;
2023-10-13 05:21:09 +02:00
2025-10-06 20:33:09 +02:00
if ( isShown )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
QPainter painter ( & pixmap ) ;
2025-10-15 23:55:08 +02:00
handler - > drawObjects ( painter , section , scene - > level , lockedObjects ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
QGraphicsPixmapItem * result = scene - > addPixmap ( pixmap ) ;
result - > setPos ( section . x ( ) , section . y ( ) ) ;
return result ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
void ObjectsLayer : : redrawObjects ( const std : : set < CGObjectInstance * > & objects )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
redraw ( objects ) ;
2022-09-18 03:23:17 +04:00
}
2023-10-13 05:21:09 +02:00
void ObjectsLayer : : setLockObject ( const CGObjectInstance * object , bool lock )
{
if ( lock )
lockedObjects . insert ( object ) ;
else
lockedObjects . erase ( object ) ;
2025-10-15 23:55:08 +02:00
QRectF area = getObjectArea ( object ) ;
redraw ( { area } ) ;
2023-10-13 05:21:09 +02:00
}
void ObjectsLayer : : unlockAll ( )
{
lockedObjects . clear ( ) ;
2025-10-15 23:55:08 +02:00
redraw ( ) ;
2023-10-13 05:21:09 +02:00
}
2025-10-06 20:33:09 +02:00
SelectionObjectsLayer : : SelectionObjectsLayer ( MapSceneBase * s ) : AbstractViewportLayer ( s ) , newObject ( nullptr )
2022-09-18 03:23:17 +04:00
{
}
2025-10-06 20:33:09 +02:00
QGraphicsItem * SelectionObjectsLayer : : draw ( const QRectF & section )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
QPixmap pixmap ( toInt ( section . width ( ) ) , toInt ( section . height ( ) ) ) ;
pixmap . fill ( Qt : : transparent ) ;
if ( isShown )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
QPainter painter ( & pixmap ) ;
painter . setCompositionMode ( QPainter : : CompositionMode_Source ) ;
painter . setPen ( Qt : : white ) ;
QPointF offset = section . topLeft ( ) ;
for ( auto * obj : selectedObjects )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
auto objectArea = getObjectArea ( obj ) ;
if ( obj ! = newObject . get ( ) & & section . intersects ( objectArea ) )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
auto pos = obj - > anchorPos ( ) ;
QRectF bbox ( pos . x , pos . y , 1 , 1 ) ;
for ( const auto & t : obj - > getBlockedPos ( ) )
{
QPointF topLeft ( std : : min ( t . x * 1.0 , bbox . topLeft ( ) . x ( ) ) , std : : min ( t . y * 1.0 , bbox . topLeft ( ) . y ( ) ) ) ;
bbox . setTopLeft ( topLeft ) ;
QPointF bottomRight ( std : : max ( t . x * 1.0 , bbox . bottomRight ( ) . x ( ) ) , std : : max ( t . y * 1.0 , bbox . bottomRight ( ) . y ( ) ) ) ;
bbox . setBottomRight ( bottomRight ) ;
}
//selection box's size was decreased by 1 px to get rid of a persistent bug
//with displaying a box on a border of two sectors. Bite me.
painter . setOpacity ( 1.0 ) ;
QRectF rect ( ( bbox . x ( ) * tileSize + 1 ) - offset . x ( ) , ( bbox . y ( ) * tileSize + 1 ) - offset . y ( ) , ( bbox . width ( ) * tileSize ) - 2 , ( bbox . height ( ) * tileSize ) - 2 ) ;
painter . drawRect ( rect ) ;
}
if ( selectionMode = = SelectionMode : : MOVEMENT & & ( shift . x ( ) | | shift . y ( ) ) )
{
objectArea . moveTo ( objectArea . topLeft ( ) + ( shift * tileSize ) ) ;
if ( section . intersects ( objectArea ) )
{
painter . setOpacity ( 0.7 ) ;
auto newPos = QPoint ( obj - > anchorPos ( ) . x , obj - > anchorPos ( ) . y ) + shift ;
handler - > drawObjectAt ( painter , obj , newPos . x ( ) , newPos . y ( ) , offset ) ;
}
2022-09-18 03:23:17 +04:00
}
}
}
2025-10-06 20:33:09 +02:00
QGraphicsPixmapItem * result = scene - > addPixmap ( pixmap ) ;
result - > setPos ( section . x ( ) , section . y ( ) ) ;
return result ;
2022-09-18 03:23:17 +04:00
}
2022-12-05 01:32:50 +04:00
CGObjectInstance * SelectionObjectsLayer : : selectObjectAt ( int x , int y , const CGObjectInstance * ignore ) const
2022-09-18 03:23:17 +04:00
{
if ( ! map | | ! map - > isInTheMap ( int3 ( x , y , scene - > level ) ) )
return nullptr ;
auto & objects = handler - > getObjects ( x , y , scene - > level ) ;
//visitable is most important
for ( auto & object : objects )
{
2023-10-13 05:21:09 +02:00
if ( ! object . obj | | object . obj = = ignore | | lockedObjects . count ( object . obj ) )
2022-09-18 03:23:17 +04:00
continue ;
2024-10-02 16:40:06 +00:00
if ( object . obj - > visitableAt ( int3 ( x , y , scene - > level ) ) )
2022-09-18 03:23:17 +04:00
{
2023-10-20 01:25:06 +02:00
return const_cast < CGObjectInstance * > ( object . obj ) ;
2022-09-18 03:23:17 +04:00
}
}
//if not visitable tile - try to get blocked
for ( auto & object : objects )
{
2023-10-13 05:21:09 +02:00
if ( ! object . obj | | object . obj = = ignore | | lockedObjects . count ( object . obj ) )
2022-09-18 03:23:17 +04:00
continue ;
2024-10-02 16:40:06 +00:00
if ( object . obj - > blockingAt ( int3 ( x , y , scene - > level ) ) )
2022-09-18 03:23:17 +04:00
{
2023-10-20 01:25:06 +02:00
return const_cast < CGObjectInstance * > ( object . obj ) ;
2022-09-18 03:23:17 +04:00
}
}
//finally, we can take any object
for ( auto & object : objects )
{
2023-10-13 05:21:09 +02:00
if ( ! object . obj | | object . obj = = ignore | | lockedObjects . count ( object . obj ) )
2022-09-18 03:23:17 +04:00
continue ;
2024-10-02 16:40:06 +00:00
if ( object . obj - > coveringAt ( int3 ( x , y , scene - > level ) ) )
2022-09-18 03:23:17 +04:00
{
2023-10-20 01:25:06 +02:00
return const_cast < CGObjectInstance * > ( object . obj ) ;
2022-09-18 03:23:17 +04:00
}
}
return nullptr ;
}
void SelectionObjectsLayer : : selectObjects ( int x1 , int y1 , int x2 , int y2 )
{
if ( ! map )
return ;
if ( x1 > x2 )
std : : swap ( x1 , x2 ) ;
if ( y1 > y2 )
std : : swap ( y1 , y2 ) ;
2025-10-06 20:33:09 +02:00
std : : set < CGObjectInstance * > selectedObjects ;
2022-09-18 03:23:17 +04:00
for ( int j = y1 ; j < y2 ; + + j )
{
for ( int i = x1 ; i < x2 ; + + i )
{
2022-12-04 16:57:46 +04:00
if ( map - > isInTheMap ( int3 ( i , j , scene - > level ) ) )
{
for ( auto & o : handler - > getObjects ( i , j , scene - > level ) )
2023-10-13 05:21:09 +02:00
if ( ! lockedObjects . count ( o . obj ) )
2025-10-06 20:33:09 +02:00
{
selectedObjects . insert ( const_cast < CGObjectInstance * > ( o . obj ) ) ;
}
2022-12-04 16:57:46 +04:00
}
2022-09-18 03:23:17 +04:00
}
}
2025-10-06 20:33:09 +02:00
selectObjects ( selectedObjects ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
void SelectionObjectsLayer : : selectObject ( CGObjectInstance * obj )
2022-09-18 03:23:17 +04:00
{
selectedObjects . insert ( obj ) ;
2025-10-06 20:33:09 +02:00
onSelection ( ) ;
redraw ( { obj } ) ;
}
void SelectionObjectsLayer : : selectObjects ( const std : : set < CGObjectInstance * > & objs )
{
for ( CGObjectInstance * obj : objs )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
selectedObjects . insert ( obj ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
onSelection ( ) ;
redraw ( objs ) ;
2022-09-18 03:23:17 +04:00
}
void SelectionObjectsLayer : : deselectObject ( CGObjectInstance * obj )
{
selectedObjects . erase ( obj ) ;
2025-10-06 20:33:09 +02:00
redraw ( { obj } ) ;
2022-09-18 03:23:17 +04:00
}
bool SelectionObjectsLayer : : isSelected ( const CGObjectInstance * obj ) const
{
return selectedObjects . count ( const_cast < CGObjectInstance * > ( obj ) ) ;
}
std : : set < CGObjectInstance * > SelectionObjectsLayer : : getSelection ( ) const
{
return selectedObjects ;
}
void SelectionObjectsLayer : : clear ( )
{
selectedObjects . clear ( ) ;
shift . setX ( 0 ) ;
shift . setY ( 0 ) ;
2025-10-06 20:33:09 +02:00
redraw ( ) ;
2022-09-18 03:23:17 +04:00
}
void SelectionObjectsLayer : : onSelection ( )
{
2025-10-06 20:33:09 +02:00
Q_EMIT selectionMade ( ! selectedObjects . empty ( ) ) ;
2022-09-18 03:23:17 +04:00
}
2025-10-06 20:33:09 +02:00
void SelectionObjectsLayer : : setShift ( int x , int y )
{
std : : vector < QRectF > areas ;
if ( shift . x ( ) | | shift . y ( ) )
{
for ( auto * selectedObject : selectedObjects )
{
QRectF formerArea = getObjectArea ( selectedObject ) ;
formerArea . moveTo ( formerArea . topLeft ( ) + ( shift * tileSize ) ) ;
areas . emplace_back ( formerArea ) ;
}
}
shift = QPoint ( x , y ) ;
for ( auto * selectedObject : selectedObjects )
{
QRectF area = getObjectArea ( selectedObject ) ;
area . moveTo ( area . topLeft ( ) + ( shift * tileSize ) ) ;
areas . emplace_back ( area ) ;
}
redraw ( areas ) ;
}
void SelectionObjectsLayer : : setLockObject ( CGObjectInstance * object , bool lock )
2023-10-13 05:21:09 +02:00
{
if ( lock )
lockedObjects . insert ( object ) ;
else
lockedObjects . erase ( object ) ;
2025-10-06 20:33:09 +02:00
redraw ( { object } ) ;
2023-10-13 05:21:09 +02:00
}
void SelectionObjectsLayer : : unlockAll ( )
{
lockedObjects . clear ( ) ;
}
2025-10-06 20:33:09 +02:00
MinimapLayer : : MinimapLayer ( MapSceneBase * s ) : AbstractFixedLayer ( s )
2022-09-18 03:23:17 +04:00
{
2025-10-06 20:33:09 +02:00
2022-09-18 03:23:17 +04:00
}
void MinimapLayer : : update ( )
{
if ( ! map )
return ;
2025-10-06 20:33:09 +02:00
pixmap = std : : make_unique < QPixmap > ( map - > width , map - > height ) ;
2022-09-18 03:23:17 +04:00
QPainter painter ( pixmap . get ( ) ) ;
2024-06-24 03:23:26 +02:00
//coordinate transformation
2022-09-18 03:23:17 +04:00
for ( int j = 0 ; j < map - > height ; + + j )
{
for ( int i = 0 ; i < map - > width ; + + i )
{
handler - > drawMinimapTile ( painter , i , j , scene - > level ) ;
}
}
redraw ( ) ;
}
2025-10-06 20:33:09 +02:00
MinimapViewLayer : : MinimapViewLayer ( MapSceneBase * s ) : AbstractFixedLayer ( s )
2022-09-18 03:23:17 +04:00
{
}
void MinimapViewLayer : : update ( )
{
if ( ! map )
return ;
2025-10-06 20:33:09 +02:00
pixmap = std : : make_unique < QPixmap > ( map - > width , map - > height ) ;
2022-10-13 01:40:52 +04:00
draw ( ) ;
2022-09-18 03:23:17 +04:00
}
void MinimapViewLayer : : draw ( )
{
if ( ! map )
return ;
2022-10-13 01:58:49 +04:00
pixmap - > fill ( Qt : : transparent ) ;
2022-09-18 03:23:17 +04:00
//maybe not optimal but ok
QPainter painter ( pixmap . get ( ) ) ;
2022-10-13 01:58:49 +04:00
painter . setPen ( Qt : : white ) ;
2022-09-18 03:23:17 +04:00
painter . drawRect ( x , y , w , h ) ;
redraw ( ) ;
}
void MinimapViewLayer : : setViewport ( int _x , int _y , int _w , int _h )
{
x = _x ;
y = _y ;
w = _w ;
h = _h ;
draw ( ) ;
}