board.cpp 7.75 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
 * Copyright (C) 2007-2008 John-Paul Stanford <jp@stanwood.org.uk>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
Frederik Schwarzer's avatar
Frederik Schwarzer committed
18
19

// own
20
21
#include "board.h"

Frederik Schwarzer's avatar
Frederik Schwarzer committed
22
23
24
25
// Qt
#include <QGraphicsView>
#include <QStandardPaths>
#include <QTimer>
Laurent Montel's avatar
Laurent Montel committed
26
#include <QRandomGenerator>
Frederik Schwarzer's avatar
Frederik Schwarzer committed
27
28
29


// Bomber
John-Paul Stanford's avatar
John-Paul Stanford committed
30
#include "bomb.h"
Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
31
32
#include "building.h"
#include "plane.h"
33
#include "settings.h"
John-Paul Stanford's avatar
John-Paul Stanford committed
34

35

36
37
38
39
40
41
42
43
44
/** The value that the plane velocity increases by */
const qreal PLANE_INC_VELOCITY = 0.0005;
/** The value of this controls the speed of the game */
const unsigned int GAME_DELAY = 15;
/** The number of tiles vertical in the playing area */
const unsigned int TILE_NUM_H = 20;
/** The number of builds to display */
const unsigned int NUMBER_BUILDINGS = 10;
/** The number of tiles horizontally in the playing area */
Laurent Montel's avatar
Laurent Montel committed
45
const unsigned int TILE_NUM_W = ((NUMBER_BUILDINGS) + 2);
46
47
48
49
50
51
52
53
/** The maximum level number before the game stops getting harder */
const unsigned int MAX_LEVEL = 11;

/** This time in milliseconds that the plane exploding animation is played for */
const unsigned int PLANE_EXPLODE_TIME = 2000;

/** This time in milliseconds that the bomb exploding animation is played for */
const unsigned int BOMB_EXPLODE_TIME = 1000;
54

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
55
56
57
58
59
BomberBoard::BomberBoard(KGameRenderer * renderer, QGraphicsView * view, QObject * parent)
    : QGraphicsScene(parent)
    , m_renderer(renderer)
    , m_bomb(nullptr)
    , m_view(view)
60
{
Laurent Montel's avatar
Laurent Montel committed
61
62
63
64
65
66
67
68
69
    m_clock = new QTimer(this);
    m_clock->setInterval(GAME_DELAY);
    connect(m_clock, &QTimer::timeout, this, &BomberBoard::tick);
    m_plane = new Plane(m_renderer, this);
    m_plane->resize(m_tileSize);
    this->addItem(m_plane);
    m_plane->show();
    resetPlane();
    clear();
70
71
72
73
}

BomberBoard::~BomberBoard()
{
Laurent Montel's avatar
Laurent Montel committed
74
75
76
77
    delete m_bomb;
    delete m_plane;
    qDeleteAll(m_buildings);
    qDeleteAll(m_explodingBombs);
78
79
80
81
}

void BomberBoard::resetPlane()
{
Frederik Schwarzer's avatar
Frederik Schwarzer committed
82
    m_plane->setState(Explodable::State::Moving);
Laurent Montel's avatar
Laurent Montel committed
83
    m_plane->resetPosition();
84
85
}

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
86
void BomberBoard::resize(QSize & size)
87
{
Laurent Montel's avatar
Laurent Montel committed
88
    setBackgroundBrush(m_renderer->spritePixmap(QStringLiteral("background"), size));
89

Laurent Montel's avatar
Laurent Montel committed
90
91
    unsigned int minTileSizeWidth = size.width() / TILE_NUM_W;
    unsigned int minTileSizeHeight = size.height() / TILE_NUM_H;
92

Laurent Montel's avatar
Laurent Montel committed
93
    m_tileSize = QSize(minTileSizeWidth, minTileSizeHeight);
94

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
95
    foreach (Building * building, m_buildings) {
Laurent Montel's avatar
Laurent Montel committed
96
97
        building->resize(m_tileSize);
    }
98

Laurent Montel's avatar
Laurent Montel committed
99
    m_plane->resize(m_tileSize);
Frederik Schwarzer's avatar
Frederik Schwarzer committed
100
    if (m_bomb != nullptr) {
Laurent Montel's avatar
Laurent Montel committed
101
102
        m_bomb->resize(m_tileSize);
    }
103

Laurent Montel's avatar
Laurent Montel committed
104
    redraw();
105

Laurent Montel's avatar
Laurent Montel committed
106
107
    size.setWidth(minTileSizeWidth * TILE_NUM_W);
    size.setHeight(minTileSizeHeight * TILE_NUM_H);
108
109
110
111
}

void BomberBoard::redraw()
{
Laurent Montel's avatar
Laurent Montel committed
112
    m_plane->resetPixmaps();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
113
    if (m_bomb != nullptr) {
Laurent Montel's avatar
Laurent Montel committed
114
115
        m_bomb->resetPixmaps();
    }
116
117
}

118
void BomberBoard::newLevel(unsigned int level)
119
{
Laurent Montel's avatar
Laurent Montel committed
120
121
122
123
124
125
126
127
128
129
130
131
    if (level > MAX_LEVEL) {
        level = MAX_LEVEL;
    }

    if (level == 1) {
        m_plane->setVelocity(Plane::DEFAULT_VELOCITY);
    } else if (level % 2 == 0) {
        m_plane->setVelocity(m_plane->velocity() + PLANE_INC_VELOCITY);
    }

    m_clock->stop();
    clear();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
132
    m_plane->setState(Explodable::State::Moving);
Laurent Montel's avatar
Laurent Montel committed
133
134
    m_buildingBlocks = 0;
    //Create the buildings
Frederik Schwarzer's avatar
Frederik Schwarzer committed
135
    for (unsigned int i = 0; i < NUMBER_BUILDINGS; ++i) {
Laurent Montel's avatar
Laurent Montel committed
136
137
138
139
140
141
142
143
        unsigned int min = level;
        if (min < 3) {
            min = 3;
        }
        unsigned int max = level + 3;
        if (max < 5) {
            max = 5;
        }
Laurent Montel's avatar
Laurent Montel committed
144
        unsigned int height = QRandomGenerator::global()->bounded(max - min) + min;
Laurent Montel's avatar
Laurent Montel committed
145
146

        m_buildingBlocks += height;
Laurent Montel's avatar
Laurent Montel committed
147
        auto building = new Building(m_renderer, this, i + 1, height);
Laurent Montel's avatar
Laurent Montel committed
148
149
150
151
152
153

        building->resize(m_tileSize);
        building->show();

        m_buildings.append(building);
    }
154
155
156
157
}

void BomberBoard::setPaused(bool val)
{
Laurent Montel's avatar
Laurent Montel committed
158
159
160
161
162
    if (val) {
        m_clock->stop();
    } else {
        m_clock->start();
    }
163
164
165
166
}

void BomberBoard::tick()
{
Laurent Montel's avatar
Laurent Montel committed
167
    checkCollisions();
168

Laurent Montel's avatar
Laurent Montel committed
169
    m_plane->advanceItem();
170

Frederik Schwarzer's avatar
Frederik Schwarzer committed
171
    if (m_bomb != nullptr) {
Laurent Montel's avatar
Laurent Montel committed
172
173
        m_bomb->advanceItem();
    }
174

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
175
    foreach (Bomb * bomb, m_explodingBombs) {
Laurent Montel's avatar
Laurent Montel committed
176
177
        bomb->advanceItem();
    }
178

Laurent Montel's avatar
Laurent Montel committed
179
180
    // Draw everything
    m_plane->update();
181

Frederik Schwarzer's avatar
Frederik Schwarzer committed
182
    if (m_bomb != nullptr) {
Laurent Montel's avatar
Laurent Montel committed
183
184
        m_bomb->update();
    }
185

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
186
    foreach (Bomb * bomb, m_explodingBombs) {
Laurent Montel's avatar
Laurent Montel committed
187
188
        bomb->update();
    }
189
190
191
192
}

void BomberBoard::dropBomb()
{
Frederik Schwarzer's avatar
Frederik Schwarzer committed
193
    if (m_bomb == nullptr && m_plane->state() == Explodable::State::Moving) {
Laurent Montel's avatar
Laurent Montel committed
194
195
196
197
198
        QPointF planePos = m_plane->position();
        m_bomb = new Bomb(m_renderer, this, planePos.x(), planePos.y() + 1, m_tileSize);
        this->addItem(m_bomb);
        m_bomb->show();
    }
199
200
201
202
}

void BomberBoard::checkCollisions()
{
Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
203
204
    foreach (Building * building, m_buildings) {
        if (m_plane->nextBoundingRect().intersects(building->boundingRect()) && m_plane->state() == Explodable::State::Moving) {
Laurent Montel's avatar
Laurent Montel committed
205
206
            // Plane crashed into the building
            building->destoryTop();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
207
            --m_buildingBlocks;
Laurent Montel's avatar
Laurent Montel committed
208
209
210
            crashed();
        }

Frederik Schwarzer's avatar
Frederik Schwarzer committed
211
        if (m_bomb != nullptr) {
Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
212
            if (m_bomb->nextBoundingRect().intersects(building->boundingRect()) && m_bomb->state() == Explodable::State::Moving) {
Laurent Montel's avatar
Laurent Montel committed
213
214
                // Bomb hit a building
                building->destoryTop();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
215
                --m_buildingBlocks;
Laurent Montel's avatar
Laurent Montel committed
216
217
                emit onBombHit();
                bombHit(m_bomb, building->position().x(), Building::BUILD_BASE_LOCATION - (building->height()));
Frederik Schwarzer's avatar
Frederik Schwarzer committed
218
                m_bomb = nullptr;
Laurent Montel's avatar
Laurent Montel committed
219
220
221
            } else if (m_bomb->position().y() >= Building::BUILD_BASE_LOCATION + 1) {
                // Bomb hit the ground
                bombHit(m_bomb, (unsigned int)m_bomb->position().x(), Building::BUILD_BASE_LOCATION);
Frederik Schwarzer's avatar
Frederik Schwarzer committed
222
                m_bomb = nullptr;
Laurent Montel's avatar
Laurent Montel committed
223
224
225
            }
        }

Frederik Schwarzer's avatar
Frederik Schwarzer committed
226
        if (m_plane->state() == Explodable::State::Moving && m_buildingBlocks == 0) {
Laurent Montel's avatar
Laurent Montel committed
227
228
229
            emit levelCleared();
        }
    }
230
}
231

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
232
void BomberBoard::bombHit(Bomb * bomb, qreal moveBombToX, qreal moveBombToY)
233
{
Laurent Montel's avatar
Laurent Montel committed
234
    bomb->setPosition(moveBombToX, moveBombToY);
Frederik Schwarzer's avatar
Frederik Schwarzer committed
235
    bomb->setState(Bomb::State::Exploding);
Laurent Montel's avatar
Laurent Montel committed
236
    m_explodingBombs.enqueue(bomb);
Laurent Montel's avatar
Laurent Montel committed
237
    QTimer::singleShot(BOMB_EXPLODE_TIME, this, &BomberBoard::bombExploded);
Frederik Schwarzer's avatar
Frederik Schwarzer committed
238
    emit playBombSound();
239
240
241
242
}

void BomberBoard::bombExploded()
{
Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
243
    Bomb * bomb = m_explodingBombs.dequeue();
Laurent Montel's avatar
Laurent Montel committed
244
245
    bomb->hide();
    delete bomb;
246
247
}

248
249
void BomberBoard::settingsChanged()
{
Laurent Montel's avatar
Laurent Montel committed
250
    setBackgroundBrush(m_renderer->spritePixmap(QStringLiteral("background"), m_view->size()));
Laurent Montel's avatar
Laurent Montel committed
251
    redraw();
252
253
}

254
255
void BomberBoard::planeExploded()
{
Frederik Schwarzer's avatar
Frederik Schwarzer committed
256
    m_plane->setState(Plane::State::Exploded);
Laurent Montel's avatar
Laurent Montel committed
257
    emit onPlaneCrash();
258
259
260
261
}

void BomberBoard::crashed()
{
Laurent Montel's avatar
Laurent Montel committed
262
263
    QPointF pos = m_plane->position();
    m_plane->setPosition(pos.x() + 1, pos.y());
Frederik Schwarzer's avatar
Frederik Schwarzer committed
264
    m_plane->setState(Plane::State::Exploding);
Laurent Montel's avatar
Laurent Montel committed
265
    QTimer::singleShot(PLANE_EXPLODE_TIME, this, &BomberBoard::planeExploded);
266
    emit playCrashSound();
267
268
269
270
}

void BomberBoard::clear()
{
Laurent Montel's avatar
Laurent Montel committed
271
272
    qDeleteAll(m_buildings);
    m_buildings.clear();
273

Laurent Montel's avatar
Laurent Montel committed
274
    delete m_bomb;
Frederik Schwarzer's avatar
Frederik Schwarzer committed
275
    m_bomb = nullptr;
276

Laurent Montel's avatar
Laurent Montel committed
277
    resetPlane();
278
279
}

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
280
QPoint BomberBoard::mapPosition(const QPointF & pos) const
281
{
Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
282
    return QPoint(static_cast<unsigned int>(m_tileSize.width() * pos.x()), static_cast<int>(m_tileSize.height() * pos.y()));
283
284
}

Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
285
QPointF BomberBoard::unmapPosition(const QPoint & pos) const
286
{
Frederik Schwarzer's avatar
Format.    
Frederik Schwarzer committed
287
    return QPointF(1.0 * pos.x() / m_tileSize.width(), 1.0 * pos.y() / m_tileSize.height());
288
}