File indexing completed on 2024-09-08 06:47:56
0001 /* 0002 SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "battlefieldview.h" 0008 0009 #include <QMouseEvent> 0010 #include <QSizePolicy> 0011 0012 #include "kbsrenderer.h" 0013 #include "sprite.h" 0014 #include "animator.h" 0015 #include "animation.h" 0016 #include "welcomescreen.h" 0017 #include "button.h" 0018 #include "delegate.h" 0019 0020 BattleFieldView::BattleFieldView(QWidget* parent, KBSRenderer* renderer, const QString& bgID, int gridSize) 0021 : QGraphicsView(parent) 0022 , m_renderer(renderer) 0023 , m_factory(renderer) 0024 , m_bgID(bgID) 0025 , m_gridSize(gridSize) 0026 , m_impact(nullptr) 0027 , m_last_hit(nullptr) 0028 , m_drawGrid(true) 0029 , m_delegate(nullptr) 0030 { 0031 m_background_lower = new KGameRenderedItem(m_renderer, bgID + QLatin1String("-layer1")); 0032 m_background_lower->setOpacity(0.98); 0033 0034 m_background = new KGameRenderedItem(m_renderer, bgID + QLatin1String("-layer2")); 0035 m_background->setOpacity(0.98); 0036 0037 m_screen = new WelcomeScreen(font()); 0038 0039 QGraphicsScene *scene = new QGraphicsScene(this); 0040 scene->addItem(m_background_lower); 0041 scene->addItem(m_background); 0042 scene->addItem(m_screen); 0043 0044 for (Sprites::iterator i = m_sprites.begin(); 0045 i != m_sprites.end(); 0046 ++i) { 0047 i.value() = nullptr; 0048 } 0049 0050 for (int i = 0; i < 11; i++) { 0051 hlines[i] = new QGraphicsLineItem; 0052 vlines[i] = new QGraphicsLineItem; 0053 0054 scene->addItem(hlines[i]); 0055 scene->addItem(vlines[i]); 0056 0057 hlines[i]->stackBefore(m_background); 0058 vlines[i]->stackBefore(m_background); 0059 } 0060 0061 setScene(scene); 0062 setMouseTracking(true); 0063 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0064 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0065 } 0066 0067 void BattleFieldView::toggleGrid(bool show) 0068 { 0069 if (m_drawGrid != show) 0070 { 0071 m_drawGrid = show; 0072 drawGrid(); 0073 } 0074 } 0075 0076 void BattleFieldView::drawGrid() 0077 { 0078 if (m_drawGrid) 0079 { 0080 int spacing = m_renderer->size().width(); 0081 int width = spacing * m_gridSize; 0082 int height = spacing * m_gridSize; 0083 0084 for (int i = 0; i < 11; i++) { 0085 hlines[i]->show(); 0086 hlines[i]->setLine(0, i * spacing, width, i * spacing); 0087 0088 vlines[i]->show(); 0089 vlines[i]->setLine(i * spacing, 0, i * spacing, height); 0090 } 0091 } 0092 else 0093 { 0094 for (int i = 0; i < 11; i++) { 0095 hlines[i]->hide(); 0096 vlines[i]->hide(); 0097 } 0098 } 0099 } 0100 0101 void BattleFieldView::refresh() 0102 { 0103 // Updates this widget. 0104 int x = pos().x(); 0105 int y = pos().x(); 0106 int w = m_renderer->size().width() * m_gridSize; 0107 int h = m_renderer->size().height() * m_gridSize; 0108 0109 setSceneRect(0, 0, w, h); 0110 // Due the rounded nature of the view, it's necessary to set its 0111 // geometry to make sure it will show the entire grid. 0112 int fs = frameWidth(); 0113 setGeometry(x, y, w + 2 * fs, h + 2 * fs); 0114 0115 // update welcome screen 0116 m_screen->setPos(0, 0); 0117 m_screen->resize(m_renderer->size() * m_gridSize); 0118 0119 // Updates the backgrounds. 0120 m_background_lower->hide(); 0121 m_background_lower->setRenderSize(m_renderer->size() * m_gridSize); 0122 m_background_lower->show(); 0123 0124 m_background->hide(); 0125 m_background->setRenderSize(m_renderer->size() * m_gridSize); 0126 m_background->show(); 0127 0128 // Updates the grid. 0129 drawGrid(); 0130 0131 // update preview 0132 if (m_preview.sprite) { 0133 m_preview.sprite->refresh(m_renderer); 0134 m_preview.sprite->setPos(m_renderer->toReal(m_preview.pos)); 0135 } 0136 0137 // update sprites 0138 for (Sprites::const_iterator i = m_sprites.constBegin(); 0139 i != m_sprites.constEnd(); 0140 ++i) { 0141 i.value()->refresh(m_renderer); 0142 i.value()->setPos(m_renderer->toReal(i.key())); 0143 } 0144 } 0145 0146 void BattleFieldView::setPreview(const QPoint & pos) 0147 { 0148 if (!m_delegate) { 0149 return; 0150 } 0151 Ship * ship = m_delegate->nextShip(); 0152 0153 if (!ship) { 0154 return; 0155 } 0156 0157 loadPreviewSprite(ship); 0158 Coord coordinate = m_renderer->toLogical(pos); 0159 0160 if (m_delegate->canAddShip(m_player, coordinate)) { 0161 m_preview.sprite->turnGreen(); 0162 } else { 0163 m_preview.sprite->turnRed(); 0164 } 0165 0166 QPointF scenePos = mapToScene(pos); 0167 m_preview.pos = m_renderer->toLogical(scenePos); 0168 m_preview.sprite->setPos(m_renderer->toReal(m_preview.pos)); 0169 } 0170 0171 void BattleFieldView::loadPreviewSprite(Ship * ship) 0172 { 0173 if (m_preview.ship) { 0174 return; 0175 } 0176 0177 m_preview.ship = ship; 0178 m_preview.sprite = m_factory.createShip(ship); 0179 0180 m_preview.sprite->setOpacity(PREVIEW_OPACITY); 0181 scene()->addItem(m_preview.sprite); 0182 } 0183 void BattleFieldView::cancelPreview() 0184 { 0185 delete m_preview.sprite; 0186 m_preview.sprite = nullptr; 0187 m_preview.ship = nullptr; 0188 } 0189 0190 void BattleFieldView::addSprite(const Coord& c, Sprite* sprite) 0191 { 0192 m_sprites.insert(c, sprite); 0193 sprite->setPos(m_renderer->toReal(c)); 0194 scene()->addItem(sprite); 0195 } 0196 0197 void BattleFieldView::add(Ship* ship) 0198 { 0199 Sprite* sprite = m_factory.createShip(ship); 0200 sprite->setZValue(BACKGROUND); 0201 addSprite(ship->position(), sprite); 0202 0203 // fading preview in 0204 if (ship->alive()) { 0205 Animation* a = new FadeAnimation(sprite, PREVIEW_OPACITY, 1, 1000); 0206 Animator::instance()->add(a); 0207 } 0208 0209 if (ship == m_preview.ship) { 0210 cancelPreview(); 0211 } 0212 else if (!ship->alive()) { 0213 sprite->setZValue(BACKGROUND); 0214 Animation* a = new FadeAnimation(sprite, 0, 0.5, 1000); 0215 Animator::instance()->add(a); 0216 } 0217 } 0218 0219 void BattleFieldView::sink(Ship* ship) 0220 { 0221 m_last_hit = nullptr; 0222 0223 Coord p = ship->position(); 0224 for (unsigned int i = 0; 0225 i < ship->size(); 0226 i++, p += ship->increment()) { 0227 const auto spritesOfPos = m_sprites.values(p); 0228 for (Sprite* s : spritesOfPos) { 0229 if (s->spriteKey().startsWith(QLatin1String("ship"))) { 0230 s->setZValue(BACKGROUND); 0231 s->setOpacity(0.5); 0232 } 0233 else if (s->spriteKey().startsWith(QLatin1String("hit"))) { 0234 s->setSpriteKey(QStringLiteral("hit-end")); 0235 } 0236 } 0237 } 0238 } 0239 0240 void BattleFieldView::hit(const Coord& c) 0241 { 0242 removeImpact(); 0243 m_last_hit = m_factory.createHit(); 0244 m_last_hit->setZValue(FOREGROUND); 0245 m_last_hit->setOpacity(1.0); 0246 addSprite(c, m_last_hit); 0247 } 0248 0249 void BattleFieldView::miss(const Coord& c) 0250 { 0251 removeImpact(); 0252 m_impact = m_factory.createImpact(); 0253 m_impact->setZValue(FOREGROUND); 0254 m_impact->setOpacity(1.0); 0255 addSprite(c, m_impact); 0256 } 0257 0258 void BattleFieldView::removeImpact() { 0259 if (m_impact) { 0260 m_impact->setSpriteKey(QStringLiteral("water")); 0261 m_impact->refresh(m_renderer); 0262 m_impact = nullptr; 0263 } 0264 if (m_last_hit) { 0265 m_last_hit->setSpriteKey(QStringLiteral("hit-after")); 0266 m_last_hit->refresh(m_renderer); 0267 m_last_hit = nullptr; 0268 } 0269 } 0270 0271 void BattleFieldView::clear() 0272 { 0273 // fixes a crash when the ships can not be placed. 0274 Animator::instance()->stop(); 0275 delete m_preview.sprite; 0276 m_preview.sprite = nullptr; 0277 m_preview.ship = nullptr; 0278 0279 m_impact = nullptr; 0280 m_last_hit = nullptr; 0281 0282 qDeleteAll(m_sprites); 0283 m_sprites.clear(); 0284 } 0285 0286 void BattleFieldView::mousePressEvent(QMouseEvent *ev) 0287 { 0288 Button *button = dynamic_cast<Button *>(itemAt(ev->pos())); 0289 0290 if (m_screen->isVisible() && button) 0291 { 0292 m_screen->onMousePress(button); 0293 } 0294 else if (ev->button() == Qt::LeftButton && m_delegate) 0295 { 0296 Coord c = m_renderer->toLogical(ev->pos()); 0297 m_delegate->action(m_player, c); 0298 } 0299 else if (ev->button() == Qt::RightButton && m_delegate) 0300 { 0301 m_delegate->changeDirection(m_player); 0302 setPreview(ev->pos()); 0303 } 0304 } 0305 0306 void BattleFieldView::mouseReleaseEvent(QMouseEvent *ev) 0307 { 0308 Button *button = dynamic_cast<Button *>(itemAt(ev->pos())); 0309 0310 if (m_screen->isVisible() && button && ev->button() == Qt::LeftButton) { 0311 m_screen->onMouseRelease(button); 0312 } 0313 } 0314 0315 void BattleFieldView::mouseMoveEvent(QMouseEvent *ev) 0316 { 0317 Button *button = dynamic_cast<Button *>(itemAt(ev->pos())); 0318 0319 if (m_screen->isVisible() && button) 0320 { 0321 m_screen->onMouseMove(button); 0322 } 0323 else if (m_screen->isVisible() && !button) 0324 { 0325 m_screen->onMouseLeave(); 0326 } 0327 else 0328 { 0329 cancelPreview(); 0330 setPreview(ev->pos()); 0331 } 0332 } 0333 0334 void BattleFieldView::leaveEvent(QEvent *) 0335 { 0336 if (m_screen->isVisible()) { 0337 m_screen->onMouseLeave(); 0338 } else { 0339 cancelPreview(); 0340 } 0341 } 0342 0343 WelcomeScreen* BattleFieldView::screen() const 0344 { 0345 return m_screen; 0346 } 0347 0348 void BattleFieldView::setDelegate(Delegate *c) 0349 { 0350 m_delegate = c; 0351 } 0352 0353 void BattleFieldView::setPlayer(Sea::Player player) 0354 { 0355 m_player = player; 0356 } 0357 0358 const qreal BattleFieldView::PREVIEW_OPACITY = 0.7;