Freeciv-3.2
Loading...
Searching...
No Matches
helpdata.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14/***********************************************************************
15 This module is for generic handling of help data, independent
16 of gui considerations.
17***********************************************************************/
18
19#ifdef HAVE_CONFIG_H
20#include <fc_config.h>
21#endif
22
23#include <stdio.h>
24#include <string.h>
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 "mem.h"
33#include "registry.h"
34#include "string_vector.h"
35#include "support.h"
36
37/* common */
38#include "counters.h"
39#include "effects.h"
40#include "game.h"
41#include "government.h"
42#include "map.h"
43#include "movement.h"
44#include "multipliers.h"
45#include "nation.h"
46#include "reqtext.h"
47#include "research.h"
48#include "server_settings.h"
49#include "specialist.h"
50#include "tilespec.h"
51#include "unit.h"
52#include "version.h"
53
54/* client */
55#include "client_main.h"
56#include "climisc.h"
57#include "gui_main_g.h" /* client_string */
58#include "music.h"
59
60#include "helpdata.h"
61
62/* TRANS: Character appearing in the beginning of each helptext point */
63#define BULLET Q_("?bullet:*")
64/* TRANS: bullet point with trailing space */
65#define BULLET_SPACE Q_("?bullet:* ")
66
67/* helper macro for easy conversion from snprintf and cat_snprintf */
68#define CATLSTR(_b, _s, _t, ...) cat_snprintf(_b, _s, _t, ## __VA_ARGS__)
69
70/* This must be in same order as enum in helpdlg_g.h */
71static const char * const help_type_names[] = {
72 "(Any)", "(Text)", "Units", "Improvements", "Wonders",
73 "Techs", "Terrain", "Extras", "Goods", "Specialists", "Governments",
74 "Ruleset", "Tileset", "Musicset", "Nations", "Multipliers", "Counters", NULL
75};
76
77#define SPECLIST_TAG help
78#define SPECLIST_TYPE struct help_item
79#include "speclist.h"
80
81#define help_list_iterate(helplist, phelp) \
82 TYPED_LIST_ITERATE(struct help_item, helplist, phelp)
83#define help_list_iterate_end LIST_ITERATE_END
84
86static struct help_list *help_nodes;
87static bool help_nodes_init = FALSE;
88/* help_nodes_init is not quite the same as booted in boot_help_texts();
89 latter can be FALSE even after call, eg if couldn't find helpdata.txt.
90*/
91
92/************************************************************************/
95void helpdata_init(void)
96{
98}
99
100/************************************************************************/
104{
106}
107
108/************************************************************************/
114static void check_help_nodes_init(void)
115{
116 if (!help_nodes_init) {
117 help_nodes_init = TRUE; /* before help_iter_start to avoid recursion! */
119 }
120}
121
122/************************************************************************/
126{
129 free(ptmp->topic);
130 free(ptmp->text);
131 free(ptmp);
134}
135
136/************************************************************************/
139static bool show_help_for_nation(const struct nation_type *pnation)
140{
141 return client_nation_is_in_current_set(pnation);
142}
143
144/************************************************************************/
149static bool insert_veteran_help(char *outbuf, size_t outlen,
150 const struct veteran_system *veteran,
151 const char *intro, const char *nolevels)
152{
153 /* game.veteran can be NULL in pregame; if so, keep quiet about
154 * veteran levels */
155 if (!veteran) {
156 return FALSE;
157 }
158
159 fc_assert_ret_val(veteran->levels >= 1, FALSE);
160
161 if (veteran->levels == 1) {
162 /* Only a single veteran level. Don't bother to name it. */
163 if (nolevels) {
164 CATLSTR(outbuf, outlen, "%s", nolevels);
165 return TRUE;
166 } else {
167 return FALSE;
168 }
169 } else {
170 int i;
172 if (intro) {
173 CATLSTR(outbuf, outlen, "%s", intro);
174 CATLSTR(outbuf, outlen, "\n\n");
175 }
176 /* TODO: Report raise_chance and work_raise_chance */
178 /* TRANS: Header for fixed-width veteran level table.
179 * TRANS: Translators cannot change column widths :(
180 * TRANS: "Level name" left-justified, other two right-justified */
181 _("Veteran level Power factor Move bonus\n"));
183 /* TRANS: Part of header for veteran level table. */
184 _("--------------------------------------------"));
185 for (i = 0; i < veteran->levels; i++) {
186 const struct veteran_level *level = &veteran->definitions[i];
187 const char *name = name_translation_get(&level->name);
188 int slen;
189
190 /* Use get_internal_string_length() for correct alignment with
191 * multibyte character encodings */
194 "\n%s%*s %4d%% %12s",
195 name, MAX(0, slen), "",
196 level->power_fact,
197 /* e.g. "- ", "+ 1/3", "+ 1 ", "+ 2 2/3" */
198 move_points_text_full(level->move_bonus, TRUE, "+ ", "-", TRUE));
199 }
200 return TRUE;
201 }
202}
203
204/************************************************************************/
208static bool insert_generated_text(char *outbuf, size_t outlen, const char *name)
209{
210 if (!game.client.ruleset_init) {
211 return FALSE;
212 }
213
214 if (0 == strcmp(name, "TerrainAlterations")) {
215 int clean_time = -1, pillage_time = -1;
217
219 /* TRANS: Header for fixed-width terrain alteration table.
220 * TRANS: Translators cannot change column widths :( */
221 _("Terrain Cultivate Plant Transform\n"));
223 "----------------------------------------------------------------\n");
224 terrain_type_iterate(pterrain) {
225 if (0 != strlen(terrain_rule_name(pterrain))) {
226 char cultivation_time[4], plant_time[4], transform_time[4];
227 const char *terrain, *cultivate_result,
228 *plant_result,*transform_result;
229 struct universal for_terr = { .kind = VUT_TERRAIN, .value = { .terrain = pterrain }};
230 int cslen, pslen, tslen;
231
233 "%d", pterrain->cultivate_time);
234 fc_snprintf(plant_time, sizeof(plant_time),
235 "%d", pterrain->plant_time);
236 fc_snprintf(transform_time, sizeof(transform_time),
237 "%d", pterrain->transform_time);
239 cultivate_result =
240 (pterrain->cultivate_result == T_NONE
242 ? ""
243 : terrain_name_translation(pterrain->cultivate_result);
244 plant_result =
245 (pterrain->plant_result == T_NONE
247 ? ""
248 : terrain_name_translation(pterrain->plant_result);
249 transform_result =
250 (pterrain->transform_result == pterrain
251 || pterrain->transform_result == T_NONE
253 NULL, &for_terr)) ? ""
254 : terrain_name_translation(pterrain->transform_result);
255
256 /* Use get_internal_string_length() for correct alignment with
257 * multibyte character encodings */
259 cslen = 12 - (int)get_internal_string_length(cultivate_result);
260 pslen = 12 - (int)get_internal_string_length(plant_result);
262 "%s%*s %3s %s%*s %3s %s%*s %3s %s\n",
263 terrain,
264 MAX(0, tslen), "",
265 (pterrain->cultivate_result == T_NONE) ? "-" : cultivation_time,
266 cultivate_result,
267 MAX(0, cslen), "",
268 (pterrain->plant_result == T_NONE) ? "-" : plant_time,
269 plant_result,
270 MAX(0, pslen), "",
271 (pterrain->transform_result == T_NONE) ? "-" : transform_time,
272 transform_result);
273
274 if (clean_time != 0) {
276 int rmtime = pterrain->extra_removal_times[extra_index(pextra)];
277
278 if (rmtime != 0) {
279 if (clean_time < 0) {
281 } else if (clean_time != rmtime) {
282 clean_time = 0; /* Give up */
283 }
284 }
286 }
287
288 if (pillage_time != 0 && pterrain->pillage_time != 0) {
289 if (pillage_time < 0) {
290 pillage_time = pterrain->pillage_time;
291 } else {
292 if (pillage_time != pterrain->pillage_time) {
293 pillage_time = 0; /* Give up */
294 }
295 }
296 }
297 }
299
300 /* Examine extras to see if time of removal activities really is
301 * terrain-independent, and take into account removal_time_factor.
302 * XXX: this is rather overwrought to handle cases which the ruleset
303 * author could express much more simply for the same result */
304 {
305 int time = -1, factor = -1;
306
308 if (pextra->removal_time == 0) {
309 if (factor < 0) {
310 factor = pextra->removal_time_factor;
311 } else if (factor != pextra->removal_time_factor) {
312 factor = 0; /* Give up */
313 }
314 } else {
315 if (time < 0) {
316 time = pextra->removal_time;
317 } else if (time != pextra->removal_time) {
318 time = 0; /* Give up */
319 }
320 }
322
323 if (factor < 0) {
324 /* No extra has terrain-dependent clean time; use extra's time */
325 if (time >= 0) {
327 } else {
328 clean_time = 0;
329 }
330 } else if (clean_time != 0) {
331 /* At least one extra's time depends on terrain */
333
334 if (time > 0 && factor > 0 && time != clean_time * factor) {
335 clean_time = 0;
336 } else if (time >= 0) {
338 } else if (factor >= 0) {
339 clean_time *= factor;
340 } else {
342 }
343 }
344 }
345
346 {
347 int time = -1, factor = -1;
348
350 if (pextra->removal_time == 0) {
351 if (factor < 0) {
352 factor = pextra->removal_time_factor;
353 } else if (factor != pextra->removal_time_factor) {
354 factor = 0; /* Give up */
355 }
356 } else {
357 if (time < 0) {
358 time = pextra->removal_time;
359 } else if (time != pextra->removal_time) {
360 time = 0; /* Give up */
361 }
362 }
364 if (factor < 0) {
365 /* No extra has terrain-dependent pillage time; use extra's time */
366 if (time >= 0) {
367 pillage_time = time;
368 } else {
369 pillage_time = 0;
370 }
371 } else if (pillage_time != 0) {
372 /* At least one extra's time depends on terrain */
373 fc_assert(pillage_time > 0);
374 if (time > 0 && factor > 0 && time != pillage_time * factor) {
375 pillage_time = 0;
376 } else if (time >= 0) {
377 pillage_time = time;
378 } else if (factor >= 0) {
379 pillage_time = pillage_time * factor;
380 } else {
382 }
383 }
384 }
385
386 /* Check whether there are any bases or roads whose build time is
387 * independent of terrain */
388
390 if (pextra->buildable && pextra->build_time > 0) {
392 break;
393 }
397 if (pextra->buildable && pextra->build_time > 0) {
399 break;
400 }
402 }
403
404 if (clean_time > 0 || pillage_time > 0
406 CATLSTR(outbuf, outlen, "\n");
408 _("Time taken for the following activities is independent of "
409 "terrain:\n"));
410 CATLSTR(outbuf, outlen, "\n");
412 /* TRANS: Header for fixed-width terrain alteration table.
413 * TRANS: Translators cannot change column widths :( */
414 _("Activity Time\n"));
416 "---------------------------");
417 if (clean_time > 0) {
419 _("\nClean %3d"), clean_time);
420 }
421 if (pillage_time > 0) {
423 _("\nPillage %3d"), pillage_time);
424 }
425
427 if (pextra->buildable && pextra->build_time > 0) {
428 const char *rname = extra_name_translation(pextra);
429 int slen = 18 - (int)get_internal_string_length(rname);
430
432 "\n%s%*s %3d",
433 rname,
434 MAX(0, slen), "",
435 pextra->build_time);
436 }
438
440 if (pextra->buildable && pextra->build_time > 0) {
441 const char *bname = extra_name_translation(pextra);
443
445 "\n%s%*s %3d",
446 bname,
447 MAX(0, slen), "",
448 pextra->build_time);
449 }
451 }
452
453 return TRUE;
454 } else if (0 == strcmp(name, "VeteranLevels")) {
456 _("In this ruleset, the following veteran levels are defined:"),
457 _("This ruleset has no default veteran levels defined."));
458 } else if (0 == strcmp(name, "FreecivVersion")) {
459 const char *ver = freeciv_name_version();
460
462 /* TRANS: First %s is version string, e.g.,
463 * "Freeciv version 3.2.0-beta1 (beta version)" (translated).
464 * Second %s is client_string, e.g., "gui-gtk-4.0". */
465 _("This is %s, %s client."), ver, client_string);
467
468 return TRUE;
469 } else if (0 == strcmp(name, "DefaultMetaserver")) {
471
472 return TRUE;
473 }
474 log_error("Unknown directive '$%s' in help", name);
475 return FALSE;
476}
477
478/************************************************************************/
487 const char *subjstr,
488 const char *const *strs,
489 char *buf, size_t bufsz,
490 const char *prefix)
491{
492 struct strvec *coreqs = strvec_new();
493 struct strvec *conoreqs = strvec_new();
496 char buf2[bufsz];
497
498 /* FIXME: show other data like range and survives. */
499
501 if (!req->quiet && are_universals_equal(psource, &req->source)) {
502 /* We're definitely going to print _something_. */
503 CATLSTR(buf, bufsz, "%s", prefix);
504 if (req->present) {
505 /* psource enables the subject, but other sources may
506 * also be required (or required to be absent). */
508 if (!coreq->quiet && !are_universals_equal(psource, &coreq->source)) {
509 universal_name_translation(&coreq->source, buf2, sizeof(buf2));
510 strvec_append(coreq->present ? coreqs : conoreqs, buf2);
511 }
513
514 if (0 < strvec_size(coreqs)) {
515 if (0 < strvec_size(conoreqs)) {
517 Q_(strs[0]), /* "Allows %s (with %s but no %s)." */
518 subjstr,
521 } else {
523 Q_(strs[1]), /* "Allows %s (with %s)." */
524 subjstr,
526 }
527 } else {
528 if (0 < strvec_size(conoreqs)) {
530 Q_(strs[2]), /* "Allows %s (absent %s)." */
531 subjstr,
533 } else {
535 Q_(strs[3]), /* "Allows %s." */
536 subjstr);
537 }
538 }
539 } else {
540 /* psource can, on its own, prevent the subject. */
542 Q_(strs[4]), /* "Prevents %s." */
543 subjstr);
544 }
545 cat_snprintf(buf, bufsz, "\n");
546 }
548
553}
554
555/************************************************************************/
571static void insert_allows(struct universal *psource,
572 char *buf, size_t bufsz, const char *prefix)
573{
574 buf[0] = '\0';
575
577 static const char *const govstrs[] = {
578 /* TRANS: First %s is a government name. */
579 N_("?gov:Allows %s (with %s but no %s)."),
580 /* TRANS: First %s is a government name. */
581 N_("?gov:Allows %s (with %s)."),
582 /* TRANS: First %s is a government name. */
583 N_("?gov:Allows %s (absent %s)."),
584 /* TRANS: %s is a government name. */
585 N_("?gov:Allows %s."),
586 /* TRANS: %s is a government name. */
587 N_("?gov:Prevents %s.")
588 };
591 buf, bufsz, prefix);
593
594 improvement_iterate(pimprove) {
595 if (valid_improvement(pimprove)) {
596 static const char *const imprstrs[] = {
597 /* TRANS: First %s is a building name. */
598 N_("?improvement:Allows %s (with %s but no %s)."),
599 /* TRANS: First %s is a building name. */
600 N_("?improvement:Allows %s (with %s)."),
601 /* TRANS: First %s is a building name. */
602 N_("?improvement:Allows %s (absent %s)."),
603 /* TRANS: %s is a building name. */
604 N_("?improvement:Allows %s."),
605 /* TRANS: %s is a building name. */
606 N_("?improvement:Prevents %s.")
607 };
608 insert_allows_single(psource, &pimprove->reqs,
610 buf, bufsz, prefix);
611 }
613
615 static const char *const utstrs[] = {
616 /* TRANS: First %s is a unit type name. */
617 N_("?unittype:Allows %s (with %s but no %s)."),
618 /* TRANS: First %s is a unit type name. */
619 N_("?unittype:Allows %s (with %s)."),
620 /* TRANS: First %s is a unit type name. */
621 N_("?unittype:Allows %s (absent %s)."),
622 /* TRANS: %s is a unit type name. */
623 N_("?unittype:Allows %s."),
624 /* TRANS: %s is a unit type name. */
625 N_("?unittype:Prevents %s.")
626 };
627 insert_allows_single(psource, &putype->build_reqs,
629 buf, bufsz, prefix);
631
632 extra_type_iterate(pextra) {
633 static const char *const estrs[] = {
634 /* TRANS: First %s is an extra name. */
635 N_("?extra:Allows %s (with %s but no %s)."),
636 /* TRANS: First %s is an extra name. */
637 N_("?extra:Allows %s (with %s)."),
638 /* TRANS: First %s is an extra name. */
639 N_("?extra:Allows %s (absent %s)."),
640 /* TRANS: %s is an extra name. */
641 N_("?extra:Allows %s."),
642 /* TRANS: %s is an extra name. */
643 N_("?extra:Prevents %s.")
644 };
645 insert_allows_single(psource, &pextra->reqs,
647 buf, bufsz, prefix);
649
651 static const char *const gstrs[] = {
652 /* TRANS: First %s is a good name. */
653 N_("?good:Allows %s (with %s but no %s)."),
654 /* TRANS: First %s is a good name. */
655 N_("?good:Allows %s (with %s)."),
656 /* TRANS: First %s is a good name. */
657 N_("?good:Allows %s (absent %s)."),
658 /* TRANS: %s is a good name. */
659 N_("?good:Allows %s."),
660 /* TRANS: %s is a good name. */
661 N_("?good:Prevents %s.")
662 };
665 buf, bufsz, prefix);
667}
668
669/************************************************************************/
672static struct help_item *new_help_item(int type)
673{
674 struct help_item *pitem;
675
676 pitem = fc_malloc(sizeof(struct help_item));
677 pitem->topic = NULL;
678 pitem->text = NULL;
679 pitem->type = type;
680
681 return pitem;
682}
683
684/************************************************************************/
688static int help_item_compar(const struct help_item *const *ppa,
689 const struct help_item *const *ppb)
690{
691 const struct help_item *ha, *hb;
692 char *ta, *tb;
693 ha = *ppa;
694 hb = *ppb;
695 for (ta = ha->topic, tb = hb->topic; *ta != '\0' && *tb != '\0'; ta++, tb++) {
696 if (*ta != ' ') {
697 if (*tb == ' ') return -1;
698 break;
699 } else if (*tb != ' ') {
700 if (*ta == ' ') return 1;
701 break;
702 }
703 }
704 return compare_strings(ta, tb);
705}
706
707/************************************************************************/
711{
712 static bool booted = FALSE;
713
714 struct section_file *sf;
715 const char *filename;
716 struct help_item *pitem;
717 int i;
718 struct section_list *sec;
719 const char **paras;
720 size_t npara;
721 char long_buffer[64000]; /* HACK: this may be overrun. */
722
724
725 /* need to do something like this or bad things happen */
727
728 if (!booted) {
729 log_verbose("Booting help texts");
730 } else {
731 /* free memory allocated last time booted */
733 log_verbose("Rebooting help texts");
734 }
735
736 filename = fileinfoname(get_data_dirs(), "helpdata.txt");
737 if (!filename) {
738 log_error("Did not read help texts");
739 return;
740 }
741 /* after following call filename may be clobbered; use sf->filename instead */
742 if (!(sf = secfile_load(filename, FALSE))) {
743 /* this is now unlikely to happen */
744 log_error("failed reading help-texts from '%s':\n%s", filename,
745 secfile_error());
746 return;
747 }
748
749 sec = secfile_sections_by_name_prefix(sf, "help_");
750
751 if (NULL != sec) {
752 section_list_iterate(sec, psection) {
754 const char *sec_name = section_name(psection);
755 const char *gen_str = secfile_lookup_str(sf, "%s.generate", sec_name);
756
757 if (gen_str) {
759 int level = strspn(gen_str, " ");
760
761 gen_str += level;
762
763 for (i = 2; help_type_names[i]; i++) {
764 if (strcmp(gen_str, help_type_names[i]) == 0) {
765 current_type = i;
766 break;
767 }
768 }
769 if (current_type == HELP_ANY) {
770 log_error("bad help-generate category \"%s\"", gen_str);
771 continue;
772 }
773
774 if (!booted) {
775 if (current_type == HELP_EXTRA) {
776 size_t ncats;
777
778 /* Avoid warnings about entries unused on this round,
779 * when the entries in question are valid once help system has been booted */
781 "%s.categories", sec_name);
782 }
783 continue; /* on initial boot data tables are empty */
784 }
785
786 {
787 /* Note these should really fill in pitem->text from auto-gen
788 data instead of doing it later on the fly, but I don't want
789 to change that now. --dwp
790 */
791 char name[2048];
793
794 switch (current_type) {
795 case HELP_UNIT:
798 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
800 pitem->topic = fc_strdup(name);
801 pitem->text = fc_strdup("");
804 break;
805 case HELP_TECH:
809 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
811 pitem->topic = fc_strdup(name);
812 pitem->text = fc_strdup("");
814 }
816 break;
817 case HELP_TERRAIN:
818 terrain_type_iterate(pterrain) {
819 if (0 != strlen(terrain_rule_name(pterrain))) {
821 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
822 terrain_name_translation(pterrain));
823 pitem->topic = fc_strdup(name);
824 pitem->text = fc_strdup("");
826 }
828 break;
829 case HELP_EXTRA:
830 {
831 const char **cats;
832 size_t ncats;
834 "%s.categories", sec_name);
835 extra_type_iterate(pextra) {
836 /* If categories not specified, don't filter */
837 if (cats) {
838 bool include = FALSE;
839 const char *cat = extra_category_name(pextra->category);
840 int ci;
841
842 for (ci = 0; ci < ncats; ci++) {
843 if (fc_strcasecmp(cats[ci], cat) == 0) {
844 include = TRUE;
845 break;
846 }
847 }
848 if (!include) {
849 continue;
850 }
851 }
853 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
854 extra_name_translation(pextra));
855 pitem->topic = fc_strdup(name);
856 pitem->text = fc_strdup("");
859 FC_FREE(cats);
860 }
861 break;
862 case HELP_GOODS:
865 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
867 pitem->topic = fc_strdup(name);
868 pitem->text = fc_strdup("");
871 break;
872 case HELP_SPECIALIST:
875
877 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
879 pitem->topic = fc_strdup(name);
880 pitem->text = fc_strdup("");
883 break;
884 case HELP_GOVERNMENT:
887 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
889 pitem->topic = fc_strdup(name);
890 pitem->text = fc_strdup("");
893 break;
894 case HELP_IMPROVEMENT:
895 improvement_iterate(pimprove) {
896 if (valid_improvement(pimprove) && !is_great_wonder(pimprove)) {
898 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
900 pitem->topic = fc_strdup(name);
901 pitem->text = fc_strdup("");
903 }
905 break;
906 case HELP_WONDER:
907 improvement_iterate(pimprove) {
908 if (valid_improvement(pimprove) && is_great_wonder(pimprove)) {
910 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
912 pitem->topic = fc_strdup(name);
913 pitem->text = fc_strdup("");
915 }
917 break;
918 case HELP_RULESET:
919 {
920 int desc_len;
921 int len;
922
924 /* pitem->topic = fc_strdup(_(game.control.name)); */
925 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
927 pitem->topic = fc_strdup(name);
930 } else {
931 desc_len = 0;
932 }
933 if (game.ruleset_summary != NULL) {
934 if (game.control.version[0] != '\0') {
936 + strlen(" ")
938 + strlen("\n\n")
940 + 1;
941
942 pitem->text = fc_malloc(len + desc_len);
943 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
946 } else {
948 + strlen("\n\n")
950 + 1;
951
952 pitem->text = fc_malloc(len + desc_len);
953 fc_snprintf(pitem->text, len, "%s\n\n%s",
955 }
956 } else {
957 const char *nodesc = _("Current ruleset contains no summary.");
958
959 if (game.control.version[0] != '\0') {
961 + strlen(" ")
963 + strlen("\n\n")
964 + strlen(nodesc)
965 + 1;
966
967 pitem->text = fc_malloc(len + desc_len);
968 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
970 nodesc);
971 } else {
973 + strlen("\n\n")
974 + strlen(nodesc)
975 + 1;
976
977 pitem->text = fc_malloc(len + desc_len);
978 fc_snprintf(pitem->text, len, "%s\n\n%s",
980 nodesc);
981 }
982 }
984 fc_strlcat(pitem->text, "\n\n", len + desc_len);
986 }
988 }
989 break;
990 case HELP_TILESET:
991 {
992 int desc_len;
993 int len;
994 const char *ts_name = tileset_name_get(tileset);
995 const char *version = tileset_version(tileset);
996 const char *summary = tileset_summary(tileset);
997 const char *description = tileset_description(tileset);
998
1000 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1002 pitem->topic = fc_strdup(name);
1003 if (description != NULL) {
1004 desc_len = strlen("\n\n") + strlen(description);
1005 } else {
1006 desc_len = 0;
1007 }
1008 if (summary != NULL) {
1009 if (version[0] != '\0') {
1010 len = strlen(_(ts_name))
1011 + strlen(" ")
1012 + strlen(version)
1013 + strlen("\n\n")
1014 + strlen(_(summary))
1015 + 1;
1016
1017 pitem->text = fc_malloc(len + desc_len);
1018 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1019 _(ts_name), version, _(summary));
1020 } else {
1021 len = strlen(_(ts_name))
1022 + strlen("\n\n")
1023 + strlen(_(summary))
1024 + 1;
1025
1026 pitem->text = fc_malloc(len + desc_len);
1027 fc_snprintf(pitem->text, len, "%s\n\n%s",
1028 _(ts_name), _(summary));
1029 }
1030 } else {
1031 const char *nodesc = _("Current tileset contains no summary.");
1032
1033 if (version[0] != '\0') {
1034 len = strlen(_(ts_name))
1035 + strlen(" ")
1036 + strlen(version)
1037 + strlen("\n\n")
1038 + strlen(nodesc)
1039 + 1;
1040
1041 pitem->text = fc_malloc(len + desc_len);
1042 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1043 _(ts_name), version,
1044 nodesc);
1045 } else {
1046 len = strlen(_(ts_name))
1047 + strlen("\n\n")
1048 + strlen(nodesc)
1049 + 1;
1050
1051 pitem->text = fc_malloc(len + desc_len);
1052 fc_snprintf(pitem->text, len, "%s\n\n%s",
1053 _(ts_name),
1054 nodesc);
1055 }
1056 }
1057 if (description != NULL) {
1058 fc_strlcat(pitem->text, "\n\n", len + desc_len);
1059 fc_strlcat(pitem->text, description, len + desc_len);
1060 }
1062 }
1063 break;
1064 case HELP_MUSICSET:
1065 {
1066 int desc_len;
1067 int len;
1068 const char *ms_name = current_musicset_name();
1069 const char *version = current_musicset_version();
1070 const char *summary = current_musicset_summary();
1071 const char *description = current_musicset_description();
1072
1074 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1076 pitem->topic = fc_strdup(name);
1077 if (description != NULL) {
1078 desc_len = strlen("\n\n") + strlen(description);
1079 } else {
1080 desc_len = 0;
1081 }
1082 if (summary != NULL) {
1083 if (version != NULL && version[0] != '\0') {
1084 len = strlen(_(ms_name))
1085 + strlen(" ")
1086 + strlen(version)
1087 + strlen("\n\n")
1088 + strlen(_(summary))
1089 + 1;
1090
1091 pitem->text = fc_malloc(len + desc_len);
1092 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1093 _(ms_name), version, _(summary));
1094 } else {
1095 len = strlen(_(ms_name))
1096 + strlen("\n\n")
1097 + strlen(_(summary))
1098 + 1;
1099
1100 pitem->text = fc_malloc(len + desc_len);
1101 fc_snprintf(pitem->text, len, "%s\n\n%s",
1102 _(ms_name), _(summary));
1103 }
1104 } else {
1105 const char *nodesc = _("Current musicset contains no summary.");
1106
1107 if (version != NULL && version[0] != '\0') {
1108 len = strlen(_(ms_name))
1109 + strlen(" ")
1110 + strlen(version)
1111 + strlen("\n\n")
1112 + strlen(nodesc)
1113 + 1;
1114
1115 pitem->text = fc_malloc(len + desc_len);
1116 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1117 _(ms_name), version,
1118 nodesc);
1119 } else {
1120 len = strlen(_(ms_name))
1121 + strlen("\n\n")
1122 + strlen(nodesc)
1123 + 1;
1124
1125 pitem->text = fc_malloc(len + desc_len);
1126 fc_snprintf(pitem->text, len, "%s\n\n%s",
1127 _(ms_name),
1128 nodesc);
1129 }
1130 }
1131 if (description != NULL) {
1132 fc_strlcat(pitem->text, "\n\n", len + desc_len);
1133 fc_strlcat(pitem->text, description, len + desc_len);
1134 }
1136 }
1137 break;
1138 case HELP_NATIONS:
1139 nations_iterate(pnation) {
1141 || show_help_for_nation(pnation)) {
1143 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1144 nation_plural_translation(pnation));
1145 pitem->topic = fc_strdup(name);
1146 pitem->text = fc_strdup("");
1148 }
1150 break;
1151 case HELP_MULTIPLIER:
1152 multipliers_iterate(pmul) {
1153 help_text_buffer[0] = '\0';
1155 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1156 name_translation_get(&pmul->name));
1157 pitem->topic = fc_strdup(name);
1158 if (pmul->helptext) {
1159 const char *sep = "";
1160 strvec_iterate(pmul->helptext, text) {
1162 "%s%s", sep, text);
1163 sep = "\n\n";
1165 }
1169 break;
1170 case HELP_COUNTER:
1171 {
1172 int j;
1173 for (j = 0; j < game.control.num_counters; j++) {
1174 struct counter *pcount = counter_by_id(j);
1175
1176 help_text_buffer[0] = '\0';
1178 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1180 pitem->topic = fc_strdup(name);
1181 if (pcount->helptext) {
1182 strvec_iterate(pcount->helptext, text) {
1184 "%s%s", "\n\n", text);
1186 }
1189 }
1190 }
1191 break;
1192 default:
1193 log_error("Bad current_type: %d.", current_type);
1194 break;
1195 }
1201 continue;
1202 }
1203 }
1204
1205 /* It wasn't a "generate" node: */
1206
1208 pitem->topic = fc_strdup(Q_(secfile_lookup_str(sf, "%s.name",
1209 sec_name)));
1210
1211 paras = secfile_lookup_str_vec(sf, &npara, "%s.text", sec_name);
1212
1213 long_buffer[0] = '\0';
1214 for (i = 0; i < npara; i++) {
1215 bool inserted;
1216 const char *para = paras[i];
1217
1218 if (!fc_strncmp(para, "$", 1)) {
1219 inserted
1221 } else {
1223 inserted = TRUE;
1224 }
1225 if (inserted && i != npara - 1) {
1226 sz_strlcat(long_buffer, "\n\n");
1227 }
1228 }
1229 free(paras);
1230 paras = NULL;
1231 pitem->text = fc_strdup(long_buffer);
1234
1236 }
1237
1239 secfile_destroy(sf);
1240 booted = TRUE;
1241 log_verbose("Booted help texts ok");
1242}
1243
1244/****************************************************************************
1245 The following few functions are essentially wrappers for the
1246 help_nodes help_list. This allows us to avoid exporting the
1247 help_list, and instead only access it through a controlled
1248 interface.
1249****************************************************************************/
1250
1251/************************************************************************/
1255{
1257 return help_list_size(help_nodes);
1258}
1259
1260/************************************************************************/
1265const struct help_item *get_help_item(int pos)
1266{
1267 int size;
1268
1271 if (pos < 0 || pos > size) {
1272 log_error("Bad index %d to get_help_item (size %d)", pos, size);
1273 return NULL;
1274 }
1275 if (pos == size) {
1276 return NULL;
1277 }
1278 return help_list_get(help_nodes, pos);
1279}
1280
1281/************************************************************************/
1287const struct help_item*
1289{
1290 int idx;
1291 const struct help_item *pitem = NULL;
1292 static struct help_item vitem; /* v = virtual */
1293 static char vtopic[128];
1294 static char vtext[256];
1295
1297 idx = 0;
1299 char *p = ptmp->topic;
1300
1301 while (*p == ' ') {
1302 p++;
1303 }
1304 if (strcmp(name, p) == 0 && (htype == HELP_ANY || htype == ptmp->type)) {
1305 pitem = ptmp;
1306 break;
1307 }
1308 idx++;
1309 }
1311
1312 if (!pitem) {
1313 idx = -1;
1314 vitem.topic = vtopic;
1316 vitem.text = vtext;
1317 if (htype == HELP_ANY || htype == HELP_TEXT) {
1318 fc_snprintf(vtext, sizeof(vtext),
1319 _("Sorry, no help topic for %s.\n"), vitem.topic);
1320 vitem.type = HELP_TEXT;
1321 } else {
1322 fc_snprintf(vtext, sizeof(vtext),
1323 _("Sorry, no help topic for %s.\n"
1324 "This page was auto-generated.\n\n"),
1325 vitem.topic);
1326 vitem.type = htype;
1327 }
1328 pitem = &vitem;
1329 }
1330 *pos = idx;
1331 return pitem;
1332}
1333
1334/************************************************************************/
1345
1346/************************************************************************/
1350const struct help_item *help_iter_next(void)
1351{
1352 const struct help_item *pitem;
1353
1356 if (pitem) {
1358 }
1359
1360 return pitem;
1361}
1362
1363/****************************************************************************
1364 FIXME:
1365 Also, in principle these could be auto-generated once, inserted
1366 into pitem->text, and then don't need to keep re-generating them.
1367 Only thing to be careful of would be changeable data, but don't
1368 have that here (for ruleset change or spacerace change must
1369 re-boot helptexts anyway). Eg, genuinely dynamic information
1370 which could be useful would be if help system said which wonders
1371 have been built (or are being built and by who/where?)
1372****************************************************************************/
1373
1374/************************************************************************/
1381char *helptext_building(char *buf, size_t bufsz, struct player *pplayer,
1382 const char *user_text, const struct impr_type *pimprove)
1383{
1384 bool reqs = FALSE;
1385 struct universal source = {
1387 .value = {.building = pimprove}
1388 };
1389
1390 fc_assert_ret_val(NULL != buf && 0 < bufsz, NULL);
1391 buf[0] = '\0';
1392
1393 if (NULL == pimprove) {
1394 return buf;
1395 }
1396
1397 if (NULL != pimprove->helptext) {
1398 strvec_iterate(pimprove->helptext, text) {
1399 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
1401 }
1402
1403 /* Add requirement text for improvement itself */
1404 requirement_vector_iterate(&pimprove->reqs, preq) {
1405 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
1406 reqs = TRUE;
1407 }
1409 if (reqs) {
1410 fc_strlcat(buf, "\n", bufsz);
1411 }
1412
1414 if (VUT_ADVANCE == pobs->source.kind && pobs->present) {
1416 _("%s The discovery of %s will make %s obsolete.\n"),
1417 BULLET,
1418 advance_name_translation(pobs->source.value.advance),
1420 }
1421 if (VUT_IMPROVEMENT == pobs->source.kind && pobs->present) {
1423 /* TRANS: both %s are improvement names */
1424 _("%s The presence of %s in the city will make %s "
1425 "obsolete.\n"),
1426 BULLET,
1427 improvement_name_translation(pobs->source.value.building),
1429 }
1431
1432 if (is_small_wonder(pimprove)) {
1434 _("%s A 'small wonder': at most one of your cities may "
1435 "possess this improvement.\n"), BULLET);
1436 }
1437 /* (Great wonders are in their own help section explaining their
1438 * uniqueness, so we don't mention it here.) */
1439
1440 if (building_has_effect(pimprove, EFT_ENABLE_NUKE)) {
1442 struct unit_type *u = NULL;
1443
1444 {
1445 /* Find Manhattan dependent nuke actions */
1446 int i = 0;
1447
1450
1452 }
1453
1455 if (num_role_units(action_id_get_role(act_id)) > 0) {
1456 u = get_role_unit(action_id_get_role(act_id), 0);
1457 break;
1458 }
1460
1461 if (u) {
1462 struct advance *req = NULL;
1463 int count = 0;
1464
1466 req = preq;
1467 count++;
1469
1470 if (req != NULL) {
1471 if (count == 1) {
1473 /* TRANS: 'Allows all players with knowledge of atomic
1474 * power to build nuclear units.' */
1475 _("%s Allows all players with knowledge of %s "
1476 "to build %s units.\n"), BULLET,
1479 } else {
1480 /* Multiple tech requirements */
1482 /* TRANS: 'Allows all players with knowledge of required
1483 * techs to build nuclear units.' */
1484 _("%s Allows all players with knowledge of required "
1485 "techs to build %s units.\n"), BULLET,
1487 }
1488 } else {
1490 /* TRANS: 'Allows all players to build nuclear units.' */
1491 _("%s Allows all players to build %s units.\n"), BULLET,
1493 }
1494 }
1495 }
1496
1498 BULLET_SPACE);
1499
1500 /* Actions that requires the building to target a city. */
1501 action_iterate(act) {
1502 /* Nothing is found yet. */
1503 bool demanded = FALSE;
1505
1506 if (action_id_get_target_kind(act) != ATK_CITY) {
1507 /* Not relevant */
1508 continue;
1509 }
1510
1511 if (action_by_number(act)->quiet) {
1512 /* The ruleset it self documents this action. */
1513 continue;
1514 }
1515
1517 if (universal_fulfills_requirements(TRUE, &(enabler->target_reqs),
1518 &source)) {
1519 /* The building is needed by this action enabler. */
1520 demanded = TRUE;
1521
1522 /* See if this enabler gives the building a wider range. */
1523 requirement_vector_iterate(&(enabler->target_reqs), preq) {
1525 /* Not relevant. */
1526 continue;
1527 }
1528
1529 if (!preq->present) {
1530 /* A !present larger range requirement would make the present
1531 * lower range illegal. */
1532 continue;
1533 }
1534
1535 if (preq->range > max_range) {
1536 /* Found a larger range. */
1537 max_range = preq->range;
1538 /* Intentionally not breaking here. The requirement vector may
1539 * contain other requirements with a larger range.
1540 * Example: Building a GreatWonder in a city with a Palace. */
1541 }
1543 }
1545
1546 if (demanded) {
1547 switch (max_range) {
1548 case REQ_RANGE_LOCAL:
1549 /* At least one action enabler needed the building in its target
1550 * requirements. */
1552 /* TRANS: Help build Wonder */
1553 _("%s Makes it possible to target the city building it "
1554 "with the action \'%s\'.\n"), BULLET,
1556 break;
1557 case REQ_RANGE_CITY:
1558 /* At least one action enabler needed the building in its target
1559 * requirements. */
1561 /* TRANS: Help build Wonder */
1562 _("%s Makes it possible to target its city with the "
1563 "action \'%s\'.\n"), BULLET,
1565 break;
1567 /* At least one action enabler needed the building in its target
1568 * requirements. */
1570 /* TRANS: Help build Wonder */
1571 _("%s Makes it possible to target its city and its "
1572 "trade partners with the action \'%s\'.\n"),
1573 BULLET,
1575 break;
1577 /* At least one action enabler needed the building in its target
1578 * requirements. */
1580 /* TRANS: Help build Wonder */
1581 _("%s Makes it possible to target all cities with its "
1582 "owner on its continent with the action \'%s\'.\n"),
1583 BULLET,
1585 break;
1586 case REQ_RANGE_PLAYER:
1587 /* At least one action enabler needed the building in its target
1588 * requirements. */
1590 /* TRANS: Help build Wonder */
1591 _("%s Makes it possible to target all cities with its "
1592 "owner with the action \'%s\'.\n"),
1593 BULLET,
1595 break;
1596 case REQ_RANGE_TEAM:
1597 /* At least one action enabler needed the building in its target
1598 * requirements. */
1600 /* TRANS: Help build Wonder */
1601 _("%s Makes it possible to target all cities on the "
1602 "same team with the action \'%s\'.\n"),
1603 BULLET,
1605 break;
1606 case REQ_RANGE_ALLIANCE:
1607 /* At least one action enabler needed the building in its target
1608 * requirements. */
1610 /* TRANS: Help build Wonder */
1611 _("%s Makes it possible to target all cities owned by "
1612 "or allied to its owner with the action \'%s\'.\n"),
1613 BULLET,
1615 break;
1616 case REQ_RANGE_WORLD:
1617 /* At least one action enabler needed the building in its target
1618 * requirements. */
1620 /* TRANS: Help build Wonder */
1621 _("%s Makes it possible to target all cities with the "
1622 "action \'%s\'.\n"),
1623 BULLET,
1625 break;
1626 case REQ_RANGE_TILE:
1628 case REQ_RANGE_ADJACENT:
1629 case REQ_RANGE_COUNT:
1630 log_error("The range %s is invalid for buildings.",
1632 break;
1633 }
1634 }
1636
1637 /* Building protects against action. */
1638 action_iterate(act) {
1639 struct action *paction = action_by_number(act);
1640 /* Nothing is found yet. */
1641 bool vulnerable = FALSE;
1643
1644 if (action_id_get_target_kind(act) != ATK_CITY) {
1645 /* Not relevant */
1646 continue;
1647 }
1648
1649 if (!action_is_in_use(paction)) {
1650 /* This action isn't enabled at all. */
1651 continue;
1652 }
1653
1654 if (action_by_number(act)->quiet) {
1655 /* The ruleset it self documents this action. */
1656 continue;
1657 }
1658
1659 /* Must be immune in all cases. */
1662 &(enabler->target_reqs))) {
1663 vulnerable = TRUE;
1664 break;
1665 } else {
1667
1668 requirement_vector_iterate(&(enabler->target_reqs), preq) {
1670 /* Not relevant. */
1671 continue;
1672 }
1673
1674 if (preq->present) {
1675 /* Not what is looked for. */
1676 continue;
1677 }
1678
1679 if (preq->range > vector_max_range) {
1680 /* Found a larger range. */
1681 vector_max_range = preq->range;
1682 }
1684
1686 /* Found a smaller range. */
1688 }
1689 }
1691
1692 if (!vulnerable) {
1693 switch (min_range) {
1694 case REQ_RANGE_LOCAL:
1696 /* TRANS: Incite City */
1697 _("%s Makes it impossible to do the action \'%s\' to "
1698 "the city building it.\n"),
1699 BULLET,
1701 break;
1702 case REQ_RANGE_CITY:
1704 /* TRANS: Incite City */
1705 _("%s Makes it impossible to do the action \'%s\' to "
1706 "its city.\n"),
1707 BULLET,
1709 break;
1712 /* TRANS: Incite City */
1713 _("%s Makes it impossible to do the action \'%s\' to "
1714 "its city or to its city's trade partners.\n"),
1715 BULLET,
1717 break;
1720 /* TRANS: Incite City */
1721 _("%s Makes it impossible to do the action \'%s\' to "
1722 "any city with its owner on its continent.\n"),
1723 BULLET,
1725 break;
1726 case REQ_RANGE_PLAYER:
1728 /* TRANS: Incite City */
1729 _("%s Makes it impossible to do the action \'%s\' to "
1730 "any city with its owner.\n"),
1731 BULLET,
1733 break;
1734 case REQ_RANGE_TEAM:
1736 /* TRANS: Incite City */
1737 _("%s Makes it impossible to do the action \'%s\' to "
1738 "any city on the same team.\n"),
1739 BULLET,
1741 break;
1742 case REQ_RANGE_ALLIANCE:
1744 /* TRANS: Incite City */
1745 _("%s Makes it impossible to do the action \'%s\' to "
1746 "any city allied to or owned by its owner.\n"),
1747 BULLET,
1749 break;
1750 case REQ_RANGE_WORLD:
1752 /* TRANS: Incite City */
1753 _("%s Makes it impossible to do the action \'%s\' to "
1754 "any city in the game.\n"),
1755 BULLET,
1757 break;
1758 case REQ_RANGE_TILE:
1760 case REQ_RANGE_ADJACENT:
1761 case REQ_RANGE_COUNT:
1762 log_error("The range %s is invalid for buildings.",
1764 break;
1765 }
1766 }
1768
1769 {
1770 int i;
1771
1772 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
1774 if (n == B_LAST) {
1775 break;
1776 } else if (improvement_by_number(n) == pimprove) {
1778 _("%s All players start with this improvement in their "
1779 "first city.\n"), BULLET);
1780 break;
1781 }
1782 }
1783 }
1784
1785 /* Assume no-one will set the same building in both global and nation
1786 * init_buildings... */
1787 nations_iterate(pnation) {
1788 int i;
1789
1790 /* Avoid mentioning nations not in current set. */
1791 if (!show_help_for_nation(pnation)) {
1792 continue;
1793 }
1794 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
1795 Impr_type_id n = pnation->init_buildings[i];
1796 if (n == B_LAST) {
1797 break;
1798 } else if (improvement_by_number(n) == pimprove) {
1800 /* TRANS: %s is a nation plural */
1801 _("%s The %s start with this improvement in their "
1802 "first city.\n"), BULLET,
1803 nation_plural_translation(pnation));
1804 break;
1805 }
1806 }
1808
1811 /* TRANS: don't translate 'savepalace' */
1812 _("%s If you lose the city containing this improvement, "
1813 "it will be rebuilt for free in another of your cities "
1814 "(if the 'savepalace' server setting is enabled).\n"),
1815 BULLET);
1816 }
1817
1818 if (user_text && user_text[0] != '\0') {
1819 cat_snprintf(buf, bufsz, "\n\n%s", user_text);
1820 }
1821 return buf;
1822}
1823
1824/************************************************************************/
1830static bool utype_may_do_escape_action(const struct unit_type *utype)
1831{
1832 action_iterate(act_id) {
1833 struct action *paction = action_by_number(act_id);
1834
1836 /* Not relevant. */
1837 continue;
1838 }
1839
1840 if (!utype_can_do_action(utype, paction->id)) {
1841 /* Can't do it. */
1842 continue;
1843 }
1844
1846 /* No escape when dead. */
1847 continue;
1848 }
1849
1850 if (paction->actor.is_unit.moves_actor == MAK_ESCAPE) {
1851 /* Survives and escapes. */
1852 return TRUE;
1853 }
1855
1856 return FALSE;
1857}
1858
1859/************************************************************************/
1866void helptext_unitclass(struct unit_class *pclass, char *buf, size_t bufsz)
1867{
1868 int flagid;
1869
1870 if (pclass->helptext != NULL) {
1871 strvec_iterate(pclass->helptext, text) {
1872 cat_snprintf(buf, bufsz, "\n%s\n", _(text));
1874 } else {
1875 CATLSTR(buf, bufsz, "\n");
1876 }
1877
1879 /* TRANS: indented unit class property, preserve leading spaces */
1880 CATLSTR(buf, bufsz, _(" %s Speed is not affected by terrain.\n"),
1881 BULLET);
1882 }
1884 /* TRANS: indented unit class property, preserve leading spaces */
1885 CATLSTR(buf, bufsz, _(" %s Does not get defense bonuses from terrain.\n"),
1886 BULLET);
1887 }
1888
1890 /* TRANS: indented unit class property, preserve leading spaces */
1891 CATLSTR(buf, bufsz, _(" %s Not subject to zones of control.\n"),
1892 BULLET);
1893 }
1894
1896 /* TRANS: indented unit class property, preserve leading spaces */
1897 CATLSTR(buf, bufsz, _(" %s Slowed down while damaged.\n"), BULLET);
1898 }
1899
1901 CATLSTR(buf, bufsz,
1902 /* TRANS: indented unit class property, preserve leading spaces */
1903 _(" %s Is unreachable. Most units cannot attack this one.\n"),
1904 BULLET);
1905 }
1906
1908 CATLSTR(buf, bufsz,
1909 /* TRANS: Indented unit class property, preserve leading spaces */
1910 _(" %s Doesn't prevent enemy cities from working the tile it's on.\n"),
1911 BULLET);
1912 }
1913
1916 const char *helptxt = unit_class_flag_helptxt(flagid);
1917
1918 if (helptxt != NULL) {
1919 /* TRANS: Indented unit class property, preserve leading spaces */
1920 CATLSTR(buf, bufsz, " %s %s\n", BULLET, _(helptxt));
1921 }
1922 }
1923 }
1924}
1925
1926/************************************************************************/
1932char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
1933 const char *user_text, const struct unit_type *utype,
1934 bool class_help)
1935{
1936 bool has_vet_levels;
1937 int flagid;
1938 struct unit_class *pclass;
1939 int fuel;
1940
1941 fc_assert_ret_val(NULL != buf && 0 < bufsz && NULL != user_text, NULL);
1942
1943 if (!utype) {
1944 log_error("Unknown unit!");
1946 return buf;
1947 }
1948
1950
1951 buf[0] = '\0';
1952
1953 pclass = utype_class(utype);
1955 _("%s Belongs to %s unit class."),
1956 BULLET,
1958
1959 if (class_help) {
1961 } else {
1962 cat_snprintf(buf, bufsz, "\n");
1963 }
1964
1966 && !utype_has_flag(utype, UTYF_IGZOC)) {
1967 /* TRANS: Indented unit class property, preserve leading spaces */
1968 CATLSTR(buf, bufsz, _(" %s Subject to zones of control.\n"),
1969 BULLET);
1970 }
1971
1972 if (utype->defense_strength > 0) {
1973 struct universal unit_is_in_city[] = {
1974 { .kind = VUT_UTYPE, .value = { .utype = utype }},
1975 { .kind = VUT_CITYTILE, .value = { .citytile = CITYT_CENTER }},
1976 };
1977 int bonus = effect_value_from_universals(
1980
1981 if (bonus > 0) {
1983 /* TRANS: Indented unit class property, preserve leading
1984 * spaces */
1985 _(" %s Gets a %d%% defensive bonus while in cities.\n"),
1986 BULLET, bonus);
1987 }
1988 }
1991 CATLSTR(buf, bufsz,
1992 /* TRANS: Indented twice; preserve leading spaces */
1993 _(" %s Doesn't prevent enemy units from attacking other "
1994 "units on its tile.\n"), BULLET);
1995 }
1996
1997 if (can_attack_non_native(utype)) {
1998 CATLSTR(buf, bufsz,
1999 /* TRANS: Indented unit class property, preserve leading spaces */
2000 _(" %s Can attack units on non-native tiles.\n"), BULLET);
2001 }
2002
2003 /* The unit's combat bonuses. Won't mention that another unit type has a
2004 * combat bonus against this unit type. Doesn't handle complex cases like
2005 * when a unit type has multiple combat bonuses of the same kind. */
2007 const char *against[utype_count()];
2008 int targets = 0;
2009
2010 if (cbonus->quiet) {
2011 /* Handled in the help text of the ruleset. */
2012 continue;
2013 }
2014
2015 /* Find the unit types of the bonus targets. */
2017 if (utype_has_flag(utype2, cbonus->flag)) {
2019 }
2021
2022 if (targets > 0) {
2023 struct astring list = ASTRING_INIT;
2024
2025 switch (cbonus->type) {
2028 /* TRANS: percentage ... or-list of unit types */
2029 _("%s %d%% defense bonus if attacked by %s.\n"),
2030 BULLET,
2031 cbonus->value * 100,
2032 astr_build_or_list(&list, against, targets));
2033 break;
2036 /* TRANS: defense divider ... or-list of unit types */
2037 _("%s Reduces target's defense to 1 / %d when "
2038 "attacking %s.\n"), BULLET,
2039 cbonus->value + 1,
2040 astr_build_or_list(&list, against, targets));
2041 break;
2044 /* TRANS: or-list of unit types */
2045 _("%s Reduces target's firepower to 1 when "
2046 "attacking %s.\n"), BULLET,
2047 astr_build_and_list(&list, against, targets));
2048 break;
2051 /* TRANS: percentage ... or-list of unit types */
2052 _("%s %d%% defense bonus if attacked by %s.\n"),
2053 BULLET, cbonus->value,
2054 astr_build_or_list(&list, against, targets));
2055 break;
2058 /* TRANS: defense divider ... or-list of unit types */
2059 _("%s Reduces target's defense to 1 / %.2f when "
2060 "attacking %s.\n"), BULLET,
2061 ((float) cbonus->value + 100.0f) / 100.0f,
2062 astr_build_or_list(&list, against, targets));
2063 break;
2066 /* TRANS: percentage ... or-list of unit types */
2067 _("%s %d%% defense bonus "
2068 "instead of any bonuses from city improvements "
2069 "if attacked by %s in a city.\n"),
2070 BULLET, cbonus->value,
2071 astr_build_or_list(&list, against, targets));
2072 break;
2073 }
2074
2075 astr_free(&list);
2076 }
2078
2079 /* Add requirement text for the unit type itself */
2082 BULLET_SPACE);
2084
2085 if (utype_has_flag(utype, UTYF_CANESCAPE)) {
2086 CATLSTR(buf, bufsz, _("%s Can escape once stack defender is lost.\n"),
2087 BULLET);
2088 }
2090 CATLSTR(buf, bufsz, _("%s Can pursue escaping units and kill them.\n"),
2091 BULLET);
2092 }
2093
2094 if (utype_has_flag(utype, UTYF_NOBUILD)) {
2095 CATLSTR(buf, bufsz, _("%s May not be built in cities.\n"), BULLET);
2096 }
2097 if (utype_has_flag(utype, UTYF_BARBARIAN_ONLY)) {
2098 CATLSTR(buf, bufsz, _("%s Only barbarians may build this.\n"), BULLET);
2099 }
2101 CATLSTR(buf, bufsz, _("%s Can only be built in games where new cities "
2102 "are allowed.\n"), BULLET);
2104 /* TRANS: indented; preserve leading spaces */
2105 CATLSTR(buf, bufsz, _(" %s New cities are not allowed in the current "
2106 "game.\n"), BULLET);
2107 } else {
2108 /* TRANS: indented; preserve leading spaces */
2109 CATLSTR(buf, bufsz, _(" %s New cities are allowed in the current "
2110 "game.\n"), BULLET);
2111 }
2112 }
2113 nations_iterate(pnation) {
2114 int i, count = 0;
2115
2116 /* Avoid mentioning nations not in current set. */
2117 if (!show_help_for_nation(pnation)) {
2118 continue;
2119 }
2120 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
2121 if (!pnation->init_units[i]) {
2122 break;
2123 } else if (pnation->init_units[i] == utype) {
2124 count++;
2125 }
2126 }
2127 if (count > 0) {
2129 /* TRANS: %s is a nation plural */
2130 PL_("%s The %s start the game with %d of these units.\n",
2131 "%s The %s start the game with %d of these units.\n",
2132 count), BULLET,
2133 nation_plural_translation(pnation), count);
2134 }
2136 {
2137 const char *types[utype_count()];
2138 int i = 0;
2139
2141 if (utype2->converted_to == utype
2143 types[i++] = utype_name_translation(utype2);
2144 }
2146 if (i > 0) {
2147 struct astring list = ASTRING_INIT;
2148
2149 astr_build_or_list(&list, types, i);
2151 /* TRANS: %s is a list of unit types separated by "or". */
2152 _("%s May be obtained by conversion of %s.\n"),
2153 BULLET, astr_str(&list));
2154 astr_free(&list);
2155 }
2156 }
2157 if (utype_has_flag(utype, UTYF_NOHOME)) {
2159 CATLSTR(buf, bufsz, _("%s Built without a home city.\n"), BULLET);
2160 } else {
2161 CATLSTR(buf, bufsz, _("%s Never has a home city.\n"), BULLET);
2162 }
2163 }
2164 if (utype_has_flag(utype, UTYF_GAMELOSS)) {
2165 CATLSTR(buf, bufsz, _("%s Losing this unit will lose you the game!\n"),
2166 BULLET);
2167 }
2168 if (utype_has_flag(utype, UTYF_UNIQUE)) {
2169 CATLSTR(buf, bufsz,
2170 _("%s Each player may only have one of this type of unit.\n"),
2171 BULLET);
2172 }
2174 if (utype_has_flag(utype, flagid)) {
2175 const char *helptxt = unit_type_flag_helptxt(flagid);
2176
2177 if (helptxt != NULL) {
2178 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
2179 }
2180 }
2181 }
2182 if (utype->pop_cost > 0) {
2184 PL_("%s Costs %d population to build.\n",
2185 "%s Costs %d population to build.\n", utype->pop_cost),
2186 BULLET, utype->pop_cost);
2187 }
2188 if (0 < utype->transport_capacity) {
2189 const char *classes[uclass_count()];
2190 int i = 0;
2191 struct astring list = ASTRING_INIT;
2192
2193 unit_class_iterate(uclass) {
2194 if (can_unit_type_transport(utype, uclass)) {
2195 classes[i++] = uclass_name_translation(uclass);
2196 }
2199
2201 /* TRANS: %s is a list of unit classes separated by "or". */
2202 PL_("%s Can carry and refuel %d %s unit.\n",
2203 "%s Can carry and refuel up to %d %s units.\n",
2204 utype->transport_capacity),
2206 astr_free(&list);
2208 /* Document restrictions on when units can load/unload */
2213 if (utype_can_freely_load(pcargo, utype)) {
2215 } else {
2217 }
2218 if (utype_can_freely_unload(pcargo, utype)) {
2220 } else {
2222 }
2223 }
2225 if (has_restricted_load) {
2227 /* At least one type of cargo can load onto us freely.
2228 * The specific exceptions will be documented in cargo help. */
2229 CATLSTR(buf, bufsz,
2230 /* TRANS: indented; preserve leading spaces */
2231 _(" %s Some cargo cannot be loaded except in a city or a "
2232 "base native to this transport.\n"), BULLET);
2233 } else {
2234 /* No exceptions */
2235 CATLSTR(buf, bufsz,
2236 /* TRANS: indented; preserve leading spaces */
2237 _(" %s Cargo cannot be loaded except in a city or a "
2238 "base native to this transport.\n"), BULLET);
2239 }
2240 } /* else, no restricted cargo exists; keep quiet */
2243 /* At least one type of cargo can unload from us freely. */
2244 CATLSTR(buf, bufsz,
2245 /* TRANS: indented; preserve leading spaces */
2246 _(" %s Some cargo cannot be unloaded except in a city or a "
2247 "base native to this transport.\n"), BULLET);
2248 } else {
2249 /* No exceptions */
2250 CATLSTR(buf, bufsz,
2251 /* TRANS: indented; preserve leading spaces */
2252 _(" %s Cargo cannot be unloaded except in a city or a "
2253 "base native to this transport.\n"), BULLET);
2254 }
2255 } /* else, no restricted cargo exists; keep quiet */
2256 }
2257 }
2258 if (utype_has_flag(utype, UTYF_COAST_STRICT)) {
2259 CATLSTR(buf, bufsz, _("%s Must stay next to safe coast.\n"), BULLET);
2260 }
2261 {
2262 /* Document exceptions to embark/disembark restrictions that we
2263 * have as cargo. */
2264 bv_unit_classes embarks, disembarks;
2265 BV_CLR_ALL(embarks);
2266 BV_CLR_ALL(disembarks);
2267 /* Determine which of our transport classes have restrictions in the first
2268 * place (that is, contain at least one transport which carries at least
2269 * one type of cargo which is restricted).
2270 * We'll suppress output for classes not in this set, since this cargo
2271 * type is not behaving exceptionally in such cases. */
2274 /* Don't waste time repeating checks on classes we've already checked,
2275 * or weren't under consideration in the first place */
2276 if (!BV_ISSET(embarks, trans_class)
2277 && BV_ISSET(utype->embarks, trans_class)) {
2281 /* At least one load restriction in transport class, which
2282 * we aren't subject to */
2283 BV_SET(embarks, trans_class);
2284 }
2285 } unit_type_iterate_end; /* cargo */
2286 }
2287 if (!BV_ISSET(disembarks, trans_class)
2288 && BV_ISSET(utype->disembarks, trans_class)) {
2292 /* At least one load restriction in transport class, which
2293 * we aren't subject to */
2294 BV_SET(disembarks, trans_class);
2295 }
2296 } unit_type_iterate_end; /* cargo */
2297 }
2298 } unit_class_iterate_end; /* transports */
2299
2300 if (BV_ISSET_ANY(embarks)) {
2301 /* Build list of embark exceptions */
2302 const char *eclasses[uclass_count()];
2303 int i = 0;
2304 struct astring elist = ASTRING_INIT;
2305
2306 unit_class_iterate(uclass) {
2307 if (BV_ISSET(embarks, uclass_index(uclass))) {
2308 eclasses[i++] = uclass_name_translation(uclass);
2309 }
2312 if (BV_ARE_EQUAL(embarks, disembarks)) {
2313 /* A common case: the list of disembark exceptions is identical */
2315 /* TRANS: %s is a list of unit classes separated
2316 * by "or". */
2317 _("%s May load onto and unload from %s transports even "
2318 "when underway.\n"),
2319 BULLET, astr_str(&elist));
2320 } else {
2322 /* TRANS: %s is a list of unit classes separated
2323 * by "or". */
2324 _("%s May load onto %s transports even when underway.\n"),
2325 BULLET, astr_str(&elist));
2326 }
2327 astr_free(&elist);
2328 }
2329 if (BV_ISSET_ANY(disembarks) && !BV_ARE_EQUAL(embarks, disembarks)) {
2330 /* Build list of disembark exceptions (if different from embarking) */
2331 const char *dclasses[uclass_count()];
2332 int i = 0;
2333 struct astring dlist = ASTRING_INIT;
2334
2335 unit_class_iterate(uclass) {
2336 if (BV_ISSET(disembarks, uclass_index(uclass))) {
2337 dclasses[i++] = uclass_name_translation(uclass);
2338 }
2342 /* TRANS: %s is a list of unit classes separated
2343 * by "or". */
2344 _("%s May unload from %s transports even when underway.\n"),
2345 BULLET, astr_str(&dlist));
2346 astr_free(&dlist);
2347 }
2348 }
2349
2350 if (utype_has_flag(utype, UTYF_SPY)) {
2351 CATLSTR(buf, bufsz, _("%s Strong in diplomatic battles.\n"), BULLET);
2352 }
2353 if (utype_has_flag(utype, UTYF_DIPLOMAT)
2354 || utype_has_flag(utype, UTYF_SUPERSPY)) {
2355 CATLSTR(buf, bufsz, _("%s Defends cities against diplomatic actions.\n"),
2356 BULLET);
2357 }
2358 if (utype_has_flag(utype, UTYF_SUPERSPY)) {
2359 CATLSTR(buf, bufsz, _("%s Will never lose a diplomat-versus-diplomat fight.\n"),
2360 BULLET);
2361 }
2363 && utype_has_flag(utype, UTYF_SUPERSPY)) {
2364 CATLSTR(buf, bufsz, _("%s Will always survive a spy mission.\n"), BULLET);
2365 }
2366 if (utype->vlayer == V_INVIS) {
2367 CATLSTR(buf, bufsz,
2368 _("%s Is invisible except when next to an enemy unit or city.\n"),
2369 BULLET);
2370 }
2372 CATLSTR(buf, bufsz,
2373 _("%s Can only attack units on native tiles.\n"), BULLET);
2374 }
2375 if (utype_has_flag(utype, UTYF_CITYBUSTER)) {
2376 CATLSTR(buf, bufsz,
2377 _("%s Gets double firepower when attacking cities.\n"), BULLET);
2378 }
2379 if (utype_has_flag(utype, UTYF_IGTER)) {
2381 /* TRANS: "MP" = movement points. %s may have a
2382 * fractional part. */
2383 _("%s Ignores terrain effects (moving costs at most %s MP "
2384 "per tile).\n"), BULLET,
2386 }
2387 if (utype_has_flag(utype, UTYF_NOZOC)) {
2388 CATLSTR(buf, bufsz, _("%s Never imposes a zone of control.\n"), BULLET);
2389 } else {
2390 CATLSTR(buf, bufsz, _("%s May impose a zone of control on its adjacent "
2391 "tiles.\n"), BULLET);
2392 }
2393 if (utype_has_flag(utype, UTYF_IGZOC)) {
2394 CATLSTR(buf, bufsz, _("%s Not subject to zones of control imposed "
2395 "by other units.\n"), BULLET);
2396 }
2397 if (utype_has_flag(utype, UTYF_CIVILIAN)) {
2398 CATLSTR(buf, bufsz,
2399 _("%s A non-military unit:\n"), BULLET);
2400 CATLSTR(buf, bufsz,
2401 /* TRANS: indented; preserve leading spaces */
2402 _(" %s Cannot attack.\n"), BULLET);
2403 CATLSTR(buf, bufsz,
2404 /* TRANS: indented; preserve leading spaces */
2405 _(" %s Doesn't impose martial law.\n"), BULLET);
2406 CATLSTR(buf, bufsz,
2407 /* TRANS: indented; preserve leading spaces */
2408 _(" %s Can enter foreign territory regardless of peace treaty.\n"),
2409 BULLET);
2410 CATLSTR(buf, bufsz,
2411 /* TRANS: indented; preserve leading spaces */
2412 _(" %s Doesn't prevent enemy cities from working the tile it's on.\n"),
2413 BULLET);
2414 }
2415 if (utype_has_flag(utype, UTYF_FIELDUNIT)) {
2416 CATLSTR(buf, bufsz,
2417 _("%s A field unit: one unhappiness applies even when non-aggressive.\n"),
2418 BULLET);
2419 }
2420 if (utype_has_flag(utype, UTYF_PROVOKING)
2422 server_setting_by_name("autoattack"))) {
2423 CATLSTR(buf, bufsz,
2424 _("%s An enemy unit considering to auto attack this unit will "
2425 "choose to do so even if it has better odds when defending "
2426 "against it than when attacking it.\n"), BULLET);
2427 }
2428 if (utype_has_flag(utype, UTYF_SHIELD2GOLD)) {
2429 /* FIXME: the conversion shield => gold is activated if
2430 * EFT_SHIELD2GOLD_FACTOR is not equal null; how to determine
2431 * possible sources? */
2432 CATLSTR(buf, bufsz,
2433 _("%s Under certain conditions the shield upkeep of this unit can "
2434 "be converted to gold upkeep.\n"), BULLET);
2435 }
2436
2437 unit_class_iterate(target) {
2438 if (uclass_has_flag(target, UCF_UNREACHABLE)
2439 && BV_ISSET(utype->targets, uclass_index(target))) {
2441 _("%s Can attack against %s units, which are usually not "
2442 "reachable.\n"), BULLET,
2443 uclass_name_translation(target));
2444 }
2446
2447 fuel = utype_fuel(utype);
2448 if (fuel > 0) {
2449 const char *types[utype_count()];
2450 int i = 0;
2451
2455 }
2457
2458 if (0 == i) {
2459 if (utype_has_flag(utype, UTYF_COAST)) {
2460 if (fuel == 1) {
2462 _("%s Unit has to end each turn next to safe coast or"
2463 " in a city or a base.\n"), BULLET);
2464 } else {
2466 /* Pluralization for the benefit of languages with
2467 * duals etc */
2468 /* TRANS: Never called for 'turns = 1' case */
2469 PL_("%s Unit has to be next to safe coast, in a city or a base"
2470 " after %d turn.\n",
2471 "%s Unit has to be next to safe coast, in a city or a base"
2472 " after %d turns.\n",
2473 fuel),
2474 BULLET, fuel);
2475 }
2476 } else {
2478 PL_("%s Unit has to be in a city or a base"
2479 " after %d turn.\n",
2480 "%s Unit has to be in a city or a base"
2481 " after %d turns.\n",
2482 fuel),
2483 BULLET, fuel);
2484 }
2485 } else {
2486 struct astring list = ASTRING_INIT;
2487
2488 if (utype_has_flag(utype, UTYF_COAST)) {
2490 /* TRANS: %s is a list of unit types separated by "or" */
2491 PL_("%s Unit has to be next to safe coast, in a city, a base, or on a %s"
2492 " after %d turn.\n",
2493 "%s Unit has to be next to safe coast, in a city, a base, or on a %s"
2494 " after %d turns.\n",
2495 fuel),
2496 BULLET, astr_build_or_list(&list, types, i), fuel);
2497 } else {
2499 /* TRANS: %s is a list of unit types separated by "or" */
2500 PL_("%s Unit has to be in a city, a base, or on a %s"
2501 " after %d turn.\n",
2502 "%s Unit has to be in a city, a base, or on a %s"
2503 " after %d turns.\n",
2504 fuel),
2505 BULLET, astr_build_or_list(&list, types, i), fuel);
2506 }
2507 astr_free(&list);
2508 }
2509 }
2510
2511 /* Auto attack immunity. (auto_attack.if_attacker ruleset setting) */
2513 server_setting_by_name("autoattack"))) {
2515
2517 if (auto_action->cause != AAPC_UNIT_MOVED_ADJ) {
2518 /* Not relevant for auto attack. */
2519 continue;
2520 }
2521
2523 /* Can be forced to auto attack. */
2525 break;
2526 }
2528
2530 CATLSTR(buf, bufsz,
2531 _("%s Will never be forced (by the autoattack server setting)"
2532 " to attack units moving to an adjacent tile.\n"), BULLET);
2533 }
2534 }
2535
2536 action_iterate(act) {
2537 struct action *paction = action_by_number(act);
2538
2539 if (action_by_number(act)->quiet) {
2540 /* The ruleset documents this action it self. */
2541 continue;
2542 }
2543
2544 if (utype_can_do_action(utype, act)) {
2545 const char *target_adjective;
2546 char sub_target_text[100];
2547 const char *blockers[MAX_NUM_ACTIONS];
2548 int i = 0;
2549
2550 /* Generic action information. */
2552 /* TRANS: %s is the action's ruleset defined ui name */
2553 _("%s Can do the action \'%s\'.\n"),
2555
2556 switch (action_id_get_target_kind(act)) {
2557 case ATK_SELF:
2558 /* No target. */
2559 break;
2560 default:
2561 if (!can_utype_do_act_if_tgt_diplrel(utype, act,
2562 DRO_FOREIGN, TRUE)) {
2563 /* TRANS: describes the target of an action. */
2564 target_adjective = _("domestic ");
2565 } else if (!can_utype_do_act_if_tgt_diplrel(utype, act,
2566 DRO_FOREIGN, FALSE)) {
2567 /* TRANS: describes the target of an action. */
2568 target_adjective = _("foreign ");
2569 } else {
2570 /* Both foreign and domestic targets are acceptable. */
2571 target_adjective = "";
2572 }
2573
2574 sub_target_text[0] = '\0';
2579 /* TRANS: action sub target extras with tile
2580 * extras target. */
2581 _("extras among "));
2582 } else {
2584 /* TRANS: action sub target kind. */
2585 _("%s "),
2588 }
2589 }
2590
2592 /* TRANS: First %s in %s%s%s is the sub target kind.
2593 * The next may be an adjective (that includes a space).
2594 * The next is the name of the target kind.
2595 * Example: "* is done to extras on foreign tiles." */
2596 _(" %s is done to %s%s%s.\n"), BULLET,
2600 }
2601
2604 /* TRANS: said about an action. %s is a unit type
2605 * name. */
2606 _(" %s uses up the %s.\n"), BULLET,
2607 utype_name_translation(utype));
2608 }
2609
2610 if (actres_get_battle_kind(paction->result) != ABK_NONE) {
2612 /* TRANS: The %s is a kind of battle defined in
2613 * actions.h. Example: "diplomatic battle". */
2614 _(" %s can lead to a %s against a defender.\n"),
2615 BULLET,
2618 }
2619
2620 {
2621 struct universal req_pattern[] = {
2622 { .kind = VUT_ACTION, .value.action = paction },
2623 { .kind = VUT_UTYPE, .value.utype = utype },
2624 };
2626
2631 ((100 - odds) * 100
2632 / odds))) {
2634 _(" %s may fail because of a dice throw.\n"),
2635 BULLET);
2636 }
2637 }
2638
2640 && paction->actor.is_unit.moves_actor == MAK_ESCAPE) {
2642 /* TRANS: said about an action. %s is a unit type
2643 * name. */
2644 _(" %s the %s may be captured while trying to"
2645 " escape after completing the mission.\n"),
2646 BULLET,
2647 utype_name_translation(utype));
2648 }
2649
2651 /* The dead don't care about movement loss. */
2652 } else if (utype_action_takes_all_mp(utype, paction)) {
2654 /* TRANS: Indented unit action property, preserve
2655 * leading spaces. */
2656 _(" %s ends this unit's turn.\n"),
2657 BULLET);
2659 USP_NATIVE_TILE)) {
2660 /* Used in the implementation of slow_invasion in many of the
2661 * bundled rulesets and in rulesets upgraded with rscompat from 3.0
2662 * to 3.1. */
2664 /* TRANS: Indented unit action property, preserve
2665 * leading spaces. */
2666 _(" %s ending up on a native tile"
2667 " after this action has been performed"
2668 " ends this unit's turn.\n"), BULLET);
2669 }
2670
2671 if (action_id_get_target_kind(act) != ATK_SELF) {
2672 /* Distance to target is relevant. */
2673
2674 /* FIXME: move paratroopers_range to the action and remove this
2675 * variable once actions are generalized. */
2679 MIN(paction->max_distance, utype->paratroopers_range) :
2680 paction->max_distance;
2681
2682 if (paction->min_distance == relative_max) {
2683 /* Only one distance to target is acceptable */
2684
2685 if (paction->min_distance == 0) {
2687 /* TRANS: distance between an actor unit and its
2688 * target when performing a specific action. */
2689 _(" %s target must be at the same tile.\n"),
2690 BULLET);
2691 } else {
2693 /* TRANS: distance between an actor unit and its
2694 * target when performing a specific action. */
2695 PL_(" %s target must be exactly %d tile away.\n",
2696 " %s target must be exactly %d tiles away.\n",
2697 paction->min_distance),
2698 BULLET, paction->min_distance);
2699 }
2701 /* No max distance */
2702
2703 if (paction->min_distance == 0) {
2705 /* TRANS: distance between an actor unit and its
2706 * target when performing a specific action. */
2707 _(" %s target can be anywhere.\n"), BULLET);
2708 } else {
2710 /* TRANS: distance between an actor unit and its
2711 * target when performing a specific action. */
2712 PL_(" %s target must be at least %d tile away.\n",
2713 " %s target must be at least %d tiles away.\n",
2714 paction->min_distance),
2715 BULLET, paction->min_distance);
2716 }
2717 } else if (paction->min_distance == 0) {
2718 /* No min distance */
2719
2721 /* TRANS: distance between an actor unit and its
2722 * target when performing a specific action. */
2723 PL_(" %s target can be max %d tile away.\n",
2724 " %s target can be max %d tiles away.\n",
2725 relative_max),
2727 } else {
2728 /* Full range. */
2729
2731 /* TRANS: distance between an actor unit and its
2732 * target when performing a specific action. */
2733 PL_(" %s target must be between %d and %d tile away.\n",
2734 " %s target must be between %d and %d tiles away.\n",
2735 relative_max),
2736 BULLET, paction->min_distance, relative_max);
2737 }
2738 }
2739
2740 /* The action may be a Casus Belli. */
2741 {
2742 const struct {
2743 const enum effect_type eft;
2744 const char *hlp_text;
2745 } casus_belli[] = {
2746 /* TRANS: ...performing this action ... Casus Belli */
2747 { EFT_CASUS_BELLI_SUCCESS, N_("successfully") },
2748 /* TRANS: ...performing this action ... Casus Belli */
2749 { EFT_CASUS_BELLI_CAUGHT, N_("getting caught before") },
2750 };
2751
2752 struct universal req_pattern[] = {
2753 { .kind = VUT_ACTION, .value.action = paction },
2754 { .kind = VUT_DIPLREL, /* value filled in later */ },
2755 };
2756
2757 /* First group by effect (currently getting caught and successfully
2758 * performing the action) */
2759 for (i = 0; i < ARRAY_SIZE(casus_belli); i++) {
2760 int diplrel;
2761
2762 /* DiplRel list of each Casus Belli size. */
2763 const char *victim_diplrel_names[DRO_LAST];
2764 const char *outrage_diplrel_names[DRO_LAST];
2765 int victim_diplrel_count = 0;
2766 int outrage_diplrel_count = 0;
2767
2768 /* Ignore Team and everything in diplrel_other. */
2769 for (diplrel = 0; diplrel < DS_NO_CONTACT; diplrel++) {
2771
2772 if (!can_utype_do_act_if_tgt_diplrel(utype, act,
2773 diplrel, TRUE)) {
2774 /* Can't do the action. Can't give Casus Belli. */
2775 continue;
2776 }
2777
2778 req_pattern[1].value.diplrel = diplrel;
2780 casus_belli[i].eft,
2782
2785 diplrel_name_translation(diplrel);
2786 } else if (CASUS_BELLI_VICTIM <= casus_belli_amount) {
2788 diplrel_name_translation(diplrel);
2789 }
2790 }
2791
2792 /* Then group by Casus Belli size (currently victim and
2793 * international outrage) */
2794 if (outrage_diplrel_count > 0) {
2795 struct astring list = ASTRING_INIT;
2797 /* TRANS: successfully ... Peace, or Alliance */
2798 _(" %s %s performing this action during %s causes"
2799 " international outrage: the whole world gets "
2800 "Casus Belli against you.\n"), BULLET,
2804 astr_free(&list);
2805 }
2806 if (victim_diplrel_count > 0) {
2807 struct astring list = ASTRING_INIT;
2809 /* TRANS: successfully ... Peace, or Alliance */
2810 _(" %s %s performing this action during %s gives"
2811 " the victim Casus Belli against you.\n"),
2812 BULLET,
2816 astr_free(&list);
2817 }
2818 }
2819 }
2820
2821 /* Custom action result specific information. */
2822 switch (paction->result) {
2823 case ACTRES_HELP_WONDER:
2825 /* TRANS: the %d is the number of shields the unit can
2826 * contribute. */
2827 _(" %s adds %d production.\n"), BULLET,
2829 break;
2830 case ACTRES_HEAL_UNIT:
2831 {
2832 struct universal req_pattern[] = {
2833 { .kind = VUT_ACTION, .value.action = paction },
2834 { .kind = VUT_UTYPE, .value.utype = utype },
2835 };
2836
2838 _(" %s restores up to %d%% of the target unit's"
2839 " hit points.\n"), BULLET,
2843 + 100);
2844 }
2845 break;
2846 case ACTRES_FOUND_CITY:
2849 /* TRANS: is talking about an action. */
2850 _(" %s is disabled in the current game.\n"),
2851 BULLET);
2852 }
2854 /* TRANS: the %d is initial population. */
2855 PL_(" %s initial population: %d.\n",
2856 " %s initial population: %d.\n",
2857 utype->city_size),
2858 BULLET, utype->city_size);
2859 break;
2860 case ACTRES_JOIN_CITY:
2862 /* TRANS: the %d is population. */
2863 PL_(" %s max target size: %d.\n",
2864 " %s max target size: %d.\n",
2868 /* TRANS: the %d is the population added. */
2869 PL_(" %s adds %d population.\n",
2870 " %s adds %d population.\n",
2871 utype->pop_cost),
2872 BULLET, utype->pop_cost);
2873 break;
2874 case ACTRES_BOMBARD:
2876 /* TRANS: %d is bombard rate. */
2877 _(" %s %d per turn.\n"), BULLET,
2878 utype->bombard_rate);
2880 /* TRANS: talking about bombard */
2881 _(" %s Will damage all"
2882 " defenders on a tile, and have no risk for the"
2883 " attacker.\n"), BULLET);
2884 break;
2887 /* TRANS: %s is a unit type. */
2888 _(" %s upgraded to %s or, when possible, to the unit "
2889 "type it upgrades to.\n"), BULLET,
2891 break;
2892 case ACTRES_ATTACK:
2893 if (game.info.tired_attack) {
2895 _(" %s weaker when tired. If performed with less "
2896 "than a single move point left the attack power "
2897 "is reduced accordingly.\n"), BULLET);
2898 }
2899 break;
2900 case ACTRES_WIPE_UNITS:
2902 _(" %s can wipe stack of units with zero defense.\n"),
2903 BULLET);
2904 break;
2905 case ACTRES_CONVERT:
2907 /* TRANS: %s is a unit type. "MP" = movement points. */
2908 PL_(" %s is converted into %s (takes %d MP).\n",
2909 " %s is converted into %s (takes %d MP).\n",
2910 utype->convert_time),
2911 BULLET,
2913 utype->convert_time);
2914 break;
2915 case ACTRES_SPY_NUKE:
2916 case ACTRES_NUKE:
2917 case ACTRES_NUKE_UNITS:
2918 if (game.info.nuke_pop_loss_pct > 0) {
2920 /* TRANS: percentage */
2921 _(" %s %d%% of the population of each city inside"
2922 " the nuclear blast dies.\n"), BULLET,
2924 if (game.info.nuke_pop_loss_pct < 50) {
2926 _(" %s can never destroy city completely "
2927 "(%d%% of size 1 rounds down to 0).\n"), BULLET,
2929 } else {
2931 _(" %s can even destroy city completely "
2932 "(%d%% of size 1 rounds up to 1).\n"), BULLET,
2934 }
2935 }
2938 _(" %s all units caught in the open by the nuclear"
2939 " blast die.\n"), BULLET);
2941 /* TRANS: percentage */
2942 _(" %s a unit caught in the nuclear blast while"
2943 " inside a city has a %d%% chance of survival.\n"),
2944 BULLET,
2946 } else {
2948 _(" %s all units caught in the nuclear blast"
2949 " die.\n"), BULLET);
2950 }
2951 {
2952 struct universal req_pattern[] = {
2953 { .kind = VUT_ACTION, .value.action = paction },
2954 { .kind = VUT_UTYPE, .value.utype = utype },
2955 };
2956
2957 int blast_radius_1 =
2961
2963 _(" %s has a squared blast radius of %d.\n"),
2965 }
2966
2967 break;
2968 case ACTRES_PLANT:
2969 case ACTRES_CULTIVATE:
2972 _(" %s converts target tile terrain to another"
2973 " type.\n"), BULLET);
2974 break;
2975 case ACTRES_ROAD:
2976 case ACTRES_MINE:
2977 case ACTRES_IRRIGATE:
2978 case ACTRES_BASE:
2979 {
2981 struct strvec *extras_vec = strvec_new();
2982
2983 extra_type_iterate(pextra) {
2984 if (actres_creates_extra(paction->result, pextra)) {
2986 }
2988
2989 if (strvec_size(extras_vec) > 0) {
2991 /* TRANS: %s is list of extra types separated by ',' and 'and' */
2992 cat_snprintf(buf, bufsz, _(" %s builds %s on tiles.\n"),
2995 }
2996
2998 }
2999 break;
3000 case ACTRES_CLEAN:
3001 {
3003 struct strvec *extras_vec = strvec_new();
3004
3005 extra_type_iterate(pextra) {
3006 if (actres_removes_extra(paction->result, pextra)) {
3008 }
3010
3011 if (strvec_size(extras_vec) > 0) {
3013 /* TRANS: list of extras separated by "and" */
3014 cat_snprintf(buf, bufsz, _(" %s cleans %s from tiles.\n"),
3017 }
3018
3020 }
3021 break;
3022 case ACTRES_PILLAGE:
3023 {
3025 struct strvec *extras_vec = strvec_new();
3026
3027 extra_type_iterate(pextra) {
3028 if (actres_removes_extra(paction->result, pextra)) {
3030 }
3032
3033 if (strvec_size(extras_vec) > 0) {
3035 /* TRANS: list of extras separated by "and" */
3036 cat_snprintf(buf, bufsz, _(" %s pillages %s from tiles.\n"),
3039 }
3040
3042 }
3043 break;
3044 case ACTRES_FORTIFY:
3045 {
3046 struct universal unit_is_fortified[] = {
3047 { .kind = VUT_ACTIVITY,
3048 .value = { .activity = ACTIVITY_FORTIFIED }},
3049 { .kind = VUT_UTYPE, .value = { .utype = utype }},
3050 };
3051 int bonus = effect_value_from_universals(
3054
3055 if (utype->defense_strength <= 0
3057 &(struct universal){
3058 .kind = VUT_UTYPE,
3059 .value = { .utype = utype }},
3060 1)
3061 <= 0)) {
3063 /* TRANS: indented unit action property, preserve
3064 * leading spaces */
3065 _(" %s to stay put. No defensive bonus.\n"),
3066 BULLET);
3067 } else if (bonus > 0) {
3069 /* TRANS: indented unit action property, preserve
3070 * leading spaces */
3071 _(" %s granting a %d%% defensive bonus.\n"),
3072 BULLET, bonus);
3073 }
3074 }
3075 break;
3077 {
3078 const char *targets[extra_count()];
3079 int j = 0;
3080
3081 /* Extra being native one is a hard requirement
3082 * Not using unit class native_bases cache here.
3083 * Sometimes it's not initialized when we run this,
3084 * and as this is not performance critical, no point
3085 * in using it conditionally and having this only as
3086 * fallback implementation. */
3088 if (!is_native_extra_to_uclass(pextra, pclass)) {
3089 continue;
3090 }
3091
3092 if (!territory_claiming_base(pextra->data.base)) {
3093 continue;
3094 }
3095
3096 targets[j++] = extra_name_translation(pextra);
3098
3099 if (j > 0) {
3100 struct astring list = ASTRING_INIT;
3101 /* TRANS: indented unit action property, preserve
3102 * leading spaces.
3103 * %s is a list of extra types separated by "and". */
3104 cat_snprintf(buf, bufsz, _(" %s done to %s.\n"),
3105 BULLET,
3106 astr_build_and_list(&list, targets, j));
3107 astr_free(&list);
3108 }
3109 }
3110 break;
3111 default:
3112 /* No action specific details. */
3113 break;
3114 }
3115
3116 /* Custom action sub result specific information. */
3117 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_ENTER)) {
3119 /* TRANS: indented unit action property, preserve
3120 * leading spaces. */
3121 _(" %s if a suitable hut is at the targetet tile it"
3122 " will be entered.\n"), BULLET);
3123 }
3124 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_FRIGHTEN)) {
3126 /* TRANS: indented unit action property, preserve
3127 * leading spaces. */
3128 _(" %s if a suitable hut is at the targetet tile it"
3129 " will be frightened.\n"), BULLET);
3130 }
3131 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_MAY_EMBARK)) {
3133 /* TRANS: indented unit action property, preserve
3134 * leading spaces.
3135 * The %s is the unit type name */
3136 _(" %s the %s may end up loaded into a transport if it"
3137 " can't survive on its own at the target tile.\n"),
3139 }
3140 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_NON_LETHAL)) {
3142 /* TRANS: talking about non lethal attacks */
3143 _(" %s These attacks will only damage (never kill)"
3144 " defenders.\n"), BULLET);
3145 }
3146
3147 i = 0;
3149 const struct action *blocker = action_by_number(blocker_id);
3150
3151 if (!utype_can_do_action(utype, blocker->id)) {
3152 /* Can't block since never legal. */
3153 continue;
3154 }
3155
3156 if (action_would_be_blocked_by(paction, blocker)) {
3157 /* action name alone can be MAX_LEN_NAME, leave space for extra
3158 * characters */
3159 int maxlen = MAX_LEN_NAME + 16;
3160 char *quoted = fc_malloc(maxlen);
3161
3163 /* TRANS: %s is an action that can block another. */
3164 _("\'%s\'"), action_name_translation(blocker));
3165 blockers[i] = quoted;
3166
3167 i++;
3168 }
3170
3171 if (i > 0) {
3172 struct astring blist = ASTRING_INIT;
3173
3175 /* TRANS: %s is a list of actions separated by "or". */
3176 _(" %s can't be done if %s is legal.\n"), BULLET,
3178
3179 astr_free(&blist);
3180
3181 for (; i > 0; i--) {
3182 /* The text was copied above. */
3183 free((char *)(blockers[i - 1]));
3184 }
3185 }
3186 }
3188 action_iterate(act) {
3189 struct action *paction = action_by_number(act);
3190 bool vulnerable;
3191
3192 if (action_by_number(act)->quiet) {
3193 /* The ruleset documents this action it self. */
3194 continue;
3195 }
3196
3197 /* Not relevant */
3201 continue;
3202 }
3203
3204 /* All units are immune to this since its not enabled */
3205 if (!action_is_in_use(paction)) {
3206 continue;
3207 }
3208
3209 /* Must be immune in all cases */
3210 vulnerable = FALSE;
3213 &(enabler->target_reqs))) {
3214 vulnerable = TRUE;
3215 break;
3216 }
3218
3219 if (!vulnerable) {
3221 _("%s Doing the action \'%s\' to this unit"
3222 " is impossible.\n"), BULLET,
3224 }
3226 if (!has_vet_levels) {
3227 /* Only mention this if the game generally does have veteran levels. */
3228 if (game.veteran->levels > 1) {
3229 CATLSTR(buf, bufsz, _("%s Will never achieve veteran status.\n"), BULLET);
3230 }
3231 } else {
3232 /* Not useful currently: */
3233#if 0
3234 /* Some units can never become veteran through combat in practice. */
3237 && utype->defense_strength == 0);
3238#endif
3239 /* FIXME: if we knew the raise chances on the client, we could be
3240 * more specific here about whether veteran status can be acquired
3241 * through combat/missions/work. Should also take into account
3242 * UTYF_NO_VETERAN when writing this text. (Gna patch #4794) */
3243 CATLSTR(buf, bufsz, _("%s May acquire veteran status.\n"), BULLET);
3244 if (utype_veteran_has_power_bonus(utype)) {
3246 || utype->defense_strength > 0) {
3247 CATLSTR(buf, bufsz,
3248 /* TRANS: indented; preserve leading spaces */
3249 _(" %s Veterans have increased strength in combat.\n"),
3250 BULLET);
3251 }
3252 /* SUPERSPY always wins/escapes */
3253 if (utype_has_flag(utype, UTYF_DIPLOMAT)
3254 && !utype_has_flag(utype, UTYF_SUPERSPY)) {
3255 CATLSTR(buf, bufsz,
3256 /* TRANS: indented; preserve leading spaces */
3257 _(" %s Veterans have improved chances in diplomatic "
3258 "contests.\n"), BULLET);
3259 if (utype_may_do_escape_action(utype)) {
3260 CATLSTR(buf, bufsz,
3261 /* TRANS: indented; preserve leading spaces */
3262 _(" %s Veterans are more likely to survive missions.\n"),
3263 BULLET);
3264 }
3265 }
3266 if (utype_has_flag(utype, UTYF_SETTLERS)) {
3267 CATLSTR(buf, bufsz,
3268 /* TRANS: indented; preserve leading spaces */
3269 _(" %s Veterans work faster.\n"), BULLET);
3270 }
3271 }
3272 }
3273 if (strlen(buf) > 0) {
3274 CATLSTR(buf, bufsz, "\n");
3275 }
3276 if (has_vet_levels && utype->veteran) {
3277 /* The case where the unit has only a single veteran level has already
3278 * been handled above, so keep quiet here if that happens */
3279 if (insert_veteran_help(buf, bufsz, utype->veteran,
3280 _("This type of unit has its own veteran levels:"), NULL)) {
3281 CATLSTR(buf, bufsz, "\n\n");
3282 }
3283 }
3284 if (NULL != utype->helptext) {
3285 strvec_iterate(utype->helptext, text) {
3286 CATLSTR(buf, bufsz, "%s\n\n", _(text));
3288 }
3289 CATLSTR(buf, bufsz, "%s", user_text);
3290
3291 return buf;
3292}
3293
3294/************************************************************************/
3299void helptext_advance(char *buf, size_t bufsz, struct player *pplayer,
3300 const char *user_text, int i)
3301{
3302 struct astring astr = ASTRING_INIT;
3304 struct universal source = {
3305 .kind = VUT_ADVANCE,
3306 .value = {.advance = vap}
3307 };
3308 int flagid;
3309
3310 fc_assert_ret(NULL != buf && 0 < bufsz && NULL != user_text);
3312
3313 if (NULL == vap) {
3314 log_error("Unknown tech %d.", i);
3315 return;
3316 }
3317
3318 if (game.control.num_tech_classes > 0) {
3319 if (vap->tclass == NULL) {
3320 cat_snprintf(buf, bufsz, _("Belongs to the default tech class.\n\n"));
3321 } else {
3322 cat_snprintf(buf, bufsz, _("Belongs to tech class %s.\n\n"),
3324 }
3325 }
3326
3327 if (NULL != pplayer) {
3328 const struct research *presearch = research_get(pplayer);
3329
3333
3335 PL_("Starting now, researching %s would need %d bulb.",
3336 "Starting now, researching %s would need %d bulbs.",
3337 bulbs),
3340 /* Split string into two to allow localization of two pluralizations. */
3341 char buf2[MAX_LEN_MSG];
3343
3345 /* TRANS: appended to another sentence. Preserve the
3346 * leading space. */
3347 PL_(" The whole project will require %d bulb to complete.",
3348 " The whole project will require %d bulbs to complete.",
3349 bulbs),
3350 bulbs);
3352 /* TRANS: last %s is a sentence pluralized separately. */
3353 PL_("To research %s you need to research %d other"
3354 " technology first.%s",
3355 "To research %s you need to research %d other"
3356 " technologies first.%s",
3360 } else {
3361 CATLSTR(buf, bufsz,
3362 _("You cannot research this technology."));
3363 }
3366 CATLSTR(buf, bufsz,
3367 /* TRANS: preserve leading space */
3368 _(" This number may vary depending on what "
3369 "other players research.\n"));
3370 } else {
3371 CATLSTR(buf, bufsz, "\n");
3372 }
3373 }
3374
3375 CATLSTR(buf, bufsz, "\n");
3376 }
3377
3378 if (requirement_vector_size(&vap->research_reqs) > 0) {
3379 CATLSTR(buf, bufsz, _("Requirements to research:\n"));
3380 requirement_vector_iterate(&vap->research_reqs, preq) {
3381 (void) req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "");
3383 CATLSTR(buf, bufsz, "\n");
3384 }
3385
3387 BULLET_SPACE);
3388
3389 {
3390 int j;
3391
3392 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
3393 if (game.rgame.global_init_techs[j] == A_LAST) {
3394 break;
3395 } else if (game.rgame.global_init_techs[j] == i) {
3396 CATLSTR(buf, bufsz,
3397 _("%s All players start the game with knowledge of this "
3398 "technology.\n"), BULLET);
3399 break;
3400 }
3401 }
3402 }
3403
3404 /* Assume no-one will set the same tech in both global and nation
3405 * init_tech... */
3406 nations_iterate(pnation) {
3407 int j;
3408
3409 /* Avoid mentioning nations not in current set. */
3410 if (!show_help_for_nation(pnation)) {
3411 continue;
3412 }
3413 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
3414 if (pnation->init_techs[j] == A_LAST) {
3415 break;
3416 } else if (pnation->init_techs[j] == i) {
3418 /* TRANS: %s is a nation plural */
3419 _("%s The %s start the game with knowledge of this "
3420 "technology.\n"), BULLET,
3421 nation_plural_translation(pnation));
3422 break;
3423 }
3424 }
3426
3427 /* Explain the effects of root_reqs. */
3428 {
3430
3434 if (proot == vap) {
3435 /* Don't say anything at all if this tech is a self-root-req one;
3436 * assume that the ruleset help will explain how to get it. */
3438 break;
3439 }
3442 /* Now find out what roots each of this tech's root_req has, so that
3443 * we can suppress them. If tech A has roots B/C, and B has root C,
3444 * it's not worth saying that A needs C, and can lead to overwhelming
3445 * lists. */
3446 /* (Special case: don't do this if the root is a self-root-req tech,
3447 * since it would appear in its own root iteration; in the scenario
3448 * where S is a self-root tech that is root for T, this would prevent
3449 * S appearing in T's help.) */
3450 /* FIXME this is quite inefficient */
3454 }
3456
3457 /* Filter out all but the direct root reqs. */
3459
3460 if (BV_ISSET_ANY(roots)) {
3461 const char *root_techs[A_LAST];
3462 size_t n_roots = 0;
3464
3466 if (BV_ISSET(roots, root)) {
3469 }
3471 fc_assert(n_roots > 0);
3473 /* TRANS: 'and'-separated list of techs */
3474 _("%s Only those who know %s can acquire this "
3475 "technology (by any means).\n"),
3476 BULLET,
3479 }
3480 }
3481
3484 _("%s The first player to learn %s gets"
3485 " an immediate advance.\n"), BULLET,
3487 }
3488
3490 if (advance_has_flag(i, flagid)) {
3491 const char *helptxt = tech_flag_helptxt(flagid);
3492
3493 if (helptxt != NULL) {
3494 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
3495 }
3496 }
3497 }
3498
3500 CATLSTR(buf, bufsz,
3501 _("%s To preserve this technology for our nation some bulbs "
3502 "are needed each turn.\n"), BULLET);
3503 }
3504
3505 if (NULL != vap->helptext) {
3506 if (strlen(buf) > 0) {
3507 CATLSTR(buf, bufsz, "\n");
3508 }
3509 strvec_iterate(vap->helptext, text) {
3510 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3512 }
3513
3514 astr_free(&astr);
3515}
3516
3517/************************************************************************/
3520void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer,
3521 const char *user_text, struct terrain *pterrain)
3522{
3523 struct universal source = {
3524 .kind = VUT_TERRAIN,
3525 .value = {.terrain = pterrain}
3526 };
3527 int flagid;
3528
3529 fc_assert_ret(NULL != buf && 0 < bufsz);
3530 buf[0] = '\0';
3531
3532 if (!pterrain) {
3533 log_error("Unknown terrain!");
3534 return;
3535 }
3536
3538 BULLET_SPACE);
3539 if (terrain_has_flag(pterrain, TER_NO_CITIES)) {
3540 CATLSTR(buf, bufsz,
3541 _("%s You cannot build cities on this terrain.\n"),
3542 BULLET);
3543 }
3545 /* Can't build roads; only mention if ruleset has buildable roads */
3547 if (pextra->buildable) {
3548 CATLSTR(buf, bufsz,
3549 _("%s Paths cannot be built on this terrain.\n"),
3550 BULLET);
3551 break;
3552 }
3554 }
3556 /* Can't build bases; only mention if ruleset has buildable bases */
3558 if (pextra->buildable) {
3559 CATLSTR(buf, bufsz,
3560 _("%s Bases cannot be built on this terrain.\n"),
3561 BULLET);
3562 break;
3563 }
3565 }
3566 if (terrain_has_flag(pterrain, TER_UNSAFE_COAST)
3567 && terrain_type_terrain_class(pterrain) != TC_OCEAN) {
3568 CATLSTR(buf, bufsz,
3569 _("%s The coastline of this terrain is unsafe.\n"),
3570 BULLET);
3571 }
3572 {
3573 const char *classes[uclass_count()];
3574 int i = 0;
3575
3576 unit_class_iterate(uclass) {
3577 if (is_native_to_class(uclass, pterrain, NULL)) {
3578 classes[i++] = uclass_name_translation(uclass);
3579 }
3581
3582 if (0 < i) {
3583 struct astring list = ASTRING_INIT;
3584
3585 /* TRANS: %s is a list of unit classes separated by "and". */
3586 cat_snprintf(buf, bufsz, _("%s Can be traveled by %s units.\n"),
3588 astr_free(&list);
3589 }
3590 }
3591 if (terrain_has_flag(pterrain, TER_NO_ZOC)) {
3592 CATLSTR(buf, bufsz,
3593 _("%s Units on this terrain neither impose zones of control "
3594 "nor are restricted by them.\n"), BULLET);
3595 } else {
3596 CATLSTR(buf, bufsz,
3597 _("%s Units on this terrain may impose a zone of control, or "
3598 "be restricted by one.\n"), BULLET);
3599 }
3600 for (flagid = TER_USER_1 ; flagid <= TER_USER_LAST; flagid++) {
3601 if (terrain_has_flag(pterrain, flagid)) {
3602 const char *helptxt = terrain_flag_helptxt(flagid);
3603
3604 if (helptxt != NULL) {
3605 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
3606 }
3607 }
3608 }
3609
3610 if (NULL != pterrain->helptext) {
3611 if (buf[0] != '\0') {
3612 CATLSTR(buf, bufsz, "\n");
3613 }
3614 strvec_iterate(pterrain->helptext, text) {
3615 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3617 }
3618 if (user_text && user_text[0] != '\0') {
3619 CATLSTR(buf, bufsz, "\n\n%s", user_text);
3620 }
3621}
3622
3623/************************************************************************/
3630const char *helptext_road_bonus_str(const struct terrain *pterrain,
3631 const struct road_type *proad)
3632{
3633 static char str[64];
3634 bool has_effect = FALSE;
3635
3636 str[0] = '\0';
3638 switch (o) {
3639 case O_FOOD:
3640 case O_SHIELD:
3641 case O_TRADE:
3642 {
3643 int bonus = proad->tile_bonus[o];
3644 int incr = proad->tile_incr_const[o];
3645
3646 if (pterrain) {
3647 incr +=
3648 proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
3649 }
3650 if (str[0] != '\0') {
3651 CATLSTR(str, sizeof(str), "/");
3652 }
3653 if (incr == 0 && bonus == 0) {
3654 cat_snprintf(str, sizeof(str), "%d", incr);
3655 } else {
3656 has_effect = TRUE;
3657 if (incr != 0) {
3658 cat_snprintf(str, sizeof(str), "%+d", incr);
3659 }
3660 if (bonus != 0) {
3661 cat_snprintf(str, sizeof(str), "%+d%%", bonus);
3662 }
3663 }
3664 }
3665 break;
3666 default:
3667 /* FIXME: there's nothing actually stopping roads having gold, etc
3668 * bonuses */
3669 fc_assert(proad->tile_incr_const[o] == 0
3670 && proad->tile_incr[o] == 0
3671 && proad->tile_bonus[o] == 0);
3672 break;
3673 }
3675
3676 return has_effect ? str : NULL;
3677}
3678
3679/**********************************************************************/
3685static void extra_bonus_for_terrain(struct extra_type *pextra,
3686 struct terrain *pterrain,
3687 int *bonus)
3688{
3689 struct universal req_pattern[] = {
3690 { .kind = VUT_EXTRA, .value.extra = pextra },
3691 { .kind = VUT_TERRAIN, .value.terrain = pterrain },
3692 { .kind = VUT_OTYPE /* value filled in later */ }
3693 };
3694
3695 fc_assert_ret(bonus != NULL);
3696
3697 /* Irrigation-like food bonuses */
3698 bonus[0] = (pterrain->irrigation_food_incr
3700 2 /* just extra+terrain */)) / 100;
3701
3702 /* Mining-like shield bonuses */
3703 bonus[1] = (pterrain->mining_shield_incr
3705 2 /* just extra+terrain */)) / 100;
3706
3707 bonus[2] = 0; /* no trade bonuses so far */
3708
3709 /* Now add fixed bonuses from roads (but not percentage bonus) */
3710 if (extra_road_get(pextra)) {
3711 const struct road_type *proad = extra_road_get(pextra);
3712
3714 switch (o) {
3715 case O_FOOD:
3716 case O_SHIELD:
3717 case O_TRADE:
3718 bonus[o] += proad->tile_incr_const[o]
3719 + proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
3720 break;
3721 default:
3722 /* not dealing with other output types here */
3723 break;
3724 }
3726 }
3727
3728 /* Fixed bonuses for extra, possibly unrelated to terrain type */
3729
3731 /* Fill in rest of requirement template */
3732 req_pattern[2].value.outputtype = o;
3733 switch (o) {
3734 case O_FOOD:
3735 case O_SHIELD:
3736 case O_TRADE:
3740 /* Any of the above bonuses is sufficient to trigger
3741 * Output_Inc_Tile, if underlying terrain does not */
3742 if (bonus[o] > 0 || pterrain->output[o] > 0) {
3746 }
3747 break;
3748 default:
3749 break;
3750 }
3752}
3753
3754/**********************************************************************/
3761 struct terrain *pterrain,
3762 enum unit_activity act)
3763{
3764 static char buffer[256];
3765 int btime;
3766 int bonus[3];
3767
3768 btime = terrain_extra_build_time(pterrain, act, pextra);
3769 fc_snprintf(buffer, sizeof(buffer), PL_("%d turn", "%d turns", btime),
3770 btime);
3771 extra_bonus_for_terrain(pextra, pterrain, bonus);
3772 if (bonus[0] > 0) {
3773 cat_snprintf(buffer, sizeof(buffer),
3774 PL_(", +%d food", ", +%d food", bonus[0]), bonus[0]);
3775 }
3776 if (bonus[1] > 0) {
3777 cat_snprintf(buffer, sizeof(buffer),
3778 PL_(", +%d shield", ", +%d shields", bonus[1]), bonus[1]);
3779 }
3780 if (bonus[2] > 0) {
3781 cat_snprintf(buffer, sizeof(buffer),
3782 PL_(", +%d trade", ", +%d trade", bonus[2]), bonus[2]);
3783 }
3784
3785 return buffer;
3786}
3787
3788/************************************************************************/
3794void helptext_extra(char *buf, size_t bufsz, struct player *pplayer,
3795 const char *user_text, struct extra_type *pextra)
3796{
3797 size_t group_start;
3798 struct base_type *pbase;
3799 struct road_type *proad;
3800 struct universal source = {
3801 .kind = VUT_EXTRA,
3802 .value = {.extra = pextra}
3803 };
3804
3805 int flagid;
3806
3807 fc_assert_ret(NULL != buf && 0 < bufsz);
3808 buf[0] = '\0';
3809
3810 if (!pextra) {
3811 log_error("Unknown extra!");
3812 return;
3813 }
3814
3815 if (is_extra_caused_by(pextra, EC_BASE)) {
3816 pbase = pextra->data.base;
3817 } else {
3818 pbase = NULL;
3819 }
3820
3821 if (is_extra_caused_by(pextra, EC_ROAD)) {
3822 proad = pextra->data.road;
3823 } else {
3824 proad = NULL;
3825 }
3826
3827 if (pextra->helptext != NULL) {
3828 strvec_iterate(pextra->helptext, text) {
3829 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3831 }
3832
3833 /* Describe how extra is created and destroyed */
3834
3836
3837 if (pextra->buildable) {
3838 if (is_extra_caused_by(pextra, EC_IRRIGATION)) {
3839 CATLSTR(buf, bufsz,
3840 _("Build by issuing an \"irrigate\" order.\n"));
3841 }
3842 if (is_extra_caused_by(pextra, EC_MINE)) {
3843 CATLSTR(buf, bufsz,
3844 _("Build by issuing a \"mine\" order.\n"));
3845 }
3846 if (is_extra_caused_by(pextra, EC_ROAD)) {
3847 CATLSTR(buf, bufsz,
3848 _("Build by issuing a \"road\" order.\n"));
3849 }
3850 if (is_extra_caused_by(pextra, EC_BASE)) {
3851 fc_assert(pbase != NULL);
3852
3853 if (pbase->gui_type == BASE_GUI_OTHER) {
3855 _("Build by issuing a \"build base\" order.\n"));
3856 } else {
3857 const char *order = "";
3858
3859 switch (pbase->gui_type) {
3860 case BASE_GUI_FORTRESS:
3861 order = Q_(terrain_control.gui_type_base0);
3862 break;
3863 case BASE_GUI_AIRBASE:
3864 order = Q_(terrain_control.gui_type_base1);
3865 break;
3866 default:
3868 break;
3869 }
3871 /* TRANS: %s is a gui_type base string from a ruleset */
3872 _("Build by issuing a \"%s\" order.\n"), order);
3873 }
3874 }
3875 }
3876
3877 if (is_extra_caused_by(pextra, EC_POLLUTION)) {
3878 CATLSTR(buf, bufsz,
3879 _("May randomly appear around polluting city.\n"));
3880 }
3881
3882 if (is_extra_caused_by(pextra, EC_FALLOUT)) {
3883 CATLSTR(buf, bufsz,
3884 _("May randomly appear around nuclear blast.\n"));
3885 }
3886
3887 if (pextra->generated
3888 && (is_extra_caused_by(pextra, EC_HUT)
3890 || (proad != NULL && road_has_flag(proad, RF_RIVER)))) {
3891 CATLSTR(buf, bufsz,
3892 _("Placed by map generator.\n"));
3893 }
3894
3895 if (is_extra_removed_by(pextra, ERM_ENTER)) {
3896 CATLSTR(buf, bufsz,
3897 _("Can be explored by certain units.\n"));
3898 }
3899
3900 if (is_extra_caused_by(pextra, EC_APPEARANCE)) {
3901 CATLSTR(buf, bufsz,
3902 _("May appear spontaneously.\n"));
3903 }
3904
3905 if (requirement_vector_size(&pextra->reqs) > 0) {
3906 char reqsbuf[8192] = "";
3907 bool buildable = pextra->buildable
3909
3911 (void) req_text_insert_nl(reqsbuf, sizeof(reqsbuf), pplayer, preq,
3913 buildable ? BULLET_SPACE : "");
3915 if (reqsbuf[0] != '\0') {
3916 if (buildable) {
3917 CATLSTR(buf, bufsz, _("Requirements to build:\n"));
3918 }
3919 CATLSTR(buf, bufsz, "%s", reqsbuf);
3920 }
3921 }
3922
3923 if (pextra->infracost > 0) {
3924 cat_snprintf(buf, bufsz, _("Cost: %d\n"), pextra->infracost);
3925 }
3926
3927 if (buf[group_start] != '\0') {
3928 CATLSTR(buf, bufsz, "\n"); /* group separator */
3929 }
3930
3932
3933 if (is_extra_removed_by(pextra, ERM_PILLAGE)) {
3934 int pillage_time = -1;
3935
3936 if (pextra->removal_time != 0) {
3937 pillage_time = pextra->removal_time;
3938 } else {
3939 terrain_type_iterate(pterrain) {
3940 int terr_pillage_time = pterrain->pillage_time
3941 * pextra->removal_time_factor;
3942
3943 if (terr_pillage_time != 0) {
3944 if (pillage_time < 0) {
3945 pillage_time = terr_pillage_time;
3946 } else if (pillage_time != terr_pillage_time) {
3947 /* Give up */
3948 pillage_time = -1;
3949 break;
3950 }
3951 }
3953 }
3954 if (pillage_time < 0) {
3955 CATLSTR(buf, bufsz,
3956 _("Can be pillaged by units (time is terrain-dependent).\n"));
3957 } else if (pillage_time > 0) {
3959 PL_("Can be pillaged by units (takes %d turn).\n",
3960 "Can be pillaged by units (takes %d turns).\n",
3961 pillage_time), pillage_time);
3962 }
3963 }
3964 if (is_extra_removed_by(pextra, ERM_CLEAN)) {
3965 int clean_time = -1;
3966
3967 if (pextra->removal_time != 0) {
3968 clean_time = pextra->removal_time;
3969 } else {
3970 terrain_type_iterate(pterrain) {
3971 int terr_clean_time = -1;
3972 int rmtime = pterrain->extra_removal_times[extra_index(pextra)];
3973
3974 if (rmtime != 0) {
3976 }
3977
3978 if (clean_time < 0) {
3980 } else if (clean_time != terr_clean_time) {
3981 /* Give up */
3982 clean_time = -1;
3983 break;
3984 }
3986 }
3987
3988 if (clean_time < 0) {
3989 CATLSTR(buf, bufsz,
3990 _("Can be cleaned by units (time is terrain-dependent).\n"));
3991 } else if (clean_time > 0) {
3993 PL_("Can be cleaned by units (takes %d turn).\n",
3994 "Can be cleaned by units (takes %d turns).\n",
3996 }
3997 }
3998
3999 if (requirement_vector_size(&pextra->rmreqs) > 0) {
4000 char reqsbuf[8192] = "";
4001
4003 (void) req_text_insert_nl(reqsbuf, sizeof(reqsbuf), pplayer, preq,
4006 if (reqsbuf[0] != '\0') {
4007 CATLSTR(buf, bufsz, _("Requirements to remove:\n"));
4008 CATLSTR(buf, bufsz, "%s", reqsbuf);
4009 }
4010 }
4011
4012 if (buf[group_start] != '\0') {
4013 CATLSTR(buf, bufsz, "\n"); /* group separator */
4014 }
4015
4016 /* Describe what other elements are enabled by extra */
4017
4019
4021
4022 if (buf[group_start] != '\0') {
4023 CATLSTR(buf, bufsz, "\n"); /* group separator */
4024 }
4025
4026 /* Describe other properties of extras */
4027
4028 if (pextra->visibility_req != A_NONE) {
4029 char vrbuf[1024];
4030
4031 fc_snprintf(vrbuf, sizeof(vrbuf),
4032 _("%s Visible only if %s known.\n"), BULLET,
4034 CATLSTR(buf, bufsz, "%s", vrbuf);
4035 }
4036
4037 if (pextra->eus == EUS_HIDDEN) {
4038 CATLSTR(buf, bufsz,
4039 _("%s Units inside are hidden from non-allied players.\n"),
4040 BULLET);
4041 }
4042
4043 {
4044 const char *classes[uclass_count()];
4045 int i = 0;
4046
4047 unit_class_iterate(uclass) {
4048 if (is_native_extra_to_uclass(pextra, uclass)) {
4049 classes[i++] = uclass_name_translation(uclass);
4050 }
4052
4053 if (0 < i) {
4054 struct astring list = ASTRING_INIT;
4055
4056 if (proad != NULL) {
4057 /* TRANS: %s is a list of unit classes separated by "and". */
4058 cat_snprintf(buf, bufsz, _("%s Can be traveled by %s units.\n"),
4059 BULLET,
4061 } else {
4062 /* TRANS: %s is a list of unit classes separated by "and". */
4063 cat_snprintf(buf, bufsz, _("%s Native to %s units.\n"),
4064 BULLET,
4066 }
4067 astr_free(&list);
4068
4069 if (extra_has_flag(pextra, EF_NATIVE_TILE)) {
4070 CATLSTR(buf, bufsz,
4071 /* TRANS: indented; preserve leading spaces */
4072 _(" %s Such units can move onto this tile even if it would "
4073 "not normally be suitable terrain.\n"), BULLET);
4074 }
4075
4076 if (pextra->no_aggr_near_city >= 0) {
4077 CATLSTR(buf, bufsz,
4078 /* TRANS: indented; preserve leading spaces */
4079 PL_(" %s Such units situated here are not considered aggressive "
4080 "if this tile is within %d tile of a friendly city.\n",
4081 " %s Such units situated here are not considered aggressive "
4082 "if this tile is within %d tiles of a friendly city.\n",
4083 pextra->no_aggr_near_city),
4084 BULLET, pextra->no_aggr_near_city);
4085 }
4086
4087 if (pextra->defense_bonus) {
4089 /* TRANS: indented; preserve leading spaces */
4090 _(" %s Such units get a %d%% defense bonus on this "
4091 "tile.\n"), BULLET,
4092 pextra->defense_bonus);
4093 }
4094 }
4095 }
4096
4098 const char *conquerors[utype_count()];
4099 int i = 0;
4100
4105 }
4107
4108 if (i > 0) {
4109 struct astring list = ASTRING_INIT;
4111 /* TRANS: %s is a list of unit types separated by "and". */
4112 _("%s Can be conquered by %s.\n"), BULLET,
4114 astr_free(&list);
4115 }
4116 }
4117
4119 if (proad->move_cost == 0) {
4120 CATLSTR(buf, bufsz, _("%s Allows infinite movement.\n"), BULLET);
4121 } else {
4123 /* TRANS: "MP" = movement points. Second %s may have a
4124 * fractional part. */
4125 _("%s Movement cost along %s is %s MP.\n"),
4126 BULLET,
4127 extra_name_translation(pextra),
4128 move_points_text(proad->move_cost, TRUE));
4129 }
4130 }
4131
4132 if (game.info.killstack
4133 && extra_has_flag(pextra, EF_NO_STACK_DEATH)) {
4134 CATLSTR(buf, bufsz,
4135 _("%s Defeat of one unit does not cause death of all other units "
4136 "on this tile.\n"), BULLET);
4137 }
4138 if (pbase != NULL) {
4140 CATLSTR(buf, bufsz,
4141 _("%s Extends national borders of the building nation.\n"),
4142 BULLET);
4143 }
4144 if (pbase->vision_main_sq >= 0) {
4145 CATLSTR(buf, bufsz,
4146 _("%s Grants permanent vision of an area around the tile to "
4147 "its owner.\n"), BULLET);
4148 }
4149 if (pbase->vision_invis_sq >= 0) {
4150 CATLSTR(buf, bufsz,
4151 _("%s Allows the owner to see normally invisible stealth units "
4152 "in an area around the tile.\n"), BULLET);
4153 }
4154 if (pbase->vision_subs_sq >= 0) {
4155 CATLSTR(buf, bufsz,
4156 _("%s Allows the owner to see normally invisible subsurface units "
4157 "in an area around the tile.\n"), BULLET);
4158 }
4159 }
4161 if (extra_has_flag(pextra, flagid)) {
4162 const char *helptxt = extra_flag_helptxt(flagid);
4163
4164 if (helptxt != NULL) {
4165 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
4166 }
4167 }
4168 }
4169
4170 /* Table of terrain-specific attributes, if needed */
4171 if (proad != NULL || pbase != NULL) {
4172 bool road, do_time, do_bonus;
4173
4174 road = (proad != NULL);
4175 /* Terrain-dependent build time? */
4176 do_time = pextra->buildable && pextra->build_time == 0;
4177 if (road) {
4178 /* Terrain-dependent output bonus? */
4179 do_bonus = FALSE;
4181 if (proad->tile_incr[o] > 0) {
4182 do_bonus = TRUE;
4183 fc_assert(o == O_FOOD || o == O_SHIELD || o == O_TRADE);
4184 }
4186 } else {
4187 /* Bases don't have output bonuses */
4188 do_bonus = FALSE;
4189 }
4190
4191 if (do_time || do_bonus) {
4192 if (do_time && do_bonus) {
4193 CATLSTR(buf, bufsz,
4194 _("\nTime to build and output bonus depends on terrain:\n\n"));
4195 CATLSTR(buf, bufsz,
4196 /* TRANS: Header for fixed-width road properties table.
4197 * TRANS: Translators cannot change column widths :( */
4198 _("Terrain Time Bonus F/P/T\n"
4199 "----------------------------------\n"));
4200 } else if (do_time) {
4201 CATLSTR(buf, bufsz,
4202 _("\nTime to build depends on terrain:\n\n"));
4203 CATLSTR(buf, bufsz,
4204 /* TRANS: Header for fixed-width extra properties table.
4205 * TRANS: Translators cannot change column widths :( */
4206 _("Terrain Time\n"
4207 "------------------\n"));
4208 } else {
4210 CATLSTR(buf, bufsz,
4211 /* TRANS: Header for fixed-width road properties table.
4212 * TRANS: Translators cannot change column widths :( */
4213 _("\nYields an output bonus with some terrains:\n\n"));
4214 CATLSTR(buf, bufsz,
4215 _("Terrain Bonus F/P/T\n"
4216 "-------------------------\n"));;
4217 }
4219 int turns = road ? terrain_extra_build_time(t, ACTIVITY_GEN_ROAD, pextra)
4221 const char *bonus_text
4223 if (turns > 0 || bonus_text) {
4224 const char *terrain = terrain_name_translation(t);
4226
4228 "%s%*s ", terrain,
4229 MAX(0, slen),
4230 "");
4231 if (do_time) {
4232 if (turns > 0) {
4233 cat_snprintf(buf, bufsz, "%3d ", turns);
4234 } else {
4235 CATLSTR(buf, bufsz, " - ");
4236 }
4237 }
4238 if (do_bonus) {
4239 fc_assert(proad != NULL);
4240 cat_snprintf(buf, bufsz, " %s", bonus_text ? bonus_text : "-");
4241 }
4242 CATLSTR(buf, bufsz, "\n");
4243 }
4245 } /* else rely on client-specific display */
4246 }
4247
4248 if (user_text && user_text[0] != '\0') {
4249 CATLSTR(buf, bufsz, "\n\n%s", user_text);
4250 }
4251}
4252
4253/************************************************************************/
4259void helptext_goods(char *buf, size_t bufsz, struct player *pplayer,
4260 const char *user_text, struct goods_type *pgood)
4261{
4262 bool reqs = FALSE;
4263
4264 fc_assert_ret(NULL != buf && 0 < bufsz);
4265 buf[0] = '\0';
4266
4267 if (NULL != pgood->helptext) {
4268 strvec_iterate(pgood->helptext, text) {
4269 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4271 }
4272
4273 if (pgood->onetime_pct == 0) {
4275 _("There's no bonuses paid when trade route gets established.\n\n"));
4276 } else if (pgood->onetime_pct != 100) {
4278 _("When trade route gets established, %d%% of the normal bonus is paid.\n"),
4279 pgood->onetime_pct);
4280 }
4281 cat_snprintf(buf, bufsz, _("Sending city enjoys %d%% income from the route.\n"),
4282 pgood->from_pct);
4283 cat_snprintf(buf, bufsz, _("Receiving city enjoys %d%% income from the route.\n\n"),
4284 pgood->to_pct);
4285
4286 /* Requirements for this good. */
4288 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
4289 reqs = TRUE;
4290 }
4292 if (reqs) {
4293 fc_strlcat(buf, "\n", bufsz);
4294 }
4295
4296 CATLSTR(buf, bufsz, "%s", user_text);
4297}
4298
4299/************************************************************************/
4305void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer,
4306 const char *user_text, struct specialist *pspec)
4307{
4308 bool reqs = FALSE;
4309
4310 fc_assert_ret(NULL != buf && 0 < bufsz);
4311 buf[0] = '\0';
4312
4313 if (NULL != pspec->helptext) {
4314 strvec_iterate(pspec->helptext, text) {
4315 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4317 }
4318
4319 /* Requirements for this specialist. */
4321 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
4322 reqs = TRUE;
4323 }
4325 if (reqs) {
4326 fc_strlcat(buf, "\n", bufsz);
4327 }
4328
4329 CATLSTR(buf, bufsz, "%s", user_text);
4330}
4331
4332/************************************************************************/
4340void helptext_government(char *buf, size_t bufsz, struct player *pplayer,
4341 const char *user_text, struct government *gov)
4342{
4343 bool reqs = FALSE;
4344 struct universal source = {
4346 .value = {.govern = gov}
4347 };
4348
4349 fc_assert_ret(NULL != buf && 0 < bufsz);
4350 buf[0] = '\0';
4351
4352 if (NULL != gov->helptext) {
4353 strvec_iterate(gov->helptext, text) {
4354 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4356 }
4357
4358 /* Add requirement text for government itself */
4360 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
4361 reqs = TRUE;
4362 }
4364 if (reqs) {
4365 fc_strlcat(buf, "\n", bufsz);
4366 }
4367
4368 /* Effects */
4369 CATLSTR(buf, bufsz, _("Features:\n"));
4371 BULLET_SPACE);
4374 struct unit_class *unitclass = NULL;
4375 const struct unit_type *unittype = NULL;
4377 struct strvec *outputs = strvec_new();
4380 bool too_complex = FALSE;
4381 bool world_value_valid = TRUE;
4382
4383 /* Grab output type, if there is one */
4385 /* Treat an effect with any negated requirements as too complex for
4386 * us to explain here.
4387 * Also don't try to explain an effect with any requirements explicitly
4388 * marked as 'quiet' by ruleset author. */
4389 if (!preq->present || preq->quiet) {
4390 too_complex = TRUE;
4391 continue;
4392 }
4393 switch (preq->source.kind) {
4394 case VUT_OTYPE:
4395 /* We should never have multiple outputtype requirements
4396 * in one list in the first place (it simply makes no sense,
4397 * output cannot be of multiple types)
4398 * Ruleset loading code should check against that. */
4400 output_type = preq->source.value.outputtype;
4402 break;
4403 case VUT_UCLASS:
4405 unitclass = preq->source.value.uclass;
4406 /* FIXME: can't easily get world bonus for unit class */
4408 break;
4409 case VUT_UTYPE:
4410 fc_assert(unittype == NULL);
4411 unittype = preq->source.value.utype;
4412 break;
4413 case VUT_UTFLAG:
4414 if (!unit_type_flag_id_is_valid(unitflag)) {
4415 unitflag = preq->source.value.unitflag;
4416 /* FIXME: can't easily get world bonus for unit type flag */
4418 } else {
4419 /* Already have a unit flag requirement. More than one is too
4420 * complex for us to explain, so say nothing. */
4421 /* FIXME: we could handle this */
4422 too_complex = TRUE;
4423 }
4424 break;
4425 case VUT_GOVERNMENT:
4426 /* This is government we are generating helptext for.
4427 * ...or if not, it's ruleset bug that should never make it
4428 * this far. Fix ruleset loading code. */
4429 fc_assert(preq->source.value.govern == gov);
4430 break;
4431 default:
4432 too_complex = TRUE;
4434 break;
4435 };
4437
4438 if (!too_complex) {
4439 /* Only list effects that don't have extra requirements too complex
4440 * for us to handle.
4441 * Anything more complicated will have to be documented by hand by the
4442 * ruleset author. */
4443
4444 /* Guard condition for simple player-wide effects descriptions.
4445 * (FIXME: in many cases, e.g. EFT_MAKE_CONTENT, additional requirements
4446 * like unittype will be ignored for gameplay, but will affect our
4447 * help here.) */
4448 const bool playerwide
4449 = world_value_valid && !unittype && (output_type == O_LAST);
4450 /* In some cases we give absolute values (world bonus + gov bonus).
4451 * We assume the fact that there's an effect with a gov requirement
4452 * is sufficient reason to list it in that gov's help.
4453 * Guard accesses to these with 'playerwide' or 'world_value_valid'. */
4454 int world_value = -999, net_value = -999;
4455 if (world_value_valid) {
4456 /* Get government-independent world value of effect if the extra
4457 * requirements were simple enough. */
4458 struct output_type *potype =
4460
4461 world_value =
4463 &(const struct req_context) {
4464 .unittype = unittype,
4465 .output = potype,
4466 },
4467 NULL,
4468 peffect->type);
4469 net_value = peffect->value + world_value;
4470 }
4471
4472 if (output_type == O_LAST) {
4473 /* There was no outputtype requirement. Effect is active for all
4474 * output types. Generate lists for that. */
4475 bool harvested_only = TRUE; /* Consider only output types from fields */
4476
4477 if (peffect->type == EFT_UPKEEP_FACTOR
4479 || peffect->type == EFT_OUTPUT_BONUS
4480 || peffect->type == EFT_OUTPUT_BONUS_2) {
4481 /* Effect can use or require any kind of output */
4483 }
4484
4486 struct output_type *pot = get_output_type(ot);
4487
4488 if (!harvested_only || pot->harvested) {
4489 strvec_append(outputs, _(pot->name));
4490 }
4492 }
4493
4494 if (0 == strvec_size(outputs)) {
4495 /* TRANS: Empty output type list, should never happen. */
4496 astr_set(&outputs_or, "%s", Q_("?outputlist: Nothing "));
4497 astr_set(&outputs_and, "%s", Q_("?outputlist: Nothing "));
4498 } else {
4501 }
4502
4503 switch (peffect->type) {
4504 case EFT_UNHAPPY_FACTOR:
4505 if (playerwide) {
4506 /* FIXME: EFT_MAKE_CONTENT_MIL_PER would cancel this out. We assume
4507 * no-one will set both, so we don't bother handling it. */
4509 PL_("%s Military units away from home and field units"
4510 " will each cause %d citizen to become unhappy.\n",
4511 "%s Military units away from home and field units"
4512 " will each cause %d citizens to become unhappy.\n",
4513 net_value),
4514 BULLET, net_value);
4515 } /* else too complicated or silly ruleset */
4516 break;
4518 if (playerwide && net_value != world_value) {
4519 if (world_value > 0) {
4520 if (net_value > 0) {
4522 _("%s Unhappiness from foreign citizens due to "
4523 "war with their home state is %d%% the usual "
4524 "value.\n"), BULLET,
4525 (net_value * 100) / world_value);
4526 } else {
4527 CATLSTR(buf, bufsz,
4528 _("%s No unhappiness from foreign citizens even when "
4529 "at war with their home state.\n"), BULLET);
4530 }
4531 } else {
4533 /* TRANS: not pluralised as gettext doesn't support
4534 * fractional numbers, which this might be */
4535 _("%s Each foreign citizen causes %.2g unhappiness "
4536 "in their city while you are at war with their "
4537 "home state.\n"), BULLET,
4538 (double)net_value / 100);
4539 }
4540 }
4541 break;
4543 if (playerwide) {
4545 PL_("%s Each of your cities will avoid %d unhappiness"
4546 " caused by units.\n",
4547 "%s Each of your cities will avoid %d unhappiness"
4548 " caused by units.\n",
4549 peffect->value),
4550 BULLET, peffect->value);
4551 }
4552 break;
4553 case EFT_MAKE_CONTENT:
4554 if (playerwide) {
4556 PL_("%s Each of your cities will avoid %d unhappiness,"
4557 " not including that caused by aggression.\n",
4558 "%s Each of your cities will avoid %d unhappiness,"
4559 " not including that caused by aggression.\n",
4560 peffect->value),
4561 BULLET, peffect->value);
4562 }
4563 break;
4564 case EFT_FORCE_CONTENT:
4565 if (playerwide) {
4567 PL_("%s Each of your cities will avoid %d unhappiness,"
4568 " including that caused by aggression.\n",
4569 "%s Each of your cities will avoid %d unhappiness,"
4570 " including that caused by aggression.\n",
4571 peffect->value),
4572 BULLET, peffect->value);
4573 }
4574 break;
4575 case EFT_UPKEEP_FACTOR:
4576 if (world_value_valid && !unittype) {
4577 if (net_value == 0) {
4578 if (output_type != O_LAST) {
4580 /* TRANS: %s is the output type, like 'shield'
4581 * or 'gold'. */
4582 _("%s You pay no %s upkeep for your units.\n"),
4584 } else {
4585 CATLSTR(buf, bufsz,
4586 _("%s You pay no upkeep for your units.\n"),
4587 BULLET);
4588 }
4589 } else if (net_value != world_value) {
4590 double ratio = (double)net_value / world_value;
4591 if (output_type != O_LAST) {
4593 /* TRANS: %s is the output type, like 'shield'
4594 * or 'gold'. */
4595 _("%s You pay %.2g times normal %s upkeep for your "
4596 "units.\n"), BULLET,
4598 } else {
4600 _("%s You pay %.2g times normal upkeep for your "
4601 "units.\n"), BULLET,
4602 ratio);
4603 }
4604 } /* else this effect somehow has no effect; keep quiet */
4605 } /* else there was some extra condition making it complicated */
4606 break;
4608 if (!unittype) {
4609 if (output_type != O_LAST) {
4611 /* TRANS: %s is the output type, like 'shield' or
4612 * 'gold'; pluralised in %d but there is currently
4613 * no way to control the singular/plural name of the
4614 * output type; sorry */
4615 PL_("%s Each of your cities will avoid paying %d %s"
4616 " upkeep for your units.\n",
4617 "%s Each of your cities will avoid paying %d %s"
4618 " upkeep for your units.\n", peffect->value),
4619 BULLET,
4620 peffect->value, astr_str(&outputs_and));
4621 } else {
4623 /* TRANS: Amount is subtracted from upkeep cost
4624 * for each upkeep type. */
4625 PL_("%s Each of your cities will avoid paying %d"
4626 " upkeep for your units.\n",
4627 "%s Each of your cities will avoid paying %d"
4628 " upkeep for your units.\n", peffect->value),
4629 BULLET, peffect->value);
4630 }
4631 } /* else too complicated */
4632 break;
4634 if (playerwide) {
4636 _("%s If you lose your capital,"
4637 " the base chance of civil war is %d%%.\n"),
4638 BULLET, net_value);
4639 }
4640 break;
4642 if (playerwide) {
4644 PL_("%s You can have %d city before an "
4645 "additional unhappy citizen appears in each city "
4646 "due to civilization size.\n",
4647 "%s You can have up to %d cities before an "
4648 "additional unhappy citizen appears in each city "
4649 "due to civilization size.\n", net_value),
4650 BULLET, net_value);
4651 }
4652 break;
4654 if (playerwide) {
4656 PL_("%s After the first unhappy citizen due to"
4657 " civilization size, for each %d additional city"
4658 " another unhappy citizen will appear.\n",
4659 "%s After the first unhappy citizen due to"
4660 " civilization size, for each %d additional cities"
4661 " another unhappy citizen will appear.\n",
4662 net_value),
4663 BULLET, net_value);
4664 }
4665 break;
4666 case EFT_MAX_RATES:
4668 if (net_value < 100) {
4670 _("%s The maximum rate you can set for science,"
4671 " gold, or luxuries is %d%%.\n"),
4672 BULLET, net_value);
4673 } else {
4674 CATLSTR(buf, bufsz,
4675 _("%s Has unlimited science/gold/luxuries rates.\n"),
4676 BULLET);
4677 }
4678 }
4679 break;
4681 if (playerwide) {
4683 PL_("%s Your units may impose martial law."
4684 " Each military unit inside a city will force %d"
4685 " unhappy citizen to become content.\n",
4686 "%s Your units may impose martial law."
4687 " Each military unit inside a city will force %d"
4688 " unhappy citizens to become content.\n",
4689 peffect->value),
4690 BULLET, peffect->value);
4691 }
4692 break;
4694 if (playerwide && net_value < 100) {
4696 PL_("%s A maximum of %d unit in each city can enforce"
4697 " martial law.\n",
4698 "%s A maximum of %d units in each city can enforce"
4699 " martial law.\n",
4700 net_value),
4701 BULLET, net_value);
4702 }
4703 break;
4704 case EFT_RAPTURE_GROW:
4705 if (playerwide && net_value > 0) {
4707 _("%s You may grow your cities by means of "
4708 "celebrations."), BULLET);
4709 if (game.info.celebratesize > 1) {
4711 /* TRANS: Preserve leading space. %d should always be
4712 * 2 or greater. */
4713 _(" (Cities below size %d cannot grow in this way.)"),
4715 }
4716 cat_snprintf(buf, bufsz, "\n");
4717 }
4718 break;
4720 if (playerwide) {
4722 PL_("%s If a city is in disorder for more than %d turn "
4723 "in a row, government will fall into anarchy.\n",
4724 "%s If a city is in disorder for more than %d turns "
4725 "in a row, government will fall into anarchy.\n",
4726 net_value),
4727 BULLET, net_value);
4728 }
4729 break;
4730 case EFT_HAS_SENATE:
4731 if (playerwide && net_value > 0) {
4732 CATLSTR(buf, bufsz,
4733 _("%s Has a senate that may prevent declaration of war.\n"),
4734 BULLET);
4735 }
4736 break;
4738 if (playerwide && net_value > 0) {
4739 CATLSTR(buf, bufsz,
4740 _("%s Allows partisans when cities are taken by the "
4741 "enemy.\n"), BULLET);
4742 }
4743 break;
4745 if (playerwide && net_value > 0) {
4746 CATLSTR(buf, bufsz,
4747 _("%s Buildings that normally confer bonuses against"
4748 " unhappiness will instead give gold.\n"), BULLET);
4749 }
4750 break;
4751 case EFT_FANATICS:
4752 if (playerwide && net_value > 0) {
4753 struct strvec *fanatics = strvec_new();
4755
4759 }
4762 /* TRANS: %s is list of unit types separated by 'or' */
4763 _("%s Pays no upkeep for %s.\n"), BULLET,
4767 }
4768 break;
4769 case EFT_NO_UNHAPPY:
4770 if (playerwide && net_value > 0) {
4771 CATLSTR(buf, bufsz, _("%s Has no unhappy citizens.\n"), BULLET);
4772 }
4773 break;
4774 case EFT_VETERAN_BUILD:
4775 {
4776 int conditions = 0;
4777 if (unitclass) {
4778 conditions++;
4779 }
4780 if (unittype) {
4781 conditions++;
4782 }
4783 if (unit_type_flag_id_is_valid(unitflag)) {
4784 conditions++;
4785 }
4786 if (conditions > 1) {
4787 /* More than one requirement on units, too complicated for us
4788 * to describe. */
4789 break;
4790 }
4791 if (unitclass) {
4792 /* FIXME: account for multiple veteran levels, or negative
4793 * values. This might lie for complicated rulesets! */
4795 /* TRANS: %s is a unit class */
4796 Q_("?unitclass:* New %s units will be veteran.\n"),
4798 } else if (unit_type_flag_id_is_valid(unitflag)) {
4799 /* FIXME: same problems as unitclass */
4801 /* TRANS: %s is a (translatable) unit type flag */
4802 Q_("?unitflag:* New %s units will be veteran.\n"),
4804 } else if (unittype != NULL) {
4805 if (world_value_valid && net_value > 0) {
4806 /* Here we can be specific about veteran level, and get
4807 * net value correct. */
4808 int maxlvl = utype_veteran_system(unittype)->levels - 1;
4809 const struct veteran_level *vlevel =
4812 /* TRANS: "* New Partisan units will have the rank
4813 * of elite." */
4814 Q_("?unittype:%s New %s units will have the rank "
4815 "of %s.\n"), BULLET,
4816 utype_name_translation(unittype),
4818 } /* else complicated */
4819 } else {
4820 /* No extra criteria. */
4821 /* FIXME: same problems as above */
4823 _("%s New units will be veteran.\n"), BULLET);
4824 }
4825 }
4826 break;
4828 if (world_value_valid) {
4830 /* TRANS: %s is list of output types, with 'or';
4831 * pluralised in %d but of course the output types
4832 * can't be pluralised; sorry */
4833 PL_("%s Each worked tile that gives more than %d %s will"
4834 " suffer a -1 penalty, unless the city working it"
4835 " is celebrating.",
4836 "%s Each worked tile that gives more than %d %s will"
4837 " suffer a -1 penalty, unless the city working it"
4838 " is celebrating.", net_value),
4840 if (game.info.celebratesize > 1) {
4842 /* TRANS: Preserve leading space. %d should always be
4843 * 2 or greater. */
4844 _(" (Cities below size %d will not celebrate.)"),
4846 }
4847 cat_snprintf(buf, bufsz, "\n");
4848 }
4849 break;
4852 /* TRANS: %s is list of output types, with 'or' */
4853 PL_("%s Each worked tile with at least 1 %s will yield"
4854 " %d more of it while the city working it is"
4855 " celebrating.",
4856 "%s Each worked tile with at least 1 %s will yield"
4857 " %d more of it while the city working it is"
4858 " celebrating.", peffect->value),
4859 BULLET, astr_str(&outputs_or), peffect->value);
4860 if (game.info.celebratesize > 1) {
4862 /* TRANS: Preserve leading space. %d should always be
4863 * 2 or greater. */
4864 _(" (Cities below size %d will not celebrate.)"),
4866 }
4867 cat_snprintf(buf, bufsz, "\n");
4868 break;
4871 /* TRANS: %s is list of output types, with 'or' */
4872 PL_("%s Each worked tile with at least 1 %s will yield"
4873 " %d more of it.\n",
4874 "%s Each worked tile with at least 1 %s will yield"
4875 " %d more of it.\n", peffect->value),
4876 BULLET, astr_str(&outputs_or), peffect->value);
4877 break;
4878 case EFT_OUTPUT_BONUS:
4879 case EFT_OUTPUT_BONUS_2:
4880 /* FIXME: makes most sense iff world_value == 0 */
4882 /* TRANS: %s is list of output types, with 'and' */
4883 _("%s %s production is increased %d%%.\n"),
4884 BULLET, astr_str(&outputs_and), peffect->value);
4885 break;
4886 case EFT_OUTPUT_WASTE:
4887 if (world_value_valid) {
4888 if (net_value > 30) {
4890 /* TRANS: %s is list of output types, with 'and' */
4891 _("%s %s production will suffer massive losses.\n"),
4893 } else if (net_value >= 15) {
4895 /* TRANS: %s is list of output types, with 'and' */
4896 _("%s %s production will suffer some losses.\n"),
4898 } else if (net_value > 0) {
4900 /* TRANS: %s is list of output types, with 'and' */
4901 _("%s %s production will suffer a small amount "
4902 "of losses.\n"),
4904 }
4905 }
4906 break;
4907 case EFT_HEALTH_PCT:
4908 if (playerwide) {
4909 if (peffect->value > 0) {
4910 CATLSTR(buf, bufsz, _("%s Increases the chance of plague"
4911 " within your cities.\n"), BULLET);
4912 } else if (peffect->value < 0) {
4913 CATLSTR(buf, bufsz, _("%s Decreases the chance of plague"
4914 " within your cities.\n"), BULLET);
4915 }
4916 }
4917 break;
4919 /* Semi-arbitrary scaling to get likely ruleset values in roughly
4920 * the same range as WASTE_BY_DISTANCE */
4921 /* FIXME: use different wording? */
4922 net_value = (net_value + 39) / 40; /* round up */
4923 fc__fallthrough; /* fall through to: */
4925 if (world_value_valid) {
4926 if (net_value >= 300) {
4928 /* TRANS: %s is list of output types, with 'and' */
4929 _("%s %s losses will increase quickly"
4930 " with distance from capital.\n"),
4932 } else if (net_value >= 200) {
4934 /* TRANS: %s is list of output types, with 'and' */
4935 _("%s %s losses will increase"
4936 " with distance from capital.\n"),
4938 } else if (net_value > 0) {
4940 /* TRANS: %s is list of output types, with 'and' */
4941 _("%s %s losses will increase slowly"
4942 " with distance from capital.\n"),
4944 }
4945 }
4946 break;
4947 case EFT_MIGRATION_PCT:
4948 if (playerwide) {
4949 if (peffect->value > 0) {
4950 CATLSTR(buf, bufsz, _("%s Increases the chance of migration"
4951 " into your cities.\n"), BULLET);
4952 } else if (peffect->value < 0) {
4953 CATLSTR(buf, bufsz, _("%s Decreases the chance of migration"
4954 " into your cities.\n"), BULLET);
4955 }
4956 }
4957 break;
4958 case EFT_BORDER_VISION:
4960 && playerwide && net_value > 0) {
4961 CATLSTR(buf, bufsz, _("%s All tiles inside your borders are"
4962 " monitored.\n"), BULLET);
4963 }
4964 break;
4965 default:
4966 break;
4967 };
4968 }
4969
4973
4975
4976 /* Action immunity */
4977 action_iterate(act) {
4978 if (action_by_number(act)->quiet) {
4979 /* The ruleset documents this action it self. */
4980 continue;
4981 }
4982
4983 if (action_immune_government(gov, act)) {
4985 /* TRANS: action name ... action target
4986 * ("individual units", etc) */
4987 _("%s Makes it impossible to do the action \'%s\'"
4988 " to your %s.\n"), BULLET,
4991 }
4993
4994 if (user_text && user_text[0] != '\0') {
4995 cat_snprintf(buf, bufsz, "\n%s", user_text);
4996 }
4997}
4998
4999/************************************************************************/
5002char *helptext_unit_upkeep_str(const struct unit_type *utype)
5003{
5004 static char buf[128];
5005 int any = 0;
5006
5007 if (!utype) {
5008 log_error("Unknown unit!");
5009 return "";
5010 }
5011
5012 buf[0] = '\0';
5014 if (utype->upkeep[o] > 0) {
5015 /* TRANS: "2 Food" or ", 1 Shield" */
5016 cat_snprintf(buf, sizeof(buf), _("%s%d %s"),
5017 (any > 0 ? Q_("?blistmore:, ") : ""), utype->upkeep[o],
5019 any++;
5020 }
5022 if (utype->happy_cost > 0) {
5023 /* TRANS: "2 Unhappy" or ", 1 Unhappy" */
5024 cat_snprintf(buf, sizeof(buf), _("%s%d Unhappy"),
5025 (any > 0 ? Q_("?blistmore:, ") : ""), utype->happy_cost);
5026 any++;
5027 }
5028
5029 if (any == 0) {
5030 /* strcpy(buf, _("None")); */
5031 fc_snprintf(buf, sizeof(buf), "%d", 0);
5032 }
5033 return buf;
5034}
5035
5036/************************************************************************/
5039void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation,
5040 const char *user_text)
5041{
5042 struct universal source = {
5043 .kind = VUT_NATION,
5044 .value = {.nation = pnation}
5045 };
5046 bool print_break = TRUE;
5047
5048#define PRINT_BREAK() do { \
5049 if (print_break) { \
5050 if (buf[0] != '\0') { \
5051 CATLSTR(buf, bufsz, "\n\n"); \
5052 } \
5053 print_break = FALSE; \
5054 } \
5055 } while (FALSE)
5056
5057 fc_assert_ret(NULL != buf && 0 < bufsz);
5058 buf[0] = '\0';
5059
5060 if (pnation->legend[0] != '\0') {
5061 /* Client side legend is stored already translated */
5062 cat_snprintf(buf, bufsz, "%s", pnation->legend);
5063 }
5064
5065 if (pnation->init_government) {
5066 PRINT_BREAK();
5068 _("Initial government is %s.\n"),
5070 }
5071 if (pnation->init_techs[0] != A_LAST) {
5072 const char *tech_names[MAX_NUM_TECH_LIST];
5073 int i;
5074 struct astring list = ASTRING_INIT;
5075
5076 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
5077 if (pnation->init_techs[i] == A_LAST) {
5078 break;
5079 }
5080 tech_names[i] =
5082 }
5084 PRINT_BREAK();
5085 if (game.rgame.global_init_techs[0] != A_LAST) {
5087 /* TRANS: %s is an and-separated list of techs */
5088 _("Starts with knowledge of %s in addition to the standard "
5089 "starting technologies.\n"), astr_str(&list));
5090 } else {
5092 /* TRANS: %s is an and-separated list of techs */
5093 _("Starts with knowledge of %s.\n"), astr_str(&list));
5094 }
5095 astr_free(&list);
5096 }
5097 if (pnation->init_units[0]) {
5098 const struct unit_type *utypes[MAX_NUM_UNIT_LIST];
5099 int count[MAX_NUM_UNIT_LIST];
5100 int i, j, n = 0, total = 0;
5101
5102 /* Count how many of each type there is. */
5103 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
5104 if (!pnation->init_units[i]) {
5105 break;
5106 }
5107 for (j = 0; j < n; j++) {
5108 if (pnation->init_units[i] == utypes[j]) {
5109 count[j]++;
5110 total++;
5111 break;
5112 }
5113 }
5114 if (j == n) {
5115 utypes[n] = pnation->init_units[i];
5116 count[n] = 1;
5117 total++;
5118 n++;
5119 }
5120 }
5121 {
5122 /* Construct the list of unit types and counts. */
5124 struct astring list = ASTRING_INIT;
5125
5126 for (i = 0; i < n; i++) {
5128 if (count[i] > 1) {
5129 /* TRANS: a unit type followed by a count. For instance,
5130 * "Fighter (2)" means two Fighters. Count is never 1.
5131 * Used in a list. */
5132 astr_set(&utype_names[i], _("%s (%d)"),
5133 utype_name_translation(utypes[i]), count[i]);
5134 } else {
5136 }
5137 }
5138 {
5140
5141 for (i = 0; i < n; i++) {
5143 }
5145 }
5146 for (i = 0; i < n; i++) {
5148 }
5149 PRINT_BREAK();
5151 /* TRANS: %s is an and-separated list of unit types
5152 * possibly with counts. Plurality is in total number of
5153 * units represented. */
5154 PL_("Starts with the following additional unit: %s.\n",
5155 "Starts with the following additional units: %s.\n",
5156 total), astr_str(&list));
5157 astr_free(&list);
5158 }
5159 }
5160 if (pnation->init_buildings[0] != B_LAST) {
5161 const char *impr_names[MAX_NUM_BUILDING_LIST];
5162 int i;
5163 struct astring list = ASTRING_INIT;
5164
5165 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
5166 if (pnation->init_buildings[i] == B_LAST) {
5167 break;
5168 }
5169 impr_names[i] =
5172 }
5174 PRINT_BREAK();
5177 /* TRANS: %s is an and-separated list of improvements */
5178 _("First city will get %s for free in addition to the "
5179 "standard improvements.\n"), astr_str(&list));
5180 } else {
5182 /* TRANS: %s is an and-separated list of improvements */
5183 _("First city will get %s for free.\n"), astr_str(&list));
5184 }
5185 astr_free(&list);
5186 }
5187
5188 if (buf[0] != '\0') {
5189 CATLSTR(buf, bufsz, "\n");
5190 }
5192
5193 if (user_text && user_text[0] != '\0') {
5194 if (buf[0] != '\0') {
5195 CATLSTR(buf, bufsz, "\n");
5196 }
5197 CATLSTR(buf, bufsz, "%s", user_text);
5198 }
5199#undef PRINT_BREAK
5200}
5201
5202/************************************************************************/
5206{
5207 if (req == NULL) {
5208 return HELP_LAST;
5209 }
5210
5211 if (req->source.kind == VUT_UTYPE) {
5212 return HELP_UNIT;
5213 }
5214 if (req->source.kind == VUT_IMPROVEMENT) {
5216 return HELP_WONDER;
5217 }
5218 return HELP_IMPROVEMENT;
5219 }
5220 if (req->source.kind == VUT_ADVANCE) {
5221 return HELP_TECH;
5222 }
5223 if (req->source.kind == VUT_TERRAIN) {
5224 return HELP_TERRAIN;
5225 }
5226 if (req->source.kind == VUT_EXTRA) {
5227 return HELP_EXTRA;
5228 }
5229 if (req->source.kind == VUT_GOOD) {
5230 return HELP_GOODS;
5231 }
5232 if (req->source.kind == VUT_SPECIALIST) {
5233 return HELP_SPECIALIST;
5234 }
5235 if (req->source.kind == VUT_GOVERNMENT) {
5236 return HELP_GOVERNMENT;
5237 }
5238
5239 if (req->source.kind == VUT_NATION) {
5240 return HELP_NATIONS;
5241 }
5242
5243 return HELP_LAST;
5244}
const char * action_name_translation(const struct action *action)
Definition actions.c:1991
enum action_actor_kind action_get_actor_kind(const struct action *paction)
Definition actions.c:1859
const char * action_id_name_translation(action_id act_id)
Definition actions.c:2011
void action_array_add_all_by_result(action_id *act_array, int *position, enum action_result result)
Definition actions.c:6486
void action_array_end(action_id *act_array, int size)
Definition actions.c:6469
bool action_is_in_use(struct action *paction)
Definition actions.c:6398
enum action_sub_target_kind action_get_sub_target_kind(const struct action *paction)
Definition actions.c:1880
bool action_would_be_blocked_by(const struct action *blocked, const struct action *blocker)
Definition actions.c:1957
bool action_immune_government(struct government *gov, action_id act)
Definition actions.c:6173
int action_dice_roll_initial_odds(const struct action *paction)
Definition actions.c:6097
const char * action_target_kind_help(enum action_target_kind kind)
Definition actions.c:8125
enum action_target_kind action_get_target_kind(const struct action *paction)
Definition actions.c:1869
struct action_enabler_list * action_enablers_for_action(action_id action)
Definition actions.c:2315
#define action_id_univs_not_blocking(act_id, act_uni, tgt_uni)
Definition actions.h:945
#define action_auto_perf_iterate_end
Definition actions.h:586
static struct action * action_by_number(action_id act_id)
Definition actions.h:635
#define action_array_iterate(_act_array_, _act_id_)
Definition actions.h:497
#define action_has_result(_act_, _res_)
Definition actions.h:431
#define action_enabler_list_iterate_end
Definition actions.h:441
#define action_id_get_role(act_id)
Definition actions.h:696
#define ACTION_DISTANCE_UNLIMITED
Definition actions.h:357
#define action_array_iterate_end
Definition actions.h:509
#define action_iterate_end
Definition actions.h:465
#define MAX_NUM_ACTIONS
Definition actions.h:314
#define action_enabler_list_iterate(action_enabler_list, aenabler)
Definition actions.h:439
#define action_iterate(_act_)
Definition actions.h:461
#define action_id_get_target_kind(act_id)
Definition actions.h:652
#define action_auto_perf_iterate(_act_perf_)
Definition actions.h:574
#define ACTION_ODDS_PCT_DICE_ROLL_NA
Definition actions.h:929
bool actres_removes_extra(enum action_result result, const struct extra_type *pextra)
Definition actres.c:803
bool actres_creates_extra(enum action_result result, const struct extra_type *pextra)
Definition actres.c:782
enum action_battle_kind actres_get_battle_kind(enum action_result result)
Definition actres.c:269
void astr_free(struct astring *astr)
Definition astring.c:153
const char * astr_build_or_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:329
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:267
const char * astr_build_and_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:367
void astr_init(struct astring *astr)
Definition astring.c:144
#define str
Definition astring.c:76
#define n
Definition astring.c:77
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:93
#define ASTRING_INIT
Definition astring.h:44
bool territory_claiming_base(const struct base_type *pbase)
Definition base.c:158
#define BV_CLR_ALL_FROM(vec_to, vec_from)
Definition bitvector.h:127
#define BV_CLR_ALL(bv)
Definition bitvector.h:95
#define BV_SET(bv, bit)
Definition bitvector.h:81
#define BV_ARE_EQUAL(vec1, vec2)
Definition bitvector.h:113
#define BV_ISSET(bv, bit)
Definition bitvector.h:78
#define BV_ISSET_ANY(vec)
Definition bitvector.h:109
struct output_type * get_output_type(Output_type_id output)
Definition city.c:638
const char * get_output_name(Output_type_id output)
Definition city.c:629
#define output_type_iterate(output)
Definition city.h:845
#define output_type_iterate_end
Definition city.h:851
enum client_states client_state(void)
@ C_S_RUNNING
Definition client_main.h:47
bool client_nation_is_in_current_set(const struct nation_type *pnation)
Definition climisc.c:1508
static struct fc_sockaddr_list * list
Definition clinet.c:102
char * utypes
Definition comments.c:35
char * incite_cost
Definition comments.c:75
#define MAX_LEN_PACKET
Definition conn_types.h:29
#define MAX_LEN_MSG
Definition conn_types.h:37
const char * counter_name_translation(const struct counter *counter)
Definition counters.c:157
struct counter * counter_by_id(int id)
Definition counters.c:82
static void road(QVariant data1, QVariant data2)
Definition dialogs.cpp:2910
bool effect_universals_value_never_below(enum effect_type type, struct universal *unis, size_t n_unis, int min_value)
Definition effects.c:545
struct @21::@22 reqs
int get_target_bonus_effects(struct effect_list *plist, const struct req_context *context, const struct player *other_player, enum effect_type effect_type)
Definition effects.c:748
int effect_value_from_universals(enum effect_type type, struct universal *unis, size_t n_unis)
Definition effects.c:459
int effect_cumulative_max(enum effect_type type, struct universal *unis, size_t n_unis)
Definition effects.c:388
struct effect_list * get_req_source_effects(const struct universal *psource)
Definition effects.c:153
bool building_has_effect(const struct impr_type *pimprove, enum effect_type effect_type)
Definition effects.c:644
#define effect_list_iterate_end
Definition effects.h:406
#define effect_list_iterate(effect_list, peffect)
Definition effects.h:404
const char * extra_flag_helptxt(enum extra_flag_id id)
Definition extras.c:974
bool is_extra_caused_by_worker_action(const struct extra_type *pextra)
Definition extras.c:1032
bool extra_has_flag(const struct extra_type *pextra, enum extra_flag_id flag)
Definition extras.c:861
bool is_extra_removed_by(const struct extra_type *pextra, enum extra_rmcause rmcause)
Definition extras.c:353
int extra_count(void)
Definition extras.c:153
bool is_native_extra_to_uclass(const struct extra_type *pextra, const struct unit_class *pclass)
Definition extras.c:843
const char * extra_name_translation(const struct extra_type *pextra)
Definition extras.c:194
#define extra_type_iterate(_p)
Definition extras.h:315
#define extra_type_iterate_end
Definition extras.h:321
#define extra_type_by_rmcause_iterate_end
Definition extras.h:358
#define is_extra_caused_by(e, c)
Definition extras.h:203
#define extra_index(_e_)
Definition extras.h:183
#define extra_type_by_rmcause_iterate(_rmcause, _extra)
Definition extras.h:353
#define extra_road_get(_e_)
Definition extras.h:191
#define extra_type_by_cause_iterate_end
Definition extras.h:339
#define EF_LAST_USER_FLAG
Definition extras.h:82
#define extra_type_by_cause_iterate(_cause, _extra)
Definition extras.h:333
#define MAX_NUM_BUILDING_LIST
Definition fc_types.h:46
int Impr_type_id
Definition fc_types.h:376
int Unit_Class_id
Definition fc_types.h:418
int action_id
Definition fc_types.h:389
#define CASUS_BELLI_OUTRAGE
Definition fc_types.h:484
#define CASUS_BELLI_VICTIM
Definition fc_types.h:478
#define MAX_NUM_UNIT_LIST
Definition fc_types.h:45
#define MAX_LEN_NAME
Definition fc_types.h:66
#define MAX_NUM_TECH_LIST
Definition fc_types.h:44
@ O_SHIELD
Definition fc_types.h:101
@ O_FOOD
Definition fc_types.h:101
@ O_TRADE
Definition fc_types.h:101
@ O_LAST
Definition fc_types.h:101
@ BORDERS_ENABLED
Definition fc_types.h:1038
enum output_type_id Output_type_id
Definition fc_types.h:378
size_t get_internal_string_length(const char *text)
Definition fciconv.c:395
#define Q_(String)
Definition fcintl.h:70
#define PL_(String1, String2, n)
Definition fcintl.h:71
#define _(String)
Definition fcintl.h:67
#define N_(String)
Definition fcintl.h:69
struct civ_game game
Definition game.c:62
const char * government_name_translation(const struct government *pgovern)
Definition government.c:143
#define governments_iterate(NAME_pgov)
Definition government.h:124
#define governments_iterate_end
Definition government.h:127
static struct tile * pos
Definition finddlg.c:53
static GtkWidget * source
Definition gotodlg.c:58
const char * client_string
Definition gui_main.c:105
void insert_client_build_info(char *outbuf, size_t outlen)
Definition gui_main.c:2539
void popdown_help_dialog(void)
Definition helpdlg.c:186
GType type
Definition repodlgs.c:1313
static const int bufsz
Definition helpdlg.c:70
static void extra_bonus_for_terrain(struct extra_type *pextra, struct terrain *pterrain, int *bonus)
Definition helpdata.c:3685
void help_iter_start(void)
Definition helpdata.c:1340
static int help_item_compar(const struct help_item *const *ppa, const struct help_item *const *ppb)
Definition helpdata.c:688
static bool help_nodes_init
Definition helpdata.c:87
void free_help_texts(void)
Definition helpdata.c:125
void boot_help_texts(void)
Definition helpdata.c:710
static void insert_allows(struct universal *psource, char *buf, size_t bufsz, const char *prefix)
Definition helpdata.c:571
#define CATLSTR(_b, _s, _t,...)
Definition helpdata.c:68
static bool insert_generated_text(char *outbuf, size_t outlen, const char *name)
Definition helpdata.c:208
static struct help_list * help_nodes
Definition helpdata.c:86
static const char *const help_type_names[]
Definition helpdata.c:71
void helptext_government(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct government *gov)
Definition helpdata.c:4340
void helptext_advance(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, int i)
Definition helpdata.c:3299
enum help_page_type help_type_by_requirement(const struct requirement *req)
Definition helpdata.c:5205
char * helptext_unit(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, const struct unit_type *utype, bool class_help)
Definition helpdata.c:1932
void helptext_extra(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct extra_type *pextra)
Definition helpdata.c:3794
void helptext_unitclass(struct unit_class *pclass, char *buf, size_t bufsz)
Definition helpdata.c:1866
void helpdata_init(void)
Definition helpdata.c:95
static void check_help_nodes_init(void)
Definition helpdata.c:114
const struct help_item * get_help_item(int pos)
Definition helpdata.c:1265
int num_help_items(void)
Definition helpdata.c:1254
static bool insert_veteran_help(char *outbuf, size_t outlen, const struct veteran_system *veteran, const char *intro, const char *nolevels)
Definition helpdata.c:149
void helptext_goods(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct goods_type *pgood)
Definition helpdata.c:4259
#define BULLET_SPACE
Definition helpdata.c:65
static bool show_help_for_nation(const struct nation_type *pnation)
Definition helpdata.c:139
#define PRINT_BREAK()
const char * helptext_road_bonus_str(const struct terrain *pterrain, const struct road_type *proad)
Definition helpdata.c:3630
static const struct help_list_link * help_nodes_iterator
Definition helpdata.c:85
char * helptext_unit_upkeep_str(const struct unit_type *utype)
Definition helpdata.c:5002
#define BULLET
Definition helpdata.c:63
const char * helptext_extra_for_terrain_str(struct extra_type *pextra, struct terrain *pterrain, enum unit_activity act)
Definition helpdata.c:3760
static void insert_allows_single(struct universal *psource, struct requirement_vector *psubjreqs, const char *subjstr, const char *const *strs, char *buf, size_t bufsz, const char *prefix)
Definition helpdata.c:485
#define help_list_iterate_end
Definition helpdata.c:83
static bool utype_may_do_escape_action(const struct unit_type *utype)
Definition helpdata.c:1830
void helpdata_done(void)
Definition helpdata.c:103
void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct specialist *pspec)
Definition helpdata.c:4305
const struct help_item * get_help_item_spec(const char *name, enum help_page_type htype, int *pos)
Definition helpdata.c:1288
#define help_list_iterate(helplist, phelp)
Definition helpdata.c:81
const struct help_item * help_iter_next(void)
Definition helpdata.c:1350
char * helptext_building(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, const struct impr_type *pimprove)
Definition helpdata.c:1381
void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct terrain *pterrain)
Definition helpdata.c:3520
void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation, const char *user_text)
Definition helpdata.c:5039
static struct help_item * new_help_item(int type)
Definition helpdata.c:672
#define HELP_MUSICSET_ITEM
#define HELP_TILESET_ITEM
#define HELP_RULESET_ITEM
help_page_type
Definition helpdlg_g.h:20
@ HELP_ANY
Definition helpdlg_g.h:20
@ HELP_MUSICSET
Definition helpdlg_g.h:23
@ HELP_MULTIPLIER
Definition helpdlg_g.h:24
@ HELP_TERRAIN
Definition helpdlg_g.h:21
@ HELP_EXTRA
Definition helpdlg_g.h:21
@ HELP_NATIONS
Definition helpdlg_g.h:24
@ HELP_LAST
Definition helpdlg_g.h:25
@ HELP_IMPROVEMENT
Definition helpdlg_g.h:20
@ HELP_UNIT
Definition helpdlg_g.h:20
@ HELP_COUNTER
Definition helpdlg_g.h:24
@ HELP_SPECIALIST
Definition helpdlg_g.h:22
@ HELP_GOVERNMENT
Definition helpdlg_g.h:22
@ HELP_GOODS
Definition helpdlg_g.h:22
@ HELP_WONDER
Definition helpdlg_g.h:21
@ HELP_TECH
Definition helpdlg_g.h:21
@ HELP_RULESET
Definition helpdlg_g.h:23
@ HELP_TEXT
Definition helpdlg_g.h:20
@ HELP_TILESET
Definition helpdlg_g.h:23
const struct impr_type * valid_improvement(const struct impr_type *pimprove)
struct impr_type * improvement_by_number(const Impr_type_id id)
bool is_great_wonder(const struct impr_type *pimprove)
bool improvement_has_flag(const struct impr_type *pimprove, enum impr_flag_id flag)
const char * improvement_name_translation(const struct impr_type *pimprove)
bool is_small_wonder(const struct impr_type *pimprove)
#define improvement_iterate_end
#define improvement_iterate(_p)
#define B_LAST
Definition improvement.h:42
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:191
#define log_verbose(message,...)
Definition log.h:109
#define fc_assert(condition)
Definition log.h:176
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define log_error(message,...)
Definition log.h:103
struct terrain_misc terrain_control
Definition map.c:69
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
const char * move_points_text(int mp, bool reduce)
Definition movement.c:1015
const char * move_points_text_full(int mp, bool reduce, const char *prefix, const char *none, bool align)
Definition movement.c:948
bool is_native_to_class(const struct unit_class *punitclass, const struct terrain *pterrain, const bv_extras *extras)
Definition movement.c:342
bool can_unit_type_transport(const struct unit_type *transporter, const struct unit_class *transported)
Definition movement.c:868
bool can_attack_non_native(const struct unit_type *utype)
Definition movement.c:214
#define multipliers_iterate(_mul_)
Definition multipliers.h:61
#define multipliers_iterate_end
Definition multipliers.h:67
const char * current_musicset_version(void)
Definition music.c:242
const char * current_musicset_name(void)
Definition music.c:234
const char * current_musicset_summary(void)
Definition music.c:254
const char * current_musicset_description(void)
Definition music.c:262
static const char * name_translation_get(const struct name_translation *ptrans)
const char * nation_plural_translation(const struct nation_type *pnation)
Definition nation.c:159
#define nations_iterate_end
Definition nation.h:336
#define nations_iterate(NAME_pnation)
Definition nation.h:333
int len
Definition packhand.c:127
const char * diplrel_name_translation(int value)
Definition player.c:1627
struct section_file * secfile_load(const char *filename, bool allow_duplicates)
Definition registry.c:50
const char * secfile_error(void)
const char * section_name(const struct section *psection)
void secfile_destroy(struct section_file *secfile)
void secfile_check_unused(const struct section_file *secfile)
struct section_list * secfile_sections_by_name_prefix(const struct section_file *secfile, const char *prefix)
const char ** secfile_lookup_str_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
#define section_list_iterate(seclist, psection)
#define section_list_iterate_end
bool req_text_insert_nl(char *buf, size_t bufsz, struct player *pplayer, const struct requirement *preq, enum rt_verbosity verb, const char *prefix)
Definition reqtext.c:3307
@ VERB_DEFAULT
Definition reqtext.h:20
bool universal_is_relevant_to_requirement(const struct requirement *req, const struct universal *source)
bool universal_fulfills_requirements(bool check_necessary, const struct requirement_vector *reqs, const struct universal *source)
bool are_universals_equal(const struct universal *psource1, const struct universal *psource2)
const char * universal_name_translation(const struct universal *psource, char *buf, size_t bufsz)
#define requirement_fulfilled_by_unit_type(_ut_, _rqs_)
#define requirement_fulfilled_by_improvement(_imp_, _rqs_)
#define requirement_vector_iterate_end
#define requirement_vector_iterate(req_vec, preq)
int research_goal_unknown_techs(const struct research *presearch, Tech_type_id goal)
Definition research.c:750
bool research_invention_reachable(const struct research *presearch, const Tech_type_id tech)
Definition research.c:668
int research_goal_bulbs_required(const struct research *presearch, Tech_type_id goal)
Definition research.c:772
int research_total_bulbs_required(const struct research *presearch, Tech_type_id tech, bool loss_value)
Definition research.c:868
struct research * research_get(const struct player *pplayer)
Definition research.c:128
enum tech_state research_invention_state(const struct research *presearch, Tech_type_id tech)
Definition research.c:619
bool road_has_flag(const struct road_type *proad, enum road_flag_id flag)
Definition road.c:410
bool road_provides_move_bonus(const struct road_type *proad)
Definition road.c:499
server_setting_id server_setting_by_name(const char *name)
bool server_setting_value_bool_get(server_setting_id id)
struct setting_list * level[OLEVELS_NUM]
Definition settings.c:190
int compare_strings(const void *first, const void *second)
Definition shared.c:359
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1101
const struct strvec * get_data_dirs(void)
Definition shared.c:893
#define ARRAY_SIZE(x)
Definition shared.h:85
#define MIN(x, y)
Definition shared.h:55
#define MAX(x, y)
Definition shared.h:54
struct specialist * specialist_by_number(const Specialist_type_id id)
Definition specialist.c:100
const char * specialist_plural_translation(const struct specialist *sp)
Definition specialist.c:155
#define specialist_type_iterate_end
Definition specialist.h:79
#define specialist_type_iterate(sp)
Definition specialist.h:73
size_t size
Definition specvec.h:72
void strvec_destroy(struct strvec *psv)
void strvec_append(struct strvec *psv, const char *string)
const char * strvec_to_or_list(const struct strvec *psv, struct astring *astr)
struct strvec * strvec_new(void)
void strvec_clear(struct strvec *psv)
size_t strvec_size(const struct strvec *psv)
const char * strvec_to_and_list(const struct strvec *psv, struct astring *astr)
#define strvec_iterate(psv, str)
#define strvec_iterate_end
action_id id
Definition actions.h:363
bool quiet
Definition actions.h:385
struct packet_ruleset_control control
Definition game.h:83
char * ruleset_summary
Definition game.h:84
int global_init_techs[MAX_NUM_TECH_LIST]
Definition game.h:110
struct packet_game_info info
Definition game.h:89
int global_init_buildings[MAX_NUM_BUILDING_LIST]
Definition game.h:111
struct packet_scenario_info scenario
Definition game.h:87
char * ruleset_description
Definition game.h:85
bool ruleset_init
Definition game.h:118
struct civ_game::@30 rgame
struct veteran_system * veteran
Definition game.h:101
struct civ_game::@31::@34 client
struct strvec * helptext
Definition extras.h:149
struct road_type * road
Definition extras.h:155
struct extra_type::@25 data
int removal_time
Definition extras.h:120
bool generated
Definition extras.h:117
struct requirement_vector rmreqs
Definition extras.h:107
Tech_type_id visibility_req
Definition extras.h:139
int removal_time_factor
Definition extras.h:121
struct requirement_vector reqs
Definition extras.h:106
bool buildable
Definition extras.h:116
enum extra_unit_seen_type eus
Definition extras.h:128
int defense_bonus
Definition extras.h:124
int build_time
Definition extras.h:118
int infracost
Definition extras.h:122
int no_aggr_near_city
Definition extras.h:137
struct base_type * base
Definition extras.h:154
struct strvec * helptext
Definition government.h:65
struct requirement_vector reqs
Definition government.h:62
struct requirement_vector obsolete_by
Definition improvement.h:59
struct requirement_vector reqs
Definition improvement.h:58
struct strvec * helptext
Definition improvement.h:65
int init_buildings[MAX_NUM_BUILDING_LIST]
Definition nation.h:123
struct government * init_government
Definition nation.h:124
struct unit_type * init_units[MAX_NUM_UNIT_LIST]
Definition nation.h:125
char * legend
Definition nation.h:107
int init_techs[MAX_NUM_TECH_LIST]
Definition nation.h:122
enum borders_mode borders
int nuke_defender_survival_chance_pct
enum tech_upkeep_style tech_upkeep_style
char version[MAX_LEN_NAME]
char name[MAX_LEN_NAME]
struct universal source
struct strvec * helptext
Definition terrain.h:263
int irrigation_food_incr
Definition terrain.h:221
int output[O_LAST]
Definition terrain.h:202
int road_output_incr_pct[O_LAST]
Definition terrain.h:211
int mining_shield_incr
Definition terrain.h:224
int transport_capacity
Definition unittype.h:523
int pop_cost
Definition unittype.h:513
struct requirement_vector build_reqs
Definition unittype.h:520
int defense_strength
Definition unittype.h:516
int paratroopers_range
Definition unittype.h:541
int convert_time
Definition unittype.h:531
int city_size
Definition unittype.h:550
struct veteran_system * veteran
Definition unittype.h:544
const struct unit_type * obsoleted_by
Definition unittype.h:529
bv_unit_classes targets
Definition unittype.h:561
enum vision_layer vlayer
Definition unittype.h:569
struct strvec * helptext
Definition unittype.h:571
int bombard_rate
Definition unittype.h:547
int upkeep[O_LAST]
Definition unittype.h:538
bv_unit_classes disembarks
Definition unittype.h:567
const struct unit_type * converted_to
Definition unittype.h:530
bv_unit_classes embarks
Definition unittype.h:564
int happy_cost
Definition unittype.h:537
struct combat_bonus_list * bonuses
Definition unittype.h:526
enum universals_n kind
Definition fc_types.h:902
universals_u value
Definition fc_types.h:901
struct veteran_level * definitions
Definition unittype.h:497
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:974
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:791
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:189
size_t fc_strlcat(char *dest, const char *src, size_t n)
Definition support.c:836
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:1000
#define sz_strlcpy(dest, src)
Definition support.h:195
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:196
#define fc_strncmp(_s1_, _s2_, _len_)
Definition support.h:160
#define fc__fallthrough
Definition support.h:119
struct advance * advance_by_number(const Tech_type_id atype)
Definition tech.c:107
bool advance_has_flag(Tech_type_id tech, enum tech_flag_id flag)
Definition tech.c:216
const char * tech_class_name_translation(const struct tech_class *ptclass)
Definition tech.c:333
struct advance * advance_requires(const struct advance *padvance, enum tech_req require)
Definition tech.c:136
const char * advance_name_translation(const struct advance *padvance)
Definition tech.c:290
struct advance * valid_advance_by_number(const Tech_type_id id)
Definition tech.c:176
bool techs_have_fixed_costs(void)
Definition tech.c:450
const char * tech_flag_helptxt(enum tech_flag_id id)
Definition tech.c:435
Tech_type_id advance_number(const struct advance *padvance)
Definition tech.c:98
#define advance_index_iterate_end
Definition tech.h:248
@ AR_ROOT
Definition tech.h:113
#define A_FIRST
Definition tech.h:44
#define A_NONE
Definition tech.h:43
#define advance_root_req_iterate_end
Definition tech.h:319
#define A_LAST
Definition tech.h:45
#define advance_index_iterate(_start, _index)
Definition tech.h:244
#define advance_root_req_iterate(_goal, _padvance)
Definition tech.h:314
const char * terrain_name_translation(const struct terrain *pterrain)
Definition terrain.c:238
const char * terrain_rule_name(const struct terrain *pterrain)
Definition terrain.c:247
enum terrain_class terrain_type_terrain_class(const struct terrain *pterrain)
Definition terrain.c:582
const char * terrain_flag_helptxt(enum terrain_flag_id id)
Definition terrain.c:829
int terrain_extra_build_time(const struct terrain *pterrain, enum unit_activity activity, const struct extra_type *tgt)
Definition terrain.c:699
#define terrain_type_iterate(_p)
Definition terrain.h:373
#define T_NONE
Definition terrain.h:56
#define terrain_type_iterate_end
Definition terrain.h:379
#define terrain_has_flag(terr, flag)
Definition terrain.h:283
const char * tileset_description(struct tileset *t)
Definition tilespec.c:7546
const char * tileset_summary(struct tileset *t)
Definition tilespec.c:7538
const char * tileset_name_get(struct tileset *t)
Definition tilespec.c:7522
const char * tileset_version(struct tileset *t)
Definition tilespec.c:7530
const char * goods_name_translation(struct goods_type *pgood)
#define goods_type_iterate_end
#define goods_type_iterate(_p)
const struct impr_type * building
Definition fc_types.h:714
const char * uclass_name_translation(const struct unit_class *pclass)
Definition unittype.c:1632
bool utype_action_takes_all_mp(const struct unit_type *putype, struct action *paction)
Definition unittype.c:1191
bool utype_can_freely_unload(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Definition unittype.c:300
const char * unit_class_flag_helptxt(enum unit_class_flag_id id)
Definition unittype.c:1854
Unit_Class_id uclass_count(void)
Definition unittype.c:2446
struct unit_type * get_role_unit(int role, int role_index)
Definition unittype.c:2253
int utype_build_shield_cost_base(const struct unit_type *punittype)
Definition unittype.c:1468
const struct veteran_system * utype_veteran_system(const struct unit_type *punittype)
Definition unittype.c:2568
int num_role_units(int role)
Definition unittype.c:2203
bool utype_can_freely_load(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Definition unittype.c:288
Unit_type_id utype_count(void)
Definition unittype.c:80
int utype_veteran_levels(const struct unit_type *punittype)
Definition unittype.c:2625
bool utype_can_do_action_result(const struct unit_type *putype, enum action_result result)
Definition unittype.c:387
bool utype_action_takes_all_mp_if_ustate_is(const struct unit_type *putype, struct action *paction, const enum ustate_prop prop)
Definition unittype.c:1207
const struct veteran_level * utype_veteran_level(const struct unit_type *punittype, int level)
Definition unittype.c:2597
bool utype_is_consumed_by_action(const struct action *paction, const struct unit_type *utype)
Definition unittype.c:1219
bool utype_veteran_has_power_bonus(const struct unit_type *punittype)
Definition unittype.c:2638
const char * unit_type_flag_helptxt(enum unit_type_flag_id id)
Definition unittype.c:1917
const char * utype_name_translation(const struct unit_type *punittype)
Definition unittype.c:1560
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)
Definition unittype.c:1017
bool utype_can_do_action(const struct unit_type *putype, const action_id act_id)
Definition unittype.c:371
#define UCF_LAST_USER_FLAG
Definition unittype.h:127
static bool uclass_has_flag(const struct unit_class *punitclass, enum unit_class_flag_id flag)
Definition unittype.h:766
#define utype_class(_t_)
Definition unittype.h:749
#define utype_fuel(ptype)
Definition unittype.h:839
#define combat_bonus_list_iterate_end
Definition unittype.h:482
#define combat_bonus_list_iterate(bonuslist, pbonus)
Definition unittype.h:480
#define unit_tech_reqs_iterate_end
Definition unittype.h:881
#define unit_class_iterate(_p)
Definition unittype.h:908
#define unit_tech_reqs_iterate(_utype_, _p)
Definition unittype.h:875
static bool utype_has_flag(const struct unit_type *punittype, int flag)
Definition unittype.h:617
#define UTYF_LAST_USER_FLAG
Definition unittype.h:331
#define unit_type_iterate(_p)
Definition unittype.h:855
#define uclass_index(_c_)
Definition unittype.h:742
#define unit_class_iterate_end
Definition unittype.h:915
#define unit_type_iterate_end
Definition unittype.h:862
const char * freeciv_name_version(void)
Definition version.c:35