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, i
;
508 for (i
= 0; i
< MAX_TRADE_ROUTES
; i
++) {
509 if (0 != pcity
->trade
[i
]) {
511 value
+= pcity
->trade_value
[i
];
516 sz_strlcpy(buf
, "0");
518 fc_snprintf(buf
, sizeof(buf
), "%d (+%d)", num
, value
);
523 /************************************************************************
524 Returns number of build slots written to string.
525 Returned string is statically allocated and its contents change when
526 this function is called again.
527 *************************************************************************/
528 static const char *cr_entry_build_slots(const struct city
*pcity
,
532 fc_snprintf(buf
, sizeof(buf
), "%3d", city_build_slots(pcity
));
536 /************************************************************************
537 Returns name of current production.
538 Returned string is statically allocated and its contents change when
539 this function is called again.
540 *************************************************************************/
541 static const char *cr_entry_building(const struct city
*pcity
,
544 static char buf
[192];
545 const char *from_worklist
=
546 worklist_is_empty(&pcity
->worklist
) ? "" :
547 gui_options
.concise_city_production
? "+" : _("(worklist)");
549 if (city_production_has_flag(pcity
, IF_GOLD
)) {
550 fc_snprintf(buf
, sizeof(buf
), "%s (%d)%s",
551 city_production_name_translation(pcity
),
552 MAX(0, pcity
->surplus
[O_SHIELD
]), from_worklist
);
554 fc_snprintf(buf
, sizeof(buf
), "%s (%d/%s)%s",
555 city_production_name_translation(pcity
),
557 city_production_cost_str(pcity
),
564 /************************************************************************
565 Returns cost of buying current production and turns to completion
566 written to string. Returned string is statically allocated and its
567 contents change when this function is called again.
568 *************************************************************************/
569 static const char *cr_entry_build_cost(const struct city
*pcity
,
578 if (city_production_has_flag(pcity
, IF_GOLD
)) {
579 fc_snprintf(buf
, sizeof(buf
), "*");
582 price
= city_production_buy_gold_cost(pcity
);
583 turns
= city_production_turns_to_build(pcity
, TRUE
);
586 fc_snprintf(bufone
, sizeof(bufone
), "---");
588 fc_snprintf(bufone
, sizeof(bufone
), "%d", price
);
591 fc_snprintf(buftwo
, sizeof(buftwo
), "--");
593 fc_snprintf(buftwo
, sizeof(buftwo
), "%3d", turns
);
595 fc_snprintf(buf
, sizeof(buf
), "%s/%s", buftwo
, bufone
);
599 /************************************************************************
600 Returns corruption amount written to string.
601 Returned string is statically allocated and its contents change when
602 this function is called again.
603 *************************************************************************/
604 static const char *cr_entry_corruption(const struct city
*pcity
,
608 fc_snprintf(buf
, sizeof(buf
), "%3d", -(pcity
->waste
[O_TRADE
]));
612 /************************************************************************
613 Returns waste amount written to string.
614 Returned string is statically allocated and its contents change when
615 this function is called again.
616 *************************************************************************/
617 static const char *cr_entry_waste(const struct city
*pcity
,
621 fc_snprintf(buf
, sizeof(buf
), "%3d", -(pcity
->waste
[O_SHIELD
]));
625 /************************************************************************
626 Returns risk percentage of plague written to string.
627 Returned string is statically allocated and its contents change when
628 this function is called again.
629 *************************************************************************/
630 static const char *cr_entry_plague_risk(const struct city
*pcity
,
634 if (!game
.info
.illness_on
) {
635 fc_snprintf(buf
, sizeof(buf
), " -.-");
637 fc_snprintf(buf
, sizeof(buf
), "%4.1f",
638 (float)city_illness_calc(pcity
, NULL
, NULL
, NULL
, NULL
)/10.0);
643 /************************************************************************
644 Returns number of continent
645 *************************************************************************/
646 static const char *cr_entry_continent(const struct city
*pcity
,
650 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->tile
->continent
);
654 /************************************************************************
655 Returns city cma description.
656 Returned string is statically allocated and its contents change when
657 this function is called again.
658 *************************************************************************/
659 static const char *cr_entry_cma(const struct city
*pcity
,
662 return cmafec_get_short_descr_of_city(pcity
);
665 /* City report options (which columns get shown)
666 * To add a new entry, you should just have to:
667 * - add a function like those above
668 * - add an entry in the base_city_report_specs[] table
671 /* This generates the function name and the tagname: */
672 #define FUNC_TAG(var) cr_entry_##var, #var
674 static const struct city_report_spec base_city_report_specs
[] = {
675 { TRUE
, -15, 0, NULL
, N_("?city:Name"), N_("City Name"),
676 NULL
, FUNC_TAG(cityname
) },
677 { FALSE
, -15, 0, NULL
, N_("Nation"), N_("Nation"),
678 NULL
, FUNC_TAG(nation
) },
679 { TRUE
, 2, 1, NULL
, N_("?size [short]:Sz"), N_("Size"),
680 NULL
, FUNC_TAG(size
) },
681 { TRUE
, -8, 1, NULL
, N_("State"), N_("Celebrating/Peace/Disorder"),
682 NULL
, FUNC_TAG(hstate_verbose
) },
683 { FALSE
, 1, 1, NULL
, NULL
, N_("Concise *=Celebrating, X=Disorder"),
684 NULL
, FUNC_TAG(hstate_concise
) },
686 { FALSE
, 2, 1, NULL
, N_("?Happy workers:H"), N_("Workers: Happy"),
687 NULL
, FUNC_TAG(happy
) },
688 { FALSE
, 2, 1, NULL
, N_("?Content workers:C"), N_("Workers: Content"),
689 NULL
, FUNC_TAG(content
) },
690 { FALSE
, 2, 1, NULL
, N_("?Unhappy workers:U"), N_("Workers: Unhappy"),
691 NULL
, FUNC_TAG(unhappy
) },
692 { FALSE
, 2, 1, NULL
, N_("?Angry workers:A"), N_("Workers: Angry"),
693 NULL
, FUNC_TAG(angry
) },
694 { TRUE
, 10, 1, N_("?city:Workers"),
695 N_("?happy/content/unhappy/angry:H/C/U/A"),
696 N_("Workers: Happy, Content, Unhappy, Angry"),
697 NULL
, FUNC_TAG(workers
) },
699 { FALSE
, 8, 1, N_("Best"), N_("attack"),
700 N_("Best attacking units"), NULL
, FUNC_TAG(attack
)},
701 { FALSE
, 8, 1, N_("Best"), N_("defense"),
702 N_("Best defending units"), NULL
, FUNC_TAG(defense
)},
703 { FALSE
, 2, 1, N_("Units"),
704 /* TRANS: Header "Number of units inside city" */
705 N_("?Present (units):Here"),
706 N_("Number of units present"), NULL
, FUNC_TAG(present
) },
707 { FALSE
, 2, 1, N_("Units"),
708 /* TRANS: Header "Number of units supported by given city" */
709 N_("?Supported (units):Owned"),
710 N_("Number of units supported"), NULL
, FUNC_TAG(supported
) },
712 { /* TRANS: Header "It will take this many turns before city grows" */
713 TRUE
, 14, 1, N_("?food (population):Grow"),
714 N_("?Stock/Target:(Have/Need)"),
715 N_("Turns until growth/famine"),
716 NULL
, FUNC_TAG(growturns
) },
718 { TRUE
, 10, 1, N_("Surplus"), N_("?food/production/trade:F/P/T"),
719 N_("Surplus: Food, Production, Trade"),
720 NULL
, FUNC_TAG(resources
) },
721 { FALSE
, 3, 1, NULL
, N_("?Food surplus [short]:+F"), N_("Surplus: Food"),
722 NULL
, FUNC_TAG(foodplus
) },
723 { FALSE
, 3, 1, NULL
, N_("?Production surplus [short]:+P"),
724 N_("Surplus: Production"), NULL
, FUNC_TAG(prodplus
) },
725 { FALSE
, 3, 1, NULL
, N_("?Production loss (waste) [short]:-P"),
726 N_("Waste"), NULL
, FUNC_TAG(waste
) },
727 { FALSE
, 3, 1, NULL
, N_("?Trade surplus [short]:+T"), N_("Surplus: Trade"),
728 NULL
, FUNC_TAG(tradeplus
) },
729 { FALSE
, 3, 1, NULL
, N_("?Trade loss (corruption) [short]:-T"),
730 N_("Corruption"), NULL
, FUNC_TAG(corruption
) },
732 { TRUE
, 10, 1, N_("Economy"), N_("?gold/luxury/science:G/L/S"),
733 N_("Economy: Gold, Luxuries, Science"),
734 NULL
, FUNC_TAG(output
) },
735 { FALSE
, 3, 1, NULL
, N_("?Gold:G"), N_("Economy: Gold"),
736 NULL
, FUNC_TAG(gold
) },
737 { FALSE
, 3, 1, NULL
, N_("?Luxury:L"), N_("Economy: Luxury"),
738 NULL
, FUNC_TAG(luxury
) },
739 { FALSE
, 3, 1, NULL
, N_("?Science:S"), N_("Economy: Science"),
740 NULL
, FUNC_TAG(science
) },
741 { FALSE
, 3, 1, NULL
, N_("?Culture:Clt"), N_("Culture"),
742 NULL
, FUNC_TAG(culture
) },
743 { FALSE
, 3, 1, NULL
, N_("?History:Hst"), N_("History"),
744 NULL
, FUNC_TAG(history
) },
745 { FALSE
, 3, 1, NULL
, N_("?Continent:C"), N_("Continent number"),
746 NULL
, FUNC_TAG(continent
) },
747 { FALSE
, 1, 1, N_("?number_trade_routes:n"), N_("?number_trade_routes:R"),
748 N_("Number (and total value) of trade routes"),
749 NULL
, FUNC_TAG(trade_routes
) },
750 { FALSE
, 3, 1, NULL
, N_("?pollution [short]:Pol"), N_("Pollution"),
751 NULL
, FUNC_TAG(pollution
) },
752 { FALSE
, 4, 1, N_("?plague risk [short]:Pla"), N_("(%)"), N_("Plague risk"),
753 NULL
, FUNC_TAG(plague_risk
) },
754 { FALSE
, 15, 1, NULL
, N_("?cma:Governor"), N_("Citizen Governor"),
755 NULL
, FUNC_TAG(cma
) },
757 /* TRANS: "BS" = "build slots" */
758 { FALSE
, 3, 1, NULL
, N_("BS"), N_("Maximum units buildable per turn"),
759 NULL
, FUNC_TAG(build_slots
) },
760 { TRUE
, 9, 1, N_("Production"), N_("Turns/Buy"),
761 /*N_("Turns or gold to complete production"), future menu needs translation */
763 NULL
, FUNC_TAG(build_cost
) },
764 { TRUE
, 0, 1, N_("Currently Building"),
765 N_("?Stock/Target:(Have/Need)"),
766 N_("Currently Building"),
767 NULL
, FUNC_TAG(building
) }
770 struct city_report_spec
*city_report_specs
;
771 static int num_creport_cols
;
773 /******************************************************************
774 Some simple wrappers:
775 ******************************************************************/
776 int num_city_report_spec(void)
778 return num_creport_cols
;
780 bool *city_report_spec_show_ptr(int i
)
782 return &(city_report_specs
[i
].show
);
784 const char *city_report_spec_tagname(int i
)
786 return city_report_specs
[i
].tagname
;
789 /******************************************************************
790 Initialize city report data. This deals with ruleset-depedent
791 columns and pre-translates the fields (to make things easier on
792 the GUI writers). Should be called before the GUI starts up.
793 ******************************************************************/
794 void init_city_report_game_data(void)
796 static char sp_explanation
[SP_MAX
][128];
797 static char sp_explanations
[SP_MAX
*128];
798 struct city_report_spec
*p
;
801 num_creport_cols
= ARRAY_SIZE(base_city_report_specs
)
802 + specialist_count() + 1;
804 = fc_realloc(city_report_specs
,
805 num_creport_cols
* sizeof(*city_report_specs
));
806 p
= &city_report_specs
[0];
808 fc_snprintf(sp_explanations
, sizeof(sp_explanations
),
809 "%s", _("Specialists: "));
810 specialist_type_iterate(sp
) {
811 struct specialist
*s
= specialist_by_number(sp
);
816 p
->title1
= Q_("?specialist:S");
817 p
->title2
= specialist_abbreviation_translation(s
);
818 fc_snprintf(sp_explanation
[sp
], sizeof(sp_explanation
[sp
]),
819 _("Specialists: %s"), specialist_plural_translation(s
));
820 cat_snprintf(sp_explanations
, sizeof(sp_explanations
),
821 "%s%s", (sp
== 0) ? "" : ", ",
822 specialist_plural_translation(s
));
823 p
->explanation
= sp_explanation
[sp
];
825 p
->func
= cr_entry_specialist
;
826 p
->tagname
= specialist_rule_name(s
);
828 } specialist_type_iterate_end
;
830 /* Summary column for all specialists. */
832 static char sp_summary
[128];
835 p
->width
= MAX(7, specialist_count()*2-1);
837 p
->title1
= _("Special");
838 fc_snprintf(sp_summary
, sizeof(sp_summary
),
839 "%s", specialists_abbreviation_string());
840 p
->title2
= sp_summary
;
841 p
->explanation
= sp_explanations
;
843 p
->func
= cr_entry_specialists
;
844 p
->tagname
= "specialists";
848 memcpy(p
, base_city_report_specs
,
849 sizeof(base_city_report_specs
));
851 for (i
= 0; i
< ARRAY_SIZE(base_city_report_specs
); i
++) {
853 p
->title1
= Q_(p
->title1
);
856 p
->title2
= Q_(p
->title2
);
858 p
->explanation
= _(p
->explanation
);
862 fc_assert(NUM_CREPORT_COLS
== ARRAY_SIZE(base_city_report_specs
)
863 + specialist_count() + 1);
866 /**********************************************************************
867 The following several functions allow intelligent sorting city report
868 fields by column. This doesn't necessarily do the right thing, but
869 it's better than sorting alphabetically.
871 The GUI gives us two values to compare (as strings). We try to split
872 them into an array of numeric and string fields, then we compare
873 lexicographically. Two numeric fields are compared in the obvious
874 way, two character fields are compared alphabetically. Arbitrarily, a
875 numeric field is sorted before a character field (for "justification"
876 note that numbers are before letters in the ASCII table).
877 **********************************************************************/
879 /* A datum is one short string, or one number.
880 A datum_vector represents a long string of alternating strings and
889 #define SPECVEC_TAG datum
892 /**********************************************************************
893 Init a datum from a substring.
894 **********************************************************************/
895 static void init_datum_string(struct datum
*dat
, const char *left
,
898 int len
= right
- left
;
900 dat
->is_numeric
= FALSE
;
901 dat
->val
.string_value
= fc_malloc(len
+ 1);
902 memcpy(dat
->val
.string_value
, left
, len
);
903 dat
->val
.string_value
[len
] = 0;
906 /**********************************************************************
907 Init a datum from a number (a float because we happen to use
909 **********************************************************************/
910 static void init_datum_number(struct datum
*dat
, float val
)
912 dat
->is_numeric
= TRUE
;
913 dat
->val
.numeric_value
= val
;
916 /**********************************************************************
917 Free the data associated with a datum -- that is, free the string if
919 **********************************************************************/
920 static void free_datum(struct datum
*dat
)
922 if(!dat
->is_numeric
) {
923 free(dat
->val
.string_value
);
927 /**********************************************************************
928 Compare two data items as described above:
929 - numbers in the obvious way
930 - strings alphabetically
931 - number < string for no good reason
932 **********************************************************************/
933 static int datum_compare(const struct datum
*a
, const struct datum
*b
)
935 if(a
->is_numeric
== b
->is_numeric
) {
937 if (a
->val
.numeric_value
== b
->val
.numeric_value
) {
939 } else if (a
->val
.numeric_value
< b
->val
.numeric_value
) {
941 } else if (a
->val
.numeric_value
> b
->val
.numeric_value
) {
944 return 0; /* shrug */
947 return strcmp(a
->val
.string_value
, b
->val
.string_value
);
958 /**********************************************************************
959 Compare two strings of data lexicographically.
960 **********************************************************************/
961 static int data_compare(const struct datum_vector
*a
,
962 const struct datum_vector
*b
)
966 n
= MIN(a
->size
, b
->size
);
968 for(i
= 0; i
< n
; i
++) {
969 int cmp
= datum_compare(&a
->p
[i
], &b
->p
[i
]);
976 /* The first n fields match; whoever has more fields goes last.
977 If they have equal numbers, the two really are equal. */
978 return a
->size
- b
->size
;
982 /**********************************************************************
983 Split a string into a vector of datum.
984 **********************************************************************/
985 static void split_string(struct datum_vector
*data
, const char *str
)
987 const char *string_start
;
989 datum_vector_init(data
);
996 value
= strtof(str
, &endptr
);
997 if (errno
!= 0 || endptr
== str
|| !isfinite(value
)) {
998 /* that wasn't a sensible number; go on */
1001 /* that was a number, so stop the string we were parsing, add
1002 it (unless it's empty), then add the number we just parsed */
1005 if(str
!= string_start
) {
1006 init_datum_string(&d
, string_start
, str
);
1007 datum_vector_append(data
, d
);
1010 init_datum_number(&d
, value
);
1011 datum_vector_append(data
, d
);
1013 /* finally, update the string position pointers */
1014 string_start
= str
= endptr
;
1018 /* if we have anything leftover then it's a string */
1019 if(str
!= string_start
) {
1022 init_datum_string(&d
, string_start
, str
);
1023 datum_vector_append(data
, d
);
1027 /**********************************************************************
1028 Free every datum in the vector.
1029 **********************************************************************/
1030 static void free_data(struct datum_vector
*data
)
1034 for(i
= 0; i
< data
->size
; i
++) {
1035 free_datum(&data
->p
[i
]);
1037 datum_vector_free(data
);
1041 /**********************************************************************
1042 The real function: split the two strings, and compare them.
1043 **********************************************************************/
1044 int cityrepfield_compare(const char *str1
, const char *str2
)
1046 struct datum_vector data1
, data2
;
1051 } else if (NULL
== str1
) {
1053 } else if (NULL
== str2
) {
1057 split_string(&data1
, str1
);
1058 split_string(&data2
, str2
);
1060 retval
= data_compare(&data1
, &data2
);