Commit d456c79a authored by Stefan Majewsky's avatar Stefan Majewsky
Browse files

Merge changes in branches/work/kdiamond-4.2 into trunk.

svn path=/trunk/KDE/kdegames/kdiamond/; revision=834782
parents fc5cac9f 75f787fa
......@@ -22,11 +22,15 @@
#include "diamond.h"
#include "renderer.h"
#include <QTime>
#include <KDebug>
Animator::Animator()
: m_duration(0)
, m_frameCount(0)
, m_started(false)
, m_playedLastFrame(false)
, m_time(0)
, m_timer(0)
{
}
......@@ -63,8 +67,10 @@ void Animator::start()
m_timer = new QTimeLine;
m_timer->setDuration(m_duration);
m_timer->setFrameRange(1, m_frameCount);
m_time = new QTime;
connect(m_timer, SIGNAL(frameChanged(int)), this, SLOT(setFrame(int)));
connect(m_timer, SIGNAL(finished()), this, SLOT(slotFinished()));
m_time->start();
m_timer->start();
}
......@@ -89,15 +95,18 @@ void MoveAnimator::setMoveLength(int moveLength)
void MoveAnimator::setFrame(int frame)
{
qreal x, y, difference = (qreal) frame / (qreal) KDiamond::MoveFrameCount;
kDebug() << "Calculating " << m_data.count() << " animations in frame " << frame << " of " << m_frameCount << " starts at " << m_time->elapsed() << " msecs with a scheduled total of " << m_duration << " msecs";
foreach (const AnimationData &data, m_data)
{
//kDebug() << "Diamond moving from " << data.from << " to " << data.to << " at " << time.elapsed();
//the absolute value of the actual difference can not be more than the calculated maximum difference
x = data.from.x() + qBound(-difference, data.to.x() - data.from.x(), difference);
y = data.from.y() + qBound(-difference, data.to.y() - data.from.y(), difference);
data.diamond->setPosInBoardCoords(QPointF(x, y));
//kDebug() << "Diamond moved from " << data.from << " to " << data.to << " at " << time.elapsed();
}
// if (frame == m_frameCount)
// m_playedLastFrame = true;
if (frame == m_frameCount)
m_playedLastFrame = true;
}
RemoveAnimator::RemoveAnimator()
......@@ -109,8 +118,11 @@ RemoveAnimator::RemoveAnimator()
void RemoveAnimator::setFrame(int frame)
{
kDebug() << "Calculating " << m_data.count() << " animations in frame " << frame << " of " << m_frameCount << " starts at " << m_time->elapsed() << " msecs with a scheduled total of " << m_duration << " msecs";
foreach (const AnimationData &data, m_data)
data.diamond->setPixmap(Renderer::self()->removeFrame(data.diamond->color(), frame - 1));
if (frame == m_frameCount)
m_playedLastFrame = true;
}
#include "animator.moc"
......@@ -26,6 +26,7 @@
#include <QList>
#include <QObject>
#include <QPointF>
class QTime;
class QTimeLine;
namespace KDiamond
......@@ -66,6 +67,7 @@ class Animator : public QObject
int m_frameCount;
bool m_started, m_playedLastFrame; //the latter prevents a race condition between the last setFrame and finished
QTime *m_time;
QTimeLine *m_timer;
QList<AnimationData> m_data;
......
......@@ -146,6 +146,72 @@ bool Board::isTimeUp() const
return m_timeIsUp;
}
//Checks amount of possible moves remaining
void Board::getMoves()
{
m_availableMoves.clear();
KDiamond::Color curColor;
for (int x = 0; x < m_size; ++x)
{
for (int y = 0; y < m_size; ++y)
{
curColor = m_diamonds[x][y]->color();
if ((x < (m_size-1)) && (m_diamonds[x+1][y]->color() == curColor))
{
if ((onBoard(x-2, y)) && (m_diamonds[x-2][y]->color() == curColor))
m_availableMoves.append(QPoint(x-2, y));
if ((onBoard(x-1, y-1)) && (m_diamonds[x-1][y-1]->color() == curColor))
m_availableMoves.append(QPoint(x-1, y-1));
if ((onBoard(x-1, y+1)) && (m_diamonds[x-1][y+1]->color() == curColor))
m_availableMoves.append(QPoint(x-1, y+1));
if ((onBoard(x+3, y)) && (m_diamonds[x+3][y]->color() == curColor))
m_availableMoves.append(QPoint(x+3, y));
if ((onBoard(x+2, y-1)) && (m_diamonds[x+2][y-1]->color() == curColor))
m_availableMoves.append(QPoint(x+2, y-1));
if ((onBoard(x+2, y+1)) && (m_diamonds[x+2][y+1]->color() == curColor))
m_availableMoves.append(QPoint(x+2, y+1));
}
if ((x < (m_size-2)) && (m_diamonds[x+2][y]->color() == curColor))
{
if ((onBoard(x+1, y-1)) && (m_diamonds[x+1][y-1]->color() == curColor))
m_availableMoves.append(QPoint(x+1, y-1));
if ((onBoard(x+1, y+1)) && (m_diamonds[x+1][y+1]->color() == curColor))
m_availableMoves.append(QPoint(x+1, y+1));
}
if ((y < (m_size-1)) && (m_diamonds[x][y+1]->color() == curColor))
{
if ((onBoard(x, y-2)) && (m_diamonds[x][y-2]->color() == curColor))
m_availableMoves.append(QPoint(x, y-2));
if ((onBoard(x-1, y-1)) && (m_diamonds[x-1][y-1]->color() == curColor))
m_availableMoves.append(QPoint(x-1, y-1));
if ((onBoard(x+1, y-1)) && (m_diamonds[x+1][y-1]->color() == curColor))
m_availableMoves.append(QPoint(x+1, y-1));
if ((onBoard(x, y+3)) && (m_diamonds[x][y+3]->color() == curColor))
m_availableMoves.append(QPoint(x, y+3));
if ((onBoard(x-1, y+2)) && (m_diamonds[x-1][y+2]->color() == curColor))
m_availableMoves.append(QPoint(x-1, y+2));
if ((onBoard(x+1, y+2)) && (m_diamonds[x+1][y+2]->color() == curColor))
m_availableMoves.append(QPoint(x+1, y+2));
}
if ((y < (m_size-2)) && (m_diamonds[x][y+2]->color() == curColor))
{
if ((onBoard(x-1, y+1)) && (m_diamonds[x-1][y+1]->color() == curColor))
m_availableMoves.append(QPoint(x-1, y+1));
if ((onBoard(x+1, y+1)) && (m_diamonds[x+1][y+1]->color() == curColor))
m_availableMoves.append(QPoint(x+1, y+1));
}
}
}
emit numberMoves(m_availableMoves.size());
if (m_availableMoves.isEmpty())
{
m_selection1->hide();
m_selection2->hide();
m_timeIsUp = true;
showMessage(i18nc("No moves remaining", "Game over."), 0);
}
}
//Converts board coordinates (i.e. (0,0) is the top left point of the board, 1 unit = 1 diamond) to scene coordinates.
QPoint Board::boardToScene(const QPointF &boardCoords) const
{
......@@ -242,6 +308,13 @@ void Board::mouseOnDiamond(int xIndex, int yIndex)
}
}
void Board::clearSelection()
{
m_selection1->hide();
m_selection2->hide();
m_selected1x = m_selected1y = m_selected2x = m_selected2y = -1;
}
void Board::pause(bool paused)
{
m_paused = paused;
......@@ -272,6 +345,8 @@ void Board::update()
//finish game if possible
if (m_timeIsUp)
emit gameOver();
else
getMoves();
return;
}
//execute first job in queue
......@@ -519,9 +594,26 @@ void Board::fillGaps()
connect(m_animator, SIGNAL(finished()), this, SLOT(animationFinished()));
}
bool Board::onBoard(int x, int y) const
{
if ((0 <= x && x < m_size) && (0 <= y && y < m_size))
return true;
else
return false;
}
void Board::showHint()
{
QPoint location = m_availableMoves.at(qrand()%m_availableMoves.size());
m_selected1x = location.x();
m_selected1y = location.y();
m_selection1->setPosInBoardCoords(location);
m_selection1->show();
}
void Board::animationFinished()
{
delete m_animator;
m_animator->deleteLater();
m_animator = 0;
}
......
......@@ -41,7 +41,7 @@ namespace KDiamond
//specification of the difficulties
enum Size
{
VeryEasySize = 12,
VeryEasySize = 50,
EasySize = 10,
MediumSize = 8,
HardSize = 8,
......@@ -49,7 +49,7 @@ namespace KDiamond
};
enum ColorCount
{
VeryEasyColors = 5,
VeryEasyColors = 3,
EasyColors = 5,
MediumColors = 5,
HardColors = 6,
......@@ -72,6 +72,7 @@ class Board : public QGraphicsScene
~Board();
int diamondCountOnEdge() const;
bool isTimeUp() const;
void getMoves();
QPoint boardToScene(const QPointF &boardCoord) const;
void resizeScene(int width, int height, bool force = false);
......@@ -80,24 +81,29 @@ class Board : public QGraphicsScene
void mouseOnDiamond(int xIndex, int yIndex);
public slots:
void animationFinished();
void clearSelection();
void hideMessage();
void pause(bool paused);
void showMessage(const QString &message, int timeout = 0);
void update();
void timeIsUp();
void showHint();
signals:
void boardResized();
void diamondsRemoved(int count, int cascade);
void numberMoves(int moves);
void updateScheduled(int milliseconds);
void gameOver();
private:
QSet<QPoint *> findCompletedRows();
void fillGaps();
bool onBoard(int x, int y) const;
private:
KDiamond::Size m_size;
KDiamond::ColorCount m_colorCount;
QList<KDiamond::Job> m_jobQueue;
QSet<QPoint *> m_diamondsToRemove;
QList<QPoint> m_availableMoves;
Diamond ***m_diamonds;
Diamond *m_selection1, *m_selection2;
......
......@@ -36,5 +36,3 @@ void Container::resizeEvent(QResizeEvent *)
{
m_widget->setGeometry(0, 0, width(), height());
}
#include "container.moc"
......@@ -20,6 +20,8 @@
#include "board.h"
#include "renderer.h"
#include <QGraphicsSceneMouseEvent>
KDiamond::Color KDiamond::colorFromNumber(int number)
{
switch (number)
......@@ -110,10 +112,55 @@ void Diamond::setPosInBoardCoords(const QPointF &pos)
setPos(m_board->boardToScene(m_pos));
}
void Diamond::mousePressEvent(QGraphicsSceneMouseEvent *)
void Diamond::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
m_mouseDown = true;
m_mouseDownPos = event->pos();
}
void Diamond::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (!m_board->isTimeUp())
if (m_mouseDown)
{
//check if diamond was dragged onto another one
const QPointF pos = event->pos();
const qreal dx = pos.x() - m_mouseDownPos.x(), dy = pos.y() - m_mouseDownPos.y();
const QSizeF diamondSize = boundingRect().size();
static const qreal draggingFuzziness = 2.0 / 3.0;
if (qAbs(dx) >= diamondSize.width() * draggingFuzziness)
{
//do not proceed if the diamond where this one was dragged on does not exist (i.e. this diamond is on one of the board edges)
if (dx < 0 && m_xIndex == 0)
return;
if (dx > 0 && m_xIndex == m_board->diamondCountOnEdge() - 1)
return;
//dragged in X direction - simulate two clicks (on this one, then on the other one -> that is logically the same operation)
m_board->clearSelection();
m_board->mouseOnDiamond(m_xIndex, m_yIndex);
m_board->mouseOnDiamond(m_xIndex + (dx < 0 ? -1 : 1), m_yIndex);
m_mouseDown = false; //mouse action has been handled
}
if (qAbs(dy) >= diamondSize.height() * draggingFuzziness)
{
if (dy < 0 && m_yIndex == 0)
return;
if (dy > 0 && m_yIndex == m_board->diamondCountOnEdge() - 1)
return;
m_board->clearSelection();
m_board->mouseOnDiamond(m_xIndex, m_yIndex);
m_board->mouseOnDiamond(m_xIndex, m_yIndex + (dy < 0 ? -1 : 1));
m_mouseDown = false;
}
}
}
void Diamond::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (!m_board->isTimeUp() && m_mouseDown && boundingRect().contains(event->pos()))
{
m_board->mouseOnDiamond(m_xIndex, m_yIndex);
m_mouseDown = false;
}
}
#include "diamond.moc"
......@@ -60,12 +60,16 @@ class Diamond : public QObject, public QGraphicsPixmapItem
void updateGeometry();
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
private:
Board *m_board;
KDiamond::Color m_color;
int m_xIndex, m_yIndex; //the index of the diamond in the Board's internal array (used for communication with Board)
QPointF m_pos, m_target; //current position of diamond in board coordinates (see Board::boardToScene for details)
bool m_mouseDown;
QPointF m_mouseDownPos; //position of last mouse-down event in diamond coordinates
};
#endif //KDIAMOND_DIAMOND_H
......@@ -19,6 +19,7 @@
#include "game.h"
#include "board.h"
#include "mainwindow.h"
#include "settings.h"
#include <QMouseEvent>
#include <QTime>
......@@ -38,6 +39,7 @@ Game::Game(KGameDifficulty::standardLevel difficulty, MainWindow *mainWindow = 0
, m_secondsRemaining(0)
, m_paused(false)
, m_finished(false)
, m_untimed(Settings::untimed())
{
//init timers
m_gameTime->start();
......@@ -89,15 +91,12 @@ void Game::pause(bool paused)
void Game::update()
{
if (m_paused)
if (m_paused || m_untimed)
return;
//calculate new time
int secondsRemaining = KDiamond::GameDuration + m_secondsEarned + (m_millisecondsPaused - m_gameTime->elapsed()) / 1000;
if (secondsRemaining <= 0)
{
m_finished = true;
KNotification::event("gamefinished");
disconnect(m_mainWindow, SIGNAL(updateScheduled(int)), this, SLOT(update()));
emit timeIsUp(m_points);
}
else if (m_secondsRemaining != secondsRemaining)
......@@ -107,6 +106,9 @@ void Game::update()
void Game::gameOver()
{
m_finished = true;
KNotification::event("gamefinished");
disconnect(m_mainWindow, SIGNAL(updateScheduled(int)), this, SLOT(update()));
disconnect(m_mainWindow, SIGNAL(updateScheduled(int)), m_board, SLOT(update()));
}
......@@ -146,4 +148,10 @@ void Game::wheelEvent(QWheelEvent *event)
event->ignore(); //prevent user-triggered scrolling
}
void Game::setUntimed(bool untimed)
{
m_untimed = untimed;
Settings::setUntimed(untimed);
}
#include "game.moc"
......@@ -50,6 +50,7 @@ class Game : public QGraphicsView
void update();
void updateTheme();
void gameOver();
void setUntimed(bool untimed);
signals:
void pointsChanged(int points);
void remainingTimeChanged(int remainingTime);
......@@ -67,7 +68,7 @@ class Game : public QGraphicsView
int m_points;
int m_secondsEarned, m_millisecondsPaused, m_secondsRemaining;
bool m_paused, m_finished;
bool m_paused, m_finished, m_untimed;
};
#endif //KDIAMOND_GAME_H
......@@ -19,5 +19,9 @@
<label>Show minutes separately on the timer.</label>
<default>false</default>
</entry>
<entry name="Untimed" type="Bool">
<label>Play an untimed game.</label>
<default>false</default>
</entry>
</group>
</kcfg>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<gui name="kdiamond" version="1">
<gui name="kdiamond" version="2">
<MenuBar>
<Menu name="settings">
<Action name="show_minutes"/>
<Action name="untimed"/>
</Menu>
</MenuBar>
<ToolBar name="mainToolBar">
<text>Main Toolbar</text>
<Action name="game_new"/>
<Action name="game_pause"/>
<Separator/>
<Action name="game_highscores"/>
<Separator/>
<Action name="game_pause"/>
<Action name="move_hint"/>
</ToolBar>
<StatusBar/>
</gui>
......@@ -34,8 +34,9 @@ static const char version[] = "1.0";
int main(int argc, char ** argv)
{
KAboutData about("kdiamond", 0, ki18nc("The application's name", "KDiamond"), version, ki18n(description),
KAboutData::License_GPL, ki18n("(C) 2008 Stefan Majewsky"), KLocalizedString(), "http://games.kde.org/kdiamond" );
KAboutData::License_GPL, ki18n("(C) 2008 Stefan Majewsky and others"), KLocalizedString(), "http://games.kde.org/kdiamond" );
about.addAuthor(ki18n("Stefan Majewsky"), ki18n("Original author and current maintainer"), "majewsky@gmx.net");
about.addAuthor(ki18n("Paul Bunbury"), ki18n("Gameplay refinement"), "happysmileman@googlemail.com");
about.addCredit(ki18n("Eugene Trounev"), ki18n("Default theme"), "eugene.trounev@gmail.com");
about.addCredit(ki18n("Felix Lemke"), ki18n("Classic theme"), "lemke.felix@ages-skripte.org");
about.addCredit(ki18n("Jeffrey Kelling"), ki18n("Technical consultant"), "kelling.jeffrey@ages-skripte.org");
......
......@@ -40,10 +40,12 @@
#include <KStandardGameAction>
#include <KStatusBar>
#include <KToggleAction>
#include <KToolBar>
MainWindow::MainWindow(QWidget *parent)
: KXmlGuiWindow(parent)
{
QAction *action;
//init timers and randomizer (necessary for the board)
m_updateTimer = new QTimer;
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateTime()), Qt::DirectConnection);
......@@ -56,15 +58,20 @@ MainWindow::MainWindow(QWidget *parent)
KStandardGameAction::highscores(this, SLOT(showHighscores()), actionCollection());
KStandardGameAction::pause(this, SIGNAL(pause(bool)), actionCollection());
KStandardGameAction::quit(kapp, SLOT(quit()), actionCollection());
KStandardGameAction::hint(this, SIGNAL(showHint()), actionCollection());
KStandardAction::preferences(this, SLOT(configureSettings()), actionCollection());
KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection());
KToggleAction *showMinutes = actionCollection()->add<KToggleAction>("show_minutes");
showMinutes->setText(i18n("Show minutes on timer"));
showMinutes->setChecked(Settings::showMinutes());
connect(showMinutes, SIGNAL(triggered(bool)), this, SLOT(showMinutesOnTimer(bool)));
untimed = actionCollection()->add<KToggleAction>("untimed");
untimed->setText(i18n("Untimed Game"));
untimed->setChecked(Settings::untimed());
//init GUI - statusbar etc.
statusBar()->insertPermanentItem(i18n("Points: %1", 0), 1, 1);
statusBar()->insertPermanentItem(i18np("Time left: 1 second", "Time left: %1 seconds", 0), 2, 1);
statusBar()->insertPermanentItem(i18n("Possible moves: %1", 0), 3, 1);
setAutoSaveSettings();
//init GUI - center area
m_game = 0;
......@@ -110,11 +117,14 @@ void MainWindow::startGame()
}
//start new game
m_game = new Game(KGameDifficulty::level(), this);
connect(untimed, SIGNAL(triggered(bool)), m_game, SLOT(setUntimed(bool)));
connect(this, SIGNAL(pause(bool)), m_game, SLOT(pause(bool)));
connect(this, SIGNAL(pause(bool)), m_game->board(), SLOT(pause(bool)));
connect(this, SIGNAL(showHint()), m_game->board(), SLOT(showHint()));
connect(m_game, SIGNAL(pointsChanged(int)), this, SLOT(updatePoints(int)));
connect(m_game, SIGNAL(remainingTimeChanged(int)), this, SLOT(updateRemainingTime(int)));
connect(m_game, SIGNAL(timeIsUp(int)), this, SLOT(timeIsUp()));
connect(m_game->board(), SIGNAL(numberMoves(int)), this, SLOT(updateMoves(int)));
connect(m_game->board(), SIGNAL(gameOver()), this, SLOT(gameOver()));
m_container->setWidget(m_game);
//reset the Pause button's state
......@@ -176,6 +186,11 @@ void MainWindow::updatePoints(int points)
statusBar()->changeItem(i18n("Points: %1", points), 1);
}
void MainWindow::updateMoves(int moves)
{
statusBar()->changeItem(i18n("Possible moves: %1", moves), 3);
}
void MainWindow::updateRemainingTime(int remainingSeconds)
{
//store the time: if remainingSeconds == -1, the old time is just re-rendered (used by the configuration action MainWindow::showMinutesOnTimer)
......
......@@ -26,6 +26,7 @@
class Game;
#endif
class KToggleAction;
class QTime;
class QTimer;
#include <KXmlGuiWindow>
......@@ -54,6 +55,7 @@ class MainWindow : public KXmlGuiWindow
void loadSettings();
void showMinutesOnTimer(bool showMinutes);
signals:
void showHint();
void pause(bool paused);
void updateScheduled(int milliseconds);
protected:
......@@ -61,8 +63,10 @@ class MainWindow : public KXmlGuiWindow
protected slots:
void updateTime();
void updatePoints(int points);
void updateMoves(int moves);
void updateRemainingTime(int remainingSeconds);
private:
KToggleAction *untimed;
Game *m_game;
Container *m_container;
......
......@@ -111,10 +111,9 @@ void Renderer::boardResized(int width, int height, int leftOffset, int topOffset
const QString svgName("kdiamond-background");
const QString boardSvgName("kdiamond-border");
QString pixName = svgName + sizeSuffix.arg(width).arg(height);
QPixmap pix;
QPixmap pix(p->m_sceneSize);
if (!p->m_cache.find(pixName, pix))
{
pix = QPixmap(p->m_sceneSize);
pix.fill(Qt::transparent);
QPainter painter(&pix);
p->m_renderer.render(&painter, svgName);
......@@ -167,12 +166,11 @@ QPixmap pixmapFromCache(RendererPrivate *p, const QString &svgName, const QSize
{
if (size.isEmpty())
return QPixmap();
QPixmap pix;
QPixmap pix(size);
QString pixName = svgName + sizeSuffix.arg(size.width()).arg(size.height());
if (!p->m_cache.find(pixName, pix))
{
pix = QPixmap(size);
pix.fill(Qt::transparent);
QPainter painter(&pix);
p->m_renderer.render(&painter, svgName);
......@@ -189,20 +187,7 @@ QPixmap Renderer::diamond(KDiamond::Color color)
if (color != KDiamond::Selection)
{
for (int i = 0; i < p->m_removeAnimFrameCount; ++i)
{
QString frameSvgName = svgName + frameSuffix.arg(i);
QString framePixName = frameSvgName + sizeSuffix.arg(p->m_diamondSize.width()).arg(p->m_diamondSize.height());
QPixmap pix;
if (!p->m_cache.find(framePixName, pix))
{
pix = QPixmap(p->m_diamondSize);
pix.fill(Qt::transparent);
QPainter painter(&pix);
p->m_renderer.render(&painter, frameSvgName);
painter.end();
p->m_cache.insert(framePixName, pix);
}
}
pixmapFromCache(p, svgName + frameSuffix.arg(i), p->m_diamondSize);
}
//return the static pixmap
return pixmapFromCache(p, svgName, p->m_diamondSize);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment