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.
7 
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 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 /* utility */
19 #include "fcintl.h"
20 #include "log.h"
21 #include "rand.h"
22 #include "shared.h"
23 
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"
32 
33 #include "achievements.h"
34 
35 static struct achievement achievements[MAX_ACHIEVEMENT_TYPES];
36 
37 /****************************************************************
38   Initialize achievements.
39 ****************************************************************/
achievements_init(void)40 void achievements_init(void)
41 {
42   int i;
43 
44   for (i = 0; i < ARRAY_SIZE(achievements); i++) {
45     achievements[i].id = i;
46     achievements[i].first = NULL;
47     achievements[i].value = 0;
48     achievements[i].culture = 0;
49     BV_CLR_ALL(achievements[i].achievers);
50     achievements[i].first_msg = NULL;
51     achievements[i].cons_msg = NULL;
52   }
53 }
54 
55 /****************************************************************************
56   Free the memory associated with achievements
57 ****************************************************************************/
achievements_free(void)58 void achievements_free(void)
59 {
60   int i;
61 
62   for (i = 0; i < ARRAY_SIZE(achievements); i++) {
63     if (achievements[i].first_msg != NULL) {
64       FC_FREE(achievements[i].first_msg);
65     }
66     if (achievements[i].cons_msg != NULL) {
67       FC_FREE(achievements[i].cons_msg);
68     }
69   }
70 }
71 
72 /**************************************************************************
73   Return the achievement id.
74 **************************************************************************/
achievement_number(const struct achievement * pach)75 int achievement_number(const struct achievement *pach)
76 {
77   fc_assert_ret_val(NULL != pach, -1);
78 
79   return pach->id;
80 }
81 
82 /**************************************************************************
83   Return the achievement index.
84 **************************************************************************/
achievement_index(const struct achievement * pach)85 int achievement_index(const struct achievement *pach)
86 {
87   fc_assert_ret_val(NULL != pach, -1);
88 
89   return pach - achievements;
90 }
91 
92 /****************************************************************************
93   Return achievements of given id.
94 ****************************************************************************/
achievement_by_number(int id)95 struct achievement *achievement_by_number(int id)
96 {
97   fc_assert_ret_val(id >= 0 && id < game.control.num_achievement_types, NULL);
98 
99   return &achievements[id];
100 }
101 
102 /****************************************************************************
103   Return translated name of this achievement type.
104 ****************************************************************************/
achievement_name_translation(struct achievement * pach)105 const char *achievement_name_translation(struct achievement *pach)
106 {
107   return name_translation_get(&pach->name);
108 }
109 
110 /****************************************************************************
111   Return untranslated name of this achievement type.
112 ****************************************************************************/
achievement_rule_name(struct achievement * pach)113 const char *achievement_rule_name(struct achievement *pach)
114 {
115   return rule_name_get(&pach->name);
116 }
117 
118 /**************************************************************************
119   Returns achievement matching rule name or NULL if there is no achievement
120   with such name.
121 **************************************************************************/
achievement_by_rule_name(const char * name)122 struct achievement *achievement_by_rule_name(const char *name)
123 {
124   const char *qs = Qn_(name);
125 
126   achievements_iterate(pach) {
127     if (!fc_strcasecmp(achievement_rule_name(pach), qs)) {
128       return pach;
129     }
130   } achievements_iterate_end;
131 
132   return NULL;
133 }
134 
135 /****************************************************************************
136   Check if some player has now achieved the achievement and return the player
137   in question.
138 ****************************************************************************/
achievement_plr(struct achievement * ach,struct player_list * achievers)139 struct player *achievement_plr(struct achievement *ach,
140                                struct player_list *achievers)
141 {
142   struct player *credited = NULL;
143 
144   players_iterate(pplayer) {
145     if (achievement_check(ach, pplayer)) {
146       if (!ach->unique) {
147         pplayer->history += ach->culture;
148         BV_SET(ach->achievers, player_index(pplayer));
149       }
150       player_list_append(achievers, pplayer);
151     }
152   } players_iterate_end;
153 
154   if (ach->first != NULL) {
155     /* Already have first one credited. */
156     return NULL;
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 = player_list_get(achievers, fc_rand(player_list_size(achievers)));
163 
164     ach->first = credited;
165 
166     if (ach->unique) {
167       /* For !ach->unique achievements culture was already added above. */
168       credited->history += ach->culture;
169     }
170 
171     /* Mark the selected player as the only one having the achievement */
172     BV_SET(ach->achievers, player_index(credited));
173   }
174 
175   return credited;
176 }
177 
178 /****************************************************************************
179   Check if player has now achieved the achievement.
180 ****************************************************************************/
achievement_check(struct achievement * ach,struct player * pplayer)181 bool achievement_check(struct achievement *ach, struct player *pplayer)
182 {
183   if ((ach->unique && ach->first != NULL)
184       || (BV_ISSET(ach->achievers, player_index(pplayer)))) {
185     /* It was already achieved */
186     return FALSE;
187   }
188 
189   switch(ach->type) {
190   case ACHIEVEMENT_SPACESHIP:
191     return pplayer->spaceship.state == SSHIP_LAUNCHED;
192   case ACHIEVEMENT_MAP:
193     {
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(ptile) {
212         bool this_is_known = FALSE;
213 
214         if (is_server()) {
215           if (dbv_isset(&pplayer->tile_known, tile_index(ptile))) {
216             this_is_known = TRUE;
217           }
218         } else {
219           /* Client */
220           if (ptile->terrain != T_UNKNOWN) {
221             this_is_known = TRUE;
222           }
223         }
224 
225         if (this_is_known) {
226           known++;
227           if (known >= required) {
228             return TRUE;
229           }
230         } else {
231           unknown++;
232           if (unknown >= max_unknown) {
233             return FALSE;
234           }
235         }
236       } whole_map_iterate_end;
237     }
238 
239     return FALSE;
240   case ACHIEVEMENT_MULTICULTURAL:
241     {
242       bv_player seen_citizens;
243       int count = 0;
244 
245       BV_CLR_ALL(seen_citizens);
246 
247       city_list_iterate(pplayer->cities, pcity) {
248         citizens_iterate(pcity, pslot, pnat) {
249           int idx = player_index(player_slot_get_player(pslot));
250 
251           if (!BV_ISSET(seen_citizens, idx)) {
252             BV_SET(seen_citizens, idx);
253             count++;
254             if (count >= ach->value) {
255               /* There's at least value different nationalities. */
256               return TRUE;
257             }
258           }
259         } citizens_iterate_end;
260       } city_list_iterate_end;
261     }
262 
263     return FALSE;
264   case ACHIEVEMENT_CULTURED_CITY:
265     city_list_iterate(pplayer->cities, pcity) {
266       if (city_culture(pcity) >= ach->value) {
267         return TRUE;
268       }
269     } city_list_iterate_end;
270 
271     return FALSE;
272   case ACHIEVEMENT_CULTURED_NATION:
273     if (player_culture(pplayer) >= ach->value) {
274       return TRUE;
275     }
276 
277     return FALSE;
278   case ACHIEVEMENT_LUCKY:
279     return (fc_rand(10000) < ach->value);
280   case ACHIEVEMENT_HUTS:
281     return pplayer->server.huts >= ach->value;
282   case ACHIEVEMENT_METROPOLIS:
283     city_list_iterate(pplayer->cities, pcity) {
284       if (city_size_get(pcity) >= ach->value) {
285         return TRUE;
286       }
287     } city_list_iterate_end;
288 
289     return FALSE;
290   case ACHIEVEMENT_LITERATE:
291     return get_literacy(pplayer) >= ach->value;
292   case ACHIEVEMENT_LAND_AHOY:
293     {
294       bool seen[game.map.num_continents];
295       int i;
296       int count = 0;
297 
298       for (i = 0; i < game.map.num_continents; i++) {
299         seen[i] = FALSE;
300       }
301 
302       whole_map_iterate(ptile) {
303         bool this_is_known = FALSE;
304 
305         if (is_server()) {
306           if (dbv_isset(&pplayer->tile_known, tile_index(ptile))) {
307             this_is_known = TRUE;
308           }
309         } else {
310           /* Client */
311           if (ptile->terrain != T_UNKNOWN) {
312             this_is_known = TRUE;
313           }
314         }
315 
316         if (this_is_known) {
317           /* FIXME: This makes the assumption that fogged tiles belonged
318            *        to their current continent when they were last seen. */
319           if (ptile->continent > 0 && !seen[ptile->continent - 1]) {
320             if (++count >= ach->value) {
321               return TRUE;
322             }
323             seen[ptile->continent - 1] = TRUE;
324           }
325         }
326       } whole_map_iterate_end;
327 
328       return FALSE;
329     }
330   case ACHIEVEMENT_COUNT:
331     break;
332   }
333 
334   log_error("achievement_check(): Illegal achievement type %d", ach->type);
335 
336   return FALSE;
337 }
338 
339 /****************************************************************************
340   Return message to send to first player gaining the achievement.
341 ****************************************************************************/
achievement_first_msg(struct achievement * pach)342 const char *achievement_first_msg(struct achievement *pach)
343 {
344   fc_assert(pach->first_msg != NULL);
345 
346   return _(pach->first_msg);
347 }
348 
349 /****************************************************************************
350   Return message to send to other players gaining the achievement.
351 ****************************************************************************/
achievement_later_msg(struct achievement * pach)352 const char *achievement_later_msg(struct achievement *pach)
353 {
354   fc_assert(pach->cons_msg != NULL);
355 
356   return _(pach->cons_msg);
357 }
358 
359 /****************************************************************************
360   Has the given player got the achievement?
361 ****************************************************************************/
achievement_player_has(const struct achievement * pach,const struct player * pplayer)362 bool achievement_player_has(const struct achievement *pach,
363                             const struct player *pplayer)
364 {
365   if (pplayer == NULL) {
366     return FALSE;
367   }
368 
369   return BV_ISSET(pach->achievers, player_index(pplayer));
370 }
371 
372 /****************************************************************************
373   Has anybody got the achievement?
374 ****************************************************************************/
achievement_claimed(const struct achievement * pach)375 bool achievement_claimed(const struct achievement *pach)
376 {
377   return pach->first != NULL;
378 }
379 
380 /****************************************************************************
381   Literacy score calculated one way. See also get_literacy2() for
382   alternative way.
383 ****************************************************************************/
get_literacy(const struct player * pplayer)384 int get_literacy(const struct player *pplayer)
385 {
386   int pop = pplayer->score.population;
387 
388   if (pop <= 0) {
389     return 0;
390   } else if (pop >= 10000) {
391     return pplayer->score.literacy / (pop / 100);
392   } else {
393     return (pplayer->score.literacy * 100) / pop;
394   }
395 }
396