File indexing completed on 2024-09-08 06:47:58
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 <KLocalizedString> 0018 #include <KMessageBox> 0019 0020 #include <KGameHighScoreDialog> 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 KGameDifficulty::global()->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 KGameHighScoreDialog* highscoredialog = new KGameHighScoreDialog( 0143 KGameHighScoreDialog::Name | KGameHighScoreDialog::Score | 0144 KGameHighScoreDialog::Custom1 | 0145 KGameHighScoreDialog::Custom2 | 0146 KGameHighScoreDialog::Custom3, 0147 this); 0148 highscoredialog->initFromDifficulty(KGameDifficulty::global()); 0149 highscoredialog->addField(KGameHighScoreDialog::Custom1, i18n("Shots"), QStringLiteral("shots")); 0150 highscoredialog->addField(KGameHighScoreDialog::Custom2, i18n("Hits"), QStringLiteral("hits")); 0151 highscoredialog->addField(KGameHighScoreDialog::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<KGameHighScoreDialog> highscoredialog = new KGameHighScoreDialog( 0163 KGameHighScoreDialog::Name | KGameHighScoreDialog::Score | 0164 KGameHighScoreDialog::Custom1 | 0165 KGameHighScoreDialog::Custom2 | 0166 KGameHighScoreDialog::Custom3, 0167 this); 0168 highscoredialog->initFromDifficulty(KGameDifficulty::global()); 0169 highscoredialog->addField(KGameHighScoreDialog::Custom1, i18n("Shots"), QStringLiteral("shots")); 0170 highscoredialog->addField(KGameHighScoreDialog::Custom2, i18n("Hits"), QStringLiteral("hits")); 0171 highscoredialog->addField(KGameHighScoreDialog::Custom3, i18n("Misses"), QStringLiteral("water")); 0172 0173 KGameHighScoreDialog::FieldInfo info; 0174 info[KGameHighScoreDialog::Name] = m_menu->player(0)->nick(); 0175 info[KGameHighScoreDialog::Score].setNum(stats->score()); 0176 info[KGameHighScoreDialog::Custom1] = QString::number(stats->shots()); 0177 info[KGameHighScoreDialog::Custom2] = QString::number(stats->hits()); 0178 info[KGameHighScoreDialog::Custom3] = QString::number(stats->misses()); 0179 0180 int temp = highscoredialog->addScore(info, KGameHighScoreDialog::AskName); 0181 qCDebug(KNAVALBATTLE_LOG) << "temp =" << temp; 0182 //if (highscoredialog->addScore(info, KGameHighScoreDialog::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 int ans = KMessageBox::questionTwoActions(this, 0238 i18n("Restart game"), 0239 i18n("Your opponent has requested to restart the game. Do you accept?"), 0240 KGuiItem(i18nc("@action:button", "Accept"), QStringLiteral("dialog-ok")), 0241 KGuiItem(i18nc("@action:button", "Reject"), QStringLiteral("dialog-cancel"))); 0242 if (ans == KMessageBox::PrimaryAction) { 0243 restart(); 0244 } 0245 } 0246 0247 void PlayField::setCompatibility(int level) 0248 { 0249 if (level == Entity::COMPAT_KBS3) { 0250 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.")); 0251 } 0252 } 0253 0254 void PlayField::updateNick(int player, const QString& nick) 0255 { 0256 m_seaView->setNick(Sea::Player(player), nick); 0257 } 0258 0259 void PlayField::changeTurn(int player) 0260 { 0261 if (player == 0) { 0262 // local user 0263 m_status_bar->showMessage(i18n("Enemy has shot. Shoot now!")); 0264 } 0265 else { 0266 // opponent 0267 m_status_bar->showMessage(i18n("Waiting for enemy to shoot...")); 0268 } 0269 } 0270 0271 void PlayField::playerReady(int player) 0272 { 0273 if (player == -1) { 0274 // game can start 0275 if (m_controller->turn() == 0) { 0276 m_status_bar->showMessage(i18n("Ships placed. Now shoot on the enemy field!")); 0277 } 0278 else { 0279 m_status_bar->showMessage(i18n("Waiting for other player to start the game...")); 0280 } 0281 } 0282 else if (player == 0) { 0283 m_status_bar->showMessage(i18n("Waiting for other player to place his ships...")); 0284 } 0285 } 0286 0287 void PlayField::startGame() 0288 { 0289 //the game has a fixed difficulty level only if there is an AI 0290 KGameDifficulty::global()->setGameRunning(m_controller->hasAI()); 0291 startPlacingShips(); 0292 Q_EMIT placeShips(); 0293 } 0294 0295 void PlayField::startPlacingShips() 0296 { 0297 m_status_bar->showMessage(i18n("Place your %1 ships. Use the right mouse button to rotate them.", m_battle_ships_configuration.totalNumberOfShipsToPlay())); 0298 } 0299 0300 0301 void PlayField::restartPlacingShips(Sea::Player player) 0302 { 0303 m_status_bar->showMessage(i18n("You can't place your remaining ships.")); 0304 KGuiItem buttonRestart(i18nc("@action", "Restart"), QStringLiteral("view-refresh")); 0305 KGuiItem buttonAbort(i18nc("@action", "Abort"), QStringLiteral("dialog-cancel")); 0306 int res=KMessageBox::warningTwoActions(this, 0307 i18n("You can't place your remaining ships. Please restart placing ships or abort game"), i18n("Restart placing ships"), buttonRestart, buttonAbort); 0308 if (res == KMessageBox::PrimaryAction) { 0309 startPlacingShips(); 0310 m_controller->notifyRestartPlacingShips(player); 0311 } 0312 else { 0313 newGame(); 0314 } 0315 } 0316 0317 0318 void PlayField::levelChanged() 0319 { 0320 if (m_controller && m_controller->hasAI()) { 0321 restart(); 0322 } 0323 } 0324 0325 SimpleMenu* PlayField::createAuxMenu() 0326 { 0327 SimpleMenu* menu = new SimpleMenu(this, nullptr); 0328 connect(menu, &SimpleMenu::done, this, &PlayField::auxMenuDone); 0329 return menu; 0330 } 0331 0332 void PlayField::auxMenuDone() 0333 { 0334 qCDebug(KNAVALBATTLE_LOG) << "aux menu done"; 0335 SimpleMenu* menu = qobject_cast<SimpleMenu*>(sender()); 0336 if (menu) { 0337 delete m_menu; 0338 m_menu = menu; 0339 setupController(); 0340 } 0341 } 0342 0343 void PlayField::localGame() 0344 { 0345 createAuxMenu()->localGame(); 0346 } 0347 0348 void PlayField::createServer() 0349 { 0350 createAuxMenu()->createServer(); 0351 } 0352 0353 void PlayField::createClient() 0354 { 0355 createAuxMenu()->createClient(); 0356 } 0357 0358 void PlayField::createClientWithUrl(const QUrl& url) 0359 { 0360 createAuxMenu()->createClientWithUrl(url); 0361 } 0362 0363 void PlayField::toggleEndOfGameMessage(bool show) 0364 { 0365 m_show_endofgame_message = show; 0366 } 0367 0368 void PlayField::toggleLeftGrid(bool show) 0369 { 0370 m_seaView->toggleLeftGrid(show); 0371 } 0372 0373 void PlayField::toggleRightGrid(bool show) 0374 { 0375 m_seaView->toggleRightGrid(show); 0376 } 0377 0378 #include "moc_playfield.cpp"