1 /**********************************************************************
2 *
3 * FreeDoko a Doppelkopf-Game
4 *
5 * Copyright (C) 2001 – 2018 by Diether Knof and Borg Enders
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You can find this license in the file 'gpl.txt'.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 * MA 02111-1307 USA
22 *
23 * Contact:
24 * Diether Knof dknof@posteo.de
25 *
26 **********************************************************************/
27
28 #include "constants.h"
29 #include "game.h"
30 #include "gameplay.h"
31 #include "gameplay_actions/poverty.h"
32 #include "../os/bug_report_replay.h"
33 #include "../misc/preferences.h"
34 #include "../party/rule.h"
35 #include "../ui/ui.h"
36
37 /** constructor
38 **
39 ** @param game corresponding game
40 **/
GamePoverty(Game & game)41 GamePoverty::GamePoverty(Game& game) noexcept :
42 game_(&game)
43 { }
44
45 /** constructor
46 **
47 ** @param poverty poverty to copy
48 ** @param game corresponding game
49 **/
GamePoverty(GamePoverty const & poverty,Game & game)50 GamePoverty::GamePoverty(GamePoverty const& poverty, Game& game) :
51 game_(&game),
52 shifted_cards_(poverty.shifted_cards_),
53 returned_cards_(poverty.returned_cards_),
54 shifted_(poverty.shifted_)
55 { }
56
57 /** @return whether cards were shifted
58 **/
59 bool
shifted() const60 GamePoverty::shifted() const noexcept
61 {
62 return this->shifted_;
63 }
64
65 /** @return the number of cards, that are shifted by the poverty player
66 **/
67 unsigned
shifted_cardno() const68 GamePoverty::shifted_cardno() const noexcept
69 {
70 return this->shifted_cards().size();
71 }
72
73 /** @return the number of cards, that are returned to the poverty player
74 **/
75 unsigned
returned_cardno() const76 GamePoverty::returned_cardno() const noexcept
77 {
78 return this->returned_cards().size();
79 }
80
81 /** @return the number of trumps, that are returned to the poverty player
82 **/
83 unsigned
returned_trumpno() const84 GamePoverty::returned_trumpno() const noexcept
85 {
86 return this->returned_cards().count_trumps();
87 }
88
89 /** @return the shifted poverty cards
90 **/
91 HandCards const&
shifted_cards() const92 GamePoverty::shifted_cards() const
93 {
94 DEBUG_ASSERTION((this->game_->type() == GameType::poverty),
95 "GamePoverty::shifted_cards():\n"
96 " invalid gametype");
97
98 return this->shifted_cards_;
99 }
100
101 /** @return the returned poverty cards
102 **/
103 HandCards const&
returned_cards() const104 GamePoverty::returned_cards() const
105 {
106 DEBUG_ASSERTION((this->game_->type() == GameType::poverty),
107 "GamePoverty::returned_cards():\n"
108 " invalid gametype '" << this->game_->type() << "'");
109
110 return this->returned_cards_;
111 }
112
113 /** reset all data
114 **/
115 void
reset()116 GamePoverty::reset() noexcept
117 {
118 this->shifted_cards_.clear();
119 this->returned_cards_.clear();
120 this->shifted_ = false;
121 }
122
123 /** make the shifting of the poverty cards
124 **/
125 void
shift()126 GamePoverty::shift()
127 {
128 auto& game = *this->game_;
129 auto& players = game.players();
130 auto const& rule = game.rule();
131 auto& soloplayer = players.soloplayer();
132 auto& gameplay = *game.gameplay_;
133
134 if ( (game.type() != GameType::poverty)
135 || !rule(Rule::Type::poverty_shift)
136 || ( (soloplayer.hand().count_trumps() <= 1)
137 && rule(Rule::Type::throw_with_one_trump)
138 )
139 ) {
140 players.set_current_player(soloplayer);
141 ::game_status = GameStatus::game_redistribute;
142 return ;
143 } // if (not poverty shift)
144
145 ::game_status = GameStatus::game_poverty_shift;
146
147 game.teaminfo().set(soloplayer, Team::re);
148 soloplayer.set_team(Team::re);
149
150 game.swines().test_swines_from_reservations();
151
152 if (!game.isvirtual()) {
153 ::ui->poverty_shifting(soloplayer);
154 this->signal_shifting_(soloplayer);
155 }
156
157 this->shifted_cards_ = soloplayer.poverty_shift();
158
159 DEBUG_ASSERTION((this->shifted_cards().player()
160 == soloplayer),
161 "Game::poverty_shift():\n"
162 " the soloplayer '" << soloplayer.no()
163 << "' shifts cards from player '"
164 << this->shifted_cards().player().no() << "'");
165
166 DEBUG_ASSERTION((soloplayer.hand().count_trumps()
167 == 0),
168 "Game::poverty_shift():\n"
169 " Poverty: the soloplayer still has trump on the hand.\n"
170 "remaining trumps: "
171 << soloplayer.hand().count_trumps() << '\n'
172 << "Hand:\n"
173 << soloplayer.hand()
174 << "shifted cards:\n"
175 << this->shifted_cards());
176
177 DEBUG_ASSERTION( (this->shifted_cardno()
178 + soloplayer.hand().cardsnumber()
179 == rule(Rule::Type::number_of_tricks_in_game)),
180 "Game::poverty_shift():\n"
181 " wrong number of cards shifted: "
182 << this->shifted_cardno());
183
184 if (rule(Rule::Type::poverty_shift_only_trump))
185 DEBUG_ASSERTION(this->shifted_cards().count_trumps()
186 == this->shifted_cardno(),
187 "Game::poverty_shift():\n"
188 " wrong number of cards shifted: "
189 << "trumps = " << this->shifted_cards().count_trumps()
190 << " != " << this->shifted_cardno() << " = cards shifted");
191 else if (!rule(Rule::Type::poverty_fox_do_not_count)
192 || game.swines().swines_announced() )
193 // normal case: shift at max 3 cards
194 DEBUG_ASSERTION(this->shifted_cardno()
195 == rule(Rule::Type::max_number_of_poverty_trumps),
196 "Game::poverty_shift():\n"
197 " wrong number of cards shifted: "
198 << "poverty cards = " << rule(Rule::Type::max_number_of_poverty_trumps)
199 << " != " << this->shifted_cardno() << " = cards shifted");
200 else if (!rule(Rule::Type::poverty_fox_shift_extra))
201 // at max 3 cards or number of trumps
202 DEBUG_ASSERTION( ( (this->shifted_cards().count_trumps()
203 == this->shifted_cardno())
204 || (this->shifted_cardno()
205 == rule(Rule::Type::max_number_of_poverty_trumps)) ),
206 "Game::poverty_shift():\n"
207 " wrong number of cards shifted: "
208 << "trumps or "
209 << "poverty cards = " << rule(Rule::Type::max_number_of_poverty_trumps)
210 << " != " << this->shifted_cardno() << " = cards shifted");
211 else
212 // fox are shifted extra
213 DEBUG_ASSERTION( (this->shifted_cardno()
214 - this->shifted_cards().count_trump_aces()
215 == rule(Rule::Type::max_number_of_poverty_trumps)),
216 "Game::poverty_shift():\n"
217 " wrong number of cards shifted: "
218 << "poverty cards = " << rule(Rule::Type::max_number_of_poverty_trumps)
219 << " != " << this->shifted_cardno() << " - "
220 << this->shifted_cards().count_trump_aces()
221 << " = cards shifted - fox");
222
223 for (auto& p : players)
224 p.poverty_shift(soloplayer, this->shifted_cardno());
225
226 gameplay.gameplay_action(GameplayActions::PovertyShift(soloplayer.no(),
227 this->shifted_cards()));
228 if (!game.isvirtual()) {
229 ::ui->poverty_shift(soloplayer, this->shifted_cardno());
230 this->signal_shift_(soloplayer, this->shifted_cardno());
231 ::ui->gameplay_action(GameplayActions::PovertyShift(soloplayer.no(),
232 this->shifted_cards()));
233 game.signal_gameplay_action_(GameplayActions::PovertyShift(soloplayer.no(),
234 this->shifted_cards()));
235 }
236
237 // ask all players, whether they accept the poverty or not
238 bool accept = false;
239 players.set_current_player(soloplayer);
240 for (players.next_current_player();
241 players.current_player() != soloplayer;
242 players.next_current_player()) {
243 auto& current_player = players.current_player();
244 if (!game.isvirtual()) {
245 ::ui->poverty_ask(current_player, this->shifted_cardno());
246 this->signal_ask_(current_player, this->shifted_cardno());
247 if ( !(::fast_play & FastPlay::pause)
248 && !( ::bug_report_replay
249 && ::bug_report_replay->auto_action())) {
250 if (current_player.type() != Player::Type::human)
251 ::ui->sleep(::preferences(Preferences::Type::card_play_delay));
252 }
253 if (::game_status != GameStatus::game_poverty_shift)
254 return ;
255 } // if (!this->isvirtual())
256
257
258 accept = current_player.poverty_take_accept(this->shifted_cardno());
259 if (::game_status != GameStatus::game_poverty_shift)
260 return ;
261
262 if (accept) {
263 { // test for swines and hyperswines before taking the cards
264 if (rule(Rule::Type::swines)
265 && rule(Rule::Type::swines_announcement_begin)
266 && game.reservation(current_player).swines)
267 game.swines().swines_announce(current_player);
268 if (rule(Rule::Type::hyperswines)
269 && rule(Rule::Type::hyperswines_announcement_begin)
270 && game.reservation(current_player).hyperswines)
271 game.swines().hyperswines_announce(current_player);
272 } // test for swines and hyperswines before taking the cards
273 gameplay.gameplay_action(GameplayActions::PovertyAccepted(current_player.no()));
274 ::ui->gameplay_action(GameplayActions::PovertyAccepted(current_player.no()));
275 game.signal_gameplay_action_(GameplayActions::PovertyAccepted(current_player.no()));
276 break;
277 } else { // if !(accept)
278 for (auto& p : players) {
279 p.poverty_take_denied(current_player);
280 }
281 gameplay.gameplay_action(GameplayActions::PovertyDenied(current_player.no()));
282 if (!game.isvirtual()) {
283 ::ui->poverty_take_denied(current_player);
284 this->signal_take_denied_(current_player);
285 ::ui->gameplay_action(GameplayActions::PovertyDenied(current_player.no()));
286 game.signal_gameplay_action_(GameplayActions::PovertyDenied(current_player.no()));
287 }
288 } // if !(accept)
289 } // for (current_player)
290
291 if (!accept) {
292 players.set_current_player(soloplayer);
293
294 for (auto& p : players)
295 p.poverty_take_denied_by_all();
296 gameplay.gameplay_action(GameplayActions::PovertyDeniedByAll());
297 if (!game.isvirtual()) {
298 ::ui->poverty_take_denied_by_all();
299 this->signal_take_denied_by_all_();
300 ::ui->gameplay_action(GameplayActions::PovertyDeniedByAll());
301 game.signal_gameplay_action_(GameplayActions::PovertyDeniedByAll());
302 }
303
304 soloplayer.hand().add(this->shifted_cards());
305
306 ::game_status = GameStatus::game_redistribute;
307 } else { // if !(!accept)
308 auto& current_player = players.current_player();
309 // set the teaminfo
310 auto& teaminfo = game.teaminfo();
311 teaminfo.set(current_player, Team::re);
312 current_player.set_team(Team::re);
313
314 for (auto& p : players) {
315 if (teaminfo.get(p) == Team::unknown) {
316 teaminfo.set(p, Team::contra);
317 p.set_team(Team::contra);
318 }
319 } // for (p)
320 teaminfo.set_at_gamestart();
321
322 this->returned_cards_
323 = current_player.poverty_cards_change(this->shifted_cards());
324 if (::game_status != GameStatus::game_poverty_shift)
325 return ;
326
327 // check for shifted swines
328 if (rule(Rule::Type::swines)
329 && game.swines().swines_announced()
330 && (game.swines().swines_owner() == soloplayer)) {
331 game.swines().swines_announce(current_player);
332 }
333 if (rule(Rule::Type::hyperswines)
334 && game.swines().hyperswines_announced()
335 && (game.swines().hyperswines_owner() == soloplayer)) {
336 if (!rule(Rule::Type::swines_and_hyperswines_joint)) {
337 game.swines().hyperswines_owner_ = nullptr;
338 game.swines().hyperswines_announced_ = false;
339 }
340 game.swines().hyperswines_announce(current_player);
341 }
342
343 DEBUG_ASSERTION((this->returned_cards().player()
344 == current_player),
345 "Game::poverty_shift():\n"
346 " the current player '" << current_player.no()
347 << "' shifts back cards from player '"
348 << this->returned_cards().player().no() << "'");
349
350 DEBUG_ASSERTION((this->returned_cards().size()
351 == this->shifted_cardno()),
352 "Game::poverty_shift():\n"
353 " number of cards returned ("
354 << this->returned_cards().size()
355 << ") is not the same as are shifted ("
356 << this->shifted_cardno() << ").");
357
358 for (auto& p : players) {
359 p.poverty_take_accepted(current_player,
360 this->returned_cardno(),
361 this->returned_trumpno());
362 }
363 gameplay.gameplay_action(GameplayActions::PovertyReturned(current_player.no(),
364 this->returned_cards()));
365 if (!game.isvirtual()) {
366 ::ui->poverty_take_accepted(current_player,
367 this->returned_cardno(),
368 this->returned_trumpno());
369 this->signal_take_accepted_(current_player,
370 this->returned_cardno(),
371 this->returned_trumpno());
372 ::ui->gameplay_action(GameplayActions::PovertyReturned(current_player.no(),
373 this->returned_cards()));
374 game.signal_gameplay_action_(GameplayActions::PovertyReturned(current_player.no(),
375 this->returned_cards()));
376 }
377
378
379 // add the cards to the player
380 players.set_current_player(soloplayer);
381 soloplayer.poverty_cards_get_back(this->returned_cards());
382 if (!game.isvirtual()) {
383 ::ui->gametype_changed();
384 game.signal_gametype_changed_();
385 }
386
387 if (::game_status != GameStatus::game_poverty_shift)
388 return ;
389
390 } // if !(!accept)
391
392 this->shifted_ = true;
393
394 if (accept) {
395 if (game.swines().swines_announced()) {
396 // check, that the swines announcement is correct
397 auto const& swines_owner = game.swines().swines_owner();
398 if (swines_owner.team() == Team::re) {
399 auto p = container_algorithm::find_if(players,
400 [this](auto player) {
401 return true;
402 });
403
404 if (p != players.end()) {
405 game.swines().swines_announce(*p);
406 } else {
407 game.swines().swines_owner_ = nullptr;
408 game.swines().swines_announced_ = false;
409 }
410 } // if (this->swines_owner().team() == Team::re)
411 } // if (this->swines_owner())
412 } // if (accept)
413
414 if (::game_status == GameStatus::game_poverty_shift)
415 ::game_status = GameStatus::game_reservation;
416 } // void GamePoverty::shift()
417
418
419 /** signals
420 **/
421
422 Signal<void(Player const&)>&
signal_shifting()423 GamePoverty::signal_shifting() noexcept
424 { return this->signal_shifting_; }
425
426 Signal<void(Player const&, unsigned)>&
signal_shift()427 GamePoverty::signal_shift() noexcept
428 { return this->signal_shift_; }
429
430 Signal<void(Player const&, unsigned)>&
signal_ask()431 GamePoverty::signal_ask() noexcept
432 { return this->signal_ask_; }
433
434 Signal<void(Player const&)>&
signal_take_denied()435 GamePoverty::signal_take_denied() noexcept
436 { return this->signal_take_denied_; }
437
438 Signal<void()>&
signal_take_denied_by_all()439 GamePoverty::signal_take_denied_by_all() noexcept
440 { return this->signal_take_denied_by_all_; }
441
442 Signal<void(Player const&, unsigned, unsigned)>&
signal_take_accepted()443 GamePoverty::signal_take_accepted() noexcept
444 { return this->signal_take_accepted_; }
445
446 Signal<void(Player const&)>&
signal_get_cards_to_shift()447 GamePoverty::signal_get_cards_to_shift() noexcept
448 { return this->signal_get_cards_to_shift_; }
449
450 Signal<void(Player const&, unsigned)>&
signal_get_take_accept()451 GamePoverty::signal_get_take_accept() noexcept
452 { return this->signal_get_take_accept_; }
453
454 Signal<void(Player const&, HandCards)>&
signal_get_exchanged_cards()455 GamePoverty::signal_get_exchanged_cards() noexcept
456 { return this->signal_get_exchanged_cards_; }
457
458 Signal<void(Player const&, HandCards)>&
signal_cards_get_back()459 GamePoverty::signal_cards_get_back() noexcept
460 { return this->signal_cards_get_back_; }
461