kgrrenderer.cpp 11 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/****************************************************************************
 *    Copyright 2012  Ian Wadham <iandw.au@gmail.com>                       *
 *                                                                          *
 *    This program is free software; you can redistribute it and/or         *
 *    modify it under the terms of the GNU General Public License as        *
 *    published by the Free Software Foundation; either version 2 of        *
 *    the License, or (at your option) any later version.                   *
 *                                                                          *
 *    This program is distributed in the hope that it will be useful,       *
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *    GNU General Public License for more details.                          *
 *                                                                          *
 *    You should have received a copy of the GNU General Public License     *
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ****************************************************************************/

18 19 20
    // TODO - Border tiles, display tiles.
    // TODO - Add attributes to theme: HasBorderTiles, HasDisplayTiles.

21
#include <KGameRenderer>
22
#include <KGameRenderedItem>
23 24 25 26
#include <KgThemeProvider>
#include <KgThemeSelector>
#include <KLocalizedString>

Laurent Montel's avatar
Laurent Montel committed
27
#include "kgoldrunner_debug.h"
28 29
#include <QString>

30
#include "kgrglobals.h"
31 32
#include "kgrthemetypes.h"
#include "kgrrenderer.h"
33
#include "kgrscene.h"
34

35 36
#include <cmath>

37
KGrRenderer::KGrRenderer (KGrScene * scene)
38
    :
39 40
    QObject (scene),
    m_scene (scene)
41
{
42 43 44 45 46
    // Set up two theme providers: for the Set and the Actors.
    m_setProvider     = new KgThemeProvider("Theme", this);	// Save config.
    m_actorsProvider  = new KgThemeProvider("",      this);	// Do not save.

    // Find SVG files for the Set, i.e. tiles and backgrounds.
47 48 49 50
    const QMetaObject * setThemeClass = & KGrSetTheme::staticMetaObject;
    m_setProvider->discoverThemes ("appdata", QLatin1String ("themes"),
                                   QLatin1String ("egypt"), setThemeClass);

51 52 53 54 55 56 57 58 59
    // Find SVG files for the Actors, i.e. hero and enemies.
    const QMetaObject * actorsThemeClass = & KGrActorsTheme::staticMetaObject;
    m_actorsProvider->discoverThemes ("appdata", QLatin1String ("themes"),
                                   QLatin1String ("egypt"), actorsThemeClass);

    // Set up a dialog for selecting themes.
    m_themeSelector  = new KgThemeSelector (m_setProvider,
                                            KgThemeSelector::DefaultBehavior,
                                            0);	// No parent: modeless dialog.
60

61 62
    // Set up the renderer for the Set, i.e. tiles and backgrounds.
    m_setRenderer    = new KGameRenderer (m_setProvider);
63
    m_setRenderer->setParent (this);
64 65
    m_setRenderer->setFrameSuffix ("_%1");
    m_setRenderer->setFrameBaseIndex (1);
66

67 68 69 70 71 72 73
    // Set up the renderer for the Actors, i.e. hero and enemies.
    m_actorsRenderer = new KGameRenderer (m_actorsProvider);
    m_actorsRenderer->setParent (this);
    m_actorsRenderer->setFrameSuffix ("_%1");
    m_actorsRenderer->setFrameBaseIndex (1);

    // Match the Actors SVG theme to the Set theme, whenever the theme changes.
Laurent Montel's avatar
Laurent Montel committed
74
    connect(m_setProvider, &KgThemeProvider::currentThemeChanged, this, &KGrRenderer::currentThemeChanged);
75 76

    // Match the starting SVG theme for the Actors to the one for the Set.
77
    matchThemes (m_setProvider->currentTheme());
78 79 80 81 82 83 84
}

KGrRenderer::~KGrRenderer()
{
    delete m_themeSelector;
}

85
void KGrRenderer::matchThemes (const KgTheme * currentSetTheme)
86 87 88 89 90 91 92 93
{
    // Start of game or change of theme: initialise the counts of pixmap keys.
    initPixmapKeys();

    foreach (const KgTheme * actorsTheme, m_actorsProvider->themes()) {
	if (actorsTheme->customData("Set") ==
            currentSetTheme->customData("Set")) {
	    m_actorsProvider->setCurrentTheme (actorsTheme);
94
	    break;
95 96
	}
    }
97
}
98

99 100
void KGrRenderer::currentThemeChanged (const KgTheme* currentSetTheme)
{
Laurent Montel's avatar
Laurent Montel committed
101
    qCDebug(KGOLDRUNNER_LOG) << "KGrRenderer::currentThemeChanged()" << currentSetTheme->name();
102 103 104

    matchThemes (currentSetTheme);
    m_scene->changeTheme();
105 106
}

107 108
void KGrRenderer::selectTheme()
{
109
    // Show the theme-selection dialog.
110 111 112 113 114 115 116 117 118 119 120 121 122
    m_themeSelector->showAsDialog (i18n("Theme Selector"));
}

KGrRenderer::PixmapSpec KGrRenderer::keyTable [] = {
    {ENEMY,    Actors, "enemy_1",       "",   -1, -2},	// For editor only.
    {HERO,     Actors, "hero_1",        "",   -1, -2},	// For editor only.
    {CONCRETE, Set,    "concrete",      "-%1", 0, -2},
    {BRICK,    Set,    "brick",         "-%1", 0, -2},
    {FBRICK,   Set,    "false_brick",   "",   -1, -2},	// For editor only.
    {HLADDER,  Set,    "hidden_ladder", "",   -1, -2},	// For editor only.
    {LADDER,   Set,    "ladder",        "-%1", 0, -2},
    {NUGGET,   Set,    "gold",          "-%1", 0, -2},
    {BAR,      Set,    "bar",           "-%1", 0, -2},
123
    {BACKDROP, Set,    "background",    "%1",  0, -2},
124
    {FREE,     Set,    "empty",         "",   -1, -2}	// Must be last entry.
125 126 127 128
};

void KGrRenderer::initPixmapKeys()
{
129
    // Set all pixmaps in keyTable[] as "not counted yet" (frameCount -2).
130
    int index = 0;
131
    do {
132 133
	keyTable[index].frameCount = -2;
	index++;
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    } while (keyTable[index].picType != FREE);
}

KGameRenderedItem * KGrRenderer::getTileItem
                    (const char picType, KGameRenderedItem * currentTile)
{
    if (currentTile) {
	// Remove the tile that was here before.
        m_scene->removeItem (currentTile);
        delete currentTile;
    }

    int index;
    if ((picType == FREE) || ((index = findKeyTableIndex (picType)) < 0)) {
        return 0;	// Empty place or missing type, so no KGameRenderedItem.
149
    }
150 151 152 153 154 155

    // Get the pixmap key and use one of the two renderers to create the tile.
    QString key = getPixmapKey (picType, index);
    KGameRenderedItem * tile =
                new KGameRenderedItem ((keyTable[index].picSource == Set) ?
                                       m_setRenderer : m_actorsRenderer, key);
156
    tile->setAcceptedMouseButtons (0);
157 158
    m_scene->addItem (tile);
    return tile;
159 160
}

161 162 163 164 165 166 167 168 169 170 171
KGrSprite * KGrRenderer::getSpriteItem (const char picType, const int tickTime)
{
    int index = findKeyTableIndex (picType);
    if (index < 0) {
        return 0;	// Missing type, so no KGrSprite item.
    }
    QString key = (picType == HERO) ? "hero" :
                  ((picType == ENEMY) ? "enemy" : "brick");
    KGrSprite * sprite = new KGrSprite ((keyTable[index].picSource == Set) ?
                                        m_setRenderer : m_actorsRenderer,
                                        key, picType, tickTime);
172
    sprite->setAcceptedMouseButtons (0);
173
    // We cannot add the sprite to the scene yet: it needs a frame and size.
174 175 176
    return sprite;
}

177 178 179 180 181 182 183 184 185 186
KGameRenderedItem * KGrRenderer::getBackground
                    (const int level, KGameRenderedItem * currentBackground)
{
    if (currentBackground) {
        m_scene->removeItem (currentBackground);
        delete currentBackground;
    }

    QString key = getBackgroundKey (level);
    KGameRenderedItem * background = new KGameRenderedItem (m_setRenderer, key);
187
    background->setAcceptedMouseButtons (0);
188
    m_scene->addItem (background);
189 190 191 192

    return background;
}

193 194
KGameRenderedItem * KGrRenderer::getBorderItem
                    (QString spriteKey, KGameRenderedItem * currentItem)
195
{
196 197 198 199
    if (currentItem) {
        m_scene->removeItem (currentItem);
        delete currentItem;
    }
200

201 202 203
    if (!hasBorder()) {
        return 0;
    }
204

205
    KGameRenderedItem * item = new KGameRenderedItem (m_setRenderer, spriteKey);
206
    item->setAcceptedMouseButtons (0);
207 208 209
    m_scene->addItem (item);
    return item;
}
210

211 212 213
bool KGrRenderer::hasBorder() const
{
    QString s = m_setRenderer->theme()->customData("DrawCanvasBorder", "0");
214

215
    if (s == QString ("1"))
216 217 218 219
        return true;
    else
        return false;
}
220

221 222 223 224 225
QColor KGrRenderer::borderColor() const
{
    QString s = m_setRenderer->theme()->customData("BorderColor", "#000000");
    return QColor (s);
}
226

227 228 229 230
QColor KGrRenderer::textColor() const
{
    QString s = m_setRenderer->theme()->customData("TextColor", "#FFFFFF");
    return QColor (s);
231 232
}

233 234 235 236 237 238 239 240 241 242 243 244
QPixmap KGrRenderer::getPixmap (const char picType)
{
    // Get the pixmap key and use one of the two renderers to create the tile.
    int index   = findKeyTableIndex (picType);
    QString key = getPixmapKey      (picType, index);

    if (keyTable[index].picSource == Set)
        return m_setRenderer->spritePixmap (key, m_scene->tileSize ());
    else
        return m_actorsRenderer->spritePixmap (key, m_scene->tileSize ());
}

245
QString KGrRenderer::getPixmapKey (const char picType, const int index)
246
{
247
    QString pixmapKey = "";
248
    // int index = findKeyTableIndex (picType);
249 250 251 252 253 254 255 256 257 258 259 260 261
    int frameCount = (index < 0) ? -1 : keyTable[index].frameCount;
    if (frameCount > -1) {
	pixmapKey = keyTable[index].picKey;	// No suffix.
	if (frameCount > 0) {
	    // Pick a random frame number and add it as a suffix.
	    // Note: We are not worried about having a good seed for this.
	    pixmapKey = pixmapKey + QString(keyTable[index].frameSuffix);
	    pixmapKey = pixmapKey.arg (keyTable[index].frameBaseIndex +
				       (rand() % frameCount));
	}
    }
    return pixmapKey;
}
262

263 264
QString KGrRenderer::getBackgroundKey (const int level)
{
265
    QString pixmapKey = "";
266 267 268 269 270 271 272 273 274 275
    int index = findKeyTableIndex (BACKDROP);
    int frameCount = (index < 0) ? -1 : keyTable[index].frameCount;
    if (frameCount > -1) {
	pixmapKey = keyTable[index].picKey;
	if (frameCount > 0) {
	    // Cycle through available backgrounds as the game-level increases.
	    pixmapKey = pixmapKey + QString(keyTable[index].frameSuffix);
	    pixmapKey = pixmapKey.arg (level % frameCount);
	}
    }
276

Laurent Montel's avatar
Laurent Montel committed
277
    qCDebug(KGOLDRUNNER_LOG) << "BACKGROUND pixmap key" << pixmapKey;
278 279 280 281 282
    return pixmapKey;
}

int KGrRenderer::findKeyTableIndex (const char picType)
{
283
    int index = 0;
284 285
    while (true) {		// Find ANY picType, including FREE.
	if (keyTable[index].picType == picType) {
286
	    if (keyTable[index].frameCount == -2) {
287
		keyTable[index].frameCount = countFrames (index);
288 289 290
	    }
	    break;
	}
291 292 293 294
	else if (keyTable[index].picType == FREE) {
	    index = -1;		// Not found.
	    break;
	}
295 296
	index++;
    }
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    return index;
}

int KGrRenderer::countFrames (const int index)
{
    int count = -1;
    int frame = keyTable[index].frameBaseIndex;
    KGameRenderer * r = (keyTable[index].picSource == Set) ? m_setRenderer :
                                                             m_actorsRenderer;
    if (r->spriteExists (keyTable[index].picKey)) {
        count++;
    }

    if ((count == 0) && (QString(keyTable[index].picKey) != QString("brick"))) {
	return count;
    }

    if (frame < 0) {
	return count;		// This element cannot have more than one frame.
    }

    count = 0;
    QString pixmapKey = QString(keyTable[index].picKey) +
                        QString(keyTable[index].frameSuffix);
    while (r->spriteExists (pixmapKey.arg (frame))) {
	count++;
	frame++;
    }

    return count;
327 328
}

Laurent Montel's avatar
port++  
Laurent Montel committed
329