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, &parameter)) {
121     cm_copy_parameter(dest, &parameter);
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, &parameter)) {
226     return _("none");
227   } else {
228     return cmafec_get_short_descr(&parameter);
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]), &parameters[i]);
437  }
438 }
439