1 /**********************************************************************
2  Freeciv - Copyright (C) 2005 - The Freeciv Project
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 #include <stdlib.h>
19 
20 /* utility */
21 #include "fcintl.h"
22 #include "log.h"
23 #include "shared.h"
24 #include "support.h"
25 
26 /* common */
27 #include "game.h"
28 #include "player.h"
29 #include "team.h"
30 
31 struct team_slot {
32   struct team *team;
33   char *defined_name;                   /* Defined by the ruleset. */
34   char *rule_name;                      /* Usable untranslated name. */
35 #ifdef ENABLE_NLS
36   char *name_translation;               /* Translated name. */
37 #endif
38 };
39 
40 struct team {
41   struct player_list *plrlist;
42   struct team_slot *slot;
43 };
44 
45 static struct {
46   struct team_slot *slots;
47   int used_slots;
48 } team_slots;
49 
50 /****************************************************************************
51   Initialise all team slots.
52 ****************************************************************************/
team_slots_init(void)53 void team_slots_init(void)
54 {
55   int i;
56 
57   /* Init team slots and names. */
58   team_slots.slots = fc_calloc(team_slot_count(), sizeof(*team_slots.slots));
59   /* Can't use the defined functions as the needed data will be
60    * defined here. */
61   for (i = 0; i < team_slot_count(); i++) {
62     struct team_slot *tslot = team_slots.slots + i;
63 
64     tslot->team = NULL;
65     tslot->defined_name = NULL;
66     tslot->rule_name = NULL;
67 #ifdef ENABLE_NLS
68     tslot->name_translation = NULL;
69 #endif
70   }
71   team_slots.used_slots = 0;
72 }
73 
74 /****************************************************************************
75   Returns TRUE if the team slots have been initialized.
76 ****************************************************************************/
team_slots_initialised(void)77 bool team_slots_initialised(void)
78 {
79   return (team_slots.slots != NULL);
80 }
81 
82 /****************************************************************************
83   Remove all team slots.
84 ****************************************************************************/
team_slots_free(void)85 void team_slots_free(void)
86 {
87   team_slots_iterate(tslot) {
88     if (NULL != tslot->team) {
89       team_destroy(tslot->team);
90     }
91     if (NULL != tslot->defined_name) {
92       free(tslot->defined_name);
93     }
94     if (NULL != tslot->rule_name) {
95       free(tslot->rule_name);
96     }
97 #ifdef ENABLE_NLS
98     if (NULL != tslot->name_translation) {
99       free(tslot->name_translation);
100     }
101 #endif
102   } team_slots_iterate_end;
103   free(team_slots.slots);
104   team_slots.slots = NULL;
105 
106   team_slots.used_slots = 0;
107 }
108 
109 /****************************************************************************
110   Returns the total number of team slots (including used slots).
111 ****************************************************************************/
team_slot_count(void)112 int team_slot_count(void)
113 {
114   return (MAX_NUM_TEAM_SLOTS);
115 }
116 
117 /****************************************************************************
118   Returns the first team slot.
119 ****************************************************************************/
team_slot_first(void)120 struct team_slot *team_slot_first(void)
121 {
122   return team_slots.slots;
123 }
124 
125 /****************************************************************************
126   Returns the next team slot.
127 ****************************************************************************/
team_slot_next(struct team_slot * tslot)128 struct team_slot *team_slot_next(struct team_slot *tslot)
129 {
130   tslot++;
131   return (tslot < team_slots.slots + team_slot_count() ? tslot : NULL);
132 }
133 
134 
135 /****************************************************************************
136   Returns the index of the team slots.
137 ****************************************************************************/
team_slot_index(const struct team_slot * tslot)138 int team_slot_index(const struct team_slot *tslot)
139 {
140   fc_assert_ret_val(team_slots_initialised(), -1);
141   fc_assert_ret_val(tslot != NULL, -1);
142 
143   return tslot - team_slots.slots;
144 }
145 
146 /****************************************************************************
147   Returns the team corresponding to the slot. If the slot is not used, it
148   will return NULL. See also team_slot_is_used().
149 ****************************************************************************/
team_slot_get_team(const struct team_slot * tslot)150 struct team *team_slot_get_team(const struct team_slot *tslot)
151 {
152   fc_assert_ret_val(team_slots_initialised(), NULL);
153   fc_assert_ret_val(tslot != NULL, NULL);
154 
155   return tslot->team;
156 }
157 
158 /****************************************************************************
159   Returns TRUE is this slot is "used" i.e. corresponds to a
160   valid, initialized team that exists in the game.
161 ****************************************************************************/
team_slot_is_used(const struct team_slot * tslot)162 bool team_slot_is_used(const struct team_slot *tslot)
163 {
164   /* No team slot available, if the game is not initialised. */
165   if (!team_slots_initialised()) {
166     return FALSE;
167   }
168 
169   return NULL != tslot->team;
170 }
171 
172 /****************************************************************************
173   Return the possibly unused and uninitialized team slot.
174 ****************************************************************************/
team_slot_by_number(int team_id)175 struct team_slot *team_slot_by_number(int team_id)
176 {
177   if (!team_slots_initialised()
178       || !(0 <= team_id && team_id < team_slot_count())) {
179     return NULL;
180   }
181 
182   return team_slots.slots + team_id;
183 }
184 
185 /****************************************************************************
186   Does a linear search for a (defined) team name. Returns NULL when none
187   match.
188 ****************************************************************************/
team_slot_by_rule_name(const char * team_name)189 struct team_slot *team_slot_by_rule_name(const char *team_name)
190 {
191   fc_assert_ret_val(team_name != NULL, NULL);
192 
193   team_slots_iterate(tslot) {
194     const char *tname = team_slot_rule_name(tslot);
195 
196     if (NULL != tname && 0 == fc_strcasecmp(tname, team_name)) {
197       return tslot;
198     }
199   } team_slots_iterate_end;
200 
201   return NULL;
202 }
203 
204 /****************************************************************************
205   Creates a default name for this team slot.
206 ****************************************************************************/
team_slot_create_default_name(struct team_slot * tslot)207 static inline void team_slot_create_default_name(struct team_slot *tslot)
208 {
209   char buf[MAX_LEN_NAME];
210 
211   fc_assert(NULL == tslot->defined_name);
212   fc_assert(NULL == tslot->rule_name);
213 #ifdef ENABLE_NLS
214   fc_assert(NULL == tslot->name_translation);
215 #endif
216 
217   fc_snprintf(buf, sizeof(buf), "Team %d", team_slot_index(tslot));
218   tslot->rule_name = fc_strdup(buf);
219 
220 #ifdef ENABLE_NLS
221   fc_snprintf(buf, sizeof(buf), _("Team %d"), team_slot_index(tslot));
222   tslot->name_translation = fc_strdup(buf);
223 #endif
224 
225   log_verbose("No name defined for team %d! Creating a default name: %s.",
226               team_slot_index(tslot), tslot->rule_name);
227 }
228 
229 /****************************************************************************
230   Returns the name (untranslated) of the slot. Creates a default one if it
231   doesn't exist currently.
232 ****************************************************************************/
team_slot_rule_name(const struct team_slot * tslot)233 const char *team_slot_rule_name(const struct team_slot *tslot)
234 {
235   fc_assert_ret_val(team_slots_initialised(), NULL);
236   fc_assert_ret_val(NULL != tslot, NULL);
237 
238   if (NULL == tslot->rule_name) {
239     /* Get the team slot as changeable (not _const_) struct. */
240     struct team_slot *changeable
241       = team_slot_by_number(team_slot_index(tslot));
242     team_slot_create_default_name(changeable);
243     return changeable->rule_name;
244   }
245 
246   return tslot->rule_name;
247 }
248 
249 /****************************************************************************
250   Returns the name (translated) of the slot. Creates a default one if it
251   doesn't exist currently.
252 ****************************************************************************/
team_slot_name_translation(const struct team_slot * tslot)253 const char *team_slot_name_translation(const struct team_slot *tslot)
254 {
255 #ifdef ENABLE_NLS
256   fc_assert_ret_val(team_slots_initialised(), NULL);
257   fc_assert_ret_val(NULL != tslot, NULL);
258 
259   if (NULL == tslot->name_translation) {
260     /* Get the team slot as changeable (not _const_) struct. */
261     struct team_slot *changeable
262       = team_slot_by_number(team_slot_index(tslot));
263     team_slot_create_default_name(changeable);
264     return changeable->name_translation;
265   }
266 
267   return tslot->name_translation;
268 #else
269   return team_slot_rule_name(tslot);
270 #endif /* ENABLE_NLS */
271 }
272 
273 /****************************************************************************
274   Returns the name defined in the ruleset for this slot. It may return NULL
275   if the ruleset didn't defined a such name.
276 ****************************************************************************/
team_slot_defined_name(const struct team_slot * tslot)277 const char *team_slot_defined_name(const struct team_slot *tslot)
278 {
279   fc_assert_ret_val(team_slots_initialised(), NULL);
280   fc_assert_ret_val(NULL != tslot, NULL);
281 
282   return tslot->defined_name;
283 }
284 
285 /****************************************************************************
286   Set the name defined in the ruleset for this slot.
287 ****************************************************************************/
team_slot_set_defined_name(struct team_slot * tslot,const char * team_name)288 void team_slot_set_defined_name(struct team_slot *tslot,
289                                 const char *team_name)
290 {
291   fc_assert_ret(team_slots_initialised());
292   fc_assert_ret(NULL != tslot);
293   fc_assert_ret(NULL != team_name);
294 
295   if (NULL != tslot->defined_name) {
296     free(tslot->defined_name);
297   }
298   tslot->defined_name = fc_strdup(team_name);
299 
300   if (NULL != tslot->rule_name) {
301     free(tslot->rule_name);
302   }
303   tslot->rule_name = fc_strdup(Qn_(team_name));
304 
305 #ifdef ENABLE_NLS
306   if (NULL != tslot->name_translation) {
307     free(tslot->name_translation);
308   }
309   tslot->name_translation = fc_strdup(Q_(team_name));
310 #endif
311 }
312 
313 
314 /****************************************************************************
315   Creates a new team for the slot. If slot is NULL, it will lookup to a
316   free slot. If the slot already used, then just return the team.
317 ****************************************************************************/
team_new(struct team_slot * tslot)318 struct team *team_new(struct team_slot *tslot)
319 {
320   struct team *pteam;
321 
322   fc_assert_ret_val(team_slots_initialised(), NULL);
323 
324   if (NULL == tslot) {
325     team_slots_iterate(aslot) {
326       if (!team_slot_is_used(aslot)) {
327         tslot = aslot;
328         break;
329       }
330     } team_slots_iterate_end;
331 
332     fc_assert_ret_val(NULL != tslot, NULL);
333   } else if (NULL != tslot->team) {
334     return tslot->team;
335   }
336 
337   /* Now create the team. */
338   log_debug("Create team for slot %d.", team_slot_index(tslot));
339   pteam = fc_calloc(1, sizeof(*pteam));
340   pteam->slot = tslot;
341   tslot->team = pteam;
342 
343   /* Set default values. */
344   pteam->plrlist = player_list_new();
345 
346   /* Increase number of teams. */
347   team_slots.used_slots++;
348 
349   return pteam;
350 }
351 
352 /****************************************************************************
353   Destroys a team.
354 ****************************************************************************/
team_destroy(struct team * pteam)355 void team_destroy(struct team *pteam)
356 {
357   struct team_slot *tslot;
358 
359   fc_assert_ret(team_slots_initialised());
360   fc_assert_ret(NULL != pteam);
361   fc_assert(0 == player_list_size(pteam->plrlist));
362 
363   tslot = pteam->slot;
364   fc_assert(tslot->team == pteam);
365 
366   player_list_destroy(pteam->plrlist);
367   free(pteam);
368   tslot->team = NULL;
369   team_slots.used_slots--;
370 }
371 
372 /****************************************************************************
373   Return the current number of teams.
374 ****************************************************************************/
team_count(void)375 int team_count(void)
376 {
377   return team_slots.used_slots;
378 }
379 
380 /****************************************************************************
381   Return the team index.
382 ****************************************************************************/
team_index(const struct team * pteam)383 int team_index(const struct team *pteam)
384 {
385   return team_number(pteam);
386 }
387 
388 /****************************************************************************
389   Return the team index/number/id.
390 ****************************************************************************/
team_number(const struct team * pteam)391 int team_number(const struct team *pteam)
392 {
393   fc_assert_ret_val(NULL != pteam, -1);
394   return team_slot_index(pteam->slot);
395 }
396 
397 /****************************************************************************
398   Return struct team pointer for the given team index.
399 ****************************************************************************/
team_by_number(const int team_id)400 struct team *team_by_number(const int team_id)
401 {
402   const struct team_slot *tslot = team_slot_by_number(team_id);
403 
404   return (NULL != tslot ? team_slot_get_team(tslot) : NULL);
405 }
406 
407 /****************************************************************************
408   Returns the name (untranslated) of the team.
409 ****************************************************************************/
team_rule_name(const struct team * pteam)410 const char *team_rule_name(const struct team *pteam)
411 {
412   fc_assert_ret_val(NULL != pteam, NULL);
413 
414   return team_slot_rule_name(pteam->slot);
415 }
416 
417 /****************************************************************************
418   Returns the name (translated) of the team.
419 ****************************************************************************/
team_name_translation(const struct team * pteam)420 const char *team_name_translation(const struct team *pteam)
421 {
422   fc_assert_ret_val(NULL != pteam, NULL);
423 
424   return team_slot_name_translation(pteam->slot);
425 }
426 
427 /****************************************************************************
428   Set in 'buf' the name of the team 'pteam' in a format like
429   "team <team_name>". To avoid to see "team Team 0", it only prints the
430   the team number when the name of this team is not defined in the ruleset.
431 ****************************************************************************/
team_pretty_name(const struct team * pteam,char * buf,size_t buf_len)432 int team_pretty_name(const struct team *pteam, char *buf, size_t buf_len)
433 {
434   if (NULL != pteam) {
435     if (NULL != pteam->slot->defined_name) {
436       /* TRANS: %s is ruleset-chosen team name (e.g. "Red") */
437       return fc_snprintf(buf, buf_len, _("team %s"),
438                          team_slot_name_translation(pteam->slot));
439     } else {
440       return fc_snprintf(buf, buf_len, _("team %d"), team_number(pteam));
441     }
442   }
443 
444   /* No need to translate, it's an error. */
445   fc_strlcpy(buf, "(null team)", buf_len);
446   return -1;
447 }
448 
449 /****************************************************************************
450   Returns the member list of the team.
451 ****************************************************************************/
team_members(const struct team * pteam)452 const struct player_list *team_members(const struct team *pteam)
453 {
454   fc_assert_ret_val(NULL != pteam, NULL);
455 
456   return pteam->plrlist;
457 }
458 
459 /****************************************************************************
460   Set a player to a team.  Removes the previous team affiliation if
461   necessary.
462 ****************************************************************************/
team_add_player(struct player * pplayer,struct team * pteam)463 void team_add_player(struct player *pplayer, struct team *pteam)
464 {
465   fc_assert_ret(pplayer != NULL);
466 
467   if (pteam == NULL) {
468     pteam = team_new(NULL);
469   }
470 
471   fc_assert_ret(pteam != NULL);
472 
473   if (pteam == pplayer->team) {
474     /* It is the team of the player. */
475     return;
476   }
477 
478   log_debug("Adding player %d/%s to team %s.", player_number(pplayer),
479             pplayer->username, team_rule_name(pteam));
480 
481   /* Remove the player from the old team, if any. */
482   team_remove_player(pplayer);
483 
484   /* Put the player on the new team. */
485   pplayer->team = pteam;
486   player_list_append(pteam->plrlist, pplayer);
487 }
488 
489 /****************************************************************************
490   Remove the player from the team.  This should only be called when deleting
491   a player; since every player must always be on a team.
492 
493   Note in some very rare cases a player may not be on a team.  It's safe
494   to call this function anyway.
495 ****************************************************************************/
team_remove_player(struct player * pplayer)496 void team_remove_player(struct player *pplayer)
497 {
498   struct team *pteam;
499 
500   if (pplayer->team) {
501     pteam = pplayer->team;
502 
503     log_debug("Removing player %d/%s from team %s (%d)",
504               player_number(pplayer), player_name(pplayer),
505               team_rule_name(pteam), player_list_size(pteam->plrlist));
506     player_list_remove(pteam->plrlist, pplayer);
507 
508     if (player_list_size(pteam->plrlist) == 0) {
509       team_destroy(pteam);
510     }
511   }
512   pplayer->team = NULL;
513 }
514