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