Rework civ2civ3 ruleset help and documentation.
[freeciv.git] / client / cityrepdata.c
blob7a7b544a9bc1ccd4551923569e1ce9a2ff2d0291
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.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <errno.h>
19 #include <math.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 /* utility */
25 #include "fcintl.h"
26 #include "log.h"
27 #include "support.h"
29 /* common */
30 #include "city.h"
31 #include "game.h"
32 #include "map.h"
33 #include "specialist.h"
34 #include "unitlist.h"
36 /* agents */
37 #include "cma_fec.h"
39 /* client */
40 #include "citydlg_common.h" /* city_production_cost_str() */
41 #include "options.h"
43 #include "cityrepdata.h"
45 /************************************************************************
46 cr_entry = return an entry (one column for one city) for the city report
47 These return ptrs to filled in static strings.
48 Note the returned string may not be exactly the right length; that
49 is handled later.
50 *************************************************************************/
52 static const char *cr_entry_cityname(const struct city *pcity,
53 const void *data)
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(pcity);
61 /************************************************************************
62 Translated name of nation who owns this city.
63 *************************************************************************/
64 static const char *cr_entry_nation(const struct city *pcity,
65 const void *data)
67 return nation_adjective_for_player(city_owner(pcity));
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 *************************************************************************/
74 static const char *cr_entry_size(const struct city *pcity,
75 const void *data)
77 static char buf[8];
78 fc_snprintf(buf, sizeof(buf), "%2d", city_size_get(pcity));
79 return buf;
82 /************************************************************************
83 Returns concise city happiness state written to string.
84 Returned string is statically allocated and its contents change when
85 this function is called again.
86 *************************************************************************/
87 static const char *cr_entry_hstate_concise(const struct city *pcity,
88 const void *data)
90 static char buf[4];
91 fc_snprintf(buf, sizeof(buf), "%s",
92 (city_celebrating(pcity) ? "*"
93 : (city_unhappy(pcity) ? "X" : " ")));
94 return buf;
97 /************************************************************************
98 Returns verbose city happiness state written to string.
99 Returned string is statically allocated and its contents change when
100 this function is called again.
101 *************************************************************************/
102 static const char *cr_entry_hstate_verbose(const struct city *pcity,
103 const void *data)
105 static char buf[16];
106 fc_snprintf(buf, sizeof(buf), "%s",
107 (city_celebrating(pcity) ? Q_("?city_state:Celebrating")
108 : (city_unhappy(pcity) ? Q_("?city_state:Disorder")
109 : Q_("?city_state:Peace"))));
110 return buf;
113 /************************************************************************
114 Returns number of citizens of each happiness state written to string.
115 Returned string is statically allocated and its contents change when
116 this function is called again.
117 *************************************************************************/
118 static const char *cr_entry_workers(const struct city *pcity,
119 const void *data)
121 static char buf[32];
122 fc_snprintf(buf, sizeof(buf), "%d/%d/%d/%d",
123 pcity->feel[CITIZEN_HAPPY][FEELING_FINAL],
124 pcity->feel[CITIZEN_CONTENT][FEELING_FINAL],
125 pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL],
126 pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
127 return buf;
130 /************************************************************************
131 Returns number of happy citizens written to string.
132 Returned string is statically allocated and its contents change when
133 this function is called again.
134 *************************************************************************/
135 static const char *cr_entry_happy(const struct city *pcity,
136 const void *data)
138 static char buf[8];
139 fc_snprintf(buf, sizeof(buf), "%2d",
140 pcity->feel[CITIZEN_HAPPY][FEELING_FINAL]);
141 return buf;
144 /************************************************************************
145 Returns number of content citizens written to string.
146 Returned string is statically allocated and its contents change when
147 this function is called again.
148 *************************************************************************/
149 static const char *cr_entry_content(const struct city *pcity,
150 const void *data)
152 static char buf[8];
153 fc_snprintf(buf, sizeof(buf), "%2d",
154 pcity->feel[CITIZEN_CONTENT][FEELING_FINAL]);
155 return buf;
158 /************************************************************************
159 Returns number of unhappy citizens written to string.
160 Returned string is statically allocated and its contents change when
161 this function is called again.
162 *************************************************************************/
163 static const char *cr_entry_unhappy(const struct city *pcity,
164 const void *data)
166 static char buf[8];
167 fc_snprintf(buf, sizeof(buf), "%2d",
168 pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]);
169 return buf;
172 /************************************************************************
173 Returns number of angry citizens written to string.
174 Returned string is statically allocated and its contents change when
175 this function is called again.
176 *************************************************************************/
177 static const char *cr_entry_angry(const struct city *pcity,
178 const void *data)
180 static char buf[8];
181 fc_snprintf(buf, sizeof(buf), "%2d",
182 pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
183 return buf;
186 /************************************************************************
187 Returns list of specialists written to string.
188 Returned string is statically allocated and its contents change when
189 this function is called again.
190 *************************************************************************/
191 static const char *cr_entry_specialists(const struct city *pcity,
192 const void *data)
194 return specialists_string(pcity->specialists);
197 /************************************************************************
198 Returns number of specialists of type given as data written to string.
199 Returned string is statically allocated and its contents change when
200 this function is called again.
201 *************************************************************************/
202 static const char *cr_entry_specialist(const struct city *pcity,
203 const void *data)
205 static char buf[8];
206 const struct specialist *sp = data;
208 fc_snprintf(buf, sizeof(buf), "%2d",
209 pcity->specialists[specialist_index(sp)]);
210 return buf;
213 /************************************************************************
214 Returns string with best attack values of units in city.
215 Returned string is statically allocated and its contents change when
216 this function is called again.
217 *************************************************************************/
218 static const char *cr_entry_attack(const struct city *pcity,
219 const void *data)
221 static char buf[32];
222 int attack_best[4] = {-1, -1, -1, -1}, i;
224 unit_list_iterate(pcity->tile->units, punit) {
225 /* What about allied units? Should we just count them? */
226 attack_best[3] = unit_type(punit)->attack_strength;
228 /* Now that the element is appended to the end of the list, we simply
229 do an insertion sort. */
230 for (i = 2; i >= 0 && attack_best[i] < attack_best[i + 1]; i--) {
231 int tmp = attack_best[i];
232 attack_best[i] = attack_best[i + 1];
233 attack_best[i + 1] = tmp;
235 } unit_list_iterate_end;
237 buf[0] = '\0';
238 for (i = 0; i < 3; i++) {
239 if (attack_best[i] >= 0) {
240 cat_snprintf(buf, sizeof(buf), "%s%d", (i > 0) ? "/" : "",
241 attack_best[i]);
242 } else {
243 cat_snprintf(buf, sizeof(buf), "%s-", (i > 0) ? "/" : "");
247 return buf;
250 /************************************************************************
251 Returns string with best defend values of units in city.
252 Returned string is statically allocated and its contents change when
253 this function is called again.
254 *************************************************************************/
255 static const char *cr_entry_defense(const struct city *pcity,
256 const void *data)
258 static char buf[32];
259 int defense_best[4] = {-1, -1, -1, -1}, i;
261 unit_list_iterate(pcity->tile->units, punit) {
262 /* What about allied units? Should we just count them? */
263 defense_best[3] = unit_type(punit)->defense_strength;
265 /* Now that the element is appended to the end of the list, we simply
266 do an insertion sort. */
267 for (i = 2; i >= 0 && defense_best[i] < defense_best[i + 1]; i--) {
268 int tmp = defense_best[i];
269 defense_best[i] = defense_best[i + 1];
270 defense_best[i + 1] = tmp;
272 } unit_list_iterate_end;
274 buf[0] = '\0';
275 for (i = 0; i < 3; i++) {
276 if (defense_best[i] >= 0) {
277 cat_snprintf(buf, sizeof(buf), "%s%d", (i > 0) ? "/" : "",
278 defense_best[i]);
279 } else {
280 cat_snprintf(buf, sizeof(buf), "%s-", (i > 0) ? "/" : "");
284 return buf;
287 /************************************************************************
288 Returns number of supported units written to string.
289 Returned string is statically allocated and its contents change when
290 this function is called again.
291 *************************************************************************/
292 static const char *cr_entry_supported(const struct city *pcity,
293 const void *data)
295 static char buf[8];
296 int num_supported = unit_list_size(pcity->units_supported);
298 fc_snprintf(buf, sizeof(buf), "%2d", num_supported);
300 return buf;
303 /************************************************************************
304 Returns number of present units written to string.
305 Returned string is statically allocated and its contents change when
306 this function is called again.
307 *************************************************************************/
308 static const char *cr_entry_present(const struct city *pcity,
309 const void *data)
311 static char buf[8];
312 int num_present = unit_list_size(pcity->tile->units);
314 fc_snprintf(buf, sizeof(buf), "%2d", num_present);
316 return buf;
319 /************************************************************************
320 Returns string listing amounts of resources.
321 Returned string is statically allocated and its contents change when
322 this function is called again.
323 *************************************************************************/
324 static const char *cr_entry_resources(const struct city *pcity,
325 const void *data)
327 static char buf[32];
328 fc_snprintf(buf, sizeof(buf), "%d/%d/%d",
329 pcity->surplus[O_FOOD],
330 pcity->surplus[O_SHIELD],
331 pcity->surplus[O_TRADE]);
332 return buf;
335 /************************************************************************
336 Returns food surplus written to string.
337 Returned string is statically allocated and its contents change when
338 this function is called again.
339 *************************************************************************/
340 static const char *cr_entry_foodplus(const struct city *pcity,
341 const void *data)
343 static char buf[8];
344 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_FOOD]);
345 return buf;
348 /************************************************************************
349 Returns production surplus written to string.
350 Returned string is statically allocated and its contents change when
351 this function is called again.
352 *************************************************************************/
353 static const char *cr_entry_prodplus(const struct city *pcity,
354 const void *data)
356 static char buf[8];
357 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_SHIELD]);
358 return buf;
361 /************************************************************************
362 Returns trade surplus written to string.
363 Returned string is statically allocated and its contents change when
364 this function is called again.
365 *************************************************************************/
366 static const char *cr_entry_tradeplus(const struct city *pcity,
367 const void *data)
369 static char buf[8];
370 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_TRADE]);
371 return buf;
374 /************************************************************************
375 Returns string describing resource output.
376 Returned string is statically allocated and its contents change when
377 this function is called again.
378 *************************************************************************/
379 static const char *cr_entry_output(const struct city *pcity,
380 const void *data)
382 static char buf[32];
383 int goldie = pcity->surplus[O_GOLD];
385 fc_snprintf(buf, sizeof(buf), "%3d/%d/%d",
386 goldie, pcity->prod[O_LUXURY], pcity->prod[O_SCIENCE]);
387 return buf;
390 /************************************************************************
391 Returns gold surplus written to string.
392 Returned string is statically allocated and its contents change when
393 this function is called again.
394 *************************************************************************/
395 static const char *cr_entry_gold(const struct city *pcity,
396 const void *data)
398 static char buf[8];
400 if (pcity->surplus[O_GOLD] > 0) {
401 fc_snprintf(buf, sizeof(buf), "+%d", pcity->surplus[O_GOLD]);
402 } else {
403 fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_GOLD]);
405 return buf;
408 /************************************************************************
409 Returns luxury output written to string.
410 Returned string is statically allocated and its contents change when
411 this function is called again.
412 *************************************************************************/
413 static const char *cr_entry_luxury(const struct city *pcity,
414 const void *data)
416 static char buf[8];
417 fc_snprintf(buf, sizeof(buf), "%3d", pcity->prod[O_LUXURY]);
418 return buf;
421 /************************************************************************
422 Returns science output written to string.
423 Returned string is statically allocated and its contents change when
424 this function is called again.
425 *************************************************************************/
426 static const char *cr_entry_science(const struct city *pcity,
427 const void *data)
429 static char buf[8];
430 fc_snprintf(buf, sizeof(buf), "%3d", pcity->prod[O_SCIENCE]);
431 return buf;
434 /************************************************************************
435 Returns number of turns before city grows written to string.
436 Returned string is statically allocated and its contents change when
437 this function is called again.
438 *************************************************************************/
439 static const char *cr_entry_growturns(const struct city *pcity,
440 const void *data)
442 int turns = city_turns_to_grow(pcity);
443 char buffer[8];
444 static char buf[32];
446 if (turns == FC_INFINITY) {
447 /* 'never' wouldn't be easily translatable here. */
448 fc_snprintf(buffer, sizeof(buffer), "---");
449 } else {
450 /* Shrinking cities get a negative value. */
451 fc_snprintf(buffer, sizeof(buffer), "%4d", turns);
453 fc_snprintf(buf, sizeof(buf), "%s (%d/%d)",
454 buffer, pcity->food_stock,
455 city_granary_size(city_size_get(pcity)));
456 return buf;
459 /************************************************************************
460 Returns pollution output written to string.
461 Returned string is statically allocated and its contents change when
462 this function is called again.
463 *************************************************************************/
464 static const char *cr_entry_pollution(const struct city *pcity,
465 const void *data)
467 static char buf[8];
468 fc_snprintf(buf, sizeof(buf), "%3d", pcity->pollution);
469 return buf;
472 /************************************************************************
473 Returns number and output of trade routes written to string.
474 Returned string is statically allocated and its contents change when
475 this function is called again.
476 *************************************************************************/
477 static const char *cr_entry_trade_routes(const struct city *pcity,
478 const void *data)
480 static char buf[16];
481 int num = 0, value = 0, i;
483 for (i = 0; i < MAX_TRADE_ROUTES; i++) {
484 if (0 != pcity->trade[i]) {
485 num++;
486 value += pcity->trade_value[i];
490 if (0 == num) {
491 sz_strlcpy(buf, "0");
492 } else {
493 fc_snprintf(buf, sizeof(buf), "%d (+%d)", num, value);
495 return buf;
498 /************************************************************************
499 Returns number of build slots written to string.
500 Returned string is statically allocated and its contents change when
501 this function is called again.
502 *************************************************************************/
503 static const char *cr_entry_build_slots(const struct city *pcity,
504 const void *data)
506 static char buf[8];
507 fc_snprintf(buf, sizeof(buf), "%3d", city_build_slots(pcity));
508 return buf;
511 /************************************************************************
512 Returns name of current production.
513 Returned string is statically allocated and its contents change when
514 this function is called again.
515 *************************************************************************/
516 static const char *cr_entry_building(const struct city *pcity,
517 const void *data)
519 static char buf[128];
520 const char *from_worklist =
521 worklist_is_empty(&pcity->worklist) ? "" :
522 concise_city_production ? "+" : _("(worklist)");
524 if (city_production_has_flag(pcity, IF_GOLD)) {
525 fc_snprintf(buf, sizeof(buf), "%s (%d)%s",
526 city_production_name_translation(pcity),
527 MAX(0, pcity->surplus[O_SHIELD]), from_worklist);
528 } else {
529 fc_snprintf(buf, sizeof(buf), "%s (%d/%s)%s",
530 city_production_name_translation(pcity),
531 pcity->shield_stock,
532 city_production_cost_str(pcity),
533 from_worklist);
536 return buf;
539 /************************************************************************
540 Returns cost of buying current production and turns to completion
541 written to string. Returned string is statically allocated and its
542 contents change when this function is called again.
543 *************************************************************************/
544 static const char *cr_entry_build_cost(const struct city *pcity,
545 const void *data)
547 char bufone[8];
548 char buftwo[8];
549 static char buf[32];
550 int price;
551 int turns;
553 if (city_production_has_flag(pcity, IF_GOLD)) {
554 fc_snprintf(buf, sizeof(buf), "*");
555 return buf;
557 price = city_production_buy_gold_cost(pcity);
558 turns = city_production_turns_to_build(pcity, TRUE);
560 if (price > 99999) {
561 fc_snprintf(bufone, sizeof(bufone), "---");
562 } else {
563 fc_snprintf(bufone, sizeof(bufone), "%d", price);
565 if (turns > 999) {
566 fc_snprintf(buftwo, sizeof(buftwo), "--");
567 } else {
568 fc_snprintf(buftwo, sizeof(buftwo), "%3d", turns);
570 fc_snprintf(buf, sizeof(buf), "%s/%s", buftwo, bufone);
571 return buf;
574 /************************************************************************
575 Returns corruption amount written to string.
576 Returned string is statically allocated and its contents change when
577 this function is called again.
578 *************************************************************************/
579 static const char *cr_entry_corruption(const struct city *pcity,
580 const void *data)
582 static char buf[8];
583 fc_snprintf(buf, sizeof(buf), "%3d", -(pcity->waste[O_TRADE]));
584 return buf;
587 /************************************************************************
588 Returns waste amount written to string.
589 Returned string is statically allocated and its contents change when
590 this function is called again.
591 *************************************************************************/
592 static const char *cr_entry_waste(const struct city *pcity,
593 const void *data)
595 static char buf[8];
596 fc_snprintf(buf, sizeof(buf), "%3d", -(pcity->waste[O_SHIELD]));
597 return buf;
600 /************************************************************************
601 Returns risk percentage of plague written to string.
602 Returned string is statically allocated and its contents change when
603 this function is called again.
604 *************************************************************************/
605 static const char *cr_entry_plague_risk(const struct city *pcity,
606 const void *data)
608 static char buf[8];
609 if (!game.info.illness_on) {
610 fc_snprintf(buf, sizeof(buf), " -.-");
611 } else {
612 fc_snprintf(buf, sizeof(buf), "%4.1f",
613 (float)city_illness_calc(pcity, NULL, NULL, NULL, NULL)/10.0);
615 return buf;
618 /************************************************************************
619 Returns city cma description.
620 Returned string is statically allocated and its contents change when
621 this function is called again.
622 *************************************************************************/
623 static const char *cr_entry_cma(const struct city *pcity,
624 const void *data)
626 return cmafec_get_short_descr_of_city(pcity);
629 /* City report options (which columns get shown)
630 * To add a new entry, you should just have to:
631 * - add a function like those above
632 * - add an entry in the base_city_report_specs[] table
635 /* This generates the function name and the tagname: */
636 #define FUNC_TAG(var) cr_entry_##var, #var
638 static const struct city_report_spec base_city_report_specs[] = {
639 { TRUE, -15, 0, NULL, N_("?city:Name"), N_("City Name"),
640 NULL, FUNC_TAG(cityname) },
641 { FALSE, -15, 0, NULL, N_("Nation"), N_("Nation"),
642 NULL, FUNC_TAG(nation) },
643 { TRUE, 2, 1, NULL, N_("?size [short]:Sz"), N_("Size"),
644 NULL, FUNC_TAG(size) },
645 { TRUE, -8, 1, NULL, N_("State"), N_("Celebrating/Peace/Disorder"),
646 NULL, FUNC_TAG(hstate_verbose) },
647 { FALSE, 1, 1, NULL, NULL, N_("Concise *=Celebrating, X=Disorder"),
648 NULL, FUNC_TAG(hstate_concise) },
650 { FALSE, 2, 1, NULL, N_("?Happy workers:H"), N_("Workers: Happy"),
651 NULL, FUNC_TAG(happy) },
652 { FALSE, 2, 1, NULL, N_("?Content workers:C"), N_("Workers: Content"),
653 NULL, FUNC_TAG(content) },
654 { FALSE, 2, 1, NULL, N_("?Unhappy workers:U"), N_("Workers: Unhappy"),
655 NULL, FUNC_TAG(unhappy) },
656 { FALSE, 2, 1, NULL, N_("?Angry workers:A"), N_("Workers: Angry"),
657 NULL, FUNC_TAG(angry) },
658 { TRUE, 10, 1, N_("?city:Workers"),
659 N_("?happy/content/unhappy/angry:H/C/U/A"),
660 N_("Workers: Happy, Content, Unhappy, Angry"),
661 NULL, FUNC_TAG(workers) },
663 { FALSE, 8, 1, N_("Best"), N_("attack"),
664 N_("Best attacking units"), NULL, FUNC_TAG(attack)},
665 { FALSE, 8, 1, N_("Best"), N_("defense"),
666 N_("Best defending units"), NULL, FUNC_TAG(defense)},
667 { FALSE, 2, 1, N_("Units"),
668 /* TRANS: Header "Number of units inside city" */
669 N_("?Present (units):Here"),
670 N_("Number of units present"), NULL, FUNC_TAG(present) },
671 { FALSE, 2, 1, N_("Units"),
672 /* TRANS: Header "Number of units supported by given city" */
673 N_("?Supported (units):Owned"),
674 N_("Number of units supported"), NULL, FUNC_TAG(supported) },
676 { /* TRANS: Header "It will take this many turns before city grows" */
677 TRUE, 14, 1, N_("?food (population):Grow"),
678 N_("?Stock/Target:(Have/Need)"),
679 N_("Turns until growth/famine"),
680 NULL, FUNC_TAG(growturns) },
682 { TRUE, 10, 1, N_("Surplus"), N_("?food/production/trade:F/P/T"),
683 N_("Surplus: Food, Production, Trade"),
684 NULL, FUNC_TAG(resources) },
685 { FALSE, 3, 1, NULL, N_("?Food surplus [short]:+F"), N_("Surplus: Food"),
686 NULL, FUNC_TAG(foodplus) },
687 { FALSE, 3, 1, NULL, N_("?Production surplus [short]:+P"),
688 N_("Surplus: Production"), NULL, FUNC_TAG(prodplus) },
689 { FALSE, 3, 1, NULL, N_("?Production loss (waste) [short]:-P"),
690 N_("Waste"), NULL, FUNC_TAG(waste) },
691 { FALSE, 3, 1, NULL, N_("?Trade surplus [short]:+T"), N_("Surplus: Trade"),
692 NULL, FUNC_TAG(tradeplus) },
693 { FALSE, 3, 1, NULL, N_("?Trade loss (corruption) [short]:-T"),
694 N_("Corruption"), NULL, FUNC_TAG(corruption) },
696 { TRUE, 10, 1, N_("Economy"), N_("?gold/luxury/science:G/L/S"),
697 N_("Economy: Gold, Luxuries, Science"),
698 NULL, FUNC_TAG(output) },
699 { FALSE, 3, 1, NULL, N_("?Gold:G"), N_("Economy: Gold"),
700 NULL, FUNC_TAG(gold) },
701 { FALSE, 3, 1, NULL, N_("?Luxury:L"), N_("Economy: Luxury"),
702 NULL, FUNC_TAG(luxury) },
703 { FALSE, 3, 1, NULL, N_("?Science:S"), N_("Economy: Science"),
704 NULL, FUNC_TAG(science) },
705 { FALSE, 1, 1, N_("?number_trade_routes:n"), N_("?number_trade_routes:R"),
706 N_("Number (and total value) of trade routes"),
707 NULL, FUNC_TAG(trade_routes) },
708 { FALSE, 3, 1, NULL, N_("?pollution [short]:Pol"), N_("Pollution"),
709 NULL, FUNC_TAG(pollution) },
710 { FALSE, 4, 1, N_("?plague risk [short]:Pla"), N_("(%)"), N_("Plague risk"),
711 NULL, FUNC_TAG(plague_risk) },
712 { FALSE, 15, 1, NULL, N_("?cma:Governor"), N_("Citizen Governor"),
713 NULL, FUNC_TAG(cma) },
715 /* TRANS: "BS" = "build slots" */
716 { FALSE, 3, 1, NULL, N_("BS"), N_("Maximum units buildable per turn"),
717 NULL, FUNC_TAG(build_slots) },
718 { TRUE, 9, 1, N_("Production"), N_("Turns/Buy"),
719 /*N_("Turns or gold to complete production"), future menu needs translation */
720 N_("Production"),
721 NULL, FUNC_TAG(build_cost) },
722 { TRUE, 0, 1, N_("Currently Building"),
723 N_("?Stock/Target:(Have/Need)"),
724 N_("Currently Building"),
725 NULL, FUNC_TAG(building) }
728 struct city_report_spec *city_report_specs;
729 static int num_creport_cols;
731 /******************************************************************
732 Some simple wrappers:
733 ******************************************************************/
734 int num_city_report_spec(void)
736 return num_creport_cols;
738 bool *city_report_spec_show_ptr(int i)
740 return &(city_report_specs[i].show);
742 const char *city_report_spec_tagname(int i)
744 return city_report_specs[i].tagname;
747 /******************************************************************
748 Initialize city report data. This deals with ruleset-depedent
749 columns and pre-translates the fields (to make things easier on
750 the GUI writers). Should be called before the GUI starts up.
751 ******************************************************************/
752 void init_city_report_game_data(void)
754 static char sp_explanation[SP_MAX][128];
755 static char sp_explanations[SP_MAX*128];
756 struct city_report_spec *p;
757 int i;
759 num_creport_cols = ARRAY_SIZE(base_city_report_specs)
760 + specialist_count() + 1;
761 city_report_specs
762 = fc_realloc(city_report_specs,
763 num_creport_cols * sizeof(*city_report_specs));
764 p = &city_report_specs[0];
766 fc_snprintf(sp_explanations, sizeof(sp_explanations),
767 "%s", _("Specialists: "));
768 specialist_type_iterate(i) {
769 struct specialist *s = specialist_by_number(i);
770 p->show = FALSE;
771 p->width = 2;
772 p->space = 1;
773 p->title1 = Q_("?specialist:S");
774 p->title2 = specialist_abbreviation_translation(s);
775 fc_snprintf(sp_explanation[i], sizeof(sp_explanation[i]),
776 _("Specialists: %s"), specialist_plural_translation(s));
777 cat_snprintf(sp_explanations, sizeof(sp_explanations),
778 "%s%s", (i == 0) ? "" : ", ",
779 specialist_plural_translation(s));
780 p->explanation = sp_explanation[i];
781 p->data = s;
782 p->func = cr_entry_specialist;
783 p->tagname = specialist_rule_name(s);
784 p++;
785 } specialist_type_iterate_end;
787 /* Summary column for all specialists. */
789 static char sp_summary[128];
790 p->show = FALSE;
791 p->width = MAX(7, specialist_count()*2-1);
792 p->space = 1;
793 p->title1 = _("Special");
794 fc_snprintf(sp_summary, sizeof(sp_summary),
795 "%s", specialists_abbreviation_string());
796 p->title2 = sp_summary;
797 p->explanation = sp_explanations;
798 p->data = NULL;
799 p->func = cr_entry_specialists;
800 p->tagname = "specialists";
801 p++;
804 memcpy(p, base_city_report_specs,
805 sizeof(base_city_report_specs));
807 for (i = 0; i < ARRAY_SIZE(base_city_report_specs); i++) {
808 if (p->title1) {
809 p->title1 = Q_(p->title1);
811 if (p->title2) {
812 p->title2 = Q_(p->title2);
814 p->explanation = _(p->explanation);
815 p++;
818 fc_assert(NUM_CREPORT_COLS == ARRAY_SIZE(base_city_report_specs)
819 + specialist_count() + 1);
822 /**********************************************************************
823 The following several functions allow intelligent sorting city report
824 fields by column. This doesn't necessarily do the right thing, but
825 it's better than sorting alphabetically.
827 The GUI gives us two values to compare (as strings). We try to split
828 them into an array of numeric and string fields, then we compare
829 lexicographically. Two numeric fields are compared in the obvious
830 way, two character fields are compared alphabetically. Arbitrarily, a
831 numeric field is sorted before a character field (for "justification"
832 note that numbers are before letters in the ASCII table).
833 **********************************************************************/
835 /* A datum is one short string, or one number.
836 A datum_vector represents a long string of alternating strings and
837 numbers. */
838 struct datum {
839 union {
840 float numeric_value;
841 char *string_value;
842 } val;
843 bool is_numeric;
845 #define SPECVEC_TAG datum
846 #include "specvec.h"
848 /**********************************************************************
849 Init a datum from a substring.
850 **********************************************************************/
851 static void init_datum_string(struct datum *dat, const char *left,
852 const char *right)
854 int len = right - left;
856 dat->is_numeric = FALSE;
857 dat->val.string_value = fc_malloc(len + 1);
858 memcpy(dat->val.string_value, left, len);
859 dat->val.string_value[len] = 0;
862 /**********************************************************************
863 Init a datum from a number (a float because we happen to use
864 strtof).
865 **********************************************************************/
866 static void init_datum_number(struct datum *dat, float val)
868 dat->is_numeric = TRUE;
869 dat->val.numeric_value = val;
872 /**********************************************************************
873 Free the data associated with a datum -- that is, free the string if
874 it was allocated.
875 **********************************************************************/
876 static void free_datum(struct datum *dat)
878 if(!dat->is_numeric) {
879 free(dat->val.string_value);
883 /**********************************************************************
884 Compare two data items as described above:
885 - numbers in the obvious way
886 - strings alphabetically
887 - number < string for no good reason
888 **********************************************************************/
889 static int datum_compare(const struct datum *a, const struct datum *b)
891 if(a->is_numeric == b->is_numeric) {
892 if(a->is_numeric) {
893 if (a->val.numeric_value == b->val.numeric_value) {
894 return 0;
895 } else if (a->val.numeric_value < b->val.numeric_value) {
896 return -1;
897 } else if (a->val.numeric_value > b->val.numeric_value) {
898 return +1;
899 } else {
900 return 0; /* shrug */
902 } else {
903 return strcmp(a->val.string_value, b->val.string_value);
905 } else {
906 if(a->is_numeric) {
907 return -1;
908 } else {
909 return 1;
914 /**********************************************************************
915 Compare two strings of data lexicographically.
916 **********************************************************************/
917 static int data_compare(const struct datum_vector *a,
918 const struct datum_vector *b)
920 int i, n;
922 n = MIN(a->size, b->size);
924 for(i = 0; i < n; i++) {
925 int cmp = datum_compare(&a->p[i], &b->p[i]);
927 if(cmp != 0) {
928 return cmp;
932 /* The first n fields match; whoever has more fields goes last.
933 If they have equal numbers, the two really are equal. */
934 return a->size - b->size;
938 /**********************************************************************
939 Split a string into a vector of datum.
940 **********************************************************************/
941 static void split_string(struct datum_vector *data, const char *str)
943 const char *string_start;
945 datum_vector_init(data);
946 string_start = str;
947 while(*str) {
948 char *endptr;
949 float value;
951 errno = 0;
952 value = strtof(str, &endptr);
953 if (errno != 0 || endptr == str || !isfinite(value)) {
954 /* that wasn't a sensible number; go on */
955 str++;
956 } else {
957 /* that was a number, so stop the string we were parsing, add
958 it (unless it's empty), then add the number we just parsed */
959 struct datum d;
961 if(str != string_start) {
962 init_datum_string(&d, string_start, str);
963 datum_vector_append(data, d);
966 init_datum_number(&d, value);
967 datum_vector_append(data, d);
969 /* finally, update the string position pointers */
970 string_start = str = endptr;
974 /* if we have anything leftover then it's a string */
975 if(str != string_start) {
976 struct datum d;
978 init_datum_string(&d, string_start, str);
979 datum_vector_append(data, d);
983 /**********************************************************************
984 Free every datum in the vector.
985 **********************************************************************/
986 static void free_data(struct datum_vector *data)
988 int i;
990 for(i = 0; i < data->size; i++) {
991 free_datum(&data->p[i]);
993 datum_vector_free(data);
997 /**********************************************************************
998 The real function: split the two strings, and compare them.
999 **********************************************************************/
1000 int cityrepfield_compare(const char *str1, const char *str2)
1002 struct datum_vector data1, data2;
1003 int retval;
1005 if (str1 == str2) {
1006 return 0;
1007 } else if (NULL == str1) {
1008 return 1;
1009 } else if (NULL == str2) {
1010 return -1;
1013 split_string(&data1, str1);
1014 split_string(&data2, str2);
1016 retval = data_compare(&data1, &data2);
1018 free_data(&data1);
1019 free_data(&data2);
1021 return retval;