1 /*
2     SPDX-FileCopyrightText: 2008-2009 Stefan Majewsky <majewsky@gmx.net>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "game-state.h"
8 #include "settings.h"
9 
10 #include <QElapsedTimer>
11 #include <KLocalizedString>
12 #include <KNotification>
13 
14 namespace KDiamond
15 {
16 
17 class GameStatePrivate
18 {
19 public:
20     GameStatePrivate();
21     ~GameStatePrivate();
22 
23     QElapsedTimer m_gameTime, m_pauseTime;
24 
25     Mode m_mode;
26     State m_state;
27     int m_earnedMilliseconds, m_leftMilliseconds, m_pausedMilliseconds;
28     int m_points, m_cascade;
29 };
30 
31 }
32 
GameStatePrivate()33 KDiamond::GameStatePrivate::GameStatePrivate()
34 //these should be the same values as in KDiamond::GameState::startNewGame
35     : m_mode(Settings::untimed() ? KDiamond::UntimedGame : KDiamond::NormalGame)
36     , m_state(KDiamond::Playing)
37     , m_earnedMilliseconds(0)
38     , m_leftMilliseconds(0)
39     , m_pausedMilliseconds(0)
40     , m_points(0)
41     , m_cascade(0)
42 {
43     m_gameTime.start();
44     m_pauseTime.start(); //now we can always call restart() when we need it
45 }
46 
~GameStatePrivate()47 KDiamond::GameStatePrivate::~GameStatePrivate()
48 {
49 }
50 
GameState()51 KDiamond::GameState::GameState()
52     : p(new KDiamond::GameStatePrivate)
53 {
54     startTimer(500);
55 }
56 
~GameState()57 KDiamond::GameState::~GameState()
58 {
59     delete p;
60 }
61 
mode() const62 KDiamond::Mode KDiamond::GameState::mode() const
63 {
64     return p->m_mode;
65 }
66 
state() const67 KDiamond::State KDiamond::GameState::state() const
68 {
69     return p->m_state;
70 }
71 
leftTime() const72 int KDiamond::GameState::leftTime() const
73 {
74     return p->m_leftMilliseconds;
75 }
76 
points() const77 int KDiamond::GameState::points() const
78 {
79     return p->m_points;
80 }
81 
setMode(KDiamond::Mode mode)82 void KDiamond::GameState::setMode(KDiamond::Mode mode)
83 {
84     p->m_mode = mode;
85     Settings::setUntimed(p->m_mode == KDiamond::UntimedGame);
86     update(true); //recalculate time
87 }
88 
setState(KDiamond::State state)89 void KDiamond::GameState::setState(KDiamond::State state)
90 {
91     if (p->m_state == KDiamond::Finished) { //cannot be changed (except with startNewGame slot)
92         return;
93     }
94     //check for important transitions
95     if (p->m_state == KDiamond::Paused && state == KDiamond::Playing) {
96         //resuming from paused state
97         p->m_pausedMilliseconds += p->m_pauseTime.elapsed();
98         update(true); //recalculate time
99         Q_EMIT message(QString()); //flush message
100     } else if (p->m_state == KDiamond::Playing && state == KDiamond::Paused) {
101         //going to paused state
102         p->m_pauseTime.restart();
103         Q_EMIT message(i18n("Click the pause button again to resume the game."));
104     }
105     //set new state
106     p->m_state = state;
107     Q_EMIT stateChanged(state);
108     if (state == KDiamond::Finished) {
109         KNotification::event(QStringLiteral("gamefinished"));
110         Q_EMIT message(i18nc("Not meant like 'You have lost', more like 'Time is up'.", "Game over."));
111     }
112 }
113 
timerEvent(QTimerEvent * event)114 void KDiamond::GameState::timerEvent(QTimerEvent *event)
115 {
116     Q_UNUSED(event)
117     update();
118 }
119 
addPoints(int removedDiamonds)120 void KDiamond::GameState::addPoints(int removedDiamonds)
121 {
122     p->m_points += ++p->m_cascade;
123     p->m_earnedMilliseconds += 500;
124     if (removedDiamonds > 3)
125         //add half an extra second for each extra diamond
126     {
127         p->m_earnedMilliseconds += 500 * (removedDiamonds - 3);
128     }
129     Q_EMIT pointsChanged(p->m_points);
130     update(true); //recalculate time
131 }
132 
removePoints(int points)133 void KDiamond::GameState::removePoints(int points)
134 {
135     p->m_points = qMax(0, p->m_points - points);
136     Q_EMIT pointsChanged(p->m_points);
137 }
138 
resetCascadeCounter()139 void KDiamond::GameState::resetCascadeCounter()
140 {
141     p->m_cascade = 0;
142 }
143 
startNewGame()144 void KDiamond::GameState::startNewGame()
145 {
146     p->m_gameTime.restart();
147     //p->m_mode does not need to be reset as it is kept in sync with Settings::untimed()
148     //these should be the same values as in KDiamond::GameStatePrivate constructor
149     p->m_state = KDiamond::Playing;
150     p->m_earnedMilliseconds = 0;
151     p->m_leftMilliseconds = 0;
152     p->m_pausedMilliseconds = 0;
153     p->m_points = 0;
154     p->m_cascade = 0;
155     update(true); //recalculate time
156     Q_EMIT message(QString()); //flush message
157     Q_EMIT stateChanged(p->m_state);
158     Q_EMIT pointsChanged(p->m_points);
159 }
160 
update(bool forceRecalculation)161 void KDiamond::GameState::update(bool forceRecalculation)
162 {
163     //will not recalculate time when not playing a normal game (unless forced)
164     if (p->m_mode == KDiamond::UntimedGame || (p->m_state != KDiamond::Playing && !forceRecalculation)) {
165         return;
166     }
167     //calculate new time
168     const int leftMilliseconds = 1000 * KDiamond::GameDuration + p->m_earnedMilliseconds + p->m_pausedMilliseconds - p->m_gameTime.elapsed();
169     const int leftSeconds = leftMilliseconds / 1000;
170     if (leftSeconds <= 0) {
171         setState(KDiamond::Finished);
172     }
173     if (p->m_leftMilliseconds / 1000 != leftSeconds) {
174         Q_EMIT leftTimeChanged(qMax(0, leftSeconds));
175     }
176     p->m_leftMilliseconds = leftMilliseconds;
177 }
178 
179