Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

game.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public
00015  * License along with this program; if not, write to the Free
00016  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 
00020 #include "game.h"
00021 
00022 /* OPIE */
00023 #include <opie2/odebug.h>
00024 #include <qpe/qpeapplication.h>
00025 using namespace Opie::Core;
00026 
00027 /* QT */
00028 #include <qtimer.h>
00029 
00030 /* STD */
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    // check for collisions
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    // emit collision
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    // apply reflection
00117    if ( reflectX ) setXVelocity( -xVelocity() );
00118    if ( reflectY ) setYVelocity( -yVelocity() );
00119 
00120    // update field
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    // setup position and direction
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         // check whether there is a ball at the moment
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     // move wall
00190     if ( m_active )
00191     {
00192         m_delay--;
00193         if ( m_delay<=0 )
00194         {
00195             m_delay =  MS2TICKS(WALL_DELAY);
00196 
00197             // set previous tile
00198             m_field->setTile( m_x, m_y, m_tile );
00199 
00200             // check whether next place is still free
00201             if ( isFree(m_x+m_dx, m_y+m_dy) )
00202             {
00203                 // move ball
00204                 m_x += m_dx;
00205                 m_y += m_dy;
00206 
00207                 // set tile
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     // create new tiles
00253     QPixmap allTiles( TILE_SIZE*(FIELD_WIDTH-2), TILE_SIZE*(FIELD_HEIGHT-1) );
00254 
00255     // handle default tiles
00256     bitBlt( &allTiles, 0, TILE_SIZE*(FIELD_HEIGHT-2),
00257             &tiles, 0, 0, tiles.width(), tiles.height() );
00258 
00259     // load tiles into canvas
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    // load gfx
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    // create field
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    // create view
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    // create balls
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    // create arrow
00352    arrow = new Arrow( m_arrowPixmaps, m_field );
00353    arrow->show();
00354 
00355    // create text label
00356    m_text = new QCanvasText( m_field );
00357 
00358    // create game clock
00359    m_clock = new QTimer( this );
00360    connect( m_clock, SIGNAL(timeout()), this, SLOT(tick()) );
00361    m_clock->start( GAME_DELAY );
00362 
00363    // setup geometry
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    // copy current field into buffer
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    // fill areas that contains a ball
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    // areas still free can be blacked now
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    // count percent value of occupied area
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    // go left
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    // fill above
00463    for ( _x=x; _x>stopx; _x-- )
00464       if ( m_buf[_x][y-1]==TILE_FREE ) fill( _x, y-1 );
00465 
00466    // fill below
00467    for ( _x=x; _x>stopx; _x-- )
00468       if ( m_buf[_x][y+1]==TILE_FREE ) fill( _x, y+1 );
00469 
00470    // go right
00471    for ( _x=x+1; m_buf[_x][y]==TILE_FREE; _x++ )
00472       m_buf[_x][y] = TILE_BORDER;
00473    stopx = _x;
00474 
00475    // fill above
00476    for ( _x=x+1; _x<stopx; _x++ )
00477       if ( m_buf[_x][y-1]==TILE_FREE ) fill( _x, y-1 );
00478 
00479    // fill below;
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       // stop walls
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       // update view
00506       m_field->update();
00507       m_view->repaint();
00508 
00509       // send death msg
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       // check whether there is a ball at the moment
00521       QCanvasItemList cols = m_field->collisions( QRect(x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE) );
00522       if ( cols.count()>0 )
00523       {
00524          //kdDebug() << "Direct collision" << endl;
00525          emit ballCollision( (Ball*)cols.first(), x, y, TILE_WALLUP );
00526          return;
00527       }
00528 
00529       // start walls
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 }

Generated on Sat Nov 5 16:17:18 2005 for OPIE by  doxygen 1.4.2