1 /*
2     SPDX-FileCopyrightText: 2012 Roney Gomes <roney477@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include <KLocalizedString>
8 
9 #include <QFont>
10 #include <QTimeLine>
11 
12 #include "kgoldrunner_debug.h"
13 #include "kgrview.h"
14 #include "kgrscene.h"
15 #include "kgrsprite.h"
16 #include "kgrrenderer.h"
17 
18 const StartFrame animationStartFrames [nAnimationTypes] = {
19                  RIGHTWALK1,    LEFTWALK1,  RIGHTCLIMB1,    LEFTCLIMB1,
20                  CLIMB1,        CLIMB1,     FALL1,          FALL2,
21                  DIGBRICK1,	// Start frame for OPEN_BRICK.
22                  DIGBRICK6};	// Start frame for CLOSE_BRICK.
23 
KGrScene(KGrView * view)24 KGrScene::KGrScene      (KGrView * view)
25     :
26     QGraphicsScene      (view),
27     // Allow FIELDWIDTH * FIELDHEIGHT tiles for the KGoldruner level-layouts,
28     // plus 2 more tile widths all around for text areas, frame and spillover
29     // for mouse actions (to avoid accidental clicks affecting the desktop).
30     m_view              (view),
31     m_background        (nullptr),
32     m_level             (1),
33     m_title             (nullptr),
34     m_replayMessage     (nullptr),
35     m_livesText         (nullptr),
36     m_scoreText         (nullptr),
37     m_hasHintText       (nullptr),
38     m_pauseResumeText   (nullptr),
39     m_heroId            (0),
40     m_tilesWide         (FIELDWIDTH  + 2 * 2),
41     m_tilesHigh         (FIELDHEIGHT + 2 * 2),
42     m_tileSize          (10),
43     m_toolbarTileSize   (10),
44     m_themeChanged      (true),
45     m_topLeftX          (0),
46     m_topLeftY          (0),
47     m_mouse             (new QCursor()),
48     m_fadingTimeLine    (new QTimeLine (1000, this))
49 {
50     setItemIndexMethod(NoIndex);
51 
52     m_tiles.fill        (nullptr,     m_tilesWide * m_tilesHigh);
53     m_tileTypes.fill    (FREE,  m_tilesWide * m_tilesHigh);
54 
55     m_renderer  = new KGrRenderer (this);
56 
57     m_frame = addRect (0, 0, 100, 100);		// Create placeholder for frame.
58     m_frame->setVisible (false);
59 
60     m_spotlight = addRect (0, 0, 100, 100);	// Create placeholder for spot.
61     m_spotlight->setVisible (false);
62 
63     m_title = new QGraphicsSimpleTextItem();
64     addItem (m_title);
65 
66     m_replayMessage = new QGraphicsSimpleTextItem();
67     addItem (m_replayMessage);
68     m_replayMessage->setVisible (false);	// Visible only in demo/replay.
69 
70     m_livesText = new QGraphicsSimpleTextItem();
71     addItem (m_livesText);
72 
73     m_scoreText = new QGraphicsSimpleTextItem();
74     addItem (m_scoreText);
75 
76     m_hasHintText = new QGraphicsSimpleTextItem();
77     addItem (m_hasHintText);
78 
79     m_pauseResumeText = new QGraphicsSimpleTextItem();
80     addItem (m_pauseResumeText);
81 
82     m_fadingTimeLine->setEasingCurve(QEasingCurve::OutCurve);
83     m_fadingTimeLine->setUpdateInterval (50);
84     connect(m_fadingTimeLine, &QTimeLine::valueChanged, this, &KGrScene::drawSpotlight);
85     connect(m_fadingTimeLine, &QTimeLine::finished, this, &KGrScene::fadeFinished);
86 }
87 
~KGrScene()88 KGrScene::~KGrScene()
89 {
90     delete m_mouse;
91     delete m_fadingTimeLine;
92 }
93 
redrawScene()94 void KGrScene::redrawScene ()
95 {
96     //qCDebug(KGOLDRUNNER_LOG) << "REDRAW: m_sizeChanged" << m_sizeChanged << "m_themeChanged" << m_themeChanged;
97     bool redrawToolbar = false;
98     if (m_sizeChanged) {
99         // Calculate what size of tile will fit in the view.
100         QSize size      = m_view->size();
101         int tileSize    = qMin (size.width()  / m_tilesWide,
102                                 size.height() / m_tilesHigh);
103         m_topLeftX   = (size.width()  - m_tilesWide * tileSize)/2.0;
104         m_topLeftY   = (size.height() - m_tilesHigh * tileSize)/2.0;
105         setSceneRect   (0, 0, size.width(), size.height());
106 	//qCDebug(KGOLDRUNNER_LOG) << "SIZE" << size << "TL" << m_topLeftX << m_topLeftY << "TILE" << tileSize << "was" << m_tileSize << m_toolbarTileSize;
107 
108         // Make the fade-out/fade-in rectangle cover the playing area.
109         m_spotlight->setRect (m_topLeftX + 2 * tileSize - 1,
110                               m_topLeftY + 2 * tileSize - 1,
111                               (m_tilesWide - 4) * tileSize + 2,
112                               (m_tilesHigh - 4) * tileSize + 2);
113         m_maxRadius = (5 * m_spotlight->rect().width() + 4) / 8;
114         m_spotlight->setPen (Qt::NoPen);
115         m_spotlight->setZValue (10);
116         m_spotlight->setVisible (false);
117 
118         // Set up the gradient to draw the spotlight (black with a hole in it).
119         QPointF center        (width() * 0.5, height() * 0.5);
120         m_gradient.setCenter  (center);
121         m_gradient.setFocalPoint (center);
122         m_gradient.setRadius  (m_maxRadius);
123         m_gradient.setColorAt (1.00, QColor (0, 0, 0, 255));
124         m_gradient.setColorAt (0.85, QColor (0, 0, 0, 0));
125 
126         int index = 0;
127         for (KGameRenderedItem * tile : std::as_const(m_tiles)) {
128             if (tile) {
129                 setTile (tile, tileSize, index/m_tilesHigh, index%m_tilesHigh);
130             }
131             index++;
132         }
133         for (KGrSprite * sprite : std::as_const(m_sprites)) {
134             if (sprite) {
135                 sprite->changeCoordinateSystem
136                         (m_topLeftX, m_topLeftY, tileSize);
137             }
138         }
139 
140         if (m_tileSize != tileSize) {
141             // Do not expand the toolbar (in edit mode) until there is room for
142             // it.  This avoids a nasty expand-contract-expand-contract loop.
143             m_toolbarTileSize = ((tileSize > m_tileSize) && (m_topLeftY == 0)) ?
144                                   m_tileSize : tileSize;
145         }
146         // When conditions are right, redraw editing icons, if in edit mode.
147         redrawToolbar = ((m_toolbarTileSize != m_tileSize) &&
148                          (m_topLeftY > 0)) ? true : false;
149         m_tileSize = tileSize;
150         m_sizeChanged = false;
151     }
152 
153     // Re-draw text, background and frame if either scene-size or theme changes.
154 
155     // Resize and draw texts for title, score, lives, hasHint and pauseResume.
156     setTextFont (m_title, 0.6);
157     setTitle (m_title->text());
158     placeTextItems();
159 
160     // Resize and draw different backgrounds, depending on the level and theme.
161     loadBackground (m_level);
162 
163     if (m_renderer->hasBorder()) {
164         // There are border tiles in the theme, so do not draw a frame.
165         m_frame->setVisible (false);
166     }
167     else {
168         // There are no border tiles, so draw a frame around the board.
169         drawFrame();
170     }
171 
172     if (m_themeChanged) {
173         // Fill the scene (and view) with the new background color.  Do this
174         // even if the background has no border, to avoid ugly white rectangles
175         // appearing if rendering and painting is momentarily a bit slow.
176         setBackgroundBrush (m_renderer->borderColor());
177 
178 	// Erase border tiles (if any) and draw new ones, if new theme has them.
179         drawBorder();
180 
181         // Redraw all the tiles, except for borders and tiles of type FREE.
182         for (int i = 1; i <= FIELDWIDTH; i++) {
183             for (int j = 1; j <= FIELDHEIGHT; j++) {
184                 int index = i * m_tilesHigh + j;
185                 paintCell (i, j, m_tileTypes[index]);
186             }
187         }
188 
189         // Redraw editing icons if theme changes when in edit mode.
190         redrawToolbar = true;
191         m_themeChanged = false;
192     }
193 
194     if (redrawToolbar) {
195         m_toolbarTileSize = m_tileSize;	// If game is in edit mode, KGoldrunner
196         Q_EMIT redrawEditToolbar();	// object redraws the editToolbar.
197     }
198 }
199 
changeTheme()200 void KGrScene::changeTheme()
201 {
202     m_themeChanged = true;
203     redrawScene();
204 }
205 
changeSize()206 void KGrScene::changeSize()
207 {
208     m_sizeChanged = true;
209     redrawScene();
210 }
211 
setTitle(const QString & newTitle)212 void KGrScene::setTitle (const QString & newTitle)
213 {
214     if (! m_title) return;
215 
216     m_title->setText (newTitle);
217     QRectF r = m_title->boundingRect();		// Centre the title.
218     m_title->setPos ((sceneRect().width() - r.width())/2,
219                       m_topLeftY + (m_tileSize - r.height())/2);
220 }
221 
setReplayMessage(const QString & msg)222 void KGrScene::setReplayMessage (const QString & msg)
223 {
224     m_replayMessage->setText (msg);
225 }
226 
showReplayMessage(bool onOff)227 void KGrScene::showReplayMessage (bool onOff)
228 {
229     m_replayMessage->setVisible (onOff);
230 }
231 
placeTextItems()232 void KGrScene::placeTextItems()
233 {
234     setTextFont (m_replayMessage, 0.5);
235     setTextFont (m_livesText, 0.5);
236     setTextFont (m_scoreText, 0.5);
237     setTextFont (m_hasHintText, 0.5);
238     setTextFont (m_pauseResumeText, 0.5);
239 
240     QRectF r = m_replayMessage->boundingRect();
241     m_replayMessage->setPos ((sceneRect().width() - r.width())/2,
242                               m_topLeftY + 1.4 * m_tileSize - 0.5 * r.height());
243     m_replayMessage->setZValue (10);
244 
245     qreal totalWidth = 0.0;
246     r = m_livesText->boundingRect();
247     qreal x = m_topLeftX + 2 * m_tileSize;
248     qreal y = sceneRect().height() - m_topLeftY - (m_tileSize + r.height())/2;
249 
250     m_livesText->setPos (x, y);
251     totalWidth += r.width();
252 
253     r = m_scoreText->boundingRect();
254     m_scoreText->setPos (x + totalWidth, y);
255     totalWidth += r.width();
256 
257     r = m_hasHintText->boundingRect();
258     m_hasHintText->setPos (x + totalWidth, y);
259     totalWidth += r.width();
260 
261     r = m_pauseResumeText->boundingRect();
262     m_pauseResumeText->setPos (x + totalWidth, y);
263     totalWidth += r.width();
264 
265     qreal spacing = ((m_tilesWide - 4) * m_tileSize - totalWidth) / 3.0;
266     if (spacing < 0.0)
267         return;
268 
269     m_scoreText->moveBy (spacing, 0.0);
270     m_hasHintText->moveBy (2.0 * spacing, 0.0);
271     m_pauseResumeText->moveBy (3.0 * spacing, 0.0);
272 }
273 
showLives(long lives)274 void KGrScene::showLives (long lives)
275 {
276     if (m_livesText)
277         m_livesText->setText (i18n("Lives: %1", QString::number(lives)
278                                                 .rightJustified(3, QLatin1Char('0'))));
279 }
280 
showScore(long score)281 void KGrScene::showScore (long score)
282 {
283     if (m_scoreText)
284         m_scoreText->setText (i18n("Score: %1", QString::number(score)
285                                                 .rightJustified(7, QLatin1Char('0'))));
286 }
287 
setHasHintText(const QString & msg)288 void KGrScene::setHasHintText (const QString & msg)
289 {
290     if (m_hasHintText)
291         m_hasHintText->setText (msg);
292 }
293 
setPauseResumeText(const QString & msg)294 void KGrScene::setPauseResumeText (const QString & msg)
295 {
296     if (m_pauseResumeText)
297         m_pauseResumeText->setText (msg);
298 }
299 
goToBlack()300 void KGrScene::goToBlack()
301 {
302         drawSpotlight (0);
303 }
304 
fadeIn(bool inOut)305 void KGrScene::fadeIn (bool inOut)
306 {
307     // For fade-in,  inOut = true,  circle opens,  from 0.0 to 1.0.
308     // For fade-out, inOut = false, circle closes, from 1.0 to 0.0.
309     m_fadingTimeLine->setDirection (inOut ? QTimeLine::Forward
310                                          : QTimeLine::Backward);
311     m_fadingTimeLine->start();
312 }
313 
drawSpotlight(qreal ratio)314 void KGrScene::drawSpotlight (qreal ratio)
315 {
316     if (ratio > 0.99) {
317 	m_spotlight->setVisible (false);	// End of the close-open cycle.
318 	return;
319     }
320     else if (ratio <= 0.01) {
321 	m_spotlight->setBrush (Qt::black);
322     }
323     else {
324         m_gradient.setRadius (ratio * m_maxRadius);
325         m_spotlight->setBrush (QBrush (m_gradient));
326     }
327 
328     m_spotlight->setVisible (true);
329 }
330 
setLevel(unsigned int level)331 void KGrScene::setLevel (unsigned int level)
332 {
333     if (level == m_level) {
334         return;
335     }
336     m_level = level;
337     loadBackground (level);	// Load background for level.
338 }
339 
loadBackground(const int level)340 void KGrScene::loadBackground (const int level)
341 {
342     // NOTE: The background picture can be the same size as the level-layout (as
343     // in the Egypt theme) OR it can be the same size as the entire viewport.
344     // In this example the background is fitted into the level-layout.
345     m_background = m_renderer->getBackground (level, m_background);
346 
347     m_background->setRenderSize (QSize ((m_tilesWide - 4) * m_tileSize,
348                                         (m_tilesHigh - 4) * m_tileSize));
349     m_background->setPos    (m_topLeftX + 2 * m_tileSize,
350                              m_topLeftY + 2 * m_tileSize);
351     // Keep the background behind the level layout.
352     m_background->setZValue (-1);
353 }
354 
setTile(KGameRenderedItem * tile,const int tileSize,const int i,const int j)355 void KGrScene::setTile (KGameRenderedItem * tile, const int tileSize,
356                         const int i, const int j)
357 {
358     tile->setRenderSize (QSize (tileSize, tileSize));
359     tile->setPos (m_topLeftX + (i+1) * tileSize, m_topLeftY + (j+1) * tileSize);
360 }
361 
setBorderTile(const QString & spriteKey,const int x,const int y)362 void KGrScene::setBorderTile (const QString &spriteKey, const int x, const int y)
363 {
364     int index               = x * m_tilesHigh + y;
365     KGameRenderedItem * t   = m_renderer->getBorderItem (spriteKey,
366                                                          m_tiles.at(index));
367     m_tiles[index]          = t;
368 
369     if (t) {
370         setTile (t, m_tileSize, x, y);
371     }
372 }
373 
drawBorder()374 void KGrScene::drawBorder()
375 {
376     // Corners.
377     setBorderTile (QStringLiteral("frame-topleft"), 0, 0);
378     setBorderTile (QStringLiteral("frame-topright"), FIELDWIDTH + 1, 0);
379     setBorderTile (QStringLiteral("frame-bottomleft"), 0, FIELDHEIGHT + 1);
380     setBorderTile (QStringLiteral("frame-bottomright"), FIELDWIDTH + 1, FIELDHEIGHT + 1);
381 
382     // Upper side.
383     for (int i = 1; i <= FIELDWIDTH; i++)
384         setBorderTile (QStringLiteral("frame-top"), i, 0);
385 
386     // Lower side.
387     for (int i = 1; i <= FIELDWIDTH; i++)
388         setBorderTile (QStringLiteral("frame-bottom"), i, FIELDHEIGHT + 1);
389 
390     // Left side.
391     for (int i = 1; i <= FIELDHEIGHT; i++)
392         setBorderTile (QStringLiteral("frame-left"), 0, i);
393 
394     // Right side.
395     for (int i = 1; i <= FIELDHEIGHT; i++)
396         setBorderTile (QStringLiteral("frame-right"), FIELDWIDTH + 1, i);
397 }
398 
drawFrame()399 void KGrScene::drawFrame()
400 {
401     int w = 0.05 * m_tileSize + 0.5;
402     w = w < 1 ? 1 : w;
403     m_frame->setRect (
404 	m_topLeftX + (2 * m_tileSize) - (3 * w),
405 	m_topLeftY + (2 * m_tileSize) - (3 * w),
406 	FIELDWIDTH  * m_tileSize + 6 * w,
407 	FIELDHEIGHT * m_tileSize + 6 * w);
408     //qCDebug(KGOLDRUNNER_LOG) << "FRAME WIDTH" << w << "tile size" << m_tileSize << "rectangle" << m_frame->rect();
409     QPen pen = QPen (m_renderer->textColor());
410     pen.setWidth (w);
411     m_frame->setPen (pen);
412     m_frame->setVisible (true);
413 }
414 
paintCell(const int i,const int j,const char type)415 void KGrScene::paintCell (const int i, const int j, const char type)
416 {
417     int index               = i * m_tilesHigh + j;
418     KGameRenderedItem * t   = m_renderer->getTileItem (type, m_tiles.at(index));
419     m_tiles[index]          = t;
420     m_tileTypes[index]      = type;
421 
422     if (t) {
423         setTile (t, m_tileSize, i, j);
424     }
425 }
426 
makeSprite(const char type,int i,int j)427 int KGrScene::makeSprite (const char type, int i, int j)
428 {
429     int spriteId;
430     KGrSprite * sprite = m_renderer->getSpriteItem (type, TickTime);
431 
432     if (m_sprites.count(nullptr) > 0 &&
433         ((spriteId = m_sprites.lastIndexOf (nullptr)) >= 0)) {
434         // Re-use a slot previously occupied by a transient member of the list.
435         m_sprites[spriteId] = sprite;
436     }
437     else {
438         // Otherwise, add to the end of the list.
439         spriteId = m_sprites.count();
440         m_sprites.append (sprite);
441     }
442 
443     int frame1 = animationStartFrames [FALL_L];
444 
445     switch (type) {
446     case HERO:
447         m_heroId = spriteId;
448         sprite->setZ (1);
449         break;
450     case ENEMY:
451         sprite->setZ (2);
452         break;
453     case BRICK:
454         frame1 = animationStartFrames [OPEN_BRICK];
455 
456         // The hero and enemies must be painted in front of dug bricks.
457         sprite->setZ (0);
458 
459         // Erase the brick-image so that animations are visible in all themes.
460         paintCell (i, j, FREE);
461         break;
462     default:
463         break;
464     }
465 
466     sprite->setFrame (frame1);
467     sprite->setCoordinateSystem (m_topLeftX, m_topLeftY, m_tileSize);
468     addItem (sprite);		// The sprite can be correctly rendered now.
469     sprite->move (i, j, frame1);
470     return spriteId;
471 }
472 
animate(bool missed)473 void KGrScene::animate (bool missed)
474 {
475     for (KGrSprite * sprite : std::as_const(m_sprites)) {
476         if (sprite != nullptr) {
477             sprite->animate (missed);
478         }
479     }
480 }
481 
startAnimation(const int id,const bool repeating,const int i,const int j,const int time,const Direction dirn,const AnimationType type)482 void KGrScene::startAnimation (const int id, const bool repeating,
483                                 const int i, const int j, const int time,
484                                 const Direction dirn, const AnimationType type)
485 {
486     // TODO - Put most of this in helper code, based on theme parameters.
487     int dx              = 0;
488     int dy              = 0;
489     int frame           = animationStartFrames [type];
490     int nFrames         = 8;
491     int nFrameChanges   = 4;
492 
493     switch (dirn) {
494     case RIGHT:
495         dx    = +1;
496         break;
497     case LEFT:
498         dx    = -1;
499         break;
500     case DOWN:
501         dy = +1;
502         if ((type == FALL_R) || (type == FALL_L)) {
503             nFrames = 1;
504         }
505         else {
506             nFrames = 2;
507         }
508         break;
509     case UP:
510         dy = -1;
511         nFrames = 2;
512         break;
513     case STAND:
514         switch (type) {
515         case OPEN_BRICK:
516             nFrames = 5;
517             break;
518         case CLOSE_BRICK:
519             nFrames = 4;
520             break;
521         default:
522             // Show a standing hero or enemy, using the previous StartFrame.
523             nFrames = 0;
524             break;
525         }
526         break;
527     default:
528         break;
529     }
530 
531     // TODO - Generalise nFrameChanges = 4, also the tick time = 20 new sprite.
532     m_sprites.at(id)->setAnimation (repeating, i, j, frame, nFrames, dx, dy,
533                                     time, nFrameChanges);
534 }
535 
gotGold(const int spriteId,const int i,const int j,const bool spriteHasGold,const bool lost)536 void KGrScene::gotGold (const int spriteId, const int i, const int j,
537                         const bool spriteHasGold, const bool lost)
538 {
539     // Hide collected gold or show dropped gold, but not if the gold was lost.
540     if (! lost) {
541         paintCell (i, j, (spriteHasGold) ? FREE : NUGGET);
542     }
543 
544     // If the rules allow, show whether or not an enemy sprite is carrying gold.
545     if (enemiesShowGold && (m_sprites.at(spriteId)->spriteType() == ENEMY)) {
546         m_sprites.at(spriteId)->setSpriteKey (spriteHasGold ? QStringLiteral("gold_enemy")
547                                                             : QStringLiteral("enemy"));
548     }
549 }
550 
showHiddenLadders(const QList<int> & ladders,const int width)551 void KGrScene::showHiddenLadders (const QList<int> & ladders, const int width)
552 {
553     for (const int &offset : ladders) {
554         int i = offset % width;
555         int j = offset / width;
556         paintCell (i, j, LADDER);
557     }
558 }
559 
deleteSprite(const int spriteId)560 void KGrScene::deleteSprite (const int spriteId)
561 {
562     QPointF loc     = m_sprites.at(spriteId)->currentLoc();
563     bool   brick    = (m_sprites.at(spriteId)->spriteType() == BRICK);
564 
565     delete m_sprites.at(spriteId);
566     m_sprites [spriteId] = nullptr;
567 
568     if (brick) {
569         // Dug-brick sprite erased: restore the tile that was at that location.
570         paintCell (loc.x(), loc.y(), BRICK);
571     }
572 }
573 
deleteAllSprites()574 void KGrScene::deleteAllSprites()
575 {
576     qDeleteAll(m_sprites);
577     m_sprites.clear();
578 }
579 
preRenderSprites()580 void KGrScene::preRenderSprites()
581 {
582     char type[2] = {HERO, ENEMY};
583     for (int t = 0; t < 2; t++) {
584         KGrSprite * sprite = m_renderer->getSpriteItem (type[t], TickTime);
585         sprite->setFrame (1);
586         sprite->setRenderSize (QSize (m_tileSize, m_tileSize));
587         int count = sprite->frameCount();
588 
589         // Pre-render all frames of the hero and an enemy, to avoid hiccups in
590         // animation during the first few seconds of KGoldrunner execution.
591         for (int n = 1; n <= count; n++) {
592             sprite->setFrame (n);
593         }
594         delete sprite;
595     }
596 }
597 
setMousePos(const int i,const int j)598 void KGrScene::setMousePos (const int i, const int j)
599 {
600     m_mouse->setPos (m_view->mapToGlobal (QPoint (
601                      m_topLeftX + (i + 1) * m_tileSize + m_tileSize/2,
602                      m_topLeftY + (j + 1) * m_tileSize + m_tileSize/2)));
603 }
604 
getMousePos(int & i,int & j)605 void KGrScene::getMousePos (int & i, int & j)
606 {
607     QPoint pos = m_view->mapFromGlobal (m_mouse->pos());
608     i = pos.x();
609     j = pos.y();
610     if (! m_view->isActiveWindow()) {
611         i = -2;
612 	j = -2;
613 	return;
614     }
615     // IDW TODO - Check for being outside scene. Use saved m_width and m_height.
616 
617     i = (i - m_topLeftX)/m_tileSize - 1;
618     j = (j - m_topLeftY)/m_tileSize - 1;
619 
620     // Make sure i and j are within the KGoldrunner playing area.
621     i = (i < 1) ? 1 : ((i > FIELDWIDTH)  ? FIELDWIDTH  : i);
622     j = (j < 1) ? 1 : ((j > FIELDHEIGHT) ? FIELDHEIGHT : j);
623 }
624 
setTextFont(QGraphicsSimpleTextItem * t,double fontFraction)625 void KGrScene::setTextFont (QGraphicsSimpleTextItem * t, double fontFraction)
626 {
627     QFont f;
628     f.setPixelSize ((int) (m_tileSize * fontFraction + 0.5));
629     f.setWeight (QFont::Bold);
630     f.setStretch (QFont::Expanded);
631     t->setBrush (m_renderer->textColor());
632     t->setFont (f);
633 }
634 
635 
636