File indexing completed on 2023-12-03 07:53:51
0001 /* 0002 SPDX-FileCopyrightText: 2000-2001 Nikolas Zimmermann <wildfox@kde.org> 0003 SPDX-FileCopyrightText: 2000-2001 Daniel Molkentin <molkentin@kde.org> 0004 SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "playfield.h" 0010 0011 #include <QStatusBar> 0012 #include <QVBoxLayout> 0013 #include "knavalbattle_debug.h" 0014 #include <QInputDialog> 0015 #include <QPointer> 0016 0017 #include <kwidgetsaddons_version.h> 0018 #include <KLocalizedString> 0019 #include <KMessageBox> 0020 #include <KScoreDialog> 0021 0022 #include "aientity.h" 0023 #include "audioplayer.h" 0024 #include "chatwidget.h" 0025 #include "controller.h" 0026 #include "playerentity.h" 0027 #include "seaview.h" 0028 #include "settings.h" 0029 #include "simplemenu.h" 0030 #include "stats.h" 0031 #include "welcomescreen.h" 0032 0033 static const int MINIMUM_HEIGHT = 400; 0034 0035 PlayField::PlayField(QWidget* parent, QStatusBar* sbar) 0036 : QWidget(parent) 0037 , m_status_bar(sbar) 0038 , m_show_endofgame_message(true) 0039 { 0040 setMinimumSize(static_cast<int>(MINIMUM_HEIGHT * 1.6), MINIMUM_HEIGHT); 0041 QVBoxLayout* layout = new QVBoxLayout; 0042 0043 m_seaView = new SeaView(this); 0044 layout->addWidget(m_seaView, 1); 0045 0046 m_chat = new ChatWidget(this); 0047 m_chat->hide(); 0048 layout->addWidget(m_chat, 1); 0049 0050 layout->setContentsMargins(0, 0, 0, 0); 0051 // layout->setSpacing(0); 0052 setLayout(layout); 0053 0054 m_controller = nullptr; 0055 m_menu = nullptr; 0056 0057 m_player = new AudioPlayer(this); 0058 m_player->setActive(Settings::enableSounds()); 0059 } 0060 0061 PlayField::~PlayField() 0062 { 0063 // controller assumes that the view is still valid 0064 // when it is destroyed 0065 delete m_controller; 0066 } 0067 0068 Controller* PlayField::createController() 0069 { 0070 // The client or server will overwrite this default configuration when 0071 // the network messages are interchanged 0072 m_battle_ships_configuration = Settings::severalShips() ? 0073 BattleShipsConfiguration::defaultMultipleShipsConfiguration(Settings::adjacentShips()): 0074 BattleShipsConfiguration::defaultSingleShipsConfiguration(Settings::adjacentShips()); 0075 Controller* controller = new Controller(this, m_player, m_battle_ships_configuration); 0076 connect(controller, &Controller::gameOver, this, &PlayField::gameOver); 0077 connect(controller, &Controller::restartRequested, this, &PlayField::restartRequested); 0078 connect(controller, &Controller::compatibility, this, &PlayField::setCompatibility); 0079 connect(controller, &Controller::nickChanged, this, &PlayField::updateNick); 0080 connect(controller, &Controller::turnChanged, this, &PlayField::changeTurn); 0081 connect(controller, &Controller::playerReady, this, &PlayField::playerReady); 0082 connect(controller, &Controller::restartPlacingShips, this, &PlayField::restartPlacingShips); 0083 connect(controller, &Controller::startPlacingShips, this, &PlayField::startPlacingShips); 0084 0085 return controller; 0086 } 0087 0088 void PlayField::setupController() 0089 { 0090 Animator::instance()->restart(); 0091 m_seaView->clear(); 0092 m_chat->hide(); 0093 0094 // remove welcome screen 0095 m_seaView->screen(Sea::Player(0))->fadeOut(); 0096 m_seaView->screen(Sea::Player(1))->fadeOut(); 0097 0098 delete m_controller; 0099 m_controller = createController(); 0100 m_menu->setupController(m_controller, nullptr, m_seaView, m_chat); 0101 startGame(); 0102 } 0103 0104 void PlayField::endGame() 0105 { 0106 Animator::instance()->restart(); 0107 delete m_controller; 0108 m_controller = nullptr; 0109 m_seaView->clear(); 0110 } 0111 0112 void PlayField::newGame() 0113 { 0114 endGame(); 0115 delete m_menu; 0116 0117 Kg::difficulty()->setGameRunning(false); 0118 0119 m_chat->hide(); 0120 m_seaView->screen(Sea::Player(0))->show(); 0121 m_seaView->screen(Sea::Player(1))->show(); 0122 0123 m_menu = new SimpleMenu(this, m_seaView->screen(Sea::Player(0))); 0124 connect(m_menu, &SimpleMenu::done, this, &PlayField::setupController); 0125 m_status_bar->showMessage(QLatin1String("")); 0126 Q_EMIT welcomeScreen(); 0127 } 0128 0129 void PlayField::restart() 0130 { 0131 Animator::instance()->restart(); 0132 m_seaView->clear(); 0133 startGame(); 0134 m_controller->restart(); 0135 m_menu->player(0)->stats()->reset(); 0136 m_menu->player(1)->stats()->reset(); 0137 } 0138 0139 0140 void PlayField::highscores() 0141 { 0142 KScoreDialog* highscoredialog = new KScoreDialog( 0143 KScoreDialog::Name | KScoreDialog::Score | 0144 KScoreDialog::Custom1 | 0145 KScoreDialog::Custom2 | 0146 KScoreDialog::Custom3, 0147 this); 0148 highscoredialog->initFromDifficulty(Kg::difficulty()); 0149 highscoredialog->addField(KScoreDialog::Custom1, i18n("Shots"), QStringLiteral("shots")); 0150 highscoredialog->addField(KScoreDialog::Custom2, i18n("Hits"), QStringLiteral("hits")); 0151 highscoredialog->addField(KScoreDialog::Custom3, i18n("Misses"), QStringLiteral("water")); 0152 0153 highscoredialog->exec(); 0154 } 0155 0156 void PlayField::gameOver(Sea::Player winner) 0157 { 0158 if (winner == Sea::Player(0)) { 0159 const Stats* stats = m_menu->player(0)->stats(); 0160 0161 if (stats && qobject_cast<const AIEntity*>(m_menu->player(1))) { 0162 QPointer<KScoreDialog> highscoredialog = new KScoreDialog( 0163 KScoreDialog::Name | KScoreDialog::Score | 0164 KScoreDialog::Custom1 | 0165 KScoreDialog::Custom2 | 0166 KScoreDialog::Custom3, 0167 this); 0168 highscoredialog->initFromDifficulty(Kg::difficulty()); 0169 highscoredialog->addField(KScoreDialog::Custom1, i18n("Shots"), QStringLiteral("shots")); 0170 highscoredialog->addField(KScoreDialog::Custom2, i18n("Hits"), QStringLiteral("hits")); 0171 highscoredialog->addField(KScoreDialog::Custom3, i18n("Misses"), QStringLiteral("water")); 0172 0173 KScoreDialog::FieldInfo info; 0174 info[KScoreDialog::Name] = m_menu->player(0)->nick(); 0175 info[KScoreDialog::Score].setNum(stats->score()); 0176 info[KScoreDialog::Custom1] = QString::number(stats->shots()); 0177 info[KScoreDialog::Custom2] = QString::number(stats->hits()); 0178 info[KScoreDialog::Custom3] = QString::number(stats->misses()); 0179 0180 int temp = highscoredialog->addScore(info, KScoreDialog::AskName); 0181 qCDebug(KNAVALBATTLE_LOG) << "temp =" << temp; 0182 //if (highscoredialog->addScore(info, KScoreDialog::AskName)) { 0183 if (temp != 0) { 0184 highscoredialog->exec(); 0185 delete highscoredialog; 0186 return; 0187 } 0188 delete highscoredialog; 0189 } 0190 0191 m_status_bar->showMessage(i18n("You win!")); 0192 if (m_show_endofgame_message) { 0193 KMessageBox::information(this, i18n("You win. Excellent!")); 0194 } 0195 } 0196 else { 0197 m_status_bar->showMessage(i18n("You lose.")); 0198 if (m_show_endofgame_message) { 0199 KMessageBox::information(this, i18n("You lose. Better luck next time!")); 0200 } 0201 } 0202 0203 // When we have finished, show again the welcome screen 0204 Q_EMIT gameFinished(); 0205 } 0206 0207 void PlayField::changeNick() 0208 { 0209 QString nick = QInputDialog::getText(this, i18n("Change Nickname"), i18n("Enter new nickname:"), QLineEdit::Normal, Settings::findNick()); 0210 if (!nick.isEmpty()) { 0211 Settings::setNickname(nick); 0212 Settings::self()->save(); 0213 } 0214 } 0215 0216 void PlayField::toggleSounds(bool enable) 0217 { 0218 Settings::setEnableSounds(enable); 0219 Settings::self()->save(); 0220 m_player->setActive(enable); 0221 } 0222 0223 void PlayField::toggleAdjacent(bool enable) 0224 { 0225 Settings::setAdjacentShips(enable); 0226 Settings::self()->save(); 0227 } 0228 0229 void PlayField::toggleMultiple(bool enable) 0230 { 0231 Settings::setSeveralShips(enable); 0232 Settings::self()->save(); 0233 } 0234 0235 void PlayField::restartRequested() 0236 { 0237 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0238 int ans = KMessageBox::questionTwoActions(this, 0239 #else 0240 int ans = KMessageBox::questionYesNo(this, 0241 #endif 0242 i18n("Restart game"), 0243 i18n("Your opponent has requested to restart the game. Do you accept?"), 0244 KGuiItem(i18nc("@action:button", "Accept"), QStringLiteral("dialog-ok")), 0245 KGuiItem(i18nc("@action:button", "Reject"), QStringLiteral("dialog-cancel"))); 0246 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0247 if (ans == KMessageBox::PrimaryAction) { 0248 #else 0249 if (ans == KMessageBox::Yes) { 0250 #endif 0251 restart(); 0252 } 0253 } 0254 0255 void PlayField::setCompatibility(int level) 0256 { 0257 if (level == Entity::COMPAT_KBS3) { 0258 KMessageBox::information(this, i18n("Your opponent is using a pre-KDE4 version of Naval Battle. Note that, according to the rules enforced by old clients, ships cannot be placed adjacent to one another and only one ship of each size is allowed.")); 0259 } 0260 } 0261 0262 void PlayField::updateNick(int player, const QString& nick) 0263 { 0264 m_seaView->setNick(Sea::Player(player), nick); 0265 } 0266 0267 void PlayField::changeTurn(int player) 0268 { 0269 if (player == 0) { 0270 // local user 0271 m_status_bar->showMessage(i18n("Enemy has shot. Shoot now!")); 0272 } 0273 else { 0274 // opponent 0275 m_status_bar->showMessage(i18n("Waiting for enemy to shoot...")); 0276 } 0277 } 0278 0279 void PlayField::playerReady(int player) 0280 { 0281 if (player == -1) { 0282 // game can start 0283 if (m_controller->turn() == 0) { 0284 m_status_bar->showMessage(i18n("Ships placed. Now shoot on the enemy field!")); 0285 } 0286 else { 0287 m_status_bar->showMessage(i18n("Waiting for other player to start the game...")); 0288 } 0289 } 0290 else if (player == 0) { 0291 m_status_bar->showMessage(i18n("Waiting for other player to place his ships...")); 0292 } 0293 } 0294 0295 void PlayField::startGame() 0296 { 0297 //the game has a fixed difficulty level only if there is an AI 0298 Kg::difficulty()->setGameRunning(m_controller->hasAI()); 0299 startPlacingShips(); 0300 Q_EMIT placeShips(); 0301 } 0302 0303 void PlayField::startPlacingShips() 0304 { 0305 m_status_bar->showMessage(i18n("Place your %1 ships. Use the right mouse button to rotate them.", m_battle_ships_configuration.totalNumberOfShipsToPlay())); 0306 } 0307 0308 0309 void PlayField::restartPlacingShips(Sea::Player player) 0310 { 0311 m_status_bar->showMessage(i18n("You can't place your remaining ships.")); 0312 KGuiItem buttonRestart(i18nc("@action", "Restart"), QStringLiteral("view-refresh")); 0313 KGuiItem buttonAbort(i18nc("@action", "Abort"), QStringLiteral("dialog-cancel")); 0314 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0315 int res=KMessageBox::warningTwoActions(this, 0316 #else 0317 int res=KMessageBox::warningYesNo(this, 0318 #endif 0319 i18n("You can't place your remaining ships. Please restart placing ships or abort game"), i18n("Restart placing ships"), buttonRestart, buttonAbort); 0320 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0321 if (res == KMessageBox::PrimaryAction) { 0322 #else 0323 if (res == KMessageBox::Yes) { 0324 #endif 0325 startPlacingShips(); 0326 m_controller->notifyRestartPlacingShips(player); 0327 } 0328 else { 0329 newGame(); 0330 } 0331 } 0332 0333 0334 void PlayField::levelChanged() 0335 { 0336 if (m_controller && m_controller->hasAI()) { 0337 restart(); 0338 } 0339 } 0340 0341 SimpleMenu* PlayField::createAuxMenu() 0342 { 0343 SimpleMenu* menu = new SimpleMenu(this, nullptr); 0344 connect(menu, &SimpleMenu::done, this, &PlayField::auxMenuDone); 0345 return menu; 0346 } 0347 0348 void PlayField::auxMenuDone() 0349 { 0350 qCDebug(KNAVALBATTLE_LOG) << "aux menu done"; 0351 SimpleMenu* menu = qobject_cast<SimpleMenu*>(sender()); 0352 if (menu) { 0353 delete m_menu; 0354 m_menu = menu; 0355 setupController(); 0356 } 0357 } 0358 0359 void PlayField::localGame() 0360 { 0361 createAuxMenu()->localGame(); 0362 } 0363 0364 void PlayField::createServer() 0365 { 0366 createAuxMenu()->createServer(); 0367 } 0368 0369 void PlayField::createClient() 0370 { 0371 createAuxMenu()->createClient(); 0372 } 0373 0374 void PlayField::createClientWithUrl(const QUrl& url) 0375 { 0376 createAuxMenu()->createClientWithUrl(url); 0377 } 0378 0379 void PlayField::toggleEndOfGameMessage(bool show) 0380 { 0381 m_show_endofgame_message = show; 0382 } 0383 0384 void PlayField::toggleLeftGrid(bool show) 0385 { 0386 m_seaView->toggleLeftGrid(show); 0387 } 0388 0389 void PlayField::toggleRightGrid(bool show) 0390 { 0391 m_seaView->toggleRightGrid(show); 0392 } 0393 0394 #include "moc_playfield.cpp"