1 /***********************************************************************
2 Freeciv - Copyright (C) 2001 - R. Falke
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 /***********************************************************************
15 This is the common file for all front-end (Front End Common) for the
16 citizen management agent (CMA).
17 ***********************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include <fc_config.h>
21 #endif
22
23 #include <string.h>
24
25 /* utility */
26 #include "fciconv.h"
27 #include "fcintl.h"
28 #include "log.h"
29 #include "mem.h"
30 #include "support.h"
31
32 /* common */
33 #include "game.h"
34 #include "specialist.h"
35
36 /* client */
37 #include "attribute.h"
38
39 /* client/agents */
40 #include "agents.h"
41
42 #include "cma_fec.h"
43
44
45 #define RESULT_COLUMNS 10
46 #define BUFFER_SIZE 100
47 #define MAX_LEN_PRESET_NAME 80
48
49 struct cma_preset {
50 char *descr;
51 struct cm_parameter parameter;
52 };
53
54 #define SPECLIST_TAG preset
55 #define SPECLIST_TYPE struct cma_preset
56 #include "speclist.h"
57
58 #define preset_list_iterate(presetlist, ppreset) \
59 TYPED_LIST_ITERATE(struct cma_preset, presetlist, ppreset)
60 #define preset_list_iterate_end LIST_ITERATE_END
61
62 static struct preset_list *preset_list = NULL;
63
64 /****************************************************************************
65 Is called if the game removes a city. It will clear the
66 "fe parameter" attribute to reduce the size of the savegame.
67 *****************************************************************************/
city_remove(int city_id)68 static void city_remove(int city_id)
69 {
70 attr_city_set(ATTR_CITY_CMAFE_PARAMETER, city_id, 0, NULL);
71 }
72
73 /**************************************************************************
74 Initialize the presets if there are no presets loaded on startup.
75 **************************************************************************/
cmafec_init(void)76 void cmafec_init(void)
77 {
78 struct agent self;
79
80 if (preset_list == NULL) {
81 preset_list = preset_list_new();
82 }
83
84 memset(&self, 0, sizeof(self));
85 strcpy(self.name, "CMA");
86 self.level = 1;
87 self.city_callbacks[CB_REMOVE] = city_remove;
88 register_agent(&self);
89 }
90
91 /**************************************************************************
92 Free resources allocated for presets system.
93 **************************************************************************/
cmafec_free(void)94 void cmafec_free(void)
95 {
96 while (cmafec_preset_num() > 0) {
97 cmafec_preset_remove(0);
98 }
99 preset_list_destroy(preset_list);
100 }
101
102 /**************************************************************************
103 Sets the front-end parameter.
104 **************************************************************************/
cmafec_set_fe_parameter(struct city * pcity,const struct cm_parameter * const parameter)105 void cmafec_set_fe_parameter(struct city *pcity,
106 const struct cm_parameter *const parameter)
107 {
108 cma_set_parameter(ATTR_CITY_CMAFE_PARAMETER, pcity->id, parameter);
109 }
110
111 /****************************************************************
112 Return the front-end parameter for the given city. Returns a dummy
113 parameter if no parameter was set.
114 *****************************************************************/
cmafec_get_fe_parameter(struct city * pcity,struct cm_parameter * dest)115 void cmafec_get_fe_parameter(struct city *pcity, struct cm_parameter *dest)
116 {
117 struct cm_parameter parameter;
118
119 /* our fe_parameter could be stale. our agents parameter is uptodate */
120 if (cma_is_city_under_agent(pcity, ¶meter)) {
121 cm_copy_parameter(dest, ¶meter);
122 cmafec_set_fe_parameter(pcity, dest);
123 } else {
124 /* Create a dummy parameter to return. */
125 cm_init_parameter(dest);
126 if (!cma_get_parameter(ATTR_CITY_CMAFE_PARAMETER, pcity->id, dest)) {
127 /* We haven't seen this city before; store the dummy. */
128 cmafec_set_fe_parameter(pcity, dest);
129 }
130 }
131 }
132
133 /**************************************************************************
134 Adds a preset.
135 **************************************************************************/
cmafec_preset_add(const char * descr_name,struct cm_parameter * pparam)136 void cmafec_preset_add(const char *descr_name, struct cm_parameter *pparam)
137 {
138 struct cma_preset *ppreset = fc_malloc(sizeof(struct cma_preset));
139
140 if (preset_list == NULL) {
141 preset_list = preset_list_new();
142 }
143
144 cm_copy_parameter(&ppreset->parameter, pparam);
145 ppreset->descr = fc_malloc(MAX_LEN_PRESET_NAME);
146 (void) fc_strlcpy(ppreset->descr, descr_name, MAX_LEN_PRESET_NAME);
147 preset_list_prepend(preset_list, ppreset);
148 }
149
150 /**************************************************************************
151 Removes a preset.
152 **************************************************************************/
cmafec_preset_remove(int idx)153 void cmafec_preset_remove(int idx)
154 {
155 struct cma_preset *ppreset;
156
157 fc_assert_ret(idx >= 0 && idx < cmafec_preset_num());
158
159 ppreset = preset_list_get(preset_list, idx);
160 preset_list_remove(preset_list, ppreset);
161
162 free(ppreset->descr);
163 free(ppreset);
164 }
165
166 /**************************************************************************
167 Returns the indexed preset's description.
168 **************************************************************************/
cmafec_preset_get_descr(int idx)169 char *cmafec_preset_get_descr(int idx)
170 {
171 struct cma_preset *ppreset;
172
173 fc_assert_ret_val(idx >= 0 && idx < cmafec_preset_num(), NULL);
174
175 ppreset = preset_list_get(preset_list, idx);
176 return ppreset->descr;
177 }
178
179 /**************************************************************************
180 Returns the indexed preset's parameter.
181 **************************************************************************/
cmafec_preset_get_parameter(int idx)182 const struct cm_parameter *cmafec_preset_get_parameter(int idx)
183 {
184 struct cma_preset *ppreset;
185
186 fc_assert_ret_val(idx >= 0 && idx < cmafec_preset_num(), NULL);
187
188 ppreset = preset_list_get(preset_list, idx);
189 return &ppreset->parameter;
190 }
191
192 /**************************************************************************
193 Returns the index of the preset which matches the given
194 parameter. Returns -1 if no preset could be found.
195 **************************************************************************/
cmafec_preset_get_index_of_parameter(const struct cm_parameter * const parameter)196 int cmafec_preset_get_index_of_parameter(const struct cm_parameter
197 *const parameter)
198 {
199 int i;
200
201 for (i = 0; i < preset_list_size(preset_list); i++) {
202 struct cma_preset *ppreset = preset_list_get(preset_list, i);
203 if (cm_are_parameter_equal(&ppreset->parameter, parameter)) {
204 return i;
205 }
206 }
207 return -1;
208 }
209
210 /**************************************************************************
211 Returns the total number of presets.
212 **************************************************************************/
cmafec_preset_num(void)213 int cmafec_preset_num(void)
214 {
215 return preset_list_size(preset_list);
216 }
217
218 /**************************************************************************
219 Return short description of city governor preset
220 **************************************************************************/
cmafec_get_short_descr_of_city(const struct city * pcity)221 const char *cmafec_get_short_descr_of_city(const struct city *pcity)
222 {
223 struct cm_parameter parameter;
224
225 if (!cma_is_city_under_agent(pcity, ¶meter)) {
226 return _("none");
227 } else {
228 return cmafec_get_short_descr(¶meter);
229 }
230 }
231
232 /**************************************************************************
233 Returns the description of the matching preset or "custom" if no
234 preset could be found.
235 **************************************************************************/
cmafec_get_short_descr(const struct cm_parameter * const parameter)236 const char *cmafec_get_short_descr(const struct cm_parameter *const
237 parameter)
238 {
239 int idx = cmafec_preset_get_index_of_parameter(parameter);
240
241 if (idx == -1) {
242 return _("custom");
243 } else {
244 return cmafec_preset_get_descr(idx);
245 }
246 }
247
248 /**************************************************************************
249 Return string describing when city is assumed to grow.
250 **************************************************************************/
get_city_growth_string(struct city * pcity,int surplus)251 static const char *get_city_growth_string(struct city *pcity, int surplus)
252 {
253 int stock, cost, turns;
254 static char buffer[50];
255
256 if (surplus == 0) {
257 fc_snprintf(buffer, sizeof(buffer), _("never"));
258 return buffer;
259 }
260
261 stock = pcity->food_stock;
262 cost = city_granary_size(city_size_get(pcity));
263
264 stock += surplus;
265
266 if (stock >= cost) {
267 turns = 1;
268 } else if (surplus > 0) {
269 turns = ((cost - stock - 1) / surplus) + 1 + 1;
270 } else {
271 if (stock < 0) {
272 turns = -1;
273 } else {
274 turns = (stock / surplus);
275 }
276 }
277 fc_snprintf(buffer, sizeof(buffer), PL_("%d turn", "%d turns", turns),
278 turns);
279 return buffer;
280 }
281
282 /**************************************************************************
283 Return string describing when city is assumed to finish current production
284 **************************************************************************/
get_prod_complete_string(struct city * pcity,int surplus)285 static const char *get_prod_complete_string(struct city *pcity, int surplus)
286 {
287 int stock, cost, turns;
288 static char buffer[50];
289
290 if (surplus <= 0) {
291 fc_snprintf(buffer, sizeof(buffer), _("never"));
292 return buffer;
293 }
294
295 if (city_production_has_flag(pcity, IF_GOLD)) {
296 fc_strlcpy(buffer, improvement_name_translation
297 (pcity->production.value.building), sizeof(buffer));
298 return buffer;
299 }
300 stock = pcity->shield_stock + surplus;
301 cost = city_production_build_shield_cost(pcity);
302
303 if (stock >= cost) {
304 turns = 1;
305 } else if (surplus > 0) {
306 turns = ((cost - stock - 1) / surplus) + 1 + 1;
307 } else {
308 if (stock < 0) {
309 turns = -1;
310 } else {
311 turns = (stock / surplus);
312 }
313 }
314 fc_snprintf(buffer, sizeof(buffer), PL_("%d turn", "%d turns", turns),
315 turns);
316 return buffer;
317 }
318
319 /**************************************************************************
320 Return string describing result
321 **************************************************************************/
cmafec_get_result_descr(struct city * pcity,const struct cm_result * result,const struct cm_parameter * const parameter)322 const char *cmafec_get_result_descr(struct city *pcity,
323 const struct cm_result *result,
324 const struct cm_parameter *const
325 parameter)
326 {
327 int j;
328 char buf[RESULT_COLUMNS][BUFFER_SIZE];
329 char citizen_types[BUFFER_SIZE];
330 static char buffer[600];
331
332 /* TRANS: "W" is worker citizens, as opposed to specialists;
333 * %s will represent the specialist types, for instance "E/S/T" */
334 fc_snprintf(citizen_types, BUFFER_SIZE, _("People (W/%s)"),
335 specialists_abbreviation_string());
336
337 if (!result->found_a_valid) {
338 for (j = 0; j < RESULT_COLUMNS; j++)
339 fc_snprintf(buf[j], BUFFER_SIZE, "---");
340 } else {
341 output_type_iterate(o) {
342 fc_snprintf(buf[o], BUFFER_SIZE, "%+3d", result->surplus[o]);
343 } output_type_iterate_end;
344
345 fc_snprintf(buf[6], BUFFER_SIZE, "%d/%s%s",
346 city_size_get(pcity) - cm_result_specialists(result),
347 specialists_string(result->specialists),
348 /* TRANS: preserve leading space */
349 result->happy ? _(" happy") : "");
350
351 fc_snprintf(buf[7], BUFFER_SIZE, "%s",
352 get_city_growth_string(pcity, result->surplus[O_FOOD]));
353 fc_snprintf(buf[8], BUFFER_SIZE, "%s",
354 get_prod_complete_string(pcity, result->surplus[O_SHIELD]));
355 fc_snprintf(buf[9], BUFFER_SIZE, "%s",
356 cmafec_get_short_descr(parameter));
357 }
358
359 fc_snprintf(buffer, sizeof(buffer),
360 _("Name: %s\n"
361 "Food: %10s Gold: %10s\n"
362 "Production: %10s Luxury: %10s\n"
363 "Trade: %10s Science: %10s\n"
364 "\n"
365 "%*s%s: %s\n"
366 " City grows: %s\n"
367 "Production completed: %s"),
368 buf[9], buf[O_FOOD], buf[O_GOLD], buf[O_SHIELD], buf[O_LUXURY],
369 buf[O_TRADE], buf[O_SCIENCE],
370 MAX(0, 20 - (int)get_internal_string_length(citizen_types)), "",
371 citizen_types,
372 buf[6], buf[7], buf[8]);
373
374 log_debug("\n%s", buffer);
375 return buffer;
376 }
377
378
379 /**************************************************************************
380 Create default cma presets for a new user (or without configuration file)
381 **************************************************************************/
create_default_cma_presets(void)382 void create_default_cma_presets(void)
383 {
384 int i;
385 struct cm_parameter parameters[] = {
386 { /* very happy */
387 .minimal_surplus = {0, 0, 0, -20, 0, 0},
388 .require_happy = FALSE,
389 .allow_disorder = FALSE,
390 .allow_specialists = TRUE,
391 .factor = {10, 5, 0, 4, 0, 4},
392 .happy_factor = 25
393 },
394 { /* prefer food */
395 .minimal_surplus = {-20, 0, 0, -20, 0, 0},
396 .require_happy = FALSE,
397 .allow_disorder = FALSE,
398 .allow_specialists = TRUE,
399 .factor = {25, 5, 0, 4, 0, 4},
400 .happy_factor = 0
401 },
402 { /* prefer prod */
403 .minimal_surplus = {0, -20, 0, -20, 0, 0},
404 .require_happy = FALSE,
405 .allow_disorder = FALSE,
406 .allow_specialists = TRUE,
407 .factor = {10, 25, 0, 4, 0, 4},
408 .happy_factor = 0
409 },
410 { /* prefer gold */
411 .minimal_surplus = {0, 0, 0, -20, 0, 0},
412 .require_happy = FALSE,
413 .allow_disorder = FALSE,
414 .allow_specialists = TRUE,
415 .factor = {10, 5, 0, 25, 0, 4},
416 .happy_factor = 0
417 },
418 { /* prefer science */
419 .minimal_surplus = {0, 0, 0, -20, 0, 0},
420 .require_happy = FALSE,
421 .allow_disorder = FALSE,
422 .allow_specialists = TRUE,
423 .factor = {10, 5, 0, 4, 0, 25},
424 .happy_factor = 0
425 }
426 };
427 const char* names[ARRAY_SIZE(parameters)] = {
428 N_("?cma:Very happy"),
429 N_("?cma:Prefer food"),
430 N_("?cma:Prefer production"),
431 N_("?cma:Prefer gold"),
432 N_("?cma:Prefer science")
433 };
434
435 for (i = ARRAY_SIZE(parameters) - 1; i >= 0; i--) {
436 cmafec_preset_add(Q_(names[i]), ¶meters[i]);
437 }
438 }
439