1/*
2 * Copyright (C) 2000-2005 Stefan Schimanski <1Stein@gmx.de>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the Free
16 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#include "wall.h"
20
21#include <cmath>
22
23#include <kdebug.h>
24#include <KRandom>
25#include <KStandardDirs>
26#include <QPainter>
27
28#include "board.h"
29#include "renderer.h"
30#include "settings.h"
31
32QSize KBounceWall::s_tileSize;
33KBounceRenderer * KBounceWall::m_renderer = NULL;
34KBounceWall::Sprites * KBounceWall::s_sprites = NULL;
35
36
37KBounceWall::KBounceWall( Direction dir, KBounceRenderer* renderer, KBounceBoard* board )
38: KGameRenderedItem( renderer,"",board )
39, m_board( board )
40, m_dir( dir )
41, m_soundWallstart( KStandardDirs::locate( "appdata", "sounds/wallstart.wav" ) )
42, m_soundReflect( KStandardDirs::locate( "appdata", "sounds/reflect.wav" ) )
43{
44 // The wall velocity would initialised on every new level.
45 m_wallVelocity = 0.0;
46
47 if (!s_sprites) {
48 s_sprites = new Sprites;
49 }
50
51 if (!m_renderer) {
52 m_renderer = renderer;
53 }
54}
55
56KBounceWall::~KBounceWall()
57{
58}
59
60void KBounceWall::collide( KBounceCollision collision )
61{
62 if ( !isVisible() )
63 return;
64
65 foreach( const KBounceHit &hit, collision ) {
66 switch (hit.type) {
67 case ALL:
68 break;
69 case TILE:
70 finish();
71 break;
72 case BALL:
73 if (safeEdgeHit(hit.boundingRect)) {
74 KBounceVector normal = hit.normal;
75 if (qAbs(normal.x) < qAbs(normal.y)) { // vertical
76 if (m_dir == Up || m_dir == Down) {
77 finish( true, m_dir );
78 }
79 }
80 else if (m_dir == Left || m_dir == Right) {
81 finish( true, m_dir );
82 }
83 } else {
84 emit died();
85 hide();
86 }
87 break;
88 case WALL:
89 if (safeEdgeHit(hit.boundingRect)) {
90 finish();
91 }
92 break;
93 }
94 }
95}
96
97
98void KBounceWall::goForward()
99{
100 if (!isVisible()) {
101 return;
102 }
103
104 switch( m_dir ) {
105 case Up:
106 m_boundingRect.setTop( m_boundingRect.top() - m_wallVelocity );
107 m_nextBoundingRect.setTop( m_boundingRect.top() - m_wallVelocity );
108 break;
109 case Left:
110 m_boundingRect.setLeft( m_boundingRect.left() - m_wallVelocity );
111 m_nextBoundingRect.setLeft( m_boundingRect.left() - m_wallVelocity );
112 break;
113 case Down:
114 m_boundingRect.setBottom( m_boundingRect.bottom() + m_wallVelocity );
115 m_nextBoundingRect.setBottom( m_boundingRect.bottom() + m_wallVelocity );
116 break;
117 case Right:
118 m_boundingRect.setRight( m_boundingRect.right() + m_wallVelocity );
119 m_nextBoundingRect.setRight( m_boundingRect.right() + m_wallVelocity );
120 break;
121 }
122}
123
124void KBounceWall::update()
125{
126 if (!isVisible())
127 return;
128
129 int boundingRectWidth = static_cast<int>(std::ceil(m_boundingRect.width() * s_tileSize.width()));
130 int boundingRectHeight = static_cast<int>(std::ceil(m_boundingRect.height() * s_tileSize.height()));
131
132 if (!(boundingRectWidth && boundingRectHeight))
133 return;
134
135 int tileWidth = s_tileSize.width();
136 int tileHeight = s_tileSize.height();
137
138 QSize pixSize;
139 if (m_dir == Left || m_dir == Right) {
140 pixSize = QSize(boundingRectWidth + 64 - (boundingRectWidth%64), boundingRectHeight);
141 } else {
142 pixSize = QSize(boundingRectWidth, boundingRectHeight + 64 - (boundingRectHeight%64));
143 }
144
145 QPixmap px;
146 if (pixmap().width() < pixSize.width() || pixmap().height() < pixSize.height()) {
147 px = QPixmap(pixSize);
148 } else {
149 px = pixmap(); // already ARGB
150 }
151 px.fill(Qt::transparent);
152
153 QPainter p(&px);
154
155 QPointF offset = m_board->mapPosition(m_boundingRect.topLeft());
156
157 switch ( m_dir ) {
158 case Up: {
159 const int split = qMin(tileHeight, boundingRectHeight);
160 p.drawPixmap(0, 0, s_sprites->wallEndUp, 0, 0, tileWidth, split);
161 p.drawTiledPixmap(0, split, tileWidth, boundingRectHeight - split, s_sprites->wallV, 0, offset.y());
162 break;
163 }
164 case Right: {
165 const int split = qMin(tileWidth, boundingRectWidth);
166 p.drawPixmap(boundingRectWidth - tileWidth, 0, split, tileHeight, s_sprites->wallEndRight);
167 p.drawTiledPixmap(0, 0, boundingRectWidth - split, tileHeight, s_sprites->wallH);
168 break;
169 }
170 case Down: {
171 const int split = qMin(tileHeight, boundingRectHeight);
172 p.drawPixmap(0, boundingRectHeight - tileHeight, tileWidth, split, s_sprites->wallEndDown);
173 p.drawTiledPixmap(0, 0, tileWidth, boundingRectHeight - split, s_sprites->wallV);
174 break;
175 }
176 case Left:
177 const int split = qMin(boundingRectWidth, tileWidth);
178 p.drawPixmap(0, 0, split, tileHeight, s_sprites->wallEndLeft);
179 p.drawTiledPixmap(split, 0, boundingRectWidth - split, tileHeight, s_sprites->wallH, offset.x());
180 }
181 setPos(offset);
182 p.end();
183 setPixmap(px);
184}
185
186void KBounceWall::loadSprites() {
187 s_sprites->wallEndLeft = m_renderer->spritePixmap("wallEndLeft", s_tileSize);
188 s_sprites->wallEndUp = m_renderer->spritePixmap("wallEndUp", s_tileSize);
189 s_sprites->wallEndRight = m_renderer->spritePixmap("wallEndRight", s_tileSize);
190 s_sprites->wallEndDown = m_renderer->spritePixmap("wallEndDown", s_tileSize);
191
192 s_sprites->wallH = m_renderer->spritePixmap("wallH", QSize(32 * s_tileSize.width(), s_tileSize.height()));
193 s_sprites->wallV = m_renderer->spritePixmap("wallV", QSize(s_tileSize.width(), 18*s_tileSize.height()));
194}
195
196void KBounceWall::resize( const QSize& tileSize )
197{
198 if ( tileSize != s_tileSize ) {
199 s_tileSize = tileSize;
200 loadSprites();
201 update();
202 }
203}
204
205void KBounceWall::build( int x, int y )
206{
207 if (isVisible())
208 return;
209
210 if ( m_dir == Up || m_dir == Down ) {
211 m_boundingRect.setTop( y );
212
213 if (m_dir == Down) {
214 m_boundingRect.setBottom(y + 1);
215 } else {
216 m_boundingRect.setBottom( y );
217 }
218
219 m_boundingRect.setLeft( x );
220 m_boundingRect.setRight( x + 1 );
221 }
222 else if ( m_dir == Left || m_dir == Right ) {
223 m_boundingRect.setTop( y );
224 m_boundingRect.setBottom( y + 1 );
225 m_boundingRect.setLeft( x );
226
227 if (m_dir == Right) {
228 m_boundingRect.setRight(x + 1);
229 } else {
230 m_boundingRect.setRight( x );
231 }
232 }
233
234
235 m_nextBoundingRect = m_boundingRect;
236
237 setPixmap(QPixmap());
238
239 setPos(m_board->mapPosition(QPointF( x, y )));
240 show();
241
242 if ( KBounceSettings::playSounds() )
243 m_soundWallstart.start();
244}
245
246QRectF KBounceWall::nextBoundingRect() const
247{
248 return m_nextBoundingRect;
249}
250
251bool KBounceWall::safeEdgeHit( const QRectF& rect2 ) const
252{
253 bool safeEdgeHit = false;
254
255 QPointF p1, p2, p3;
256 switch ( m_dir )
257 {
258 case Up:
259 p1 = m_nextBoundingRect.topLeft();
260 p2 = m_nextBoundingRect.topRight();
261 break;
262 case Right:
263 p1 = m_nextBoundingRect.topRight();
264 p2 = m_nextBoundingRect.bottomRight();
265 break;
266 case Down:
267 p1 = m_nextBoundingRect.bottomRight();
268 p2 = m_nextBoundingRect.bottomLeft();
269 break;
270 case Left:
271 p1 = m_nextBoundingRect.bottomLeft();
272 p2 = m_nextBoundingRect.topLeft();
273 break;
274 default:
275 Q_ASSERT(false);
276 break;
277 }
278 p3.setX( ( p1.x() + p2.x() ) / 2.0 );
279 p3.setY( ( p1.y() + p2.y() ) / 2.0 );
280
281 if ( rect2.contains( p1 ) )
282 safeEdgeHit = true;
283 else if ( rect2.contains( p2 ) )
284 safeEdgeHit = true;
285 else if ( rect2.contains( p3 ) )
286 safeEdgeHit = true;
287
288 return safeEdgeHit;
289}
290
291void KBounceWall::finish( bool shorten, Direction dir )
292{
293 int left = static_cast<int>( m_boundingRect.left() );
294 int top = static_cast<int>( m_boundingRect.top() );
295 int right = static_cast<int>( m_boundingRect.right() );
296 int bottom = static_cast<int>( m_boundingRect.bottom() );
297
298 if ( shorten ) {
299 switch ( dir )
300 {
301 case Left: left++; break;
302 case Up: top++; break;
303 case Right: right--; break;
304 case Down: bottom--; break;
305 }
306 }
307
308 emit finished( left, top, right, bottom );
309 hide();
310
311 if (KBounceSettings::playSounds())
312 m_soundReflect.start();
313}
314
315void KBounceWall::setWallVelocity(qreal velocity)
316{
317 m_wallVelocity = velocity;
318}
319
320
321#include "wall.moc"
322
323