Changeset View
Changeset View
Standalone View
Standalone View
board.cpp
Show All 10 Lines | |||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | * Library General Public License for more details. | 12 | * Library General Public License for more details. | ||
13 | * | 13 | * | ||
14 | * You should have received a copy of the GNU Library General Public | 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 | 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. | 16 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | 17 | */ | ||
18 | 18 | | |||
19 | #include "board.h" | | |||
20 | | ||||
21 | #include <KRandom> | 19 | #include <KRandom> | ||
22 | 20 | | |||
23 | #include <QGraphicsScene> | 21 | #include <QGraphicsScene> | ||
24 | #include <QTimer> | 22 | #include <QTimer> | ||
25 | #include <QPainter> | 23 | #include <QPainter> | ||
26 | 24 | | |||
27 | #include "ball.h" | 25 | #include "ball.h" | ||
28 | #include "gameobject.h" | 26 | #include "gameobject.h" | ||
29 | #include "wall.h" | 27 | #include "wall.h" | ||
30 | #include "debug.h" | 28 | #include "debug.h" | ||
29 | #include "board.h" | ||||
31 | 30 | | |||
32 | #include <cmath> | 31 | #include <cmath> | ||
33 | 32 | | |||
34 | #define DIR_UP 0 | 33 | #define DIR_UP 0 | ||
35 | #define DIR_RIGHT 1 | 34 | #define DIR_RIGHT 1 | ||
36 | #define DIR_DOWN 2 | 35 | #define DIR_DOWN 2 | ||
37 | #define DIR_LEFT 3 | 36 | #define DIR_LEFT 3 | ||
38 | 37 | | |||
39 | 38 | /** | |||
39 | * KBounceBoard class | ||||
40 | * | ||||
41 | * Constructor to set game board configurations | ||||
42 | * @param parent Superclass parameter | ||||
yurchor: @param renderer | |||||
43 | * @see board.h | ||||
44 | */ | ||||
40 | KBounceBoard::KBounceBoard( KBounceRenderer* renderer ) | 45 | KBounceBoard::KBounceBoard(KBounceRenderer* renderer) | ||
41 | : QGraphicsObject() | 46 | : QGraphicsObject() | ||
42 | , m_renderer( renderer ) | 47 | , m_renderer(renderer) // Set initial renderer value | ||
aacid: this comment is useless | |||||
43 | { | 48 | { | ||
49 | // Create timer and set it interval | ||||
44 | m_clock = new QTimer( this ); | 50 | m_clock = new QTimer(this); | ||
45 | m_clock->setInterval( GAME_DELAY ); | 51 | m_clock->setInterval(GAME_DELAY); | ||
52 | | ||||
53 | // Add game rule "timeout" | ||||
46 | connect(m_clock, &QTimer::timeout, this, &KBounceBoard::tick); | 54 | connect(m_clock, &QTimer::timeout, this, &KBounceBoard::tick); | ||
47 | 55 | | |||
48 | m_walls.append( new KBounceWall( KBounceWall::Up, m_renderer, this ) ); | 56 | // Add the 4 initial walls (border) to the walls list | ||
49 | m_walls.append( new KBounceWall( KBounceWall::Right, m_renderer, this ) ); | 57 | m_list_walls.append(new KBounceWall( KBounceWall::Up, m_renderer, this )); | ||
50 | m_walls.append( new KBounceWall( KBounceWall::Down, m_renderer, this ) ); | 58 | m_list_walls.append(new KBounceWall( KBounceWall::Right, m_renderer, this )); | ||
51 | m_walls.append( new KBounceWall( KBounceWall::Left, m_renderer, this ) ); | 59 | m_list_walls.append(new KBounceWall( KBounceWall::Down, m_renderer, this )); | ||
52 | foreach( KBounceWall* wall, m_walls ) | 60 | m_list_walls.append(new KBounceWall( KBounceWall::Left, m_renderer, this )); | ||
61 | | ||||
62 | foreach(KBounceWall* wall, m_list_walls) | ||||
53 | { | 63 | { | ||
64 | // Hide the wall | ||||
54 | wall->hide(); | 65 | wall->hide(); | ||
66 | | ||||
67 | // Set game rule "wall died" | ||||
55 | connect( wall, &KBounceWall::died, this, &KBounceBoard::wallDied ); | 68 | connect(wall, &KBounceWall::died, this, &KBounceBoard::wallDied); | ||
69 | | ||||
70 | // Set game rule "wall finished" | ||||
56 | connect( wall, &KBounceWall::finished, this, &KBounceBoard::wallFinished ); | 71 | connect(wall, &KBounceWall::finished, this, &KBounceBoard::wallFinished); | ||
57 | } | 72 | } | ||
58 | 73 | | |||
59 | clear(); | 74 | clear(); | ||
60 | 75 | | |||
61 | // Initialize this members with the old default values. | 76 | // Initialize this members with the old default values. | ||
62 | m_ballVelocity = 0.125; | 77 | m_ballVelocity = 0.125; | ||
63 | m_wallVelocity = 0.125; | 78 | m_wallVelocity = 0.125; | ||
64 | } | 79 | } | ||
65 | 80 | | |||
81 | | ||||
82 | /** | ||||
83 | * KbounceBoard destructor | ||||
84 | * @see board.h | ||||
85 | */ | ||||
66 | KBounceBoard::~KBounceBoard() | 86 | KBounceBoard::~KBounceBoard() | ||
67 | { | 87 | { | ||
68 | qDeleteAll( m_balls ); | 88 | qDeleteAll(m_list_balls); | ||
69 | qDeleteAll( m_walls ); | 89 | qDeleteAll(m_list_walls); | ||
70 | } | 90 | } | ||
71 | 91 | | |||
92 | /** | ||||
93 | * Resize the board and their components | ||||
94 | * @param size new size | ||||
95 | * @see board.h | ||||
96 | */ | ||||
72 | void KBounceBoard::resize( QSize& size ) | 97 | void KBounceBoard::resize(QSize& size) | ||
73 | { | 98 | { | ||
74 | //Pause the clock to prevent ticks during resize ... | 99 | //Pause the clock to prevent ticks during resize ... | ||
75 | bool alreadyPaused = false; | 100 | bool alreadyPaused = false; | ||
101 | | ||||
76 | if (!m_clock->isActive()) | 102 | if (!m_clock->isActive()) | ||
77 | { | 103 | alreadyPaused = true; // ... but only when we are not already paused | ||
78 | // ... but only when we are not already paused | | |||
79 | alreadyPaused = true; | | |||
80 | } | | |||
81 | else | 104 | else | ||
82 | { | | |||
83 | setPaused(true); | 105 | setPaused(true); | ||
84 | } | | |||
85 | 106 | | |||
86 | int minTileSize; | 107 | // Calculate minimum tile size | ||
87 | if ( TILE_NUM_H * size.width() - TILE_NUM_W * size.height() > 0 ) { | 108 | int sizeFactor = (TILE_NUM_H * size.width()) - (TILE_NUM_W * size.height()); | ||
88 | minTileSize = size.height() / TILE_NUM_H; | 109 | int minTileSize = sizeFactor > 0 ? size.height() / TILE_NUM_H : size.width() / TILE_NUM_W; | ||
89 | } else { | | |||
90 | minTileSize = size.width() / TILE_NUM_W; | | |||
91 | } | | |||
92 | 110 | | |||
111 | // Create new tile size | ||||
93 | m_tileSize = QSize( minTileSize, minTileSize ); | 112 | m_tileSize = QSize(minTileSize, minTileSize); | ||
94 | 113 | | |||
95 | foreach( KBounceBall* ball, m_balls ) { | 114 | // Resize each ball | ||
115 | foreach(KBounceBall* ball, m_list_balls) { | ||||
96 | ball->resize( m_tileSize ); | 116 | ball->resize(m_tileSize); | ||
97 | } | 117 | } | ||
98 | 118 | | |||
99 | foreach( KBounceWall* wall, m_walls ) { | 119 | // Resize each wall | ||
120 | foreach(KBounceWall* wall, m_list_walls) { | ||||
100 | wall->resize( m_tileSize ); | 121 | wall->resize(m_tileSize); | ||
101 | } | 122 | } | ||
102 | 123 | | |||
124 | // Set new board size | ||||
103 | size.setWidth( minTileSize * TILE_NUM_W ); | 125 | size.setWidth(minTileSize * TILE_NUM_W); | ||
104 | size.setHeight( minTileSize * TILE_NUM_H ); | 126 | size.setHeight(minTileSize * TILE_NUM_H); | ||
127 | | ||||
128 | // Unpause the board | ||||
105 | if (!alreadyPaused) | 129 | if (!alreadyPaused) | ||
106 | { | | |||
107 | setPaused(false); | 130 | setPaused(false); | ||
108 | } | 131 | } | ||
109 | } | | |||
110 | 132 | | |||
133 | /** | ||||
134 | * Set variables to start a new level | ||||
135 | * @param level new level | ||||
136 | * @see board.h | ||||
137 | */ | ||||
111 | void KBounceBoard::newLevel( int level ) | 138 | void KBounceBoard::newLevel(int level) | ||
112 | { | 139 | { | ||
140 | // Stop the timer | ||||
113 | m_clock->stop(); | 141 | m_clock->stop(); | ||
142 | | ||||
143 | // Clear the board | ||||
114 | clear(); | 144 | clear(); | ||
145 | | ||||
146 | // Emit signal to apply filled reset | ||||
115 | emit fillChanged( m_filled ); | 147 | emit fillChanged(m_filled); | ||
116 | 148 | | |||
117 | while ( m_balls.count() > level + 1 ) | 149 | // "if the board have more balls than necessary, remove the last one" | ||
150 | while (m_list_balls.count() > level + 1) | ||||
118 | { | 151 | { | ||
119 | delete m_balls.back(); | 152 | delete m_list_balls.back(); | ||
120 | m_balls.removeLast(); | 153 | m_list_balls.removeLast(); | ||
121 | } | 154 | } | ||
122 | while ( m_balls.count() < level + 1) | 155 | | ||
156 | // "if the board are lack of balls, add a new one" | ||||
157 | while (m_list_balls.count() < level + 1) | ||||
123 | { | 158 | { | ||
124 | KBounceBall* ball = new KBounceBall( m_renderer, this ); | 159 | KBounceBall* ball = new KBounceBall(m_renderer, this); | ||
125 | ball->resize( m_tileSize ); | 160 | ball->resize(m_tileSize); | ||
126 | m_balls.append( ball ); | 161 | m_list_balls.append(ball); | ||
127 | } | 162 | } | ||
128 | foreach( KBounceBall* ball, m_balls ) | 163 | | ||
164 | // Set the position and velocity for each ball | ||||
165 | foreach(KBounceBall* ball, m_list_balls) | ||||
129 | { | 166 | { | ||
130 | ball->setRelativePos( 4 + KRandom::random() % ( TILE_NUM_W - 8 ), | 167 | // Get random position | ||
131 | 4 + KRandom::random() % ( TILE_NUM_H - 8 ) ); | 168 | qreal random_x_pos = 4 + KRandom::random() % ( TILE_NUM_W - 8 ); | ||
132 | ball->setVelocity( ((KRandom::random() & 1)*2-1)*m_ballVelocity, | 169 | qreal random_y_pos = 4 + KRandom::random() % ( TILE_NUM_H - 8 ); | ||
133 | ((KRandom::random() & 1)*2-1)*m_ballVelocity ); | 170 | | ||
171 | // Set random positon | ||||
172 | ball->setRelativePos(random_x_pos ,random_y_pos); | ||||
173 | | ||||
174 | // Get random velocity | ||||
175 | qreal random_x_vel = ((KRandom::random() & 1)*2 - 1) * m_ballVelocity; | ||||
176 | qreal random_y_vel = ((KRandom::random() & 1)*2 - 1)* m_ballVelocity; | ||||
177 | | ||||
178 | // Set random velocity | ||||
179 | ball->setVelocity(random_x_vel ,random_y_vel); | ||||
180 | | ||||
134 | ball->setRandomFrame(); | 181 | ball->setRandomFrame(); | ||
182 | | ||||
183 | // Show the ball | ||||
135 | ball->show(); | 184 | ball->show(); | ||
136 | } | 185 | } | ||
186 | | ||||
187 | // Emit signal to apply level changes | ||||
137 | emit ballsChanged( level + 1 ); | 188 | emit ballsChanged(level + 1); | ||
138 | 189 | | |||
139 | foreach( KBounceWall* wall, m_walls ) | 190 | // Set velocity for each wall, and hide them | ||
191 | foreach(KBounceWall* wall, m_list_walls) | ||||
140 | { | 192 | { | ||
141 | wall->setWallVelocity(m_wallVelocity); | 193 | wall->setWallVelocity(m_wallVelocity); | ||
142 | wall->hide(); | 194 | wall->hide(); | ||
143 | } | 195 | } | ||
144 | } | 196 | } | ||
145 | 197 | | |||
146 | void KBounceBoard::setPaused( bool val ) | 198 | /** | ||
199 | * Pause the timer | ||||
200 | * @param flag "T" to pause "F" to unpause | ||||
201 | * @see board.h | ||||
202 | */ | ||||
203 | void KBounceBoard::setPaused(bool flag) | ||||
147 | { | 204 | { | ||
148 | if ( val ) | 205 | if (flag) | ||
149 | m_clock->stop(); | 206 | m_clock->stop(); | ||
150 | else | 207 | else | ||
151 | m_clock->start(); | 208 | m_clock->start(); | ||
152 | } | 209 | } | ||
153 | 210 | | |||
211 | /** | ||||
212 | * Set ball velocity | ||||
213 | * @param vel new velocity | ||||
214 | * @see board.h | ||||
215 | */ | ||||
154 | void KBounceBoard::setBallVelocity(qreal vel) | 216 | void KBounceBoard::setBallVelocity(qreal vel) | ||
155 | { | 217 | { | ||
156 | m_ballVelocity = vel; | 218 | m_ballVelocity = vel; | ||
157 | } | 219 | } | ||
158 | 220 | | |||
221 | /** | ||||
222 | * Set wall velocity | ||||
223 | * @param vel new velocity | ||||
224 | * @see board.h | ||||
225 | */ | ||||
159 | void KBounceBoard::setWallVelocity(qreal vel) | 226 | void KBounceBoard::setWallVelocity(qreal vel) | ||
160 | { | 227 | { | ||
161 | m_wallVelocity = vel; | 228 | m_wallVelocity = vel; | ||
162 | } | 229 | } | ||
163 | 230 | | |||
231 | /** | ||||
232 | * Build a wall | ||||
233 | * @param pos mouse position | ||||
234 | * @param vertical wall orientation | ||||
235 | * @see board.h | ||||
236 | */ | ||||
164 | void KBounceBoard::buildWall( const QPointF& pos, bool vertical ) | 237 | void KBounceBoard::buildWall(const QPointF& pos, bool vertical) | ||
165 | { | 238 | { | ||
166 | QPointF unmapped( pos.x() - x(), pos.y() - y()); | 239 | QPointF unmapped(pos.x() - x(), pos.y() - y()); | ||
167 | int x = static_cast<int>( unmapped.x() / m_tileSize.width() ); | 240 | int x = static_cast<int>( unmapped.x() / m_tileSize.width() ); | ||
168 | int y = static_cast<int>( unmapped.y() / m_tileSize.height() ); | 241 | int y = static_cast<int>( unmapped.y() / m_tileSize.height() ); | ||
169 | 242 | | |||
170 | if ( x < 0 || x >= TILE_NUM_W ) | 243 | if (x < 0 || x >= TILE_NUM_W) | ||
171 | { | 244 | { | ||
172 | qCDebug(KBOUNCE_LOG) << "Wall x position out of board."; | 245 | qCDebug(KBOUNCE_LOG) << "Wall x position out of board."; | ||
173 | return; | 246 | return; | ||
174 | } | 247 | } | ||
248 | | ||||
175 | if ( y < 0 || y >= TILE_NUM_H ) | 249 | if (y < 0 || y >= TILE_NUM_H) | ||
176 | { | 250 | { | ||
177 | qCDebug(KBOUNCE_LOG) << "Wall y position out of board."; | 251 | qCDebug(KBOUNCE_LOG) << "Wall y position out of board."; | ||
178 | return; | 252 | return; | ||
179 | } | 253 | } | ||
254 | | ||||
180 | if ( m_tiles[x][y] != Free ) | 255 | if (m_tiles[x][y] != Free) | ||
181 | { | 256 | { | ||
182 | qCDebug(KBOUNCE_LOG) << "Wall could not be build in a field which is not free."; | 257 | qCDebug(KBOUNCE_LOG) << "Wall could not be build in a field which is not free."; | ||
183 | return; | 258 | return; | ||
184 | } | 259 | } | ||
185 | 260 | | |||
186 | if ( !vertical ) | 261 | // Set wall orientation based position | ||
262 | int direction_1, direction_2; | ||||
263 | | ||||
264 | if(vertical) | ||||
187 | { | 265 | { | ||
188 | m_walls[DIR_LEFT]->build( x, y ); | 266 | direction_1 = DIR_UP; | ||
189 | m_walls[DIR_RIGHT]->build( x, y ); | 267 | direction_2 = DIR_DOWN; | ||
190 | } | 268 | } | ||
191 | else | 269 | else | ||
192 | { | 270 | { | ||
193 | m_walls[DIR_UP]->build( x, y ); | 271 | direction_1 = DIR_LEFT; | ||
194 | m_walls[DIR_DOWN]->build( x, y ); | 272 | direction_2 = DIR_RIGHT; | ||
195 | } | 273 | } | ||
274 | | ||||
275 | // Build two oposite walls | ||||
276 | m_list_walls[direction_1]->build(x, y); | ||||
277 | m_list_walls[direction_2]->build(x, y); | ||||
196 | } | 278 | } | ||
197 | 279 | | |||
280 | /** | ||||
281 | * Get ball ammount | ||||
282 | * @return ball ammount | ||||
283 | * @see board.h | ||||
284 | */ | ||||
198 | int KBounceBoard::balls() | 285 | int KBounceBoard::balls() | ||
199 | { | 286 | { | ||
200 | return m_balls.count(); | 287 | return m_list_balls.count(); | ||
201 | } | 288 | } | ||
202 | 289 | | |||
290 | /** | ||||
291 | * Get filled percentage | ||||
292 | * @return filled percentage | ||||
293 | * @see board.h | ||||
294 | */ | ||||
203 | int KBounceBoard::filled() | 295 | int KBounceBoard::filled() | ||
204 | { | 296 | { | ||
205 | return m_filled; | 297 | return m_filled; | ||
206 | } | 298 | } | ||
207 | 299 | | |||
300 | /** | ||||
301 | * Check object collision | ||||
302 | * @param object Object to be checked | ||||
303 | * @param rect tile rectangle | ||||
304 | * @param type object type | ||||
305 | * @return collision result | ||||
306 | * @see board.h | ||||
307 | */ | ||||
208 | KBounceCollision KBounceBoard::checkCollision( void* object, const QRectF& rect, int type ) | 308 | KBounceCollision KBounceBoard::checkCollision(void* object, const QRectF& rect, int type) | ||
209 | { | 309 | { | ||
310 | // Create collision variables | ||||
210 | KBounceCollision result; | 311 | KBounceCollision result; | ||
312 | bool collisionCondition; | ||||
211 | 313 | | |||
314 | // Check for walls in a rectangular area | ||||
212 | if ( (type & TILE) != 0 ) | 315 | if ((type & TILE) != 0) | ||
213 | { | | |||
214 | result += checkCollisionTiles( rect ); | 316 | result += checkCollisionTiles(rect); | ||
215 | } | | |||
216 | 317 | | |||
318 | // Check for collision on top of the wall | ||||
217 | if ( (type & WALL) != 0 ) | 319 | if ((type & WALL) != 0) | ||
320 | foreach(KBounceWall* wall, m_list_walls) | ||||
218 | { | 321 | { | ||
219 | foreach( KBounceWall* wall, m_walls ) | 322 | collisionCondition = (object != wall) && wall->isVisible() && rect.intersects(wall->nextBoundingRect()); | ||
220 | { | 323 | if (collisionCondition) | ||
221 | if ( object != wall ) | | |||
222 | { | | |||
223 | if ( wall->isVisible() && rect.intersects( wall->nextBoundingRect() ) ) | | |||
224 | { | 324 | { | ||
225 | KBounceHit hit; | 325 | KBounceHit hit; | ||
226 | hit.type = WALL; | 326 | hit.type = WALL; | ||
227 | hit.boundingRect = wall->nextBoundingRect(); | 327 | hit.boundingRect = wall->nextBoundingRect(); | ||
228 | hit.normal = KBounceVector::normal( rect, hit.boundingRect ); | 328 | hit.normal = KBounceVector::normal(rect, hit.boundingRect); | ||
229 | result += hit; | 329 | result += hit; | ||
230 | } | 330 | } | ||
231 | } | 331 | } | ||
232 | } | | |||
233 | } | | |||
234 | 332 | | |||
333 | // Check for collision on a unfinished wall | ||||
235 | if ( (type & BALL) != 0 ) | 334 | if ((type & BALL) != 0) | ||
335 | foreach(KBounceBall* ball, m_list_balls) | ||||
236 | { | 336 | { | ||
237 | foreach( KBounceBall* ball, m_balls ) | 337 | collisionCondition = (object != ball) && rect.intersects(ball->nextBoundingRect()); | ||
238 | { | 338 | if (collisionCondition) | ||
239 | if ( object != ball ) | | |||
240 | { | | |||
241 | if ( rect.intersects( ball->nextBoundingRect() ) ) | | |||
242 | { | 339 | { | ||
243 | KBounceHit hit; | 340 | KBounceHit hit; | ||
244 | hit.type = BALL; | 341 | hit.type = BALL; | ||
245 | hit.boundingRect = ball->nextBoundingRect(); | 342 | hit.boundingRect = ball->nextBoundingRect(); | ||
246 | hit.normal = KBounceVector::normal( rect, hit.boundingRect ); | 343 | hit.normal = KBounceVector::normal(rect, hit.boundingRect); | ||
247 | result += hit; | 344 | result += hit; | ||
248 | } | 345 | } | ||
249 | } | 346 | } | ||
250 | } | | |||
251 | } | | |||
252 | 347 | | |||
253 | return result; | 348 | return result; | ||
254 | } | 349 | } | ||
255 | 350 | | |||
351 | /** | ||||
352 | * Check for collision to get bounce "normal" | ||||
353 | * @param rect tile rectangle | ||||
354 | * @return collision result | ||||
355 | * @see board.h | ||||
356 | */ | ||||
256 | KBounceCollision KBounceBoard::checkCollisionTiles( const QRectF& rect) | 357 | KBounceCollision KBounceBoard::checkCollisionTiles(const QRectF& rect) | ||
257 | { | 358 | { | ||
258 | KBounceVector normal( 0, 0 ); | 359 | KBounceVector normal(0, 0); | ||
259 | 360 | | |||
260 | // This small constant is added to each of the coordinates to | 361 | // This small constant is added to each of the coordinates to | ||
261 | // avoid positive collision test result when tested rect lies | 362 | // avoid positive collision test result when tested rect lies | ||
262 | // on the edge of non-free space | 363 | // on the edge of non-free space | ||
263 | qreal D = 0.01; | 364 | qreal D = 0.01; | ||
264 | 365 | | |||
366 | // Check for bounce top left | ||||
265 | QPointF p = rect.topLeft(); | 367 | QPointF p = rect.topLeft(); | ||
266 | int ul = m_tiles[static_cast<int>( p.x() + D )][static_cast<int>( p.y() + D )]; | 368 | int ul = m_tiles[static_cast<int>( p.x() + D )][static_cast<int>( p.y() + D )]; | ||
267 | if ( ul != Free ) normal += KBounceVector( 1, 1 ); | 369 | if (ul != Free) normal += KBounceVector(1, 1); | ||
268 | 370 | | |||
371 | // Check for bounce top right | ||||
269 | p = rect.topRight(); | 372 | p = rect.topRight(); | ||
270 | int ur = m_tiles[static_cast<int>( p.x() - D )][static_cast<int>( p.y() + D )]; | 373 | int ur = m_tiles[static_cast<int>( p.x() - D )][static_cast<int>( p.y() + D )]; | ||
271 | if ( ur != Free) normal += KBounceVector( -1, 1 ); | 374 | if (ur != Free) normal += KBounceVector(-1, 1); | ||
272 | 375 | | |||
376 | // Check for bounce bottom right | ||||
273 | p = rect.bottomRight(); | 377 | p = rect.bottomRight(); | ||
274 | int lr = m_tiles[static_cast<int>( p.x() - D )][static_cast<int>( p.y() - D )]; | 378 | int lr = m_tiles[static_cast<int>( p.x() - D )][static_cast<int>( p.y() - D )]; | ||
275 | if ( lr != Free ) normal += KBounceVector( -1, -1 ); | 379 | if (lr != Free) normal += KBounceVector(-1, -1); | ||
276 | 380 | | |||
381 | // Check for bounce bottom left | ||||
277 | p = rect.bottomLeft(); | 382 | p = rect.bottomLeft(); | ||
278 | int ll = m_tiles[static_cast<int>( p.x() + D )][static_cast<int>( p.y() - D )]; | 383 | int ll = m_tiles[static_cast<int>( p.x() + D )][static_cast<int>( p.y() - D )]; | ||
279 | if ( ll != Free ) normal += KBounceVector( 1, -1 ); | 384 | if (ll != Free) normal += KBounceVector(1, -1); | ||
280 | 385 | | |||
386 | // Add collision entity | ||||
281 | KBounceCollision collision; | 387 | KBounceCollision collision; | ||
282 | if ( (ul != Free ) || ( ur != Free ) || ( lr != Free ) || ( ll != Free ) ) | 388 | if ((ul != Free) || (ur != Free) || (lr != Free) || (ll != Free)) | ||
283 | { | 389 | { | ||
284 | KBounceHit hit; | 390 | KBounceHit hit; | ||
285 | hit.type = TILE; | 391 | hit.type = TILE; | ||
286 | hit.normal = normal; | 392 | hit.normal = normal; | ||
287 | collision += hit; | 393 | collision += hit; | ||
288 | } | 394 | } | ||
289 | return collision; | 395 | return collision; | ||
290 | } | 396 | } | ||
291 | 397 | | |||
398 | /** | ||||
399 | * Check for collisions on all objects | ||||
400 | * @see board.h | ||||
401 | */ | ||||
292 | void KBounceBoard::checkCollisions() | 402 | void KBounceBoard::checkCollisions() | ||
293 | { | 403 | { | ||
294 | foreach( KBounceWall* wall, m_walls ) | 404 | // Collision for walls | ||
405 | foreach(KBounceWall* wall, m_list_walls) | ||||
295 | { | 406 | { | ||
296 | QRectF rect = wall->nextBoundingRect(); | 407 | QRectF rect = wall->nextBoundingRect(); | ||
297 | KBounceCollision collision; | 408 | KBounceCollision collision; | ||
298 | collision = checkCollision( wall, rect, ALL ); | 409 | collision = checkCollision(wall, rect, ALL); | ||
299 | wall->collide( collision ); | 410 | wall->collide(collision); | ||
300 | } | 411 | } | ||
301 | foreach( KBounceBall* ball, m_balls ) | 412 | | ||
413 | // Collision for balls | ||||
414 | foreach(KBounceBall* ball, m_list_balls) | ||||
302 | { | 415 | { | ||
303 | QRectF rect = ball->nextBoundingRect(); | 416 | QRectF rect = ball->nextBoundingRect(); | ||
304 | KBounceCollision collision; | 417 | KBounceCollision collision; | ||
305 | collision = checkCollision( ball, rect, ALL ); | 418 | collision = checkCollision(ball, rect, ALL); | ||
306 | ball->collide( collision ); | 419 | ball->collide(collision); | ||
307 | } | 420 | } | ||
308 | } | 421 | } | ||
309 | 422 | | |||
423 | /** | ||||
424 | * Get map tile position | ||||
425 | * @param pos mouse position | ||||
426 | * @return tile position | ||||
427 | * @see board.h | ||||
428 | */ | ||||
310 | QPoint KBounceBoard::mapPosition( const QPointF& pos ) const | 429 | QPoint KBounceBoard::mapPosition(const QPointF& pos) const | ||
311 | { | 430 | { | ||
312 | return QPoint( static_cast<int>( m_tileSize.width() * pos.x() ), | 431 | return QPoint( | ||
313 | static_cast<int>( m_tileSize.height() * pos.y() ) ); | 432 | static_cast<int>( m_tileSize.width() * pos.x()), | ||
433 | static_cast<int>( m_tileSize.height() * pos.y()) | ||||
434 | ); | ||||
314 | } | 435 | } | ||
315 | 436 | | |||
437 | /** | ||||
438 | * Get tile template | ||||
439 | * @return tile template | ||||
440 | * @see board.h | ||||
441 | */ | ||||
316 | QRectF KBounceBoard::boundingRect() const | 442 | QRectF KBounceBoard::boundingRect() const | ||
317 | { | 443 | { | ||
318 | return QRectF( x(), y(), | 444 | return QRectF( | ||
445 | x(), y(), | ||||
319 | TILE_NUM_W * m_tileSize.width(), | 446 | TILE_NUM_W * m_tileSize.width(), | ||
320 | TILE_NUM_H * m_tileSize.height() ); | 447 | TILE_NUM_H * m_tileSize.height() | ||
448 | ); | ||||
321 | } | 449 | } | ||
322 | 450 | | |||
451 | /** | ||||
452 | * Event to apply tick changes | ||||
453 | * @see board.h | ||||
454 | */ | ||||
323 | void KBounceBoard::tick() | 455 | void KBounceBoard::tick() | ||
324 | { | 456 | { | ||
457 | // Check all collisions | ||||
325 | checkCollisions(); | 458 | checkCollisions(); | ||
326 | 459 | | |||
327 | foreach( KBounceBall* ball, m_balls ) | 460 | // Move and update the balls | ||
461 | foreach(KBounceBall* ball, m_list_balls) | ||||
328 | { | 462 | { | ||
329 | ball->goForward(); | 463 | ball->goForward(); | ||
330 | } | | |||
331 | foreach( KBounceWall* wall, m_walls ) | | |||
332 | { | | |||
333 | wall->goForward(); | | |||
334 | } | | |||
335 | | ||||
336 | foreach( KBounceBall* ball, m_balls ) | | |||
337 | { | | |||
338 | ball->update(); | 464 | ball->update(); | ||
339 | } | 465 | } | ||
340 | 466 | | |||
341 | foreach( KBounceWall* wall, m_walls ) | 467 | // Move and update the walls | ||
468 | foreach(KBounceWall* wall, m_list_walls) | ||||
342 | { | 469 | { | ||
470 | wall->goForward(); | ||||
343 | wall->update(); | 471 | wall->update(); | ||
344 | } | 472 | } | ||
345 | } | 473 | } | ||
346 | 474 | | |||
475 | /** | ||||
476 | * Paint walls on board | ||||
477 | * @param background canvas context | ||||
478 | * @return canvas painted | ||||
479 | * @see board.h | ||||
480 | */ | ||||
347 | QPixmap KBounceBoard::applyWallsOn(const QPixmap &background) const | 481 | QPixmap KBounceBoard::applyWallsOn(const QPixmap &background) const | ||
348 | { | 482 | { | ||
349 | if (m_tileSize.isEmpty()) | 483 | if (m_tileSize.isEmpty()) | ||
350 | return background; | 484 | return background; | ||
351 | 485 | | |||
486 | // Get grid and wall images | ||||
352 | QPixmap walledBackground = background; | 487 | QPixmap walledBackground = background; | ||
353 | const QPixmap gridTile = m_renderer->spritePixmap(QStringLiteral("gridTile"), m_tileSize); | 488 | const QPixmap gridTile = m_renderer->spritePixmap(QStringLiteral("gridTile"), m_tileSize); | ||
354 | const QPixmap wallTile = m_renderer->spritePixmap(QStringLiteral("wallTile"), m_tileSize); | 489 | const QPixmap wallTile = m_renderer->spritePixmap(QStringLiteral("wallTile"), m_tileSize); | ||
490 | | ||||
491 | // Create painter | ||||
355 | QPainter p(&walledBackground); | 492 | QPainter p(&walledBackground); | ||
356 | for (int i = 0; i < TILE_NUM_W; ++i) { | 493 | | ||
357 | for (int j = 0; j < TILE_NUM_H; ++j) { | 494 | for (int i = 0; i < TILE_NUM_W; ++i) | ||
358 | switch (m_tiles[i][j]) { | 495 | { | ||
496 | for (int j = 0; j < TILE_NUM_H; ++j) | ||||
497 | { | ||||
498 | | ||||
499 | // Calculate pixmap values | ||||
500 | int pixmap_x = x() + i * m_tileSize.width(); | ||||
501 | int pixmap_y = y() + j * m_tileSize.height(); | ||||
502 | | ||||
503 | switch (m_tiles[i][j]) | ||||
504 | { | ||||
505 | // Draw grid tile | ||||
359 | case Free: | 506 | case Free: | ||
360 | p.drawPixmap(x() + i * m_tileSize.width(), y() + j * m_tileSize.height(), gridTile); | 507 | p.drawPixmap(pixmap_x, pixmap_y, gridTile); | ||
361 | break; | 508 | break; | ||
362 | 509 | | |||
363 | case Border: | 510 | case Border: | ||
364 | case Wall: | 511 | case Wall: | ||
365 | p.drawPixmap(x() + i * m_tileSize.width(), y() + j * m_tileSize.height(), wallTile); | 512 | // Draw wall tile | ||
513 | p.drawPixmap(pixmap_x, pixmap_y, wallTile); | ||||
366 | break; | 514 | break; | ||
367 | 515 | | |||
368 | default: | 516 | default: | ||
369 | break; | 517 | break; | ||
370 | } | 518 | } | ||
371 | } | 519 | } | ||
372 | } | 520 | } | ||
521 | | ||||
522 | // Return new painted background | ||||
373 | return walledBackground; | 523 | return walledBackground; | ||
374 | } | 524 | } | ||
375 | 525 | | |||
526 | /** | ||||
527 | * Insert wall and paint them | ||||
528 | * @param x1 initial x position | ||||
529 | * @param y1 initial x position | ||||
530 | * @param x2 final y position | ||||
531 | * @param y2 final y position | ||||
532 | * @see board.h | ||||
533 | */ | ||||
376 | void KBounceBoard::wallFinished( int x1, int y1, int x2, int y2 ) | 534 | void KBounceBoard::wallFinished(int x1, int y1, int x2, int y2) | ||
377 | { | 535 | { | ||
536 | // Insert walls | ||||
378 | for ( int x = x1; x < x2; x++ ) | 537 | for (int x = x1; x < x2; x++) | ||
379 | for ( int y = y1; y < y2; y++ ) | 538 | for (int y = y1; y < y2; y++) | ||
380 | m_tiles[x][y] = Wall; | 539 | m_tiles[x][y] = Wall; | ||
381 | 540 | | |||
382 | foreach ( KBounceBall* ball, m_balls ) | 541 | // Fill walls | ||
542 | foreach (KBounceBall* ball, m_list_balls) | ||||
383 | { | 543 | { | ||
384 | int x1 = static_cast<int>( ball->ballBoundingRect().x() ); | 544 | int x1 = static_cast<int>( ball->ballBoundingRect().x() ); | ||
385 | int y1 = static_cast<int>( ball->ballBoundingRect().y() ); | 545 | int y1 = static_cast<int>( ball->ballBoundingRect().y() ); | ||
386 | int x2 = static_cast<int>( ball->ballBoundingRect().right() ); | 546 | int x2 = static_cast<int>( ball->ballBoundingRect().right() ); | ||
387 | int y2 = static_cast<int>( ball->ballBoundingRect().bottom() ); | 547 | int y2 = static_cast<int>( ball->ballBoundingRect().bottom() ); | ||
388 | // try to fill from all edges | 548 | | ||
389 | // this way we can avoid most precision-related issues | 549 | // try to fill from all edges, this way we can avoid most precision-related issues | ||
390 | fill(x1, y1); | 550 | fill(x1, y1); | ||
391 | fill(x1, y2); | 551 | fill(x1, y2); | ||
392 | fill(x2, y1); | 552 | fill(x2, y1); | ||
393 | fill(x2, y2); | 553 | fill(x2, y2); | ||
394 | } | 554 | } | ||
395 | 555 | | |||
396 | for ( int x = 0; x < TILE_NUM_W; x++ ) | | |||
397 | for ( int y = 0; y < TILE_NUM_H; y++ ) | | |||
398 | if ( m_tiles[x][y] == Free ) | | |||
399 | m_tiles[x][y] = Wall; | | |||
400 | for ( int x = 0; x < TILE_NUM_W; x++ ) | | |||
401 | for ( int y = 0; y < TILE_NUM_H; y++ ) | | |||
402 | if ( m_tiles[x][y] == Temp ) | | |||
403 | m_tiles[x][y] = Free; | | |||
404 | | ||||
405 | int filled = 0; | 556 | int filled = 0; | ||
406 | for ( int i = 1; i < TILE_NUM_W - 1; i++ ) | 557 | | ||
407 | for ( int j = 1; j < TILE_NUM_H - 1; j++ ) | 558 | for (int i = 0; i < TILE_NUM_W; i++) | ||
408 | if ( m_tiles[i][j] == Wall ) | 559 | for (int j = 0; j < TILE_NUM_H; j++) { | ||
560 | | ||||
561 | // Validade wall interval | ||||
562 | bool inside_i = (i > 0) && (i < TILE_NUM_W - 1); | ||||
563 | bool inside_j = (j > 0) && (j < TILE_NUM_H - 1); | ||||
564 | | ||||
565 | // Convert free to wall | ||||
566 | if (m_tiles[i][j] == Free) | ||||
567 | m_tiles[i][j] = Wall; | ||||
568 | | ||||
569 | // Convert temp to free | ||||
570 | if (m_tiles[i][j] == Temp) | ||||
571 | m_tiles[i][j] = Free; | ||||
572 | | ||||
573 | // Count wall as filled if inside the board | ||||
574 | if (m_tiles[i][j] == Wall && inside_i && inside_j) | ||||
409 | filled++; | 575 | filled++; | ||
576 | } | ||||
577 | | ||||
578 | // Calculate filled percentage | ||||
410 | m_filled = filled * 100 / ( ( TILE_NUM_W - 2 ) * ( TILE_NUM_H - 2 ) ); | 579 | m_filled = filled * 100 / ((TILE_NUM_W - 2) * (TILE_NUM_H - 2)); | ||
411 | 580 | | |||
412 | scene()->setBackgroundBrush(applyWallsOn(m_renderer->renderBackground())); | 581 | // Render wall background | ||
582 | scene()->setBackgroundBrush( | ||||
583 | applyWallsOn(m_renderer->renderBackground()) | ||||
584 | ); | ||||
413 | 585 | | |||
586 | // Emit signal to apply filled percentage | ||||
414 | emit fillChanged( m_filled ); | 587 | emit fillChanged(m_filled); | ||
415 | } | 588 | } | ||
416 | 589 | | |||
590 | /** | ||||
591 | * Clear the board | ||||
592 | * @see board.h | ||||
593 | */ | ||||
417 | void KBounceBoard::clear() | 594 | void KBounceBoard::clear() | ||
418 | { | 595 | { | ||
419 | for ( int i = 0; i < TILE_NUM_W; i++ ) | 596 | for (int i = 0; i < TILE_NUM_W; i++) | ||
420 | m_tiles[i][0] = m_tiles[i][TILE_NUM_H-1] = Border; | 597 | for (int j = 0; j < TILE_NUM_H; j++) { | ||
421 | for ( int j = 0; j < TILE_NUM_H; j++ ) | 598 | | ||
422 | m_tiles[0][j] = m_tiles[TILE_NUM_W-1][j] = Border; | 599 | // Validade wall interval | ||
423 | for ( int i = 1; i < TILE_NUM_W - 1; i++ ) | 600 | bool inside_i = (i > 0) && (i < TILE_NUM_W - 1); | ||
424 | for ( int j = 1; j < TILE_NUM_H -1; j++ ) | 601 | bool inside_j = (j > 0) && (j < TILE_NUM_H - 1); | ||
425 | m_tiles[i][j] = Free; | 602 | | ||
603 | // Update to free or border | ||||
604 | m_tiles[i][j] = (inside_i && inside_j) ? Free : Border; | ||||
605 | } | ||||
606 | | ||||
607 | // Reset filled percentage | ||||
426 | m_filled = 0; | 608 | m_filled = 0; | ||
427 | } | 609 | } | ||
428 | 610 | | |||
611 | /** | ||||
612 | * Fill the tile | ||||
613 | * @param x position x | ||||
614 | * @param y position y | ||||
615 | * @see board.h | ||||
616 | */ | ||||
429 | void KBounceBoard::fill( int x, int y ) | 617 | void KBounceBoard::fill(int x, int y) | ||
430 | { | 618 | { | ||
619 | // "Stop if you found something" | ||||
431 | if ( m_tiles[x][y] != Free ) | 620 | if (m_tiles[x][y] != Free) | ||
432 | return; | 621 | return; | ||
622 | | ||||
433 | m_tiles[x][y] = Temp; | 623 | m_tiles[x][y] = Temp; | ||
434 | 624 | | |||
625 | // Fill the tile | ||||
435 | if ( y > 0 ) fill( x, y - 1 ); | 626 | if (y > 0) fill(x, y - 1); | ||
436 | if ( x < TILE_NUM_W - 1 ) fill ( x + 1, y ); | 627 | if (x < TILE_NUM_W - 1) fill(x + 1, y); | ||
437 | if ( y < TILE_NUM_H - 1 ) fill ( x, y + 1 ); | 628 | if (y < TILE_NUM_H - 1) fill(x, y + 1); | ||
438 | if ( x > 0 ) fill ( x - 1, y ); | 629 | if (x > 0) fill(x - 1, y); | ||
439 | } | 630 | } | ||
440 | 631 | | |||
441 | 632 | | |||
442 | 633 | |
@param renderer