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"