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