File indexing completed on 2024-12-01 06:51:50

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 "controller.h"
0008 
0009 #include <KLocalizedString>
0010 
0011 #include "playerentity.h"
0012 #include "aientity.h"
0013 #include "networkentity.h"
0014 #include "seaview.h"
0015 #include "shot.h"
0016 #include "audioplayer.h"
0017 #include "ships.h"
0018 
0019 Controller::Controller(QObject* parent, AudioPlayer* audioPlayer, const BattleShipsConfiguration& battleShipsConfiguration)
0020 : QObject(parent)
0021 , m_shot(nullptr)
0022 , m_ready(0)
0023 , m_player(audioPlayer)
0024 , m_has_ai(false)
0025 , mBattleShipsConfiguration(battleShipsConfiguration)
0026 {
0027     m_ui = nullptr;
0028     m_sea = new Sea(this, battleShipsConfiguration);
0029 }
0030 
0031 PlayerEntity* Controller::createPlayer(Sea::Player player, SeaView* view,
0032                                               ChatWidget* chat, const QString& nick)
0033 {
0034     if (m_ui) {
0035         qCDebug(KNAVALBATTLE_LOG) << "Cannot create more than one human player";
0036         return nullptr;
0037     }
0038     PlayerEntity* entity = new PlayerEntity(player, m_sea, view, chat);
0039     entity->setNick(nick);
0040     m_ui = entity;
0041     setupEntity(m_ui);
0042     return entity;
0043 }
0044 
0045 AIEntity* Controller::createAI(Sea::Player player, SeaView* view)
0046 {
0047     qCDebug(KNAVALBATTLE_LOG) << "created ai entity";
0048     m_has_ai = true;
0049     AIEntity* e = new AIEntity(player, m_sea, view);
0050     e->setNick(i18n("Computer"));
0051     setupEntity(e);
0052 
0053     return e;
0054 }
0055 
0056 NetworkEntity* Controller::createRemotePlayer(Sea::Player player, SeaView* view, Protocol* protocol, bool client)
0057 {
0058     NetworkEntity* e = new NetworkEntity(player, m_sea, view, protocol, client);
0059     setupEntity(e);
0060     connect(e, &NetworkEntity::restartRequested, this, &Controller::restartRequested);
0061     if (client) {
0062         m_sea->switchTurn();
0063     }
0064     return e;
0065 }
0066 
0067 void Controller::setupEntity(Entity* entity)
0068 {
0069     entity->setParent(this);
0070 
0071     connect(entity, &Entity::shoot,
0072             this, &Controller::shoot, Qt::QueuedConnection);
0073     connect(entity, &Entity::ready,
0074             this, &Controller::ready);
0075     connect(entity, &Entity::shipsPlaced,
0076             this, &Controller::shipsPlaced);
0077     connect(entity, &Entity::chat,
0078             this, &Controller::receivedChat);
0079     connect(entity, &Entity::nickChanged,
0080             this, &Controller::nick);
0081     connect(entity, &Entity::compatibility,
0082             this, &Controller::compatibility);
0083     connect(entity, &Entity::gameOptionsInterchanged,
0084             this, &Controller::placing);
0085 
0086     for (Entity* e : std::as_const(m_entities)) {
0087         connect(e, &Entity::compatibility,
0088                 entity, &Entity::setCompatibilityLevel);
0089         connect(entity, &Entity::compatibility,
0090                 e, &Entity::setCompatibilityLevel);
0091 
0092         connect(e, &Entity::abortGame,
0093                 entity, &Entity::notifyAbort);
0094         connect(entity, &Entity::abortGame,
0095                 e, &Entity::notifyAbort);
0096         connect(e, &Entity::restartPlacingShips,
0097                 this, &Controller::restartPlacingShips);
0098         connect(e, &Entity::restartPlacingShips,
0099                 this, &Controller::notifyRestartPlacingShips);
0100     }
0101 
0102     m_entities.append(entity);
0103 }
0104 
0105 void Controller::setBattleShipsConfiguration(const BattleShipsConfiguration& battleConfiguration)
0106 {
0107     mBattleShipsConfiguration = battleConfiguration;
0108 }
0109 
0110 
0111 bool Controller::allPlayers() const
0112 {
0113     unsigned char bitmap = 0;
0114     for (Entity* entity : m_entities) {
0115         int player = entity->player();
0116         qCDebug(KNAVALBATTLE_LOG) << "found player" << player;
0117         bitmap |= (1 << player);    
0118     }
0119     
0120     qCDebug(KNAVALBATTLE_LOG) << "bitmap =" << (unsigned) bitmap;
0121     return bitmap == 3;
0122 }
0123 
0124 bool Controller::start(SeaView* view)
0125 {
0126     if (!allPlayers()) {
0127         return false;
0128     }
0129 
0130     if (!m_ui) {
0131         m_ui = new UIEntity(Sea::NO_PLAYER, m_sea, view);
0132         setupEntity(m_ui);
0133     }
0134 
0135     for (Entity* entity : std::as_const(m_entities)) {
0136         entity->notifyGameOptions();
0137     }
0138     
0139     for (Entity* source : std::as_const(m_entities)) {
0140         for (Entity* target : std::as_const(m_entities)) {
0141             if (source->player() != target->player() &&
0142                 !source->nick().isEmpty()) {
0143                 target->notifyNick(source->player(), source->nick());
0144             }
0145         }
0146     }
0147 
0148     return true;
0149 }
0150 
0151 void Controller::restart()
0152 {
0153     m_ready = 0;
0154     m_sea->clear(Sea::PLAYER_A);
0155     m_sea->clear(Sea::PLAYER_B);
0156 
0157     for (Entity* entity : std::as_const(m_entities)) {
0158         m_sea->clear(entity->player());
0159             Q_EMIT startPlacingShips(Sea::PLAYER_A);
0160             entity->startPlacing();
0161     }
0162 }
0163 
0164 
0165 // It is sure the entities has interchanged the GameOptions (if any)
0166 // when the opposite nick is received
0167 void Controller::placing()
0168 {
0169     for (Entity* entity : std::as_const(m_entities)) {
0170         entity->startPlacing();
0171     }
0172 }
0173 
0174 void Controller::shoot(int player, const Coord& c)
0175 {
0176     Entity* entity = findEntity(Sea::opponent(Sea::Player(player)));
0177     if (!entity) {
0178         qCDebug(KNAVALBATTLE_LOG) << "no entity!";
0179         return;
0180     }
0181 
0182     if (m_shot) {
0183         qCDebug(KNAVALBATTLE_LOG) << "shot in progress";
0184         // shot in progress
0185         return;
0186     }
0187 
0188     if (m_sea->status() == Sea::PLAYING) {
0189         entity->hit(m_shot = new Shot(this, Sea::Player(player), c)); // kind of CPS
0190     }
0191 }
0192 
0193 void Controller::finalizeShot(Sea::Player player, const Coord& c, const HitInfo& info)
0194 {
0195     if (info.type != HitInfo::INVALID) {
0196         // notify entities
0197         notify(player, c, info);
0198 
0199         // play sounds
0200         if (m_player) {
0201             m_player->play(player, info);
0202         }
0203 
0204         if (m_sea->status() == Sea::A_WINS) {
0205             finalizeGame(Sea::PLAYER_A);
0206         }
0207         else if (m_sea->status() == Sea::B_WINS) {
0208             finalizeGame(Sea::PLAYER_B);
0209         }
0210         else {
0211             Q_EMIT turnChanged(m_sea->turn());
0212         }
0213     }
0214     else {
0215         qCDebug(KNAVALBATTLE_LOG) << "illegal move" << c << "for player" << player;
0216     }
0217     
0218     delete m_shot;
0219     m_shot = nullptr;
0220 }
0221 
0222 void Controller::notify(Sea::Player player, const Coord& c, const HitInfo& info)
0223 {
0224     for (Entity* entity : std::as_const(m_entities)) {
0225         entity->notify(player, c, info);
0226         if (player == entity->player()) {
0227             entity->stats()->addInfo(info);
0228         }
0229     }
0230 }
0231 
0232 void Controller::shipsPlaced()
0233 {
0234     m_ready++;
0235     if (m_ready >= 2 )
0236     {
0237         for (Entity* entity : std::as_const(m_entities)) {
0238             entity->start();
0239         }
0240     }
0241 }
0242 
0243 
0244 void Controller::ready(int player)
0245 {
0246     m_ready++;
0247     for (Entity* entity : std::as_const(m_entities)) {
0248         entity->notifyReady(Sea::Player(player));
0249     }
0250     // when two entities are ready (ships placed and ready)
0251     // start all engines
0252     if (m_ready >= 4) {
0253         m_sea->startPlaying();
0254 
0255         for (Entity* entity : std::as_const(m_entities)) {
0256             entity->startPlaying();
0257         }
0258         Q_EMIT playerReady(-1);
0259     }
0260     else {
0261         Q_EMIT playerReady(player);
0262     }
0263 }
0264 
0265 void Controller::finalizeGame(Sea::Player winner)
0266 {
0267     // first, every entity will notify the other entity its ships
0268     for (Entity* entity : std::as_const(m_entities)) {
0269             entity->notifyShips(winner);
0270     }
0271     // then, it will notify the end of the game
0272     for (Entity* entity : std::as_const(m_entities)) {
0273         entity->notifyGameOver(winner);
0274     }
0275     Q_EMIT gameOver(winner);
0276 }
0277 
0278 void Controller::notifyRestartPlacingShips(Sea::Player player)
0279 {
0280     for (Entity* entity : std::as_const(m_entities)) {
0281         if (entity->player() == player) {
0282             entity->notifyRestartPlacing(player);
0283         }
0284     }
0285 }
0286 
0287 Entity* Controller::findEntity(Sea::Player player) const
0288 {
0289     for (Entity* entity : m_entities) {
0290         if (entity->player() == player) {
0291             return entity;
0292         }
0293     }
0294     
0295     return nullptr;
0296 }
0297 
0298 void Controller::receivedChat(const QString& text)
0299 {
0300     Entity* chat_sender = qobject_cast<Entity*>(sender());
0301     
0302     if (chat_sender) {    
0303         for (Entity* entity : std::as_const(m_entities)) {
0304             if (entity != chat_sender) {
0305                 qCDebug(KNAVALBATTLE_LOG) << "forwarding to" << entity->nick();
0306                 entity->notifyChat(chat_sender, text);
0307             }
0308         }
0309     }
0310 }
0311 
0312 void Controller::nick(int player, const QString& nick)
0313 {
0314     qCDebug(KNAVALBATTLE_LOG) << "controller: nick";
0315     for (Entity* entity : std::as_const(m_entities)) {
0316         if (entity->player() != Sea::Player(player)) {
0317             entity->notifyNick(Sea::Player(player), nick);
0318         }
0319     }
0320     Q_EMIT nickChanged(player, nick);
0321 }
0322 
0323 Sea::Player Controller::turn() const
0324 {
0325     return m_sea->turn();
0326 }
0327 
0328 bool Controller::hasAI() const
0329 {
0330     return m_has_ai;
0331 }
0332 
0333 #include "moc_controller.cpp"