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)
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 ***********************************************************************/
15 #include <fc_config.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)
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)
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);
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
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
)) {
131 } achievements_iterate_end
;
136 /****************************************************************************
137 Check if some player has now achieved the achievement and return the player
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
)) {
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. */
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
));
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 */
187 case ACHIEVEMENT_SPACESHIP
:
188 return pplayer
->spaceship
.state
== SSHIP_LAUNCHED
;
189 case ACHIEVEMENT_MAP
:
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
;
212 if (dbv_isset(&pplayer
->tile_known
, tile_index(ptile
))) {
213 this_is_known
= TRUE
;
217 if (ptile
->terrain
!= T_UNKNOWN
) {
218 this_is_known
= TRUE
;
224 if (known
>= required
) {
229 if (unknown
>= max_unknown
) {
233 } whole_map_iterate_end
;
237 case ACHIEVEMENT_MULTICULTURAL
:
239 bv_player seen_citizens
;
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
);
251 if (count
>= ach
->value
) {
252 /* There's at least value different nationalities. */
256 } citizens_iterate_end
;
257 } city_list_iterate_end
;
261 case ACHIEVEMENT_CULTURED_CITY
:
262 city_list_iterate(pplayer
->cities
, pcity
) {
263 if (city_culture(pcity
) >= ach
->value
) {
266 } city_list_iterate_end
;
269 case ACHIEVEMENT_CULTURED_NATION
:
270 if (player_culture(pplayer
) >= ach
->value
) {
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
) {
284 } city_list_iterate_end
;
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));
294 whole_map_iterate(&(wld
.map
), ptile
) {
295 bool this_is_known
= FALSE
;
298 if (dbv_isset(&pplayer
->tile_known
, tile_index(ptile
))) {
299 this_is_known
= TRUE
;
303 if (ptile
->terrain
!= T_UNKNOWN
) {
304 this_is_known
= TRUE
;
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
) {
316 seen
[ptile
->continent
] = TRUE
;
319 } whole_map_iterate_end
;
324 case ACHIEVEMENT_COUNT
:
328 log_error("achievement_check(): Illegal achievement type %d", ach
->type
);
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
) {
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
;