File indexing completed on 2024-04-28 04:02:14

0001 /*
0002     SPDX-FileCopyrightText: 2012 Ian Wadham <iandw.au@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007     // TODO - Border tiles, display tiles.
0008     // TODO - Add attributes to theme: HasBorderTiles, HasDisplayTiles.
0009 
0010 // KDEGames
0011 #include <KGameRenderedItem>
0012 #include <KGameThemeProvider>
0013 #include <KGameThemeSelector>
0014 // KF
0015 #include <KLocalizedString>
0016 
0017 
0018 #include "kgoldrunner_debug.h"
0019 #include "kgrglobals.h"
0020 #include "kgrthemetypes.h"
0021 #include "kgrrenderer.h"
0022 #include "kgrscene.h"
0023 
0024 #include <cmath>
0025 
0026 KGrRenderer::KGrRenderer (KGrScene * scene)
0027     :
0028     QObject (scene),
0029     m_scene (scene)
0030 {
0031     // Set up two theme providers: for the Set and the Actors.
0032     m_setProvider     = new KGameThemeProvider("Theme", this);  // Save config.
0033     m_actorsProvider  = new KGameThemeProvider("",      this);  // Do not save.
0034 
0035     // Find SVG files for the Set, i.e. tiles and backgrounds.
0036     const QMetaObject * setThemeClass = & KGrSetTheme::staticMetaObject;
0037     m_setProvider->discoverThemes (QStringLiteral ("themes"),
0038                                    QStringLiteral ("egypt"), setThemeClass);
0039 
0040     // Find SVG files for the Actors, i.e. hero and enemies.
0041     const QMetaObject * actorsThemeClass = & KGrActorsTheme::staticMetaObject;
0042     m_actorsProvider->discoverThemes (QStringLiteral ("themes"),
0043                                    QStringLiteral ("egypt"), actorsThemeClass);
0044 
0045     // Set up a dialog for selecting themes.
0046     m_themeSelector  = new KGameThemeSelector (m_setProvider,
0047                                             KGameThemeSelector::DefaultBehavior,
0048                                             nullptr);   // No parent: modeless dialog.
0049 
0050     // Set up the renderer for the Set, i.e. tiles and backgrounds.
0051     m_setRenderer    = new KGameRenderer (m_setProvider);
0052     m_setRenderer->setParent (this);
0053     m_setRenderer->setFrameSuffix (QStringLiteral("_%1"));
0054     m_setRenderer->setFrameBaseIndex (1);
0055 
0056     // Set up the renderer for the Actors, i.e. hero and enemies.
0057     m_actorsRenderer = new KGameRenderer (m_actorsProvider);
0058     m_actorsRenderer->setParent (this);
0059     m_actorsRenderer->setFrameSuffix (QStringLiteral("_%1"));
0060     m_actorsRenderer->setFrameBaseIndex (1);
0061 
0062     // Match the Actors SVG theme to the Set theme, whenever the theme changes.
0063     connect(m_setProvider, &KGameThemeProvider::currentThemeChanged, this, &KGrRenderer::currentThemeChanged);
0064 
0065     // Match the starting SVG theme for the Actors to the one for the Set.
0066     matchThemes (m_setProvider->currentTheme());
0067 }
0068 
0069 KGrRenderer::~KGrRenderer()
0070 {
0071     delete m_themeSelector;
0072 }
0073 
0074 void KGrRenderer::matchThemes (const KGameTheme * currentSetTheme)
0075 {
0076     // Start of game or change of theme: initialise the counts of pixmap keys.
0077     initPixmapKeys();
0078 
0079     const auto themes = m_actorsProvider->themes();
0080     for (const KGameTheme * actorsTheme : themes) {
0081     if (actorsTheme->customData(QStringLiteral("Set")) ==
0082             currentSetTheme->customData(QStringLiteral("Set"))) {
0083         m_actorsProvider->setCurrentTheme (actorsTheme);
0084         break;
0085     }
0086     }
0087 }
0088 
0089 void KGrRenderer::currentThemeChanged (const KGameTheme* currentSetTheme)
0090 {
0091     //qCDebug(KGOLDRUNNER_LOG) << "KGrRenderer::currentThemeChanged()" << currentSetTheme->name();
0092 
0093     matchThemes (currentSetTheme);
0094     m_scene->changeTheme();
0095 }
0096 
0097 void KGrRenderer::selectTheme()
0098 {
0099     // Show the theme-selection dialog.
0100     m_themeSelector->showAsDialog (i18n("Theme Selector"));
0101 }
0102 
0103 KGrRenderer::PixmapSpec KGrRenderer::keyTable [] = {
0104     {ENEMY,    Actors, "enemy_1",       "",   -1, -2},  // For editor only.
0105     {HERO,     Actors, "hero_1",        "",   -1, -2},  // For editor only.
0106     {CONCRETE, Set,    "concrete",      "-%1", 0, -2},
0107     {BRICK,    Set,    "brick",         "-%1", 0, -2},
0108     {FBRICK,   Set,    "false_brick",   "",   -1, -2},  // For editor only.
0109     {HLADDER,  Set,    "hidden_ladder", "",   -1, -2},  // For editor only.
0110     {LADDER,   Set,    "ladder",        "-%1", 0, -2},
0111     {NUGGET,   Set,    "gold",          "-%1", 0, -2},
0112     {BAR,      Set,    "bar",           "-%1", 0, -2},
0113     {BACKDROP, Set,    "background",    "%1",  0, -2},
0114     {FREE,     Set,    "empty",         "",   -1, -2}   // Must be last entry.
0115 };
0116 
0117 void KGrRenderer::initPixmapKeys()
0118 {
0119     // Set all pixmaps in keyTable[] as "not counted yet" (frameCount -2).
0120     int index = 0;
0121     do {
0122     keyTable[index].frameCount = -2;
0123     index++;
0124     } while (keyTable[index].picType != FREE);
0125 }
0126 
0127 KGameRenderedItem * KGrRenderer::getTileItem
0128                     (const char picType, KGameRenderedItem * currentTile)
0129 {
0130     if (currentTile) {
0131     // Remove the tile that was here before.
0132         m_scene->removeItem (currentTile);
0133         delete currentTile;
0134     }
0135 
0136     int index;
0137     if ((picType == FREE) || ((index = findKeyTableIndex (picType)) < 0)) {
0138         return nullptr; // Empty place or missing type, so no KGameRenderedItem.
0139     }
0140 
0141     // Get the pixmap key and use one of the two renderers to create the tile.
0142     QString key = getPixmapKey (index);
0143     KGameRenderedItem * tile =
0144                 new KGameRenderedItem ((keyTable[index].picSource == Set) ?
0145                                        m_setRenderer : m_actorsRenderer, key);
0146     tile->setAcceptedMouseButtons (Qt::NoButton);
0147     m_scene->addItem (tile);
0148     return tile;
0149 }
0150 
0151 KGrSprite * KGrRenderer::getSpriteItem (const char picType, const int tickTime)
0152 {
0153     int index = findKeyTableIndex (picType);
0154     if (index < 0) {
0155         return nullptr; // Missing type, so no KGrSprite item.
0156     }
0157     QString key = (picType == HERO) ? QStringLiteral("hero") :
0158                   ((picType == ENEMY) ? QStringLiteral("enemy") : QStringLiteral("brick"));
0159     KGrSprite * sprite = new KGrSprite ((keyTable[index].picSource == Set) ?
0160                                         m_setRenderer : m_actorsRenderer,
0161                                         key, picType, tickTime);
0162     sprite->setAcceptedMouseButtons (Qt::NoButton);
0163     // We cannot add the sprite to the scene yet: it needs a frame and size.
0164     return sprite;
0165 }
0166 
0167 KGameRenderedItem * KGrRenderer::getBackground
0168                     (const int level, KGameRenderedItem * currentBackground)
0169 {
0170     if (currentBackground) {
0171         m_scene->removeItem (currentBackground);
0172         delete currentBackground;
0173     }
0174 
0175     QString key = getBackgroundKey (level);
0176     KGameRenderedItem * background = new KGameRenderedItem (m_setRenderer, key);
0177     background->setAcceptedMouseButtons (Qt::NoButton);
0178     m_scene->addItem (background);
0179 
0180     return background;
0181 }
0182 
0183 KGameRenderedItem * KGrRenderer::getBorderItem
0184                     (const QString &spriteKey, KGameRenderedItem * currentItem)
0185 {
0186     if (currentItem) {
0187         m_scene->removeItem (currentItem);
0188         delete currentItem;
0189     }
0190 
0191     if (!hasBorder()) {
0192         return nullptr;
0193     }
0194 
0195     KGameRenderedItem * item = new KGameRenderedItem (m_setRenderer, spriteKey);
0196     item->setAcceptedMouseButtons (Qt::NoButton);
0197     m_scene->addItem (item);
0198     return item;
0199 }
0200 
0201 bool KGrRenderer::hasBorder() const
0202 {
0203     QString s = m_setRenderer->theme()->customData(QStringLiteral("DrawCanvasBorder"), QStringLiteral("0"));
0204 
0205     if (s == QLatin1Char('1'))
0206         return true;
0207     else
0208         return false;
0209 }
0210 
0211 QColor KGrRenderer::borderColor() const
0212 {
0213     QString s = m_setRenderer->theme()->customData(QStringLiteral("BorderColor"), QStringLiteral("#000000"));
0214     return QColor (s);
0215 }
0216 
0217 QColor KGrRenderer::textColor() const
0218 {
0219     QString s = m_setRenderer->theme()->customData(QStringLiteral("TextColor"), QStringLiteral("#FFFFFF"));
0220     return QColor (s);
0221 }
0222 
0223 QPixmap KGrRenderer::getPixmap (const char picType)
0224 {
0225     // Get the pixmap key and use one of the two renderers to create the tile.
0226     int index   = findKeyTableIndex (picType);
0227     QString key = getPixmapKey      (index);
0228 
0229     if (keyTable[index].picSource == Set)
0230         return m_setRenderer->spritePixmap (key, m_scene->tileSize ());
0231     else
0232         return m_actorsRenderer->spritePixmap (key, m_scene->tileSize ());
0233 }
0234 
0235 QString KGrRenderer::getPixmapKey (const int index)
0236 {
0237     QString pixmapKey;
0238     // int index = findKeyTableIndex (picType);
0239     int frameCount = (index < 0) ? -1 : keyTable[index].frameCount;
0240     if (frameCount > -1) {
0241     pixmapKey = QLatin1String(keyTable[index].picKey);  // No suffix.
0242     if (frameCount > 0) {
0243         // Pick a random frame number and add it as a suffix.
0244         // Note: We are not worried about having a good seed for this.
0245         pixmapKey = pixmapKey + QLatin1String(keyTable[index].frameSuffix);
0246         pixmapKey = pixmapKey.arg (keyTable[index].frameBaseIndex +
0247                        (rand() % frameCount));
0248     }
0249     }
0250     return pixmapKey;
0251 }
0252 
0253 QString KGrRenderer::getBackgroundKey (const int level)
0254 {
0255     QString pixmapKey;
0256     int index = findKeyTableIndex (BACKDROP);
0257     int frameCount = (index < 0) ? -1 : keyTable[index].frameCount;
0258     if (frameCount > -1) {
0259     pixmapKey = QLatin1String(keyTable[index].picKey);
0260     if (frameCount > 0) {
0261         // Cycle through available backgrounds as the game-level increases.
0262         pixmapKey = pixmapKey + QLatin1String(keyTable[index].frameSuffix);
0263         pixmapKey = pixmapKey.arg (level % frameCount);
0264     }
0265     }
0266 
0267     //qCDebug(KGOLDRUNNER_LOG) << "BACKGROUND pixmap key" << pixmapKey;
0268     return pixmapKey;
0269 }
0270 
0271 int KGrRenderer::findKeyTableIndex (const char picType)
0272 {
0273     int index = 0;
0274     while (true) {      // Find ANY picType, including FREE.
0275     if (keyTable[index].picType == picType) {
0276         if (keyTable[index].frameCount == -2) {
0277         keyTable[index].frameCount = countFrames (index);
0278         }
0279         break;
0280     }
0281     else if (keyTable[index].picType == FREE) {
0282         index = -1;     // Not found.
0283         break;
0284     }
0285     index++;
0286     }
0287     return index;
0288 }
0289 
0290 int KGrRenderer::countFrames (const int index)
0291 {
0292     int count = -1;
0293     int frame = keyTable[index].frameBaseIndex;
0294     KGameRenderer * r = (keyTable[index].picSource == Set) ? m_setRenderer :
0295                                                              m_actorsRenderer;
0296     if (r->spriteExists (QLatin1String(keyTable[index].picKey))) {
0297         count++;
0298     }
0299 
0300     if ((count == 0) && (QLatin1String(keyTable[index].picKey) != QLatin1String("brick"))) {
0301     return count;
0302     }
0303 
0304     if (frame < 0) {
0305     return count;       // This element cannot have more than one frame.
0306     }
0307 
0308     count = 0;
0309     QString pixmapKey = QLatin1String(keyTable[index].picKey) +
0310                         QLatin1String(keyTable[index].frameSuffix);
0311     while (r->spriteExists (pixmapKey.arg (frame))) {
0312     count++;
0313     frame++;
0314     }
0315 
0316     return count;
0317 }
0318 
0319 #include "moc_kgrrenderer.cpp"