Stop sharing requirement_unit_state_ereq().
[freeciv.git] / common / achievements.c
blob06d93b8123013700e38aed3ad031e42a988ab2d4
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 /* utility */
19 #include "fcintl.h"
20 #include "log.h"
21 #include "rand.h"
22 #include "shared.h"
24 /* common */
25 #include "citizens.h"
26 #include "city.h"
27 #include "culture.h"
28 #include "game.h"
29 #include "map.h"
30 #include "player.h"
31 #include "spaceship.h"
33 #include "achievements.h"
35 static struct achievement achievements[MAX_ACHIEVEMENT_TYPES];
37 /****************************************************************
38 Initialize achievements.
39 ****************************************************************/
40 void achievements_init(void)
42 int i;
44 for (i = 0; i < ARRAY_SIZE(achievements); i++) {
45 achievements[i].id = i;
46 achievements[i].disabled = FALSE;
47 achievements[i].first = NULL;
48 achievements[i].value = 0;
49 achievements[i].culture = 0;
50 BV_CLR_ALL(achievements[i].achievers);
51 achievements[i].first_msg = NULL;
52 achievements[i].cons_msg = NULL;
56 /****************************************************************************
57 Free the memory associated with achievements
58 ****************************************************************************/
59 void achievements_free(void)
61 int i;
63 for (i = 0; i < ARRAY_SIZE(achievements); i++) {
64 if (achievements[i].first_msg != NULL) {
65 FC_FREE(achievements[i].first_msg);
67 if (achievements[i].cons_msg != NULL) {
68 FC_FREE(achievements[i].cons_msg);
73 /**************************************************************************
74 Return the achievement id.
75 **************************************************************************/
76 int achievement_number(const struct achievement *pach)
78 fc_assert_ret_val(NULL != pach, -1);
80 return pach->id;
83 /**************************************************************************
84 Return the achievement index.
85 **************************************************************************/
86 int achievement_index(const struct achievement *pach)
88 fc_assert_ret_val(NULL != pach, -1);
90 return pach - achievements;
93 /****************************************************************************
94 Return achievements of given id.
95 ****************************************************************************/
96 struct achievement *achievement_by_number(int id)
98 fc_assert_ret_val(id >= 0 && id < game.control.num_achievement_types, NULL);
100 return &achievements[id];
103 /****************************************************************************
104 Return translated name of this achievement type.
105 ****************************************************************************/
106 const char *achievement_name_translation(struct achievement *pach)
108 return name_translation_get(&pach->name);
111 /****************************************************************************
112 Return untranslated name of this achievement type.
113 ****************************************************************************/
114 const char *achievement_rule_name(struct achievement *pach)
116 return rule_name_get(&pach->name);
119 /**************************************************************************
120 Returns achievement matching rule name or NULL if there is no achievement
121 with such name.
122 **************************************************************************/
123 struct achievement *achievement_by_rule_name(const char *name)
125 const char *qs = Qn_(name);
127 achievements_iterate(pach) {
128 if (!fc_strcasecmp(achievement_rule_name(pach), qs)) {
129 return pach;
131 } achievements_iterate_end;
133 return NULL;
136 /****************************************************************************
137 Check if some player has now achieved the achievement and return the player
138 in question.
139 ****************************************************************************/
140 struct player *achievement_plr(struct achievement *ach,
141 struct player_list *achievers)
143 struct player *credited = NULL;
145 players_iterate(pplayer) {
146 if (achievement_check(ach, pplayer)) {
147 if (!ach->unique) {
148 pplayer->culture += ach->culture;
149 BV_SET(ach->achievers, player_index(pplayer));
151 player_list_append(achievers, pplayer);
153 } players_iterate_end;
155 if (ach->first != NULL) {
156 /* Already have first one credited. */
157 return NULL;
160 if (player_list_size(achievers) > 0) {
161 /* If multiple players achieved at the same turn, randomly select one
162 * as the one who won the race. */
163 credited = player_list_get(achievers, fc_rand(player_list_size(achievers)));
165 ach->first = credited;
166 credited->culture += ach->culture;
168 /* Mark the selected player as the only one having the achievement */
169 BV_SET(ach->achievers, player_index(credited));
172 return credited;
175 /****************************************************************************
176 Check if player has now achieved the achievement.
177 ****************************************************************************/
178 bool achievement_check(struct achievement *ach, struct player *pplayer)
180 if ((ach->unique && ach->first != NULL)
181 || (BV_ISSET(ach->achievers, player_index(pplayer)))) {
182 /* It was already achieved */
183 return FALSE;
186 switch(ach->type) {
187 case ACHIEVEMENT_SPACESHIP:
188 return pplayer->spaceship.state == SSHIP_LAUNCHED;
189 case ACHIEVEMENT_MAP:
191 int max_unknown;
192 int required;
193 int total;
194 int known = 0;
195 int unknown = 0;
197 /* We calculate max_unknown first for getting the
198 * rounding correctly.
199 * Consider 50 tile map from which we want 25% known.
200 * 50 * 25% = 12.5. Would we round that number of tiles
201 * down, we would get < 25% that's minimum requirement.
202 * Instead we round down (50 - 12.5 = 37.5) -> 37 and then
203 * get the minimum number of full tiles as 50 - 37 = 13. */
204 total = map_num_tiles();
205 max_unknown = (total * (100 - ach->value)) / 100;
206 required = total - max_unknown;
208 whole_map_iterate(&(wld.map), ptile) {
209 bool this_is_known = FALSE;
211 if (is_server()) {
212 if (dbv_isset(&pplayer->tile_known, tile_index(ptile))) {
213 this_is_known = TRUE;
215 } else {
216 /* Client */
217 if (ptile->terrain != T_UNKNOWN) {
218 this_is_known = TRUE;
222 if (this_is_known) {
223 known++;
224 if (known >= required) {
225 return TRUE;
227 } else {
228 unknown++;
229 if (unknown >= max_unknown) {
230 return FALSE;
233 } whole_map_iterate_end;
236 return FALSE;
237 case ACHIEVEMENT_MULTICULTURAL:
239 bv_player seen_citizens;
240 int count = 0;
242 BV_CLR_ALL(seen_citizens);
244 city_list_iterate(pplayer->cities, pcity) {
245 citizens_iterate(pcity, pslot, pnat) {
246 int idx = player_index(player_slot_get_player(pslot));
248 if (!BV_ISSET(seen_citizens, idx)) {
249 BV_SET(seen_citizens, idx);
250 count++;
251 if (count >= ach->value) {
252 /* There's at least value different nationalities. */
253 return TRUE;
256 } citizens_iterate_end;
257 } city_list_iterate_end;
260 return FALSE;
261 case ACHIEVEMENT_CULTURED_CITY:
262 city_list_iterate(pplayer->cities, pcity) {
263 if (city_culture(pcity) >= ach->value) {
264 return TRUE;
266 } city_list_iterate_end;
268 return FALSE;
269 case ACHIEVEMENT_CULTURED_NATION:
270 if (player_culture(pplayer) >= ach->value) {
271 return TRUE;
274 return FALSE;
275 case ACHIEVEMENT_LUCKY:
276 return ((int)fc_rand(10000) < ach->value);
277 case ACHIEVEMENT_HUTS:
278 return pplayer->server.huts >= ach->value;
279 case ACHIEVEMENT_METROPOLIS:
280 city_list_iterate(pplayer->cities, pcity) {
281 if (city_size_get(pcity) >= ach->value) {
282 return TRUE;
284 } city_list_iterate_end;
286 return FALSE;
287 case ACHIEVEMENT_LITERATE:
288 return pplayer->score.literacy >= ach->value;
289 case ACHIEVEMENT_LAND_AHOY:
291 bool *seen = fc_calloc(wld.map.num_continents, sizeof(bool));
292 int count = 0;
294 whole_map_iterate(&(wld.map), ptile) {
295 bool this_is_known = FALSE;
297 if (is_server()) {
298 if (dbv_isset(&pplayer->tile_known, tile_index(ptile))) {
299 this_is_known = TRUE;
301 } else {
302 /* Client */
303 if (ptile->terrain != T_UNKNOWN) {
304 this_is_known = TRUE;
308 if (this_is_known) {
309 /* FIXME: This makes the assumption that fogged tiles belonged
310 * to their current continent when they were last seen. */
311 if (ptile->continent > 0 && !seen[ptile->continent]) {
312 if (++count >= ach->value) {
313 free(seen);
314 return TRUE;
316 seen[ptile->continent] = TRUE;
319 } whole_map_iterate_end;
321 free(seen);
322 return FALSE;
324 case ACHIEVEMENT_COUNT:
325 break;
328 log_error("achievement_check(): Illegal achievement type %d", ach->type);
330 return FALSE;
333 /****************************************************************************
334 Return message to send to first player gaining the achievement.
335 ****************************************************************************/
336 const char *achievement_first_msg(struct achievement *pach)
338 fc_assert(pach->first_msg != NULL);
340 return _(pach->first_msg);
343 /****************************************************************************
344 Return message to send to other players gaining the achievement.
345 ****************************************************************************/
346 const char *achievement_later_msg(struct achievement *pach)
348 fc_assert(pach->cons_msg != NULL);
350 return _(pach->cons_msg);
353 /****************************************************************************
354 Has the given player got the achievement?
355 ****************************************************************************/
356 bool achievement_player_has(const struct achievement *pach,
357 const struct player *pplayer)
359 if (pplayer == NULL) {
360 return FALSE;
363 return BV_ISSET(pach->achievers, player_index(pplayer));
366 /****************************************************************************
367 Has anybody got the achievement?
368 ****************************************************************************/
369 bool achievement_claimed(const struct achievement *pach)
371 return pach->first != NULL;