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 #include <errno.h>
19 #include <math.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 /* utility */
25 #include "fcintl.h"
26 #include "log.h"
27 #include "support.h"
28
29 /* common */
30 #include "city.h"
31 #include "culture.h"
32 #include "game.h"
33 #include "map.h"
34 #include "specialist.h"
35 #include "unitlist.h"
36
37 /* agents */
38 #include "cma_fec.h"
39
40 /* client */
41 #include "citydlg_common.h" /* city_production_cost_str() */
42 #include "options.h"
43
44 #include "cityrepdata.h"
45
46 /************************************************************************
47 cr_entry = return an entry (one column for one city) for the city report
48 These return ptrs to filled in static strings.
49 Note the returned string may not be exactly the right length; that
50 is handled later.
51 *************************************************************************/
cr_entry_cityname(const struct city * pcity,const void * data)52 static const char *cr_entry_cityname(const struct city *pcity,
53 const void *data)
54 {
55 /* We used to truncate the name to 14 bytes. This should not be needed
56 * in any modern GUI library and may give an invalid string if a
57 * multibyte character is clipped. */
58 return city_name_get(pcity);
59 }
60
61 /************************************************************************
62 Translated name of nation who owns this city.
63 *************************************************************************/
cr_entry_nation(const struct city * pcity,const void * data)64 static const char *cr_entry_nation(const struct city *pcity,
65 const void *data)
66 {
67 return nation_adjective_for_player(city_owner(pcity));
68 }
69
70 /************************************************************************
71 Returns city size written to string. Returned string is statically
72 allocated and its contents change when this function is called again.
73 *************************************************************************/
cr_entry_size(const struct city * pcity,const void * data)74 static const char *cr_entry_size(const struct city *pcity,
75 const void *data)
76 {
77 static char buf[8];
78
79 fc_snprintf(buf, sizeof(buf), "%2d", city_size_get(pcity));
80 return buf;
81 }
82
83 /************************************************************************
84 Returns concise city happiness state written to string.
85 Returned string is statically allocated and its contents change when
86 this function is called again.
87 *************************************************************************/
cr_entry_hstate_concise(const struct city * pcity,const void * data)88 static const char *cr_entry_hstate_concise(const struct city *pcity,
89 const void *data)
90 {
91 static char buf[4];
92 fc_snprintf(buf, sizeof(buf), "%s",
93 (city_celebrating(pcity) ? "*"
94 : (city_unhappy(pcity) ? "X" : " ")));
95 return buf;
96 }
97
98 /************************************************************************
99 Returns verbose city happiness state written to string.
100 Returned string is statically allocated and its contents change when
101 this function is called again.
102 *************************************************************************/
cr_entry_hstate_verbose(const struct city * pcity,const void * data)103 static const char *cr_entry_hstate_verbose(const struct city *pcity,
104 const void *data)
105 {
106 static char buf[32];
107
108 fc_snprintf(buf, sizeof(buf), "%s",
109 (city_celebrating(pcity) ? Q_("?city_state:Celebrating")
110 : (city_unhappy(pcity) ? Q_("?city_state:Disorder")
111 : Q_("?city_state:Peace"))));
112 return buf;
113 }
114
115 /************************************************************************
116 Returns number of citizens of each happiness state written to string.
117 Returned string is statically allocated and its contents change when
118 this function is called again.
119 *************************************************************************/
cr_entry_workers(const struct city * pcity,const void * data)120 static const char *cr_entry_workers(const struct city *pcity,
121 const void *data)
122 {
123 static char buf[32];
124
125 fc_snprintf(buf, sizeof(buf), "%d/%d/%d/%d",
126 pcity->feel[CITIZEN_HAPPY][FEELING_FINAL],
127 pcity->feel[CITIZEN_CONTENT][FEELING_FINAL],
128 pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL],
129 pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
130 return buf;
131 }
132
133 /************************************************************************
134 Returns number of happy citizens written to string.
135 Returned string is statically allocated and its contents change when
136 this function is called again.
137 *************************************************************************/
cr_entry_happy(const struct city * pcity,const void * data)138 static const char *cr_entry_happy(const struct city *pcity,
139 const void *data)
140 {
141 static char buf[8];
142 fc_snprintf(buf, sizeof(buf), "%2d",
143 pcity->feel[CITIZEN_HAPPY][FEELING_FINAL]);
144 return buf;
145 }
146
147 /************************************************************************
148 Returns city total culture written to string
149 *************************************************************************/
cr_entry_culture(const struct city * pcity,const void * data)150 static const char *cr_entry_culture(const struct city *pcity,
151 const void *data)
152 {
153 static char buf[8];
154 fc_snprintf(buf, sizeof(buf), "%3d", pcity->client.culture);
155 return buf;
156 }
157
158 /************************************************************************
159 Returns city history culture value written to string
160 *************************************************************************/
cr_entry_history(const struct city * pcity,const void * data)161 static const char *cr_entry_history(const struct city *pcity,
162 const void *data)
163 {
164 static char buf[20];
165 int perturn = city_history_gain(pcity);
166
167 if (perturn != 0) {
168 fc_snprintf(buf, sizeof(buf), "%3d (%+d)", pcity->history, perturn);
169 } else {
170 fc_snprintf(buf, sizeof(buf), "%3d", pcity->history);
171 }
172 return buf;
173 }
174
175 /************************************************************************
176 Returns city performance culture value written to string
177 *************************************************************************/
cr_entry_performance(const struct city * pcity,const void * data)178 static const char *cr_entry_performance(const struct city *pcity,
179 const void *data)
180 {
181 static char buf[8];
182
183 /*
184 * Infer the actual performance component of culture from server-supplied
185 * values, rather than using the client's guess at EFT_PERFORMANCE.
186 * XXX: if culture ever gets more complicated than history+performance,
187 * this will need revising, possibly to use a server-supplied value.
188 */
189 fc_snprintf(buf, sizeof(buf), "%3d", pcity->client.culture - pcity->history);
190 return buf;
191 }
192
193 /************************************************************************
194 Returns number of content citizens written to string.
195 Returned string is statically allocated and its contents change when
196 this function is called again.
197 *************************************************************************/
cr_entry_content(const struct city * pcity,const void * data)198 static const char *cr_entry_content(const struct city *pcity,
199 const void *data)
200 {
201 static char buf[8];
202 fc_snprintf(buf, sizeof(buf), "%2d",
203 pcity->feel[CITIZEN_CONTENT][FEELING_FINAL]);
204 return buf;
205 }
206
207 /************************************************************************
208 Returns number of unhappy citizens written to string.
209 Returned string is statically allocated and its contents change when
210 this function is called again.
211 *************************************************************************/
cr_entry_unhappy(const struct city * pcity,const void * data)212 static const char *cr_entry_unhappy(const struct city *pcity,
213 const void *data)
214 {
215 static char buf[8];
216 fc_snprintf(buf, sizeof(buf), "%2d",
217 pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]);
218 return buf;
219 }
220
221 /************************************************************************
222 Returns number of angry citizens written to string.
223 Returned string is statically allocated and its contents change when
224 this function is called again.
225 *************************************************************************/
cr_entry_angry(const struct city * pcity,const void * data)226 static const char *cr_entry_angry(const struct city *pcity,
227 const void *data)
228 {
229 static char buf[8];
230 fc_snprintf(buf, sizeof(buf), "%2d",
231 pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
232 return buf;
233 }
234
235 /************************************************************************
236 Returns list of specialists written to string.
237 Returned string is statically allocated and its contents change when
238 this function is called again.
239 *************************************************************************/
cr_entry_specialists(const struct city * pcity,const void * data)240 static const char *cr_entry_specialists(const struct city *pcity,
241 const void *data)
242 {
243 return specialists_string(pcity->specialists);
244 }
245
246 /************************************************************************
247 Returns number of specialists of type given as data written to string.
248 Returned string is statically allocated and its contents change when
249 this function is called again.
250 *************************************************************************/
cr_entry_specialist(const struct city * pcity,const void * data)251 static const char *cr_entry_specialist(const struct city *pcity,
252 const void *data)
253 {
254 static char buf[8];
255 const struct specialist *sp = data;
256
257 fc_snprintf(buf, sizeof(buf), "%2d",
258 pcity->specialists[specialist_index(sp)]);
259 return buf;
260 }
261
262 /************************************************************************
263 Returns string with best attack values of units in city.
264 Returned string is statically allocated and its contents change when
265 this function is called again.
266 *************************************************************************/
cr_entry_attack(const struct city * pcity,const void * data)267 static const char *cr_entry_attack(const struct city *pcity,
268 const void *data)
269 {
270 static char buf[32];
271 int attack_best[4] = {-1, -1, -1, -1}, i;
272
273 unit_list_iterate(pcity->tile->units, punit) {
274 /* What about allied units? Should we just count them? */
275 attack_best[3] = unit_type_get(punit)->attack_strength;
276
277 /* Now that the element is appended to the end of the list, we simply
278 do an insertion sort. */
279 for (i = 2; i >= 0 && attack_best[i] < attack_best[i + 1]; i--) {
280 int tmp = attack_best[i];
281 attack_best[i] = attack_best[i + 1];
282 attack_best[i + 1] = tmp;
283 }
284 } unit_list_iterate_end;
285
286 buf[0] = '\0';
287 for (i = 0; i < 3; i++) {
288 if (attack_best[i] >= 0) {
289 cat_snprintf(buf, sizeof(buf), "%s%d", (i > 0) ? "/" : "",
290 attack_best[i]);
291 } else {
292 cat_snprintf(buf, sizeof(buf), "%s-", (i > 0) ? "/" : "");
293 }
294 }
295
296 return buf;
297 }
298
299 /************************************************************************
300 Returns string with best defend values of units in city.
301 Returned string is statically allocated and its contents change when
302 this function is called again.
303 *************************************************************************/
cr_entry_defense(const struct city * pcity,const void * data)304 static const char *cr_entry_defense(const struct city *pcity,
305 const void *data)
306 {
307 static char buf[32];
308 int defense_best[4] = {-1, -1, -1, -1}, i;
309
310 unit_list_iterate(pcity->tile->units, punit) {
311 /* What about allied units? Should we just count them? */
312 defense_best[3] = unit_type_get(punit)->defense_strength;
313
314 /* Now that the element is appended to the end of the list, we simply
315 do an insertion sort. */
316 for (i = 2; i >= 0 && defense_best[i] < defense_best[i + 1]; i--) {
317 int tmp = defense_best[i];
318
319 defense_best[i] = defense_best[i + 1];
320 defense_best[i + 1] = tmp;
321 }
322 } unit_list_iterate_end;
323
324 buf[0] = '\0';
325 for (i = 0; i < 3; i++) {
326 if (defense_best[i] >= 0) {
327 cat_snprintf(buf, sizeof(buf), "%s%d", (i > 0) ? "/" : "",
328 defense_best[i]);
329 } else {
330 cat_snprintf(buf, sizeof(buf), "%s-", (i > 0) ? "/" : "");
331 }
332 }
333
334 return buf;
335 }
336
337 /************************************************************************
338 Returns number of supported units written to string.
339 Returned string is statically allocated and its contents change when
340 this function is called again.
341 *************************************************************************/
cr_entry_supported(const struct city * pcity,const void * data)342 static const char *cr_entry_supported(const struct city *pcity,
343 const void *data)
344 {
345 static char buf[8];
346 int num_supported = unit_list_size(pcity->units_supported);
347
348 fc_snprintf(buf, sizeof(buf), "%2d", num_supported);
349
350 return buf;
351 }
352
353 /************************************************************************
354 Returns number of present units written to string.
355 Returned string is statically allocated and its contents change when
356 this function is called again.
357 *************************************************************************/
cr_entry_present(const struct city * pcity,const void * data)358 static const char *cr_entry_present(const struct city *pcity,
359 const void *data)
360 {
361 static char buf[8];
362 int num_present = unit_list_size(pcity->tile->units);
363
364 fc_snprintf(buf, sizeof(buf), "%2d", num_present);
365
366 return buf;
367 }
368
369 /************************************************************************
370 Returns string listing amounts of resources.
371 Returned string is statically allocated and its contents change when
372 this function is called again.
373 *************************************************************************/
cr_entry_resources(const struct city * pcity,const void * data)374 static const char *cr_entry_resources(const struct city *pcity,
375 const void *data)
376 {
377 static char buf[32];
378 fc_snprintf(buf, sizeof(buf), "%d/%d/%d",
379 pcity->surplus[O_FOOD],
380 pcity->surplus[O_SHIELD],
381 pcity->surplus[O_TRADE]);
382 return buf;
383 }
384
385 /************************************************************************
386 Returns food surplus written to string.
387 Returned string is statically allocated and its contents change when
388 this function is called again.
389 *************************************************************************/
cr_entry_foodplus(const struct city * pcity,const void * data)390 static const char *cr_entry_foodplus(const struct city *pcity,
391 const void *data)
392 {
393 static char buf[8];
394 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_FOOD]);
395 return buf;
396 }
397
398 /************************************************************************
399 Returns production surplus written to string.
400 Returned string is statically allocated and its contents change when
401 this function is called again.
402 *************************************************************************/
cr_entry_prodplus(const struct city * pcity,const void * data)403 static const char *cr_entry_prodplus(const struct city *pcity,
404 const void *data)
405 {
406 static char buf[8];
407 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_SHIELD]);
408 return buf;
409 }
410
411 /************************************************************************
412 Returns trade surplus written to string.
413 Returned string is statically allocated and its contents change when
414 this function is called again.
415 *************************************************************************/
cr_entry_tradeplus(const struct city * pcity,const void * data)416 static const char *cr_entry_tradeplus(const struct city *pcity,
417 const void *data)
418 {
419 static char buf[8];
420 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_TRADE]);
421 return buf;
422 }
423
424 /************************************************************************
425 Returns string describing resource output.
426 Returned string is statically allocated and its contents change when
427 this function is called again.
428 *************************************************************************/
cr_entry_output(const struct city * pcity,const void * data)429 static const char *cr_entry_output(const struct city *pcity,
430 const void *data)
431 {
432 static char buf[32];
433 int goldie = pcity->surplus[O_GOLD];
434
435 fc_snprintf(buf, sizeof(buf), "%3d/%d/%d",
436 goldie, pcity->prod[O_LUXURY], pcity->prod[O_SCIENCE]);
437 return buf;
438 }
439
440 /************************************************************************
441 Returns gold surplus written to string.
442 Returned string is statically allocated and its contents change when
443 this function is called again.
444 *************************************************************************/
cr_entry_gold(const struct city * pcity,const void * data)445 static const char *cr_entry_gold(const struct city *pcity,
446 const void *data)
447 {
448 static char buf[8];
449
450 if (pcity->surplus[O_GOLD] > 0) {
451 fc_snprintf(buf, sizeof(buf), "+%d", pcity->surplus[O_GOLD]);
452 } else {
453 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_GOLD]);
454 }
455 return buf;
456 }
457
458 /************************************************************************
459 Returns luxury output written to string.
460 Returned string is statically allocated and its contents change when
461 this function is called again.
462 *************************************************************************/
cr_entry_luxury(const struct city * pcity,const void * data)463 static const char *cr_entry_luxury(const struct city *pcity,
464 const void *data)
465 {
466 static char buf[8];
467 fc_snprintf(buf, sizeof(buf), "%3d", pcity->prod[O_LUXURY]);
468 return buf;
469 }
470
471 /************************************************************************
472 Returns science output written to string.
473 Returned string is statically allocated and its contents change when
474 this function is called again.
475 *************************************************************************/
cr_entry_science(const struct city * pcity,const void * data)476 static const char *cr_entry_science(const struct city *pcity,
477 const void *data)
478 {
479 static char buf[8];
480 fc_snprintf(buf, sizeof(buf), "%3d", pcity->prod[O_SCIENCE]);
481 return buf;
482 }
483
484 /************************************************************************
485 Returns number of turns before city grows written to string.
486 Returned string is statically allocated and its contents change when
487 this function is called again.
488 *************************************************************************/
cr_entry_growturns(const struct city * pcity,const void * data)489 static const char *cr_entry_growturns(const struct city *pcity,
490 const void *data)
491 {
492 int turns = city_turns_to_grow(pcity);
493 char buffer[8];
494 static char buf[32];
495
496 if (turns == FC_INFINITY) {
497 /* 'never' wouldn't be easily translatable here. */
498 fc_snprintf(buffer, sizeof(buffer), "---");
499 } else {
500 /* Shrinking cities get a negative value. */
501 fc_snprintf(buffer, sizeof(buffer), "%4d", turns);
502 }
503 fc_snprintf(buf, sizeof(buf), "%s (%d/%d)",
504 buffer, pcity->food_stock,
505 city_granary_size(city_size_get(pcity)));
506 return buf;
507 }
508
509 /************************************************************************
510 Returns pollution output written to string.
511 Returned string is statically allocated and its contents change when
512 this function is called again.
513 *************************************************************************/
cr_entry_pollution(const struct city * pcity,const void * data)514 static const char *cr_entry_pollution(const struct city *pcity,
515 const void *data)
516 {
517 static char buf[8];
518 fc_snprintf(buf, sizeof(buf), "%3d", pcity->pollution);
519 return buf;
520 }
521
522 /************************************************************************
523 Returns number and output of trade routes written to string.
524 Returned string is statically allocated and its contents change when
525 this function is called again.
526 *************************************************************************/
cr_entry_trade_routes(const struct city * pcity,const void * data)527 static const char *cr_entry_trade_routes(const struct city *pcity,
528 const void *data)
529 {
530 static char buf[16];
531 int num = 0, value = 0, i;
532
533 for (i = 0; i < MAX_TRADE_ROUTES; i++) {
534 if (0 != pcity->trade[i]) {
535 num++;
536 value += pcity->trade_value[i];
537 }
538 }
539
540 if (0 == num) {
541 sz_strlcpy(buf, "0");
542 } else {
543 fc_snprintf(buf, sizeof(buf), "%d (+%d)", num, value);
544 }
545 return buf;
546 }
547
548 /************************************************************************
549 Returns number of build slots written to string.
550 Returned string is statically allocated and its contents change when
551 this function is called again.
552 *************************************************************************/
cr_entry_build_slots(const struct city * pcity,const void * data)553 static const char *cr_entry_build_slots(const struct city *pcity,
554 const void *data)
555 {
556 static char buf[8];
557 fc_snprintf(buf, sizeof(buf), "%3d", city_build_slots(pcity));
558 return buf;
559 }
560
561 /************************************************************************
562 Returns name of current production.
563 Returned string is statically allocated and its contents change when
564 this function is called again.
565 *************************************************************************/
cr_entry_building(const struct city * pcity,const void * data)566 static const char *cr_entry_building(const struct city *pcity,
567 const void *data)
568 {
569 static char buf[192];
570 const char *from_worklist =
571 worklist_is_empty(&pcity->worklist) ? "" :
572 gui_options.concise_city_production ? "+" : _("(worklist)");
573
574 if (city_production_has_flag(pcity, IF_GOLD)) {
575 fc_snprintf(buf, sizeof(buf), "%s (%d)%s",
576 city_production_name_translation(pcity),
577 MAX(0, pcity->surplus[O_SHIELD]), from_worklist);
578 } else {
579 fc_snprintf(buf, sizeof(buf), "%s (%d/%s)%s",
580 city_production_name_translation(pcity),
581 pcity->shield_stock,
582 city_production_cost_str(pcity),
583 from_worklist);
584 }
585
586 return buf;
587 }
588
589 /************************************************************************
590 Returns cost of buying current production and turns to completion
591 written to string. Returned string is statically allocated and its
592 contents change when this function is called again.
593 *************************************************************************/
cr_entry_build_cost(const struct city * pcity,const void * data)594 static const char *cr_entry_build_cost(const struct city *pcity,
595 const void *data)
596 {
597 char bufone[8];
598 char buftwo[8];
599 static char buf[32];
600 int price;
601 int turns;
602
603 if (city_production_has_flag(pcity, IF_GOLD)) {
604 fc_snprintf(buf, sizeof(buf), "*");
605 return buf;
606 }
607 price = city_production_buy_gold_cost(pcity);
608 turns = city_production_turns_to_build(pcity, TRUE);
609
610 if (price > 99999) {
611 fc_snprintf(bufone, sizeof(bufone), "---");
612 } else {
613 fc_snprintf(bufone, sizeof(bufone), "%d", price);
614 }
615 if (turns > 999) {
616 fc_snprintf(buftwo, sizeof(buftwo), "--");
617 } else {
618 fc_snprintf(buftwo, sizeof(buftwo), "%3d", turns);
619 }
620 fc_snprintf(buf, sizeof(buf), "%s/%s", buftwo, bufone);
621 return buf;
622 }
623
624 /************************************************************************
625 Returns corruption amount written to string.
626 Returned string is statically allocated and its contents change when
627 this function is called again.
628 *************************************************************************/
cr_entry_corruption(const struct city * pcity,const void * data)629 static const char *cr_entry_corruption(const struct city *pcity,
630 const void *data)
631 {
632 static char buf[8];
633 fc_snprintf(buf, sizeof(buf), "%3d", -(pcity->waste[O_TRADE]));
634 return buf;
635 }
636
637 /************************************************************************
638 Returns waste amount written to string.
639 Returned string is statically allocated and its contents change when
640 this function is called again.
641 *************************************************************************/
cr_entry_waste(const struct city * pcity,const void * data)642 static const char *cr_entry_waste(const struct city *pcity,
643 const void *data)
644 {
645 static char buf[8];
646 fc_snprintf(buf, sizeof(buf), "%3d", -(pcity->waste[O_SHIELD]));
647 return buf;
648 }
649
650 /************************************************************************
651 Returns risk percentage of plague written to string.
652 Returned string is statically allocated and its contents change when
653 this function is called again.
654 *************************************************************************/
cr_entry_plague_risk(const struct city * pcity,const void * data)655 static const char *cr_entry_plague_risk(const struct city *pcity,
656 const void *data)
657 {
658 static char buf[8];
659 if (!game.info.illness_on) {
660 fc_snprintf(buf, sizeof(buf), " -.-");
661 } else {
662 fc_snprintf(buf, sizeof(buf), "%4.1f",
663 (float)city_illness_calc(pcity, NULL, NULL, NULL, NULL)/10.0);
664 }
665 return buf;
666 }
667
668 /************************************************************************
669 Returns number of continent
670 *************************************************************************/
cr_entry_continent(const struct city * pcity,const void * data)671 static const char *cr_entry_continent(const struct city *pcity,
672 const void *data)
673 {
674 static char buf[8];
675 fc_snprintf(buf, sizeof(buf), "%3d", pcity->tile->continent);
676 return buf;
677 }
678
679 /************************************************************************
680 Returns city cma description.
681 Returned string is statically allocated and its contents change when
682 this function is called again.
683 *************************************************************************/
cr_entry_cma(const struct city * pcity,const void * data)684 static const char *cr_entry_cma(const struct city *pcity,
685 const void *data)
686 {
687 return cmafec_get_short_descr_of_city(pcity);
688 }
689
690 /* City report options (which columns get shown)
691 * To add a new entry, you should just have to:
692 * - add a function like those above
693 * - add an entry in the base_city_report_specs[] table
694 */
695
696 /* This generates the function name and the tagname: */
697 #define FUNC_TAG(var) cr_entry_##var, #var
698
699 static const struct city_report_spec base_city_report_specs[] = {
700 { TRUE, -15, 0, NULL, N_("?city:Name"), N_("City Name"),
701 NULL, FUNC_TAG(cityname) },
702 { FALSE, -15, 0, NULL, N_("Nation"), N_("Nation"),
703 NULL, FUNC_TAG(nation) },
704 { TRUE, 2, 1, NULL, N_("?size [short]:Sz"), N_("Size"),
705 NULL, FUNC_TAG(size) },
706 { TRUE, -8, 1, NULL, N_("State"), N_("Celebrating/Peace/Disorder"),
707 NULL, FUNC_TAG(hstate_verbose) },
708 { FALSE, 1, 1, NULL, NULL, N_("Concise *=Celebrating, X=Disorder"),
709 NULL, FUNC_TAG(hstate_concise) },
710
711 { FALSE, 2, 1, NULL, N_("?Happy workers:H"), N_("Workers: Happy"),
712 NULL, FUNC_TAG(happy) },
713 { FALSE, 2, 1, NULL, N_("?Content workers:C"), N_("Workers: Content"),
714 NULL, FUNC_TAG(content) },
715 { FALSE, 2, 1, NULL, N_("?Unhappy workers:U"), N_("Workers: Unhappy"),
716 NULL, FUNC_TAG(unhappy) },
717 { FALSE, 2, 1, NULL, N_("?Angry workers:A"), N_("Workers: Angry"),
718 NULL, FUNC_TAG(angry) },
719 { TRUE, 10, 1, N_("?city:Workers"),
720 N_("?happy/content/unhappy/angry:H/C/U/A"),
721 N_("Workers: Happy, Content, Unhappy, Angry"),
722 NULL, FUNC_TAG(workers) },
723
724 { FALSE, 8, 1, N_("Best"), N_("attack"),
725 N_("Best attacking units"), NULL, FUNC_TAG(attack)},
726 { FALSE, 8, 1, N_("Best"), N_("defense"),
727 N_("Best defending units"), NULL, FUNC_TAG(defense)},
728 { FALSE, 2, 1, N_("Units"),
729 /* TRANS: Header "Number of units inside city" */
730 N_("?Present (units):Here"),
731 N_("Number of units present"), NULL, FUNC_TAG(present) },
732 { FALSE, 2, 1, N_("Units"),
733 /* TRANS: Header "Number of units supported by given city" */
734 N_("?Supported (units):Owned"),
735 N_("Number of units supported"), NULL, FUNC_TAG(supported) },
736
737 { /* TRANS: Header "It will take this many turns before city grows" */
738 TRUE, 14, 1, N_("?food (population):Grow"),
739 N_("?Stock/Target:(Have/Need)"),
740 N_("Turns until growth/famine"),
741 NULL, FUNC_TAG(growturns) },
742
743 { TRUE, 10, 1, N_("Surplus"), N_("?food/production/trade:F/P/T"),
744 N_("Surplus: Food, Production, Trade"),
745 NULL, FUNC_TAG(resources) },
746 { FALSE, 3, 1, NULL, N_("?Food surplus [short]:+F"), N_("Surplus: Food"),
747 NULL, FUNC_TAG(foodplus) },
748 { FALSE, 3, 1, NULL, N_("?Production surplus [short]:+P"),
749 N_("Surplus: Production"), NULL, FUNC_TAG(prodplus) },
750 { FALSE, 3, 1, NULL, N_("?Production loss (waste) [short]:-P"),
751 N_("Waste"), NULL, FUNC_TAG(waste) },
752 { FALSE, 3, 1, NULL, N_("?Trade surplus [short]:+T"), N_("Surplus: Trade"),
753 NULL, FUNC_TAG(tradeplus) },
754 { FALSE, 3, 1, NULL, N_("?Trade loss (corruption) [short]:-T"),
755 N_("Corruption"), NULL, FUNC_TAG(corruption) },
756
757 { TRUE, 10, 1, N_("Economy"), N_("?gold/luxury/science:G/L/S"),
758 N_("Economy: Gold, Luxuries, Science"),
759 NULL, FUNC_TAG(output) },
760 { FALSE, 3, 1, NULL, N_("?Gold:G"), N_("Economy: Gold"),
761 NULL, FUNC_TAG(gold) },
762 { FALSE, 3, 1, NULL, N_("?Luxury:L"), N_("Economy: Luxury"),
763 NULL, FUNC_TAG(luxury) },
764 { FALSE, 3, 1, NULL, N_("?Science:S"), N_("Economy: Science"),
765 NULL, FUNC_TAG(science) },
766 { FALSE, 3, 1, NULL, N_("?Culture:Clt"), N_("Culture (History+Performance)"),
767 NULL, FUNC_TAG(culture) },
768 { FALSE, 3, 1, NULL, N_("?History:Hst"),
769 N_("Culture: History (and gain per turn)"),
770 NULL, FUNC_TAG(history) },
771 { FALSE, 3, 1, NULL, N_("?Performance:Prf"), N_("Culture: Performance"),
772 NULL, FUNC_TAG(performance) },
773 { FALSE, 3, 1, NULL, N_("?Continent:C"), N_("Continent number"),
774 NULL, FUNC_TAG(continent) },
775 { FALSE, 1, 1, N_("?number_trade_routes:n"), N_("?number_trade_routes:R"),
776 N_("Number (and total value) of trade routes"),
777 NULL, FUNC_TAG(trade_routes) },
778 { FALSE, 3, 1, NULL, N_("?pollution [short]:Pol"), N_("Pollution"),
779 NULL, FUNC_TAG(pollution) },
780 { FALSE, 4, 1, N_("?plague risk [short]:Pla"), N_("(%)"),
781 N_("Plague risk per turn"),
782 NULL, FUNC_TAG(plague_risk) },
783 { FALSE, 15, 1, NULL, N_("?cma:Governor"), N_("Citizen Governor"),
784 NULL, FUNC_TAG(cma) },
785
786 /* TRANS: "BS" = "build slots" */
787 { FALSE, 3, 1, NULL, N_("BS"), N_("Maximum units buildable per turn"),
788 NULL, FUNC_TAG(build_slots) },
789 { TRUE, 9, 1, N_("Production"), N_("Turns/Buy"),
790 /*N_("Turns or gold to complete production"), future menu needs translation */
791 N_("Production"),
792 NULL, FUNC_TAG(build_cost) },
793 { TRUE, 0, 1, N_("Currently Building"),
794 N_("?Stock/Target:(Have/Need)"),
795 N_("Currently Building"),
796 NULL, FUNC_TAG(building) }
797 };
798
799 struct city_report_spec *city_report_specs;
800 static int num_creport_cols;
801
802 /******************************************************************
803 Some simple wrappers:
804 ******************************************************************/
num_city_report_spec(void)805 int num_city_report_spec(void)
806 {
807 return num_creport_cols;
808 }
city_report_spec_show_ptr(int i)809 bool *city_report_spec_show_ptr(int i)
810 {
811 return &(city_report_specs[i].show);
812 }
city_report_spec_tagname(int i)813 const char *city_report_spec_tagname(int i)
814 {
815 return city_report_specs[i].tagname;
816 }
817
818 /******************************************************************
819 Initialize city report data. This deals with ruleset-depedent
820 columns and pre-translates the fields (to make things easier on
821 the GUI writers). Should be called before the GUI starts up.
822 ******************************************************************/
init_city_report_game_data(void)823 void init_city_report_game_data(void)
824 {
825 static char sp_explanation[SP_MAX][128];
826 static char sp_explanations[SP_MAX*128];
827 struct city_report_spec *p;
828 int i;
829
830 num_creport_cols = ARRAY_SIZE(base_city_report_specs)
831 + specialist_count() + 1;
832 city_report_specs
833 = fc_realloc(city_report_specs,
834 num_creport_cols * sizeof(*city_report_specs));
835 p = &city_report_specs[0];
836
837 fc_snprintf(sp_explanations, sizeof(sp_explanations),
838 "%s", _("Specialists: "));
839 specialist_type_iterate(sp) {
840 struct specialist *s = specialist_by_number(sp);
841
842 p->show = FALSE;
843 p->width = 2;
844 p->space = 1;
845 p->title1 = Q_("?specialist:S");
846 p->title2 = specialist_abbreviation_translation(s);
847 fc_snprintf(sp_explanation[sp], sizeof(sp_explanation[sp]),
848 _("Specialists: %s"), specialist_plural_translation(s));
849 cat_snprintf(sp_explanations, sizeof(sp_explanations),
850 "%s%s", (sp == 0) ? "" : ", ",
851 specialist_plural_translation(s));
852 p->explanation = sp_explanation[sp];
853 p->data = s;
854 p->func = cr_entry_specialist;
855 p->tagname = specialist_rule_name(s);
856 p++;
857 } specialist_type_iterate_end;
858
859 /* Summary column for all specialists. */
860 {
861 static char sp_summary[128];
862
863 p->show = FALSE;
864 p->width = MAX(7, specialist_count()*2-1);
865 p->space = 1;
866 p->title1 = _("Special");
867 fc_snprintf(sp_summary, sizeof(sp_summary),
868 "%s", specialists_abbreviation_string());
869 p->title2 = sp_summary;
870 p->explanation = sp_explanations;
871 p->data = NULL;
872 p->func = cr_entry_specialists;
873 p->tagname = "specialists";
874 p++;
875 }
876
877 memcpy(p, base_city_report_specs,
878 sizeof(base_city_report_specs));
879
880 for (i = 0; i < ARRAY_SIZE(base_city_report_specs); i++) {
881 if (p->title1) {
882 p->title1 = Q_(p->title1);
883 }
884 if (p->title2) {
885 p->title2 = Q_(p->title2);
886 }
887 p->explanation = _(p->explanation);
888 p++;
889 }
890
891 fc_assert(NUM_CREPORT_COLS == ARRAY_SIZE(base_city_report_specs)
892 + specialist_count() + 1);
893 }
894
895 /**********************************************************************
896 The following several functions allow intelligent sorting city report
897 fields by column. This doesn't necessarily do the right thing, but
898 it's better than sorting alphabetically.
899
900 The GUI gives us two values to compare (as strings). We try to split
901 them into an array of numeric and string fields, then we compare
902 lexicographically. Two numeric fields are compared in the obvious
903 way, two character fields are compared alphabetically. Arbitrarily, a
904 numeric field is sorted before a character field (for "justification"
905 note that numbers are before letters in the ASCII table).
906 **********************************************************************/
907
908 /* A datum is one short string, or one number.
909 A datum_vector represents a long string of alternating strings and
910 numbers. */
911 struct datum {
912 union {
913 float numeric_value;
914 char *string_value;
915 } val;
916 bool is_numeric;
917 };
918 #define SPECVEC_TAG datum
919 #include "specvec.h"
920
921 /**********************************************************************
922 Init a datum from a substring.
923 **********************************************************************/
init_datum_string(struct datum * dat,const char * left,const char * right)924 static void init_datum_string(struct datum *dat, const char *left,
925 const char *right)
926 {
927 int len = right - left;
928
929 dat->is_numeric = FALSE;
930 dat->val.string_value = fc_malloc(len + 1);
931 memcpy(dat->val.string_value, left, len);
932 dat->val.string_value[len] = 0;
933 }
934
935 /**********************************************************************
936 Init a datum from a number (a float because we happen to use
937 strtof).
938 **********************************************************************/
init_datum_number(struct datum * dat,float val)939 static void init_datum_number(struct datum *dat, float val)
940 {
941 dat->is_numeric = TRUE;
942 dat->val.numeric_value = val;
943 }
944
945 /**********************************************************************
946 Free the data associated with a datum -- that is, free the string if
947 it was allocated.
948 **********************************************************************/
free_datum(struct datum * dat)949 static void free_datum(struct datum *dat)
950 {
951 if(!dat->is_numeric) {
952 free(dat->val.string_value);
953 }
954 }
955
956 /**********************************************************************
957 Compare two data items as described above:
958 - numbers in the obvious way
959 - strings alphabetically
960 - number < string for no good reason
961 **********************************************************************/
datum_compare(const struct datum * a,const struct datum * b)962 static int datum_compare(const struct datum *a, const struct datum *b)
963 {
964 if(a->is_numeric == b->is_numeric) {
965 if(a->is_numeric) {
966 if (a->val.numeric_value == b->val.numeric_value) {
967 return 0;
968 } else if (a->val.numeric_value < b->val.numeric_value) {
969 return -1;
970 } else if (a->val.numeric_value > b->val.numeric_value) {
971 return +1;
972 } else {
973 return 0; /* shrug */
974 }
975 } else {
976 return strcmp(a->val.string_value, b->val.string_value);
977 }
978 } else {
979 if(a->is_numeric) {
980 return -1;
981 } else {
982 return 1;
983 }
984 }
985 }
986
987 /**********************************************************************
988 Compare two strings of data lexicographically.
989 **********************************************************************/
data_compare(const struct datum_vector * a,const struct datum_vector * b)990 static int data_compare(const struct datum_vector *a,
991 const struct datum_vector *b)
992 {
993 int i, n;
994
995 n = MIN(a->size, b->size);
996
997 for(i = 0; i < n; i++) {
998 int cmp = datum_compare(&a->p[i], &b->p[i]);
999
1000 if(cmp != 0) {
1001 return cmp;
1002 }
1003 }
1004
1005 /* The first n fields match; whoever has more fields goes last.
1006 If they have equal numbers, the two really are equal. */
1007 return a->size - b->size;
1008 }
1009
1010
1011 /**********************************************************************
1012 Split a string into a vector of datum.
1013 **********************************************************************/
split_string(struct datum_vector * data,const char * str)1014 static void split_string(struct datum_vector *data, const char *str)
1015 {
1016 const char *string_start;
1017
1018 datum_vector_init(data);
1019 string_start = str;
1020 while(*str) {
1021 char *endptr;
1022 float value;
1023
1024 errno = 0;
1025 value = strtof(str, &endptr);
1026 if (errno != 0 || endptr == str || !isfinite(value)) {
1027 /* that wasn't a sensible number; go on */
1028 str++;
1029 } else {
1030 /* that was a number, so stop the string we were parsing, add
1031 it (unless it's empty), then add the number we just parsed */
1032 struct datum d;
1033
1034 if(str != string_start) {
1035 init_datum_string(&d, string_start, str);
1036 datum_vector_append(data, d);
1037 }
1038
1039 init_datum_number(&d, value);
1040 datum_vector_append(data, d);
1041
1042 /* finally, update the string position pointers */
1043 string_start = str = endptr;
1044 }
1045 }
1046
1047 /* if we have anything leftover then it's a string */
1048 if(str != string_start) {
1049 struct datum d;
1050
1051 init_datum_string(&d, string_start, str);
1052 datum_vector_append(data, d);
1053 }
1054 }
1055
1056 /**********************************************************************
1057 Free every datum in the vector.
1058 **********************************************************************/
free_data(struct datum_vector * data)1059 static void free_data(struct datum_vector *data)
1060 {
1061 int i;
1062
1063 for(i = 0; i < data->size; i++) {
1064 free_datum(&data->p[i]);
1065 }
1066 datum_vector_free(data);
1067 }
1068
1069
1070 /**********************************************************************
1071 The real function: split the two strings, and compare them.
1072 **********************************************************************/
cityrepfield_compare(const char * str1,const char * str2)1073 int cityrepfield_compare(const char *str1, const char *str2)
1074 {
1075 struct datum_vector data1, data2;
1076 int retval;
1077
1078 if (str1 == str2) {
1079 return 0;
1080 } else if (NULL == str1) {
1081 return 1;
1082 } else if (NULL == str2) {
1083 return -1;
1084 }
1085
1086 split_string(&data1, str1);
1087 split_string(&data2, str2);
1088
1089 retval = data_compare(&data1, &data2);
1090
1091 free_data(&data1);
1092 free_data(&data2);
1093
1094 return retval;
1095 }
1096