Freeciv21
Develop your civilization from humble roots to a global empire
view_cities_data.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2023 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 /*
15  * This file contains functions used to gather varying data elements for use
16  * in the city view (formally known as the city report).
17  */
18 
19 #include <cerrno>
20 #include <cmath>
21 #include <cstdlib>
22 #include <cstring>
23 
24 // utility
25 #include "fcintl.h"
26 #include "log.h"
27 #include "nation.h"
28 #include "support.h"
29 
30 // common
31 #include "city.h"
32 #include "culture.h"
33 #include "game.h"
34 #include "specialist.h"
35 #include "unitlist.h"
36 
37 // client
38 #include "citydlg_common.h" // city_production_cost_str()
39 #include "governor.h"
40 #include "options.h"
41 
42 #include "views/view_cities_data.h"
43 
50 static QString cr_entry_cityname(const struct city *pcity, const void *data)
51 {
52  /* We used to truncate the name to 14 bytes. This should not be needed
53  * in any modern GUI library and may give an invalid string if a
54  * multibyte character is clipped. */
55  return city_name_get(pcity);
56 }
57 
61 static QString cr_entry_nation(const struct city *pcity, const void *data)
62 {
64 }
65 
70 static QString cr_entry_size(const struct city *pcity, const void *data)
71 {
72  static char buf[8];
73 
74  fc_snprintf(buf, sizeof(buf), "%2d", city_size_get(pcity));
75  return buf;
76 }
77 
83 static QString cr_entry_hstate_concise(const struct city *pcity,
84  const void *data)
85 {
86  if (city_unhappy(pcity)) {
87  return "X"; // Disorder
88  } else if (city_celebrating(pcity)) {
89  return "*"; // Celebrating
90  } else if (city_happy(pcity)) {
91  return "+"; // Happy
92  } else {
93  return " "; // Content
94  }
95  static char buf[4];
97  buf, sizeof(buf), "%s",
98  (city_celebrating(pcity) ? "*" : (city_unhappy(pcity) ? "X" : " ")));
99  return buf;
100 }
101 
107 static QString cr_entry_hstate_verbose(const struct city *pcity,
108  const void *data)
109 {
110  Q_UNUSED(data);
111  return get_city_dialog_status_text(pcity);
112 }
113 
119 static QString cr_entry_workers(const struct city *pcity, const void *data)
120 {
121  static char buf[32];
122 
123  fc_snprintf(buf, sizeof(buf), "%d/%d/%d/%d",
127  pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
128  return buf;
129 }
130 
136 static QString cr_entry_happy(const struct city *pcity, const void *data)
137 {
138  static char buf[8];
139  fc_snprintf(buf, sizeof(buf), "%2d",
140  pcity->feel[CITIZEN_HAPPY][FEELING_FINAL]);
141  return buf;
142 }
143 
147 static QString cr_entry_culture(const struct city *pcity, const void *data)
148 {
149  static char buf[8];
150  fc_snprintf(buf, sizeof(buf), "%3d", pcity->client.culture);
151  return buf;
152 }
153 
157 static QString cr_entry_history(const struct city *pcity, const void *data)
158 {
159  static char buf[20];
160  int perturn = city_history_gain(pcity);
161 
162  if (perturn != 0) {
163  fc_snprintf(buf, sizeof(buf), "%3d (%+d)", pcity->history, perturn);
164  } else {
165  fc_snprintf(buf, sizeof(buf), "%3d", pcity->history);
166  }
167  return buf;
168 }
169 
173 static QString cr_entry_performance(const struct city *pcity,
174  const void *data)
175 {
176  static char buf[8];
177 
178  /*
179  * Infer the actual performance component of culture from server-supplied
180  * values, rather than using the client's guess at EFT_PERFORMANCE.
181  * XXX: if culture ever gets more complicated than history+performance,
182  * this will need revising, possibly to use a server-supplied value.
183  */
184  fc_snprintf(buf, sizeof(buf), "%3d",
185  pcity->client.culture - pcity->history);
186  return buf;
187 }
188 
194 static QString cr_entry_content(const struct city *pcity, const void *data)
195 {
196  static char buf[8];
197  fc_snprintf(buf, sizeof(buf), "%2d",
199  return buf;
200 }
201 
207 static QString cr_entry_unhappy(const struct city *pcity, const void *data)
208 {
209  static char buf[8];
210  fc_snprintf(buf, sizeof(buf), "%2d",
212  return buf;
213 }
214 
220 static QString cr_entry_angry(const struct city *pcity, const void *data)
221 {
222  static char buf[8];
223  fc_snprintf(buf, sizeof(buf), "%2d",
224  pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
225  return buf;
226 }
227 
233 static QString cr_entry_specialists(const struct city *pcity,
234  const void *data)
235 {
236  return specialists_string(pcity->specialists);
237 }
238 
244 static QString cr_entry_specialist(const struct city *pcity,
245  const void *data)
246 {
247  static char buf[8];
248  const struct specialist *sp = static_cast<const specialist *>(data);
249 
250  fc_snprintf(buf, sizeof(buf), "%2d",
251  pcity->specialists[specialist_index(sp)]);
252  return buf;
253 }
254 
260 static QString cr_entry_attack(const struct city *pcity, const void *data)
261 {
262  static char buf[32];
263  int attack_best[4] = {-1, -1, -1, -1}, i;
264 
265  unit_list_iterate(pcity->tile->units, punit)
266  {
267  // What about allied units? Should we just count them?
268  attack_best[3] = unit_type_get(punit)->attack_strength;
269 
270  /* Now that the element is appended to the end of the list, we simply
271  do an insertion sort. */
272  for (i = 2; i >= 0 && attack_best[i] < attack_best[i + 1]; i--) {
273  int tmp = attack_best[i];
274  attack_best[i] = attack_best[i + 1];
275  attack_best[i + 1] = tmp;
276  }
277  }
279 
280  buf[0] = '\0';
281  for (i = 0; i < 3; i++) {
282  if (attack_best[i] >= 0) {
283  cat_snprintf(buf, sizeof(buf), "%s%d", (i > 0) ? "/" : "",
284  attack_best[i]);
285  } else {
286  cat_snprintf(buf, sizeof(buf), "%s-", (i > 0) ? "/" : "");
287  }
288  }
289 
290  return buf;
291 }
292 
298 static QString cr_entry_defense(const struct city *pcity, const void *data)
299 {
300  static char buf[32];
301  int defense_best[4] = {-1, -1, -1, -1}, i;
302 
303  unit_list_iterate(pcity->tile->units, punit)
304  {
305  // What about allied units? Should we just count them?
306  defense_best[3] = unit_type_get(punit)->defense_strength;
307 
308  /* Now that the element is appended to the end of the list, we simply
309  do an insertion sort. */
310  for (i = 2; i >= 0 && defense_best[i] < defense_best[i + 1]; i--) {
311  int tmp = defense_best[i];
312 
313  defense_best[i] = defense_best[i + 1];
314  defense_best[i + 1] = tmp;
315  }
316  }
318 
319  buf[0] = '\0';
320  for (i = 0; i < 3; i++) {
321  if (defense_best[i] >= 0) {
322  cat_snprintf(buf, sizeof(buf), "%s%d", (i > 0) ? "/" : "",
323  defense_best[i]);
324  } else {
325  cat_snprintf(buf, sizeof(buf), "%s-", (i > 0) ? "/" : "");
326  }
327  }
328 
329  return buf;
330 }
331 
337 static QString cr_entry_supported(const struct city *pcity, const void *data)
338 {
339  static char buf[8];
340  int num_supported = unit_list_size(pcity->units_supported);
341 
342  fc_snprintf(buf, sizeof(buf), "%2d", num_supported);
343 
344  return buf;
345 }
346 
352 static QString cr_entry_present(const struct city *pcity, const void *data)
353 {
354  static char buf[8];
355  int num_present = unit_list_size(pcity->tile->units);
356 
357  fc_snprintf(buf, sizeof(buf), "%2d", num_present);
358 
359  return buf;
360 }
361 
367 static QString cr_entry_resources(const struct city *pcity, const void *data)
368 {
369  static char buf[32];
370  fc_snprintf(buf, sizeof(buf), "%d/%d/%d", pcity->surplus[O_FOOD],
371  pcity->surplus[O_SHIELD], pcity->surplus[O_TRADE]);
372  return buf;
373 }
374 
380 static QString cr_entry_foodplus(const struct city *pcity, const void *data)
381 {
382  static char buf[8];
383  fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_FOOD]);
384  return buf;
385 }
386 
392 static QString cr_entry_prodplus(const struct city *pcity, const void *data)
393 {
394  static char buf[8];
395  fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_SHIELD]);
396  return buf;
397 }
398 
404 static QString cr_entry_tradeplus(const struct city *pcity, const void *data)
405 {
406  static char buf[8];
407  fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_TRADE]);
408  return buf;
409 }
410 
416 static QString cr_entry_output(const struct city *pcity, const void *data)
417 {
418  static char buf[32];
419  int goldie = pcity->surplus[O_GOLD];
420 
421  fc_snprintf(buf, sizeof(buf), "%3d/%d/%d", goldie, pcity->prod[O_LUXURY],
422  pcity->prod[O_SCIENCE]);
423  return buf;
424 }
425 
431 static QString cr_entry_gold(const struct city *pcity, const void *data)
432 {
433  static char buf[8];
434 
435  if (pcity->surplus[O_GOLD] > 0) {
436  fc_snprintf(buf, sizeof(buf), "+%d", pcity->surplus[O_GOLD]);
437  } else {
438  fc_snprintf(buf, sizeof(buf), "%3d", pcity->surplus[O_GOLD]);
439  }
440  return buf;
441 }
442 
448 static QString cr_entry_luxury(const struct city *pcity, const void *data)
449 {
450  static char buf[8];
451  fc_snprintf(buf, sizeof(buf), "%3d", pcity->prod[O_LUXURY]);
452  return buf;
453 }
454 
460 static QString cr_entry_science(const struct city *pcity, const void *data)
461 {
462  static char buf[8];
463  fc_snprintf(buf, sizeof(buf), "%3d", pcity->prod[O_SCIENCE]);
464  return buf;
465 }
466 
472 static QString cr_entry_growturns(const struct city *pcity, const void *data)
473 {
474  int turns = city_turns_to_grow(pcity);
475  char buffer[8];
476  static char buf[32];
477 
478  if (turns == FC_INFINITY) {
479  // 'never' wouldn't be easily translatable here.
480  fc_snprintf(buffer, sizeof(buffer), "---");
481  } else {
482  // Shrinking cities get a negative value.
483  fc_snprintf(buffer, sizeof(buffer), "%4d", turns);
484  }
485  fc_snprintf(buf, sizeof(buf), "%s (%d/%d)", buffer, pcity->food_stock,
487  return buf;
488 }
489 
495 static QString cr_entry_pollution(const struct city *pcity, const void *data)
496 {
497  static char buf[8];
498  fc_snprintf(buf, sizeof(buf), "%3d", pcity->pollution);
499  return buf;
500 }
501 
507 static QString cr_entry_trade_routes(const struct city *pcity,
508  const void *data)
509 {
510  static char buf[16];
511  int num = 0, value = 0;
512 
513  trade_routes_iterate(pcity, proute)
514  {
515  num++;
516  value += proute->value;
517  }
519 
520  if (0 == num) {
521  sz_strlcpy(buf, "0");
522  } else {
523  fc_snprintf(buf, sizeof(buf), "%d (+%d)", num, value);
524  }
525  return buf;
526 }
527 
533 static QString cr_entry_build_slots(const struct city *pcity,
534  const void *data)
535 {
536  static char buf[8];
537  fc_snprintf(buf, sizeof(buf), "%3d", city_build_slots(pcity));
538  return buf;
539 }
540 
546 static QString cr_entry_building(const struct city *pcity, const void *data)
547 {
548  static char buf[192];
549  const char *from_worklist = worklist_is_empty(&pcity->worklist) ? ""
551  ? "+"
552  : _("(worklist)");
553 
554  if (city_production_has_flag(pcity, IF_GOLD)) {
555  fc_snprintf(buf, sizeof(buf), "%s (%d)%s",
557  MAX(0, pcity->surplus[O_SHIELD]), from_worklist);
558  } else {
559  fc_snprintf(buf, sizeof(buf), "%s (%d/%s)%s",
561  city_production_cost_str(pcity), from_worklist);
562  }
563 
564  return buf;
565 }
566 
572 static QString cr_entry_build_cost(const struct city *pcity,
573  const void *data)
574 {
575  Q_UNUSED(data)
576  char bufone[8];
577  char buftwo[8];
578  static char buf[32];
579  int price;
580  int turns;
581 
582  if (city_production_has_flag(pcity, IF_GOLD)) {
583  fc_snprintf(buf, sizeof(buf), "*");
584  return buf;
585  }
586  price = pcity->client.buy_cost;
587  turns = city_production_turns_to_build(pcity, true);
588 
589  if (price > 99999) {
590  fc_snprintf(bufone, sizeof(bufone), "---");
591  } else {
592  fc_snprintf(bufone, sizeof(bufone), "%d", price);
593  }
594  if (turns > 999) {
595  fc_snprintf(buftwo, sizeof(buftwo), "--");
596  } else {
597  fc_snprintf(buftwo, sizeof(buftwo), "%3d", turns);
598  }
599  fc_snprintf(buf, sizeof(buf), "%s/%s", buftwo, bufone);
600  return buf;
601 }
602 
608 static QString cr_entry_build_cost_gold(const struct city *pcity,
609  const void *data)
610 {
611  Q_UNUSED(data)
612  char bufone[8];
613  static char buf[32];
614  int price;
615 
616  if (city_production_has_flag(pcity, IF_GOLD)) {
617  fc_snprintf(buf, sizeof(buf), "*");
618  return buf;
619  }
620  price = pcity->client.buy_cost;
621 
622  if (price > 99999) {
623  fc_snprintf(bufone, sizeof(bufone), "---");
624  } else {
625  fc_snprintf(bufone, sizeof(bufone), "%d", price);
626  }
627  fc_snprintf(buf, sizeof(buf), "%s", bufone);
628  return buf;
629 }
630 
636 static QString cr_entry_build_cost_turns(const struct city *pcity,
637  const void *data)
638 {
639  Q_UNUSED(data)
640  char bufone[8];
641  static char buf[32];
642  int turns;
643 
644  turns = city_production_turns_to_build(pcity, true);
645 
646  if (turns > 999) {
647  fc_snprintf(bufone, sizeof(bufone), "--");
648  } else {
649  fc_snprintf(bufone, sizeof(bufone), "%3d", turns);
650  }
651  fc_snprintf(buf, sizeof(buf), "%s", bufone);
652  return buf;
653 }
654 
660 static QString cr_entry_corruption(const struct city *pcity,
661  const void *data)
662 {
663  Q_UNUSED(data)
664  static char buf[8];
665  fc_snprintf(buf, sizeof(buf), "%3d", -(pcity->waste[O_TRADE]));
666  return buf;
667 }
668 
674 static QString cr_entry_waste(const struct city *pcity, const void *data)
675 {
676  static char buf[8];
677  fc_snprintf(buf, sizeof(buf), "%3d", -(pcity->waste[O_SHIELD]));
678  return buf;
679 }
680 
686 static QString cr_entry_plague_risk(const struct city *pcity,
687  const void *data)
688 {
689  Q_UNUSED(data)
690  static char buf[8];
691  if (!game.info.illness_on) {
692  fc_snprintf(buf, sizeof(buf), " -.-");
693  } else {
694  fc_snprintf(buf, sizeof(buf), "%4.1f",
695  static_cast<float>(city_illness_calc(pcity, nullptr, nullptr,
696  nullptr, nullptr))
697  / 10.0);
698  }
699  return buf;
700 }
701 
705 static QString cr_entry_continent(const struct city *pcity, const void *data)
706 {
707  Q_UNUSED(data)
708  static char buf[8];
709  fc_snprintf(buf, sizeof(buf), "%3d", pcity->tile->continent);
710  return buf;
711 }
712 
718 static QString cr_entry_cma(const struct city *pcity, const void *data)
719 {
720  Q_UNUSED(data)
721  return cmafec_get_short_descr_of_city(pcity);
722 }
723 
724 /* City report options (which columns get shown)
725  * To add a new entry, you should just have to:
726  * - add a function like those above
727  * - add an entry in the base_city_report_specs[] table
728  */
729 
730 // This generates the function name and the tagname:
731 #define FUNC_TAG(var) cr_entry_##var, #var
732 
733 static const struct city_report_spec base_city_report_specs[] = {
734  // CITY columns
735  {true, -15, 0, nullptr, N_("?city:Name"), N_("City: Name"), nullptr,
736  FUNC_TAG(cityname)},
737  {false, -15, 0, nullptr, N_("Nation"), N_("City: Nation"), nullptr,
738  FUNC_TAG(nation)},
739  {false, 3, 1, nullptr, N_("?Continent:Cn"), N_("City: Continent number"),
740  nullptr, FUNC_TAG(continent)},
741  {true, 2, 1, nullptr, N_("?size [short]:Sz"), N_("City: Size"), nullptr,
742  FUNC_TAG(size)},
743  {// TRANS: Header "It will take this many turns before city grows"
744  true, 14, 1, N_("?food (population):Grow"),
745  N_("?Stock/Target:(Have/Need)"), N_("City: Turns until growth/famine"),
746  nullptr, FUNC_TAG(growturns)},
747  {true, -8, 1, nullptr, N_("State"),
748  N_("City: State: Celebrating/Happy/Peace/Disorder"), nullptr,
749  FUNC_TAG(hstate_verbose)},
750  {false, 1, 1, nullptr, nullptr,
751  N_("City: Concise state: *=Celebrating, +=Happy, X=Disorder"), nullptr,
752  FUNC_TAG(hstate_concise)},
753  {true, 15, 1, nullptr, N_("?cma:Governor"), N_("City: Governor"),
754  nullptr, FUNC_TAG(cma)},
755  {false, 4, 1, N_("?plague risk [short]:Pla"), N_("(%)"),
756  N_("City: Plague risk per turn"), nullptr, FUNC_TAG(plague_risk)},
757 
758  // CITIZEN columns
759  {false, 2, 1, nullptr, N_("?Happy workers:H"), N_("Citizens: Happy"),
760  nullptr, FUNC_TAG(happy)},
761  {false, 2, 1, nullptr, N_("?Content workers:C"), N_("Citizens: Content"),
762  nullptr, FUNC_TAG(content)},
763  {false, 2, 1, nullptr, N_("?Unhappy workers:U"), N_("Citizens: Unhappy"),
764  nullptr, FUNC_TAG(unhappy)},
765  {false, 2, 1, nullptr, N_("?Angry workers:A"), N_("Citizens: Angry"),
766  nullptr, FUNC_TAG(angry)},
767  {false, 10, 1, N_("?city:Citizens"),
768  N_("?happy/content/unhappy/angry:H/C/U/A"),
769  N_("Citizens: Concise: Happy/Content/Unhappy/Angry"), nullptr,
770  FUNC_TAG(workers)},
771 
772  // UNIT columns
773  {false, 8, 1, N_("Best"), N_("attack"), N_("Units: Best attackers"),
774  nullptr, FUNC_TAG(attack)},
775  {false, 8, 1, N_("Best"), N_("defense"), N_("Units: Best defenders"),
776  nullptr, FUNC_TAG(defense)},
777  {false, 2, 1, N_("Units"),
778  // TRANS: Header "Number of units inside city"
779  N_("?Present (units):Here"), N_("Units: Number present"), nullptr,
780  FUNC_TAG(present)},
781  {false, 2, 1, N_("Units"),
782  // TRANS: Header "Number of units supported by given city"
783  N_("?Supported (units):Owned"), N_("Units: Number supported"), nullptr,
784  FUNC_TAG(supported)},
785  // TRANS: "UpT" = "Units per turn"
786  {false, 3, 1, nullptr, N_("UpT"),
787  N_("Units: Maximum buildable per turn"), nullptr,
788  FUNC_TAG(build_slots)},
789 
790  // RESOURCE columns
791  {false, 10, 1, N_("Surplus"), N_("?food/production/trade:F/P/T"),
792  N_("Resources: Surplus Food, Production, Trade"), nullptr,
794  {true, 3, 1, nullptr, N_("?Food surplus [short]:+F"),
795  N_("Resources: Surplus Food"), nullptr, FUNC_TAG(foodplus)},
796  {true, 3, 1, nullptr, N_("?Production surplus [short]:+P"),
797  N_("Resources: Surplus Production"), nullptr, FUNC_TAG(prodplus)},
798  {false, 3, 1, nullptr, N_("?Production loss (waste) [short]:-P"),
799  N_("Resources: Waste (lost production)"), nullptr, FUNC_TAG(waste)},
800  {true, 3, 1, nullptr, N_("?Trade surplus [short]:+T"),
801  N_("Resources: Surplus Trade"), nullptr, FUNC_TAG(tradeplus)},
802  {true, 3, 1, nullptr, N_("?Trade loss (corruption) [short]:-T"),
803  N_("Resources: Corruption (lost trade)"), nullptr,
804  FUNC_TAG(corruption)},
805  {false, 1, 1, N_("?number_trade_routes:n"), N_("?number_trade_routes:R"),
806  N_("Resources: Number (and total value) of trade routes"), nullptr,
807  FUNC_TAG(trade_routes)},
808  {false, 3, 1, nullptr, N_("?pollution [short]:Pol"),
809  N_("Production: Pollution"), nullptr, FUNC_TAG(pollution)},
810 
811  // ECONOMY columns
812  {false, 10, 1, N_("Surplus"), N_("?gold/luxury/science:G/L/S"),
813  N_("Surplus: Gold, Luxuries, Science"), nullptr, FUNC_TAG(output)},
814  {true, 3, 1, nullptr, N_("?Gold:G"), N_("Surplus: Gold"), nullptr,
815  FUNC_TAG(gold)},
816  {false, 3, 1, nullptr, N_("?Luxury:L"), N_("Surplus: Luxury Goods"),
817  nullptr, FUNC_TAG(luxury)},
818  {true, 3, 1, nullptr, N_("?Science:S"), N_("Surplus: Science"), nullptr,
819  FUNC_TAG(science)},
820 
821  // CULTURE columns
822  {false, 3, 1, nullptr, N_("?Culture:Clt"),
823  N_("Culture: History + Performance"), nullptr, FUNC_TAG(culture)},
824  {false, 3, 1, nullptr, N_("?History:Hst"),
825  N_("Culture: History (and gain per turn)"), nullptr, FUNC_TAG(history)},
826  {false, 3, 1, nullptr, N_("?Performance:Prf"),
827  N_("Culture: Performance"), nullptr, FUNC_TAG(performance)},
828 
829  // PRODUCTION columns
830  {true, 9, 1, N_("Production"), N_("Turns/Buy"),
831  /*N_("Turns or gold to complete production"), future menu needs
832  translation */
833  N_("Production: Turns/gold to complete"), nullptr,
834  FUNC_TAG(build_cost)},
835  {false, 0, 1, N_("Buy"), N_("(Gold)"), N_("Production: Buy Cost"),
836  nullptr, FUNC_TAG(build_cost_gold)},
837  {false, 0, 1, N_("Finish"), N_("(Turns)"), N_("Production: Build Turns"),
838  nullptr, FUNC_TAG(build_cost_turns)},
839  {true, 0, 1, N_("Currently Building"), N_("?Stock/Target:(Have/Need)"),
840  N_("Production: Currently Building"), nullptr, FUNC_TAG(building)}};
841 
842 std::vector<city_report_spec> city_report_specs;
843 static int num_creport_cols;
844 
849 
854 {
855  return &(city_report_specs[i].show);
856 }
857 
861 const char *city_report_spec_tagname(int i)
862 {
863  return city_report_specs[i].tagname;
864 }
865 
872 {
873  static char sp_explanation[SP_MAX][128];
874  static char sp_explanations[SP_MAX * 128];
875  struct city_report_spec *p;
876  int i;
877 
880  city_report_specs = std::vector<city_report_spec>(num_creport_cols);
881  p = &city_report_specs[0];
882 
883  fc_snprintf(sp_explanations, sizeof(sp_explanations), "%s",
884  _("Specialists: "));
886  {
887  struct specialist *s = specialist_by_number(sp);
888 
889  p->show = false;
890  p->width = 2;
891  p->space = 1;
892  p->title1 = Q_("?specialist:S");
894  fc_snprintf(sp_explanation[sp], sizeof(sp_explanation[sp]),
895  _("Specialists: %s"), specialist_plural_translation(s));
896  cat_snprintf(sp_explanations, sizeof(sp_explanations), "%s%s",
897  (sp == 0) ? "" : ", ", specialist_plural_translation(s));
898  p->explanation = sp_explanation[sp];
899  p->data = s;
902  p++;
903  }
905 
906  // Summary column for all specialists.
907  {
908  static char sp_summary[128];
909 
910  p->show = false;
911  p->width = MAX(7, specialist_count() * 2 - 1);
912  p->space = 1;
913  p->title1 = _("Special");
914  fc_snprintf(sp_summary, sizeof(sp_summary), "%s",
916  p->title2 = sp_summary;
917  p->explanation = sp_explanations;
918  p->data = nullptr;
920  p->tagname = "specialists";
921  p++;
922  }
923 
925 
926  for (i = 0; i < ARRAY_SIZE(base_city_report_specs); i++) {
927  if (p->title1) {
928  p->title1 = Q_(p->title1);
929  }
930  if (p->title2) {
931  p->title2 = Q_(p->title2);
932  }
933  p->explanation = _(p->explanation);
934  p++;
935  }
936 
939 }
940 
954 /* A datum is one short string, or one number.
955  A datum_vector represents a long string of alternating strings and
956  numbers.
957  */
958 struct datum {
959  union {
962  } val;
964 };
965 #define SPECVEC_TAG datum
966 #include "specvec.h"
967 
971 static void init_datum_string(struct datum *dat, const char *left,
972  const char *right)
973 {
974  int len = right - left;
975 
976  dat->is_numeric = false;
977  dat->val.string_value = new char[len + 1];
978  memcpy(dat->val.string_value, left, len);
979  dat->val.string_value[len] = 0;
980 }
981 
986 static void init_datum_number(struct datum *dat, float val)
987 {
988  dat->is_numeric = true;
989  dat->val.numeric_value = val;
990 }
991 
996 static void free_datum(struct datum *dat)
997 {
998  if (!dat->is_numeric) {
999  delete[] dat->val.string_value;
1000  }
1001 }
1002 
1009 static int datum_compare(const struct datum *a, const struct datum *b)
1010 {
1011  if (a->is_numeric == b->is_numeric) {
1012  if (a->is_numeric) {
1013  if (a->val.numeric_value == b->val.numeric_value) {
1014  return 0;
1015  } else if (a->val.numeric_value < b->val.numeric_value) {
1016  return -1;
1017  } else if (a->val.numeric_value > b->val.numeric_value) {
1018  return +1;
1019  } else {
1020  return 0; // shrug
1021  }
1022  } else {
1023  return strcmp(a->val.string_value, b->val.string_value);
1024  }
1025  } else {
1026  if (a->is_numeric) {
1027  return -1;
1028  } else {
1029  return 1;
1030  }
1031  }
1032 }
1033 
1037 static int data_compare(const struct datum_vector *a,
1038  const struct datum_vector *b)
1039 {
1040  int i, n;
1041 
1042  n = MIN(a->size, b->size);
1043 
1044  for (i = 0; i < n; i++) {
1045  int cmp = datum_compare(&a->p[i], &b->p[i]);
1046 
1047  if (cmp != 0) {
1048  return cmp;
1049  }
1050  }
1051 
1052  /* The first n fields match; whoever has more fields goes last.
1053  If they have equal numbers, the two really are equal. */
1054  return a->size - b->size;
1055 }
1056 
1060 static void split_string(struct datum_vector *data, const char *str)
1061 {
1062  const char *string_start;
1063 
1064  datum_vector_init(data);
1065  string_start = str;
1066  while (*str) {
1067  char *endptr;
1068  float value;
1069 
1070  errno = 0;
1071  value = strtof(str, &endptr);
1072  if (errno != 0 || endptr == str || !std::isfinite(value)) {
1073  // that wasn't a sensible number; go on
1074  str++;
1075  } else {
1076  /* that was a number, so stop the string we were parsing, add
1077  it (unless it's empty), then add the number we just parsed */
1078  struct datum d;
1079 
1080  if (str != string_start) {
1081  init_datum_string(&d, string_start, str);
1082  datum_vector_append(data, d);
1083  }
1084 
1085  init_datum_number(&d, value);
1086  datum_vector_append(data, d);
1087 
1088  // finally, update the string position pointers
1089  string_start = str = endptr;
1090  }
1091  }
1092 
1093  // if we have anything leftover then it's a string
1094  if (str != string_start) {
1095  struct datum d;
1096 
1097  init_datum_string(&d, string_start, str);
1098  datum_vector_append(data, d);
1099  }
1100 }
1101 
1105 static void free_data(struct datum_vector *data)
1106 {
1107  int i;
1108 
1109  for (i = 0; i < data->size; i++) {
1110  free_datum(&data->p[i]);
1111  }
1112  datum_vector_free(data);
1113 }
1114 
1118 int cityrepfield_compare(const char *str1, const char *str2)
1119 {
1120  struct datum_vector data1, data2;
1121  int retval;
1122 
1123  if (str1 == str2) {
1124  return 0;
1125  } else if (nullptr == str1) {
1126  return 1;
1127  } else if (nullptr == str2) {
1128  return -1;
1129  }
1130 
1131  split_string(&data1, str1);
1132  split_string(&data2, str2);
1133 
1134  retval = data_compare(&data1, &data2);
1135 
1136  free_data(&data1);
1137  free_data(&data2);
1138 
1139  return retval;
1140 }
1141 
1145 bool can_city_sell_universal(const struct city *pcity,
1146  const struct universal *target)
1147 {
1148  return target->kind == VUT_IMPROVEMENT
1149  && can_city_sell_building(pcity, target->value.building);
1150 }
int city_granary_size(int city_size)
Generalized formula used to calculate granary size.
Definition: city.cpp:2034
int city_build_slots(const struct city *pcity)
The maximum number of units a city can build per turn.
Definition: city.cpp:2809
struct player * city_owner(const struct city *pcity)
Return the owner of the city.
Definition: city.cpp:1083
bool city_production_has_flag(const struct city *pcity, enum impr_flag_id flag)
Return TRUE when the current production has this flag.
Definition: city.cpp:691
const char * city_name_get(const struct city *pcity)
Return the name of the city.
Definition: city.cpp:1077
int city_production_turns_to_build(const struct city *pcity, bool include_shield_stock)
Calculates the turns which are needed to build the requested production in the city.
Definition: city.cpp:784
bool city_unhappy(const struct city *pcity)
Return TRUE iff the city is unhappy.
Definition: city.cpp:1544
bool city_celebrating(const struct city *pcity)
cities celebrate only after consecutive happy turns
Definition: city.cpp:1564
int city_illness_calc(const struct city *pcity, int *ill_base, int *ill_size, int *ill_trade, int *ill_pollution)
Calculate city's illness in tenth of percent:
Definition: city.cpp:2746
bool city_happy(const struct city *pcity)
Return TRUE iff the city is happy.
Definition: city.cpp:1531
static int cmp(int v1, int v2)
Compare two integer values, as required by qsort.
Definition: city.cpp:317
int city_turns_to_grow(const struct city *pcity)
Calculates the turns which are needed for the city to grow.
Definition: city.cpp:1897
citizens city_size_get(const struct city *pcity)
Get the city size.
Definition: city.cpp:1101
const char * city_production_name_translation(const struct city *pcity)
Return the extended name of the current production.
Definition: city.cpp:672
@ CITIZEN_ANGRY
Definition: city.h:243
@ CITIZEN_HAPPY
Definition: city.h:240
@ CITIZEN_CONTENT
Definition: city.h:241
@ CITIZEN_UNHAPPY
Definition: city.h:242
@ FEELING_FINAL
Definition: city.h:256
QString get_city_dialog_status_text(const struct city *pcity)
Return text describing the city's status: disorder/celebrating/...
char * city_production_cost_str(const struct city *pcity)
Return a string describing the cost for the production of the city considerung several build slots fo...
char * resources
Definition: comments.cpp:33
int city_history_gain(const struct city *pcity)
How much history city gains this turn.
Definition: culture.cpp:31
static void attack(QVariant data1, QVariant data2)
Action "Attack" for choice dialog.
Definition: dialogs.cpp:2464
#define SP_MAX
Definition: fc_types.h:324
@ O_SHIELD
Definition: fc_types.h:86
@ O_FOOD
Definition: fc_types.h:85
@ O_TRADE
Definition: fc_types.h:87
@ O_SCIENCE
Definition: fc_types.h:90
@ O_LUXURY
Definition: fc_types.h:89
@ O_GOLD
Definition: fc_types.h:88
#define Q_(String)
Definition: fcintl.h:53
#define _(String)
Definition: fcintl.h:50
#define N_(String)
Definition: fcintl.h:52
struct civ_game game
Definition: game.cpp:47
const char * cmafec_get_short_descr_of_city(const struct city *pcity)
Return short description of city governor preset.
Definition: governor.cpp:774
bool can_city_sell_building(const struct city *pcity, const struct impr_type *pimprove)
Return TRUE iff the city can sell the given improvement.
#define fc_assert(condition)
Definition: log.h:89
const char * nation_adjective_for_player(const struct player *pplayer)
Return the (translated) adjective for the given nation of a player.
Definition: nation.cpp:146
client_options * gui_options
Definition: options.cpp:74
int len
Definition: packhand.cpp:127
#define ARRAY_SIZE(x)
Definition: shared.h:79
#define MIN(x, y)
Definition: shared.h:49
#define FC_INFINITY
Definition: shared.h:32
#define MAX(x, y)
Definition: shared.h:48
const char * specialists_string(const citizens *specialist_list)
Return a string showing the number of specialists in the array.
Definition: specialist.cpp:198
const char * specialist_rule_name(const struct specialist *sp)
Return the (untranslated) rule name of the specialist type.
Definition: specialist.cpp:142
Specialist_type_id specialist_index(const struct specialist *sp)
Return the specialist index.
Definition: specialist.cpp:74
const char * specialist_abbreviation_translation(const struct specialist *sp)
Return the (translated) abbreviation of the specialist type.
Definition: specialist.cpp:160
const char * specialists_abbreviation_string()
Return a string containing all the specialist abbreviations, for instance "E/S/T".
Definition: specialist.cpp:170
struct specialist * specialist_by_number(const Specialist_type_id id)
Return the specialist pointer for the given number.
Definition: specialist.cpp:92
Specialist_type_id specialist_count()
Return the number of specialist_types.
Definition: specialist.cpp:63
const char * specialist_plural_translation(const struct specialist *sp)
Return the (translated, plural) name of the specialist type.
Definition: specialist.cpp:151
#define specialist_type_iterate_end
Definition: specialist.h:73
#define specialist_type_iterate(sp)
Definition: specialist.h:67
size_t size
Definition: specvec.h:64
const char * tagname
const char * title2
QString(* func)(const struct city *pcity, const void *data)
const char * explanation
const char * title1
Definition: city.h:291
struct city::@15::@18 client
int surplus[O_LAST]
Definition: city.h:324
int food_stock
Definition: city.h:338
int history
Definition: city.h:379
int pollution
Definition: city.h:340
int waste[O_LAST]
Definition: city.h:325
struct worklist worklist
Definition: city.h:373
citizens feel[CITIZEN_LAST][FEELING_LAST]
Definition: city.h:302
citizens specialists[SP_MAX]
Definition: city.h:305
struct tile * tile
Definition: city.h:293
int shield_stock
Definition: city.h:339
int prod[O_LAST]
Definition: city.h:327
struct unit_list * units_supported
Definition: city.h:377
struct packet_game_info info
Definition: game.h:80
bool concise_city_production
Definition: options.h:93
The following several functions allow intelligent sorting city report fields by column.
float numeric_value
char * string_value
union datum::@153 val
struct unit_list * units
Definition: tile.h:50
Continent_id continent
Definition: tile.h:46
int defense_strength
Definition: unittype.h:480
int attack_strength
Definition: unittype.h:479
enum universals_n kind
Definition: fc_types.h:740
universals_u value
Definition: fc_types.h:739
int fc_snprintf(char *str, size_t n, const char *format,...)
See also fc_utf8_snprintf_trunc(), fc_utf8_snprintf_rep().
Definition: support.cpp:537
int cat_snprintf(char *str, size_t n, const char *format,...)
cat_snprintf is like a combination of fc_snprintf and fc_strlcat; it does snprintf to the end of an e...
Definition: support.cpp:564
#define sz_strlcpy(dest, src)
Definition: support.h:140
#define trade_routes_iterate_end
Definition: traderoutes.h:127
#define trade_routes_iterate(c, proute)
Definition: traderoutes.h:122
const struct impr_type * building
Definition: fc_types.h:579
#define unit_list_iterate(unitlist, punit)
Definition: unitlist.h:25
#define unit_list_iterate_end
Definition: unitlist.h:27
const struct unit_type * unit_type_get(const struct unit *punit)
Return the unit type for this unit.
Definition: unittype.cpp:114
static QString cr_entry_defense(const struct city *pcity, const void *data)
Returns string with best defend values of units in city.
const char * city_report_spec_tagname(int i)
Simple wrapper for city_report_specs.tagname.
static QString cr_entry_culture(const struct city *pcity, const void *data)
Returns city total culture written to string.
static QString cr_entry_history(const struct city *pcity, const void *data)
Returns city history culture value written to string.
static QString cr_entry_plague_risk(const struct city *pcity, const void *data)
Returns risk percentage of plague written to string.
static QString cr_entry_size(const struct city *pcity, const void *data)
Returns city size written to string.
static QString cr_entry_trade_routes(const struct city *pcity, const void *data)
Returns number and output of trade routes written to string.
static QString cr_entry_present(const struct city *pcity, const void *data)
Returns number of present units written to string.
static QString cr_entry_tradeplus(const struct city *pcity, const void *data)
Returns trade surplus written to string.
static QString cr_entry_resources(const struct city *pcity, const void *data)
Returns string listing amounts of resources.
static void init_datum_string(struct datum *dat, const char *left, const char *right)
Init a datum from a substring.
static QString cr_entry_supported(const struct city *pcity, const void *data)
Returns number of supported units written to string.
static QString cr_entry_growturns(const struct city *pcity, const void *data)
Returns number of turns before city grows written to string.
static QString cr_entry_angry(const struct city *pcity, const void *data)
Returns number of angry citizens written to string.
static QString cr_entry_cma(const struct city *pcity, const void *data)
Returns city cma description.
static QString cr_entry_prodplus(const struct city *pcity, const void *data)
Returns production surplus written to string.
static QString cr_entry_build_cost_turns(const struct city *pcity, const void *data)
Returns current production turns to completion written to string.
static void split_string(struct datum_vector *data, const char *str)
Split a string into a vector of datum.
static QString cr_entry_content(const struct city *pcity, const void *data)
Returns number of content citizens written to string.
static void free_data(struct datum_vector *data)
Free every datum in the vector.
static QString cr_entry_performance(const struct city *pcity, const void *data)
Returns city performance culture value written to string.
static QString cr_entry_continent(const struct city *pcity, const void *data)
Returns number of continent.
static QString cr_entry_build_cost_gold(const struct city *pcity, const void *data)
Returns cost of buying current production only written to string.
static int data_compare(const struct datum_vector *a, const struct datum_vector *b)
Compare two strings of data lexicographically.
static const struct city_report_spec base_city_report_specs[]
#define FUNC_TAG(var)
static QString cr_entry_building(const struct city *pcity, const void *data)
Returns name of current production.
static QString cr_entry_luxury(const struct city *pcity, const void *data)
Returns luxury output written to string.
static QString cr_entry_build_cost(const struct city *pcity, const void *data)
Returns cost of buying current production and turns to completion written to string.
static QString cr_entry_build_slots(const struct city *pcity, const void *data)
Returns number of build slots written to string.
static QString cr_entry_cityname(const struct city *pcity, const void *data)
cr_entry = return an entry (one column for one city) for the city report These return ptrs to filled ...
bool can_city_sell_universal(const struct city *pcity, const struct universal *target)
Same as can_city_sell_building(), but with universal argument.
static QString cr_entry_hstate_verbose(const struct city *pcity, const void *data)
Returns verbose city happiness state written to string.
static QString cr_entry_corruption(const struct city *pcity, const void *data)
Returns corruption amount written to string.
static QString cr_entry_workers(const struct city *pcity, const void *data)
Returns number of citizens of each happiness state written to string.
int cityrepfield_compare(const char *str1, const char *str2)
The real function: split the two strings, and compare them.
int num_city_report_spec()
Simple wrapper for num_creport_cols()
static QString cr_entry_happy(const struct city *pcity, const void *data)
Returns number of happy citizens written to string.
static QString cr_entry_pollution(const struct city *pcity, const void *data)
Returns pollution output written to string.
std::vector< city_report_spec > city_report_specs
static void free_datum(struct datum *dat)
Free the data associated with a datum – that is, free the string if it was allocated.
static QString cr_entry_specialist(const struct city *pcity, const void *data)
Returns number of specialists of type given as data written to string.
bool * city_report_spec_show_ptr(int i)
Simple wrapper for city_report_specs.show.
static QString cr_entry_output(const struct city *pcity, const void *data)
Returns string describing resource output.
static QString cr_entry_specialists(const struct city *pcity, const void *data)
Returns list of specialists written to string.
static QString cr_entry_hstate_concise(const struct city *pcity, const void *data)
Returns concise city happiness state written to string.
static QString cr_entry_unhappy(const struct city *pcity, const void *data)
Returns number of unhappy citizens written to string.
void init_city_report_game_data()
Initialize city report data.
static void init_datum_number(struct datum *dat, float val)
Init a datum from a number (a float because we happen to use strtof).
static QString cr_entry_attack(const struct city *pcity, const void *data)
Returns string with best attack values of units in city.
static int datum_compare(const struct datum *a, const struct datum *b)
Compare two data items as described above:
static QString cr_entry_nation(const struct city *pcity, const void *data)
Translated name of nation who owns this city.
static QString cr_entry_science(const struct city *pcity, const void *data)
Returns science output written to string.
static QString cr_entry_waste(const struct city *pcity, const void *data)
Returns waste amount written to string.
static int num_creport_cols
static QString cr_entry_foodplus(const struct city *pcity, const void *data)
Returns food surplus written to string.
static QString cr_entry_gold(const struct city *pcity, const void *data)
Returns gold surplus written to string.
#define NUM_CREPORT_COLS
bool worklist_is_empty(const struct worklist *pwl)
Returns whether worklist has no elements.
Definition: worklist.cpp:60