File indexing completed on 2024-11-10 06:45:25

0001 /*
0002     SPDX-FileCopyrightText: 2000-2005 Stefan Schimanski <1Stein@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "wall.h"
0008 
0009 #include <cmath>
0010 
0011 
0012 #include <QPainter>
0013 #include <QStandardPaths>
0014 
0015 #include "board.h"
0016 #include "renderer.h"
0017 #include "settings.h"
0018 
0019 QSize KBounceWall::s_tileSize;
0020 KBounceRenderer * KBounceWall::m_renderer = nullptr;
0021 KBounceWall::Sprites * KBounceWall::s_sprites = nullptr;
0022 
0023 
0024 KBounceWall::KBounceWall( Direction dir, KBounceRenderer* renderer, KBounceBoard* board )
0025     : KGameRenderedItem( renderer,QLatin1String(""),board )
0026     , m_board( board )
0027       , m_dir( dir )
0028       , m_soundWallstart( QStandardPaths::locate( QStandardPaths::AppDataLocation, QStringLiteral("sounds/wallstart.wav") ) )
0029       , m_soundReflect( QStandardPaths::locate( QStandardPaths::AppDataLocation, QStringLiteral("sounds/reflect.wav") ) )
0030 {
0031     // The wall velocity is initialised on every new level.
0032     m_wallVelocity = 0.0;
0033 
0034     if (!s_sprites) {
0035         s_sprites = new Sprites;
0036     }
0037 
0038     if (!m_renderer) {
0039         m_renderer = renderer;
0040     }
0041 }
0042 
0043 KBounceWall::~KBounceWall()
0044 {
0045 }
0046 
0047 void KBounceWall::collide(const KBounceCollision &collision)
0048 {
0049     if ( !isVisible() )
0050         return;
0051 
0052     for (const KBounceHit &hit : collision) {
0053         switch (hit.type) {
0054             case ALL:
0055                 break;
0056             case TILE:
0057                 finish();
0058                 break;
0059             case BALL:
0060                 if (safeEdgeHit(hit.boundingRect)) {
0061                     KBounceVector normal = hit.normal;
0062                     if (qAbs(normal.x) < qAbs(normal.y)) { // vertical
0063                         if (m_dir == Up || m_dir == Down) {
0064                             finish( true, m_dir );
0065                         }
0066                     }
0067                     else if (m_dir == Left || m_dir == Right) {
0068                         finish( true, m_dir );
0069                     }
0070                 } else {
0071                     Q_EMIT died();
0072                     hide();
0073                 }
0074                 break;
0075             case WALL:
0076                 if (safeEdgeHit(hit.boundingRect)) {
0077                     finish();
0078                 }
0079                 break;
0080         }
0081     }
0082 }
0083 
0084 
0085 void KBounceWall::goForward()
0086 {
0087     if (!isVisible()) {
0088         return;
0089     }
0090 
0091     switch( m_dir ) {
0092         case Up:
0093             m_boundingRect.setTop( m_boundingRect.top() - m_wallVelocity );
0094             m_nextBoundingRect.setTop( m_boundingRect.top() - m_wallVelocity );
0095             break;
0096         case Left:
0097             m_boundingRect.setLeft( m_boundingRect.left() - m_wallVelocity );
0098             m_nextBoundingRect.setLeft( m_boundingRect.left() - m_wallVelocity );
0099             break;
0100         case Down:
0101             m_boundingRect.setBottom( m_boundingRect.bottom() + m_wallVelocity );
0102             m_nextBoundingRect.setBottom( m_boundingRect.bottom() + m_wallVelocity );
0103             break;
0104         case Right:
0105             m_boundingRect.setRight( m_boundingRect.right() + m_wallVelocity );
0106             m_nextBoundingRect.setRight( m_boundingRect.right() + m_wallVelocity );
0107             break;
0108     }
0109 }
0110 
0111 void KBounceWall::update()
0112 {
0113     if (!isVisible())
0114         return;
0115 
0116     int boundingRectWidth = static_cast<int>(std::ceil(m_boundingRect.width() * s_tileSize.width()));
0117     int boundingRectHeight = static_cast<int>(std::ceil(m_boundingRect.height() * s_tileSize.height()));
0118 
0119     if (!(boundingRectWidth && boundingRectHeight))
0120         return;
0121 
0122     int tileWidth = s_tileSize.width();
0123     int tileHeight = s_tileSize.height();
0124 
0125     QSize pixSize;
0126     if (m_dir == Left || m_dir == Right) {
0127         pixSize = QSize(boundingRectWidth + 64 - (boundingRectWidth%64), boundingRectHeight);
0128     } else {
0129         pixSize = QSize(boundingRectWidth, boundingRectHeight + 64 - (boundingRectHeight%64));
0130     }
0131 
0132     QPixmap px;
0133     if (pixmap().width() < pixSize.width() || pixmap().height() < pixSize.height()) {
0134         px = QPixmap(pixSize);
0135     } else {
0136         px = pixmap(); // already ARGB
0137     }
0138     px.fill(Qt::transparent);
0139 
0140     QPainter p(&px);
0141 
0142     QPointF offset = m_board->mapPosition(m_boundingRect.topLeft());
0143 
0144     switch ( m_dir ) {
0145         case Up: {
0146                      const int split = qMin(tileHeight, boundingRectHeight);
0147                      p.drawPixmap(0, 0, s_sprites->wallEndUp, 0, 0, tileWidth, split);
0148                      p.drawTiledPixmap(0, split, tileWidth, boundingRectHeight - split, s_sprites->wallV, 0, offset.y());
0149                      break;
0150                  }
0151         case Right: {
0152                         const int split = qMin(tileWidth, boundingRectWidth);
0153                         p.drawPixmap(boundingRectWidth - tileWidth, 0, split, tileHeight, s_sprites->wallEndRight);
0154                         p.drawTiledPixmap(0, 0, boundingRectWidth - split, tileHeight, s_sprites->wallH);
0155                         break;
0156                     }
0157         case Down: {
0158                        const int split = qMin(tileHeight, boundingRectHeight);
0159                        p.drawPixmap(0, boundingRectHeight - tileHeight, tileWidth, split, s_sprites->wallEndDown);
0160                        p.drawTiledPixmap(0, 0, tileWidth, boundingRectHeight - split, s_sprites->wallV);
0161                        break;
0162                    }
0163         case Left:
0164                    const int split = qMin(boundingRectWidth, tileWidth);
0165                    p.drawPixmap(0, 0, split, tileHeight, s_sprites->wallEndLeft);
0166                    p.drawTiledPixmap(split, 0, boundingRectWidth - split, tileHeight, s_sprites->wallH, offset.x());
0167     }
0168     setPos(offset);
0169     p.end();
0170     setPixmap(px);
0171 }
0172 
0173 void KBounceWall::loadSprites() {
0174     s_sprites->wallEndLeft = m_renderer->spritePixmap(QStringLiteral("wallEndLeft"), s_tileSize);
0175     s_sprites->wallEndUp = m_renderer->spritePixmap(QStringLiteral("wallEndUp"), s_tileSize);
0176     s_sprites->wallEndRight = m_renderer->spritePixmap(QStringLiteral("wallEndRight"), s_tileSize);
0177     s_sprites->wallEndDown = m_renderer->spritePixmap(QStringLiteral("wallEndDown"), s_tileSize);
0178 
0179     s_sprites->wallH = m_renderer->spritePixmap(QStringLiteral("wallH"), QSize(32 * s_tileSize.width(), s_tileSize.height()));
0180     s_sprites->wallV = m_renderer->spritePixmap(QStringLiteral("wallV"), QSize(s_tileSize.width(), 18*s_tileSize.height()));
0181 }
0182 
0183 void KBounceWall::resize( const QSize& tileSize )
0184 {
0185     if ( tileSize != s_tileSize ) {
0186         s_tileSize = tileSize;
0187         loadSprites();
0188         update();
0189     }
0190 }
0191 
0192 void KBounceWall::build( int x, int y )
0193 {
0194     if (isVisible())
0195         return;
0196 
0197     if ( m_dir == Up || m_dir == Down ) {
0198         m_boundingRect.setTop( y );
0199 
0200         if (m_dir == Down) {
0201             m_boundingRect.setBottom(y + 1);
0202         } else {
0203             m_boundingRect.setBottom( y );
0204         }
0205 
0206         m_boundingRect.setLeft( x );
0207         m_boundingRect.setRight( x + 1 );
0208     }
0209     else if ( m_dir == Left || m_dir == Right ) {
0210         m_boundingRect.setTop( y );
0211         m_boundingRect.setBottom( y + 1 );
0212         m_boundingRect.setLeft( x );
0213 
0214         if (m_dir == Right) {
0215             m_boundingRect.setRight(x + 1);
0216         } else {
0217             m_boundingRect.setRight( x );
0218         }
0219     }
0220 
0221 
0222     m_nextBoundingRect = m_boundingRect;
0223 
0224     setPixmap(QPixmap());
0225 
0226     setPos(m_board->mapPosition(QPointF( x, y )));
0227     show();
0228 
0229     if ( KBounceSettings::playSounds() )
0230         m_soundWallstart.start();
0231 }
0232 
0233 QRectF KBounceWall::nextBoundingRect() const
0234 {
0235     return m_nextBoundingRect;
0236 }
0237 
0238 bool KBounceWall::safeEdgeHit( const QRectF& rect2 ) const
0239 {
0240     bool safeEdgeHit = false;
0241 
0242     QPointF p1, p2, p3;
0243     switch ( m_dir )
0244     {
0245         case Up:
0246             p1 = m_nextBoundingRect.topLeft();
0247             p2 = m_nextBoundingRect.topRight();
0248             break;
0249         case Right:
0250             p1 = m_nextBoundingRect.topRight();
0251             p2 = m_nextBoundingRect.bottomRight();
0252             break;
0253         case Down:
0254             p1 = m_nextBoundingRect.bottomRight();
0255             p2 = m_nextBoundingRect.bottomLeft();
0256             break;
0257         case Left:
0258             p1 = m_nextBoundingRect.bottomLeft();
0259             p2 = m_nextBoundingRect.topLeft();
0260             break;
0261         default:
0262             Q_ASSERT(false);
0263             break;
0264     }
0265     p3.setX( ( p1.x() + p2.x() ) / 2.0 );
0266     p3.setY( ( p1.y() + p2.y() ) / 2.0 );
0267 
0268     if ( rect2.contains( p1 ) )
0269         safeEdgeHit = true;
0270     else if ( rect2.contains( p2 ) )
0271         safeEdgeHit = true;
0272     else if ( rect2.contains( p3 ) )
0273         safeEdgeHit = true;
0274 
0275     return safeEdgeHit;
0276 }
0277 
0278 void KBounceWall::finish( bool shorten, Direction dir )
0279 {
0280     int left = static_cast<int>( m_boundingRect.left() );
0281     int top = static_cast<int>( m_boundingRect.top() );
0282     int right = static_cast<int>( m_boundingRect.right() );
0283     int bottom = static_cast<int>( m_boundingRect.bottom() );
0284 
0285     if ( shorten ) {
0286         switch ( dir )
0287         {
0288             case Left: left++; break;
0289             case Up: top++; break;
0290             case Right: right--; break;
0291             case Down: bottom--; break;
0292         }
0293     }
0294 
0295     Q_EMIT finished( left, top, right, bottom );
0296     hide();
0297 
0298     if (KBounceSettings::playSounds())
0299         m_soundReflect.start();
0300 }
0301 
0302 void KBounceWall::setWallVelocity(qreal velocity)
0303 {
0304     m_wallVelocity = velocity;
0305 }
0306 
0307 #include "moc_wall.cpp"