1 /*
2 SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "controller.h"
8
9 #include <KLocalizedString>
10
11 #include "playerentity.h"
12 #include "aientity.h"
13 #include "networkentity.h"
14 #include "seaview.h"
15 #include "shot.h"
16 #include "audioplayer.h"
17 #include "ships.h"
18
Controller(QObject * parent,AudioPlayer * audioPlayer,const BattleShipsConfiguration & battleShipsConfiguration)19 Controller::Controller(QObject* parent, AudioPlayer* audioPlayer, const BattleShipsConfiguration& battleShipsConfiguration)
20 : QObject(parent)
21 , m_shot(nullptr)
22 , m_ready(0)
23 , m_player(audioPlayer)
24 , m_has_ai(false)
25 , mBattleShipsConfiguration(battleShipsConfiguration)
26 {
27 m_ui = nullptr;
28 m_sea = new Sea(this, battleShipsConfiguration);
29 }
30
createPlayer(Sea::Player player,SeaView * view,ChatWidget * chat,const QString & nick)31 PlayerEntity* Controller::createPlayer(Sea::Player player, SeaView* view,
32 ChatWidget* chat, const QString& nick)
33 {
34 if (m_ui) {
35 qCDebug(KNAVALBATTLE_LOG) << "Cannot create more than one human player";
36 return nullptr;
37 }
38 PlayerEntity* entity = new PlayerEntity(player, m_sea, view, chat);
39 entity->setNick(nick);
40 m_ui = entity;
41 setupEntity(m_ui);
42 return entity;
43 }
44
createAI(Sea::Player player,SeaView * view)45 AIEntity* Controller::createAI(Sea::Player player, SeaView* view)
46 {
47 qCDebug(KNAVALBATTLE_LOG) << "created ai entity";
48 m_has_ai = true;
49 AIEntity* e = new AIEntity(player, m_sea, view);
50 e->setNick(i18n("Computer"));
51 setupEntity(e);
52
53 return e;
54 }
55
createRemotePlayer(Sea::Player player,SeaView * view,Protocol * protocol,bool client)56 NetworkEntity* Controller::createRemotePlayer(Sea::Player player, SeaView* view, Protocol* protocol, bool client)
57 {
58 NetworkEntity* e = new NetworkEntity(player, m_sea, view, protocol, client);
59 setupEntity(e);
60 connect(e, &NetworkEntity::restartRequested, this, &Controller::restartRequested);
61 if (client) {
62 m_sea->switchTurn();
63 }
64 return e;
65 }
66
setupEntity(Entity * entity)67 void Controller::setupEntity(Entity* entity)
68 {
69 entity->setParent(this);
70
71 connect(entity, &Entity::shoot,
72 this, &Controller::shoot, Qt::QueuedConnection);
73 connect(entity, &Entity::ready,
74 this, &Controller::ready);
75 connect(entity, &Entity::shipsPlaced,
76 this, &Controller::shipsPlaced);
77 connect(entity, &Entity::chat,
78 this, &Controller::receivedChat);
79 connect(entity, &Entity::nickChanged,
80 this, &Controller::nick);
81 connect(entity, &Entity::compatibility,
82 this, &Controller::compatibility);
83 connect(entity, &Entity::gameOptionsInterchanged,
84 this, &Controller::placing);
85
86 for (Entity* e : std::as_const(m_entities)) {
87 connect(e, &Entity::compatibility,
88 entity, &Entity::setCompatibilityLevel);
89 connect(entity, &Entity::compatibility,
90 e, &Entity::setCompatibilityLevel);
91
92 connect(e, &Entity::abortGame,
93 entity, &Entity::notifyAbort);
94 connect(entity, &Entity::abortGame,
95 e, &Entity::notifyAbort);
96 connect(e, &Entity::restartPlacingShips,
97 this, &Controller::restartPlacingShips);
98 connect(e, &Entity::restartPlacingShips,
99 this, &Controller::notifyRestartPlacingShips);
100 }
101
102 m_entities.append(entity);
103 }
104
setBattleShipsConfiguration(const BattleShipsConfiguration & battleConfiguration)105 void Controller::setBattleShipsConfiguration(const BattleShipsConfiguration& battleConfiguration)
106 {
107 mBattleShipsConfiguration = battleConfiguration;
108 }
109
110
allPlayers() const111 bool Controller::allPlayers() const
112 {
113 unsigned char bitmap = 0;
114 for (Entity* entity : m_entities) {
115 int player = entity->player();
116 qCDebug(KNAVALBATTLE_LOG) << "found player" << player;
117 bitmap |= (1 << player);
118 }
119
120 qCDebug(KNAVALBATTLE_LOG) << "bitmap =" << (unsigned) bitmap;
121 return bitmap == 3;
122 }
123
start(SeaView * view)124 bool Controller::start(SeaView* view)
125 {
126 if (!allPlayers()) {
127 return false;
128 }
129
130 if (!m_ui) {
131 m_ui = new UIEntity(Sea::NO_PLAYER, m_sea, view);
132 setupEntity(m_ui);
133 }
134
135 for (Entity* entity : std::as_const(m_entities)) {
136 entity->notifyGameOptions();
137 }
138
139 for (Entity* source : std::as_const(m_entities)) {
140 for (Entity* target : std::as_const(m_entities)) {
141 if (source->player() != target->player() &&
142 !source->nick().isEmpty()) {
143 target->notifyNick(source->player(), source->nick());
144 }
145 }
146 }
147
148 return true;
149 }
150
restart()151 void Controller::restart()
152 {
153 m_ready = 0;
154 m_sea->clear(Sea::PLAYER_A);
155 m_sea->clear(Sea::PLAYER_B);
156
157 for (Entity* entity : std::as_const(m_entities)) {
158 m_sea->clear(entity->player());
159 Q_EMIT startPlacingShips(Sea::PLAYER_A);
160 entity->startPlacing();
161 }
162 }
163
164
165 // It is sure the entities has interchanged the GameOptions (if any)
166 // when the opposite nick is received
placing()167 void Controller::placing()
168 {
169 for (Entity* entity : std::as_const(m_entities)) {
170 entity->startPlacing();
171 }
172 }
173
shoot(int player,const Coord & c)174 void Controller::shoot(int player, const Coord& c)
175 {
176 Entity* entity = findEntity(Sea::opponent(Sea::Player(player)));
177 if (!entity) {
178 qCDebug(KNAVALBATTLE_LOG) << "no entity!";
179 return;
180 }
181
182 if (m_shot) {
183 qCDebug(KNAVALBATTLE_LOG) << "shot in progress";
184 // shot in progress
185 return;
186 }
187
188 if (m_sea->status() == Sea::PLAYING) {
189 entity->hit(m_shot = new Shot(this, Sea::Player(player), c)); // kind of CPS
190 }
191 }
192
finalizeShot(Sea::Player player,const Coord & c,const HitInfo & info)193 void Controller::finalizeShot(Sea::Player player, const Coord& c, const HitInfo& info)
194 {
195 if (info.type != HitInfo::INVALID) {
196 // notify entities
197 notify(player, c, info);
198
199 // play sounds
200 if (m_player) {
201 m_player->play(player, info);
202 }
203
204 if (m_sea->status() == Sea::A_WINS) {
205 finalizeGame(Sea::PLAYER_A);
206 }
207 else if (m_sea->status() == Sea::B_WINS) {
208 finalizeGame(Sea::PLAYER_B);
209 }
210 else {
211 Q_EMIT turnChanged(m_sea->turn());
212 }
213 }
214 else {
215 qCDebug(KNAVALBATTLE_LOG) << "illegal move" << c << "for player" << player;
216 }
217
218 delete m_shot;
219 m_shot = nullptr;
220 }
221
notify(Sea::Player player,const Coord & c,const HitInfo & info)222 void Controller::notify(Sea::Player player, const Coord& c, const HitInfo& info)
223 {
224 for (Entity* entity : std::as_const(m_entities)) {
225 entity->notify(player, c, info);
226 if (player == entity->player()) {
227 entity->stats()->addInfo(info);
228 }
229 }
230 }
231
shipsPlaced()232 void Controller::shipsPlaced()
233 {
234 m_ready++;
235 if (m_ready >= 2 )
236 {
237 for (Entity* entity : std::as_const(m_entities)) {
238 entity->start();
239 }
240 }
241 }
242
243
ready(int player)244 void Controller::ready(int player)
245 {
246 m_ready++;
247 for (Entity* entity : std::as_const(m_entities)) {
248 entity->notifyReady(Sea::Player(player));
249 }
250 // when two entities are ready (ships placed and ready)
251 // start all engines
252 if (m_ready >= 4) {
253 m_sea->startPlaying();
254
255 for (Entity* entity : std::as_const(m_entities)) {
256 entity->startPlaying();
257 }
258 Q_EMIT playerReady(-1);
259 }
260 else {
261 Q_EMIT playerReady(player);
262 }
263 }
264
finalizeGame(Sea::Player winner)265 void Controller::finalizeGame(Sea::Player winner)
266 {
267 // first, every entity will notify the other entity its ships
268 for (Entity* entity : std::as_const(m_entities)) {
269 entity->notifyShips(winner);
270 }
271 // then, it will notify the end of the game
272 for (Entity* entity : std::as_const(m_entities)) {
273 entity->notifyGameOver(winner);
274 }
275 Q_EMIT gameOver(winner);
276 }
277
notifyRestartPlacingShips(Sea::Player player)278 void Controller::notifyRestartPlacingShips(Sea::Player player)
279 {
280 for (Entity* entity : std::as_const(m_entities)) {
281 if (entity->player() == player) {
282 entity->notifyRestartPlacing(player);
283 }
284 }
285 }
286
findEntity(Sea::Player player) const287 Entity* Controller::findEntity(Sea::Player player) const
288 {
289 for (Entity* entity : m_entities) {
290 if (entity->player() == player) {
291 return entity;
292 }
293 }
294
295 return nullptr;
296 }
297
receivedChat(const QString & text)298 void Controller::receivedChat(const QString& text)
299 {
300 Entity* chat_sender = qobject_cast<Entity*>(sender());
301
302 if (chat_sender) {
303 for (Entity* entity : std::as_const(m_entities)) {
304 if (entity != chat_sender) {
305 qCDebug(KNAVALBATTLE_LOG) << "forwarding to" << entity->nick();
306 entity->notifyChat(chat_sender, text);
307 }
308 }
309 }
310 }
311
nick(int player,const QString & nick)312 void Controller::nick(int player, const QString& nick)
313 {
314 qCDebug(KNAVALBATTLE_LOG) << "controller: nick";
315 for (Entity* entity : std::as_const(m_entities)) {
316 if (entity->player() != Sea::Player(player)) {
317 entity->notifyNick(Sea::Player(player), nick);
318 }
319 }
320 Q_EMIT nickChanged(player, nick);
321 }
322
turn() const323 Sea::Player Controller::turn() const
324 {
325 return m_sea->turn();
326 }
327
hasAI() const328 bool Controller::hasAI() const
329 {
330 return m_has_ai;
331 }
332
333
334