Freeciv21
Develop your civilization from humble roots to a global empire
helpdata.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 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 
19 #include <fc_config.h>
20 
21 #include <QBitArray>
22 #include <QList>
23 
24 #include <cstring>
25 
26 // utility
27 #include "astring.h"
28 #include "bitvector.h"
29 #include "fciconv.h"
30 #include "fcintl.h"
31 #include "log.h"
32 #include "registry.h"
33 #include "registry_ini.h"
34 #include "requirements.h"
35 #include "support.h"
36 
37 // common
38 #include "effects.h"
39 #include "game.h"
40 #include "government.h"
41 #include "map.h"
42 #include "movement.h"
43 #include "multipliers.h"
44 #include "nation.h"
45 #include "reqtext.h"
46 #include "research.h"
47 #include "server_settings.h"
48 #include "specialist.h"
49 #include "unit.h"
50 #include "version.h"
51 
52 #include "helpdata.h"
53 
54 // helper macro for easy conversion from snprintf and cat_snprintf
55 #define CATLSTR(_b, _s, _t) fc_strlcat(_b, _t, _s)
56 
57 // This must be in same order as enum in helpdata.h
58 static const char *const help_type_names[] = {
59  "(Any)", "(Text)", "Units", "Improvements", "Wonders",
60  "Techs", "Terrain", "Extras", "Goods", "Specialists",
61  "Governments", "Ruleset", "Tileset", "Nations", "Multipliers",
62  "Effects", nullptr};
63 
64 typedef QList<const struct help_item *> helpList;
66 /* help_nodes_init is not quite the same as booted in boot_help_texts();
67  latter can be FALSE even after call, eg if couldn't find helpdata.txt.
68 */
69 
74 {
75  if (!help_nodes) {
76  return;
77  }
78  for (const auto *ptmp : qAsConst(*help_nodes)) {
79  delete[] ptmp->topic;
80  delete[] ptmp->text;
81  delete ptmp;
82  }
83  delete help_nodes;
84  help_nodes = nullptr;
85 }
86 
92 static bool insert_veteran_help(char *outbuf, size_t outlen,
93  const struct veteran_system *veteran,
94  const char *intro, const char *nolevels)
95 {
96  /* game.veteran can be nullptr in pregame; if so, keep quiet about
97  * veteran levels */
98  if (!veteran) {
99  return false;
100  }
101 
102  fc_assert_ret_val(veteran->levels >= 1, false);
103 
104  if (veteran->levels == 1) {
105  // Only a single veteran level. Don't bother to name it.
106  if (nolevels) {
107  CATLSTR(outbuf, outlen, nolevels);
108  return true;
109  } else {
110  return false;
111  }
112  } else {
113  int i;
114  fc_assert_ret_val(veteran->definitions != nullptr, false);
115  if (intro) {
116  CATLSTR(outbuf, outlen, intro);
117  CATLSTR(outbuf, outlen, "\n\n");
118  }
119  // TODO: Report raise_chance and work_raise_chance
120  CATLSTR(
121  outbuf, outlen,
122  /* TRANS: Header for fixed-width veteran level table.
123  * TRANS: Translators cannot change column widths :(
124  * TRANS: "Level name" left-justified, other two right-justified */
125  _("Veteran level Power factor Move bonus\n"));
126  CATLSTR(outbuf, outlen,
127  // TRANS: Part of header for veteran level table.
128  _("--------------------------------------------"));
129  for (i = 0; i < veteran->levels; i++) {
130  const struct veteran_level *level = &veteran->definitions[i];
131  const char *name = name_translation_get(&level->name);
132  /* Use get_internal_string_length() for correct alignment with
133  * multibyte character encodings */
134  cat_snprintf(
135  outbuf, outlen, "\n%s%*s %4d%% %12s", name,
136  MAX(0, 25 - (int) get_internal_string_length(name)), "",
137  level->power_fact,
138  /* e.g. "- ", "+ 1/3", "+ 1 ", "+ 2 2/3" */
139  move_points_text_full(level->move_bonus, true, "+ ", "-", true));
140  }
141  return true;
142  }
143 }
144 
149 static void format_change_terrain_string(char *buf, int bufsize,
150  enum gen_action act, int time,
151  terrain *from, const terrain *to)
152 {
153  universal u = {.value = {.terrain = from}, .kind = VUT_TERRAIN};
154  if (from == to || to == T_NONE
155  || !action_id_univs_not_blocking(act, nullptr, &u)) {
156  // Terrain cannot be changed
157  fc_snprintf(buf, bufsize, " -");
158  } else {
159  fc_snprintf(buf, bufsize, "%3d %s", time, terrain_name_translation(to));
160  }
161 }
162 
167 static bool insert_generated_text(char *outbuf, size_t outlen,
168  const char *name)
169 {
170  if (!game.client.ruleset_init) {
171  return false;
172  }
173 
174  if (0 == strcmp(name, "TerrainAlterations")) {
175  int clean_pollution_time = -1, clean_fallout_time = -1,
176  pillage_time = -1;
177  bool terrain_independent_extras = false;
178 
179  CATLSTR(outbuf, outlen,
180  /* TRANS: Header for fixed-width terrain alteration table.
181  * TRANS: Translators cannot change column widths :( */
182  _("Terrain Cultivate Plant "
183  "Transform\n"));
184  CATLSTR(
185  outbuf, outlen,
186  "-------------------------------------------------------------------"
187  "-\n");
188  terrain_type_iterate(pterrain)
189  {
190  if (0 != qstrlen(terrain_rule_name(pterrain))) {
191  char cultivate[MAX_LEN_NAME + 4], plant[MAX_LEN_NAME + 4],
192  transform[MAX_LEN_NAME + 4];
194  cultivate, sizeof(cultivate), ACTION_CULTIVATE,
195  pterrain->cultivate_time, pterrain, pterrain->irrigation_result);
196  format_change_terrain_string(plant, sizeof(plant), ACTION_PLANT,
197  pterrain->plant_time, pterrain,
198  pterrain->mining_result);
200  transform, sizeof(transform), ACTION_TRANSFORM_TERRAIN,
201  pterrain->transform_time, pterrain, pterrain->transform_result);
202 
203  auto terrain = terrain_name_translation(pterrain);
204 
205  /* Use get_internal_string_length() for correct alignment with
206  * multibyte character encodings */
207  cat_snprintf(
208  outbuf, outlen, "%s%*s %s%*s %s%*s %s\n", terrain,
209  MAX(0, 16 - (int) get_internal_string_length(terrain)), "",
210  cultivate,
211  MAX(0, 16 - (int) get_internal_string_length(cultivate)), "",
212  plant, MAX(0, 16 - (int) get_internal_string_length(plant)), "",
213  transform);
214 
215  if (clean_pollution_time != 0
216  && pterrain->clean_pollution_time != 0) {
217  if (clean_pollution_time < 0) {
218  clean_pollution_time = pterrain->clean_pollution_time;
219  } else {
220  if (clean_pollution_time != pterrain->clean_pollution_time) {
221  clean_pollution_time = 0; // give up
222  }
223  }
224  }
225  if (clean_fallout_time != 0 && pterrain->clean_fallout_time != 0) {
226  if (clean_fallout_time < 0) {
227  clean_fallout_time = pterrain->clean_fallout_time;
228  } else {
229  if (clean_fallout_time != pterrain->clean_fallout_time) {
230  clean_fallout_time = 0; // give up
231  }
232  }
233  }
234  if (pillage_time != 0 && pterrain->pillage_time != 0) {
235  if (pillage_time < 0) {
236  pillage_time = pterrain->pillage_time;
237  } else {
238  if (pillage_time != pterrain->pillage_time) {
239  pillage_time = 0; // give up
240  }
241  }
242  }
243  }
244  }
246 
247  /* Examine extras to see if time of removal activities really is
248  * terrain-independent, and take into account removal_time_factor.
249  * XXX: this is rather overwrought to handle cases which the ruleset
250  * author could express much more simply for the same result */
251  {
252  int time = -1, factor = -1;
253 
254  extra_type_by_rmcause_iterate(ERM_CLEANPOLLUTION, pextra)
255  {
256  if (pextra->removal_time == 0) {
257  if (factor < 0) {
258  factor = pextra->removal_time_factor;
259  } else if (factor != pextra->removal_time_factor) {
260  factor = 0; // give up
261  }
262  } else {
263  if (time < 0) {
264  time = pextra->removal_time;
265  } else if (time != pextra->removal_time) {
266  time = 0; // give up
267  }
268  }
269  }
271  if (factor < 0) {
272  // No extra has terrain-dependent clean time; use extra's time
273  if (time >= 0) {
274  clean_pollution_time = time;
275  } else {
276  clean_pollution_time = 0;
277  }
278  } else if (clean_pollution_time != 0) {
279  // At least one extra's time depends on terrain
280  fc_assert(clean_pollution_time > 0);
281  if (time > 0 && factor > 0
282  && time != clean_pollution_time * factor) {
283  clean_pollution_time = 0;
284  } else if (time >= 0) {
285  clean_pollution_time = time;
286  } else if (factor >= 0) {
287  clean_pollution_time = clean_pollution_time * factor;
288  } else {
289  fc_assert(false);
290  }
291  }
292  }
293 
294  {
295  int time = -1, factor = -1;
296 
297  extra_type_by_rmcause_iterate(ERM_CLEANFALLOUT, pextra)
298  {
299  if (pextra->removal_time == 0) {
300  if (factor < 0) {
301  factor = pextra->removal_time_factor;
302  } else if (factor != pextra->removal_time_factor) {
303  factor = 0; // give up
304  }
305  } else {
306  if (time < 0) {
307  time = pextra->removal_time;
308  } else if (time != pextra->removal_time) {
309  time = 0; // give up
310  }
311  }
312  }
314  if (factor < 0) {
315  // No extra has terrain-dependent clean time; use extra's time
316  if (time >= 0) {
317  clean_fallout_time = time;
318  } else {
319  clean_fallout_time = 0;
320  }
321  } else if (clean_fallout_time != 0) {
322  // At least one extra's time depends on terrain
323  fc_assert(clean_fallout_time > 0);
324  if (time > 0 && factor > 0 && time != clean_fallout_time * factor) {
325  clean_fallout_time = 0;
326  } else if (time >= 0) {
327  clean_fallout_time = time;
328  } else if (factor >= 0) {
329  clean_fallout_time = clean_fallout_time * factor;
330  } else {
331  fc_assert(false);
332  }
333  }
334  }
335 
336  {
337  int time = -1, factor = -1;
338 
339  extra_type_by_rmcause_iterate(ERM_PILLAGE, pextra)
340  {
341  if (pextra->removal_time == 0) {
342  if (factor < 0) {
343  factor = pextra->removal_time_factor;
344  } else if (factor != pextra->removal_time_factor) {
345  factor = 0; // give up
346  }
347  } else {
348  if (time < 0) {
349  time = pextra->removal_time;
350  } else if (time != pextra->removal_time) {
351  time = 0; // give up
352  }
353  }
354  }
356  if (factor < 0) {
357  // No extra has terrain-dependent pillage time; use extra's time
358  if (time >= 0) {
359  pillage_time = time;
360  } else {
361  pillage_time = 0;
362  }
363  } else if (pillage_time != 0) {
364  // At least one extra's time depends on terrain
365  fc_assert(pillage_time > 0);
366  if (time > 0 && factor > 0 && time != pillage_time * factor) {
367  pillage_time = 0;
368  } else if (time >= 0) {
369  pillage_time = time;
370  } else if (factor >= 0) {
371  pillage_time = pillage_time * factor;
372  } else {
373  fc_assert(false);
374  }
375  }
376  }
377 
378  /* Check whether there are any bases or roads whose build time is
379  * independent of terrain */
380 
381  extra_type_by_cause_iterate(EC_BASE, pextra)
382  {
383  if (pextra->buildable && pextra->build_time > 0) {
384  terrain_independent_extras = true;
385  break;
386  }
387  }
389  if (!terrain_independent_extras) {
390  extra_type_by_cause_iterate(EC_ROAD, pextra)
391  {
392  if (pextra->buildable && pextra->build_time > 0) {
393  terrain_independent_extras = true;
394  break;
395  }
396  }
398  }
399 
400  if (clean_pollution_time > 0 || clean_fallout_time > 0
401  || pillage_time > 0 || terrain_independent_extras) {
402  CATLSTR(outbuf, outlen, "\n");
403  CATLSTR(outbuf, outlen,
404  _("Time taken for the following activities is independent of "
405  "terrain:\n"));
406  CATLSTR(outbuf, outlen, "\n");
407  CATLSTR(outbuf, outlen,
408  /* TRANS: Header for fixed-width terrain alteration table.
409  * TRANS: Translators cannot change column widths :( */
410  _("Activity Time\n"));
411  CATLSTR(outbuf, outlen, "---------------------------");
412  if (clean_pollution_time > 0) {
413  cat_snprintf(outbuf, outlen, _("\nClean pollution %3d"),
414  clean_pollution_time);
415  }
416  if (clean_fallout_time > 0) {
417  cat_snprintf(outbuf, outlen, _("\nClean fallout %3d"),
418  clean_fallout_time);
419  }
420  if (pillage_time > 0) {
421  cat_snprintf(outbuf, outlen, _("\nPillage %3d"),
422  pillage_time);
423  }
424  extra_type_by_cause_iterate(EC_ROAD, pextra)
425  {
426  if (pextra->buildable && pextra->build_time > 0) {
427  const char *rname = extra_name_translation(pextra);
428 
429  cat_snprintf(outbuf, outlen, "\n%s%*s %3d", rname,
430  MAX(0, 18 - (int) get_internal_string_length(rname)),
431  "", pextra->build_time);
432  }
433  }
435  extra_type_by_cause_iterate(EC_BASE, pextra)
436  {
437  if (pextra->buildable && pextra->build_time > 0) {
438  const char *bname = extra_name_translation(pextra);
439 
440  cat_snprintf(outbuf, outlen, "\n%s%*s %3d", bname,
441  MAX(0, 18 - (int) get_internal_string_length(bname)),
442  "", pextra->build_time);
443  }
444  }
446  }
447  return true;
448  } else if (0 == strcmp(name, "VeteranLevels")) {
449  return insert_veteran_help(
450  outbuf, outlen, game.veteran,
451  _("In this ruleset, the following veteran levels are defined:"),
452  _("This ruleset has no default veteran levels defined."));
453  } else if (0 == strcmp(name, "Freeciv21Version")) {
454  const char *ver = freeciv_name_version();
455 
456  cat_snprintf(
457  outbuf, outlen,
458  /* TRANS: First %s is version string, e.g.,
459  * "Freeciv version 2.3.0-beta1 (beta version)" (translated). */
460  _("This is %s."), ver);
461 
462  // There's also an separate entry about Qt.
463  cat_snprintf(outbuf, outlen, _("\nBuilt against Qt %s, using %s"),
464  QT_VERSION_STR, qVersion());
465 
466  QString data_dirs_info = "\n\n";
467  data_dirs_info += _("This instance of Freeciv21 searches the following "
468  "directories for data files:");
469  data_dirs_info += "\n\n";
470  for (const auto &path : qAsConst(get_data_dirs())) {
471  QFileInfo info(path + "/");
472  data_dirs_info += "* " + info.absolutePath() + " ";
473  if (!info.exists()) {
474  // TRANS: Folder does not exist
475  data_dirs_info += _("(does not exist)");
476  } else if (info.isWritable()) {
477  // TRANS: Folder can be modified by the current user
478  data_dirs_info += _("(user)");
479  } else {
480  // TRANS: Folder cannot be modified by the current user
481  data_dirs_info += _("(system)");
482  }
483  data_dirs_info += "\n";
484  }
485  data_dirs_info += "\n";
486  data_dirs_info += _("Note that for multiplayer games, the server may "
487  "use different paths.");
488 
489  cat_snprintf(outbuf, outlen, "%s", qUtf8Printable(data_dirs_info));
490 
491  return true;
492  } else if (0 == strcmp(name, "DefaultMetaserver")) {
493  cat_snprintf(outbuf, outlen, " %s", FREECIV_META_URL);
494 
495  return true;
496  }
497  qCritical("Unknown directive '$%s' in help", name);
498  return false;
499 }
500 
508 static void insert_allows_single(struct universal *psource,
509  const requirement_vector *psubjreqs,
510  const char *subjstr,
511  const char *const *strs, char *buf,
512  size_t bufsz, const char *prefix)
513 {
514  QVector<QString> coreqs;
515  QVector<QString> conoreqs;
516  char *buf2 = new char[bufsz];
517 
518  // TODO: show other data like range and survives.
519 
520  requirement_vector_iterate(psubjreqs, req)
521  {
522  if (!req->quiet && are_universals_equal(psource, &req->source)) {
523  // We're definitely going to print _something_.
524  CATLSTR(buf, bufsz, prefix);
525  if (req->present) {
526  /* psource enables the subject, but other sources may
527  * also be required (or required to be absent). */
528  requirement_vector_iterate(psubjreqs, coreq)
529  {
530  if (!coreq->quiet
531  && !are_universals_equal(psource, &coreq->source)) {
532  universal_name_translation(&coreq->source, buf2, bufsz);
533  if (coreq->present) {
534  coreqs.append(buf2);
535  } else {
536  conoreqs.append(buf2);
537  }
538  }
539  }
541 
542  if (0 < coreqs.count()) {
543  if (0 < conoreqs.count()) {
544  cat_snprintf(buf, bufsz,
545  Q_(strs[0]), // "Allows %s (with %s but no %s)."
546  subjstr, qUtf8Printable(strvec_to_and_list(coreqs)),
547  qUtf8Printable(strvec_to_or_list(conoreqs)));
548  } else {
549  cat_snprintf(buf, bufsz, Q_(strs[1]), // "Allows %s (with %s)."
550  subjstr,
551  qUtf8Printable(strvec_to_and_list(coreqs)));
552  }
553  } else {
554  if (0 < conoreqs.count()) {
555  cat_snprintf(buf, bufsz, Q_(strs[2]), // "Allows %s (absent %s)."
556  subjstr,
557  qUtf8Printable(strvec_to_and_list(conoreqs)));
558  } else {
559  cat_snprintf(buf, bufsz, Q_(strs[3]), // "Allows %s."
560  subjstr);
561  }
562  }
563  } else {
564  // psource can, on its own, prevent the subject.
565  cat_snprintf(buf, bufsz, Q_(strs[4]), // "Prevents %s."
566  subjstr);
567  }
568  cat_snprintf(buf, bufsz, "\n");
569  }
570  }
572 
573  delete[] buf2;
574 }
575 
592 static void insert_allows(struct universal *psource, char *buf, size_t bufsz,
593  const char *prefix)
594 {
595  buf[0] = '\0';
596 
597  for (const auto &pgov : governments) {
598  static const char *const govstrs[] = {
599  // TRANS: First %s is a government name.
600  N_("?gov:Allows %s (with %s but no %s)."),
601  // TRANS: First %s is a government name.
602  N_("?gov:Allows %s (with %s)."),
603  // TRANS: First %s is a government name.
604  N_("?gov:Allows %s (absent %s)."),
605  // TRANS: %s is a government name.
606  N_("?gov:Allows %s."),
607  // TRANS: %s is a government name.
608  N_("?gov:Prevents %s.")};
609  insert_allows_single(psource, &pgov.reqs,
610  government_name_translation(&pgov), govstrs, buf,
611  bufsz, prefix);
612  } // governments iterate - gov
613 
614  improvement_iterate(pimprove)
615  {
616  static const char *const imprstrs[] = {
617  // TRANS: First %s is a building name.
618  N_("?improvement:Allows %s (with %s but no %s)."),
619  // TRANS: First %s is a building name.
620  N_("?improvement:Allows %s (with %s)."),
621  // TRANS: First %s is a building name.
622  N_("?improvement:Allows %s (absent %s)."),
623  // TRANS: %s is a building name.
624  N_("?improvement:Allows %s."),
625  // TRANS: %s is a building name.
626  N_("?improvement:Prevents %s.")};
627  insert_allows_single(psource, &pimprove->reqs,
628  improvement_name_translation(pimprove), imprstrs,
629  buf, bufsz, prefix);
630  }
632 
633  unit_type_iterate(putype)
634  {
635  static const char *const utstrs[] = {
636  // TRANS: First %s is a unit type name.
637  N_("?unittype:Allows %s (with %s but no %s)."),
638  // TRANS: First %s is a unit type name.
639  N_("?unittype:Allows %s (with %s)."),
640  // TRANS: First %s is a unit type name.
641  N_("?unittype:Allows %s (absent %s)."),
642  // TRANS: %s is a unit type name.
643  N_("?unittype:Allows %s."),
644  // TRANS: %s is a unit type name.
645  N_("?unittype:Prevents %s.")};
646  insert_allows_single(psource, &putype->build_reqs,
647  utype_name_translation(putype), utstrs, buf, bufsz,
648  prefix);
649  }
651 }
652 
657 {
658  struct help_item *pitem = new help_item;
659  pitem->topic = nullptr;
660  pitem->text = nullptr;
661  pitem->type = type;
662  return pitem;
663 }
664 
669 static int help_item_compar(const struct help_item *v1,
670  const struct help_item *v2)
671 {
672  if (QString(v1->topic) != QString(v2->topic)) {
673  return QString(v1->topic) < QString(v2->topic);
674  } else {
675  return 0;
676  }
677 }
678 
682 void boot_help_texts(const nation_set *nations_to_show,
684 {
685  static bool booted = false;
686 
687  struct section_file *sf;
688  QString filename;
689  struct help_item *pitem;
690  struct section_list *sec;
691  const char **paras;
692  size_t npara;
693  char empty[1];
694  char long_buffer[64000]; // HACK: this may be overrun.
695 
696  empty[0] = '\0';
697 
698  // need to do something like this or bad things happen
699  free_help_texts();
700  help_nodes = new helpList;
701 
702  filename = fileinfoname(get_data_dirs(), "helpdata.txt");
703  if (filename.isEmpty()) {
704  qCritical("Did not read help texts");
705  return;
706  }
707  /* after following call filename may be clobbered; use sf->filename instead
708  */
709  if (!(sf = secfile_load(filename, false))) {
710  // this is now unlikely to happen
711  qCritical("failed reading help-texts from '%s':\n%s",
712  qUtf8Printable(filename), secfile_error());
713  return;
714  }
715 
716  sec = secfile_sections_by_name_prefix(sf, "help_");
717 
718  if (nullptr != sec) {
719  section_list_iterate(sec, psection)
720  {
721  char help_text_buffer[MAX_LEN_PACKET];
722  const char *sec_name = section_name(psection);
723  const char *gen_str = secfile_lookup_str(sf, "%s.generate", sec_name);
724 
725  if (gen_str) {
726  enum help_page_type current_type = HELP_ANY;
727  int level = strspn(gen_str, " ");
728 
729  gen_str += level;
730 
731  for (int i = 2; help_type_names[i]; i++) {
732  if (strcmp(gen_str, help_type_names[i]) == 0) {
733  current_type = static_cast<help_page_type>(i);
734  break;
735  }
736  }
737  if (current_type == HELP_ANY) {
738  qCritical("bad help-generate category \"%s\"", gen_str);
739  continue;
740  }
741 
742  if (!booted) {
743  if (current_type == HELP_EXTRA) {
744  size_t ncats;
745 
746  /* Avoid warnings about entries unused on this round,
747  * when the entries in question are valid once help system has
748  * been booted */
749  const char **delete_me_pls = secfile_lookup_str_vec(
750  sf, &ncats, "%s.categories", sec_name);
751  delete[] delete_me_pls;
752  }
753  continue; // on initial boot data tables are empty
754  }
755 
756  {
757  /* Note these should really fill in pitem->text from auto-gen
758  data instead of doing it later on the fly, but I don't want
759  to change that now. --dwp
760  */
761  char name[2048];
762  helpList category_nodes;
763 
764  switch (current_type) {
765  case HELP_UNIT:
766  unit_type_iterate(punittype)
767  {
768  pitem = new_help_item(current_type);
769  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
770  utype_name_translation(punittype));
771  pitem->topic = qstrdup(name);
772  pitem->text = qstrdup(empty);
773  category_nodes.append(pitem);
774  }
776  break;
777  case HELP_TECH:
779  {
780  if (valid_advance_by_number(advi)) {
781  pitem = new_help_item(current_type);
782  fc_snprintf(
783  name, sizeof(name), "%*s%s", level, "",
785  pitem->topic = qstrdup(name);
786  pitem->text = qstrdup(empty);
787  category_nodes.append(pitem);
788  }
789  }
791  break;
792  case HELP_TERRAIN:
793  terrain_type_iterate(pterrain)
794  {
795  if (0 != qstrlen(terrain_rule_name(pterrain))) {
796  pitem = new_help_item(current_type);
797  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
798  terrain_name_translation(pterrain));
799  pitem->topic = qstrdup(name);
800  pitem->text = qstrdup(empty);
801  category_nodes.append(pitem);
802  }
803  }
805  break;
806  case HELP_EXTRA: {
807  const char **cats;
808  size_t ncats;
809  cats = secfile_lookup_str_vec(sf, &ncats, "%s.categories",
810  sec_name);
811  extra_type_iterate(pextra)
812  {
813  // If categories not specified, don't filter
814  if (cats) {
815  bool include = false;
816  const char *cat = extra_category_name(pextra->category);
817  int ci;
818 
819  for (ci = 0; ci < ncats; ci++) {
820  if (fc_strcasecmp(cats[ci], cat) == 0) {
821  include = true;
822  break;
823  }
824  }
825  if (!include) {
826  continue;
827  }
828  }
829  pitem = new_help_item(current_type);
830  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
831  extra_name_translation(pextra));
832  pitem->topic = qstrdup(name);
833  pitem->text = qstrdup(empty);
834  category_nodes.append(pitem);
835  }
837  delete[] cats;
838  cats = nullptr;
839  } break;
840  case HELP_GOODS:
841  goods_type_iterate(pgood)
842  {
843  pitem = new_help_item(current_type);
844  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
845  goods_name_translation(pgood));
846  pitem->topic = qstrdup(name);
847  pitem->text = qstrdup(empty);
848  category_nodes.append(pitem);
849  }
851  break;
852  case HELP_SPECIALIST:
854  {
855  struct specialist *pspec = specialist_by_number(sp);
856 
857  pitem = new_help_item(current_type);
858  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
860  pitem->topic = qstrdup(name);
861  pitem->text = qstrdup(empty);
862  category_nodes.append(pitem);
863  }
865  break;
866  case HELP_GOVERNMENT:
867  for (const auto &gov : governments) {
868  pitem = new_help_item(current_type);
869  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
871  pitem->topic = qstrdup(name);
872  pitem->text = qstrdup(empty);
873  category_nodes.append(pitem);
874  };
875  break;
876  case HELP_IMPROVEMENT:
877  improvement_iterate(pimprove)
878  {
879  if (valid_improvement(pimprove)
880  && !is_great_wonder(pimprove)) {
881  pitem = new_help_item(current_type);
882  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
883  improvement_name_translation(pimprove));
884  pitem->topic = qstrdup(name);
885  pitem->text = qstrdup(empty);
886  category_nodes.append(pitem);
887  }
888  }
890  break;
891  case HELP_WONDER:
892  improvement_iterate(pimprove)
893  {
894  if (valid_improvement(pimprove) && is_great_wonder(pimprove)) {
895  pitem = new_help_item(current_type);
896  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
897  improvement_name_translation(pimprove));
898  pitem->topic = qstrdup(name);
899  pitem->text = qstrdup(empty);
900  category_nodes.append(pitem);
901  }
902  }
904  break;
905  case HELP_RULESET: {
906  int desc_len;
907  int len;
908 
909  pitem = new_help_item(HELP_RULESET);
910  // pitem->topic = qstrdup(_(game.control.name));
911  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
913  pitem->topic = qstrdup(name);
914  if (game.ruleset_description != nullptr) {
915  desc_len = qstrlen("\n\n") + qstrlen(game.ruleset_description);
916  } else {
917  desc_len = 0;
918  }
919  if (game.ruleset_summary != nullptr) {
920  if (game.control.version[0] != '\0') {
921  len = qstrlen(_(game.control.name)) + qstrlen(" ")
922  + qstrlen(game.control.version) + qstrlen("\n\n")
923  + qstrlen(_(game.ruleset_summary)) + 1;
924 
925  pitem->text = new char[len + desc_len];
926  fc_snprintf(pitem->text, len, "%s %s\n\n%s",
927  _(game.control.name), game.control.version,
929  } else {
930  len = qstrlen(_(game.control.name)) + qstrlen("\n\n")
931  + qstrlen(_(game.ruleset_summary)) + 1;
932 
933  pitem->text = new char[len + desc_len];
934  fc_snprintf(pitem->text, len, "%s\n\n%s",
935  _(game.control.name), _(game.ruleset_summary));
936  }
937  } else {
938  const char *nodesc = _("Current ruleset contains no summary.");
939 
940  if (game.control.version[0] != '\0') {
941  len = qstrlen(_(game.control.name)) + qstrlen(" ")
942  + qstrlen(game.control.version) + qstrlen("\n\n")
943  + qstrlen(nodesc) + 1;
944 
945  pitem->text = new char[len + desc_len];
946  fc_snprintf(pitem->text, len, "%s %s\n\n%s",
947  _(game.control.name), game.control.version,
948  nodesc);
949  } else {
950  len = qstrlen(_(game.control.name)) + qstrlen("\n\n")
951  + qstrlen(nodesc) + 1;
952 
953  pitem->text = new char[len + desc_len];
954  fc_snprintf(pitem->text, len, "%s\n\n%s",
955  _(game.control.name), nodesc);
956  }
957  }
958  if (game.ruleset_description != nullptr) {
959  fc_strlcat(pitem->text, "\n\n", len + desc_len);
961  len + desc_len);
962  }
963  help_nodes->append(pitem);
964  } break;
965  case HELP_TILESET: {
966  if (tileset_help) {
967  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
969  tileset_help->topic = qstrdup(name);
970  help_nodes->append(tileset_help);
971  }
972  } break;
973  case HELP_NATIONS:
974  for (const auto &pnation : nations) {
975  if (nations_to_show
976  && nation_is_in_set(&pnation, nations_to_show)) {
977  pitem = new_help_item(current_type);
978  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
979  nation_plural_translation(&pnation));
980  pitem->topic = qstrdup(name);
981  pitem->text = qstrdup(empty);
982  category_nodes.append(pitem);
983  }
984  } // iterate over nations - pnation
985  break;
986  case HELP_MULTIPLIER:
987  multipliers_iterate(pmul)
988  {
989  help_text_buffer[0] = '\0';
990  pitem = new_help_item(current_type);
991  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
992  name_translation_get(&pmul->name));
993  pitem->topic = qstrdup(name);
994  if (pmul->helptext) {
995  const char *sep = "";
996  for (const auto &text : qAsConst(*pmul->helptext)) {
997  cat_snprintf(help_text_buffer, sizeof(help_text_buffer),
998  "%s%s", sep, qUtf8Printable(text));
999  sep = "\n\n";
1000  }
1001  }
1002  pitem->text = qstrdup(help_text_buffer);
1003  help_nodes->append(pitem);
1004  }
1006  break;
1007  case HELP_EFFECT: {
1008  std::list<help_item *> effect_help;
1009 
1010  for (int i = 0; i < EFT_COUNT; ++i) {
1011  auto effects = get_effects(static_cast<effect_type>(i));
1012  if (effect_list_size(effects) > 0) {
1013  pitem = new_help_item(current_type);
1014  fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1015  effect_type_name(static_cast<effect_type>(i)));
1016  pitem->topic = qstrdup(name);
1017 
1018  QString all_text = _("The following rules contribute to the "
1019  "value of this effect:\n");
1020  effect_list_iterate(effects, peffect)
1021  {
1022  if (requirement_vector_size(&peffect->reqs) == 0) {
1023  all_text += QString(_("* %1 by default\n"))
1024  .arg(effect_type_unit_text(
1025  peffect->type, peffect->value));
1026  } else {
1027  help_text_buffer[0] = '\0';
1028  get_effect_req_text(peffect, help_text_buffer,
1029  sizeof(help_text_buffer));
1030  all_text += QString(_("* %1 with %2\n"))
1031  .arg(effect_type_unit_text(
1032  peffect->type, peffect->value))
1033  .arg(help_text_buffer);
1034  }
1035  }
1037 
1038  pitem->text = qstrdup(qUtf8Printable(all_text));
1039  effect_help.push_back(pitem);
1040  }
1041  }
1042 
1043  effect_help.sort(help_item_compar);
1044  for (auto pitem : effect_help) {
1045  help_nodes->append(pitem);
1046  }
1047  break;
1048  }
1049  default:
1050  qCritical("Bad current_type: %d.", current_type);
1051  break;
1052  }
1053  std::sort(category_nodes.begin(), category_nodes.end(),
1055  helpList::iterator it = category_nodes.begin();
1056  while (it != category_nodes.end()) {
1057  help_nodes->append(*it);
1058  it++;
1059  }
1060  continue;
1061  }
1062  }
1063 
1064  // It wasn't a "generate" node:
1065 
1066  pitem = new_help_item(HELP_TEXT);
1067  pitem->topic =
1068  qstrdup(Q_(secfile_lookup_str(sf, "%s.name", sec_name)));
1069 
1070  paras = secfile_lookup_str_vec(sf, &npara, "%s.text", sec_name);
1071 
1072  long_buffer[0] = '\0';
1073  for (int i = 0; i < npara; i++) {
1074  bool inserted;
1075  const char *para = paras[i];
1076 
1077  if (strncmp(para, "$", 1) == 0) {
1078  inserted = insert_generated_text(long_buffer, sizeof(long_buffer),
1079  para + 1);
1080  } else {
1081  sz_strlcat(long_buffer, _(para));
1082  inserted = true;
1083  }
1084  if (inserted && i != npara - 1) {
1085  sz_strlcat(long_buffer, "\n\n");
1086  }
1087  }
1088  delete[] paras;
1089  paras = nullptr;
1090  pitem->text = qstrdup(long_buffer);
1091  help_nodes->append(pitem);
1092  }
1094 
1095  section_list_destroy(sec);
1096  }
1097 
1099  secfile_destroy(sf);
1100  booted = true;
1101  qDebug("Booted help texts ok");
1102 }
1103 
1117 const struct help_item *
1118 get_help_item_spec(const char *name, enum help_page_type htype, int *pos)
1119 {
1120  int idx;
1121  const struct help_item *pitem = nullptr;
1122  static struct help_item vitem; // v = virtual
1123  static char vtopic[128];
1124  static char vtext[256];
1125 
1126  idx = 0;
1127 
1128  for (const auto *ptmp : qAsConst(*help_nodes)) {
1129  char *p = ptmp->topic;
1130 
1131  while (*p == ' ') {
1132  p++;
1133  }
1134  if (strcmp(name, p) == 0 && (htype == HELP_ANY || htype == ptmp->type)) {
1135  pitem = ptmp;
1136  break;
1137  }
1138  idx++;
1139  }
1140 
1141  if (!pitem) {
1142  idx = -1;
1143  vitem.topic = vtopic;
1144  sz_strlcpy(vtopic, name);
1145  vitem.text = vtext;
1146  if (htype == HELP_ANY || htype == HELP_TEXT) {
1147  fc_snprintf(vtext, sizeof(vtext), _("Sorry, no help topic for %s.\n"),
1148  vitem.topic);
1149  vitem.type = HELP_TEXT;
1150  } else {
1151  fc_snprintf(vtext, sizeof(vtext),
1152  _("Sorry, no help topic for %s.\n"
1153  "This page was auto-generated.\n\n"),
1154  vitem.topic);
1155  vitem.type = htype;
1156  }
1157  pitem = &vitem;
1158  }
1159  *pos = idx;
1160  return pitem;
1161 }
1162 
1181 char *helptext_building(char *buf, size_t bufsz, struct player *pplayer,
1182  const char *user_text,
1183  const struct impr_type *pimprove,
1184  const nation_set *nations_to_show)
1185 {
1186  bool reqs = false;
1187  struct universal source = {.value = {.building = pimprove},
1188  .kind = VUT_IMPROVEMENT};
1189 
1190  fc_assert_ret_val(nullptr != buf && 0 < bufsz, nullptr);
1191  buf[0] = '\0';
1192 
1193  if (nullptr == pimprove) {
1194  return buf;
1195  }
1196 
1197  if (nullptr != pimprove->helptext) {
1198  for (const auto &text : qAsConst(*pimprove->helptext)) {
1199  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
1200  }
1201  }
1202 
1203  // Add requirement text for improvement itself
1204  requirement_vector_iterate(&pimprove->reqs, preq)
1205  {
1206  if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
1207  reqs = true;
1208  }
1209  }
1211  if (reqs) {
1212  fc_strlcat(buf, "\n", bufsz);
1213  }
1214 
1215  requirement_vector_iterate(&pimprove->obsolete_by, pobs)
1216  {
1217  if (VUT_ADVANCE == pobs->source.kind && pobs->present) {
1218  cat_snprintf(buf, bufsz,
1219  _("* The discovery of %s will make %s obsolete.\n"),
1220  advance_name_translation(pobs->source.value.advance),
1221  improvement_name_translation(pimprove));
1222  }
1223  if (VUT_IMPROVEMENT == pobs->source.kind && pobs->present) {
1224  cat_snprintf(buf, bufsz,
1225  // TRANS: both %s are improvement names
1226  _("* The presence of %s in the city will make %s "
1227  "obsolete.\n"),
1228  improvement_name_translation(pobs->source.value.building),
1229  improvement_name_translation(pimprove));
1230  }
1231  }
1233 
1234  if (is_small_wonder(pimprove)) {
1235  cat_snprintf(buf, bufsz,
1236  _("* A 'small wonder': at most one of your cities may "
1237  "possess this improvement.\n"));
1238  }
1239  /* (Great wonders are in their own help section explaining their
1240  * uniqueness, so we don't mention it here.) */
1241 
1242  if (building_has_effect(pimprove, EFT_ENABLE_NUKE)) {
1243  action_id nuke_actions[MAX_NUM_ACTIONS];
1244  struct unit_type *u = nullptr;
1245 
1246  {
1247  // Find Manhattan dependent nuke actions
1248  int i = 0;
1249 
1250  action_list_add_all_by_result(nuke_actions, &i, ACTRES_NUKE);
1251  action_list_add_all_by_result(nuke_actions, &i, ACTRES_NUKE_CITY);
1252  action_list_add_all_by_result(nuke_actions, &i, ACTRES_NUKE_UNITS);
1253 
1254  action_list_end(nuke_actions, i);
1255  }
1256 
1257  action_list_iterate(nuke_actions, act_id)
1258  {
1259  if (num_role_units(action_id_get_role(act_id)) > 0) {
1260  u = get_role_unit(action_id_get_role(act_id), 0);
1261  break;
1262  }
1263  }
1265 
1266  if (u) {
1267  cat_snprintf(buf, bufsz,
1268  /* TRANS: 'Allows all players with knowledge of atomic
1269  * power to build nuclear units.' */
1270  _("* Allows all players with knowledge of %s "
1271  "to build %s units.\n"),
1274  }
1275  }
1276 
1277  insert_allows(&source, buf + qstrlen(buf), bufsz - qstrlen(buf),
1278  // TRANS: bullet point; note trailing space
1279  Q_("?bullet:* "));
1280 
1281  // Actions that requires the building to target a city.
1282  action_iterate(act)
1283  {
1284  // Nothing is found yet.
1285  bool demanded = false;
1286  enum req_range max_range = REQ_RANGE_LOCAL;
1287 
1288  if (action_id_get_target_kind(act) != ATK_CITY) {
1289  // Not relevant
1290  continue;
1291  }
1292 
1293  if (action_by_number(act)->quiet) {
1294  // The ruleset it self documents this action.
1295  continue;
1296  }
1297 
1299  {
1300  if (universal_fulfills_requirements(true, &(enabler->target_reqs),
1301  &source)) {
1302  // The building is needed by this action enabler.
1303  demanded = true;
1304 
1305  // See if this enabler gives the building a wider range.
1306  requirement_vector_iterate(&(enabler->target_reqs), preq)
1307  {
1308  if (!universal_is_relevant_to_requirement(preq, &source)) {
1309  // Not relevant.
1310  continue;
1311  }
1312 
1313  if (!preq->present) {
1314  /* A !present larger range requirement would make the present
1315  * lower range illegal. */
1316  continue;
1317  }
1318 
1319  if (preq->range > max_range) {
1320  // Found a larger range.
1321  max_range = preq->range;
1322  /* Intentionally not breaking here. The requirement vector may
1323  * contain other requirements with a larger range.
1324  * Example: Building a GreatWonder in a city with a Palace. */
1325  }
1326  }
1328  }
1329  }
1331 
1332  if (demanded) {
1333  switch (max_range) {
1334  case REQ_RANGE_LOCAL:
1335  /* At least one action enabler needed the building in its target
1336  * requirements. */
1337  cat_snprintf(buf, bufsz,
1338  // TRANS: Help build Wonder
1339  _("* Makes it possible to target the city building it "
1340  "with the action \'%s\'.\n"),
1341  qUtf8Printable(action_id_name_translation(act)));
1342  break;
1343  case REQ_RANGE_CITY:
1344  /* At least one action enabler needed the building in its target
1345  * requirements. */
1346  cat_snprintf(buf, bufsz,
1347  // TRANS: Help build Wonder
1348  _("* Makes it possible to target its city with the "
1349  "action \'%s\'.\n"),
1350  qUtf8Printable(action_id_name_translation(act)));
1351  break;
1352  case REQ_RANGE_TRADEROUTE:
1353  /* At least one action enabler needed the building in its target
1354  * requirements. */
1355  cat_snprintf(buf, bufsz,
1356  // TRANS: Help build Wonder
1357  _("* Makes it possible to target its city and its "
1358  "trade partners with the action \'%s\'.\n"),
1359  qUtf8Printable(action_id_name_translation(act)));
1360  break;
1361  case REQ_RANGE_CONTINENT:
1362  /* At least one action enabler needed the building in its target
1363  * requirements. */
1364  cat_snprintf(buf, bufsz,
1365  // TRANS: Help build Wonder
1366  _("* Makes it possible to target all cities with its "
1367  "owner on its continent with the action \'%s\'.\n"),
1368  qUtf8Printable(action_id_name_translation(act)));
1369  break;
1370  case REQ_RANGE_PLAYER:
1371  /* At least one action enabler needed the building in its target
1372  * requirements. */
1373  cat_snprintf(buf, bufsz,
1374  // TRANS: Help build Wonder
1375  _("* Makes it possible to target all cities with its "
1376  "owner with the action \'%s\'.\n"),
1377  qUtf8Printable(action_id_name_translation(act)));
1378  break;
1379  case REQ_RANGE_TEAM:
1380  /* At least one action enabler needed the building in its target
1381  * requirements. */
1382  cat_snprintf(buf, bufsz,
1383  // TRANS: Help build Wonder
1384  _("* Makes it possible to target all cities on the "
1385  "same team with the action \'%s\'.\n"),
1386  qUtf8Printable(action_id_name_translation(act)));
1387  break;
1388  case REQ_RANGE_ALLIANCE:
1389  /* At least one action enabler needed the building in its target
1390  * requirements. */
1391  cat_snprintf(buf, bufsz,
1392  // TRANS: Help build Wonder
1393  _("* Makes it possible to target all cities owned by "
1394  "or allied to its owner with the action \'%s\'.\n"),
1395  qUtf8Printable(action_id_name_translation(act)));
1396  break;
1397  case REQ_RANGE_WORLD:
1398  /* At least one action enabler needed the building in its target
1399  * requirements. */
1400  cat_snprintf(buf, bufsz,
1401  // TRANS: Help build Wonder
1402  _("* Makes it possible to target all cities with the "
1403  "action \'%s\'.\n"),
1404  qUtf8Printable(action_id_name_translation(act)));
1405  break;
1406  case REQ_RANGE_CADJACENT:
1407  case REQ_RANGE_ADJACENT:
1408  case REQ_RANGE_COUNT:
1409  qCritical("The range %s is invalid for buildings.",
1410  req_range_name(max_range));
1411  break;
1412  }
1413  }
1414  }
1416 
1417  // Building protects against action.
1418  action_iterate(act)
1419  {
1420  // Nothing is found yet.
1421  bool vulnerable = false;
1422  enum req_range min_range = REQ_RANGE_COUNT;
1423 
1424  if (action_id_get_target_kind(act) != ATK_CITY) {
1425  // Not relevant
1426  continue;
1427  }
1428 
1429  if (action_enabler_list_size(action_enablers_for_action(act)) == 0) {
1430  // This action isn't enabled at all.
1431  continue;
1432  }
1433 
1434  if (action_by_number(act)->quiet) {
1435  // The ruleset it self documents this action.
1436  continue;
1437  }
1438 
1439  // Must be immune in all cases.
1441  {
1443  &(enabler->target_reqs))) {
1444  vulnerable = true;
1445  break;
1446  } else {
1447  enum req_range vector_max_range = REQ_RANGE_LOCAL;
1448 
1449  requirement_vector_iterate(&(enabler->target_reqs), preq)
1450  {
1451  if (!universal_is_relevant_to_requirement(preq, &source)) {
1452  // Not relevant.
1453  continue;
1454  }
1455 
1456  if (preq->present) {
1457  // Not what is looked for.
1458  continue;
1459  }
1460 
1461  if (preq->range > vector_max_range) {
1462  // Found a larger range.
1463  vector_max_range = preq->range;
1464  }
1465  }
1467 
1468  if (vector_max_range < min_range) {
1469  // Found a smaller range.
1470  min_range = vector_max_range;
1471  }
1472  }
1473  }
1475 
1476  if (!vulnerable) {
1477  switch (min_range) {
1478  case REQ_RANGE_LOCAL:
1479  cat_snprintf(buf, bufsz,
1480  // TRANS: Incite City
1481  _("* Makes it impossible to do the action \'%s\' to "
1482  "the city building it.\n"),
1483  qUtf8Printable(action_id_name_translation(act)));
1484  break;
1485  case REQ_RANGE_CITY:
1486  cat_snprintf(buf, bufsz,
1487  // TRANS: Incite City
1488  _("* Makes it impossible to do the action \'%s\' to "
1489  "its city.\n"),
1490  qUtf8Printable(action_id_name_translation(act)));
1491  break;
1492  case REQ_RANGE_TRADEROUTE:
1493  cat_snprintf(buf, bufsz,
1494  // TRANS: Incite City
1495  _("* Makes it impossible to do the action \'%s\' to "
1496  "its city or to its city's trade partners.\n"),
1497  qUtf8Printable(action_id_name_translation(act)));
1498  break;
1499  case REQ_RANGE_CONTINENT:
1500  cat_snprintf(buf, bufsz,
1501  // TRANS: Incite City
1502  _("* Makes it impossible to do the action \'%s\' to "
1503  "any city with its owner on its continent.\n"),
1504  qUtf8Printable(action_id_name_translation(act)));
1505  break;
1506  case REQ_RANGE_PLAYER:
1507  cat_snprintf(buf, bufsz,
1508  // TRANS: Incite City
1509  _("* Makes it impossible to do the action \'%s\' to "
1510  "any city with its owner.\n"),
1511  qUtf8Printable(action_id_name_translation(act)));
1512  break;
1513  case REQ_RANGE_TEAM:
1514  cat_snprintf(buf, bufsz,
1515  // TRANS: Incite City
1516  _("* Makes it impossible to do the action \'%s\' to "
1517  "any city on the same team.\n"),
1518  qUtf8Printable(action_id_name_translation(act)));
1519  break;
1520  case REQ_RANGE_ALLIANCE:
1521  cat_snprintf(buf, bufsz,
1522  // TRANS: Incite City
1523  _("* Makes it impossible to do the action \'%s\' to "
1524  "any city allied to or owned by its owner.\n"),
1525  qUtf8Printable(action_id_name_translation(act)));
1526  break;
1527  case REQ_RANGE_WORLD:
1528  cat_snprintf(buf, bufsz,
1529  // TRANS: Incite City
1530  _("* Makes it impossible to do the action \'%s\' to "
1531  "any city in the game.\n"),
1532  qUtf8Printable(action_id_name_translation(act)));
1533  break;
1534  case REQ_RANGE_CADJACENT:
1535  case REQ_RANGE_ADJACENT:
1536  case REQ_RANGE_COUNT:
1537  qCritical("The range %s is invalid for buildings.",
1538  req_range_name(min_range));
1539  break;
1540  }
1541  }
1542  }
1544 
1545  {
1546  int i;
1547 
1548  for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
1550  if (n == B_LAST) {
1551  break;
1552  } else if (improvement_by_number(n) == pimprove) {
1553  cat_snprintf(buf, bufsz,
1554  _("* All players start with this improvement in their "
1555  "first city.\n"));
1556  break;
1557  }
1558  }
1559  }
1560 
1561  /* Assume no-one will set the same building in both global and nation
1562  * init_buildings... */
1563  for (const auto &pnation : nations) {
1564  // Avoid mentioning nations not in current set.
1565  if (nations_to_show && !nation_is_in_set(&pnation, nations_to_show)) {
1566  continue;
1567  }
1568  for (int n : pnation.init_buildings) {
1569  if (n == B_LAST) {
1570  break;
1571  } else if (improvement_by_number(n) == pimprove) {
1572  cat_snprintf(buf, bufsz,
1573  // TRANS: %s is a nation plural
1574  _("* The %s start with this improvement in their "
1575  "first city.\n"),
1576  nation_plural_translation(&pnation));
1577  break;
1578  }
1579  }
1580  } // iterate over nations - pnation
1581 
1582  if (improvement_has_flag(pimprove, IF_SAVE_SMALL_WONDER)) {
1583  cat_snprintf(buf, bufsz,
1584  // TRANS: don't translate 'savepalace'
1585  _("* If you lose the city containing this improvement, "
1586  "it will be rebuilt for free in another of your cities "
1587  "(if the 'savepalace' server setting is enabled).\n"));
1588  }
1589 
1590  if (user_text && user_text[0] != '\0') {
1591  cat_snprintf(buf, bufsz, "\n\n%s", user_text);
1592  }
1593  return buf;
1594 }
1595 
1602 static bool utype_may_do_escape_action(const struct unit_type *utype)
1603 {
1604  action_iterate(act_id)
1605  {
1606  struct action *paction = action_by_number(act_id);
1607 
1608  if (action_get_actor_kind(paction) != AAK_UNIT) {
1609  // Not relevant.
1610  continue;
1611  }
1612 
1613  if (!utype_can_do_action(utype, paction->id)) {
1614  // Can't do it.
1615  continue;
1616  }
1617 
1618  if (utype_is_consumed_by_action(paction, utype)) {
1619  // No escape when dead.
1620  continue;
1621  }
1622 
1623  if (paction->actor.is_unit.moves_actor == MAK_ESCAPE) {
1624  // Survives and escapes.
1625  return true;
1626  }
1627  }
1629 
1630  return false;
1631 }
1632 
1639 char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
1640  const char *user_text, const struct unit_type *utype,
1641  const nation_set *nations_to_show)
1642 {
1643  bool has_vet_levels;
1644  int flagid;
1645  struct unit_class *pclass;
1646  int fuel;
1647 
1648  fc_assert_ret_val(nullptr != buf && 0 < bufsz && nullptr != user_text,
1649  nullptr);
1650 
1651  if (!utype) {
1652  qCritical("Unknown unit!");
1653  fc_strlcpy(buf, user_text, bufsz);
1654  return buf;
1655  }
1656 
1657  has_vet_levels = utype_veteran_levels(utype) > 1;
1658 
1659  buf[0] = '\0';
1660 
1661  pclass = utype_class(utype);
1662  cat_snprintf(buf, bufsz, _("* Belongs to %s unit class."),
1663  uclass_name_translation(pclass));
1664  if (nullptr != pclass->helptext) {
1665  for (const auto &text : qAsConst(*pclass->helptext)) {
1666  cat_snprintf(buf, bufsz, "\n%s\n", _(qUtf8Printable(text)));
1667  }
1668  } else {
1669  CATLSTR(buf, bufsz, "\n");
1670  }
1671  if (uclass_has_flag(pclass, UCF_CAN_OCCUPY_CITY)
1672  && !utype_has_flag(utype, UTYF_CIVILIAN)) {
1673  // TRANS: indented unit class property, preserve leading spaces
1674  CATLSTR(buf, bufsz, _(" * Can occupy empty enemy cities.\n"));
1675  }
1676  if (!uclass_has_flag(pclass, UCF_TERRAIN_SPEED)) {
1677  // TRANS: indented unit class property, preserve leading spaces
1678  CATLSTR(buf, bufsz, _(" * Speed is not affected by terrain.\n"));
1679  }
1680  if (!uclass_has_flag(pclass, UCF_TERRAIN_DEFENSE)) {
1681  // TRANS: indented unit class property, preserve leading spaces
1682  CATLSTR(buf, bufsz,
1683  _(" * Does not get defense bonuses from terrain.\n"));
1684  }
1685  if (!uclass_has_flag(pclass, UCF_ZOC)) {
1686  // TRANS: indented unit class property, preserve leading spaces
1687  CATLSTR(buf, bufsz, _(" * Not subject to zones of control.\n"));
1688  } else if (!utype_has_flag(utype, UTYF_IGZOC)) {
1689  // TRANS: indented unit class property, preserve leading spaces
1690  CATLSTR(buf, bufsz, _(" * Subject to zones of control.\n"));
1691  }
1692  if (uclass_has_flag(pclass, UCF_DAMAGE_SLOWS)) {
1693  // TRANS: indented unit class property, preserve leading spaces
1694  CATLSTR(buf, bufsz, _(" * Slowed down while damaged.\n"));
1695  }
1696  if (utype->defense_strength > 0) {
1697  struct universal unit_is_in_city[] = {
1698  {.value = {.utype = utype}, .kind = VUT_UTYPE},
1699  {.value = {.citytile = CITYT_CENTER}, .kind = VUT_CITYTILE},
1700  };
1701  int bonus = effect_value_from_universals(EFT_FORTIFY_DEFENSE_BONUS,
1702  unit_is_in_city,
1703  ARRAY_SIZE(unit_is_in_city));
1704 
1705  if (bonus > 0) {
1706  cat_snprintf(buf, bufsz,
1707  /* TRANS: indented unit class property, preserve leading
1708  * spaces */
1709  _(" * Gets a %d%% defensive bonus while in cities.\n"),
1710  bonus);
1711  }
1712  }
1713  if (uclass_has_flag(pclass, UCF_UNREACHABLE)) {
1714  CATLSTR(buf, bufsz,
1715  // TRANS: indented unit class property, preserve leading spaces
1716  _(" * Is unreachable. Most units cannot attack this one.\n"));
1717  if (utype_has_flag(utype, UTYF_NEVER_PROTECTS)) {
1718  CATLSTR(buf, bufsz,
1719  // TRANS: indented twice; preserve leading spaces
1720  _(" * Doesn't prevent enemy units from attacking other "
1721  "units on its tile.\n"));
1722  }
1723  }
1724  if (uclass_has_flag(pclass, UCF_DOESNT_OCCUPY_TILE)
1725  && !utype_has_flag(utype, UTYF_CIVILIAN)) {
1726  CATLSTR(buf, bufsz,
1727  // TRANS: indented unit class property, preserve leading spaces
1728  _(" * Doesn't prevent enemy cities from working the tile it's "
1729  "on.\n"));
1730  }
1731  if (can_attack_non_native(utype)) {
1732  CATLSTR(buf, bufsz,
1733  // TRANS: indented unit class property, preserve leading spaces
1734  _(" * Can attack units on non-native tiles.\n"));
1735  }
1736  for (flagid = UCF_USER_FLAG_1; flagid <= UCF_LAST_USER_FLAG; flagid++) {
1737  if (uclass_has_flag(pclass, static_cast<unit_class_flag_id>(flagid))) {
1738  const char *helptxt =
1739  unit_class_flag_helptxt(static_cast<unit_class_flag_id>(flagid));
1740 
1741  if (helptxt != nullptr) {
1742  // TRANS: indented unit class property, preserve leading spaces
1743  CATLSTR(buf, bufsz, Q_("?bullet: * "));
1744  CATLSTR(buf, bufsz, _(helptxt));
1745  CATLSTR(buf, bufsz, "\n");
1746  }
1747  }
1748  }
1749 
1750  /* The unit's combat bonuses. Won't mention that another unit type has a
1751  * combat bonus against this unit type. Doesn't handle complex cases like
1752  * when a unit type has multiple combat bonuses of the same kind. */
1753  combat_bonus_list_iterate(utype->bonuses, cbonus)
1754  {
1755  if (cbonus->quiet) {
1756  // Handled in the help text of the ruleset.
1757  continue;
1758  }
1759  QVector<QString> against;
1760  against.reserve(utype_count());
1761 
1762  // Find the unit types of the bonus targets.
1763  unit_type_iterate(utype2)
1764  {
1765  if (utype_has_flag(utype2, cbonus->flag)) {
1766  against.append(utype_name_translation(utype2));
1767  }
1768  }
1770 
1771  if (!against.isEmpty()) {
1772  switch (cbonus->type) {
1773  case CBONUS_DEFENSE_MULTIPLIER:
1774  cat_snprintf(buf, bufsz,
1775  // TRANS: percentage ... or-list of unit types
1776  _("* %d%% defense bonus if attacked by %s.\n"),
1777  cbonus->value * 100,
1778  qUtf8Printable(strvec_to_or_list(against)));
1779  break;
1780  case CBONUS_DEFENSE_DIVIDER:
1781  cat_snprintf(buf, bufsz,
1782  // TRANS: defense divider ... or-list of unit types
1783  _("* Reduces target's defense to 1 / %d when "
1784  "attacking %s.\n"),
1785  cbonus->value + 1,
1786  qUtf8Printable(strvec_to_or_list(against)));
1787  break;
1788  case CBONUS_FIREPOWER1:
1789  cat_snprintf(buf, bufsz,
1790  // TRANS: or-list of unit types
1791  _("* Reduces target's fire power to 1 when "
1792  "attacking %s.\n"),
1793  qUtf8Printable(strvec_to_and_list(against)));
1794  break;
1795  case CBONUS_DEFENSE_MULTIPLIER_PCT:
1796  cat_snprintf(buf, bufsz,
1797  // TRANS: percentage ... or-list of unit types
1798  _("* %d%% defense bonus if attacked by %s.\n"),
1799  cbonus->value,
1800  qUtf8Printable(strvec_to_or_list(against)));
1801  break;
1802  case CBONUS_DEFENSE_DIVIDER_PCT:
1803  cat_snprintf(buf, bufsz,
1804  // TRANS: defense divider ... or-list of unit types
1805  _("* Reduces target's defense to 1 / %.2f when "
1806  "attacking %s.\n"),
1807  (static_cast<float>(cbonus->value) + 100.0f) / 100.0f,
1808  qUtf8Printable(strvec_to_or_list(against)));
1809  break;
1810  }
1811  }
1812  }
1814 
1815  // Add requirement text for the unit type itself
1816  requirement_vector_iterate(&utype->build_reqs, preq)
1817  {
1818  (void) req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT,
1819  Q_("?bullet:* "));
1820  }
1822 
1823  if (utype_has_flag(utype, UTYF_CANESCAPE)) {
1824  CATLSTR(buf, bufsz, _("* Can escape once stack defender is lost.\n"));
1825  }
1826  if (utype_has_flag(utype, UTYF_CANKILLESCAPING)) {
1827  CATLSTR(buf, bufsz, _("* Can pursue escaping units and kill them.\n"));
1828  }
1829 
1830  if (utype_has_flag(utype, UTYF_NOBUILD)) {
1831  CATLSTR(buf, bufsz, _("* May not be built in cities.\n"));
1832  }
1833  if (utype_has_flag(utype, UTYF_BARBARIAN_ONLY)) {
1834  CATLSTR(buf, bufsz, _("* Only barbarians may build this.\n"));
1835  }
1836  if (utype_has_flag(utype, UTYF_NEWCITY_GAMES_ONLY)) {
1837  CATLSTR(buf, bufsz,
1838  _("* Can only be built in games where new cities "
1839  "are allowed.\n"));
1840  if (game.scenario.prevent_new_cities) {
1841  // TRANS: indented; preserve leading spaces
1842  CATLSTR(buf, bufsz,
1843  _(" - New cities are not allowed in the current "
1844  "game.\n"));
1845  } else {
1846  // TRANS: indented; preserve leading spaces
1847  CATLSTR(buf, bufsz,
1848  _(" - New cities are allowed in the current "
1849  "game.\n"));
1850  }
1851  }
1852  for (const auto &pnation : nations) {
1853  int i, count = 0;
1854 
1855  // Avoid mentioning nations not in current set.
1856  if (nations_to_show && !nation_is_in_set(&pnation, nations_to_show)) {
1857  continue;
1858  }
1859  for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
1860  if (!pnation.init_units[i]) {
1861  break;
1862  } else if (pnation.init_units[i] == utype) {
1863  count++;
1864  }
1865  }
1866  if (count > 0) {
1867  cat_snprintf(buf, bufsz,
1868  // TRANS: %s is a nation plural
1869  PL_("* The %s start the game with %d of these units.\n",
1870  "* The %s start the game with %d of these units.\n",
1871  count),
1872  nation_plural_translation(&pnation), count);
1873  }
1874  } // iterate over nations - pnation
1875  {
1876  QVector<QString> types;
1877  types.reserve(utype_count());
1878 
1879  unit_type_iterate(utype2)
1880  {
1881  if (utype2->converted_to == utype
1882  && utype_can_do_action_result(utype2, ACTRES_CONVERT)) {
1883  types.append(utype_name_translation(utype2));
1884  }
1885  }
1887  if (!types.isEmpty()) {
1888  cat_snprintf(buf, bufsz,
1889  // TRANS: %s is a list of unit types separated by "or".
1890  _("* May be obtained by conversion of %s.\n"),
1891  qUtf8Printable(strvec_to_or_list(types)));
1892  }
1893  }
1894  if (utype_has_flag(utype, UTYF_NOHOME)) {
1895  CATLSTR(buf, bufsz, _("* Never has a home city.\n"));
1896  }
1897  if (utype_has_flag(utype, UTYF_GAMELOSS)) {
1898  CATLSTR(buf, bufsz, _("* Losing this unit will lose you the game!\n"));
1899  }
1900  if (utype_has_flag(utype, UTYF_UNIQUE)) {
1901  CATLSTR(buf, bufsz,
1902  _("* Each player may only have one of this type of unit.\n"));
1903  }
1904  for (flagid = UTYF_USER_FLAG_1; flagid <= UTYF_LAST_USER_FLAG; flagid++) {
1905  if (utype_has_flag(utype, flagid)) {
1906  const char *helptxt =
1907  unit_type_flag_helptxt(static_cast<unit_type_flag_id>(flagid));
1908 
1909  if (helptxt != nullptr) {
1910  // TRANS: bullet point; note trailing space
1911  CATLSTR(buf, bufsz, Q_("?bullet:* "));
1912  CATLSTR(buf, bufsz, _(helptxt));
1913  CATLSTR(buf, bufsz, "\n");
1914  }
1915  }
1916  }
1917  if (utype->pop_cost > 0) {
1918  cat_snprintf(buf, bufsz,
1919  PL_("* Costs %d population to build.\n",
1920  "* Costs %d population to build.\n", utype->pop_cost),
1921  utype->pop_cost);
1922  }
1923  if (0 < utype->transport_capacity) {
1924  QVector<QString> classes;
1925  classes.reserve(uclass_count());
1926 
1927  unit_class_iterate(uclass)
1928  {
1929  if (can_unit_type_transport(utype, uclass)) {
1930  classes.append(uclass_name_translation(uclass));
1931  }
1932  }
1934 
1935  cat_snprintf(buf, bufsz,
1936  // TRANS: %s is a list of unit classes separated by "or".
1937  PL_("* Can carry and refuel %d %s unit.\n",
1938  "* Can carry and refuel up to %d %s units.\n",
1939  utype->transport_capacity),
1940  utype->transport_capacity,
1941  qUtf8Printable(strvec_to_or_list(classes)));
1942  if (uclass_has_flag(utype_class(utype), UCF_UNREACHABLE)) {
1943  /* Document restrictions on when units can load/unload */
1944  bool has_restricted_load = false, has_unrestricted_load = false,
1945  has_restricted_unload = false, has_unrestricted_unload = false;
1946  unit_type_iterate(pcargo)
1947  {
1948  if (can_unit_type_transport(utype, utype_class(pcargo))) {
1949  if (utype_can_freely_load(pcargo, utype)) {
1950  has_unrestricted_load = true;
1951  } else {
1952  has_restricted_load = true;
1953  }
1954  if (utype_can_freely_unload(pcargo, utype)) {
1955  has_unrestricted_unload = true;
1956  } else {
1957  has_restricted_unload = true;
1958  }
1959  }
1960  }
1962  if (has_restricted_load) {
1963  if (has_unrestricted_load) {
1964  /* At least one type of cargo can load onto us freely.
1965  * The specific exceptions will be documented in cargo help. */
1966  CATLSTR(buf, bufsz,
1967  // TRANS: indented; preserve leading spaces
1968  _(" * Some cargo cannot be loaded except in a city or a "
1969  "base native to this transport.\n"));
1970  } else {
1971  // No exceptions
1972  CATLSTR(buf, bufsz,
1973  // TRANS: indented; preserve leading spaces
1974  _(" * Cargo cannot be loaded except in a city or a "
1975  "base native to this transport.\n"));
1976  }
1977  } // else, no restricted cargo exists; keep quiet
1978  if (has_restricted_unload) {
1979  if (has_unrestricted_unload) {
1980  // At least one type of cargo can unload from us freely.
1981  CATLSTR(
1982  buf, bufsz,
1983  // TRANS: indented; preserve leading spaces
1984  _(" * Some cargo cannot be unloaded except in a city or a "
1985  "base native to this transport.\n"));
1986  } else {
1987  // No exceptions
1988  CATLSTR(buf, bufsz,
1989  // TRANS: indented; preserve leading spaces
1990  _(" * Cargo cannot be unloaded except in a city or a "
1991  "base native to this transport.\n"));
1992  }
1993  } // else, no restricted cargo exists; keep quiet
1994  }
1995  }
1996  if (utype_has_flag(utype, UTYF_COAST_STRICT)) {
1997  CATLSTR(buf, bufsz, _("* Must stay next to safe coast.\n"));
1998  }
1999  {
2000  /* Document exceptions to embark/disembark restrictions that we
2001  * have as cargo. */
2002  QBitArray embarks, disembarks;
2003  embarks.resize(UCL_LAST);
2004  disembarks.resize(UCL_LAST);
2005  /* Determine which of our transport classes have restrictions in the
2006  * first place (that is, contain at least one transport which carries at
2007  * least one type of cargo which is restricted). We'll suppress output
2008  * for classes not in this set, since this cargo type is not behaving
2009  * exceptionally in such cases. */
2010  unit_type_iterate(utrans)
2011  {
2012  const Unit_Class_id trans_class = uclass_index(utype_class(utrans));
2013  /* Don't waste time repeating checks on classes we've already checked,
2014  * or weren't under consideration in the first place */
2015  if (!embarks.at(trans_class)
2016  && BV_ISSET(utype->embarks, trans_class)) {
2017  unit_type_iterate(other_cargo)
2018  {
2019  if (can_unit_type_transport(utrans, utype_class(other_cargo))
2020  && !utype_can_freely_load(other_cargo, utrans)) {
2021  /* At least one load restriction in transport class, which
2022  * we aren't subject to */
2023  embarks.setBit(trans_class);
2024  }
2025  }
2026  unit_type_iterate_end; // cargo
2027  }
2028  if (!disembarks.at(trans_class)
2029  && BV_ISSET(utype->disembarks, trans_class)) {
2030  unit_type_iterate(other_cargo)
2031  {
2032  if (can_unit_type_transport(utrans, utype_class(other_cargo))
2033  && !utype_can_freely_unload(other_cargo, utrans)) {
2034  /* At least one load restriction in transport class, which
2035  * we aren't subject to */
2036  disembarks.setBit(trans_class);
2037  }
2038  }
2039  unit_type_iterate_end; // cargo
2040  }
2041  }
2042  unit_class_iterate_end; // transports
2043 
2044  if (is_any_set(embarks)) {
2045  // Build list of embark exceptions
2046  QVector<QString> eclasses;
2047  eclasses.reserve(uclass_count());
2048 
2049  unit_class_iterate(uclass)
2050  {
2051  if (embarks.at(uclass_index(uclass))) {
2052  eclasses.append(uclass_name_translation(uclass));
2053  }
2054  }
2056  QString elist = strvec_to_or_list(eclasses);
2057  if (embarks == disembarks) {
2058  // A common case: the list of disembark exceptions is identical
2059  cat_snprintf(buf, bufsz,
2060  /* TRANS: %s is a list of unit classes separated
2061  * by "or". */
2062  _("* May load onto and unload from %s transports even "
2063  "when underway.\n"),
2064  qUtf8Printable(elist));
2065  } else {
2066  cat_snprintf(
2067  buf, bufsz,
2068  /* TRANS: %s is a list of unit classes separated
2069  * by "or". */
2070  _("* May load onto %s transports even when underway.\n"),
2071  qUtf8Printable(elist));
2072  }
2073  }
2074  if (is_any_set(disembarks) && embarks != disembarks) {
2075  // Build list of disembark exceptions (if different from embarking)
2076  QVector<QString> dclasses;
2077  dclasses.reserve(uclass_count());
2078 
2079  unit_class_iterate(uclass)
2080  {
2081  if (disembarks.at(uclass_index(uclass))) {
2082  dclasses.append(uclass_name_translation(uclass));
2083  }
2084  }
2086  QString dlist = strvec_to_or_list(dclasses);
2087  cat_snprintf(
2088  buf, bufsz,
2089  /* TRANS: %s is a list of unit classes separated
2090  * by "or". */
2091  _("* May unload from %s transports even when underway.\n"),
2092  qUtf8Printable(dlist));
2093  }
2094  }
2095 
2096  if (utype_has_flag(utype, UTYF_SPY)) {
2097  CATLSTR(buf, bufsz, _("* Strong in diplomatic battles.\n"));
2098  }
2099  if (utype_has_flag(utype, UTYF_DIPLOMAT)
2100  || utype_has_flag(utype, UTYF_SUPERSPY)) {
2101  CATLSTR(buf, bufsz, _("* Defends cities against diplomatic actions.\n"));
2102  }
2103  if (utype_has_flag(utype, UTYF_SUPERSPY)) {
2104  CATLSTR(buf, bufsz,
2105  _("* Will never lose a diplomat-versus-diplomat fight.\n"));
2106  }
2107  if (utype_may_do_escape_action(utype)
2108  && utype_has_flag(utype, UTYF_SUPERSPY)) {
2109  CATLSTR(buf, bufsz, _("* Will always survive a spy mission.\n"));
2110  }
2111  if (utype->vlayer == V_INVIS) {
2112  CATLSTR(
2113  buf, bufsz,
2114  _("* Is invisible except when next to an enemy unit or city.\n"));
2115  }
2116  if (utype_has_flag(utype, UTYF_ONLY_NATIVE_ATTACK)) {
2117  CATLSTR(buf, bufsz, _("* Can only attack units on native tiles.\n"));
2118  }
2119  if (utype_has_flag(utype, UTYF_CITYBUSTER)) {
2120  CATLSTR(buf, bufsz,
2121  _("* Gets double firepower when attacking cities.\n"));
2122  }
2123  if (utype_has_flag(utype, UTYF_IGTER)) {
2124  cat_snprintf(buf, bufsz,
2125  /* TRANS: "MP" = movement points. %s may have a
2126  * fractional part. */
2127  _("* Ignores terrain effects (moving costs at most %s MP "
2128  "per tile).\n"),
2129  move_points_text(terrain_control.igter_cost, true));
2130  }
2131  if (utype_has_flag(utype, UTYF_NOZOC)) {
2132  CATLSTR(buf, bufsz, _("* Never imposes a zone of control.\n"));
2133  } else {
2134  CATLSTR(buf, bufsz,
2135  _("* May impose a zone of control on its adjacent "
2136  "tiles.\n"));
2137  }
2138  if (utype_has_flag(utype, UTYF_IGZOC)) {
2139  CATLSTR(buf, bufsz,
2140  _("* Not subject to zones of control imposed "
2141  "by other units.\n"));
2142  }
2143  if (utype_has_flag(utype, UTYF_CIVILIAN)) {
2144  CATLSTR(buf, bufsz, _("* A non-military unit:\n"));
2145  CATLSTR(buf, bufsz,
2146  // TRANS: indented; preserve leading spaces
2147  _(" * Cannot attack.\n"));
2148  CATLSTR(buf, bufsz,
2149  // TRANS: indented; preserve leading spaces
2150  _(" * Doesn't impose martial law.\n"));
2151  CATLSTR(
2152  buf, bufsz,
2153  // TRANS: indented; preserve leading spaces
2154  _(" * Can enter foreign territory regardless of peace treaty.\n"));
2155  CATLSTR(buf, bufsz,
2156  // TRANS: indented; preserve leading spaces
2157  _(" * Doesn't prevent enemy cities from working the tile it's "
2158  "on.\n"));
2159  }
2160  if (utype_has_flag(utype, UTYF_FIELDUNIT)) {
2161  CATLSTR(buf, bufsz,
2162  _("* A field unit: one unhappiness applies even when "
2163  "non-aggressive.\n"));
2164  }
2165  if (utype_has_flag(utype, UTYF_PROVOKING)
2167  server_setting_by_name("autoattack"))) {
2168  CATLSTR(buf, bufsz,
2169  _("* An enemy unit considering to auto attack this unit will "
2170  "choose to do so even if it has better odds when defending "
2171  "against it than when attacking it.\n"));
2172  }
2173  if (utype_has_flag(utype, UTYF_SHIELD2GOLD)) {
2174  /* FIXME: the conversion shield => gold is activated if
2175  * EFT_SHIELD2GOLD_FACTOR is not equal null; how to determine
2176  * possible sources? */
2177  CATLSTR(
2178  buf, bufsz,
2179  _("* Under certain conditions the shield upkeep of this unit can "
2180  "be converted to gold upkeep.\n"));
2181  }
2182 
2183  unit_class_iterate(target)
2184  {
2185  if (uclass_has_flag(target, UCF_UNREACHABLE)
2186  && BV_ISSET(utype->targets, uclass_index(target))) {
2187  cat_snprintf(buf, bufsz,
2188  _("* Can attack against %s units, which are usually not "
2189  "reachable.\n"),
2190  uclass_name_translation(target));
2191  }
2192  }
2194 
2195  fuel = utype_fuel(utype);
2196  if (fuel > 0) {
2197  QVector<QString> types;
2198  types.reserve(utype_count() + 1);
2199 
2200  unit_type_iterate(transport)
2201  {
2202  if (can_unit_type_transport(transport, utype_class(utype))) {
2203  types.append(utype_name_translation(transport));
2204  }
2205  }
2207 
2208  if (types.isEmpty()) {
2209  if (utype_has_flag(utype, UTYF_COAST)) {
2210  if (fuel == 1) {
2211  cat_snprintf(buf, bufsz,
2212  _("* Unit has to end each turn next to safe coast or"
2213  " in a city or a base.\n"));
2214  } else {
2215  cat_snprintf(
2216  buf, bufsz,
2217  /* Pluralization for the benefit of languages with
2218  * duals etc */
2219  // TRANS: Never called for 'turns = 1' case
2220  PL_("* Unit has to be next to safe coast, in a city or a base"
2221  " after %d turn.\n",
2222  "* Unit has to be next to safe coast, in a city or a base"
2223  " after %d turns.\n",
2224  fuel),
2225  fuel);
2226  }
2227  } else {
2228  cat_snprintf(buf, bufsz,
2229  PL_("* Unit has to be in a city or a base"
2230  " after %d turn.\n",
2231  "* Unit has to be in a city or a base"
2232  " after %d turns.\n",
2233  fuel),
2234  fuel);
2235  }
2236  } else {
2237  if (utype_has_flag(utype, UTYF_COAST)) {
2238  cat_snprintf(
2239  buf, bufsz,
2240  // TRANS: %s is a list of unit types separated by "or"
2241  PL_("* Unit has to be next to safe coast, in a city, a base, or "
2242  "on a %s"
2243  " after %d turn.\n",
2244  "* Unit has to be next to safe coast, in a city, a base, or "
2245  "on a %s"
2246  " after %d turns.\n",
2247  fuel),
2248  qUtf8Printable(strvec_to_or_list(types)), fuel);
2249  } else {
2250  cat_snprintf(buf, bufsz,
2251  // TRANS: %s is a list of unit types separated by "or"
2252  PL_("* Unit has to be in a city, a base, or on a %s"
2253  " after %d turn.\n",
2254  "* Unit has to be in a city, a base, or on a %s"
2255  " after %d turns.\n",
2256  fuel),
2257  qUtf8Printable(strvec_to_or_list(types)), fuel);
2258  }
2259  }
2260  }
2261 
2262  // Auto attack immunity. (auto_attack.if_attacker ruleset setting)
2264  bool not_an_auto_attacker = true;
2265 
2266  action_auto_perf_iterate(auto_action)
2267  {
2268  if (auto_action->cause != AAPC_UNIT_MOVED_ADJ) {
2269  // Not relevant for auto attack.
2270  continue;
2271  }
2272 
2273  if (requirement_fulfilled_by_unit_type(utype, &auto_action->reqs)) {
2274  // Can be forced to auto attack.
2275  not_an_auto_attacker = false;
2276  break;
2277  }
2278  }
2280 
2281  if (not_an_auto_attacker) {
2282  CATLSTR(buf, bufsz,
2283  _("* Will never be forced (by the autoattack server setting)"
2284  " to attack units moving to an adjacent tile.\n"));
2285  }
2286  }
2287 
2288  action_iterate(act)
2289  {
2290  struct action *paction = action_by_number(act);
2291 
2292  if (action_by_number(act)->quiet) {
2293  // The ruleset documents this action it self.
2294  continue;
2295  }
2296 
2297  if (utype_can_do_action(utype, act)) {
2298  const char *target_adjective;
2299  char sub_target_text[100];
2300  QVector<QString> blockers;
2301  blockers.reserve(MAX_NUM_ACTIONS);
2302  int i = 0;
2303 
2304  // Generic action information.
2305  cat_snprintf(buf, bufsz,
2306  // TRANS: %s is the action's ruleset defined ui name
2307  _("* Can do the action \'%s\'.\n"),
2308  qUtf8Printable(action_id_name_translation(act)));
2309 
2310  switch (action_id_get_target_kind(act)) {
2311  case ATK_SELF:
2312  // No target.
2313  break;
2314  default:
2315  if (!can_utype_do_act_if_tgt_diplrel(utype, act, DRO_FOREIGN,
2316  true)) {
2317  // TRANS: describes the target of an action.
2318  target_adjective = _("domestic ");
2319  } else if (!can_utype_do_act_if_tgt_diplrel(utype, act, DRO_FOREIGN,
2320  false)) {
2321  // TRANS: describes the target of an action.
2322  target_adjective = _("foreign ");
2323  } else {
2324  // Both foreign and domestic targets are acceptable.
2325  target_adjective = "";
2326  }
2327 
2328  sub_target_text[0] = '\0';
2329  if (action_get_sub_target_kind(paction) != ASTK_NONE) {
2330  cat_snprintf(sub_target_text, sizeof(sub_target_text), _("%s "),
2331  _(action_sub_target_kind_name(
2332  action_get_sub_target_kind(paction))));
2333  }
2334 
2335  cat_snprintf(
2336  buf, bufsz,
2337  /* TRANS: The first %s is the sub target kind. The next
2338  * may be an adjective (that includes a space). The
2339  * next is the name of the target kind.
2340  * Example: is done to extras on foreign tiles */
2341  _(" * is done to %s%s%s.\n"), sub_target_text, target_adjective,
2342  _(action_target_kind_name(action_id_get_target_kind(act))));
2343  }
2344 
2345  if (utype_is_consumed_by_action(paction, utype)) {
2346  cat_snprintf(buf, bufsz,
2347  /* TRANS: said about an action. %s is a unit type
2348  * name. */
2349  _(" * uses up the %s.\n"),
2350  utype_name_translation(utype));
2351  }
2352 
2353  if (action_get_battle_kind(paction) != ABK_NONE) {
2354  cat_snprintf(buf, bufsz,
2355  /* TRANS: The %s is a kind of battle defined in
2356  * actions.h. Example: "diplomatic battle". */
2357  _(" * can lead to a %s against a defender.\n"),
2358  action_battle_kind_translated_name(
2359  action_get_battle_kind(paction)));
2360  }
2361 
2362  {
2363  int odds = action_dice_roll_initial_odds(paction);
2364 
2365  if (odds != ACTION_ODDS_PCT_DICE_ROLL_NA) {
2366  /* TODO: try to detect that the odds always will be 100% because
2367  * of the Action_Odds_Pct effect. */
2368  cat_snprintf(buf, bufsz,
2369  _(" * may fail because of a dice throw.\n"));
2370  }
2371  }
2372 
2373  if (!utype_is_consumed_by_action(paction, utype)
2374  && paction->actor.is_unit.moves_actor == MAK_ESCAPE) {
2375  cat_snprintf(buf, bufsz,
2376  /* TRANS: said about an action. %s is a unit type
2377  * name. */
2378  _(" * the %s may be captured while trying to"
2379  " escape after completing the mission.\n"),
2380  utype_name_translation(utype));
2381  }
2382  {
2383  struct universal req_pattern[] = {
2384  {.value = {.action = paction}, .kind = VUT_ACTION},
2385  {.value = {.utype = utype}, .kind = VUT_UTYPE},
2386  };
2387  int success_move_frag_cost = effect_value_from_universals(
2388  EFT_ACTION_SUCCESS_MOVE_COST, req_pattern,
2389  ARRAY_SIZE(req_pattern));
2390 
2391  success_move_frag_cost +=
2392  utype_pays_mp_for_action_base(paction, utype);
2393 
2394  /* Can't print the exact amount of move fragments. It isn't known.
2395  * The action performer function may subtract some movement itself
2396  * on top of what EFT_ACTION_SUCCESS_MOVE_COST takes. */
2397  if (MAX_MOVE_FRAGS <= success_move_frag_cost) {
2398  // A value this size takes all the actor unit's move fragments.
2399  cat_snprintf(buf, bufsz, _(" * ends this unit's turn.\n"));
2400  }
2401  }
2402 
2403  if (action_id_get_target_kind(act) != ATK_SELF) {
2404  // Distance to target is relevant.
2405 
2406  /* FIXME: move paratroopers_range to the action and remove this
2407  * variable once actions are generalized. */
2408  int relative_max =
2409  action_has_result(paction, ACTRES_PARADROP)
2410  ? MIN(paction->max_distance, utype->paratroopers_range)
2411  : paction->max_distance;
2412 
2413  if (paction->min_distance == relative_max) {
2414  // Only one distance to target is acceptable
2415 
2416  if (paction->min_distance == 0) {
2417  cat_snprintf(buf, bufsz,
2418  /* TRANS: distance between an actor unit and its
2419  * target when performing a specific action. */
2420  _(" * target must be at the same tile.\n"));
2421  } else {
2422  cat_snprintf(buf, bufsz,
2423  /* TRANS: distance between an actor unit and its
2424  * target when performing a specific action. */
2425  PL_(" * target must be exactly %d tile away.\n",
2426  " * target must be exactly %d tiles away.\n",
2427  paction->min_distance),
2428  paction->min_distance);
2429  }
2430  } else if (relative_max == ACTION_DISTANCE_UNLIMITED) {
2431  // No max distance
2432 
2433  if (paction->min_distance == 0) {
2434  cat_snprintf(buf, bufsz,
2435  /* TRANS: distance between an actor unit and its
2436  * target when performing a specific action. */
2437  _(" * target can be anywhere.\n"));
2438  } else {
2439  cat_snprintf(buf, bufsz,
2440  /* TRANS: distance between an actor unit and its
2441  * target when performing a specific action. */
2442  PL_(" * target must be at least %d tile away.\n",
2443  " * target must be at least %d tiles away.\n",
2444  paction->min_distance),
2445  paction->min_distance);
2446  }
2447  } else if (paction->min_distance == 0) {
2448  // No min distance
2449 
2450  cat_snprintf(buf, bufsz,
2451  /* TRANS: distance between an actor unit and its
2452  * target when performing a specific action. */
2453  PL_(" * target can be max %d tile away.\n",
2454  " * target can be max %d tiles away.\n",
2455  relative_max),
2456  relative_max);
2457  } else {
2458  // Full range.
2459 
2460  cat_snprintf(
2461  buf, bufsz,
2462  /* TRANS: distance between an actor unit and its
2463  * target when performing a specific action. */
2464  PL_(" * target must be between %d and %d tile away.\n",
2465  " * target must be between %d and %d tiles away.\n",
2466  relative_max),
2467  paction->min_distance, relative_max);
2468  }
2469  }
2470 
2471  // The action may be a Casus Belli.
2472  {
2473  const struct {
2474  const enum effect_type eft;
2475  const char *hlp_text;
2476  } casus_belli[] = {
2477  // TRANS: ...performing this action ... Casus Belli
2478  {EFT_CASUS_BELLI_SUCCESS, N_("successfully")},
2479  // TRANS: ...performing this action ... Casus Belli
2480  {EFT_CASUS_BELLI_CAUGHT, N_("getting caught before")},
2481  };
2482 
2483  struct universal req_pattern[] = {
2484  {.value = {.action = paction}, .kind = VUT_ACTION},
2485  {.kind = VUT_DIPLREL, /* value filled in later */},
2486  };
2487 
2488  /* First group by effect (currently getting caught and successfully
2489  * performing the action) */
2490  for (i = 0; i < ARRAY_SIZE(casus_belli); i++) {
2491  int diplrel;
2492 
2493  // DiplRel list of each Casus Belli size.
2494  QVector<QString> victim_diplrel_names;
2495  QVector<QString> outrage_diplrel_names;
2496  outrage_diplrel_names.reserve(DRO_LAST);
2497  victim_diplrel_names.reserve(DRO_LAST);
2498 
2499  // Ignore Team and everything in diplrel_other.
2500  for (diplrel = 0; diplrel < DS_NO_CONTACT; diplrel++) {
2501  int casus_belli_amount;
2502 
2503  if (!can_utype_do_act_if_tgt_diplrel(utype, act, diplrel,
2504  true)) {
2505  // Can't do the action. Can't give Casus Belli.
2506  continue;
2507  }
2508 
2509  req_pattern[1].value.diplrel = diplrel;
2510  casus_belli_amount = effect_value_from_universals(
2511  casus_belli[i].eft, req_pattern, ARRAY_SIZE(req_pattern));
2512 
2513  if (CASUS_BELLI_OUTRAGE <= casus_belli_amount) {
2514  outrage_diplrel_names.append(
2515  diplrel_name_translation(diplrel));
2516  } else if (CASUS_BELLI_VICTIM <= casus_belli_amount) {
2517  victim_diplrel_names.append(diplrel_name_translation(diplrel));
2518  }
2519  }
2520 
2521  /* Then group by Casus Belli size (currently victim and
2522  * international outrage) */
2523  if (!outrage_diplrel_names.isEmpty()) {
2524  cat_snprintf(
2525  buf, bufsz,
2526  // TRANS: successfully ... Peace, or Alliance
2527  _(" * %s performing this action during %s causes"
2528  " international outrage: the whole world gets "
2529  "Casus Belli against you.\n"),
2530  _(casus_belli[i].hlp_text),
2531  qUtf8Printable(strvec_to_or_list(outrage_diplrel_names)));
2532  }
2533  if (!victim_diplrel_names.isEmpty()) {
2534  cat_snprintf(
2535  buf, bufsz,
2536  // TRANS: successfully ... Peace, or Alliance
2537  _(" * %s performing this action during %s gives"
2538  " the victim Casus Belli against you.\n"),
2539  _(casus_belli[i].hlp_text),
2540  qUtf8Printable(strvec_to_or_list(victim_diplrel_names)));
2541  }
2542  }
2543  }
2544 
2545  // Custom action result specific information.
2546  switch (paction->result) {
2547  case ACTRES_HELP_WONDER:
2548  cat_snprintf(buf, bufsz,
2549  /* TRANS: the %d is the number of shields the unit can
2550  * contribute. */
2551  _(" * adds %d production.\n"),
2553  break;
2554  case ACTRES_HEAL_UNIT:
2555  cat_snprintf(buf, bufsz,
2556  _(" * restores up to 25%% of the target unit's"
2557  " hit points.\n"));
2558  break;
2559  case ACTRES_FOUND_CITY:
2560  if (game.scenario.prevent_new_cities) {
2561  cat_snprintf(buf, bufsz,
2562  // TRANS: is talking about an action.
2563  _(" * is disabled in the current game.\n"));
2564  }
2565  cat_snprintf(buf, bufsz,
2566  // TRANS: the %d is initial population.
2567  PL_(" * initial population: %d.\n",
2568  " * initial population: %d.\n", utype->city_size),
2569  utype->city_size);
2570  break;
2571  case ACTRES_JOIN_CITY:
2572  cat_snprintf(
2573  buf, bufsz,
2574  // TRANS: the %d is population.
2575  PL_(" * max target size: %d.\n", " * max target size: %d.\n",
2576  game.info.add_to_size_limit - utype_pop_value(utype)),
2577  game.info.add_to_size_limit - utype_pop_value(utype));
2578  cat_snprintf(buf, bufsz,
2579  // TRANS: the %d is the population added.
2580  PL_(" * adds %d population.\n",
2581  " * adds %d population.\n",
2582  utype_pop_value(utype)),
2583  utype_pop_value(utype));
2584  break;
2585  case ACTRES_BOMBARD:
2586  cat_snprintf(buf, bufsz,
2587  // TRANS: %d is bombard rate.
2588  PL_(" * %d round per action.\n",
2589  " * %d rounds per action.\n", utype->bombard_rate),
2590  utype->bombard_rate);
2591  cat_snprintf(buf, bufsz,
2592  // TRANS: talking about bombard
2593  _(" * These attacks will only damage (never kill)"
2594  " defenders, but damage all"
2595  " defenders on a tile, and have no risk for the"
2596  " attacker.\n"));
2597  break;
2598  case ACTRES_UPGRADE_UNIT:
2599  cat_snprintf(buf, bufsz,
2600  // TRANS: %s is a unit type.
2601  _(" * upgraded to %s or, when possible, to the unit "
2602  "type it upgrades to.\n"),
2604  break;
2605  case ACTRES_ATTACK:
2606  if (game.info.tired_attack) {
2607  cat_snprintf(buf, bufsz,
2608  _(" * weaker when tired. If performed with less "
2609  "than a single move point left the attack power "
2610  "is reduced accordingly.\n"));
2611  }
2612  break;
2613  case ACTRES_CONVERT:
2614  cat_snprintf(buf, bufsz,
2615  // TRANS: %s is a unit type. "MP" = movement points.
2616  PL_(" * is converted into %s (takes %d MP).\n",
2617  " * is converted into %s (takes %d MP).\n",
2618  utype->convert_time),
2620  utype->convert_time);
2621  break;
2622  case ACTRES_SPY_NUKE:
2623  case ACTRES_NUKE:
2624  case ACTRES_NUKE_CITY:
2625  case ACTRES_NUKE_UNITS:
2626  if (game.info.nuke_pop_loss_pct > 0) {
2627  cat_snprintf(buf, bufsz,
2628  // TRANS: percentage
2629  _(" * %d%% of the population of each city inside"
2630  " the nuclear blast dies.\n"),
2631  game.info.nuke_pop_loss_pct);
2632  }
2633  if (game.info.nuke_defender_survival_chance_pct > 0) {
2634  cat_snprintf(buf, bufsz,
2635  _(" * all units caught in the open by the nuclear"
2636  " blast dies.\n"));
2637  cat_snprintf(buf, bufsz,
2638  // TRANS: percentage
2639  _(" * a unit caught in the nuclear blast while"
2640  " inside a city has a %d%% chance of survival.\n"),
2641  game.info.nuke_defender_survival_chance_pct);
2642  } else {
2643  cat_snprintf(buf, bufsz,
2644  _(" * all units caught in the nuclear blast"
2645  " dies.\n"));
2646  }
2647 
2648  break;
2649  case ACTRES_PLANT:
2650  case ACTRES_CULTIVATE:
2651  case ACTRES_TRANSFORM_TERRAIN:
2652  cat_snprintf(buf, bufsz,
2653  _(" * converts target tile terrain to another"
2654  " type.\n"));
2655  break;
2656  case ACTRES_ROAD:
2657  case ACTRES_MINE:
2658  case ACTRES_IRRIGATE:
2659  case ACTRES_BASE: {
2660  QVector<QString> extras_vec;
2661 
2662  extra_type_iterate(pextra)
2663  {
2664  if (action_creates_extra(paction, pextra)) {
2665  extras_vec.append(extra_name_translation(pextra));
2666  }
2667  }
2669  if (extras_vec.count() > 0) {
2670  ;
2671  // TRANS: %s is list of extra types separated by ',' and 'and'
2672  cat_snprintf(buf, bufsz, _(" * builds %s on tiles.\n"),
2673  qUtf8Printable(strvec_to_and_list(extras_vec)));
2674  }
2675  } break;
2676  case ACTRES_CLEAN_POLLUTION:
2677  case ACTRES_CLEAN_FALLOUT: {
2678  QVector<QString> extras_vec;
2679 
2680  extra_type_iterate(pextra)
2681  {
2682  if (action_removes_extra(paction, pextra)) {
2683  extras_vec.append(extra_name_translation(pextra));
2684  }
2685  }
2687 
2688  if (extras_vec.count() > 0) {
2689  // TRANS: list of extras separated by "and"
2690  cat_snprintf(buf, bufsz, _(" * cleans %s from tiles.\n"),
2691  qUtf8Printable(strvec_to_and_list(extras_vec)));
2692  }
2693  } break;
2694  case ACTRES_PILLAGE: {
2695  QVector<QString> extras_vec;
2696 
2697  extra_type_iterate(pextra)
2698  {
2699  if (action_removes_extra(paction, pextra)) {
2700  extras_vec.append(extra_name_translation(pextra));
2701  }
2702  }
2704 
2705  if (extras_vec.count() > 0) {
2706  // TRANS: list of extras separated by "and"
2707  cat_snprintf(buf, bufsz, _(" * pillages %s from tiles.\n"),
2708  qUtf8Printable(strvec_to_and_list(extras_vec)));
2709  }
2710  } break;
2711  case ACTRES_FORTIFY: {
2712  struct universal uni_cazfi = {.value = {.utype = utype},
2713  .kind = VUT_UTYPE};
2714  struct universal unit_is_fortified[] = {
2715  {.value = {.activity = ACTIVITY_FORTIFIED},
2716  .kind = VUT_ACTIVITY},
2717  {.value = {.utype = utype}, .kind = VUT_UTYPE},
2718  };
2719  int bonus = effect_value_from_universals(
2720  EFT_FORTIFY_DEFENSE_BONUS, unit_is_fortified,
2721  ARRAY_SIZE(unit_is_fortified));
2722 
2723  if (utype->defense_strength <= 0
2724  || (effect_cumulative_max(EFT_FORTIFY_DEFENSE_BONUS, &uni_cazfi)
2725  <= 0)) {
2726  cat_snprintf(buf, bufsz,
2727  /* TRANS: indented unit action property, preserve
2728  * leading spaces */
2729  _(" * to stay put. No defensive bonus.\n"));
2730  } else if (bonus > 0) {
2731  cat_snprintf(buf, bufsz,
2732  /* TRANS: indented unit action property, preserve
2733  * leading spaces */
2734  _(" * granting a %d%% defensive bonus.\n"), bonus);
2735  }
2736  } break;
2737  default:
2738  // No action specific details.
2739  break;
2740  }
2741 
2742  i = 0;
2743  action_iterate(blocker)
2744  {
2745  if (!utype_can_do_action(utype, blocker)) {
2746  // Can't block since never legal.
2747  continue;
2748  }
2749 
2750  if (action_id_would_be_blocked_by(act, blocker)) {
2751  /* action name alone can be MAX_LEN_NAME, leave space for extra
2752  * characters */
2753  int maxlen = MAX_LEN_NAME + 16;
2754  char *quoted = new char[maxlen];
2755 
2756  fc_snprintf(quoted, maxlen,
2757  // TRANS: %s is an action that can block another.
2758  _("\'%s\'"),
2759  qUtf8Printable(action_id_name_translation(blocker)));
2760  blockers.append(quoted);
2761  }
2762  }
2764 
2765  if (!blockers.isEmpty()) {
2766  cat_snprintf(buf, bufsz,
2767  // TRANS: %s is a list of actions separated by "or".
2768  _(" * can't be done if %s is legal.\n"),
2769  qUtf8Printable(strvec_to_or_list(blockers)));
2770  }
2771  }
2772  }
2774  action_iterate(act)
2775  {
2776  bool vulnerable;
2777 
2778  if (action_by_number(act)->quiet) {
2779  // The ruleset documents this action it self.
2780  continue;
2781  }
2782 
2783  // Not relevant
2784  if (action_id_get_target_kind(act) != ATK_UNIT
2785  && action_id_get_target_kind(act) != ATK_UNITS
2786  && action_id_get_target_kind(act) != ATK_SELF) {
2787  continue;
2788  }
2789 
2790  // All units are immune to this since its not enabled
2791  if (action_enabler_list_size(action_enablers_for_action(act)) == 0) {
2792  continue;
2793  }
2794 
2795  // Must be immune in all cases
2796  vulnerable = false;
2798  {
2800  &(enabler->target_reqs))) {
2801  vulnerable = true;
2802  break;
2803  }
2804  }
2806 
2807  if (!vulnerable) {
2808  cat_snprintf(buf, bufsz,
2809  _("* Doing the action \'%s\' to this unit"
2810  " is impossible.\n"),
2811  qUtf8Printable(action_id_name_translation(act)));
2812  }
2813  }
2815  if (!has_vet_levels) {
2816  // Only mention this if the game generally does have veteran levels.
2817  if (game.veteran->levels > 1) {
2818  CATLSTR(buf, bufsz, _("* Will never achieve veteran status.\n"));
2819  }
2820  } else {
2821  // Not useful currently:
2822 #if 0
2823  // Some units can never become veteran through combat in practice.
2824  bool veteran_through_combat =
2825  !(!utype_can_do_action(utype, ACTION_ATTACK)
2826  && utype->defense_strength == 0);
2827 #endif
2828  /* FIXME: if we knew the raise chances on the client, we could be
2829  * more specific here about whether veteran status can be acquired
2830  * through combat/missions/work. Should also take into account
2831  * UTYF_NO_VETERAN when writing this text. (Gna patch #4794) */
2832  CATLSTR(buf, bufsz, _("* May acquire veteran status.\n"));
2833  if (utype_veteran_has_power_bonus(utype)) {
2834  if (utype_can_do_action(utype, ACTION_ATTACK)
2835  || utype->defense_strength > 0) {
2836  CATLSTR(buf, bufsz,
2837  // TRANS: indented; preserve leading spaces
2838  _(" * Veterans have increased strength in combat.\n"));
2839  }
2840  /* SUPERSPY always wins/escapes */
2841  if (utype_has_flag(utype, UTYF_DIPLOMAT)
2842  && !utype_has_flag(utype, UTYF_SUPERSPY)) {
2843  CATLSTR(buf, bufsz,
2844  // TRANS: indented; preserve leading spaces
2845  _(" * Veterans have improved chances in diplomatic "
2846  "contests.\n"));
2847  if (utype_may_do_escape_action(utype)) {
2848  CATLSTR(buf, bufsz,
2849  // TRANS: indented; preserve leading spaces
2850  _(" * Veterans are more likely to survive missions.\n"));
2851  }
2852  }
2853  if (utype_has_flag(utype, UTYF_SETTLERS)) {
2854  CATLSTR(buf, bufsz,
2855  // TRANS: indented; preserve leading spaces
2856  _(" * Veterans work faster.\n"));
2857  }
2858  }
2859  }
2860  if (strlen(buf) > 0) {
2861  CATLSTR(buf, bufsz, "\n");
2862  }
2863  if (has_vet_levels && utype->veteran) {
2864  /* The case where the unit has only a single veteran level has already
2865  * been handled above, so keep quiet here if that happens */
2866  if (insert_veteran_help(
2867  buf, bufsz, utype->veteran,
2868  _("This type of unit has its own veteran levels:"), nullptr)) {
2869  CATLSTR(buf, bufsz, "\n\n");
2870  }
2871  }
2872  if (nullptr != utype->helptext) {
2873  for (const auto &text : qAsConst(*utype->helptext)) {
2874  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
2875  }
2876  }
2877  CATLSTR(buf, bufsz, user_text);
2878  return buf;
2879 }
2880 
2886 void helptext_advance(char *buf, size_t bufsz, struct player *pplayer,
2887  const char *user_text, int i,
2888  const nation_set *nations_to_show)
2889 {
2890  struct advance *vap = valid_advance_by_number(i);
2891  struct universal source = {.value = {.advance = vap}, .kind = VUT_ADVANCE};
2892  int flagid;
2893 
2894  fc_assert_ret(nullptr != buf && 0 < bufsz && nullptr != user_text);
2895  fc_strlcpy(buf, user_text, bufsz);
2896 
2897  if (nullptr == vap) {
2898  qCritical("Unknown tech %d.", i);
2899  return;
2900  }
2901 
2902  if (game.control.num_tech_classes > 0) {
2903  if (vap->tclass == nullptr) {
2904  cat_snprintf(buf, bufsz, _("Belongs to the default tech class.\n\n"));
2905  } else {
2906  cat_snprintf(buf, bufsz, _("Belongs to tech class %s.\n\n"),
2908  }
2909  }
2910 
2911  if (nullptr != pplayer) {
2912  const struct research *presearch = research_get(pplayer);
2913 
2914  if (research_invention_state(presearch, i) != TECH_KNOWN) {
2915  if (research_invention_state(presearch, i) == TECH_PREREQS_KNOWN) {
2916  int bulbs = research_total_bulbs_required(presearch, i, false);
2917 
2918  cat_snprintf(buf, bufsz,
2919  PL_("Starting now, researching %s would need %d bulb.",
2920  "Starting now, researching %s would need %d bulbs.",
2921  bulbs),
2922  advance_name_translation(vap), bulbs);
2923  } else if (research_invention_reachable(presearch, i)) {
2924  /* Split string into two to allow localization of two pluralizations.
2925  */
2926  char buf2[MAX_LEN_MSG];
2927  int bulbs = research_goal_bulbs_required(presearch, i);
2928 
2929  fc_snprintf(
2930  buf2, ARRAY_SIZE(buf2),
2931  /* TRANS: appended to another sentence. Preserve the
2932  * leading space. */
2933  PL_(" The whole project will require %d bulb to complete.",
2934  " The whole project will require %d bulbs to complete.",
2935  bulbs),
2936  bulbs);
2937  cat_snprintf(buf, bufsz,
2938  // TRANS: last %s is a sentence pluralized separately.
2939  PL_("To research %s you need to research %d other"
2940  " technology first.%s",
2941  "To research %s you need to research %d other"
2942  " technologies first.%s",
2943  research_goal_unknown_techs(presearch, i) - 1),
2945  research_goal_unknown_techs(presearch, i) - 1, buf2);
2946  } else {
2947  CATLSTR(buf, bufsz, _("You cannot research this technology."));
2948  }
2949  if (!techs_have_fixed_costs()
2950  && research_invention_reachable(presearch, i)) {
2951  CATLSTR(buf, bufsz,
2952  // TRANS: preserve leading space
2953  _(" This number may vary depending on what "
2954  "other players research.\n"));
2955  } else {
2956  CATLSTR(buf, bufsz, "\n");
2957  }
2958  }
2959 
2960  CATLSTR(buf, bufsz, "\n");
2961  }
2962 
2963  if (requirement_vector_size(&vap->research_reqs) > 0) {
2964  CATLSTR(buf, bufsz, _("Requirements to research:\n"));
2966  {
2967  (void) req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "");
2968  }
2970  CATLSTR(buf, bufsz, "\n");
2971  }
2972 
2973  insert_allows(&source, buf + qstrlen(buf), bufsz - qstrlen(buf),
2974  // TRANS: bullet point; note trailing space
2975  Q_("?bullet:* "));
2976 
2977  {
2978  int j;
2979 
2980  for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
2981  if (game.rgame.global_init_techs[j] == A_LAST) {
2982  break;
2983  } else if (game.rgame.global_init_techs[j] == i) {
2984  CATLSTR(buf, bufsz,
2985  _("* All players start the game with knowledge of this "
2986  "technology.\n"));
2987  break;
2988  }
2989  }
2990  }
2991 
2992  /* Assume no-one will set the same tech in both global and nation
2993  * init_tech... */
2994  for (const auto &pnation : nations) {
2995  // Avoid mentioning nations not in current set.
2996  if (nations_to_show && !nation_is_in_set(&pnation, nations_to_show)) {
2997  continue;
2998  }
2999  for (int j = 0; j < MAX_NUM_TECH_LIST; j++) {
3000  if (pnation.init_techs[j] == A_LAST) {
3001  break;
3002  } else if (pnation.init_techs[j] == i) {
3003  cat_snprintf(buf, bufsz,
3004  // TRANS: %s is a nation plural
3005  _("* The %s start the game with knowledge of this "
3006  "technology.\n"),
3007  nation_plural_translation(&pnation));
3008  break;
3009  }
3010  }
3011  } // iterate over nations - pnation
3012 
3013  // Explain the effects of root_reqs.
3014  {
3015  bv_techs roots, rootsofroots;
3016 
3017  BV_CLR_ALL(roots);
3018  BV_CLR_ALL(rootsofroots);
3019  advance_root_req_iterate(vap, proot)
3020  {
3021  if (proot == vap) {
3022  /* Don't say anything at all if this tech is a self-root-req one;
3023  * assume that the ruleset help will explain how to get it. */
3024  BV_CLR_ALL(roots);
3025  break;
3026  }
3027  BV_SET(roots, advance_number(proot));
3028  if (advance_requires(proot, AR_ROOT) != proot) {
3029  /* Now find out what roots each of this tech's root_req has, so that
3030  * we can suppress them. If tech A has roots B/C, and B has root C,
3031  * it's not worth saying that A needs C, and can lead to overwhelming
3032  * lists. */
3033  /* (Special case: don't do this if the root is a self-root-req tech,
3034  * since it would appear in its own root iteration; in the scenario
3035  * where S is a self-root tech that is root for T, this would prevent
3036  * S appearing in T's help.) */
3037  // FIXME this is quite inefficient
3038  advance_root_req_iterate(proot, prootroot)
3039  {
3040  BV_SET(rootsofroots, advance_number(prootroot));
3041  }
3043  }
3044  }
3046 
3047  // Filter out all but the direct root reqs.
3048  BV_CLR_ALL_FROM(roots, rootsofroots);
3049 
3050  if (BV_ISSET_ANY(roots)) {
3051  QVector<QString> root_techs;
3052  root_techs.reserve(A_LAST);
3053  size_t n_roots = 0;
3054 
3056  {
3057  if (BV_ISSET(roots, root)) {
3058  root_techs.append(
3060  }
3061  }
3063  fc_assert(n_roots > 0);
3064  cat_snprintf(buf, bufsz,
3065  // TRANS: 'and'-separated list of techs
3066  _("* Only those who know %s can acquire this "
3067  "technology (by any means).\n"),
3068  qUtf8Printable(strvec_to_and_list(root_techs)));
3069  }
3070  }
3071 
3072  if (advance_has_flag(i, TF_BONUS_TECH)) {
3073  cat_snprintf(buf, bufsz,
3074  _("* The first player to learn %s gets"
3075  " an immediate advance.\n"),
3077  }
3078 
3079  for (flagid = TECH_USER_1; flagid <= TECH_USER_LAST; flagid++) {
3080  if (advance_has_flag(i, static_cast<tech_flag_id>(flagid))) {
3081  const char *helptxt =
3082  tech_flag_helptxt(static_cast<tech_flag_id>(flagid));
3083 
3084  if (helptxt != nullptr) {
3085  // TRANS: bullet point; note trailing space
3086  CATLSTR(buf, bufsz, Q_("?bullet:* "));
3087  CATLSTR(buf, bufsz, _(helptxt));
3088  CATLSTR(buf, bufsz, "\n");
3089  }
3090  }
3091  }
3092 
3093  /* FIXME: bases -- but there is no good way to find out which bases a tech
3094  * can enable currently, so we have to remain silent. */
3095 
3096  if (game.info.tech_upkeep_style != TECH_UPKEEP_NONE) {
3097  CATLSTR(buf, bufsz,
3098  _("* To preserve this technology for our nation some bulbs "
3099  "are needed each turn.\n"));
3100  }
3101 
3102  if (nullptr != vap->helptext) {
3103  if (strlen(buf) > 0) {
3104  CATLSTR(buf, bufsz, "\n");
3105  }
3106  for (const auto &text : qAsConst(*vap->helptext)) {
3107  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
3108  }
3109  }
3110 }
3111 
3115 void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer,
3116  const char *user_text, struct terrain *pterrain)
3117 {
3118  Q_UNUSED(pplayer)
3119  struct universal source = {.value = {.terrain = pterrain},
3120  .kind = VUT_TERRAIN};
3121  int flagid;
3122 
3123  fc_assert_ret(nullptr != buf && 0 < bufsz);
3124  buf[0] = '\0';
3125 
3126  if (!pterrain) {
3127  qCritical("Unknown terrain!");
3128  return;
3129  }
3130 
3131  insert_allows(&source, buf + qstrlen(buf), bufsz - qstrlen(buf),
3132  // TRANS: bullet point; note trailing space
3133  Q_("?bullet:* "));
3134  if (terrain_has_flag(pterrain, TER_NO_CITIES)) {
3135  CATLSTR(buf, bufsz, _("* You cannot build cities on this terrain."));
3136  CATLSTR(buf, bufsz, "\n");
3137  }
3138  if (pterrain->road_time == 0) {
3139  // Can't build roads; only mention if ruleset has buildable roads
3140  extra_type_by_cause_iterate(EC_ROAD, pextra)
3141  {
3142  if (pextra->buildable) {
3143  CATLSTR(buf, bufsz, _("* Paths cannot be built on this terrain."));
3144  CATLSTR(buf, bufsz, "\n");
3145  break;
3146  }
3147  }
3149  }
3150  if (pterrain->base_time == 0) {
3151  // Can't build bases; only mention if ruleset has buildable bases
3152  extra_type_by_cause_iterate(EC_BASE, pextra)
3153  {
3154  if (pextra->buildable) {
3155  CATLSTR(buf, bufsz, _("* Bases cannot be built on this terrain."));
3156  CATLSTR(buf, bufsz, "\n");
3157  break;
3158  }
3159  }
3161  }
3162  if (terrain_has_flag(pterrain, TER_UNSAFE_COAST)
3163  && terrain_type_terrain_class(pterrain) != TC_OCEAN) {
3164  CATLSTR(buf, bufsz, _("* The coastline of this terrain is unsafe."));
3165  CATLSTR(buf, bufsz, "\n");
3166  }
3167  {
3168  QVector<QString> classes;
3169  classes.reserve(uclass_count());
3170 
3171  unit_class_iterate(uclass)
3172  {
3173  if (is_native_to_class(uclass, pterrain, nullptr)) {
3174  classes.append(uclass_name_translation(uclass));
3175  }
3176  }
3178 
3179  if (!classes.isEmpty()) {
3180  // TRANS: %s is a list of unit classes separated by "and".
3181  cat_snprintf(buf, bufsz, _("* Can be traveled by %s units.\n"),
3182  qUtf8Printable(strvec_to_and_list(classes)));
3183  }
3184  }
3185  if (terrain_has_flag(pterrain, TER_NO_ZOC)) {
3186  CATLSTR(buf, bufsz,
3187  _("* Units on this terrain neither impose zones of control "
3188  "nor are restricted by them.\n"));
3189  } else {
3190  CATLSTR(buf, bufsz,
3191  _("* Units on this terrain may impose a zone of control, or "
3192  "be restricted by one.\n"));
3193  }
3194  for (flagid = TER_USER_1; flagid <= TER_USER_LAST; flagid++) {
3195  if (terrain_has_flag(pterrain, flagid)) {
3196  const char *helptxt =
3197  terrain_flag_helptxt(static_cast<terrain_flag_id>(flagid));
3198 
3199  if (helptxt != nullptr) {
3200  // TRANS: bullet point; note trailing space
3201  CATLSTR(buf, bufsz, Q_("?bullet:* "));
3202  CATLSTR(buf, bufsz, _(helptxt));
3203  CATLSTR(buf, bufsz, "\n");
3204  }
3205  }
3206  }
3207 
3208  if (nullptr != pterrain->helptext) {
3209  if (buf[0] != '\0') {
3210  CATLSTR(buf, bufsz, "\n");
3211  }
3212  for (const auto &text : qAsConst(*pterrain->helptext)) {
3213  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
3214  }
3215  }
3216  if (user_text && user_text[0] != '\0') {
3217  CATLSTR(buf, bufsz, "\n\n");
3218  CATLSTR(buf, bufsz, user_text);
3219  }
3220 }
3221 
3229 const char *helptext_road_bonus_str(const struct terrain *pterrain,
3230  const struct road_type *proad)
3231 {
3232  static char str[64];
3233  bool has_effect = false;
3234 
3235  str[0] = '\0';
3237  {
3238  switch (o) {
3239  case O_FOOD:
3240  case O_SHIELD:
3241  case O_TRADE: {
3242  int bonus = proad->tile_bonus[o];
3243  int incr = proad->tile_incr_const[o];
3244 
3245  if (pterrain) {
3246  incr +=
3247  proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
3248  }
3249  if (str[0] != '\0') {
3250  CATLSTR(str, sizeof(str), "/");
3251  }
3252  if (incr == 0 && bonus == 0) {
3253  cat_snprintf(str, sizeof(str), "%d", incr);
3254  } else {
3255  has_effect = true;
3256  if (incr != 0) {
3257  cat_snprintf(str, sizeof(str), "%+d", incr);
3258  }
3259  if (bonus != 0) {
3260  cat_snprintf(str, sizeof(str), "%+d%%", bonus);
3261  }
3262  }
3263  } break;
3264  default:
3265  /* FIXME: there's nothing actually stopping roads having gold, etc
3266  * bonuses */
3267  fc_assert(proad->tile_incr_const[o] == 0 && proad->tile_incr[o] == 0
3268  && proad->tile_bonus[o] == 0);
3269  break;
3270  }
3271  }
3273 
3274  return has_effect ? str : nullptr;
3275 }
3276 
3283 static void extra_bonus_for_terrain(struct extra_type *pextra,
3284  struct terrain *pterrain, int *bonus)
3285 {
3286  struct universal req_pattern[] = {
3287  {.value = {.extra = pextra}, .kind = VUT_EXTRA},
3288  {.value = {.terrain = pterrain}, .kind = VUT_TERRAIN},
3289  {.kind = VUT_OTYPE /* value filled in later */}};
3290 
3291  fc_assert_ret(bonus != nullptr);
3292 
3293  // Irrigation-like food bonuses
3294  bonus[0] = (pterrain->irrigation_food_incr
3295  * effect_value_from_universals(EFT_IRRIGATION_PCT, req_pattern,
3296  2 /* just extra+terrain */))
3297  / 100;
3298 
3299  // Mining-like shield bonuses
3300  bonus[1] = (pterrain->mining_shield_incr
3301  * effect_value_from_universals(EFT_MINING_PCT, req_pattern,
3302  2 /* just extra+terrain */))
3303  / 100;
3304 
3305  bonus[2] = 0; // no trade bonuses so far
3306 
3307  // Now add fixed bonuses from roads (but not percentage bonus)
3308  if (extra_road_get(pextra)) {
3309  const struct road_type *proad = extra_road_get(pextra);
3310 
3312  {
3313  switch (o) {
3314  case O_FOOD:
3315  case O_SHIELD:
3316  case O_TRADE:
3317  bonus[o] +=
3318  proad->tile_incr_const[o]
3319  + proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
3320  break;
3321  default:
3322  // not dealing with other output types here
3323  break;
3324  }
3325  }
3327  }
3328 
3329  // Fixed bonuses for extra, possibly unrelated to terrain type
3330 
3332  {
3333  // Fill in rest of requirement template
3334  req_pattern[2].value.outputtype = static_cast<Output_type_id>(o);
3335  switch (o) {
3336  case O_FOOD:
3337  case O_SHIELD:
3338  case O_TRADE:
3339  bonus[o] += effect_value_from_universals(
3340  EFT_OUTPUT_ADD_TILE, req_pattern, ARRAY_SIZE(req_pattern));
3341  /* Any of the above bonuses is sufficient to trigger
3342  * Output_Inc_Tile, if underlying terrain does not */
3343  if (bonus[o] > 0 || pterrain->output[o] > 0) {
3344  bonus[o] += effect_value_from_universals(
3345  EFT_OUTPUT_INC_TILE, req_pattern, ARRAY_SIZE(req_pattern));
3346  }
3347  break;
3348  default:
3349  break;
3350  }
3351  }
3353 }
3354 
3361 const char *helptext_extra_for_terrain_str(struct extra_type *pextra,
3362  struct terrain *pterrain,
3363  enum unit_activity act)
3364 {
3365  static char buffer[256];
3366  int btime;
3367  int bonus[3];
3368 
3369  btime = terrain_extra_build_time(pterrain, act, pextra);
3370  fc_snprintf(buffer, sizeof(buffer), PL_("%d turn", "%d turns", btime),
3371  btime);
3372  extra_bonus_for_terrain(pextra, pterrain, bonus);
3373  if (bonus[0] > 0) {
3374  cat_snprintf(buffer, sizeof(buffer),
3375  PL_(", +%d food", ", +%d food", bonus[0]), bonus[0]);
3376  }
3377  if (bonus[1] > 0) {
3378  cat_snprintf(buffer, sizeof(buffer),
3379  PL_(", +%d shield", ", +%d shields", bonus[1]), bonus[1]);
3380  }
3381  if (bonus[2] > 0) {
3382  cat_snprintf(buffer, sizeof(buffer),
3383  PL_(", +%d trade", ", +%d trade", bonus[2]), bonus[2]);
3384  }
3385 
3386  return buffer;
3387 }
3388 
3395 void helptext_extra(char *buf, size_t bufsz, struct player *pplayer,
3396  const char *user_text, struct extra_type *pextra)
3397 {
3398  size_t group_start;
3399  struct base_type *pbase;
3400  struct road_type *proad;
3401  struct universal source = {.value = {.extra = pextra}, .kind = VUT_EXTRA};
3402 
3403  int flagid;
3404 
3405  fc_assert_ret(nullptr != buf && 0 < bufsz);
3406  buf[0] = '\0';
3407 
3408  if (!pextra) {
3409  qCritical("Unknown extra!");
3410  return;
3411  }
3412 
3413  if (is_extra_caused_by(pextra, EC_BASE)) {
3414  pbase = pextra->data.base;
3415  } else {
3416  pbase = nullptr;
3417  }
3418 
3419  if (is_extra_caused_by(pextra, EC_ROAD)) {
3420  proad = pextra->data.road;
3421  } else {
3422  proad = nullptr;
3423  }
3424 
3425  if (pextra->helptext != nullptr) {
3426  for (const auto &text : qAsConst(*pextra->helptext)) {
3427  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
3428  }
3429  }
3430 
3431  // Describe how extra is created and destroyed
3432 
3433  group_start = qstrlen(buf);
3434 
3435  if (pextra->buildable) {
3436  if (is_extra_caused_by(pextra, EC_IRRIGATION)) {
3437  CATLSTR(buf, bufsz, _("Build by issuing an \"irrigate\" order.\n"));
3438  }
3439  if (is_extra_caused_by(pextra, EC_MINE)) {
3440  CATLSTR(buf, bufsz, _("Build by issuing a \"mine\" order.\n"));
3441  }
3442  if (is_extra_caused_by(pextra, EC_ROAD)) {
3443  CATLSTR(buf, bufsz, _("Build by issuing a \"road\" order.\n"));
3444  }
3445  if (is_extra_caused_by(pextra, EC_BASE)) {
3446  fc_assert(pbase);
3447  if (pbase->gui_type == BASE_GUI_OTHER) {
3448  cat_snprintf(buf, bufsz,
3449  _("Build by issuing a \"build base\" order.\n"));
3450  } else {
3451  const char *order = "";
3452 
3453  switch (pbase->gui_type) {
3454  case BASE_GUI_FORTRESS:
3455  order = Q_(terrain_control.gui_type_base0);
3456  break;
3457  case BASE_GUI_AIRBASE:
3458  order = Q_(terrain_control.gui_type_base1);
3459  break;
3460  default:
3461  fc_assert(false);
3462  break;
3463  }
3464  cat_snprintf(buf, bufsz,
3465  // TRANS: %s is a gui_type base string from a ruleset
3466  _("Build by issuing a \"%s\" order.\n"), order);
3467  }
3468  }
3469  }
3470 
3471  if (is_extra_caused_by(pextra, EC_POLLUTION)) {
3472  CATLSTR(buf, bufsz, _("May randomly appear around polluting city.\n"));
3473  }
3474 
3475  if (is_extra_caused_by(pextra, EC_FALLOUT)) {
3476  CATLSTR(buf, bufsz, _("May randomly appear around nuclear blast.\n"));
3477  }
3478 
3479  if (pextra->generated
3480  && (is_extra_caused_by(pextra, EC_HUT)
3481  || is_extra_caused_by(pextra, EC_RESOURCE)
3482  || (proad != nullptr && road_has_flag(proad, RF_RIVER)))) {
3483  CATLSTR(buf, bufsz, _("This resource is placed by map generator.\n"));
3484  }
3485 
3486  if (is_extra_removed_by(pextra, ERM_ENTER)) {
3487  CATLSTR(buf, bufsz, _("Can be explored by certain units.\n"));
3488  }
3489 
3490  if (is_extra_caused_by(pextra, EC_APPEARANCE)) {
3491  CATLSTR(buf, bufsz, _("May appear spontaneously.\n"));
3492  }
3493 
3494  if (requirement_vector_size(&pextra->reqs) > 0) {
3495  char reqsbuf[8192] = "";
3496  bool buildable =
3497  pextra->buildable && is_extra_caused_by_worker_action(pextra);
3498 
3499  requirement_vector_iterate(&pextra->reqs, preq)
3500  {
3501  (void) req_text_insert_nl(reqsbuf, sizeof(reqsbuf), pplayer, preq,
3502  VERB_DEFAULT,
3503  // TRANS: bullet point; note trailing space
3504  buildable ? Q_("?bullet:* ") : "");
3505  }
3507  if (reqsbuf[0] != '\0') {
3508  if (buildable) {
3509  CATLSTR(buf, bufsz, _("Requirements to build:\n"));
3510  }
3511  CATLSTR(buf, bufsz, reqsbuf);
3512  }
3513  }
3514 
3515  if (buf[group_start] != '\0') {
3516  CATLSTR(buf, bufsz, "\n"); // group separator
3517  }
3518 
3519  group_start = qstrlen(buf);
3520 
3521  if (is_extra_removed_by(pextra, ERM_PILLAGE)) {
3522  int pillage_time = -1;
3523 
3524  if (pextra->removal_time != 0) {
3525  pillage_time = pextra->removal_time;
3526  } else {
3527  terrain_type_iterate(pterrain)
3528  {
3529  int terr_pillage_time =
3530  pterrain->pillage_time * pextra->removal_time_factor;
3531 
3532  if (terr_pillage_time != 0) {
3533  if (pillage_time < 0) {
3534  pillage_time = terr_pillage_time;
3535  } else if (pillage_time != terr_pillage_time) {
3536  // Give up
3537  pillage_time = -1;
3538  break;
3539  }
3540  }
3541  }
3543  }
3544  if (pillage_time < 0) {
3545  CATLSTR(buf, bufsz,
3546  _("Can be pillaged by units (time is terrain-dependent).\n"));
3547  } else if (pillage_time > 0) {
3548  cat_snprintf(buf, bufsz,
3549  PL_("Can be pillaged by units (takes %d turn).\n",
3550  "Can be pillaged by units (takes %d turns).\n",
3551  pillage_time),
3552  pillage_time);
3553  }
3554  }
3555  if (is_extra_removed_by(pextra, ERM_CLEANPOLLUTION)
3556  || is_extra_removed_by(pextra, ERM_CLEANFALLOUT)) {
3557  int clean_time = -1;
3558 
3559  if (pextra->removal_time != 0) {
3560  clean_time = pextra->removal_time;
3561  } else {
3562  terrain_type_iterate(pterrain)
3563  {
3564  int terr_clean_time = -1;
3565 
3566  if (is_extra_removed_by(pextra, ERM_CLEANPOLLUTION)
3567  && pterrain->clean_pollution_time != 0) {
3568  terr_clean_time =
3569  pterrain->clean_pollution_time * pextra->removal_time_factor;
3570  }
3571  if (is_extra_removed_by(pextra, ERM_CLEANFALLOUT)
3572  && pterrain->clean_fallout_time != 0) {
3573  int terr_clean_fall_time =
3574  pterrain->clean_fallout_time * pextra->removal_time_factor;
3575  if (terr_clean_time > 0
3576  && terr_clean_time != terr_clean_fall_time) {
3577  /* Pollution/fallout cleaning activities taking different time
3578  * on same terrain. Give up. */
3579  clean_time = -1;
3580  break;
3581  }
3582  terr_clean_time = terr_clean_fall_time;
3583  }
3584  if (clean_time < 0) {
3585  clean_time = terr_clean_time;
3586  } else if (clean_time != terr_clean_time) {
3587  // Give up
3588  clean_time = -1;
3589  break;
3590  }
3591  }
3593  }
3594  if (clean_time < 0) {
3595  CATLSTR(buf, bufsz,
3596  _("Can be cleaned by units (time is terrain-dependent).\n"));
3597  } else if (clean_time > 0) {
3598  cat_snprintf(buf, bufsz,
3599  PL_("Can be cleaned by units (takes %d turn).\n",
3600  "Can be cleaned by units (takes %d turns).\n",
3601  clean_time),
3602  clean_time);
3603  }
3604  }
3605 
3606  if (requirement_vector_size(&pextra->rmreqs) > 0) {
3607  char reqsbuf[8192] = "";
3608 
3609  requirement_vector_iterate(&pextra->rmreqs, preq)
3610  {
3611  (void) req_text_insert_nl(reqsbuf, sizeof(reqsbuf), pplayer, preq,
3612  VERB_DEFAULT, Q_("?bullet:* "));
3613  }
3615  if (reqsbuf[0] != '\0') {
3616  CATLSTR(buf, bufsz, _("Requirements to remove:\n"));
3617  CATLSTR(buf, bufsz, reqsbuf);
3618  }
3619  }
3620 
3621  if (buf[group_start] != '\0') {
3622  CATLSTR(buf, bufsz, "\n"); // group separator
3623  }
3624 
3625  // Describe what other elements are enabled by extra
3626 
3627  group_start = qstrlen(buf);
3628 
3629  insert_allows(&source, buf + qstrlen(buf), bufsz - qstrlen(buf), "");
3630 
3631  if (buf[group_start] != '\0') {
3632  CATLSTR(buf, bufsz, "\n"); // group separator
3633  }
3634 
3635  // Describe other properties of extras
3636 
3637  if (pextra->visibility_req != A_NONE) {
3638  char vrbuf[1024];
3639 
3640  fc_snprintf(
3641  vrbuf, sizeof(vrbuf), _("* Visible only if %s known.\n"),
3643  CATLSTR(buf, bufsz, vrbuf);
3644  }
3645 
3646  if (pextra->eus == EUS_HIDDEN) {
3647  CATLSTR(buf, bufsz,
3648  _("* Units inside are hidden from non-allied players.\n"));
3649  }
3650 
3651  {
3652  QVector<QString> classes;
3653  classes.reserve(uclass_count());
3654 
3655  unit_class_iterate(uclass)
3656  {
3657  if (is_native_extra_to_uclass(pextra, uclass)) {
3658  classes.append(uclass_name_translation(uclass));
3659  }
3660  }
3662  if (!classes.isEmpty()) {
3663  if (proad != nullptr) {
3664  // TRANS: %s is a list of unit classes separated by "and".
3665  cat_snprintf(buf, bufsz, _("* Can be traveled by %s units.\n"),
3666  qUtf8Printable(strvec_to_and_list(classes)));
3667  } else {
3668  // TRANS: %s is a list of unit classes separated by "and".
3669  cat_snprintf(buf, bufsz, _("* Native to %s units.\n"),
3670  qUtf8Printable(strvec_to_and_list(classes)));
3671  }
3672 
3673  if (extra_has_flag(pextra, EF_NATIVE_TILE)) {
3674  CATLSTR(buf, bufsz,
3675  // TRANS: indented; preserve leading spaces
3676  _(" * Such units can move onto this tile even if it would "
3677  "not normally be suitable terrain.\n"));
3678  }
3679  if (pbase != nullptr) {
3680  if (base_has_flag(pbase, BF_NOT_AGGRESSIVE)) {
3681  // "3 tiles" is hardcoded in is_friendly_city_near()
3682  CATLSTR(
3683  buf, bufsz,
3684  // TRANS: indented; preserve leading spaces
3685  _(" * Such units situated here are not considered aggressive "
3686  "if this tile is within 3 tiles of a friendly city.\n"));
3687  }
3688  if (territory_claiming_base(pbase)) {
3689  CATLSTR(buf, bufsz,
3690  // TRANS: indented; preserve leading spaces
3691  _(" * Can be captured by such units if at war with the "
3692  "nation that currently owns it.\n"));
3693  }
3694  }
3695  if (pextra->defense_bonus) {
3696  cat_snprintf(buf, bufsz,
3697  // TRANS: indented; preserve leading spaces
3698  _(" * Such units get a %d%% defense bonus on this "
3699  "tile.\n"),
3700  pextra->defense_bonus);
3701  }
3702  }
3703  }
3704 
3705  if (proad != nullptr && road_provides_move_bonus(proad)) {
3706  if (proad->move_cost == 0) {
3707  CATLSTR(buf, bufsz, _("* Allows infinite movement.\n"));
3708  } else {
3709  cat_snprintf(buf, bufsz,
3710  /* TRANS: "MP" = movement points. Second %s may have a
3711  * fractional part. */
3712  _("* Movement cost along %s is %s MP.\n"),
3713  extra_name_translation(pextra),
3714  move_points_text(proad->move_cost, true));
3715  }
3716  }
3717 
3718  if (game.info.killstack && extra_has_flag(pextra, EF_NO_STACK_DEATH)) {
3719  CATLSTR(buf, bufsz,
3720  _("* Defeat of one unit does not cause death of all other units "
3721  "on this tile.\n"));
3722  }
3723  if (pbase != nullptr) {
3724  if (territory_claiming_base(pbase)) {
3725  CATLSTR(buf, bufsz,
3726  _("* Extends national borders of the building nation.\n"));
3727  }
3728  if (pbase->vision_main_sq >= 0) {
3729  CATLSTR(buf, bufsz,
3730  _("* Grants permanent vision of an area around the tile to "
3731  "its owner.\n"));
3732  }
3733  if (pbase->vision_invis_sq >= 0) {
3734  CATLSTR(buf, bufsz,
3735  _("* Allows the owner to see normally invisible stealth units "
3736  "in an area around the tile.\n"));
3737  }
3738  if (pbase->vision_subs_sq >= 0) {
3739  CATLSTR(
3740  buf, bufsz,
3741  _("* Allows the owner to see normally invisible subsurface units "
3742  "in an area around the tile.\n"));
3743  }
3744  }
3745  for (flagid = EF_USER_FLAG_1; flagid <= EF_LAST_USER_FLAG; flagid++) {
3746  if (extra_has_flag(pextra, static_cast<extra_flag_id>(flagid))) {
3747  const char *helptxt =
3748  extra_flag_helptxt(static_cast<extra_flag_id>(flagid));
3749 
3750  if (helptxt != nullptr) {
3751  // TRANS: bullet point; note trailing space
3752  CATLSTR(buf, bufsz, Q_("?bullet:* "));
3753  CATLSTR(buf, bufsz, _(helptxt));
3754  CATLSTR(buf, bufsz, "\n");
3755  }
3756  }
3757  }
3758 
3759  // Table of terrain-specific attributes, if needed
3760  if (proad != nullptr || pbase != nullptr) {
3761  bool road, do_time, do_bonus;
3762 
3763  road = (proad != nullptr);
3764  // Terrain-dependent build time?
3765  do_time = pextra->buildable && pextra->build_time == 0;
3766  if (road) {
3767  // Terrain-dependent output bonus?
3768  do_bonus = false;
3770  {
3771  if (proad->tile_incr[o] > 0) {
3772  do_bonus = true;
3773  fc_assert(o == O_FOOD || o == O_SHIELD || o == O_TRADE);
3774  }
3775  }
3777  } else {
3778  // Bases don't have output bonuses
3779  do_bonus = false;
3780  }
3781 
3782  if (do_time || do_bonus) {
3783  if (do_time && do_bonus) {
3784  CATLSTR(
3785  buf, bufsz,
3786  _("\nTime to build and output bonus depends on terrain:\n\n"));
3787  CATLSTR(buf, bufsz,
3788  /* TRANS: Header for fixed-width road properties table.
3789  * TRANS: Translators cannot change column widths :( */
3790  _("Terrain Time Bonus F/P/T\n"
3791  "----------------------------------\n"));
3792  } else if (do_time) {
3793  CATLSTR(buf, bufsz, _("\nTime to build depends on terrain:\n\n"));
3794  CATLSTR(buf, bufsz,
3795  /* TRANS: Header for fixed-width extra properties table.
3796  * TRANS: Translators cannot change column widths :( */
3797  _("Terrain Time\n"
3798  "------------------\n"));
3799  } else {
3800  fc_assert(do_bonus);
3801  CATLSTR(buf, bufsz,
3802  /* TRANS: Header for fixed-width road properties table.
3803  * TRANS: Translators cannot change column widths :( */
3804  _("\nYields an output bonus with some terrains:\n\n"));
3805  CATLSTR(buf, bufsz,
3806  _("Terrain Bonus F/P/T\n"
3807  "-------------------------\n"));
3808  ;
3809  }
3811  {
3812  int turns =
3813  road ? terrain_extra_build_time(t, ACTIVITY_GEN_ROAD, pextra)
3814  : terrain_extra_build_time(t, ACTIVITY_BASE, pextra);
3815  const char *bonus_text =
3816  road ? helptext_road_bonus_str(t, proad) : nullptr;
3817  if (turns > 0 || bonus_text) {
3818  const char *terrain = terrain_name_translation(t);
3819 
3820  cat_snprintf(
3821  buf, bufsz, "%s%*s ", terrain,
3822  MAX(0, 12 - (int) get_internal_string_length(terrain)), "");
3823  if (do_time) {
3824  if (turns > 0) {
3825  cat_snprintf(buf, bufsz, "%3d ", turns);
3826  } else {
3827  CATLSTR(buf, bufsz, " - ");
3828  }
3829  }
3830  if (do_bonus) {
3831  fc_assert(proad != nullptr);
3832  cat_snprintf(buf, bufsz, " %s", bonus_text ? bonus_text : "-");
3833  }
3834  CATLSTR(buf, bufsz, "\n");
3835  }
3836  }
3838  } // else rely on client-specific display
3839  }
3840 
3841  if (user_text && user_text[0] != '\0') {
3842  CATLSTR(buf, bufsz, "\n\n");
3843  CATLSTR(buf, bufsz, user_text);
3844  }
3845 }
3846 
3853 void helptext_goods(char *buf, size_t bufsz, struct player *pplayer,
3854  const char *user_text, struct goods_type *pgood)
3855 {
3856  bool reqs = false;
3857 
3858  fc_assert_ret(nullptr != buf && 0 < bufsz);
3859  buf[0] = '\0';
3860 
3861  if (nullptr != pgood->helptext) {
3862  for (const auto &text : qAsConst(*pgood->helptext)) {
3863  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
3864  }
3865  }
3866 
3867  if (pgood->onetime_pct == 0) {
3868  cat_snprintf(
3869  buf, bufsz,
3870  _("There's no bonuses paid when traderoute is established.\n\n"));
3871  } else if (pgood->onetime_pct != 100) {
3872  cat_snprintf(buf, bufsz,
3873  _("When traderoute is established, %d%% of the normal "
3874  "bonus is paid.\n"),
3875  pgood->onetime_pct);
3876  }
3877  cat_snprintf(buf, bufsz,
3878  _("Sending city enjoys %d%% income from the route.\n"),
3879  pgood->from_pct);
3880  cat_snprintf(buf, bufsz,
3881  _("Receiving city enjoys %d%% income from the route.\n\n"),
3882  pgood->to_pct);
3883 
3884  // Requirements for this good.
3885  requirement_vector_iterate(&pgood->reqs, preq)
3886  {
3887  if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
3888  reqs = true;
3889  }
3890  }
3892  if (reqs) {
3893  fc_strlcat(buf, "\n", bufsz);
3894  }
3895 
3896  CATLSTR(buf, bufsz, user_text);
3897 }
3898 
3905 void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer,
3906  const char *user_text, struct specialist *pspec)
3907 {
3908  bool reqs = false;
3909 
3910  fc_assert_ret(nullptr != buf && 0 < bufsz);
3911  buf[0] = '\0';
3912 
3913  if (nullptr != pspec->helptext) {
3914  for (const auto &text : qAsConst(*pspec->helptext)) {
3915  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
3916  }
3917  }
3918 
3919  // Requirements for this specialist.
3920  requirement_vector_iterate(&pspec->reqs, preq)
3921  {
3922  if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
3923  reqs = true;
3924  }
3925  }
3927  if (reqs) {
3928  fc_strlcat(buf, "\n", bufsz);
3929  }
3930 
3931  CATLSTR(buf, bufsz, user_text);
3932 }
3933 
3942 void helptext_government(char *buf, size_t bufsz, struct player *pplayer,
3943  const char *user_text, struct government *gov)
3944 {
3945  bool reqs = false;
3946  QVector<QString> outputs;
3947  struct universal source = {.value = {.govern = gov},
3948  .kind = VUT_GOVERNMENT};
3949 
3950  fc_assert_ret(nullptr != buf && 0 < bufsz);
3951  buf[0] = '\0';
3952 
3953  if (nullptr != gov->helptext) {
3954  for (const auto &text : qAsConst(*gov->helptext)) {
3955  cat_snprintf(buf, bufsz, "%s\n\n", _(qUtf8Printable(text)));
3956  }
3957  }
3958 
3959  // Add requirement text for government itself
3960  requirement_vector_iterate(&gov->reqs, preq)
3961  {
3962  if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
3963  reqs = true;
3964  }
3965  }
3967  if (reqs) {
3968  fc_strlcat(buf, "\n", bufsz);
3969  }
3970 
3971  // Effects
3972  CATLSTR(buf, bufsz, _("Features:\n"));
3973  insert_allows(&source, buf + qstrlen(buf), bufsz - qstrlen(buf),
3974  // TRANS: bullet point; note trailing space
3975  Q_("?bullet:* "));
3976  effect_list_iterate(get_req_source_effects(&source), peffect)
3977  {
3979  struct unit_class *unitclass = nullptr;
3980  const struct unit_type *unittype = nullptr;
3981  enum unit_type_flag_id unitflag = unit_type_flag_id_invalid();
3982  outputs.clear();
3983  QString or_outputs = Q_("?outputlist: Nothing ");
3984  QString and_outputs = Q_("?outputlist: Nothing ");
3985  bool too_complex = false;
3986  bool world_value_valid = true;
3987 
3988  // Grab output type, if there is one
3989  requirement_vector_iterate(&peffect->reqs, preq)
3990  {
3991  /* Treat an effect with any negated requirements as too complex for
3992  * us to explain here.
3993  * Also don't try to explain an effect with any requirements explicitly
3994  * marked as 'quiet' by ruleset author. */
3995  if (!preq->present || preq->quiet) {
3996  too_complex = true;
3997  continue;
3998  }
3999  switch (preq->source.kind) {
4000  case VUT_OTYPE:
4001  /* We should never have multiple outputtype requirements
4002  * in one list in the first place (it simply makes no sense,
4003  * output cannot be of multiple types)
4004  * Ruleset loading code should check against that. */
4006  output_type = preq->source.value.outputtype;
4007  outputs.append(get_output_name(output_type));
4008  break;
4009  case VUT_UCLASS:
4010  fc_assert(unitclass == nullptr);
4011  unitclass = preq->source.value.uclass;
4012  // FIXME: can't easily get world bonus for unit class
4013  world_value_valid = false;
4014  break;
4015  case VUT_UTYPE:
4016  fc_assert(unittype == nullptr);
4017  unittype = preq->source.value.utype;
4018  break;
4019  case VUT_UTFLAG:
4020  if (!unit_type_flag_id_is_valid(unitflag)) {
4021  unitflag =
4022  static_cast<unit_type_flag_id>(preq->source.value.unitflag);
4023  // FIXME: can't easily get world bonus for unit type flag
4024  world_value_valid = false;
4025  } else {
4026  /* Already have a unit flag requirement. More than one is too
4027  * complex for us to explain, so say nothing. */
4028  // FIXME: we could handle this
4029  too_complex = true;
4030  }
4031  break;
4032  case VUT_GOVERNMENT:
4033  /* This is government we are generating helptext for.
4034  * ...or if not, it's ruleset bug that should never make it
4035  * this far. Fix ruleset loading code. */
4036  fc_assert(preq->source.value.govern == gov);
4037  break;
4038  default:
4039  too_complex = true;
4040  world_value_valid = false;
4041  break;
4042  };
4043  }
4045 
4046  if (!too_complex) {
4047  /* Only list effects that don't have extra requirements too complex
4048  * for us to handle.
4049  * Anything more complicated will have to be documented by hand by the
4050  * ruleset author. */
4051 
4052  /* Guard condition for simple player-wide effects descriptions.
4053  * (FIXME: in many cases, e.g. EFT_MAKE_CONTENT, additional
4054  * requirements like unittype will be ignored for gameplay, but will
4055  * affect our help here.) */
4056  const bool playerwide =
4057  world_value_valid && !unittype && (output_type == O_LAST);
4058  /* In some cases we give absolute values (world bonus + gov bonus).
4059  * We assume the fact that there's an effect with a gov requirement
4060  * is sufficient reason to list it in that gov's help.
4061  * Guard accesses to these with 'playerwide' or 'world_value_valid'. */
4062  int world_value = -999, net_value = -999;
4063  if (world_value_valid) {
4064  /* Get government-independent world value of effect if the extra
4065  * requirements were simple enough. */
4066  struct output_type *potype =
4068  world_value = get_target_bonus_effects(
4069  nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
4070  unittype, potype, nullptr, nullptr, peffect->type);
4071  net_value = peffect->value + world_value;
4072  }
4073 
4074  if (output_type == O_LAST) {
4075  /* There was no outputtype requirement. Effect is active for all
4076  * output types. Generate lists for that. */
4077  bool harvested_only = true; // Consider only output types from fields
4078 
4079  if (peffect->type == EFT_UPKEEP_FACTOR
4080  || peffect->type == EFT_UNIT_UPKEEP_FREE_PER_CITY
4081  || peffect->type == EFT_OUTPUT_BONUS
4082  || peffect->type == EFT_OUTPUT_BONUS_2) {
4083  // Effect can use or require any kind of output
4084  harvested_only = false;
4085  }
4086 
4088  {
4089  struct output_type *pot =
4090  get_output_type(static_cast<Output_type_id>(ot));
4091 
4092  if (!harvested_only || pot->harvested) {
4093  outputs.append(_(pot->name));
4094  }
4095  }
4097  }
4098  if (outputs.count()) {
4099  or_outputs = strvec_to_or_list(outputs);
4100  and_outputs = strvec_to_and_list(outputs);
4101  }
4102 
4103  switch (peffect->type) {
4104  case EFT_UNHAPPY_FACTOR:
4105  if (playerwide) {
4106  /* FIXME: EFT_MAKE_CONTENT_MIL_PER would cancel this out. We assume
4107  * no-one will set both, so we don't bother handling it. */
4108  cat_snprintf(
4109  buf, bufsz,
4110  PL_("* Military units away from home and field units"
4111  " will each cause %d citizen to become unhappy.\n",
4112  "* Military units away from home and field units"
4113  " will each cause %d citizens to become unhappy.\n",
4114  net_value),
4115  net_value);
4116  } // else too complicated or silly ruleset
4117  break;
4118  case EFT_ENEMY_CITIZEN_UNHAPPY_PCT:
4119  if (playerwide && net_value != world_value) {
4120  if (world_value > 0) {
4121  if (net_value > 0) {
4122  cat_snprintf(buf, bufsz,
4123  _("* Unhappiness from foreign citizens due to "
4124  "war with their home state is %d%% the usual "
4125  "value.\n"),
4126  (net_value * 100) / world_value);
4127  } else {
4128  CATLSTR(buf, bufsz,
4129  _("* No unhappiness from foreign citizens even when "
4130  "at war with their home state.\n"));
4131  }
4132  } else {
4133  cat_snprintf(buf, bufsz,
4134  /* TRANS: not pluralised as gettext doesn't support
4135  * fractional numbers, which this might be */
4136  _("* Each foreign citizen causes %.2g unhappiness "
4137  "in their city while you are at war with their "
4138  "home state.\n"),
4139  static_cast<double>(net_value) / 100);
4140  }
4141  }
4142  break;
4143  case EFT_MAKE_CONTENT_MIL:
4144  if (playerwide) {
4145  cat_snprintf(buf, bufsz,
4146  PL_("* Each of your cities will avoid %d unhappiness"
4147  " caused by units.\n",
4148  "* Each of your cities will avoid %d unhappiness"
4149  " caused by units.\n",
4150  peffect->value),
4151  peffect->value);
4152  }
4153  break;
4154  case EFT_MAKE_CONTENT:
4155  if (playerwide) {
4156  cat_snprintf(buf, bufsz,
4157  PL_("* Each of your cities will avoid %d unhappiness,"
4158  " not including that caused by aggression.\n",
4159  "* Each of your cities will avoid %d unhappiness,"
4160  " not including that caused by aggression.\n",
4161  peffect->value),
4162  peffect->value);
4163  }
4164  break;
4165  case EFT_FORCE_CONTENT:
4166  if (playerwide) {
4167  cat_snprintf(buf, bufsz,
4168  PL_("* Each of your cities will avoid %d unhappiness,"
4169  " including that caused by aggression.\n",
4170  "* Each of your cities will avoid %d unhappiness,"
4171  " including that caused by aggression.\n",
4172  peffect->value),
4173  peffect->value);
4174  }
4175  break;
4176  case EFT_UPKEEP_FACTOR:
4177  if (world_value_valid && !unittype) {
4178  if (net_value == 0) {
4179  if (output_type != O_LAST) {
4180  cat_snprintf(buf, bufsz,
4181  /* TRANS: %s is the output type, like 'shield'
4182  * or 'gold'. */
4183  _("* You pay no %s upkeep for your units.\n"),
4184  qUtf8Printable(or_outputs));
4185  } else {
4186  CATLSTR(buf, bufsz,
4187  _("* You pay no upkeep for your units.\n"));
4188  }
4189  } else if (net_value != world_value) {
4190  double ratio = static_cast<double>(net_value) / world_value;
4191  if (output_type != O_LAST) {
4192  cat_snprintf(
4193  buf, bufsz,
4194  /* TRANS: %s is the output type, like 'shield'
4195  * or 'gold'. */
4196  _("* You pay %.2g times normal %s upkeep for your "
4197  "units.\n"),
4198  ratio, qUtf8Printable(and_outputs));
4199  } else {
4200  cat_snprintf(buf, bufsz,
4201  _("* You pay %.2g times normal upkeep for your "
4202  "units.\n"),
4203  ratio);
4204  }
4205  } // else this effect somehow has no effect; keep quiet
4206  } // else there was some extra condition making it complicated
4207  break;
4208  case EFT_UNIT_UPKEEP_FREE_PER_CITY:
4209  if (!unittype) {
4210  if (output_type != O_LAST) {
4211  cat_snprintf(buf, bufsz,
4212  /* TRANS: %s is the output type, like 'shield' or
4213  * 'gold'; pluralised in %d but there is currently
4214  * no way to control the singular/plural name of the
4215  * output type; sorry */
4216  PL_("* Each of your cities will avoid paying %d %s"
4217  " upkeep for your units.\n",
4218  "* Each of your cities will avoid paying %d %s"
4219  " upkeep for your units.\n",
4220  peffect->value),
4221  peffect->value, qUtf8Printable(and_outputs));
4222  } else {
4223  cat_snprintf(buf, bufsz,
4224  /* TRANS: Amount is subtracted from upkeep cost
4225  * for each upkeep type. */
4226  PL_("* Each of your cities will avoid paying %d"
4227  " upkeep for your units.\n",
4228  "* Each of your cities will avoid paying %d"
4229  " upkeep for your units.\n",
4230  peffect->value),
4231  peffect->value);
4232  }
4233  } // else too complicated
4234  break;
4235  case EFT_CIVIL_WAR_CHANCE:
4236  if (playerwide) {
4237  cat_snprintf(buf, bufsz,
4238  _("* If you lose your capital,"
4239  " the base chance of civil war is %d%%.\n"),
4240  net_value);
4241  }
4242  break;
4243  case EFT_EMPIRE_SIZE_BASE:
4244  if (playerwide) {
4245  cat_snprintf(buf, bufsz,
4246  PL_("* You can have %d city before an "
4247  "additional unhappy citizen appears in each city "
4248  "due to civilization size.\n",
4249  "* You can have up to %d cities before an "
4250  "additional unhappy citizen appears in each city "
4251  "due to civilization size.\n",
4252  net_value),
4253  net_value);
4254  }
4255  break;
4256  case EFT_EMPIRE_SIZE_STEP:
4257  if (playerwide) {
4258  cat_snprintf(
4259  buf, bufsz,
4260  PL_("* After the first unhappy citizen due to"
4261  " civilization size, for each %d additional city"
4262  " another unhappy citizen will appear.\n",
4263  "* After the first unhappy citizen due to"
4264  " civilization size, for each %d additional cities"
4265  " another unhappy citizen will appear.\n",
4266  net_value),
4267  net_value);
4268  }
4269  break;
4270  case EFT_MAX_RATES:
4271  if (playerwide && game.info.changeable_budget) {
4272  if (net_value < 100) {
4273  cat_snprintf(buf, bufsz,
4274  _("* The maximum rate you can set for science,"
4275  " gold, or luxuries is %d%%.\n"),
4276  net_value);
4277  } else {
4278  CATLSTR(buf, bufsz,
4279  _("* Has unlimited science/gold/luxuries rates.\n"));
4280  }
4281  }
4282  break;
4283  case EFT_MARTIAL_LAW_EACH:
4284  if (playerwide) {
4285  cat_snprintf(buf, bufsz,
4286  PL_("* Your units may impose martial law."
4287  " Each military unit inside a city will force %d"
4288  " unhappy citizen to become content.\n",
4289  "* Your units may impose martial law."
4290  " Each military unit inside a city will force %d"
4291  " unhappy citizens to become content.\n",
4292  peffect->value),
4293  peffect->value);
4294  }
4295  break;
4296  case EFT_MARTIAL_LAW_MAX:
4297  if (playerwide && net_value < 100) {
4298  cat_snprintf(buf, bufsz,
4299  PL_("* A maximum of %d unit in each city can enforce"
4300  " martial law.\n",
4301  "* A maximum of %d units in each city can enforce"
4302  " martial law.\n",
4303  net_value),
4304  net_value);
4305  }
4306  break;
4307  case EFT_RAPTURE_GROW:
4308  if (playerwide && net_value > 0) {
4309  cat_snprintf(buf, bufsz,
4310  _("* You may grow your cities by means of "
4311  "celebrations."));
4312  if (game.info.celebratesize > 1) {
4313  cat_snprintf(
4314  buf, bufsz,
4315  /* TRANS: Preserve leading space. %d should always be
4316  * 2 or greater. */
4317  _(" (Cities below size %d cannot grow in this way.)"),
4318  game.info.celebratesize);
4319  }
4320  cat_snprintf(buf, bufsz, "\n");
4321  }
4322  break;
4323  case EFT_REVOLUTION_UNHAPPINESS:
4324  if (playerwide) {
4325  cat_snprintf(
4326  buf, bufsz,
4327  PL_("* If a city is in disorder for more than %d turn "
4328  "in a row, government will fall into anarchy.\n",
4329  "* If a city is in disorder for more than %d turns "
4330  "in a row, government will fall into anarchy.\n",
4331  net_value),
4332  net_value);
4333  }
4334  break;
4335  case EFT_HAS_SENATE:
4336  if (playerwide && net_value > 0) {
4337  CATLSTR(
4338  buf, bufsz,
4339  _("* Has a senate that may prevent declaration of war.\n"));
4340  }
4341  break;
4342  case EFT_INSPIRE_PARTISANS:
4343  if (playerwide && net_value > 0) {
4344  CATLSTR(buf, bufsz,
4345  _("* Allows partisans when cities are taken by the "
4346  "enemy.\n"));
4347  }
4348  break;
4349  case EFT_HAPPINESS_TO_GOLD:
4350  if (playerwide && net_value > 0) {
4351  CATLSTR(buf, bufsz,
4352  _("* Buildings that normally confer bonuses against"
4353  " unhappiness will instead give gold.\n"));
4354  }
4355  break;
4356  case EFT_FANATICS:
4357  if (playerwide && net_value > 0) {
4358  QVector<QString> fanatics;
4359 
4360  unit_type_iterate(putype)
4361  {
4362  if (utype_has_flag(putype, UTYF_FANATIC)) {
4363  fanatics.append(utype_name_translation(putype));
4364  }
4365  }
4367  cat_snprintf(buf, bufsz,
4368  // TRANS: %s is list of unit types separated by 'or'
4369  _("* Pays no upkeep for %s.\n"),
4370  qUtf8Printable(strvec_to_or_list(fanatics)));
4371  }
4372  break;
4373  case EFT_NO_UNHAPPY:
4374  if (playerwide && net_value > 0) {
4375  CATLSTR(buf, bufsz, _("* Has no unhappy citizens.\n"));
4376  }
4377  break;
4378  case EFT_VETERAN_BUILD: {
4379  int conditions = 0;
4380  if (unitclass) {
4381  conditions++;
4382  }
4383  if (unittype) {
4384  conditions++;
4385  }
4386  if (unit_type_flag_id_is_valid(unitflag)) {
4387  conditions++;
4388  }
4389  if (conditions > 1) {
4390  /* More than one requirement on units, too complicated for us
4391  * to describe. */
4392  break;
4393  }
4394  if (unitclass) {
4395  /* FIXME: account for multiple veteran levels, or negative
4396  * values. This might lie for complicated rulesets! */
4397  cat_snprintf(buf, bufsz,
4398  // TRANS: %s is a unit class
4399  Q_("?unitclass:* New %s units will be veteran.\n"),
4400  uclass_name_translation(unitclass));
4401  } else if (unit_type_flag_id_is_valid(unitflag)) {
4402  // FIXME: same problems as unitclass
4403  cat_snprintf(buf, bufsz,
4404  // TRANS: %s is a (translatable) unit type flag
4405  Q_("?unitflag:* New %s units will be veteran.\n"),
4406  unit_type_flag_id_translated_name(unitflag));
4407  } else if (unittype != nullptr) {
4408  if (world_value_valid && net_value > 0) {
4409  /* Here we can be specific about veteran level, and get
4410  * net value correct. */
4411  int maxlvl = utype_veteran_system(unittype)->levels - 1;
4412  const struct veteran_level *vlevel =
4413  utype_veteran_level(unittype, MIN(net_value, maxlvl));
4414  cat_snprintf(buf, bufsz,
4415  /* TRANS: "* New Partisan units will have the rank
4416  * of elite." */
4417  Q_("?unittype:* New %s units will have the rank "
4418  "of %s.\n"),
4419  utype_name_translation(unittype),
4420  name_translation_get(&vlevel->name));
4421  } // else complicated
4422  } else {
4423  // No extra criteria.
4424  // FIXME: same problems as above
4425  cat_snprintf(buf, bufsz, _("* New units will be veteran.\n"));
4426  }
4427  } break;
4428  case EFT_OUTPUT_PENALTY_TILE:
4429  if (world_value_valid) {
4430  cat_snprintf(
4431  buf, bufsz,
4432  /* TRANS: %s is list of output types, with 'or';
4433  * pluralised in %d but of course the output types
4434  * can't be pluralised; sorry */
4435  PL_("* Each worked tile that gives more than %d %s will"
4436  " suffer a -1 penalty, unless the city working it"
4437  " is celebrating.",
4438  "* Each worked tile that gives more than %d %s will"
4439  " suffer a -1 penalty, unless the city working it"
4440  " is celebrating.",
4441  net_value),
4442  net_value, qUtf8Printable(or_outputs));
4443  if (game.info.celebratesize > 1) {
4444  cat_snprintf(buf, bufsz,
4445  /* TRANS: Preserve leading space. %d should always
4446  * be 2 or greater. */
4447  _(" (Cities below size %d will not celebrate.)"),
4448  game.info.celebratesize);
4449  }
4450  cat_snprintf(buf, bufsz, "\n");
4451  }
4452  break;
4453  case EFT_OUTPUT_INC_TILE_CELEBRATE:
4454  cat_snprintf(buf, bufsz,
4455  // TRANS: %s is list of output types, with 'or'
4456  PL_("* Each worked tile with at least 1 %s will yield"
4457  " %d more of it while the city working it is"
4458  " celebrating.",
4459  "* Each worked tile with at least 1 %s will yield"
4460  " %d more of it while the city working it is"
4461  " celebrating.",
4462  peffect->value),
4463  qUtf8Printable(or_outputs), peffect->value);
4464  if (game.info.celebratesize > 1) {
4465  cat_snprintf(buf, bufsz,
4466  /* TRANS: Preserve leading space. %d should always be
4467  * 2 or greater. */
4468  _(" (Cities below size %d will not celebrate.)"),
4469  game.info.celebratesize);
4470  }
4471  cat_snprintf(buf, bufsz, "\n");
4472  break;
4473  case EFT_OUTPUT_INC_TILE:
4474  cat_snprintf(buf, bufsz,
4475  // TRANS: %s is list of output types, with 'or'
4476  PL_("* Each worked tile with at least 1 %s will yield"
4477  " %d more of it.\n",
4478  "* Each worked tile with at least 1 %s will yield"
4479  " %d more of it.\n",
4480  peffect->value),
4481  qUtf8Printable(or_outputs), peffect->value);
4482  break;
4483  case EFT_OUTPUT_BONUS:
4484  case EFT_OUTPUT_BONUS_2:
4485  // FIXME: makes most sense iff world_value == 0
4486  cat_snprintf(buf, bufsz,
4487  // TRANS: %s is list of output types, with 'and'
4488  _("* %s production is increased %d%%.\n"),
4489  qUtf8Printable(and_outputs), peffect->value);
4490  break;
4491  case EFT_OUTPUT_WASTE:
4492  if (world_value_valid) {
4493  if (net_value > 30) {
4494  cat_snprintf(buf, bufsz,
4495  // TRANS: %s is list of output types, with 'and'
4496  _("* %s production will suffer massive losses.\n"),
4497  qUtf8Printable(and_outputs));
4498  } else if (net_value >= 15) {
4499  cat_snprintf(buf, bufsz,
4500  // TRANS: %s is list of output types, with 'and'
4501  _("* %s production will suffer some losses.\n"),
4502  qUtf8Printable(and_outputs));
4503  } else if (net_value > 0) {
4504  cat_snprintf(buf, bufsz,
4505  // TRANS: %s is list of output types, with 'and'
4506  _("* %s production will suffer a small amount "
4507  "of losses.\n"),
4508  qUtf8Printable(and_outputs));
4509  }
4510  }
4511  break;
4512  case EFT_HEALTH_PCT:
4513  if (playerwide) {
4514  if (peffect->value > 0) {
4515  CATLSTR(buf, bufsz,
4516  _("* Increases the chance of plague"
4517  " within your cities.\n"));
4518  } else if (peffect->value < 0) {
4519  CATLSTR(buf, bufsz,
4520  _("* Decreases the chance of plague"
4521  " within your cities.\n"));
4522  }
4523  }
4524  break;
4525  case EFT_OUTPUT_WASTE_BY_REL_DISTANCE:
4526  /* Semi-arbitrary scaling to get likely ruleset values in roughly
4527  * the same range as WASTE_BY_DISTANCE */
4528  // FIXME: use different wording?
4529  net_value = (net_value + 39) / 40; // round up
4530  fc__fallthrough; // fall through to:
4531  case EFT_OUTPUT_WASTE_BY_DISTANCE:
4532  if (world_value_valid) {
4533  if (net_value >= 300) {
4534  cat_snprintf(buf, bufsz,
4535  // TRANS: %s is list of output types, with 'and'
4536  _("* %s losses will increase quickly"
4537  " with distance from capital.\n"),
4538  qUtf8Printable(and_outputs));
4539  } else if (net_value >= 200) {
4540  cat_snprintf(buf, bufsz,
4541  // TRANS: %s is list of output types, with 'and'
4542  _("* %s losses will increase"
4543  " with distance from capital.\n"),
4544  qUtf8Printable(and_outputs));
4545  } else if (net_value > 0) {
4546  cat_snprintf(buf, bufsz,
4547  // TRANS: %s is list of output types, with 'and'
4548  _("* %s losses will increase slowly"
4549  " with distance from capital.\n"),
4550  qUtf8Printable(and_outputs));
4551  }
4552  }
4553  break;
4554  case EFT_MIGRATION_PCT:
4555  if (playerwide) {
4556  if (peffect->value > 0) {
4557  CATLSTR(buf, bufsz,
4558  _("* Increases the chance of migration"
4559  " into your cities.\n"));
4560  } else if (peffect->value < 0) {
4561  CATLSTR(buf, bufsz,
4562  _("* Decreases the chance of migration"
4563  " into your cities.\n"));
4564  }
4565  }
4566  break;
4567  case EFT_BORDER_VISION:
4568  if (game.info.borders == BORDERS_ENABLED && playerwide
4569  && net_value > 0) {
4570  CATLSTR(buf, bufsz,
4571  _("* All tiles inside your borders are"
4572  " monitored.\n"));
4573  }
4574  break;
4575  default:
4576  break;
4577  };
4578  }
4579  }
4581 
4582  // Action immunity
4583  action_iterate(act)
4584  {
4585  if (action_by_number(act)->quiet) {
4586  // The ruleset documents this action it self.
4587  continue;
4588  }
4589 
4590  if (action_immune_government(gov, act)) {
4591  cat_snprintf(
4592  buf, bufsz,
4593  /* TRANS: action name ... action target
4594  * ("individual units", etc) */
4595  _("* Makes it impossible to do the action \'%s\'"
4596  " to your %s.\n"),
4597  qUtf8Printable(action_id_name_translation(act)),
4598  _(action_target_kind_name(action_id_get_target_kind(act))));
4599  }
4600  }
4602 
4603  if (user_text && user_text[0] != '\0') {
4604  cat_snprintf(buf, bufsz, "\n%s", user_text);
4605  }
4606 }
4607 
4611 char *helptext_unit_upkeep_str(const struct unit_type *utype)
4612 {
4613  static char buf[128];
4614  char *empty = new char;
4615  int any = 0;
4616  empty[0] = '\0';
4617 
4618  if (!utype) {
4619  qCritical("Unknown unit!");
4620  return empty;
4621  }
4622 
4623  buf[0] = '\0';
4625  {
4626  if (utype->upkeep[o] > 0) {
4627  // TRANS: "2 Food" or ", 1 Shield"
4628  cat_snprintf(buf, sizeof(buf), _("%s%d %s"),
4629  (any > 0 ? Q_("?blistmore:, ") : ""), utype->upkeep[o],
4630  get_output_name(static_cast<Output_type_id>(o)));
4631  any++;
4632  }
4633  }
4635  if (utype->happy_cost > 0) {
4636  // TRANS: "2 Unhappy" or ", 1 Unhappy"
4637  cat_snprintf(buf, sizeof(buf), _("%s%d Unhappy"),
4638  (any > 0 ? Q_("?blistmore:, ") : ""), utype->happy_cost);
4639  any++;
4640  }
4641 
4642  if (any == 0) {
4643  // qstrcpy(buf, _("None"));
4644  fc_snprintf(buf, sizeof(buf), "%d", 0);
4645  }
4646  delete empty;
4647  return buf;
4648 }
4649 
4653 void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation,
4654  const char *user_text)
4655 {
4656  struct universal source = {.value = {.nation = pnation},
4657  .kind = VUT_NATION};
4658  bool print_break = true;
4659 
4660 #define PRINT_BREAK() \
4661  do { \
4662  if (print_break) { \
4663  if (buf[0] != '\0') { \
4664  CATLSTR(buf, bufsz, "\n\n"); \
4665  } \
4666  print_break = false; \
4667  } \
4668  } while (false)
4669 
4670  fc_assert_ret(nullptr != buf && 0 < bufsz);
4671  buf[0] = '\0';
4672 
4673  if (pnation->legend[0] != '\0') {
4674  // Client side legend is stored already translated
4675  cat_snprintf(buf, bufsz, "%s", pnation->legend);
4676  }
4677 
4678  if (pnation->init_government) {
4679  PRINT_BREAK();
4680  cat_snprintf(buf, bufsz, _("Initial government is %s.\n"),
4682  }
4683  if (pnation->init_techs[0] != A_LAST) {
4684  QVector<QString> tech_names;
4685  tech_names.reserve(MAX_NUM_TECH_LIST);
4686 
4687  for (int init_tech : pnation->init_techs) {
4688  if (init_tech == A_LAST) {
4689  break;
4690  }
4691  tech_names.append(
4693  }
4694  QString list = strvec_to_and_list(tech_names);
4695  PRINT_BREAK();
4696  if (game.rgame.global_init_techs[0] != A_LAST) {
4697  cat_snprintf(
4698  buf, bufsz,
4699  // TRANS: %s is an and-separated list of techs
4700  _("Starts with knowledge of %s in addition to the standard "
4701  "starting technologies.\n"),
4702  qUtf8Printable(list));
4703  } else {
4704  cat_snprintf(buf, bufsz,
4705  // TRANS: %s is an and-separated list of techs
4706  _("Starts with knowledge of %s.\n"),
4707  qUtf8Printable(list));
4708  }
4709  }
4710  if (pnation->init_units[0]) {
4711  const struct unit_type *utypes[MAX_NUM_UNIT_LIST];
4712  int count[MAX_NUM_UNIT_LIST];
4713  int i, j, n = 0, total = 0;
4714 
4715  // Count how many of each type there is.
4716  for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
4717  if (!pnation->init_units[i]) {
4718  break;
4719  }
4720  for (j = 0; j < n; j++) {
4721  if (pnation->init_units[i] == utypes[j]) {
4722  count[j]++;
4723  total++;
4724  break;
4725  }
4726  }
4727  if (j == n) {
4728  utypes[n] = pnation->init_units[i];
4729  count[n] = 1;
4730  total++;
4731  n++;
4732  }
4733  }
4734  // Construct the list of unit types and counts.
4735  QVector<QString> utype_names;
4736  utype_names.reserve(MAX_NUM_UNIT_LIST);
4737 
4738  for (i = 0; i < n; i++) {
4739  if (count[i] > 1) {
4740  /* TRANS: a unit type followed by a count. For instance,
4741  * "Fighter (2)" means two Fighters. Count is never 1.
4742  * Used in a list. */
4743  utype_names.append(QString(_("%1 (%2)"))
4744  .arg(utype_name_translation(utypes[i]),
4745  QString::number(count[i])));
4746  } else {
4747  utype_names.append(
4748  QStringLiteral("%s").arg(utype_name_translation(utypes[i])));
4749  }
4750  }
4751  QString list = strvec_to_and_list(utype_names);
4752 
4753  PRINT_BREAK();
4754  cat_snprintf(buf, bufsz,
4755  /* TRANS: %s is an and-separated list of unit types
4756  * possibly with counts. Plurality is in total number of
4757  * units represented. */
4758  PL_("Starts with the following additional unit: %s.\n",
4759  "Starts with the following additional units: %s.\n",
4760  total),
4761  qUtf8Printable(list));
4762  }
4763  if (pnation->init_buildings[0] != B_LAST) {
4764  QVector<QString> impr_names;
4765  impr_names.reserve(MAX_NUM_BUILDING_LIST);
4766 
4767  for (int init_building : pnation->init_buildings) {
4768  if (init_building == B_LAST) {
4769  break;
4770  }
4771  impr_names.append(improvement_name_translation(
4772  improvement_by_number(init_building)));
4773  }
4774  QString list = strvec_to_and_list(impr_names);
4775  PRINT_BREAK();
4776  if (game.rgame.global_init_buildings[0] != B_LAST) {
4777  cat_snprintf(buf, bufsz,
4778  // TRANS: %s is an and-separated list of improvements
4779  _("First city will get %s for free in addition to the "
4780  "standard improvements.\n"),
4781  qUtf8Printable(list));
4782  } else {
4783  cat_snprintf(buf, bufsz,
4784  // TRANS: %s is an and-separated list of improvements
4785  _("First city will get %s for free.\n"),
4786  qUtf8Printable(list));
4787  }
4788  }
4789 
4790  if (buf[0] != '\0') {
4791  CATLSTR(buf, bufsz, "\n");
4792  }
4793  insert_allows(&source, buf + qstrlen(buf), bufsz - qstrlen(buf), "");
4794 
4795  if (user_text && user_text[0] != '\0') {
4796  if (buf[0] != '\0') {
4797  CATLSTR(buf, bufsz, "\n");
4798  }
4799  CATLSTR(buf, bufsz, user_text);
4800  }
4801 #undef PRINT_BREAK
4802 }
bool action_removes_extra(const struct action *paction, const struct extra_type *pextra)
Returns TRUE iff the specified action can remove the specified extra.
Definition: actions.cpp:1732
enum action_actor_kind action_get_actor_kind(const struct action *paction)
Get the actor kind of an action.
Definition: actions.cpp:1188
void action_list_end(action_id *act_list, int size)
Terminate an action list of the specified size.
Definition: actions.cpp:5881
enum action_battle_kind action_get_battle_kind(const struct action *pact)
Get the battle kind that can prevent an action.
Definition: actions.cpp:1219
enum action_sub_target_kind action_get_sub_target_kind(const struct action *paction)
Get the sub target kind of an action.
Definition: actions.cpp:1209
bool action_immune_government(struct government *gov, action_id act)
Will a player with the government gov be immune to the action act?
Definition: actions.cpp:5687
int action_dice_roll_initial_odds(const struct action *paction)
Returns the initial odds of an action not failing its dice roll.
Definition: actions.cpp:5580
void action_list_add_all_by_result(action_id *act_list, int *position, enum action_result result)
Add all actions with the specified result to the specified action list starting at the specified posi...
Definition: actions.cpp:5898
const QString action_id_name_translation(action_id act_id)
Get the action name used when displaying the action in the UI.
Definition: actions.cpp:1373
struct action * action_by_number(action_id act_id)
Return the action with the given id.
Definition: actions.cpp:1149
bool action_has_result(const struct action *paction, enum action_result result)
Returns TRUE iff performing the specified action has the specified result.
Definition: actions.cpp:1248
bool action_creates_extra(const struct action *paction, const struct extra_type *pextra)
Returns TRUE iff the specified action can create the specified extra.
Definition: actions.cpp:1649
struct action_enabler_list * action_enablers_for_action(action_id action)
Get all enablers for an action in the current ruleset.
Definition: actions.cpp:1884
#define action_id_univs_not_blocking(act_id, act_uni, tgt_uni)
Definition: actions.h:754
#define action_auto_perf_iterate_end
Definition: actions.h:479
#define action_list_iterate_end
Definition: actions.h:413
#define action_list_iterate(_act_list_, _act_id_)
Definition: actions.h:401
#define action_enabler_list_iterate_end
Definition: actions.h:376
#define action_id_get_role(act_id)
Definition: actions.h:580
#define ACTION_DISTANCE_UNLIMITED
Definition: actions.h:279
#define action_iterate_end
Definition: actions.h:383
#define MAX_NUM_ACTIONS
Definition: actions.h:223
#define action_id_would_be_blocked_by(blocked_id, blocker_id)
Definition: actions.h:575
#define action_enabler_list_iterate(action_enabler_list, aenabler)
Definition: actions.h:374
#define action_iterate(_act_)
Definition: actions.h:378
#define action_id_get_target_kind(act_id)
Definition: actions.h:522
#define action_auto_perf_iterate(_act_perf_)
Definition: actions.h:468
#define ACTION_ODDS_PCT_DICE_ROLL_NA
Definition: actions.h:739
QString strvec_to_and_list(const QVector< QString > &psv)
Definition: astring.cpp:43
QString strvec_to_or_list(const QVector< QString > &psv)
Definition: astring.cpp:20
bool base_has_flag(const struct base_type *pbase, enum base_flag_id flag)
Check if base provides effect.
Definition: base.cpp:24
bool territory_claiming_base(const struct base_type *pbase)
Does this base type claim territory?
Definition: base.cpp:196
bool is_any_set(QBitArray &ba)
Definition: bitvector.cpp:112
#define BV_CLR_ALL_FROM(vec_to, vec_from)
Definition: bitvector.h:92
#define BV_CLR_ALL(bv)
Definition: bitvector.h:62
#define BV_SET(bv, bit)
Definition: bitvector.h:44
bool BV_ISSET(const BV &bv, int bit)
Definition: bitvector.h:37
#define BV_ISSET_ANY(vec)
Definition: bitvector.h:76
struct output_type * get_output_type(Output_type_id output)
Return the output type for this index.
Definition: city.cpp:611
const char * get_output_name(Output_type_id output)
Return a translated name for the output type.
Definition: city.cpp:602
#define output_type_iterate(output)
Definition: city.h:764
#define output_type_iterate_end
Definition: city.h:771
char * utypes
Definition: comments.cpp:31
#define MAX_LEN_PACKET
Definition: connection.h:41
static void road(QVariant data1, QVariant data2)
Action "Build Road" for choice dialog.
Definition: dialogs.cpp:2374
static void cultivate(QVariant data1, QVariant data2)
Action "Cultivate" for choice dialog.
Definition: dialogs.cpp:2304
static void plant(QVariant data1, QVariant data2)
Action "Plant" for choice dialog.
Definition: dialogs.cpp:2312
int get_target_bonus_effects(struct effect_list *plist, const struct player *target_player, const struct player *other_player, const struct city *target_city, const struct impr_type *target_building, const struct tile *target_tile, const struct unit *target_unit, const struct unit_type *target_unittype, const struct output_type *target_output, const struct specialist *target_specialist, const struct action *target_action, enum effect_type effect_type, enum vision_layer vision_layer, enum national_intelligence nintel)
Returns the effect bonus of a given type for any target.
Definition: effects.cpp:611
struct effect_list * effects[EFT_COUNT]
Definition: effects.cpp:102
struct @19::@20 reqs
QString effect_type_unit_text(effect_type type, int value)
Returns a string describing an effect value as interpreted in the context of an effect_type,...
Definition: effects.cpp:1058
void get_effect_req_text(const struct effect *peffect, char *buf, size_t buf_len)
Make user-friendly text for the source.
Definition: effects.cpp:1011
struct effect_list * get_req_source_effects(struct universal *psource)
Get a list of effects with this requirement source.
Definition: effects.cpp:132
const effect_list * get_effects()
Get a list of all effects.
Definition: effects.cpp:117
int effect_value_from_universals(enum effect_type type, struct universal *unis, size_t n_unis)
Return the base value of a given effect that can always be expected from just the sources in the list...
Definition: effects.cpp:368
int effect_cumulative_max(enum effect_type type, struct universal *for_uni)
Get the maximum effect value in this ruleset for the universal (that is, the sum of all positive effe...
Definition: effects.cpp:307
bool building_has_effect(const struct impr_type *pimprove, enum effect_type effect_type)
Returns TRUE if the building has any effect bonuses of the given type.
Definition: effects.cpp:504
#define effect_list_iterate_end
Definition: effects.h:349
#define effect_list_iterate(effect_list, peffect)
Definition: effects.h:347
bool is_extra_caused_by_worker_action(const struct extra_type *pextra)
Is the extra caused by some kind of worker action?
Definition: extras.cpp:931
bool extra_has_flag(const struct extra_type *pextra, enum extra_flag_id flag)
Check if extra has given flag.
Definition: extras.cpp:779
const char * extra_name_translation(const struct extra_type *pextra)
Return the (translated) name of the extra type.
Definition: extras.cpp:165
const char * extra_flag_helptxt(enum extra_flag_id id)
Return the (untranslated) help text of the user extra flag.
Definition: extras.cpp:892
bool is_extra_removed_by(const struct extra_type *pextra, enum extra_rmcause rmcause)
Is given cause one of the removal causes for the given extra?
Definition: extras.cpp:307
bool is_native_extra_to_uclass(const struct extra_type *pextra, const struct unit_class *pclass)
Is extra native to unit class?
Definition: extras.cpp:761
#define extra_type_iterate(_p)
Definition: extras.h:279
#define extra_type_iterate_end
Definition: extras.h:285
#define extra_type_by_rmcause_iterate_end
Definition: extras.h:330
#define is_extra_caused_by(e, c)
Definition: extras.h:182
#define extra_type_by_rmcause_iterate(_rmcause, _extra)
Definition: extras.h:324
#define extra_road_get(_e_)
Definition: extras.h:171
#define extra_type_by_cause_iterate_end
Definition: extras.h:307
#define EF_LAST_USER_FLAG
Definition: extras.h:69
#define extra_type_by_cause_iterate(_cause, _extra)
Definition: extras.h:299
#define MAX_NUM_BUILDING_LIST
Definition: fc_types.h:38
int Impr_type_id
Definition: fc_types.h:293
int Unit_Class_id
Definition: fc_types.h:333
int action_id
Definition: fc_types.h:306
#define CASUS_BELLI_OUTRAGE
Definition: fc_types.h:399
#define CASUS_BELLI_VICTIM
Definition: fc_types.h:393
#define MAX_NUM_UNIT_LIST
Definition: fc_types.h:37
#define MAX_LEN_NAME
Definition: fc_types.h:61
#define MAX_NUM_TECH_LIST
Definition: fc_types.h:36
@ O_SHIELD
Definition: fc_types.h:86
@ O_FOOD
Definition: fc_types.h:85
@ O_TRADE
Definition: fc_types.h:87
@ O_LAST
Definition: fc_types.h:91
@ BORDERS_ENABLED
Definition: fc_types.h:864
#define UCL_LAST
Definition: fc_types.h:332
enum output_type_id Output_type_id
Definition: fc_types.h:295
size_t get_internal_string_length(const char *text)
Return the length, in characters, of the string.
Definition: fciconv.cpp:153
#define Q_(String)
Definition: fcintl.h:53
#define PL_(String1, String2, n)
Definition: fcintl.h:54
#define _(String)
Definition: fcintl.h:50
#define N_(String)
Definition: fcintl.h:52
struct civ_game game
Definition: game.cpp:47
std::vector< government > governments
Definition: government.cpp:28
const char * government_name_translation(const struct government *pgovern)
Return the (translated) name of the given government.
Definition: government.cpp:136
#define CATLSTR(_b, _s, _t)
This module is for generic handling of help data, independent of gui considerations.
Definition: helpdata.cpp:55
static int help_item_compar(const struct help_item *v1, const struct help_item *v2)
For help_list_sort(); sort by topic via compare_strings() (sort topics with more leading spaces after...
Definition: helpdata.cpp:669
static void extra_bonus_for_terrain(struct extra_type *pextra, struct terrain *pterrain, int *bonus)
Calculate any fixed food/prod/trade bonus that 'pextra' will always add to terrain type,...
Definition: helpdata.cpp:3283
static void insert_allows(struct universal *psource, char *buf, size_t bufsz, const char *prefix)
Generate text for what this requirement source allows.
Definition: helpdata.cpp:592
static bool insert_generated_text(char *outbuf, size_t outlen, const char *name)
Insert generated text for the helpdata "name".
Definition: helpdata.cpp:167
static void insert_allows_single(struct universal *psource, const requirement_vector *psubjreqs, const char *subjstr, const char *const *strs, char *buf, size_t bufsz, const char *prefix)
Append text to 'buf' if the given requirements list 'subjreqs' contains 'psource',...
Definition: helpdata.cpp:508
void helptext_advance(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, int i, const nation_set *nations_to_show)
Append misc dynamic text for advance/technology.
Definition: helpdata.cpp:2886
static const char *const help_type_names[]
Definition: helpdata.cpp:58
void helptext_government(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct government *gov)
Append text for government.
Definition: helpdata.cpp:3942
void helptext_extra(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct extra_type *pextra)
Append misc dynamic text for extras.
Definition: helpdata.cpp:3395
char * helptext_unit(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, const struct unit_type *utype, const nation_set *nations_to_show)
Append misc dynamic text for units.
Definition: helpdata.cpp:1639
static void format_change_terrain_string(char *buf, int bufsize, enum gen_action act, int time, terrain *from, const terrain *to)
Formats the number of turns to transform between terrains so it can be included in the terrain altera...
Definition: helpdata.cpp:149
const char * helptext_road_bonus_str(const struct terrain *pterrain, const struct road_type *proad)
Return a textual representation of the F/P/T bonus a road provides to a terrain if supplied,...
Definition: helpdata.cpp:3229
helpList * help_nodes
Definition: helpdata.cpp:65
const struct help_item * get_help_item_spec(const char *name, enum help_page_type htype, int *pos)
The following few functions are essentially wrappers for the help_nodes help_list.
Definition: helpdata.cpp:1118
char * helptext_unit_upkeep_str(const struct unit_type *utype)
Returns pointer to static string with eg: "1 shield, 1 unhappy".
Definition: helpdata.cpp:4611
char * helptext_building(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, const struct impr_type *pimprove, const nation_set *nations_to_show)
FIXME: Also, in principle these could be auto-generated once, inserted into pitem->text,...
Definition: helpdata.cpp:1181
static bool insert_veteran_help(char *outbuf, size_t outlen, const struct veteran_system *veteran, const char *intro, const char *nolevels)
Insert fixed-width table describing veteran system.
Definition: helpdata.cpp:92
QList< const struct help_item * > helpList
Definition: helpdata.cpp:64
void helptext_goods(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct goods_type *pgood)
Append misc dynamic text for goods.
Definition: helpdata.cpp:3853
#define PRINT_BREAK()
struct help_item * new_help_item(help_page_type type)
Allocate and initialize new help item.
Definition: helpdata.cpp:656
void free_help_texts()
Free all allocations associated with help_nodes.
Definition: helpdata.cpp:73
const char * helptext_extra_for_terrain_str(struct extra_type *pextra, struct terrain *pterrain, enum unit_activity act)
Return a brief description specific to the extra and terrain, when extra is built by cause 'act'.
Definition: helpdata.cpp:3361
void boot_help_texts(const nation_set *nations_to_show, help_item *tileset_help)
pplayer may be nullptr.
Definition: helpdata.cpp:682
static bool utype_may_do_escape_action(const struct unit_type *utype)
Returns TRUE iff the specified unit type is able to perform an action that allows it to escape to the...
Definition: helpdata.cpp:1602
void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct specialist *pspec)
Append misc dynamic text for specialists.
Definition: helpdata.cpp:3905
void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct terrain *pterrain)
Append text for terrain.
Definition: helpdata.cpp:3115
void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation, const char *user_text)
Returns nation legend and characteristics.
Definition: helpdata.cpp:4653
#define HELP_TILESET_ITEM
Definition: helpdata.h:50
#define HELP_RULESET_ITEM
Definition: helpdata.h:49
help_page_type
Definition: helpdata.h:20
@ HELP_ANY
Definition: helpdata.h:21
@ HELP_MULTIPLIER
Definition: helpdata.h:35
@ HELP_TERRAIN
Definition: helpdata.h:27
@ HELP_EXTRA
Definition: helpdata.h:28
@ HELP_NATIONS
Definition: helpdata.h:34
@ HELP_IMPROVEMENT
Definition: helpdata.h:24
@ HELP_UNIT
Definition: helpdata.h:23
@ HELP_SPECIALIST
Definition: helpdata.h:30
@ HELP_GOVERNMENT
Definition: helpdata.h:31
@ HELP_EFFECT
Definition: helpdata.h:36
@ HELP_GOODS
Definition: helpdata.h:29
@ HELP_WONDER
Definition: helpdata.h:25
@ HELP_TECH
Definition: helpdata.h:26
@ HELP_RULESET
Definition: helpdata.h:32
@ HELP_TEXT
Definition: helpdata.h:22
@ HELP_TILESET
Definition: helpdata.h:33
const struct impr_type * valid_improvement(const struct impr_type *pimprove)
Returns pointer when the improvement_type "exists" in this game, returns nullptr otherwise.
struct impr_type * improvement_by_number(const Impr_type_id id)
Returns the improvement type for the given index/ID.
bool is_great_wonder(const struct impr_type *pimprove)
Is this building a great wonder?
bool improvement_has_flag(const struct impr_type *pimprove, enum impr_flag_id flag)
Return TRUE if the impr has this flag otherwise FALSE.
const char * improvement_name_translation(const struct impr_type *pimprove)
Return the (translated) name of the given improvement.
bool is_small_wonder(const struct impr_type *pimprove)
Is this building a small wonder?
#define improvement_iterate_end
Definition: improvement.h:199
#define improvement_iterate(_p)
Definition: improvement.h:193
#define B_LAST
Definition: improvement.h:33
const char * name
Definition: inputfile.cpp:118
#define fc_assert_ret(condition)
Definition: log.h:112
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
struct terrain_misc terrain_control
Definition: map.cpp:40
const char * move_points_text_full(int mp, bool reduce, const char *prefix, const char *none, bool align)
Render positive movement points as text, including fractional movement points, scaled by SINGLE_MOVE.
Definition: movement.cpp:797
bool is_native_to_class(const struct unit_class *punitclass, const struct terrain *pterrain, const bv_extras *extras)
This terrain is native to unit class.
Definition: movement.cpp:290
bool can_unit_type_transport(const struct unit_type *transporter, const struct unit_class *transported)
Return TRUE iff transporter type has ability to transport transported class.
Definition: movement.cpp:698
bool can_attack_non_native(const struct unit_type *utype)
This unit can attack non-native tiles (eg.
Definition: movement.cpp:164
const char * move_points_text(int mp, bool reduce)
Simple version of move_points_text_full() – render positive movement points as text without any prefi...
Definition: movement.cpp:856
#define MAX_MOVE_FRAGS
Definition: movement.h:20
#define multipliers_iterate(_mul_)
Definition: multipliers.h:45
#define multipliers_iterate_end
Definition: multipliers.h:51
static const char * name_translation_get(const struct name_translation *ptrans)
std::vector< nation_type > nations
Definition: nation.cpp:38
bool nation_is_in_set(const struct nation_type *pnation, const struct nation_set *pset)
Check if the given nation is in a given set.
Definition: nation.cpp:712
const char * nation_plural_translation(const struct nation_type *pnation)
Return the (translated) plural noun of the given nation.
Definition: nation.cpp:136
#define MAX_LEN_MSG
Definition: packets.h:37
int len
Definition: packhand.cpp:127
const char * diplrel_name_translation(int value)
Return the translated name of the given diplomatic relation.
Definition: player.cpp:1538
struct section_file * secfile_load(const QString &filename, bool allow_duplicates)
Create a section file from a file.
Definition: registry.cpp:21
const char * secfile_error()
Returns the last error which occurred in a string.
const char * section_name(const struct section *psection)
Returns the section name.
void secfile_destroy(struct section_file *secfile)
Free a section file.
void secfile_check_unused(const struct section_file *secfile)
Print log messages for any entries in the file which have not been looked up – ie,...
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
Lookup a string value in the secfile.
const char ** secfile_lookup_str_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
Lookup a string vector in the secfile.
struct section_list * secfile_sections_by_name_prefix(const struct section_file *secfile, const char *prefix)
Returns the list of sections which match the name prefix.
#define section_list_iterate(seclist, psection)
Definition: registry_ini.h:46
#define section_list_iterate_end
Definition: registry_ini.h:48
bool req_text_insert_nl(char *buf, size_t bufsz, struct player *pplayer, const struct requirement *preq, enum rt_verbosity verb, const char *prefix)
Append text for the requirement.
Definition: reqtext.cpp:2892
@ VERB_DEFAULT
Definition: reqtext.h:15
bool universal_is_relevant_to_requirement(const struct requirement *req, const struct universal *source)
Returns TRUE iff the specified universal is relevant to fulfilling the specified requirement.
bool universal_fulfills_requirements(bool check_necessary, const struct requirement_vector *reqs, const struct universal *source)
Will the universal 'source' fulfill the requirements in the list? If 'check_necessary' is FALSE: are ...
const char * universal_name_translation(const struct universal *psource, char *buf, size_t bufsz)
Make user-friendly text for the source.
bool are_universals_equal(const struct universal *psource1, const struct universal *psource2)
Return TRUE iff the two sources are equivalent.
#define requirement_fulfilled_by_unit_type(_ut_, _rqs_)
Definition: requirements.h:306
#define requirement_fulfilled_by_improvement(_imp_, _rqs_)
Definition: requirements.h:292
#define requirement_vector_iterate_end
Definition: requirements.h:80
#define requirement_vector_iterate(req_vec, preq)
Definition: requirements.h:78
int research_goal_unknown_techs(const struct research *presearch, Tech_type_id goal)
Returns the number of technologies the player need to research to get the goal technology.
Definition: research.cpp:745
bool research_invention_reachable(const struct research *presearch, const Tech_type_id tech)
Returns TRUE iff the given tech is ever reachable via research by the players sharing the research by...
Definition: research.cpp:659
struct research * research_get(const struct player *pplayer)
Returns the research structure associated with the player.
Definition: research.cpp:110
int research_goal_bulbs_required(const struct research *presearch, Tech_type_id goal)
Function to determine cost (in bulbs) of reaching goal technology.
Definition: research.cpp:767
int research_total_bulbs_required(const struct research *presearch, Tech_type_id tech, bool loss_value)
Function to determine cost for technology.
Definition: research.cpp:855
enum tech_state research_invention_state(const struct research *presearch, Tech_type_id tech)
Returns state of the tech for current research.
Definition: research.cpp:609
bool road_has_flag(const struct road_type *proad, enum road_flag_id flag)
Check if road provides effect.
Definition: road.cpp:367
bool road_provides_move_bonus(const struct road_type *proad)
Does road type provide move bonus.
Definition: road.cpp:437
server_setting_id server_setting_by_name(const char *name)
Returns the server setting with the specified name.
bool server_setting_value_bool_get(server_setting_id id)
Returns the value of the server setting with the specified id.
struct setting_list * level[OLEVELS_NUM]
Definition: settings.cpp:167
const QStringList & get_data_dirs()
Returns a list of data directory paths, in the order in which they should be searched.
Definition: shared.cpp:533
QString fileinfoname(const QStringList &dirs, const QString &filename)
Returns a filename to access the specified file from a directory by searching all specified directori...
Definition: shared.cpp:661
#define ARRAY_SIZE(x)
Definition: shared.h:79
#define MIN(x, y)
Definition: shared.h:49
#define MAX(x, y)
Definition: shared.h:48
struct specialist * specialist_by_number(const Specialist_type_id id)
Return the specialist pointer for the given number.
Definition: specialist.cpp:92
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
struct action::@10::@11 is_unit
action_id id
Definition: actions.h:306
int max_distance
Definition: actions.h:320
bool quiet
Definition: actions.h:327
enum action_result result
Definition: actions.h:308
union action::@10 actor
int min_distance
Definition: actions.h:320
Definition: tech.h:113
struct requirement_vector research_reqs
Definition: tech.h:125
struct tech_class * tclass
Definition: tech.h:118
QVector< QString > * helptext
Definition: tech.h:128
Definition: base.h:43
int vision_main_sq
Definition: base.h:47
enum base_gui_type gui_type
Definition: base.h:45
int vision_invis_sq
Definition: base.h:48
int vision_subs_sq
Definition: base.h:49
struct packet_ruleset_control control
Definition: game.h:74
char * ruleset_summary
Definition: game.h:75
struct civ_game::@28::@31 client
int global_init_techs[MAX_NUM_TECH_LIST]
Definition: game.h:98
struct packet_game_info info
Definition: game.h:80
int global_init_buildings[MAX_NUM_BUILDING_LIST]
Definition: game.h:99
struct packet_scenario_info scenario
Definition: game.h:78
char * ruleset_description
Definition: game.h:76
struct civ_game::@27 rgame
struct veteran_system * veteran
Definition: game.h:91
struct road_type * road
Definition: extras.h:135
int removal_time
Definition: extras.h:104
bool generated
Definition: extras.h:101
struct requirement_vector rmreqs
Definition: extras.h:91
Tech_type_id visibility_req
Definition: extras.h:120
struct extra_type::@22 data
int removal_time_factor
Definition: extras.h:105
struct requirement_vector reqs
Definition: extras.h:90
bool buildable
Definition: extras.h:100
enum extra_unit_seen_type eus
Definition: extras.h:111
int defense_bonus
Definition: extras.h:107
int build_time
Definition: extras.h:102
QVector< QString > * helptext
Definition: extras.h:130
struct base_type * base
Definition: extras.h:134
struct requirement_vector reqs
Definition: traderoutes.h:185
int onetime_pct
Definition: traderoutes.h:189
QVector< QString > * helptext
Definition: traderoutes.h:193
struct requirement_vector reqs
Definition: government.h:41
QVector< QString > * helptext
Definition: government.h:44
char * topic
Definition: helpdata.h:71
enum help_page_type type
Definition: helpdata.h:72
char * text
Definition: helpdata.h:71
struct requirement_vector obsolete_by
Definition: improvement.h:67
struct requirement_vector reqs
Definition: improvement.h:66
QVector< QString > * helptext
Definition: improvement.h:73
Functions for handling the nations.
Definition: nation.cpp:33
int init_buildings[MAX_NUM_BUILDING_LIST]
Definition: nation.h:102
std::array< unit_type *, MAX_NUM_UNIT_LIST > init_units
Definition: nation.h:105
char * legend
Definition: nation.h:86
int init_techs[MAX_NUM_TECH_LIST]
Definition: nation.h:101
government * init_government
Definition: nation.h:103
bool harvested
Definition: city.h:235
const char * name
Definition: city.h:233
Definition: player.h:231
Definition: road.h:54
int tile_bonus[O_LAST]
Definition: road.h:61
int tile_incr_const[O_LAST]
Definition: road.h:59
int tile_incr[O_LAST]
Definition: road.h:60
int move_cost
Definition: road.h:57
struct requirement_vector reqs
Definition: specialist.h:32
QVector< QString > * helptext
Definition: specialist.h:34
int road_time
Definition: terrain.h:194
int irrigation_food_incr
Definition: terrain.h:201
QVector< QString > * helptext
Definition: terrain.h:242
int output[O_LAST]
Definition: terrain.h:188
int road_output_incr_pct[O_LAST]
Definition: terrain.h:192
int base_time
Definition: terrain.h:193
int mining_shield_incr
Definition: terrain.h:205
QVector< QString > * helptext
Definition: unittype.h:132
int transport_capacity
Definition: unittype.h:488
int pop_cost
Definition: unittype.h:477
struct requirement_vector build_reqs
Definition: unittype.h:485
int defense_strength
Definition: unittype.h:480
QVector< QString > * helptext
Definition: unittype.h:534
int paratroopers_range
Definition: unittype.h:506
int convert_time
Definition: unittype.h:496
int city_size
Definition: unittype.h:515
struct veteran_system * veteran
Definition: unittype.h:509
const struct unit_type * obsoleted_by
Definition: unittype.h:494
bv_unit_classes targets
Definition: unittype.h:524
enum vision_layer vlayer
Definition: unittype.h:532
struct advance * require_advance
Definition: unittype.h:484
int bombard_rate
Definition: unittype.h:512
int upkeep[O_LAST]
Definition: unittype.h:503
bv_unit_classes disembarks
Definition: unittype.h:530
const struct unit_type * converted_to
Definition: unittype.h:495
bv_unit_classes embarks
Definition: unittype.h:527
int happy_cost
Definition: unittype.h:502
struct combat_bonus_list * bonuses
Definition: unittype.h:491
universals_u value
Definition: fc_types.h:739
struct name_translation name
Definition: unittype.h:452
struct veteran_level * definitions
Definition: unittype.h:462
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
size_t fc_strlcpy(char *dest, const char *src, size_t n)
fc_strlcpy() provides utf-8 version of (non-standard) function strlcpy() It is intended as more user-...
Definition: support.cpp:412
int fc_strcasecmp(const char *str0, const char *str1)
Compare strings like strcmp(), but ignoring case.
Definition: support.cpp:89
size_t fc_strlcat(char *dest, const char *src, size_t n)
fc_strlcat() provides utf-8 version of (non-standard) function strlcat() It is intended as more user-...
Definition: support.cpp:448
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 sz_strlcat(dest, src)
Definition: support.h:142
#define fc__fallthrough
Definition: support.h:49
const char * tech_class_name_translation(const struct tech_class *ptclass)
Return the (translated) name of the given tech_class You must not free the return pointer.
Definition: tech.cpp:317
bool advance_has_flag(Tech_type_id tech, enum tech_flag_id flag)
Return TRUE if the tech has this flag otherwise FALSE.
Definition: tech.cpp:198
struct advance * advance_requires(const struct advance *padvance, enum tech_req require)
Accessor for requirements.
Definition: tech.cpp:122
struct advance * advance_by_number(const Tech_type_id atype)
Return the advance for the given advance index.
Definition: tech.cpp:94
const char * tech_flag_helptxt(enum tech_flag_id id)
Return the (untranslated) helptxt of the user tech flag.
Definition: tech.cpp:413
struct advance * valid_advance_by_number(const Tech_type_id id)
Returns pointer when the advance "exists" in this game, returns nullptr otherwise.
Definition: tech.cpp:154
bool techs_have_fixed_costs()
Returns true if the costs for the given technology will stay constant during the game.
Definition: tech.cpp:428
Tech_type_id advance_number(const struct advance *padvance)
Return the advance index.
Definition: tech.cpp:85
const char * advance_name_translation(const struct advance *padvance)
Return the (translated) name of the given advance/technology.
Definition: tech.cpp:274
#define advance_index_iterate_end
Definition: tech.h:226
@ AR_ROOT
Definition: tech.h:104
#define A_FIRST
Definition: tech.h:37
#define A_NONE
Definition: tech.h:36
#define advance_root_req_iterate_end
Definition: tech.h:281
#define A_LAST
Definition: tech.h:38
#define advance_index_iterate(_start, _index)
Definition: tech.h:221
#define advance_root_req_iterate(_goal, _padvance)
Definition: tech.h:277
void init_tech(struct research *research, bool update)
Initializes tech data for the research.
Definition: techtools.cpp:1075
const char * terrain_rule_name(const struct terrain *pterrain)
Return the (untranslated) rule name of the terrain.
Definition: terrain.cpp:184
const char * terrain_flag_helptxt(enum terrain_flag_id id)
Return the (untranslated) helptxt of the user terrain flag.
Definition: terrain.cpp:708
enum terrain_class terrain_type_terrain_class(const struct terrain *pterrain)
What terrain class terrain type belongs to.
Definition: terrain.cpp:464
const char * terrain_name_translation(const struct terrain *pterrain)
Return the (translated) name of the terrain.
Definition: terrain.cpp:175
int terrain_extra_build_time(const struct terrain *pterrain, enum unit_activity activity, const struct extra_type *tgt)
Time to complete the extra building activity on the given terrain.
Definition: terrain.cpp:579
#define terrain_type_iterate(_p)
Definition: terrain.h:331
#define T_NONE
Definition: terrain.h:50
#define terrain_type_iterate_end
Definition: terrain.h:337
#define terrain_has_flag(terr, flag)
Definition: terrain.h:260
help_item * tileset_help(const struct tileset *t)
Creates the help item for the given tileset.
Definition: tilespec.cpp:3859
const char * goods_name_translation(struct goods_type *pgood)
Return translated name of this goods type.
#define goods_type_iterate_end
Definition: traderoutes.h:224
#define goods_type_iterate(_p)
Definition: traderoutes.h:218
const struct unit_type * utype
Definition: fc_types.h:585
struct extra_type * extra
Definition: fc_types.h:586
struct nation_type * nation
Definition: fc_types.h:580
struct government * govern
Definition: fc_types.h:578
const struct impr_type * building
Definition: fc_types.h:579
struct advance * advance
Definition: fc_types.h:577
enum unit_activity activity
Definition: fc_types.h:616
struct terrain * terrain
Definition: fc_types.h:583
Output_type_id outputtype
Definition: fc_types.h:603
struct action * action
Definition: fc_types.h:590
int utype_pop_value(const struct unit_type *punittype)
How much city shrinks when it builds unit of this type.
Definition: unittype.cpp:1231
const char * uclass_name_translation(const struct unit_class *pclass)
Return the (translated) name of the unit class.
Definition: unittype.cpp:1324
int utype_pays_mp_for_action_base(const struct action *paction, const struct unit_type *putype)
Returns the amount of movement points successfully performing the specified action will consume in th...
Definition: unittype.cpp:1083
bool utype_can_freely_unload(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Return TRUE iff the given cargo type has no restrictions on when it can unload from the given transpo...
Definition: unittype.cpp:239
const char * unit_class_flag_helptxt(enum unit_class_flag_id id)
Return the (untranslated) help text of the user unit class flag.
Definition: unittype.cpp:1534
Unit_type_id utype_count()
Return the number of unit types.
Definition: unittype.cpp:74
int utype_build_shield_cost_base(const struct unit_type *punittype)
Returns the number of shields this unit type represents.
Definition: unittype.cpp:1168
const char * unit_type_flag_helptxt(enum unit_type_flag_id id)
Return the (untranslated) helptxt of the user unit flag.
Definition: unittype.cpp:1589
int num_role_units(int role)
How many unit types have specified role/flag.
Definition: unittype.cpp:1866
bool utype_can_freely_load(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Return TRUE iff the given cargo type has no restrictions on when it can load onto the given transport...
Definition: unittype.cpp:227
Unit_Class_id uclass_count()
Return the unit_class count.
Definition: unittype.cpp:2101
int utype_veteran_levels(const struct unit_type *punittype)
Return veteran levels of the given unit type.
Definition: unittype.cpp:2254
bool utype_can_do_action_result(const struct unit_type *putype, enum action_result result)
Return TRUE iff units of the given type can do any enabler controlled action with the specified actio...
Definition: unittype.cpp:402
const struct veteran_system * utype_veteran_system(const struct unit_type *punittype)
Return veteran system used for this unit type.
Definition: unittype.cpp:2208
const char * utype_name_translation(const struct unit_type *punittype)
Return the (translated) name of the unit type.
Definition: unittype.cpp:1256
bool utype_is_consumed_by_action(const struct action *paction, const struct unit_type *utype)
Returns TRUE iff performing the specified action will consume an actor unit of the specified type.
Definition: unittype.cpp:936
bool utype_veteran_has_power_bonus(const struct unit_type *punittype)
Return whether this unit type's veteran system, if any, confers a power factor bonus at any level (it...
Definition: unittype.cpp:2267
struct unit_type * get_role_unit(int role, int role_index)
Return index-th unit with specified role/flag.
Definition: unittype.cpp:1900
bool can_utype_do_act_if_tgt_diplrel(const struct unit_type *punit_type, const action_id act_id, const int prop, const bool is_there)
Return TRUE iff the given (action enabler controlled) action can be performed by a unit of the given ...
Definition: unittype.cpp:784
bool utype_can_do_action(const struct unit_type *putype, const action_id act_id)
Return TRUE iff units of the given type can do the specified generalized (ruleset defined) action ena...
Definition: unittype.cpp:386
const struct veteran_level * utype_veteran_level(const struct unit_type *punittype, int level)
Return veteran level properties of given unit in given veterancy level.
Definition: unittype.cpp:2224
#define UCF_LAST_USER_FLAG
Definition: unittype.h:97
static bool uclass_has_flag(const struct unit_class *punitclass, enum unit_class_flag_id flag)
Definition: unittype.h:704
#define utype_class(_t_)
Definition: unittype.h:691
#define utype_fuel(ptype)
Definition: unittype.h:772
#define combat_bonus_list_iterate_end
Definition: unittype.h:447
#define combat_bonus_list_iterate(bonuslist, pbonus)
Definition: unittype.h:445
#define unit_class_iterate(_p)
Definition: unittype.h:823
static bool utype_has_flag(const struct unit_type *punittype, int flag)
Definition: unittype.h:584
#define UTYF_LAST_USER_FLAG
Definition: unittype.h:302
#define unit_type_iterate(_p)
Definition: unittype.h:785
#define uclass_index(_c_)
Definition: unittype.h:684
#define unit_class_iterate_end
Definition: unittype.h:829
#define unit_type_iterate_end
Definition: unittype.h:791
const char * freeciv_name_version()
Return string containing both name of Freeciv21 and version.
Definition: version.cpp:34