Freeciv21
Develop your civilization from humble roots to a global empire
achievements.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 #include <QBitArray>
14 #include <vector>
15 
16 // utility
17 #include "fcintl.h"
18 #include "log.h"
19 #include "rand.h"
20 #include "shared.h"
21 
22 // common
23 #include "citizens.h"
24 #include "city.h"
25 #include "culture.h"
26 #include "game.h"
27 #include "map.h"
28 #include "player.h"
29 #include "spaceship.h"
30 
31 #include "achievements.h"
32 
34 
39 {
40  int i;
41 
42  for (i = 0; i < ARRAY_SIZE(achievements); i++) {
43  achievements[i].id = i;
44  achievements[i].ruledit_disabled = false;
45  achievements[i].first = nullptr;
46  achievements[i].value = 0;
47  achievements[i].culture = 0;
49  achievements[i].first_msg = nullptr;
50  achievements[i].cons_msg = nullptr;
51  }
52 }
53 
58 {
59  int i;
60 
61  for (i = 0; i < ARRAY_SIZE(achievements); i++) {
62  delete[] achievements[i].first_msg;
63  delete[] achievements[i].cons_msg;
64  }
65 }
66 
70 int achievement_number(const struct achievement *pach)
71 {
72  fc_assert_ret_val(nullptr != pach, 0);
73 
74  return pach->id;
75 }
76 
80 int achievement_index(const struct achievement *pach)
81 {
82  fc_assert_ret_val(nullptr != pach, 0);
83 
84  return pach - achievements;
85 }
86 
91 {
92  fc_assert_ret_val(id >= 0 && id < game.control.num_achievement_types,
93  nullptr);
94 
95  return &achievements[id];
96 }
97 
101 const char *achievement_name_translation(struct achievement *pach)
102 {
103  return name_translation_get(&pach->name);
104 }
105 
109 const char *achievement_rule_name(struct achievement *pach)
110 {
111  return rule_name_get(&pach->name);
112 }
113 
119 {
120  const char *qs = Qn_(name);
121 
123  {
124  if (!fc_strcasecmp(achievement_rule_name(pach), qs)) {
125  return pach;
126  }
127  }
129 
130  return nullptr;
131 }
132 
137 struct player *achievement_plr(struct achievement *ach,
138  struct player_list *achievers)
139 {
140  struct player *credited = nullptr;
141 
142  players_iterate(pplayer)
143  {
144  if (achievement_check(ach, pplayer)) {
145  if (!ach->unique) {
146  pplayer->history += ach->culture;
147  BV_SET(ach->achievers, player_index(pplayer));
148  }
149  player_list_append(achievers, pplayer);
150  }
151  }
153 
154  if (ach->first != nullptr) {
155  // Already have first one credited.
156  return nullptr;
157  }
158 
159  if (player_list_size(achievers) > 0) {
160  /* If multiple players achieved at the same turn, randomly select one
161  * as the one who won the race. */
162  credited =
163  player_list_get(achievers, fc_rand(player_list_size(achievers)));
164 
165  ach->first = credited;
166 
167  if (ach->unique) {
168  // For !ach->unique achievements culture was already added above.
169  credited->history += ach->culture;
170  }
171 
172  // Mark the selected player as the only one having the achievement
173  BV_SET(ach->achievers, player_index(credited));
174  }
175 
176  return credited;
177 }
178 
182 bool achievement_check(struct achievement *ach, struct player *pplayer)
183 {
184  if ((ach->unique && ach->first != nullptr)
185  || (BV_ISSET(ach->achievers, player_index(pplayer)))) {
186  // It was already achieved
187  return false;
188  }
189 
190  switch (ach->type) {
191  case ACHIEVEMENT_SPACESHIP:
192  return pplayer->spaceship.state == SSHIP_LAUNCHED;
193  case ACHIEVEMENT_MAP: {
194  int max_unknown;
195  int required;
196  int total;
197  int known = 0;
198  int unknown = 0;
199 
200  /* We calculate max_unknown first for getting the
201  * rounding correctly.
202  * Consider 50 tile map from which we want 25% known.
203  * 50 * 25% = 12.5. Would we round that number of tiles
204  * down, we would get < 25% that's minimum requirement.
205  * Instead we round down (50 - 12.5 = 37.5) -> 37 and then
206  * get the minimum number of full tiles as 50 - 37 = 13. */
207  total = map_num_tiles();
208  max_unknown = (total * (100 - ach->value)) / 100;
209  required = total - max_unknown;
210 
211  whole_map_iterate(&(wld.map), ptile)
212  {
213  bool this_is_known = false;
214 
215  if (is_server()) {
216  if (pplayer->tile_known->at(tile_index(ptile))) {
217  this_is_known = true;
218  }
219  } else {
220  // Client
221  if (ptile->terrain != T_UNKNOWN) {
222  this_is_known = true;
223  }
224  }
225 
226  if (this_is_known) {
227  known++;
228  if (known >= required) {
229  return true;
230  }
231  } else {
232  unknown++;
233  if (unknown >= max_unknown) {
234  return false;
235  }
236  }
237  }
239  }
240 
241  return false;
242  case ACHIEVEMENT_MULTICULTURAL: {
243  bv_player seen_citizens;
244  int count = 0;
245 
246  BV_CLR_ALL(seen_citizens);
247 
248  city_list_iterate(pplayer->cities, pcity)
249  {
250  citizens_iterate(pcity, pslot, pnat)
251  {
252  int idx = player_index(player_slot_get_player(pslot));
253 
254  if (!BV_ISSET(seen_citizens, idx)) {
255  BV_SET(seen_citizens, idx);
256  count++;
257  if (count >= ach->value) {
258  // There's at least value different nationalities.
259  return true;
260  }
261  }
262  }
264  }
266  }
267 
268  return false;
269  case ACHIEVEMENT_CULTURED_CITY:
270  city_list_iterate(pplayer->cities, pcity)
271  {
272  if (city_culture(pcity) >= ach->value) {
273  return true;
274  }
275  }
277 
278  return false;
279  case ACHIEVEMENT_CULTURED_NATION:
280  if (player_culture(pplayer) >= ach->value) {
281  return true;
282  }
283 
284  return false;
285  case ACHIEVEMENT_LUCKY:
286  return (static_cast<int>(fc_rand(10000)) < ach->value);
287  case ACHIEVEMENT_HUTS:
288  return pplayer->server.huts >= ach->value;
289  case ACHIEVEMENT_METROPOLIS:
290  city_list_iterate(pplayer->cities, pcity)
291  {
292  if (city_size_get(pcity) >= ach->value) {
293  return true;
294  }
295  }
297 
298  return false;
299  case ACHIEVEMENT_LITERATE:
300  return get_literacy(pplayer) >= ach->value;
301  case ACHIEVEMENT_LAND_AHOY: {
302  std::vector<bool> seen(wld.map.num_continents);
303  int count = 0;
304 
305  whole_map_iterate(&(wld.map), ptile)
306  {
307  bool this_is_known = false;
308 
309  if (is_server()) {
310  if (pplayer->tile_known->at(tile_index(ptile))) {
311  this_is_known = true;
312  }
313  } else {
314  // Client
315  if (ptile->terrain != T_UNKNOWN) {
316  this_is_known = true;
317  }
318  }
319 
320  if (this_is_known) {
321  /* FIXME: This makes the assumption that fogged tiles belonged
322  * to their current continent when they were last seen. */
323  if (ptile->continent > 0 && !seen[ptile->continent - 1]) {
324  if (++count >= ach->value) {
325  return true;
326  }
327  seen[ptile->continent - 1] = true;
328  }
329  }
330  }
332 
333  return false;
334  }
335  case ACHIEVEMENT_COUNT:
336  break;
337  }
338 
339  qCritical("achievement_check(): Illegal achievement type %d", ach->type);
340 
341  return false;
342 }
343 
347 const char *achievement_first_msg(struct achievement *pach)
348 {
349  fc_assert(pach->first_msg != nullptr);
350 
351  return _(pach->first_msg);
352 }
353 
357 const char *achievement_later_msg(struct achievement *pach)
358 {
359  fc_assert(pach->cons_msg != nullptr);
360 
361  return _(pach->cons_msg);
362 }
363 
367 bool achievement_player_has(const struct achievement *pach,
368  const struct player *pplayer)
369 {
370  if (pplayer == nullptr) {
371  return false;
372  }
373 
374  return BV_ISSET(pach->achievers, player_index(pplayer));
375 }
376 
380 bool achievement_claimed(const struct achievement *pach)
381 {
382  return pach->first != nullptr;
383 }
384 
389 int get_literacy(const struct player *pplayer)
390 {
391  int pop = civ_population(pplayer);
392 
393  if (pop <= 0) {
394  return 0;
395  } else if (pop >= 10000) {
396  return pplayer->score.literacy / (pop / 100);
397  } else {
398  return (pplayer->score.literacy * 100) / pop;
399  }
400 }
bool achievement_check(struct achievement *ach, struct player *pplayer)
Check if player has now achieved the achievement.
void achievements_init()
Initialize achievements.
bool achievement_player_has(const struct achievement *pach, const struct player *pplayer)
Has the given player got the achievement?
const char * achievement_first_msg(struct achievement *pach)
Return message to send to first player gaining the achievement.
static struct achievement achievements[MAX_ACHIEVEMENT_TYPES]
const char * achievement_later_msg(struct achievement *pach)
Return message to send to other players gaining the achievement.
int achievement_index(const struct achievement *pach)
Return the achievement index.
struct player * achievement_plr(struct achievement *ach, struct player_list *achievers)
Check if some player has now achieved the achievement and return the player in question.
int get_literacy(const struct player *pplayer)
Literacy score calculated one way.
int achievement_number(const struct achievement *pach)
Return the achievement id.
const char * achievement_rule_name(struct achievement *pach)
Return untranslated name of this achievement type.
void achievements_free()
Free the memory associated with achievements.
struct achievement * achievement_by_number(int id)
Return achievements of given id.
struct achievement * achievement_by_rule_name(const char *name)
Returns achievement matching rule name or nullptr if there is no achievement with such name.
bool achievement_claimed(const struct achievement *pach)
Has anybody got the achievement?
const char * achievement_name_translation(struct achievement *pach)
Return translated name of this achievement type.
#define achievements_iterate_end
Definition: achievements.h:64
#define achievements_iterate(_ach_)
Definition: achievements.h:58
#define BV_CLR_ALL(bv)
Definition: bitvector.h:62
#define BV_SET(bv, bit)
Definition: bitvector.h:44
bool BV_ISSET(const BV &bv, int bit)
Definition: bitvector.h:37
#define citizens_iterate_end
Definition: citizens.h:44
#define citizens_iterate(_pcity, _pslot, _nationality)
Definition: citizens.h:37
citizens city_size_get(const struct city *pcity)
Get the city size.
Definition: city.cpp:1101
#define city_list_iterate(citylist, pcity)
Definition: city.h:482
#define city_list_iterate_end
Definition: city.h:484
int city_culture(const struct city *pcity)
Return current culture score of the city.
Definition: culture.cpp:23
int player_culture(const struct player *plr)
Return current culture score of the player.
Definition: culture.cpp:40
#define MAX_ACHIEVEMENT_TYPES
Definition: fc_types.h:47
#define _(String)
Definition: fcintl.h:50
#define Qn_(String)
Definition: fcintl.h:66
struct civ_game game
Definition: game.cpp:47
bool is_server()
Is program type server?
Definition: game.cpp:57
struct world wld
Definition: game.cpp:48
int civ_population(const struct player *pplayer)
Count the # of thousand citizen in a civilisation.
Definition: game.cpp:72
const char * name
Definition: inputfile.cpp:118
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
int map_num_tiles()
Returns the total number of (real) positions (or tiles) on the map.
Definition: map.cpp:954
#define whole_map_iterate(_map, _tile)
Definition: map.h:473
#define whole_map_iterate_end
Definition: map.h:480
static const char * rule_name_get(const struct name_translation *ptrans)
static const char * name_translation_get(const struct name_translation *ptrans)
int player_index(const struct player *pplayer)
Return the player index.
Definition: player.cpp:748
struct player * player_slot_get_player(const struct player_slot *pslot)
Returns the team corresponding to the slot.
Definition: player.cpp:384
#define players_iterate_end
Definition: player.h:520
#define players_iterate(_pplayer)
Definition: player.h:514
#define fc_rand(_size)
Definition: rand.h:16
#define ARRAY_SIZE(x)
Definition: shared.h:79
@ SSHIP_LAUNCHED
Definition: spaceship.h:80
char * first_msg
Definition: achievements.h:32
enum achievement_type type
Definition: achievements.h:24
struct player * first
Definition: achievements.h:28
bv_player achievers
Definition: achievements.h:29
char * cons_msg
Definition: achievements.h:33
struct name_translation name
Definition: achievements.h:22
bool ruledit_disabled
Definition: achievements.h:23
struct packet_ruleset_control control
Definition: game.h:74
int num_continents
Definition: map_types.h:74
int literacy
Definition: player.h:93
enum spaceship_state state
Definition: spaceship.h:105
Definition: player.h:231
struct city_list * cities
Definition: player.h:263
struct player::@65::@67 server
struct player_spaceship spaceship
Definition: player.h:268
struct player_score score
Definition: player.h:265
QBitArray * tile_known
Definition: player.h:291
int history
Definition: player.h:300
struct civ_map map
Definition: world_object.h:21
int fc_strcasecmp(const char *str0, const char *str1)
Compare strings like strcmp(), but ignoring case.
Definition: support.cpp:89
#define T_UNKNOWN
Definition: terrain.h:51
#define tile_index(_pt_)
Definition: tile.h:70