1 /*
2 This file is part of the KDE games kwin4 program
3 SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "displaygame.h"
9
10 // own
11 #include "piecesprite.h"
12 #include "pixmapsprite.h"
13 #include "scoresprite.h"
14 #include "spritenotify.h"
15 #include "kwin4doc.h"
16 #include "kwin4view.h"
17 #include "reflectiongraphicsscene.h"
18 // KF
19 #include "kfourinline_debug.h"
20 // Qt
21 #include <QColor>
22 #include <QPixmap>
23 #include <QPoint>
24 // Std
25 #include <cmath>
26 #include <cassert>
27
28
29 // Constructor for the display
DisplayGame(ReflectionGraphicsScene * scene,ThemeManager * theme,QGraphicsView * parent)30 DisplayGame::DisplayGame(ReflectionGraphicsScene* scene, ThemeManager* theme, QGraphicsView* parent)
31 : Themeable(QStringLiteral("gamedisplay"),theme), QObject(parent)
32 {
33 // Store arguments as attributes
34 mScene = scene;
35 mView = parent;
36 mTheme = theme;
37
38 // Choose a background color
39 scene->setBackgroundBrush(QColor(0,0,128));
40
41 // Clear storage of all sprites
42 mSprites.clear();
43
44 // Create piece sprites
45 for (int i=0; i<42; i++)
46 {
47 PieceSprite* sprite = new PieceSprite(QStringLiteral("piece"), mTheme, i, mScene);
48 if (!sprite) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "piece";
49 mSprites.append(sprite);
50 mPieces.append(sprite);
51 sprite->hide();
52 }
53
54 // Create stars
55 for (int i=0;i<4;i++)
56 {
57 PixmapSprite* sprite = new PixmapSprite(QStringLiteral("star"), mTheme, i, mScene);
58 if (!sprite) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "star";
59 mSprites.append(sprite);
60 mStars.append(sprite);
61 sprite->hide();
62 }
63
64 // Create board
65 mBoard = new PixmapSprite(QStringLiteral("board"), mTheme, 0, mScene);
66 if (!mBoard) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "board";
67 mSprites.append(mBoard);
68 mBoard->hide();
69
70 // Create hint
71 mHint = new PixmapSprite(QStringLiteral("hint"), mTheme, 0, mScene);
72 if (!mHint) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "hint";
73 mSprites.append(mHint);
74 mHint->hide();
75
76 // Create Game Over
77 // mGameOver = new PixmapSprite("gameover", mTheme, 0, mScene);
78 // if (!mGameOver) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "gameover";
79 // mSprites.append(mGameOver);
80 // mGameOver->hide();
81
82 // Create score board
83 mScoreBoard = new ScoreSprite(QStringLiteral("scoreboard"), mTheme, 0, mScene);
84 if (!mScoreBoard) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "scoreboard";
85 mSprites.append(mScoreBoard);
86 mScoreBoard->hide();
87
88
89 // Create movement indication arrows
90 for (int i=0; i<7; i++)
91 {
92 PixmapSprite* arrow = new PixmapSprite(QStringLiteral("arrow"), mTheme, i, mScene);
93 if (!arrow) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "arrow";
94 mSprites.append(arrow);
95 mArrows.append(arrow);
96 arrow->hide();
97 }
98
99 // Static decoration
100 KConfigGroup config = thememanager()->config(id());
101 QStringList deco = config.readEntry("decoration", QStringList());
102 for (int i = 0; i < deco.size(); i++)
103 {
104 PixmapSprite* sprite = new PixmapSprite(deco.at(i), mTheme, i, mScene);
105 if (!sprite) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << deco.at(i);
106 mSprites.append(sprite);
107 sprite->show();
108 }
109
110 // Animation timer
111 mTimer = new QTimer(this);
112 connect(mTimer, &QTimer::timeout, this, &DisplayGame::advance);
113
114 // Redraw
115 if (theme) theme->updateTheme(this);
116
117 }
118
119
120 // Destructor: clean up
~DisplayGame()121 DisplayGame::~DisplayGame()
122 {
123 delete mTimer;
124 qDeleteAll(mSprites);
125 }
126
127
128 // Called by thememanager when theme or theme geometry changes. Redraw and resize
129 // this display.
changeTheme()130 void DisplayGame::changeTheme()
131 {
132 // Retrieve theme data
133 KConfigGroup config = thememanager()->config(id());
134 QPointF board_pos = config.readEntry("board-pos", QPointF(1.0,1.0));
135 QPointF arrow_pos = config.readEntry("arrow-pos", QPointF(1.0,1.0));
136 QPointF board_spread = config.readEntry("board-spread", QPointF(1.0,1.0));
137
138 // Retrieve background pixmap
139 QString bgsvgid = config.readEntry("background-svgid");
140 QPixmap pixmap = thememanager()->getPixmap(bgsvgid, mScene->sceneRect().size().toSize());
141 mScene->setBackgroundBrush(pixmap);
142 QRectF pos = mBoard->sceneBoundingRect();
143
144
145 // Move movement arrows
146 for (int i=0; i<7; i++)
147 {
148 PixmapSprite* arrow = mArrows.value(i);
149 QPointF to = QPointF(board_spread.x()*i + arrow_pos.x(),
150 board_spread.y()*0 + arrow_pos.y());
151 arrow->setPosition(to);
152
153 }
154
155 // Move piece sprites
156 for (int i=0; i<42; i++)
157 {
158 PieceSprite* piece = mPieces.value(i);
159 int x = piece->logicalPos().x();
160 int y = piece->logicalPos().y();
161 QPointF to = QPointF(board_spread.x()*x + board_pos.x(),
162 board_spread.y()*y + board_pos.y());
163 piece->setPosition(to);
164 }
165
166 // Move hint
167 {
168 int x = mHint->logicalPos().x();
169 int y = mHint->logicalPos().y();
170 QPointF to = QPointF(board_spread.x()*x + board_pos.x(),
171 board_spread.y()*y + board_pos.y());
172 mHint->setPosition(to);
173 }
174
175
176 // Move stars
177 for (int i=0;i<4;i++)
178 {
179 PixmapSprite* star = mStars.value(i);
180 int x = star->logicalPos().x();
181 int y = star->logicalPos().y();
182 QPointF to = QPointF(board_spread.x()*x + board_pos.x(),
183 board_spread.y()*y + board_pos.y());
184 star->setPosition(to);
185 }
186
187
188 // Check whether the theme uses reflection handling
189 if( config.readEntry("use-reflection", false))
190 {
191 dynamic_cast<KWin4View*>(mView)->setReflection((int)pos.x(), (int)(pos.y()+pos.height()),
192 (int)(mScene->sceneRect().width() - pos.x()), (int)(pos.height()*0.3));
193 }
194 else
195 {
196 // Zero width disables the reflections
197 dynamic_cast<KWin4View*>(mView)->setReflection(0,0,0,0);
198 }
199
200 mView->update();
201 }
202
203
204 // Start a new game. Initialize graphics.
start()205 void DisplayGame::start()
206 {
207 // Run timer (unused)
208 // mTimer->setSingleShot(true);
209 // mTimer->start(0);
210
211
212 // Show decoration
213 mBoard->show();
214 mScoreBoard->show();
215
216
217 // Show movement arrows
218 for (int i=0; i<7; i++)
219 {
220 mArrows.value(i)->setFrame(0);
221 mArrows.value(i)->show();
222 }
223
224 // Hide piece sprites
225 for (int i=0; i<42; i++)
226 {
227 mPieces.value(i)->hide();
228 }
229
230 // Hide stars
231 for (int i=0;i<4;i++)
232 {
233 mStars.value(i)->setAnimation(false);
234 mStars.value(i)->hide();
235 }
236
237 }
238
239
240 // Run game animation
advance()241 void DisplayGame::advance()
242 {
243 // Currently unused
244 }
245
246
247 // Display end game sprite
displayEnd()248 void DisplayGame::displayEnd()
249 {
250 // assert(mGameOver != 0);
251 // mGameOver->show();
252 }
253
254
255 // Set the movement indicator arrows above the game board
displayArrow(int x,int color)256 void DisplayGame::displayArrow(int x,int color)
257 {
258 // Set all arrows back to frame 0
259 for (int i=0; i<7; i++)
260 {
261 mArrows.value(i)->setFrame(0);
262 }
263
264 // Check for no color
265 if (color==Nobody)
266 {
267 return ;
268 }
269
270 // Make sure the frames are chosen properly
271 if (color==Yellow) mArrows.value(x)->setFrame(1);
272 else mArrows.value(x)->setFrame(2);
273 }
274
275
276 // Set a game HINT sprite
displayHint(int x,int y,bool show)277 void DisplayGame::displayHint(int x, int y, bool show)
278 {
279 // Invert height
280 y=5-y;
281
282 // Check for removal of the sprite
283 if (!show)
284 {
285 mHint->hide();
286 return;
287 }
288
289 // Retrieve theme data
290 KConfigGroup config = thememanager()->config(id());
291 QPointF board_pos = config.readEntry("board-pos", QPointF(1.0,1.0));
292 QPointF board_spread = config.readEntry("board-spread", QPointF(1.0,1.0));
293
294 QPointF to = QPointF(board_spread.x()*x + board_pos.x(),
295 board_spread.y()*y + board_pos.y());
296 mHint->setLogicalPos(QPoint(x,y));
297 mHint->setPosition(to);
298 mHint->show();
299 }
300
301
302 // Set a game piece, red or yellow or hidden depending on 'color'
displayPiece(int x,int y,int color,int no,bool animation)303 SpriteNotify* DisplayGame::displayPiece(int x, int y, int color, int no, bool animation)
304 {
305 // Invert height
306 y=5-y;
307
308 // Get piece
309 PieceSprite *sprite = mPieces.value(no);
310 assert(sprite != nullptr);
311
312 // Check for removal of sprite
313 if (color==Nobody)
314 {
315 sprite->hide();
316 return nullptr;
317 }
318
319 // Retrieve theme data
320 KConfigGroup config = thememanager()->config(id());
321 QPointF board_pos = config.readEntry("board-pos", QPointF(1.0,1.0));
322 QPointF board_spread = config.readEntry("board-spread", QPointF(1.0,1.0));
323 double velocity = config.readEntry("move-velocity", 0.8);
324
325 // Make sure the frames are ok
326 int frame;
327 if (color==Yellow) frame = 0;
328 else frame = 1;
329
330
331 // Just draw the sprites or show an movement animation?
332 if (animation)
333 {
334 QPointF from = QPointF(board_spread.x()*x + board_pos.x(),
335 board_spread.y()*(-1) + board_pos.y());
336 QPointF to = QPointF(board_spread.x()*x + board_pos.x(),
337 board_spread.y()*y + board_pos.y());
338 sprite->setFrame(frame);
339 sprite->startLinear(from, to, velocity);
340 // For theme store (x,y) board coordinate
341 sprite->setLogicalPos(QPoint(x, y));
342 }
343 else
344 {
345 QPointF to = QPointF(board_spread.x()*x + board_pos.x(),
346 board_spread.y()*y + board_pos.y());
347 sprite->setFrame(frame);
348 sprite->setPosition(to);
349 // For theme store (x,y) board coordinate
350 sprite->setLogicalPos(QPoint(x, y));
351 }
352
353 sprite->show();
354 return sprite->notify();
355 }
356
357
358 // Draw Star Sprites as winning indicator
displayStar(int x,int y,int no)359 void DisplayGame::displayStar(int x,int y,int no)
360 {
361 // Invert height
362 y=5-y;
363 PixmapSprite* star = mStars.value(no-1);
364 assert(star != nullptr);
365
366 // Retrieve theme data
367 KConfigGroup config = thememanager()->config(id());
368 QPointF board_pos = config.readEntry("board-pos", QPointF(1.0,1.0));
369 QPointF board_spread = config.readEntry("board-spread", QPointF(1.0,1.0));
370
371 QPointF pos = QPointF(board_spread.x()*x + board_pos.x(),
372 board_spread.y()*y + board_pos.y());
373 star->setAnimation(true);
374 star->setLogicalPos(QPoint(x,y));
375 star->setPosition(pos);
376 star->show();
377 }
378
379
380 // Return the mouse mapped to the board or bar item so that a
381 // move 0..6 is generated. -1 means an illegal position.
mapMouseToMove(const QPoint & pos)382 int DisplayGame::mapMouseToMove(const QPoint &pos)
383 {
384 // Error?
385 if (!mBoard) return -1;
386
387 // Find which arrow the mouse is closest to. This way
388 // all board scaling become irrelevant. An alternative
389 // would be to calculate the position using board_pos and
390 // board_spread.
391 for (int i=0; i<7; i++)
392 {
393 PixmapSprite* arrow = mArrows.value(i);
394 int width = int(arrow->boundingRect().width());
395 int relPos = int(arrow->mapFromParent(QPointF(pos)).x());
396 // Found matching arrow
397 if (relPos > 0 && relPos<= width)
398 {
399 return i;
400 }
401 }
402
403 // Nothing found
404 return -1;
405 }
406
407
408
409 // Retrieve the score sprite.
score()410 ScoreSprite* DisplayGame::score()
411 {
412 return mScoreBoard;
413 }
414
415
416
417