00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "game.h"
00021
00022
00023 #include <opie2/odebug.h>
00024 #include <qpe/qpeapplication.h>
00025 using namespace Opie::Core;
00026
00027
00028 #include <qtimer.h>
00029
00030
00031 #include <stdlib.h>
00032
00033 #define TILE_SIZE 9
00034
00035 #define TILE_FIRST ((FIELD_WIDTH-2)*(FIELD_HEIGHT-2))
00036 #define TILE_FREE (TILE_FIRST + 0)
00037 #define TILE_BORDER (TILE_FIRST + 1)
00038 #define TILE_WALLEND (TILE_FIRST + 2)
00039 #define TILE_WALLUP (TILE_FIRST + 3)
00040 #define TILE_WALLDOWN (TILE_FIRST + 4)
00041 #define TILE_WALLLEFT (TILE_FIRST + 5)
00042 #define TILE_WALLRIGHT (TILE_FIRST + 6)
00043
00044 #define GAME_DELAY 15
00045 #define BALL_ANIM_DELAY 60
00046 #define WALL_DELAY 100
00047
00048 #define MS2TICKS( ms ) ((ms)/GAME_DELAY)
00049
00050 Arrow::Arrow(QCanvasPixmapArray* array, QCanvas* canvas)
00051 : QCanvasSprite( array, canvas )
00052 {
00053 m_vertical = true;
00054 move(3,3);
00055 }
00056
00057 void Arrow::update()
00058 {
00059 if ( m_vertical )
00060 setFrame( 0 );
00061 else
00062 setFrame( 1 );
00063 }
00064
00065 void Arrow::changeDirection()
00066 {
00067 m_vertical = ! m_vertical;
00068 update();
00069 }
00070
00071
00072 Ball::Ball(QCanvasPixmapArray* array, QCanvas* canvas)
00073 : QCanvasSprite( array, canvas ), m_animDelay( 0 ), m_soundDelay( MS2TICKS(BALL_ANIM_DELAY)/2 )
00074 {
00075 }
00076
00077 void Ball::update()
00078 {
00079 m_animDelay--;
00080 if ( m_animDelay<=0 )
00081 {
00082 m_animDelay = MS2TICKS(BALL_ANIM_DELAY);
00083 int frameNum = frame();
00084 frameNum++;
00085 if ( frameNum>=frameCount() )
00086 frameNum = 0;
00087 setFrame( frameNum );
00088 }
00089 }
00090
00091 void Ball::advance(int stage)
00092 {
00093 bool reflectX = false;
00094 bool reflectY = false;
00095
00096
00097 if ( collide(xVelocity(), 0) ) reflectX = true;
00098 if ( collide(0, yVelocity()) ) reflectY = true;
00099 if ( !reflectX && !reflectY && collide(xVelocity(), yVelocity()) ) reflectX = reflectY = true;
00100
00101
00102 QRect r = boundingRect();
00103 r.moveBy( static_cast<int>(xVelocity()), static_cast<int>( yVelocity() ) );
00104 JezzField* field = (JezzField *)canvas();
00105
00106 int ul = field->tile( r.left() / TILE_SIZE, r.top() / TILE_SIZE );
00107 int ur = field->tile( r.right() / TILE_SIZE, r.top() / TILE_SIZE );
00108 int bl = field->tile( r.left() / TILE_SIZE, r.bottom() / TILE_SIZE );
00109 int br = field->tile( r.right() / TILE_SIZE, r.bottom() / TILE_SIZE );
00110
00111 if ( ul!=TILE_FREE ) field->emitBallCollisiton( this, r.left() / TILE_SIZE, r.top() / TILE_SIZE, ul ); else
00112 if ( ur!=TILE_FREE ) field->emitBallCollisiton( this, r.right() / TILE_SIZE, r.top() / TILE_SIZE, ur ); else
00113 if ( bl!=TILE_FREE ) field->emitBallCollisiton( this, r.left() / TILE_SIZE, r.bottom() / TILE_SIZE, bl ); else
00114 if ( br!=TILE_FREE ) field->emitBallCollisiton( this, r.right() / TILE_SIZE, r.bottom() / TILE_SIZE, br );
00115
00116
00117 if ( reflectX ) setXVelocity( -xVelocity() );
00118 if ( reflectY ) setYVelocity( -yVelocity() );
00119
00120
00121 update();
00122 QCanvasSprite::advance( stage );
00123 }
00124
00125 bool Ball::collide( double dx, double dy )
00126 {
00127 QRect r = boundingRect();
00128 r.moveBy( static_cast<int>( dx ), static_cast<int>( dy ) );
00129 JezzField* field = (JezzField *)canvas();
00130
00131 int ul = field->tile( r.left() / TILE_SIZE, r.top() / TILE_SIZE );
00132 int ur = field->tile( r.right() / TILE_SIZE, r.top() / TILE_SIZE );
00133 int bl = field->tile( r.left() / TILE_SIZE, r.bottom() / TILE_SIZE );
00134 int br = field->tile( r.right() / TILE_SIZE, r.bottom() / TILE_SIZE );
00135
00136 return ( ul!=TILE_FREE || ur!=TILE_FREE || bl!=TILE_FREE || br!=TILE_FREE );
00137 }
00138
00139
00140
00141 Wall::Wall( JezzField *field, int x, int y, Direction dir, int tile, QObject *parent, const char *name )
00142 : QObject( parent, name ), m_dir( dir ), m_field( field ), m_startX( x ), m_startY( y ),
00143 m_tile( tile ), m_delay( MS2TICKS(WALL_DELAY)/2 ), m_active( true )
00144 {
00145
00146 m_dx = 0;
00147 m_dy = 0;
00148 switch ( m_dir )
00149 {
00150 case Up: m_dy = -1; break;
00151 case Down: m_dy = 1; break;
00152 case Left: m_dx = -1; break;
00153 case Right: m_dx = 1; break;
00154 }
00155
00156 m_x = m_startX;
00157 m_y = m_startY;
00158
00159 m_field->setTile( m_x, m_y, m_tile );
00160 }
00161
00162 void Wall::finish()
00163 {
00164 m_active = false;
00165 }
00166
00167 bool Wall::isFree( int x, int y )
00168 {
00169 if ( m_field->tile(x, y)==TILE_FREE )
00170 {
00171
00172 QCanvasItemList cols = m_field->collisions( QRect(x*TILE_SIZE, y*TILE_SIZE,
00173 TILE_SIZE, TILE_SIZE) );
00174 if ( cols.count()==0 )
00175 return true;
00176 }
00177
00178 return false;
00179 }
00180
00181 void Wall::update()
00182 {
00183 }
00184
00185 void Wall::advance()
00186 {
00187 update();
00188
00189
00190 if ( m_active )
00191 {
00192 m_delay--;
00193 if ( m_delay<=0 )
00194 {
00195 m_delay = MS2TICKS(WALL_DELAY);
00196
00197
00198 m_field->setTile( m_x, m_y, m_tile );
00199
00200
00201 if ( isFree(m_x+m_dx, m_y+m_dy) )
00202 {
00203
00204 m_x += m_dx;
00205 m_y += m_dy;
00206
00207
00208 m_field->setTile( m_x, m_y, TILE_WALLEND );
00209 } else
00210 {
00211 finish();
00212 emit finished( this, m_field->tile( m_x+m_dx, m_y+m_dy ) );
00213 }
00214 }
00215 }
00216 }
00217
00218 void Wall::fill( bool black )
00219 {
00220 if ( m_dx )
00221 {
00222 for ( int x=m_startX ; x!=m_x; x+=m_dx )
00223 if ( m_field->tile(x, m_startY)==m_tile )
00224 m_field->setGameTile( x, m_startY, black );
00225
00226 m_field->setGameTile( m_x, m_startY, black );
00227 } else
00228 {
00229 for ( int y=m_startY ; y!=m_y; y+=m_dy )
00230 if ( m_field->tile(m_startX, y)==m_tile )
00231 m_field->setGameTile( m_startX, y, black );
00232
00233 m_field->setGameTile( m_startX, m_y, black );
00234 }
00235 }
00236
00237
00238
00239 JezzField::JezzField( QPixmap tiles, QObject* parent, const char* name )
00240 : QCanvas( parent, name ), m_tiles( tiles )
00241 {
00242 setPixmaps( tiles );
00243 }
00244
00245 void JezzField::setGameTile( int x, int y, bool black )
00246 {
00247 setTile( x, y, black ? TILE_BORDER : TILE_FREE );
00248 }
00249
00250 void JezzField::setPixmaps( QPixmap tiles )
00251 {
00252
00253 QPixmap allTiles( TILE_SIZE*(FIELD_WIDTH-2), TILE_SIZE*(FIELD_HEIGHT-1) );
00254
00255
00256 bitBlt( &allTiles, 0, TILE_SIZE*(FIELD_HEIGHT-2),
00257 &tiles, 0, 0, tiles.width(), tiles.height() );
00258
00259
00260 setTiles( allTiles, FIELD_WIDTH, FIELD_HEIGHT, TILE_SIZE, TILE_SIZE );
00261 }
00262
00263
00264
00265 JezzView::JezzView(QCanvas* viewing, QWidget* parent, const char* name, WFlags f)
00266 : QCanvasView( viewing, parent, name, f ), m_vertical( false )
00267 {
00268 setResizePolicy( AutoOne );
00269 setHScrollBarMode( AlwaysOff );
00270 setVScrollBarMode( AlwaysOff );
00271
00272 setCursor( sizeHorCursor );
00273 }
00274
00275 void JezzView::viewportMouseReleaseEvent( QMouseEvent *ev )
00276 {
00277 if ( ev->button() & LeftButton )
00278 {
00279 emit buildWall( ev->x()/TILE_SIZE, ev->y()/TILE_SIZE, m_vertical );
00280 }
00281 }
00282
00283 void JezzView::changeCursor()
00284 {
00285 m_vertical = !m_vertical;
00286 if ( m_vertical )
00287 {
00288 setCursor( sizeVerCursor );
00289 }
00290 else
00291 {
00292 setCursor( sizeHorCursor );
00293 }
00294 }
00295
00296
00297
00298 JezzGame::JezzGame( int ballNum, QWidget *parent, const char *name )
00299 : QWidget( parent, name ), m_wall1( 0 ), m_wall2( 0 ),
00300 m_text( 0 ), m_running( false ), m_percent( 0 ), m_pictured( false )
00301 {
00302 QString path = QPEApplication::qpeDir()+"pics/bounce/";
00303
00304
00305 m_ballPixmaps = new QCanvasPixmapArray( path + "ball%1.png", 25 );
00306 for ( unsigned n=0; n < m_ballPixmaps->count(); n++ )
00307 m_ballPixmaps->image(n)->setOffset( 0, 0 );
00308
00309 m_arrowPixmaps = new QCanvasPixmapArray( path + "arrow%1.png", 2 );
00310 for ( unsigned n=0; n < m_arrowPixmaps->count(); n++ )
00311 m_arrowPixmaps->image(n)->setOffset( 0, 0 );
00312
00313 QPixmap tiles( path + "tiles.png" );
00314
00315
00316 m_field = new JezzField( tiles, this, "m_field" );
00317 m_field->resize( TILE_SIZE*FIELD_WIDTH, TILE_SIZE*FIELD_HEIGHT );
00318
00319 for ( int x=0; x<FIELD_WIDTH; x++ )
00320 m_field->setTile( x, 0, TILE_BORDER );
00321 for ( int y=1; y<FIELD_HEIGHT-1; y++ )
00322 {
00323 m_field->setTile( 0, y, TILE_BORDER );
00324 for ( int x=1; x<FIELD_WIDTH-1; x++ )
00325 m_field->setTile( x, y, TILE_FREE );
00326 m_field->setTile( FIELD_WIDTH-1, y, TILE_BORDER );
00327 }
00328 for ( int x=0; x<FIELD_WIDTH; x++ )
00329 m_field->setTile( x, FIELD_HEIGHT-1, TILE_BORDER );
00330
00331 connect( m_field, SIGNAL(ballCollision(Ball*,int,int,int)), this, SLOT(ballCollision(Ball*,int,int,int)) );
00332
00333
00334 m_view = new JezzView( m_field, this, "m_view" );
00335 m_view->move( 0, 0 );
00336 m_view->adjustSize();
00337 connect( m_view, SIGNAL(buildWall(int,int,bool)), this, SLOT(buildWall(int,int,bool)) );
00338
00339
00340 for ( int n=0; n<ballNum; n++ )
00341 {
00342 Ball *ball = new Ball( m_ballPixmaps, m_field );
00343 m_balls.append( ball );
00344 ball->setVelocity( ((rand() & 1)*2-1)*2, ((rand() & 1)*2-1)*2 );
00345 ball->setFrame( rand() % 25 );
00346 ball->move( 4*TILE_SIZE + ( rand() - 50 ) % ( (FIELD_WIDTH-8)*TILE_SIZE ),
00347 4*TILE_SIZE + rand() % ( (FIELD_HEIGHT-8)*TILE_SIZE ) );
00348 ball->show();
00349 }
00350
00351
00352 arrow = new Arrow( m_arrowPixmaps, m_field );
00353 arrow->show();
00354
00355
00356 m_text = new QCanvasText( m_field );
00357
00358
00359 m_clock = new QTimer( this );
00360 connect( m_clock, SIGNAL(timeout()), this, SLOT(tick()) );
00361 m_clock->start( GAME_DELAY );
00362
00363
00364 setFixedSize( m_view->size() );
00365 }
00366
00367 JezzGame::~JezzGame()
00368 {
00369 m_balls.clear();
00370 delete m_view;
00371 delete m_field;
00372 delete m_ballPixmaps;
00373 }
00374
00375 void JezzGame::display( QString text, int size )
00376 {
00377 odebug << "This function \"display\" shouldn't be called!!!" << oendl;
00378 if ( !text.isEmpty() )
00379 {
00380 QFont font( "Helvetica", size, QFont::Bold );
00381 font.setStyleHint( QFont::Helvetica );
00382 m_text->setFont( font );
00383 m_text->setText( text );
00384
00385 QRect size = m_text->boundingRect();
00386 m_text->move( ( FIELD_WIDTH*TILE_SIZE - size.width() ) / 2,
00387 ( FIELD_HEIGHT*TILE_SIZE - size.height() ) / 2 );
00388
00389 m_text->show();
00390 } else
00391 {
00392 m_text->hide();
00393 }
00394 }
00395
00396 void JezzGame::start()
00397 {
00398 m_running = true;
00399 }
00400
00401 void JezzGame::stop()
00402 {
00403 m_running = false;
00404 }
00405
00406
00407 void JezzGame::makeBlack()
00408 {
00409
00410 for ( int y=0; y<FIELD_HEIGHT; y++ )
00411 for ( int x=0; x<FIELD_WIDTH; x++ )
00412 m_buf[x][y] = m_field->tile( x, y );
00413
00414
00415 for ( Ball *ball=m_balls.first(); ball!=0; ball=m_balls.next() )
00416 fill( static_cast<int>( ball->x()/TILE_SIZE ),
00417 static_cast<int>( ball->y()/TILE_SIZE ) );
00418
00419
00420 for ( int y=0; y<FIELD_HEIGHT; y++ )
00421 for ( int x=0; x<FIELD_WIDTH; x++ )
00422 {
00423 if ( m_buf[x][y]==TILE_FREE )
00424 m_field->setGameTile( x, y, true );
00425 }
00426
00427 m_field->update();
00428 m_view->repaint();
00429
00430
00431 int p = percent();
00432 if ( p!=m_percent )
00433 {
00434 m_percent = p;
00435 emit newPercent( m_percent );
00436 }
00437 }
00438
00439 int JezzGame::percent()
00440 {
00441 int notFree = 0;
00442 for ( int y=1; y<FIELD_HEIGHT-1; y++ )
00443 for ( int x=1; x<FIELD_WIDTH-1; x++ )
00444 {
00445 if ( m_field->tile(x,y)!=TILE_FREE )
00446 notFree++;
00447 }
00448
00449 return 100 * notFree / ( (FIELD_WIDTH-1) * (FIELD_HEIGHT-1) );
00450 }
00451
00452 void JezzGame::fill( int x, int y )
00453 {
00454 if ( m_buf[x][y]!=TILE_FREE) return;
00455
00456
00457 int _x=x;
00458 for ( ; m_buf[_x][y]==TILE_FREE; _x-- )
00459 m_buf[_x][y] = TILE_BORDER;
00460 int stopx = _x;
00461
00462
00463 for ( _x=x; _x>stopx; _x-- )
00464 if ( m_buf[_x][y-1]==TILE_FREE ) fill( _x, y-1 );
00465
00466
00467 for ( _x=x; _x>stopx; _x-- )
00468 if ( m_buf[_x][y+1]==TILE_FREE ) fill( _x, y+1 );
00469
00470
00471 for ( _x=x+1; m_buf[_x][y]==TILE_FREE; _x++ )
00472 m_buf[_x][y] = TILE_BORDER;
00473 stopx = _x;
00474
00475
00476 for ( _x=x+1; _x<stopx; _x++ )
00477 if ( m_buf[_x][y-1]==TILE_FREE ) fill( _x, y-1 );
00478
00479
00480 for ( _x=x+1; _x<stopx; _x++ )
00481 if ( m_buf[_x][y+1]==TILE_FREE ) fill( _x, y+1 );
00482 }
00483
00484 void JezzGame::ballCollision( Ball*, int, int, int tile )
00485 {
00486 if ( tile!=TILE_BORDER && tile>TILE_FREE && tile!=TILE_WALLEND )
00487 {
00488
00489 if ( (tile==TILE_WALLUP || tile==TILE_WALLLEFT) && m_wall1 )
00490 {
00491 m_wall1->finish();
00492 m_wall1->fill( false );
00493 delete m_wall1;
00494 m_wall1 = 0;
00495 }
00496
00497 if ( (tile==TILE_WALLDOWN || tile==TILE_WALLRIGHT) && m_wall2 )
00498 {
00499 m_wall2->finish();
00500 m_wall2->fill( false );
00501 delete m_wall2;
00502 m_wall2 = 0;
00503 }
00504
00505
00506 m_field->update();
00507 m_view->repaint();
00508
00509
00510 emit died();
00511 }
00512 }
00513
00514 void JezzGame::buildWall( int x, int y, bool vertical )
00515 {
00516 if ( !m_running ) return;
00517
00518 if ( m_field->tile(x, y)==TILE_FREE )
00519 {
00520
00521 QCanvasItemList cols = m_field->collisions( QRect(x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE) );
00522 if ( cols.count()>0 )
00523 {
00524
00525 emit ballCollision( (Ball*)cols.first(), x, y, TILE_WALLUP );
00526 return;
00527 }
00528
00529
00530 if ( !m_wall1 )
00531 {
00532 m_wall1 = new Wall( m_field, x, y,
00533 vertical? Wall::Up : Wall::Left,
00534 vertical? TILE_WALLUP : TILE_WALLLEFT,
00535 this, "m_wall1" );
00536 connect( m_wall1, SIGNAL(finished(Wall*,int)),
00537 this, SLOT(wallFinished(Wall*,int)) ); }
00538
00539 if ( !m_wall2 )
00540 {
00541 m_wall2 = new Wall( m_field, x, y,
00542 vertical? Wall::Down: Wall::Right,
00543 vertical? TILE_WALLDOWN : TILE_WALLRIGHT,
00544 this, "m_wall2" );
00545 connect( m_wall2, SIGNAL(finished(Wall*,int)),
00546 this, SLOT(wallFinished(Wall*,int)) );
00547 }
00548 }
00549 }
00550
00551 void JezzGame::wallFinished( Wall *wall, int tile )
00552 {
00553 if ( tile==TILE_WALLEND )
00554 {
00555 if ( m_wall1 )
00556 {
00557 m_wall1->fill( false );
00558 delete m_wall1;
00559 m_wall1 = 0;
00560 }
00561
00562 if ( m_wall2 )
00563 {
00564 m_wall2->fill( false );
00565 delete m_wall2;
00566 m_wall2 = 0;
00567 }
00568 } else
00569 {
00570 if ( m_wall1==wall && m_wall1 )
00571 {
00572 m_wall1->fill( true );
00573 delete m_wall1;
00574 m_wall1 = 0;
00575 }
00576
00577 if ( m_wall2==wall && m_wall2 )
00578 {
00579 m_wall2->fill( true );
00580 delete m_wall2;
00581 m_wall2 = 0;
00582 }
00583 }
00584
00585 m_field->update();
00586 m_view->repaint();
00587
00588 makeBlack();
00589 }
00590
00591 void JezzGame::tick()
00592 {
00593 if ( m_running )
00594 {
00595 if ( m_field ) m_field->advance();
00596 if ( m_wall1 ) m_wall1->advance();
00597 if ( m_wall2 ) m_wall2->advance();
00598 } else
00599 {
00600 for ( Ball *ball=m_balls.first(); ball!=0; ball=m_balls.next() )
00601 ball->update();
00602
00603 if ( m_field ) m_field->update();
00604 if ( m_wall1 ) m_wall1->update();
00605 if ( m_wall2 ) m_wall2->update();
00606 }
00607 }
00608
00609 void JezzGame::changeCursor()
00610 {
00611 arrow->changeDirection();
00612 m_view->changeCursor();
00613 }