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"