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 | |
32 | QSize KBounceWall::s_tileSize; |
33 | KBounceRenderer * KBounceWall::m_renderer = NULL; |
34 | KBounceWall::Sprites * KBounceWall::s_sprites = NULL; |
35 | |
36 | |
37 | KBounceWall::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 | |
56 | KBounceWall::~KBounceWall() |
57 | { |
58 | } |
59 | |
60 | void 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 | |
98 | void 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 | |
124 | void 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 | |
186 | void 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 | |
196 | void KBounceWall::resize( const QSize& tileSize ) |
197 | { |
198 | if ( tileSize != s_tileSize ) { |
199 | s_tileSize = tileSize; |
200 | loadSprites(); |
201 | update(); |
202 | } |
203 | } |
204 | |
205 | void 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 | |
246 | QRectF KBounceWall::nextBoundingRect() const |
247 | { |
248 | return m_nextBoundingRect; |
249 | } |
250 | |
251 | bool 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 | |
291 | void 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 | |
315 | void KBounceWall::setWallVelocity(qreal velocity) |
316 | { |
317 | m_wallVelocity = velocity; |
318 | } |
319 | |
320 | |
321 | #include "wall.moc" |
322 | |
323 | |