~ubuntu-branches/ubuntu/precise/kdegames/precise

1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
1
/*******************************************************************
2
 *
3
 * Copyright 2006-2007 Dmitry Suzdalev <dimsuz@gmail.com>
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
4
 * Copyright 2010 Brian Croom <brian.s.croom@gmail.com>
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
5
 *
6
 * This file is part of the KDE project "KReversi"
7
 *
8
 * KReversi is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2, or (at your option)
11
 * any later version.
12
 *
13
 * KReversi is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with KReversi; see the file COPYING.  If not, write to
20
 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
21
 * Boston, MA 02110-1301, USA.
22
 *
23
 ********************************************************************/
24
#include "kreversiscene.h"
25
#include "kreversigame.h"
26
#include "kreversichip.h"
27
28
#include <QGraphicsSceneMouseEvent>
29
#include <QPainter>
30
#include <QTimer>
31
32
#include <kdebug.h>
33
#include <KLocale>
34
#include <KGamePopupItem>
35
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
36
KReversiScene::KReversiScene( KReversiGame* game , const QString& chipsPrefix )
37
    : m_renderer(QLatin1String("pics/default_theme.desktop")), m_game(0),
38
    m_pendingNewGame(0), m_hintChip(0), m_lastMoveChip(0), m_timerDelay(25),
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
39
    m_showingHint(false), m_demoMode(false), m_showLastMove(false), m_showPossibleMoves(false),
40
    m_showLabels(false)
41
{
42
    m_messageItem = new KGamePopupItem();
43
    m_messageItem->setMessageOpacity(0.9);
44
    addItem(m_messageItem);
45
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
46
    m_renderer.setFrameBaseIndex(1);
47
    m_curCellSize = qMin(width(), height()) / 10;
48
    KReversiChip::initLastMoveMarker(m_curCellSize);
49
    setChipsPrefix(chipsPrefix);
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
50
51
    m_animTimer = new QTimer(this);
52
    connect(m_animTimer, SIGNAL(timeout()), SLOT(slotAnimationStep()));
53
54
    setGame(game);
55
}
56
57
void KReversiScene::resizeScene( int width, int height )
58
{
59
    kDebug() << "resizeScene" << width << "x" << height;
60
    setSceneRect( 0, 0, width, height );
61
62
    int size = qMin(width, height);
63
    m_boardRect.setRect( width/2 - size/2, height/2 - size/2, size, size );
64
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
65
    m_curCellSize = size / 10.0;
66
67
    if(m_lastMoveChip)
68
        m_lastMoveChip->showLastMoveMarker(false);
69
    KReversiChip::initLastMoveMarker(m_curCellSize);
70
    if(m_lastMoveChip && m_showLastMove)
71
        m_lastMoveChip->showLastMoveMarker(true);
72
73
    // adopt new chip size
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
74
    QList<QGraphicsItem*> allItems = items();
75
    KReversiChip *chip = 0;
76
    foreach( QGraphicsItem* item, allItems )
77
    {
78
        chip = qgraphicsitem_cast<KReversiChip*>(item);
79
        if( chip )
80
        {
81
            // adjust pos to new one
82
            chip->setPos( cellTopLeft( chip->row(), chip->col() ) );
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
83
            chip->setChipSize( (int)m_curCellSize );
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
84
        }
85
    }
86
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
87
    foreach( KGameRenderedItem* item, m_possibleMovesItems )
88
        item->setRenderSize(QSize(m_curCellSize, m_curCellSize));
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
89
    displayLastAndPossibleMoves();
90
}
91
92
void KReversiScene::setChipsPrefix( const QString& chipsPrefix )
93
{
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
94
    m_chipsPrefix = chipsPrefix;
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
95
    if(m_game)
96
    {
97
        QList<QGraphicsItem*> allItems = items(m_boardRect);
98
        KReversiChip *chip = 0;
99
        foreach( QGraphicsItem* item, allItems )
100
        {
101
            chip = qgraphicsitem_cast<KReversiChip*>(item);
102
            if( chip )
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
103
                chip->setChipPrefix( chipsPrefix );
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
104
        }
105
106
        if(m_hintChip)
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
107
            m_hintChip->setChipPrefix( chipsPrefix );
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
108
    }
109
}
110
111
void KReversiScene::setShowBoardLabels( bool show )
112
{
113
    m_showLabels = show;
114
}
115
116
void KReversiScene::setGame( KReversiGame* game )
117
{
118
    // if animation is running or we are thinking save this game in pending var.
119
    // It will be made current animation slot will get called
120
    // @see setNewGameObject and @see slotAnimationStep
121
    //
122
    // NOTE: all this magic is needed for game not to crash while pressing new game:
123
    // if we'd simply set the new object right away and deleted an old one, slotAnimationStep
124
    // might got called *after* this, trying to do smth with already deleted object.
125
    // For example see BUG #154946
126
    // So we postpone the setting && deletion...
127
    if( m_animTimer->isActive() || ( m_game && m_game->isThinking() ) )
128
        m_pendingNewGame = game;
129
    else
130
        setNewGameObject( game );
131
}
132
133
void KReversiScene::setNewGameObject( KReversiGame* game )
134
{
135
    m_animTimer->stop();
136
137
    // disconnect signals from previous game if it exists,
138
    // we are not interested in them anymore
139
    if( m_game )
140
    {
141
        disconnect( m_game, SIGNAL(boardChanged()), this, SLOT(updateBoard()) );
142
        disconnect( m_game, SIGNAL(moveFinished()), this, SLOT(slotGameMoveFinished()) );
143
        disconnect( m_game, SIGNAL(gameOver()), this, SLOT(slotGameOver()) );
144
        disconnect( m_game, SIGNAL(computerCantMove()), this, SLOT(slotComputerCantMove()) );
145
        disconnect( m_game, SIGNAL(playerCantMove()), this, SLOT(slotPlayerCantMove()) );
146
    }
147
148
    // delete old object
149
    delete m_game;
150
151
    m_game = game;
152
153
    m_pendingNewGame = 0; // it's made official now ;)
154
155
    connect( m_game, SIGNAL(boardChanged()), SLOT(updateBoard()) );
156
    connect( m_game, SIGNAL(moveFinished()), SLOT(slotGameMoveFinished()) );
157
    connect( m_game, SIGNAL(gameOver()), SLOT(slotGameOver()) );
158
    connect( m_game, SIGNAL(computerCantMove()), SLOT(slotComputerCantMove()) );
159
    connect( m_game, SIGNAL(playerCantMove()), SLOT(slotPlayerCantMove()) );
160
161
    // this will remove all chips left from previous game
162
    QList<QGraphicsItem*> allItems = items();
163
    KReversiChip* chip;
164
    foreach( QGraphicsItem* item, allItems )
165
    {
166
        chip = qgraphicsitem_cast<KReversiChip*>(item);
167
        if(chip)
168
        {
169
            removeItem( chip );
170
            delete chip;
171
        }
172
    }
173
174
    foreach( QGraphicsItem* item, m_possibleMovesItems )
175
    {
176
        removeItem( item );
177
        delete item;
178
    }
179
    m_possibleMovesItems.clear();
180
181
    m_hintChip = 0; // it was deleted above if it was shown
182
    m_showingHint = false;
183
    m_lastMoveChip = 0;
184
    m_demoMode = false;
185
186
    updateBoard();
187
}
188
189
void KReversiScene::setShowLastMove( bool show )
190
{
191
    m_showLastMove = show;
192
    if(show)
193
        displayLastAndPossibleMoves();
194
    else
195
    {
196
        if(m_lastMoveChip)
197
            m_lastMoveChip->showLastMoveMarker(false);
198
    }
199
}
200
201
void KReversiScene::setShowLegalMoves( bool show )
202
{
203
    m_showPossibleMoves = show;
204
    if(show)
205
        displayLastAndPossibleMoves();
206
    else
207
    {
208
        // NOTE: or delete?
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
209
        foreach( KGameRenderedItem* item, m_possibleMovesItems )
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
210
            item->hide();
211
    }
212
}
213
214
void KReversiScene::setAnimationSpeed(int speed)
215
{
216
    if( speed == 0 ) // slow
217
        m_timerDelay = 40;
218
    else if( speed == 1 ) // normal
219
        m_timerDelay = 25;
220
    else if( speed == 2 ) // fast
221
        m_timerDelay = 15;
222
}
223
224
bool KReversiScene::isBusy() const
225
{
226
    return m_animTimer->isActive();
227
}
228
229
void KReversiScene::updateBoard()
230
{
231
    for(int row=0; row<8; ++row)
232
        for(int col=0; col<8; ++col )
233
        {
234
            ChipColor color = m_game->chipColorAt( row, col );
235
            if( color != NoColor )
236
            {
237
                // if there's a chip, just change it color
238
                // otherwise create new
239
                KReversiChip *chip = static_cast<KReversiChip*>(itemAt( cellCenter(row, col) ));
240
                if( chip != 0 )
241
                {
242
                    if( chip->color() != color )
243
                    {
244
                        //kDebug() << "Found item at (" << row << "," << col << "). Setting its color.";
245
                        chip->setColor( color );
246
                    }
247
                }
248
                else
249
                {
250
                    //kDebug() << "No item at (" << row << "," << col << "). Creating.";
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
251
                    chip = new KReversiChip( &m_renderer, color, m_chipsPrefix, m_curCellSize, this );
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
252
                    chip->setPos( cellTopLeft(row, col) );
253
                    chip->setRowCol( row, col );
254
                }
255
            }
256
            else
257
            {
258
                // this if-branch happens on undos
259
260
                // deleting only KReversiChips
261
                // (for other QGItems cast will return 0 causing "delete" to be no-op)
262
                KReversiChip *chip = qgraphicsitem_cast<KReversiChip*>(itemAt( cellCenter(row, col) ));
263
                delete chip;
264
            }
265
        }
266
    m_lastMoveChip = 0;
267
    displayLastAndPossibleMoves();
268
    update();
269
}
270
271
void KReversiScene::toggleDemoMode( bool toggle )
272
{
273
    if( m_game->isGameOver() )
274
        return;
275
    m_demoMode = toggle;
276
    stopHintAnimation();
277
    // if we are currently waiting for user mouse input and not animating,
278
    // let's do the turn right now!
279
    if( !m_game->isComputersTurn() && !m_animTimer->isActive() )
280
        m_game->startNextTurn(m_demoMode);
281
}
282
283
void KReversiScene::slotGameMoveFinished()
284
{
285
    // hide shown legal moves
286
    if( m_showPossibleMoves )
287
    {
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
288
        foreach( KGameRenderedItem* item, m_possibleMovesItems )
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
289
            item->hide();
290
    }
291
292
    m_changedChips = m_game->changedChips();
293
    // create an item that was placed by player
294
    // by convention it will be the first in the list of changed items
295
    KReversiPos move = m_changedChips.takeFirst();
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
296
    KReversiChip *newchip = new KReversiChip( &m_renderer, move.color, m_chipsPrefix, m_curCellSize, this );
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
297
    newchip->setPos( cellTopLeft( move.row, move.col ) );
298
    newchip->setRowCol( move.row, move.col );
299
    // start animation
300
    if( m_lastMoveChip )
301
        m_lastMoveChip->showLastMoveMarker( false );
302
    m_animTimer->start(m_timerDelay);
303
}
304
305
void KReversiScene::slotAnimationStep()
306
{
307
    if( m_pendingNewGame != 0 )
308
    {
309
	// aha! we have new game waiting for us
310
	m_animTimer->stop();
311
	setNewGameObject( m_pendingNewGame );
312
	return;
313
    }
314
1.3.5 by Modestas Vainius
Import upstream version 4.4.3
315
    if(m_changedChips.isEmpty() && !m_showingHint)
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
316
    {
317
        // FIXME: GGZ only - doesn't yet report flipped chips
318
        m_animTimer->stop();
319
        emit moveFinished();
320
        return;
321
    }
322
323
    if( !m_showingHint )
324
    { // we're animating chips move
325
326
        KReversiPos move = m_changedChips.at(0);
1.3.5 by Modestas Vainius
Import upstream version 4.4.3
327
        QPointF pos = cellCenter(move.row, move.col);
328
        KReversiChip *chip = qgraphicsitem_cast<KReversiChip*>(itemAt(pos));
329
        if (!chip)
330
        {
331
            kDebug() << "looks like pos" << move.row << "," << move.col << "is hovered by message item, searching below";
332
            QList<QGraphicsItem*> allItemsAtPos = items(pos);
333
            foreach (QGraphicsItem* item, allItemsAtPos)
334
            {
335
                chip = qgraphicsitem_cast<KReversiChip*>(item);
336
                if (chip)
337
                    break;
338
            }
339
            kDebug() << (chip ? "found it!" : "not found... skipping all animation frames and hoping for the best");
340
        }
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
341
1.3.5 by Modestas Vainius
Import upstream version 4.4.3
342
        bool animFinished = chip ? chip->nextFrame() : true /* skip frames */;
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
343
        if(animFinished)
344
        {
345
            chip->setColor( move.color );
346
347
            m_changedChips.removeFirst(); // we finished animating it
348
349
            if(m_changedChips.count() == 0)
350
            {
351
                // whole animation sequence finished. On to next turn!
352
                m_animTimer->stop();
353
                emit moveFinished();
354
355
                displayLastAndPossibleMoves();
356
357
                m_game->startNextTurn(m_demoMode);
358
            }
359
        }
360
    }
361
    else
362
    { // we're just showing hint to the user
363
        m_hintChip->setVisible( !m_hintChip->isVisible() );
364
        update(m_hintChip->sceneBoundingRect());
365
    }
366
}
367
368
void KReversiScene::displayLastAndPossibleMoves()
369
{
370
    // ==== Show What Last Move Was ====
371
    if( m_showLastMove )
372
    {
373
        KReversiPos lastPos = m_game->getLastMove();
374
        m_lastMoveChip = qgraphicsitem_cast<KReversiChip*>(itemAt(cellCenter(lastPos.row, lastPos.col)));
375
        if(m_lastMoveChip)
376
            m_lastMoveChip->showLastMoveMarker(true);
377
    }
378
379
    // ==== Show Possible Moves ====
380
    if( m_showPossibleMoves && !m_game->isComputersTurn() )
381
    {
382
        //hide currently displayed if any
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
383
        foreach( KGameRenderedItem* item, m_possibleMovesItems )
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
384
            item->hide();
385
386
        PosList l = m_game->possibleMoves();
387
        // if m_possibleMovesItems contains less rects then there are items in l
388
        // lets fill it with additional rects.
389
        // Else we'll just reuse rects that we already have.
390
        // NOTE: maybe make m_possibleMovesItems a QVector and simply do resize()?
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
391
        while( m_possibleMovesItems.count() < l.count() )
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
392
        {
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
393
            KGameRenderedItem *item = new KGameRenderedItem( &m_renderer, QLatin1String( "move_hint" ) );
394
            addItem(item);
395
            item->setRenderSize( QSize(m_curCellSize, m_curCellSize) );
396
            item->setZValue(-1);
397
            m_possibleMovesItems.append( item );
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
398
        }
399
400
        // now let's setup rects to appropriate positions
401
        for(int i=0; i<l.size(); ++i )
402
        {
403
            m_possibleMovesItems[i]->setPos( cellTopLeft( l.at(i).row, l.at(i).col ) );
404
            m_possibleMovesItems[i]->show();
405
        }
406
    }
407
}
408
409
void KReversiScene::slotHint()
410
{
411
    if( m_game->isComputersTurn() )
412
    {
413
        kDebug() << "It is not a very good time to ask me for a hint, human. I'm thinking...";
414
        return;
415
    }
416
    if( m_animTimer->isActive() )
417
    {
418
        kDebug() << "Don't you see I'm animating? Be patient, human child...";
419
        return;
420
    }
421
    KReversiPos hint = m_game->getHint();
422
    if( !hint.isValid() )
423
        return;
424
    if( m_hintChip == 0 )
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
425
        m_hintChip = new KReversiChip( &m_renderer, hint.color, m_chipsPrefix, m_curCellSize, this );
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
426
    m_hintChip->setPos( cellTopLeft( hint.row, hint.col ) );
427
    m_hintChip->setRowCol( hint.row, hint.col );
428
    m_showingHint = true;
429
    m_animTimer->start(500);
430
}
431
432
void KReversiScene::slotGameOver()
433
{
434
    m_demoMode = false;
435
}
436
437
QPointF KReversiScene::cellCenter( int row, int col ) const
438
{
439
    return cellTopLeft(row,col) + QPointF( m_curCellSize / 2, m_curCellSize / 2 );
440
}
441
442
QPointF KReversiScene::cellTopLeft( int row, int col ) const
443
{
444
    return m_boardRect.topLeft()+QPointF( (col+1) * m_curCellSize, (row+1) * m_curCellSize );
445
}
446
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
447
void KReversiScene::drawBackground( QPainter *p, const QRectF& )
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
448
{
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
449
    p->drawPixmap(sceneRect().topLeft(), m_renderer.spritePixmap(QLatin1String( "background" ), sceneRect().size().toSize()));
450
    p->drawPixmap(m_boardRect.topLeft(), m_renderer.spritePixmap(QLatin1String( "board" ), m_boardRect.size().toSize()));
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
451
    if(m_showLabels)
1.2.54 by Jonathan Riddell
Import upstream version 4.5.80
452
        p->drawPixmap(m_boardRect.topLeft(), m_renderer.spritePixmap(QLatin1String( "board_numbers" ), m_boardRect.size().toSize()));
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
453
}
454
455
void KReversiScene::stopHintAnimation()
456
{
457
    if( m_animTimer->isActive() )
458
    {
459
        if( m_showingHint )
460
        {
461
            m_animTimer->stop();
462
            m_hintChip->hide();
463
            m_showingHint = false;
464
            update(m_hintChip->sceneBoundingRect());
465
        }
466
    }
467
}
468
469
void KReversiScene::mousePressEvent( QGraphicsSceneMouseEvent* ev )
470
{
471
    stopHintAnimation();
472
473
    // user moves not allowed in demo mode
474
    if( m_demoMode )
475
        return;
476
477
    if( m_animTimer->isActive() || m_game->isComputersTurn() )
478
    {
479
        kDebug() << "Don't you see I'm busy? Be patient, human child...";
480
        return;
481
    }
482
483
    QRectF boardRect( cellTopLeft(0, 0), QSizeF( m_curCellSize * 8, m_curCellSize * 8) );
484
    if( !boardRect.contains(ev->scenePos()) )
485
        return;
486
    int row = (int)((-boardRect.y() + ev->scenePos().y()) / m_curCellSize);
487
    int col = (int)((-boardRect.x() + ev->scenePos().x()) / m_curCellSize);
488
489
    if( row < 0 ) row = 0;
490
    if( row > 7 ) row = 7;
491
    if( col < 0 ) col = 0;
492
    if( col > 7 ) col = 7;
493
494
    m_game->makePlayerMove( row, col, false );
495
}
496
497
void KReversiScene::slotComputerCantMove()
498
{
499
    m_messageItem->setMessageTimeout(3000);
500
    m_messageItem->showMessage(i18n("Computer can not move. It is your turn again."), KGamePopupItem::BottomLeft);
501
502
    displayLastAndPossibleMoves();
503
}
504
505
void KReversiScene::slotPlayerCantMove()
506
{
507
    m_messageItem->setMessageTimeout(3000);
1.2.27 by Roderick B. Greening
Import upstream version 4.2.85
508
    m_messageItem->showMessage(i18n("You can not perform any move. Computer takes next turn now."), KGamePopupItem::BottomLeft);
1.2.14 by Jonathan Riddell
Import upstream version 4.0.80
509
510
    displayLastAndPossibleMoves();
511
}
512
513
#include "kreversiscene.moc"