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)
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 ***********************************************************************/
15 #include <fc_config.h>
33 #include "specialist.h"
40 #include "citydlg_common.h" /* city_production_cost_str() */
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
50 *************************************************************************/
51 static const char *cr_entry_cityname(const struct city
*pcity
,
54 /* We used to truncate the name to 14 bytes. This should not be needed
55 * in any modern GUI library and may give an invalid string if a
56 * multibyte character is clipped. */
57 return city_name_get(pcity
);
60 /************************************************************************
61 Translated name of nation who owns this city.
62 *************************************************************************/
63 static const char *cr_entry_nation(const struct city
*pcity
,
66 return nation_adjective_for_player(city_owner(pcity
));
69 /************************************************************************
70 Returns city size written to string. Returned string is statically
71 allocated and its contents change when this function is called again.
72 *************************************************************************/
73 static const char *cr_entry_size(const struct city
*pcity
,
78 fc_snprintf(buf
, sizeof(buf
), "%2d", city_size_get(pcity
));
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
,
91 fc_snprintf(buf
, sizeof(buf
), "%s",
92 (city_celebrating(pcity
) ? "*"
93 : (city_unhappy(pcity
) ? "X" : " ")));
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
,
107 fc_snprintf(buf
, sizeof(buf
), "%s",
108 (city_celebrating(pcity
) ? Q_("?city_state:Celebrating")
109 : (city_unhappy(pcity
) ? Q_("?city_state:Disorder")
110 : Q_("?city_state:Peace"))));
114 /************************************************************************
115 Returns number of citizens of each happiness state written to string.
116 Returned string is statically allocated and its contents change when
117 this function is called again.
118 *************************************************************************/
119 static const char *cr_entry_workers(const struct city
*pcity
,
124 fc_snprintf(buf
, sizeof(buf
), "%d/%d/%d/%d",
125 pcity
->feel
[CITIZEN_HAPPY
][FEELING_FINAL
],
126 pcity
->feel
[CITIZEN_CONTENT
][FEELING_FINAL
],
127 pcity
->feel
[CITIZEN_UNHAPPY
][FEELING_FINAL
],
128 pcity
->feel
[CITIZEN_ANGRY
][FEELING_FINAL
]);
132 /************************************************************************
133 Returns number of happy citizens written to string.
134 Returned string is statically allocated and its contents change when
135 this function is called again.
136 *************************************************************************/
137 static const char *cr_entry_happy(const struct city
*pcity
,
141 fc_snprintf(buf
, sizeof(buf
), "%2d",
142 pcity
->feel
[CITIZEN_HAPPY
][FEELING_FINAL
]);
146 /************************************************************************
147 Returns city culture written to string
148 *************************************************************************/
149 static const char *cr_entry_culture(const struct city
*pcity
,
153 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->client
.culture
);
157 /************************************************************************
158 Returns city history written to string
159 *************************************************************************/
160 static const char *cr_entry_history(const struct city
*pcity
,
164 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->history
);
168 /************************************************************************
169 Returns number of content citizens written to string.
170 Returned string is statically allocated and its contents change when
171 this function is called again.
172 *************************************************************************/
173 static const char *cr_entry_content(const struct city
*pcity
,
177 fc_snprintf(buf
, sizeof(buf
), "%2d",
178 pcity
->feel
[CITIZEN_CONTENT
][FEELING_FINAL
]);
182 /************************************************************************
183 Returns number of unhappy citizens written to string.
184 Returned string is statically allocated and its contents change when
185 this function is called again.
186 *************************************************************************/
187 static const char *cr_entry_unhappy(const struct city
*pcity
,
191 fc_snprintf(buf
, sizeof(buf
), "%2d",
192 pcity
->feel
[CITIZEN_UNHAPPY
][FEELING_FINAL
]);
196 /************************************************************************
197 Returns number of angry citizens written to string.
198 Returned string is statically allocated and its contents change when
199 this function is called again.
200 *************************************************************************/
201 static const char *cr_entry_angry(const struct city
*pcity
,
205 fc_snprintf(buf
, sizeof(buf
), "%2d",
206 pcity
->feel
[CITIZEN_ANGRY
][FEELING_FINAL
]);
210 /************************************************************************
211 Returns list of specialists written to string.
212 Returned string is statically allocated and its contents change when
213 this function is called again.
214 *************************************************************************/
215 static const char *cr_entry_specialists(const struct city
*pcity
,
218 return specialists_string(pcity
->specialists
);
221 /************************************************************************
222 Returns number of specialists of type given as data written to string.
223 Returned string is statically allocated and its contents change when
224 this function is called again.
225 *************************************************************************/
226 static const char *cr_entry_specialist(const struct city
*pcity
,
230 const struct specialist
*sp
= data
;
232 fc_snprintf(buf
, sizeof(buf
), "%2d",
233 pcity
->specialists
[specialist_index(sp
)]);
237 /************************************************************************
238 Returns string with best attack values of units in city.
239 Returned string is statically allocated and its contents change when
240 this function is called again.
241 *************************************************************************/
242 static const char *cr_entry_attack(const struct city
*pcity
,
246 int attack_best
[4] = {-1, -1, -1, -1}, i
;
248 unit_list_iterate(pcity
->tile
->units
, punit
) {
249 /* What about allied units? Should we just count them? */
250 attack_best
[3] = unit_type_get(punit
)->attack_strength
;
252 /* Now that the element is appended to the end of the list, we simply
253 do an insertion sort. */
254 for (i
= 2; i
>= 0 && attack_best
[i
] < attack_best
[i
+ 1]; i
--) {
255 int tmp
= attack_best
[i
];
256 attack_best
[i
] = attack_best
[i
+ 1];
257 attack_best
[i
+ 1] = tmp
;
259 } unit_list_iterate_end
;
262 for (i
= 0; i
< 3; i
++) {
263 if (attack_best
[i
] >= 0) {
264 cat_snprintf(buf
, sizeof(buf
), "%s%d", (i
> 0) ? "/" : "",
267 cat_snprintf(buf
, sizeof(buf
), "%s-", (i
> 0) ? "/" : "");
274 /************************************************************************
275 Returns string with best defend values of units in city.
276 Returned string is statically allocated and its contents change when
277 this function is called again.
278 *************************************************************************/
279 static const char *cr_entry_defense(const struct city
*pcity
,
283 int defense_best
[4] = {-1, -1, -1, -1}, i
;
285 unit_list_iterate(pcity
->tile
->units
, punit
) {
286 /* What about allied units? Should we just count them? */
287 defense_best
[3] = unit_type_get(punit
)->defense_strength
;
289 /* Now that the element is appended to the end of the list, we simply
290 do an insertion sort. */
291 for (i
= 2; i
>= 0 && defense_best
[i
] < defense_best
[i
+ 1]; i
--) {
292 int tmp
= defense_best
[i
];
294 defense_best
[i
] = defense_best
[i
+ 1];
295 defense_best
[i
+ 1] = tmp
;
297 } unit_list_iterate_end
;
300 for (i
= 0; i
< 3; i
++) {
301 if (defense_best
[i
] >= 0) {
302 cat_snprintf(buf
, sizeof(buf
), "%s%d", (i
> 0) ? "/" : "",
305 cat_snprintf(buf
, sizeof(buf
), "%s-", (i
> 0) ? "/" : "");
312 /************************************************************************
313 Returns number of supported units written to string.
314 Returned string is statically allocated and its contents change when
315 this function is called again.
316 *************************************************************************/
317 static const char *cr_entry_supported(const struct city
*pcity
,
321 int num_supported
= unit_list_size(pcity
->units_supported
);
323 fc_snprintf(buf
, sizeof(buf
), "%2d", num_supported
);
328 /************************************************************************
329 Returns number of present units written to string.
330 Returned string is statically allocated and its contents change when
331 this function is called again.
332 *************************************************************************/
333 static const char *cr_entry_present(const struct city
*pcity
,
337 int num_present
= unit_list_size(pcity
->tile
->units
);
339 fc_snprintf(buf
, sizeof(buf
), "%2d", num_present
);
344 /************************************************************************
345 Returns string listing amounts of resources.
346 Returned string is statically allocated and its contents change when
347 this function is called again.
348 *************************************************************************/
349 static const char *cr_entry_resources(const struct city
*pcity
,
353 fc_snprintf(buf
, sizeof(buf
), "%d/%d/%d",
354 pcity
->surplus
[O_FOOD
],
355 pcity
->surplus
[O_SHIELD
],
356 pcity
->surplus
[O_TRADE
]);
360 /************************************************************************
361 Returns food surplus written to string.
362 Returned string is statically allocated and its contents change when
363 this function is called again.
364 *************************************************************************/
365 static const char *cr_entry_foodplus(const struct city
*pcity
,
369 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_FOOD
]);
373 /************************************************************************
374 Returns production surplus written to string.
375 Returned string is statically allocated and its contents change when
376 this function is called again.
377 *************************************************************************/
378 static const char *cr_entry_prodplus(const struct city
*pcity
,
382 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_SHIELD
]);
386 /************************************************************************
387 Returns trade surplus written to string.
388 Returned string is statically allocated and its contents change when
389 this function is called again.
390 *************************************************************************/
391 static const char *cr_entry_tradeplus(const struct city
*pcity
,
395 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_TRADE
]);
399 /************************************************************************
400 Returns string describing resource output.
401 Returned string is statically allocated and its contents change when
402 this function is called again.
403 *************************************************************************/
404 static const char *cr_entry_output(const struct city
*pcity
,
408 int goldie
= pcity
->surplus
[O_GOLD
];
410 fc_snprintf(buf
, sizeof(buf
), "%3d/%d/%d",
411 goldie
, pcity
->prod
[O_LUXURY
], pcity
->prod
[O_SCIENCE
]);
415 /************************************************************************
416 Returns gold surplus written to string.
417 Returned string is statically allocated and its contents change when
418 this function is called again.
419 *************************************************************************/
420 static const char *cr_entry_gold(const struct city
*pcity
,
425 if (pcity
->surplus
[O_GOLD
] > 0) {
426 fc_snprintf(buf
, sizeof(buf
), "+%d", pcity
->surplus
[O_GOLD
]);
428 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_GOLD
]);
433 /************************************************************************
434 Returns luxury output written to string.
435 Returned string is statically allocated and its contents change when
436 this function is called again.
437 *************************************************************************/
438 static const char *cr_entry_luxury(const struct city
*pcity
,
442 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->prod
[O_LUXURY
]);
446 /************************************************************************
447 Returns science output written to string.
448 Returned string is statically allocated and its contents change when
449 this function is called again.
450 *************************************************************************/
451 static const char *cr_entry_science(const struct city
*pcity
,
455 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->prod
[O_SCIENCE
]);
459 /************************************************************************
460 Returns number of turns before city grows 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_growturns(const struct city
*pcity
,
467 int turns
= city_turns_to_grow(pcity
);
471 if (turns
== FC_INFINITY
) {
472 /* 'never' wouldn't be easily translatable here. */
473 fc_snprintf(buffer
, sizeof(buffer
), "---");
475 /* Shrinking cities get a negative value. */
476 fc_snprintf(buffer
, sizeof(buffer
), "%4d", turns
);
478 fc_snprintf(buf
, sizeof(buf
), "%s (%d/%d)",
479 buffer
, pcity
->food_stock
,
480 city_granary_size(city_size_get(pcity
)));
484 /************************************************************************
485 Returns pollution output written to string.
486 Returned string is statically allocated and its contents change when
487 this function is called again.
488 *************************************************************************/
489 static const char *cr_entry_pollution(const struct city
*pcity
,
493 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->pollution
);
497 /************************************************************************
498 Returns number and output of trade routes written to string.
499 Returned string is statically allocated and its contents change when
500 this function is called again.
501 *************************************************************************/
502 static const char *cr_entry_trade_routes(const struct city
*pcity
,
506 int num
= 0, value
= 0;
508 trade_routes_iterate(pcity
, proute
) {
510 value
+= proute
->value
;
511 } trade_routes_iterate_end
;
514 sz_strlcpy(buf
, "0");
516 fc_snprintf(buf
, sizeof(buf
), "%d (+%d)", num
, value
);
521 /************************************************************************
522 Returns number of build slots written to string.
523 Returned string is statically allocated and its contents change when
524 this function is called again.
525 *************************************************************************/
526 static const char *cr_entry_build_slots(const struct city
*pcity
,
530 fc_snprintf(buf
, sizeof(buf
), "%3d", city_build_slots(pcity
));
534 /************************************************************************
535 Returns name of current production.
536 Returned string is statically allocated and its contents change when
537 this function is called again.
538 *************************************************************************/
539 static const char *cr_entry_building(const struct city
*pcity
,
542 static char buf
[192];
543 const char *from_worklist
=
544 worklist_is_empty(&pcity
->worklist
) ? "" :
545 gui_options
.concise_city_production
? "+" : _("(worklist)");
547 if (city_production_has_flag(pcity
, IF_GOLD
)) {
548 fc_snprintf(buf
, sizeof(buf
), "%s (%d)%s",
549 city_production_name_translation(pcity
),
550 MAX(0, pcity
->surplus
[O_SHIELD
]), from_worklist
);
552 fc_snprintf(buf
, sizeof(buf
), "%s (%d/%s)%s",
553 city_production_name_translation(pcity
),
555 city_production_cost_str(pcity
),
562 /************************************************************************
563 Returns cost of buying current production and turns to completion
564 written to string. Returned string is statically allocated and its
565 contents change when this function is called again.
566 *************************************************************************/
567 static const char *cr_entry_build_cost(const struct city
*pcity
,
576 if (city_production_has_flag(pcity
, IF_GOLD
)) {
577 fc_snprintf(buf
, sizeof(buf
), "*");
580 price
= city_production_buy_gold_cost(pcity
);
581 turns
= city_production_turns_to_build(pcity
, TRUE
);
584 fc_snprintf(bufone
, sizeof(bufone
), "---");
586 fc_snprintf(bufone
, sizeof(bufone
), "%d", price
);
589 fc_snprintf(buftwo
, sizeof(buftwo
), "--");
591 fc_snprintf(buftwo
, sizeof(buftwo
), "%3d", turns
);
593 fc_snprintf(buf
, sizeof(buf
), "%s/%s", buftwo
, bufone
);
597 /************************************************************************
598 Returns corruption amount written to string.
599 Returned string is statically allocated and its contents change when
600 this function is called again.
601 *************************************************************************/
602 static const char *cr_entry_corruption(const struct city
*pcity
,
606 fc_snprintf(buf
, sizeof(buf
), "%3d", -(pcity
->waste
[O_TRADE
]));
610 /************************************************************************
611 Returns waste amount written to string.
612 Returned string is statically allocated and its contents change when
613 this function is called again.
614 *************************************************************************/
615 static const char *cr_entry_waste(const struct city
*pcity
,
619 fc_snprintf(buf
, sizeof(buf
), "%3d", -(pcity
->waste
[O_SHIELD
]));
623 /************************************************************************
624 Returns risk percentage of plague written to string.
625 Returned string is statically allocated and its contents change when
626 this function is called again.
627 *************************************************************************/
628 static const char *cr_entry_plague_risk(const struct city
*pcity
,
632 if (!game
.info
.illness_on
) {
633 fc_snprintf(buf
, sizeof(buf
), " -.-");
635 fc_snprintf(buf
, sizeof(buf
), "%4.1f",
636 (float)city_illness_calc(pcity
, NULL
, NULL
, NULL
, NULL
)/10.0);
641 /************************************************************************
642 Returns number of continent
643 *************************************************************************/
644 static const char *cr_entry_continent(const struct city
*pcity
,
648 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->tile
->continent
);
652 /************************************************************************
653 Returns city cma description.
654 Returned string is statically allocated and its contents change when
655 this function is called again.
656 *************************************************************************/
657 static const char *cr_entry_cma(const struct city
*pcity
,
660 return cmafec_get_short_descr_of_city(pcity
);
663 /* City report options (which columns get shown)
664 * To add a new entry, you should just have to:
665 * - add a function like those above
666 * - add an entry in the base_city_report_specs[] table
669 /* This generates the function name and the tagname: */
670 #define FUNC_TAG(var) cr_entry_##var, #var
672 static const struct city_report_spec base_city_report_specs
[] = {
673 { TRUE
, -15, 0, NULL
, N_("?city:Name"), N_("City Name"),
674 NULL
, FUNC_TAG(cityname
) },
675 { FALSE
, -15, 0, NULL
, N_("Nation"), N_("Nation"),
676 NULL
, FUNC_TAG(nation
) },
677 { TRUE
, 2, 1, NULL
, N_("?size [short]:Sz"), N_("Size"),
678 NULL
, FUNC_TAG(size
) },
679 { TRUE
, -8, 1, NULL
, N_("State"), N_("Celebrating/Peace/Disorder"),
680 NULL
, FUNC_TAG(hstate_verbose
) },
681 { FALSE
, 1, 1, NULL
, NULL
, N_("Concise *=Celebrating, X=Disorder"),
682 NULL
, FUNC_TAG(hstate_concise
) },
684 { FALSE
, 2, 1, NULL
, N_("?Happy workers:H"), N_("Workers: Happy"),
685 NULL
, FUNC_TAG(happy
) },
686 { FALSE
, 2, 1, NULL
, N_("?Content workers:C"), N_("Workers: Content"),
687 NULL
, FUNC_TAG(content
) },
688 { FALSE
, 2, 1, NULL
, N_("?Unhappy workers:U"), N_("Workers: Unhappy"),
689 NULL
, FUNC_TAG(unhappy
) },
690 { FALSE
, 2, 1, NULL
, N_("?Angry workers:A"), N_("Workers: Angry"),
691 NULL
, FUNC_TAG(angry
) },
692 { TRUE
, 10, 1, N_("?city:Workers"),
693 N_("?happy/content/unhappy/angry:H/C/U/A"),
694 N_("Workers: Happy, Content, Unhappy, Angry"),
695 NULL
, FUNC_TAG(workers
) },
697 { FALSE
, 8, 1, N_("Best"), N_("attack"),
698 N_("Best attacking units"), NULL
, FUNC_TAG(attack
)},
699 { FALSE
, 8, 1, N_("Best"), N_("defense"),
700 N_("Best defending units"), NULL
, FUNC_TAG(defense
)},
701 { FALSE
, 2, 1, N_("Units"),
702 /* TRANS: Header "Number of units inside city" */
703 N_("?Present (units):Here"),
704 N_("Number of units present"), NULL
, FUNC_TAG(present
) },
705 { FALSE
, 2, 1, N_("Units"),
706 /* TRANS: Header "Number of units supported by given city" */
707 N_("?Supported (units):Owned"),
708 N_("Number of units supported"), NULL
, FUNC_TAG(supported
) },
710 { /* TRANS: Header "It will take this many turns before city grows" */
711 TRUE
, 14, 1, N_("?food (population):Grow"),
712 N_("?Stock/Target:(Have/Need)"),
713 N_("Turns until growth/famine"),
714 NULL
, FUNC_TAG(growturns
) },
716 { TRUE
, 10, 1, N_("Surplus"), N_("?food/production/trade:F/P/T"),
717 N_("Surplus: Food, Production, Trade"),
718 NULL
, FUNC_TAG(resources
) },
719 { FALSE
, 3, 1, NULL
, N_("?Food surplus [short]:+F"), N_("Surplus: Food"),
720 NULL
, FUNC_TAG(foodplus
) },
721 { FALSE
, 3, 1, NULL
, N_("?Production surplus [short]:+P"),
722 N_("Surplus: Production"), NULL
, FUNC_TAG(prodplus
) },
723 { FALSE
, 3, 1, NULL
, N_("?Production loss (waste) [short]:-P"),
724 N_("Waste"), NULL
, FUNC_TAG(waste
) },
725 { FALSE
, 3, 1, NULL
, N_("?Trade surplus [short]:+T"), N_("Surplus: Trade"),
726 NULL
, FUNC_TAG(tradeplus
) },
727 { FALSE
, 3, 1, NULL
, N_("?Trade loss (corruption) [short]:-T"),
728 N_("Corruption"), NULL
, FUNC_TAG(corruption
) },
730 { TRUE
, 10, 1, N_("Economy"), N_("?gold/luxury/science:G/L/S"),
731 N_("Economy: Gold, Luxuries, Science"),
732 NULL
, FUNC_TAG(output
) },
733 { FALSE
, 3, 1, NULL
, N_("?Gold:G"), N_("Economy: Gold"),
734 NULL
, FUNC_TAG(gold
) },
735 { FALSE
, 3, 1, NULL
, N_("?Luxury:L"), N_("Economy: Luxury"),
736 NULL
, FUNC_TAG(luxury
) },
737 { FALSE
, 3, 1, NULL
, N_("?Science:S"), N_("Economy: Science"),
738 NULL
, FUNC_TAG(science
) },
739 { FALSE
, 3, 1, NULL
, N_("?Culture:Clt"), N_("Culture"),
740 NULL
, FUNC_TAG(culture
) },
741 { FALSE
, 3, 1, NULL
, N_("?History:Hst"), N_("History"),
742 NULL
, FUNC_TAG(history
) },
743 { FALSE
, 3, 1, NULL
, N_("?Continent:C"), N_("Continent number"),
744 NULL
, FUNC_TAG(continent
) },
745 { FALSE
, 1, 1, N_("?number_trade_routes:n"), N_("?number_trade_routes:R"),
746 N_("Number (and total value) of trade routes"),
747 NULL
, FUNC_TAG(trade_routes
) },
748 { FALSE
, 3, 1, NULL
, N_("?pollution [short]:Pol"), N_("Pollution"),
749 NULL
, FUNC_TAG(pollution
) },
750 { FALSE
, 4, 1, N_("?plague risk [short]:Pla"), N_("(%)"), N_("Plague risk"),
751 NULL
, FUNC_TAG(plague_risk
) },
752 { FALSE
, 15, 1, NULL
, N_("?cma:Governor"), N_("Citizen Governor"),
753 NULL
, FUNC_TAG(cma
) },
755 /* TRANS: "BS" = "build slots" */
756 { FALSE
, 3, 1, NULL
, N_("BS"), N_("Maximum units buildable per turn"),
757 NULL
, FUNC_TAG(build_slots
) },
758 { TRUE
, 9, 1, N_("Production"), N_("Turns/Buy"),
759 /*N_("Turns or gold to complete production"), future menu needs translation */
761 NULL
, FUNC_TAG(build_cost
) },
762 { TRUE
, 0, 1, N_("Currently Building"),
763 N_("?Stock/Target:(Have/Need)"),
764 N_("Currently Building"),
765 NULL
, FUNC_TAG(building
) }
768 struct city_report_spec
*city_report_specs
;
769 static int num_creport_cols
;
771 /******************************************************************
772 Some simple wrappers:
773 ******************************************************************/
774 int num_city_report_spec(void)
776 return num_creport_cols
;
778 bool *city_report_spec_show_ptr(int i
)
780 return &(city_report_specs
[i
].show
);
782 const char *city_report_spec_tagname(int i
)
784 return city_report_specs
[i
].tagname
;
787 /******************************************************************
788 Initialize city report data. This deals with ruleset-depedent
789 columns and pre-translates the fields (to make things easier on
790 the GUI writers). Should be called before the GUI starts up.
791 ******************************************************************/
792 void init_city_report_game_data(void)
794 static char sp_explanation
[SP_MAX
][128];
795 static char sp_explanations
[SP_MAX
*128];
796 struct city_report_spec
*p
;
799 num_creport_cols
= ARRAY_SIZE(base_city_report_specs
)
800 + specialist_count() + 1;
802 = fc_realloc(city_report_specs
,
803 num_creport_cols
* sizeof(*city_report_specs
));
804 p
= &city_report_specs
[0];
806 fc_snprintf(sp_explanations
, sizeof(sp_explanations
),
807 "%s", _("Specialists: "));
808 specialist_type_iterate(sp
) {
809 struct specialist
*s
= specialist_by_number(sp
);
814 p
->title1
= Q_("?specialist:S");
815 p
->title2
= specialist_abbreviation_translation(s
);
816 fc_snprintf(sp_explanation
[sp
], sizeof(sp_explanation
[sp
]),
817 _("Specialists: %s"), specialist_plural_translation(s
));
818 cat_snprintf(sp_explanations
, sizeof(sp_explanations
),
819 "%s%s", (sp
== 0) ? "" : ", ",
820 specialist_plural_translation(s
));
821 p
->explanation
= sp_explanation
[sp
];
823 p
->func
= cr_entry_specialist
;
824 p
->tagname
= specialist_rule_name(s
);
826 } specialist_type_iterate_end
;
828 /* Summary column for all specialists. */
830 static char sp_summary
[128];
833 p
->width
= MAX(7, specialist_count()*2-1);
835 p
->title1
= _("Special");
836 fc_snprintf(sp_summary
, sizeof(sp_summary
),
837 "%s", specialists_abbreviation_string());
838 p
->title2
= sp_summary
;
839 p
->explanation
= sp_explanations
;
841 p
->func
= cr_entry_specialists
;
842 p
->tagname
= "specialists";
846 memcpy(p
, base_city_report_specs
,
847 sizeof(base_city_report_specs
));
849 for (i
= 0; i
< ARRAY_SIZE(base_city_report_specs
); i
++) {
851 p
->title1
= Q_(p
->title1
);
854 p
->title2
= Q_(p
->title2
);
856 p
->explanation
= _(p
->explanation
);
860 fc_assert(NUM_CREPORT_COLS
== ARRAY_SIZE(base_city_report_specs
)
861 + specialist_count() + 1);
864 /**********************************************************************
865 The following several functions allow intelligent sorting city report
866 fields by column. This doesn't necessarily do the right thing, but
867 it's better than sorting alphabetically.
869 The GUI gives us two values to compare (as strings). We try to split
870 them into an array of numeric and string fields, then we compare
871 lexicographically. Two numeric fields are compared in the obvious
872 way, two character fields are compared alphabetically. Arbitrarily, a
873 numeric field is sorted before a character field (for "justification"
874 note that numbers are before letters in the ASCII table).
875 **********************************************************************/
877 /* A datum is one short string, or one number.
878 A datum_vector represents a long string of alternating strings and
887 #define SPECVEC_TAG datum
890 /**********************************************************************
891 Init a datum from a substring.
892 **********************************************************************/
893 static void init_datum_string(struct datum
*dat
, const char *left
,
896 int len
= right
- left
;
898 dat
->is_numeric
= FALSE
;
899 dat
->val
.string_value
= fc_malloc(len
+ 1);
900 memcpy(dat
->val
.string_value
, left
, len
);
901 dat
->val
.string_value
[len
] = 0;
904 /**********************************************************************
905 Init a datum from a number (a float because we happen to use
907 **********************************************************************/
908 static void init_datum_number(struct datum
*dat
, float val
)
910 dat
->is_numeric
= TRUE
;
911 dat
->val
.numeric_value
= val
;
914 /**********************************************************************
915 Free the data associated with a datum -- that is, free the string if
917 **********************************************************************/
918 static void free_datum(struct datum
*dat
)
920 if (!dat
->is_numeric
) {
921 free(dat
->val
.string_value
);
925 /**********************************************************************
926 Compare two data items as described above:
927 - numbers in the obvious way
928 - strings alphabetically
929 - number < string for no good reason
930 **********************************************************************/
931 static int datum_compare(const struct datum
*a
, const struct datum
*b
)
933 if (a
->is_numeric
== b
->is_numeric
) {
935 if (a
->val
.numeric_value
== b
->val
.numeric_value
) {
937 } else if (a
->val
.numeric_value
< b
->val
.numeric_value
) {
939 } else if (a
->val
.numeric_value
> b
->val
.numeric_value
) {
942 return 0; /* shrug */
945 return strcmp(a
->val
.string_value
, b
->val
.string_value
);
956 /**********************************************************************
957 Compare two strings of data lexicographically.
958 **********************************************************************/
959 static int data_compare(const struct datum_vector
*a
,
960 const struct datum_vector
*b
)
964 n
= MIN(a
->size
, b
->size
);
966 for (i
= 0; i
< n
; i
++) {
967 int cmp
= datum_compare(&a
->p
[i
], &b
->p
[i
]);
974 /* The first n fields match; whoever has more fields goes last.
975 If they have equal numbers, the two really are equal. */
976 return a
->size
- b
->size
;
979 /**********************************************************************
980 Split a string into a vector of datum.
981 **********************************************************************/
982 static void split_string(struct datum_vector
*data
, const char *str
)
984 const char *string_start
;
986 datum_vector_init(data
);
993 value
= strtof(str
, &endptr
);
994 if (errno
!= 0 || endptr
== str
|| !isfinite(value
)) {
995 /* that wasn't a sensible number; go on */
998 /* that was a number, so stop the string we were parsing, add
999 it (unless it's empty), then add the number we just parsed */
1002 if (str
!= string_start
) {
1003 init_datum_string(&d
, string_start
, str
);
1004 datum_vector_append(data
, d
);
1007 init_datum_number(&d
, value
);
1008 datum_vector_append(data
, d
);
1010 /* finally, update the string position pointers */
1011 string_start
= str
= endptr
;
1015 /* if we have anything leftover then it's a string */
1016 if (str
!= string_start
) {
1019 init_datum_string(&d
, string_start
, str
);
1020 datum_vector_append(data
, d
);
1024 /**********************************************************************
1025 Free every datum in the vector.
1026 **********************************************************************/
1027 static void free_data(struct datum_vector
*data
)
1031 for (i
= 0; i
< data
->size
; i
++) {
1032 free_datum(&data
->p
[i
]);
1034 datum_vector_free(data
);
1037 /**********************************************************************
1038 The real function: split the two strings, and compare them.
1039 **********************************************************************/
1040 int cityrepfield_compare(const char *str1
, const char *str2
)
1042 struct datum_vector data1
, data2
;
1047 } else if (NULL
== str1
) {
1049 } else if (NULL
== str2
) {
1053 split_string(&data1
, str1
);
1054 split_string(&data2
, str2
);
1056 retval
= data_compare(&data1
, &data2
);
1064 /****************************************************************
1065 Same as can_city_sell_building(), but with universal argument.
1066 *****************************************************************/
1067 bool can_city_sell_universal(const struct city
*pcity
,
1068 const struct universal
*target
)
1070 return target
->kind
== VUT_IMPROVEMENT
1071 && can_city_sell_building(pcity
, target
->value
.building
);