Freeciv21
Develop your civilization from humble roots to a global empire
citizenshand.cpp
Go to the documentation of this file.
1 /*
2 _ ._ Copyright (c) 1996-2021 Freeciv21 and Freeciv contributors.
3  \ | This file is part of Freeciv21. Freeciv21 is free software: you
4  \_| can redistribute it and/or modify it under the terms of the
5  .' '. GNU General Public License as published by the Free
6  :O O: Software Foundation, either version 3 of the License,
7  '/ \' or (at your option) any later version. You should have
8  :X: received a copy of the GNU General Public License along with
9  :X: Freeciv21. If not, see https://www.gnu.org/licenses/.
10  */
11 
12 // utility
13 #include "log.h"
14 #include "rand.h"
15 
16 // common
17 #include "citizens.h"
18 #include "city.h"
19 #include "fc_types.h"
20 #include "game.h"
21 #include "player.h"
22 
23 // server
24 #include "citizenshand.h"
25 
26 #define log_citizens log_debug
27 
33 #define log_citizens_add(_pcity, _delta, _pplayer) \
34  log_citizens("%s (size %d; %s): %+d citizen(s) for %s (now: %d)", \
35  city_name_get(_pcity), city_size_get(_pcity), \
36  player_name(city_owner(_pcity)), _delta, \
37  player_name(_pplayer), \
38  citizens_nation_get(_pcity, _pplayer->slot));
39 void citizens_update(struct city *pcity, struct player *plr)
40 {
41  int delta;
42 
43  fc_assert_ret(pcity);
44 
45  if (pcity->server.debug) {
46  // before
47  citizens_print(pcity);
48  }
49 
50  if (!game.info.citizen_nationality) {
51  return;
52  }
53 
54  if (pcity->nationality == nullptr) {
55  // If nationalities are not set (virtual cities) do nothing.
56  return;
57  }
58 
59  delta = city_size_get(pcity) - citizens_count(pcity);
60 
61  if (delta == 0) {
62  // No change of the city size
63  return;
64  }
65 
66  if (delta > 0) {
67  if (plr != nullptr) {
68  citizens_nation_add(pcity, plr->slot, delta);
69  log_citizens_add(pcity, delta, plr);
70  } else {
71  // Add new citizens with the nationality of the current owner.
72  citizens_nation_add(pcity, city_owner(pcity)->slot, delta);
73  log_citizens_add(pcity, delta, city_owner(pcity));
74  }
75  } else {
76  // Removed citizens.
77  struct player_slot *city_nations[MAX_NUM_PLAYER_SLOTS];
78  int count = 0;
79 
80  // Create a list of foreign nationalities.
81  citizens_foreign_iterate(pcity, pslot, nationality)
82  {
83  city_nations[count] = pslot;
84  count++;
85  }
87 
88  // First remove from foreign nationalities.
89  while (count > 0 && delta < 0) {
90  int selected = fc_rand(count);
91  struct player_slot *pslot = city_nations[selected];
92  struct player *pplayer = player_slot_get_player(pslot);
93  citizens nationality = citizens_nation_get(pcity, pslot);
94 
95  fc_assert_ret(nationality != 0);
96  fc_assert_ret(pplayer != nullptr);
97 
98  if (nationality == 1) {
99  // Remove one citizen.
100  delta++;
101  citizens_nation_set(pcity, pslot, 0);
102  // Remove this nation from the list of nationalities.
103  if (selected != count) {
104  city_nations[selected] = city_nations[count - 1];
105  }
106  count--;
107 
108  log_citizens_add(pcity, -1, pplayer);
109  } else {
110  /* Get the minimal reduction = the maximum value of two negative
111  * numbers. */
112  int diff = MAX(delta, -nationality / 2);
113  delta -= diff;
114  citizens_nation_add(pcity, pslot, diff);
115  log_citizens_add(pcity, diff, pplayer);
116  }
117  }
118 
119  if (delta < 0) {
120  /* Now take the remaining citizens loss from the nation of the owner.
121  */
122  citizens_nation_add(pcity, city_owner(pcity)->slot, delta);
123  log_citizens_add(pcity, delta, city_owner(pcity));
124  }
125  }
126 
127  fc_assert_ret(city_size_get(pcity) == citizens_count(pcity));
128 
129  if (pcity->server.debug) {
130  // after
131  citizens_print(pcity);
132  }
133 }
134 #undef log_citizens_add
135 
139 void citizens_print(const struct city *pcity)
140 {
141  fc_assert_ret(pcity);
142 
143  if (!game.info.citizen_nationality) {
144  return;
145  }
146 
147  log_citizens("%s (size %d; %s): %d citizen(s)", city_name_get(pcity),
148  city_size_get(pcity), player_name(city_owner(pcity)),
149  citizens_count(pcity));
150 
151  citizens_iterate(pcity, pslot, nationality)
152  {
153  struct player *pplayer = player_slot_get_player(pslot);
154 
155  fc_assert_ret(pplayer != nullptr);
156 
157  log_citizens("%s (size %d; %s): %d citizen(s) for %s",
158  city_name_get(pcity), city_size_get(pcity),
159  player_name(city_owner(pcity)), nationality,
160  player_name(pplayer));
161  }
163 }
164 
168 static bool citizen_convert_check(struct city *pcity)
169 {
170  return fc_rand(1000) + 1 <= game.info.citizen_convert_speed;
171 }
172 
176 void citizens_convert(struct city *pcity)
177 {
178  struct player_slot *city_nations[MAX_NUM_PLAYER_SLOTS] = {nullptr}, *pslot;
179  struct player *pplayer;
180  int count = 0;
181 
182  fc_assert_ret(pcity);
183 
184  if (!game.info.citizen_nationality) {
185  return;
186  }
187 
188  if (!citizen_convert_check(pcity)) {
189  return;
190  }
191 
192  if (citizens_nation_foreign(pcity) == 0) {
193  // Only our own citizens.
194  return;
195  }
196 
197  // Create a list of foreign nationalities.
198  citizens_foreign_iterate(pcity, foreign_slot, nationality)
199  {
200  if (nationality != 0) {
201  city_nations[count++] = foreign_slot;
202  }
203  }
205 
206  // Now convert one citizens to the city owners nationality.
207  pslot = city_nations[fc_rand(count)];
208  pplayer = player_slot_get_player(pslot);
209 
210  fc_assert_ret(pplayer != nullptr);
211 
212  log_citizens("%s (size %d; %s): convert 1 citizen from %s",
213  city_name_get(pcity), city_size_get(pcity),
214  player_name(city_owner(pcity)), player_name(pplayer));
215  citizens_nation_move(pcity, pslot, city_owner(pcity)->slot, 1);
216 }
217 
221 void citizens_convert_conquest(struct city *pcity)
222 {
223  struct player_slot *conqueror;
224 
225  if (!game.info.citizen_nationality
226  || game.info.conquest_convert_pct == 0) {
227  return;
228  }
229 
230  conqueror = city_owner(pcity)->slot;
231 
232  citizens_foreign_iterate(pcity, pslot, nat)
233  {
234  /* Convert 'game.info.conquest_convert_pct' citizens of each foreign
235  * nationality to the nation of the new owner (but at least 1). */
236  citizens convert = MAX(1, nat * game.info.conquest_convert_pct / 100);
237  struct player *pplayer = player_slot_get_player(pslot);
238 
239  fc_assert_ret(pplayer != nullptr);
240 
241  log_citizens("%s (size %d; %s): convert %d citizen from %s (conquered)",
242  city_name_get(pcity), city_size_get(pcity),
243  player_name(city_owner(pcity)), convert,
244  player_name(pplayer));
245  citizens_nation_move(pcity, pslot, conqueror, convert);
246  }
248 }
void citizens_nation_move(struct city *pcity, const struct player_slot *pslot_from, const struct player_slot *pslot_to, int move)
Convert a (positive or negative) value to the citizens from one nation to another.
Definition: citizens.cpp:123
void citizens_nation_add(struct city *pcity, const struct player_slot *pslot, int add)
Add a (positive or negative) value to the citizens of the given nationality.
Definition: citizens.cpp:96
citizens citizens_nation_get(const struct city *pcity, const struct player_slot *pslot)
Get the number of citizens with the given nationality.
Definition: citizens.cpp:65
void citizens_nation_set(struct city *pcity, const struct player_slot *pslot, citizens count)
Set the number of citizens with the given nationality.
Definition: citizens.cpp:137
citizens citizens_nation_foreign(const struct city *pcity)
Get the number of foreign citizens.
Definition: citizens.cpp:82
citizens citizens_count(const struct city *pcity)
Return the number of citizens in a city.
Definition: citizens.cpp:154
#define citizens_foreign_iterate_end
Definition: citizens.h:55
#define citizens_foreign_iterate(_pcity, _pslot, _nationality)
Definition: citizens.h:49
#define citizens_iterate_end
Definition: citizens.h:44
#define citizens_iterate(_pcity, _pslot, _nationality)
Definition: citizens.h:37
void citizens_convert_conquest(struct city *pcity)
Convert citizens to the nationality of the one conquering the city.
void citizens_convert(struct city *pcity)
Convert one (random) foreign citizen to the nationality of the owner.
#define log_citizens_add(_pcity, _delta, _pplayer)
Update the nationality according to the city size.
static bool citizen_convert_check(struct city *pcity)
Return whether citizen should be converted this turn.
void citizens_update(struct city *pcity, struct player *plr)
#define log_citizens
void citizens_print(const struct city *pcity)
Print the data about the citizens.
struct player * city_owner(const struct city *pcity)
Return the owner of the city.
Definition: city.cpp:1083
const char * city_name_get(const struct city *pcity)
Return the name of the city.
Definition: city.cpp:1077
citizens city_size_get(const struct city *pcity)
Get the city size.
Definition: city.cpp:1101
unsigned char citizens
Definition: fc_types.h:305
#define MAX_NUM_PLAYER_SLOTS
Definition: fc_types.h:24
struct civ_game game
Definition: game.cpp:47
#define fc_assert_ret(condition)
Definition: log.h:112
struct player * player_slot_get_player(const struct player_slot *pslot)
Returns the team corresponding to the slot.
Definition: player.cpp:384
const char * player_name(const struct player *pplayer)
Return the leader name of the player.
Definition: player.cpp:816
#define fc_rand(_size)
Definition: rand.h:16
#define MAX(x, y)
Definition: shared.h:48
Definition: city.h:291
citizens * nationality
Definition: city.h:310
struct city::@15::@17 server
struct packet_game_info info
Definition: game.h:80
Definition: player.h:231
struct player_slot * slot
Definition: player.h:232