Freeciv-3.4
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
218 /* Special handling for transform.
219 * Transforming from a land to ocean, or from ocean to land, may require
220 * a number of adjacent tiles of the right terrain class. If so,
221 * we provide that bit of info.
222 * The terrain.ruleset file may include a transform from a land to
223 * ocean, or from ocean to land, which then is not possible because
224 * the value of land_channel_requirement or ocean_reclaim_requirement
225 * prevents it. 101 is the value that prevents it. */
227 (terrain_control.ocean_reclaim_requirement_pct < 101);
229 (terrain_control.land_channel_requirement_pct < 101);
232 ceil((terrain_control.ocean_reclaim_requirement_pct/100.0) *
235 ceil((terrain_control.land_channel_requirement_pct/100.0) *
237
240 PL_("To transform a water tile to a land tile, the water tile "
241 "must have %d adjacent land tile.\n",
242 "To transform a water tile to a land tile, the water tile "
243 "must have %d adjacent land tiles.\n",
246 }
249 PL_("To transform a land tile to a water tile, the land tile "
250 "must have %d adjacent water tile.\n",
251 "To transform a land tile to a water tile, the land tile "
252 "must have %d adjacent water tiles.\n",
255 }
256 CATLSTR(outbuf, outlen, "\n");
257
259 /* TRANS: Header for fixed-width terrain alteration table.
260 * TRANS: Translators cannot change column widths :( */
261 _("Terrain Cultivate Plant Transform\n"));
263 "----------------------------------------------------------------\n");
264 terrain_type_iterate(pterrain) {
265 if (0 != strlen(terrain_rule_name(pterrain))) {
266 char cultivation_time[4], plant_time[4], transform_time[4];
267 const char *terrain, *cultivate_result,
268 *plant_result,*transform_result;
269 struct universal for_terr = { .kind = VUT_TERRAIN, .value = { .terrain = pterrain }};
270 int cslen, pslen, tslen;
271
273 "%d", pterrain->cultivate_time);
274 fc_snprintf(plant_time, sizeof(plant_time),
275 "%d", pterrain->plant_time);
276 fc_snprintf(transform_time, sizeof(transform_time),
277 "%d", pterrain->transform_time);
279 cultivate_result =
280 (pterrain->cultivate_result == T_NONE
282 ? ""
283 : terrain_name_translation(pterrain->cultivate_result);
284 plant_result =
285 (pterrain->plant_result == T_NONE
287 ? ""
288 : terrain_name_translation(pterrain->plant_result);
289 transform_result =
290 (pterrain->transform_result == pterrain
291 || pterrain->transform_result == T_NONE
293 NULL, &for_terr)) ? ""
294 : terrain_name_translation(pterrain->transform_result);
295
296 /* More special handling for transform.
297 * Check if it is really possible. */
298 if (strcmp(transform_result, "") != 0
299 && pterrain->transform_result != T_NONE) {
303 terrain_type_terrain_class(pterrain->transform_result);
306 transform_result = "";
307 }
310 transform_result = "";
311 }
312 }
313
314 /* Use get_internal_string_length() for correct alignment with
315 * multibyte character encodings */
317 cslen = 12 - (int)get_internal_string_length(cultivate_result);
318 pslen = 12 - (int)get_internal_string_length(plant_result);
320 "%s%*s %3s %s%*s %3s %s%*s %3s %s\n",
321 terrain,
322 MAX(0, tslen), "",
323 (pterrain->cultivate_result == T_NONE) ? "-" : cultivation_time,
324 cultivate_result,
325 MAX(0, cslen), "",
326 (pterrain->plant_result == T_NONE) ? "-" : plant_time,
327 plant_result,
328 MAX(0, pslen), "",
329 (!strcmp(transform_result, "")) ? "-" : transform_time,
330 transform_result);
331
332 if (clean_time != 0) {
334 int rmtime = pterrain->extra_removal_times[extra_index(pextra)];
335
336 if (rmtime != 0) {
337 if (clean_time < 0) {
339 } else if (clean_time != rmtime) {
340 clean_time = 0; /* Give up */
341 }
342 }
344 }
345
346 if (pillage_time != 0 && pterrain->pillage_time != 0) {
347 if (pillage_time < 0) {
348 pillage_time = pterrain->pillage_time;
349 } else {
350 if (pillage_time != pterrain->pillage_time) {
351 pillage_time = 0; /* Give up */
352 }
353 }
354 }
355 }
357
358 /* Examine extras to see if time of removal activities really is
359 * terrain-independent, and take into account removal_time_factor.
360 * XXX: this is rather overwrought to handle cases which the ruleset
361 * author could express much more simply for the same result */
362 {
363 int time = -1, factor = -1;
364
366 if (pextra->removal_time == 0) {
367 if (factor < 0) {
368 factor = pextra->removal_time_factor;
369 } else if (factor != pextra->removal_time_factor) {
370 factor = 0; /* Give up */
371 }
372 } else {
373 if (time < 0) {
374 time = pextra->removal_time;
375 } else if (time != pextra->removal_time) {
376 time = 0; /* Give up */
377 }
378 }
380
381 if (factor < 0) {
382 /* No extra has terrain-dependent clean time; use extra's time */
383 if (time >= 0) {
384 clean_time = time;
385 } else {
386 clean_time = 0;
387 }
388 } else if (clean_time != 0) {
389 /* At least one extra's time depends on terrain */
391
392 if (time > 0 && factor > 0 && time != clean_time * factor) {
393 clean_time = 0;
394 } else if (time >= 0) {
395 clean_time = time;
396 } else if (factor >= 0) {
397 clean_time *= factor;
398 } else {
400 }
401 }
402 }
403
404 {
405 int time = -1, factor = -1;
406
408 if (pextra->removal_time == 0) {
409 if (factor < 0) {
410 factor = pextra->removal_time_factor;
411 } else if (factor != pextra->removal_time_factor) {
412 factor = 0; /* Give up */
413 }
414 } else {
415 if (time < 0) {
416 time = pextra->removal_time;
417 } else if (time != pextra->removal_time) {
418 time = 0; /* Give up */
419 }
420 }
422 if (factor < 0) {
423 /* No extra has terrain-dependent pillage time; use extra's time */
424 if (time >= 0) {
425 pillage_time = time;
426 } else {
427 pillage_time = 0;
428 }
429 } else if (pillage_time != 0) {
430 /* At least one extra's time depends on terrain */
431 fc_assert(pillage_time > 0);
432 if (time > 0 && factor > 0 && time != pillage_time * factor) {
433 pillage_time = 0;
434 } else if (time >= 0) {
435 pillage_time = time;
436 } else if (factor >= 0) {
437 pillage_time = pillage_time * factor;
438 } else {
440 }
441 }
442 }
443
444 /* Check whether there are any bases or roads whose build time is
445 * independent of terrain */
446
448 if (pextra->buildable && pextra->build_time > 0) {
450 break;
451 }
455 if (pextra->buildable && pextra->build_time > 0) {
457 break;
458 }
460 }
461
462 if (clean_time > 0 || pillage_time > 0
464 CATLSTR(outbuf, outlen, "\n");
466 _("Time taken for the following activities is independent of "
467 "terrain:\n"));
468 CATLSTR(outbuf, outlen, "\n");
470 /* TRANS: Header for fixed-width terrain alteration table.
471 * TRANS: Translators cannot change column widths :( */
472 _("Activity Time\n"));
474 "---------------------------");
475 if (clean_time > 0) {
477 _("\nClean %3d"), clean_time);
478 }
479 if (pillage_time > 0) {
481 _("\nPillage %3d"), pillage_time);
482 }
483
485 if (pextra->buildable && pextra->build_time > 0) {
486 const char *rname = extra_name_translation(pextra);
487 int slen = 18 - (int)get_internal_string_length(rname);
488
490 "\n%s%*s %3d",
491 rname,
492 MAX(0, slen), "",
493 pextra->build_time);
494 }
496
498 if (pextra->buildable && pextra->build_time > 0) {
499 const char *bname = extra_name_translation(pextra);
501
503 "\n%s%*s %3d",
504 bname,
505 MAX(0, slen), "",
506 pextra->build_time);
507 }
509 }
510
511 return TRUE;
512 } else if (0 == strcmp(name, "VeteranLevels")) {
514 _("In this ruleset, the following veteran levels are defined:"),
515 _("This ruleset has no default veteran levels defined."));
516 } else if (0 == strcmp(name, "FreecivVersion")) {
517 const char *ver = freeciv_name_version();
518
520 /* TRANS: First %s is version string, e.g.,
521 * "Freeciv version 3.2.0-beta1 (beta version)" (translated).
522 * Second %s is client_string, e.g., "gui-gtk-4.0". */
523 _("This is %s, %s client."), ver, client_string);
525
526 return TRUE;
527 } else if (0 == strcmp(name, "DefaultMetaserver")) {
529
530 return TRUE;
531 }
532 log_error("Unknown directive '$%s' in help", name);
533 return FALSE;
534}
535
536/************************************************************************/
545 const char *subjstr,
546 const char *const *strs,
547 char *buf, size_t bufsz,
548 const char *prefix)
549{
550 struct strvec *coreqs = strvec_new();
551 struct strvec *conoreqs = strvec_new();
554 char buf2[bufsz];
555
556 /* FIXME: show other data like range and survives. */
557
559 if (!req->quiet && are_universals_equal(psource, &req->source)) {
560 /* We're definitely going to print _something_. */
561 CATLSTR(buf, bufsz, "%s", prefix);
562 if (req->present) {
563 /* psource enables the subject, but other sources may
564 * also be required (or required to be absent). */
566 if (!coreq->quiet && !are_universals_equal(psource, &coreq->source)) {
567 universal_name_translation(&coreq->source, buf2, sizeof(buf2));
568 strvec_append(coreq->present ? coreqs : conoreqs, buf2);
569 }
571
572 if (0 < strvec_size(coreqs)) {
573 if (0 < strvec_size(conoreqs)) {
575 Q_(strs[0]), /* "Allows %s (with %s but no %s)." */
576 subjstr,
579 } else {
581 Q_(strs[1]), /* "Allows %s (with %s)." */
582 subjstr,
584 }
585 } else {
586 if (0 < strvec_size(conoreqs)) {
588 Q_(strs[2]), /* "Allows %s (absent %s)." */
589 subjstr,
591 } else {
593 Q_(strs[3]), /* "Allows %s." */
594 subjstr);
595 }
596 }
597 } else {
598 /* psource can, on its own, prevent the subject. */
600 Q_(strs[4]), /* "Prevents %s." */
601 subjstr);
602 }
603 cat_snprintf(buf, bufsz, "\n");
604 }
606
611}
612
613/************************************************************************/
629static void insert_allows(struct universal *psource,
630 char *buf, size_t bufsz, const char *prefix)
631{
632 buf[0] = '\0';
633
635 static const char *const govstrs[] = {
636 /* TRANS: First %s is a government name. */
637 N_("?gov:Allows %s (with %s but no %s)."),
638 /* TRANS: First %s is a government name. */
639 N_("?gov:Allows %s (with %s)."),
640 /* TRANS: First %s is a government name. */
641 N_("?gov:Allows %s (absent %s)."),
642 /* TRANS: %s is a government name. */
643 N_("?gov:Allows %s."),
644 /* TRANS: %s is a government name. */
645 N_("?gov:Prevents %s.")
646 };
649 buf, bufsz, prefix);
651
652 improvement_iterate(pimprove) {
653 if (valid_improvement(pimprove)) {
654 static const char *const imprstrs[] = {
655 /* TRANS: First %s is a building name. */
656 N_("?improvement:Allows %s (with %s but no %s)."),
657 /* TRANS: First %s is a building name. */
658 N_("?improvement:Allows %s (with %s)."),
659 /* TRANS: First %s is a building name. */
660 N_("?improvement:Allows %s (absent %s)."),
661 /* TRANS: %s is a building name. */
662 N_("?improvement:Allows %s."),
663 /* TRANS: %s is a building name. */
664 N_("?improvement:Prevents %s.")
665 };
666 insert_allows_single(psource, &pimprove->reqs,
668 buf, bufsz, prefix);
669 }
671
673 static const char *const utstrs[] = {
674 /* TRANS: First %s is a unit type name. */
675 N_("?unittype:Allows %s (with %s but no %s)."),
676 /* TRANS: First %s is a unit type name. */
677 N_("?unittype:Allows %s (with %s)."),
678 /* TRANS: First %s is a unit type name. */
679 N_("?unittype:Allows %s (absent %s)."),
680 /* TRANS: %s is a unit type name. */
681 N_("?unittype:Allows %s."),
682 /* TRANS: %s is a unit type name. */
683 N_("?unittype:Prevents %s.")
684 };
685 insert_allows_single(psource, &putype->build_reqs,
687 buf, bufsz, prefix);
689
690 extra_type_iterate(pextra) {
691 static const char *const estrs[] = {
692 /* TRANS: First %s is an extra name. */
693 N_("?extra:Allows %s (with %s but no %s)."),
694 /* TRANS: First %s is an extra name. */
695 N_("?extra:Allows %s (with %s)."),
696 /* TRANS: First %s is an extra name. */
697 N_("?extra:Allows %s (absent %s)."),
698 /* TRANS: %s is an extra name. */
699 N_("?extra:Allows %s."),
700 /* TRANS: %s is an extra name. */
701 N_("?extra:Prevents %s.")
702 };
703 insert_allows_single(psource, &pextra->reqs,
705 buf, bufsz, prefix);
707
709 static const char *const gstrs[] = {
710 /* TRANS: First %s is a good name. */
711 N_("?good:Allows %s (with %s but no %s)."),
712 /* TRANS: First %s is a good name. */
713 N_("?good:Allows %s (with %s)."),
714 /* TRANS: First %s is a good name. */
715 N_("?good:Allows %s (absent %s)."),
716 /* TRANS: %s is a good name. */
717 N_("?good:Allows %s."),
718 /* TRANS: %s is a good name. */
719 N_("?good:Prevents %s.")
720 };
723 buf, bufsz, prefix);
725}
726
727/************************************************************************/
730static struct help_item *new_help_item(int type)
731{
732 struct help_item *pitem;
733
734 pitem = fc_malloc(sizeof(struct help_item));
735 pitem->topic = NULL;
736 pitem->text = NULL;
737 pitem->type = type;
738
739 return pitem;
740}
741
742/************************************************************************/
746static int help_item_compar(const struct help_item *const *ppa,
747 const struct help_item *const *ppb)
748{
749 const struct help_item *ha, *hb;
750 char *ta, *tb;
751 ha = *ppa;
752 hb = *ppb;
753 for (ta = ha->topic, tb = hb->topic; *ta != '\0' && *tb != '\0'; ta++, tb++) {
754 if (*ta != ' ') {
755 if (*tb == ' ') return -1;
756 break;
757 } else if (*tb != ' ') {
758 if (*ta == ' ') return 1;
759 break;
760 }
761 }
762 return compare_strings(ta, tb);
763}
764
765/************************************************************************/
769{
770 static bool booted = FALSE;
771
772 struct section_file *sf;
773 const char *filename;
774 struct help_item *pitem;
775 int i;
776 struct section_list *sec;
777 const char **paras;
778 size_t npara;
779 char long_buffer[64000]; /* HACK: this may be overrun. */
780
782
783 /* need to do something like this or bad things happen */
785
786 if (!booted) {
787 log_verbose("Booting help texts");
788 } else {
789 /* free memory allocated last time booted */
791 log_verbose("Rebooting help texts");
792 }
793
794 filename = fileinfoname(get_data_dirs(), "helpdata.txt");
795 if (!filename) {
796 log_error("Did not read help texts");
797 return;
798 }
799 /* after following call filename may be clobbered; use sf->filename instead */
800 if (!(sf = secfile_load(filename, FALSE))) {
801 /* this is now unlikely to happen */
802 log_error("failed reading help-texts from '%s':\n%s", filename,
803 secfile_error());
804 return;
805 }
806
807 sec = secfile_sections_by_name_prefix(sf, "help_");
808
809 if (NULL != sec) {
810 section_list_iterate(sec, psection) {
812 const char *sec_name = section_name(psection);
813 const char *gen_str = secfile_lookup_str(sf, "%s.generate", sec_name);
814
815 if (gen_str) {
817 int level = strspn(gen_str, " ");
818
819 gen_str += level;
820
821 for (i = 2; help_type_names[i]; i++) {
822 if (strcmp(gen_str, help_type_names[i]) == 0) {
823 current_type = i;
824 break;
825 }
826 }
827 if (current_type == HELP_ANY) {
828 log_error("bad help-generate category \"%s\"", gen_str);
829 continue;
830 }
831
832 if (!booted) {
833 if (current_type == HELP_EXTRA) {
834 size_t ncats;
835
836 /* Avoid warnings about entries unused on this round,
837 * when the entries in question are valid once help system has been booted */
839 "%s.categories", sec_name);
840 }
841 continue; /* on initial boot data tables are empty */
842 }
843
844 {
845 /* Note these should really fill in pitem->text from auto-gen
846 data instead of doing it later on the fly, but I don't want
847 to change that now. --dwp
848 */
849 char name[2048];
851
852 switch (current_type) {
853 case HELP_UNIT:
856 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
858 pitem->topic = fc_strdup(name);
859 pitem->text = fc_strdup("");
862 break;
863 case HELP_TECH:
867 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
869 pitem->topic = fc_strdup(name);
870 pitem->text = fc_strdup("");
872 }
874 break;
875 case HELP_TERRAIN:
876 terrain_type_iterate(pterrain) {
877 if (0 != strlen(terrain_rule_name(pterrain))) {
879 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
880 terrain_name_translation(pterrain));
881 pitem->topic = fc_strdup(name);
882 pitem->text = fc_strdup("");
884 }
886 break;
887 case HELP_EXTRA:
888 {
889 const char **cats;
890 size_t ncats;
892 "%s.categories", sec_name);
893 extra_type_iterate(pextra) {
894 /* If categories not specified, don't filter */
895 if (cats) {
896 bool include = FALSE;
897 const char *cat = extra_category_name(pextra->category);
898 int ci;
899
900 for (ci = 0; ci < ncats; ci++) {
901 if (fc_strcasecmp(cats[ci], cat) == 0) {
902 include = TRUE;
903 break;
904 }
905 }
906 if (!include) {
907 continue;
908 }
909 }
911 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
912 extra_name_translation(pextra));
913 pitem->topic = fc_strdup(name);
914 pitem->text = fc_strdup("");
917 FC_FREE(cats);
918 }
919 break;
920 case HELP_GOODS:
923 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
925 pitem->topic = fc_strdup(name);
926 pitem->text = fc_strdup("");
929 break;
930 case HELP_SPECIALIST:
933
935 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
937 pitem->topic = fc_strdup(name);
938 pitem->text = fc_strdup("");
941 break;
942 case HELP_GOVERNMENT:
945 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
947 pitem->topic = fc_strdup(name);
948 pitem->text = fc_strdup("");
951 break;
952 case HELP_IMPROVEMENT:
953 improvement_iterate(pimprove) {
954 if (valid_improvement(pimprove) && !is_great_wonder(pimprove)) {
956 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
958 pitem->topic = fc_strdup(name);
959 pitem->text = fc_strdup("");
961 }
963 break;
964 case HELP_WONDER:
965 improvement_iterate(pimprove) {
966 if (valid_improvement(pimprove) && is_great_wonder(pimprove)) {
968 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
970 pitem->topic = fc_strdup(name);
971 pitem->text = fc_strdup("");
973 }
975 break;
976 case HELP_RULESET:
977 {
978 int desc_len;
979 int len;
980
982 /* pitem->topic = fc_strdup(_(game.control.name)); */
983 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
985 pitem->topic = fc_strdup(name);
988 } else {
989 desc_len = 0;
990 }
991 if (game.ruleset_summary != NULL) {
992 if (game.control.version[0] != '\0') {
994 + strlen(" ")
996 + strlen("\n\n")
998 + 1;
999
1000 pitem->text = fc_malloc(len + desc_len);
1001 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1004 } else {
1006 + strlen("\n\n")
1008 + 1;
1009
1010 pitem->text = fc_malloc(len + desc_len);
1011 fc_snprintf(pitem->text, len, "%s\n\n%s",
1013 }
1014 } else {
1015 const char *nodesc = _("Current ruleset contains no summary.");
1016
1017 if (game.control.version[0] != '\0') {
1019 + strlen(" ")
1021 + strlen("\n\n")
1022 + strlen(nodesc)
1023 + 1;
1024
1025 pitem->text = fc_malloc(len + desc_len);
1026 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1028 nodesc);
1029 } else {
1031 + strlen("\n\n")
1032 + strlen(nodesc)
1033 + 1;
1034
1035 pitem->text = fc_malloc(len + desc_len);
1036 fc_snprintf(pitem->text, len, "%s\n\n%s",
1037 _(game.control.name),
1038 nodesc);
1039 }
1040 }
1041 if (game.ruleset_description != NULL) {
1042 fc_strlcat(pitem->text, "\n\n", len + desc_len);
1044 }
1046 }
1047 break;
1048 case HELP_TILESET:
1049 {
1050 int desc_len;
1051 int len;
1052 const char *ts_name = tileset_name_get(tileset);
1053 const char *version = tileset_version(tileset);
1054 const char *summary = tileset_summary(tileset);
1055 const char *description = tileset_description(tileset);
1056
1058 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1060 pitem->topic = fc_strdup(name);
1061 if (description != NULL) {
1062 desc_len = strlen("\n\n") + strlen(description);
1063 } else {
1064 desc_len = 0;
1065 }
1066 if (summary != NULL) {
1067 if (version[0] != '\0') {
1068 len = strlen(_(ts_name))
1069 + strlen(" ")
1070 + strlen(version)
1071 + strlen("\n\n")
1072 + strlen(_(summary))
1073 + 1;
1074
1075 pitem->text = fc_malloc(len + desc_len);
1076 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1077 _(ts_name), version, _(summary));
1078 } else {
1079 len = strlen(_(ts_name))
1080 + strlen("\n\n")
1081 + strlen(_(summary))
1082 + 1;
1083
1084 pitem->text = fc_malloc(len + desc_len);
1085 fc_snprintf(pitem->text, len, "%s\n\n%s",
1086 _(ts_name), _(summary));
1087 }
1088 } else {
1089 const char *nodesc = _("Current tileset contains no summary.");
1090
1091 if (version[0] != '\0') {
1092 len = strlen(_(ts_name))
1093 + strlen(" ")
1094 + strlen(version)
1095 + strlen("\n\n")
1096 + strlen(nodesc)
1097 + 1;
1098
1099 pitem->text = fc_malloc(len + desc_len);
1100 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1101 _(ts_name), version,
1102 nodesc);
1103 } else {
1104 len = strlen(_(ts_name))
1105 + strlen("\n\n")
1106 + strlen(nodesc)
1107 + 1;
1108
1109 pitem->text = fc_malloc(len + desc_len);
1110 fc_snprintf(pitem->text, len, "%s\n\n%s",
1111 _(ts_name),
1112 nodesc);
1113 }
1114 }
1115 if (description != NULL) {
1116 fc_strlcat(pitem->text, "\n\n", len + desc_len);
1117 fc_strlcat(pitem->text, description, len + desc_len);
1118 }
1120 }
1121 break;
1122 case HELP_MUSICSET:
1123 {
1124 int desc_len;
1125 int len;
1126 const char *ms_name = current_musicset_name();
1127 const char *version = current_musicset_version();
1128 const char *summary = current_musicset_summary();
1129 const char *description = current_musicset_description();
1130
1132 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1134 pitem->topic = fc_strdup(name);
1135 if (description != NULL) {
1136 desc_len = strlen("\n\n") + strlen(description);
1137 } else {
1138 desc_len = 0;
1139 }
1140 if (summary != NULL) {
1141 if (version != NULL && version[0] != '\0') {
1142 len = strlen(_(ms_name))
1143 + strlen(" ")
1144 + strlen(version)
1145 + strlen("\n\n")
1146 + strlen(_(summary))
1147 + 1;
1148
1149 pitem->text = fc_malloc(len + desc_len);
1150 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1151 _(ms_name), version, _(summary));
1152 } else {
1153 len = strlen(_(ms_name))
1154 + strlen("\n\n")
1155 + strlen(_(summary))
1156 + 1;
1157
1158 pitem->text = fc_malloc(len + desc_len);
1159 fc_snprintf(pitem->text, len, "%s\n\n%s",
1160 _(ms_name), _(summary));
1161 }
1162 } else {
1163 const char *nodesc = _("Current musicset contains no summary.");
1164
1165 if (version != NULL && version[0] != '\0') {
1166 len = strlen(_(ms_name))
1167 + strlen(" ")
1168 + strlen(version)
1169 + strlen("\n\n")
1170 + strlen(nodesc)
1171 + 1;
1172
1173 pitem->text = fc_malloc(len + desc_len);
1174 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
1175 _(ms_name), version,
1176 nodesc);
1177 } else {
1178 len = strlen(_(ms_name))
1179 + strlen("\n\n")
1180 + strlen(nodesc)
1181 + 1;
1182
1183 pitem->text = fc_malloc(len + desc_len);
1184 fc_snprintf(pitem->text, len, "%s\n\n%s",
1185 _(ms_name),
1186 nodesc);
1187 }
1188 }
1189 if (description != NULL) {
1190 fc_strlcat(pitem->text, "\n\n", len + desc_len);
1191 fc_strlcat(pitem->text, description, len + desc_len);
1192 }
1194 }
1195 break;
1196 case HELP_NATIONS:
1197 nations_iterate(pnation) {
1199 || show_help_for_nation(pnation)) {
1201 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1202 nation_plural_translation(pnation));
1203 pitem->topic = fc_strdup(name);
1204 pitem->text = fc_strdup("");
1206 }
1208 break;
1209 case HELP_MULTIPLIER:
1210 multipliers_iterate(pmul) {
1211 help_text_buffer[0] = '\0';
1213 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1214 name_translation_get(&pmul->name));
1215 pitem->topic = fc_strdup(name);
1216 if (pmul->helptext) {
1217 const char *sep = "";
1218 strvec_iterate(pmul->helptext, text) {
1220 "%s%s", sep, text);
1221 sep = "\n\n";
1223 }
1227 break;
1228 case HELP_COUNTER:
1229 {
1230 int j;
1231 for (j = 0; j < game.control.num_counters; j++) {
1232 struct counter *pcount = counter_by_id(j);
1233
1234 help_text_buffer[0] = '\0';
1236 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
1238 pitem->topic = fc_strdup(name);
1239 if (pcount->helptext) {
1240 strvec_iterate(pcount->helptext, text) {
1242 "%s%s", "\n\n", text);
1244 }
1247 }
1248 }
1249 break;
1250 default:
1251 log_error("Bad current_type: %d.", current_type);
1252 break;
1253 }
1259 continue;
1260 }
1261 }
1262
1263 /* It wasn't a "generate" node: */
1264
1266 pitem->topic = fc_strdup(Q_(secfile_lookup_str(sf, "%s.name",
1267 sec_name)));
1268
1269 paras = secfile_lookup_str_vec(sf, &npara, "%s.text", sec_name);
1270
1271 long_buffer[0] = '\0';
1272 for (i = 0; i < npara; i++) {
1273 bool inserted;
1274 const char *para = paras[i];
1275
1276 if (!fc_strncmp(para, "$", 1)) {
1277 inserted
1279 } else {
1281 inserted = TRUE;
1282 }
1283 if (inserted && i != npara - 1) {
1284 sz_strlcat(long_buffer, "\n\n");
1285 }
1286 }
1287 free(paras);
1288 paras = NULL;
1289 pitem->text = fc_strdup(long_buffer);
1292
1294 }
1295
1297 secfile_destroy(sf);
1298 booted = TRUE;
1299 log_verbose("Booted help texts ok");
1300}
1301
1302/****************************************************************************
1303 The following few functions are essentially wrappers for the
1304 help_nodes help_list. This allows us to avoid exporting the
1305 help_list, and instead only access it through a controlled
1306 interface.
1307****************************************************************************/
1308
1309/************************************************************************/
1313{
1315 return help_list_size(help_nodes);
1316}
1317
1318/************************************************************************/
1323const struct help_item *get_help_item(int pos)
1324{
1325 int size;
1326
1329 if (pos < 0 || pos > size) {
1330 log_error("Bad index %d to get_help_item (size %d)", pos, size);
1331 return NULL;
1332 }
1333 if (pos == size) {
1334 return NULL;
1335 }
1336 return help_list_get(help_nodes, pos);
1337}
1338
1339/************************************************************************/
1345const struct help_item*
1347{
1348 int idx;
1349 const struct help_item *pitem = NULL;
1350 static struct help_item vitem; /* v = virtual */
1351 static char vtopic[128];
1352 static char vtext[256];
1353
1355 idx = 0;
1357 char *p = ptmp->topic;
1358
1359 while (*p == ' ') {
1360 p++;
1361 }
1362 if (strcmp(name, p) == 0 && (htype == HELP_ANY || htype == ptmp->type)) {
1363 pitem = ptmp;
1364 break;
1365 }
1366 idx++;
1367 }
1369
1370 if (!pitem) {
1371 idx = -1;
1372 vitem.topic = vtopic;
1374 vitem.text = vtext;
1375 if (htype == HELP_ANY || htype == HELP_TEXT) {
1376 fc_snprintf(vtext, sizeof(vtext),
1377 _("Sorry, no help topic for %s.\n"), vitem.topic);
1378 vitem.type = HELP_TEXT;
1379 } else {
1380 fc_snprintf(vtext, sizeof(vtext),
1381 _("Sorry, no help topic for %s.\n"
1382 "This page was auto-generated.\n\n"),
1383 vitem.topic);
1384 vitem.type = htype;
1385 }
1386 pitem = &vitem;
1387 }
1388 *pos = idx;
1389 return pitem;
1390}
1391
1392/************************************************************************/
1403
1404/************************************************************************/
1408const struct help_item *help_iter_next(void)
1409{
1410 const struct help_item *pitem;
1411
1414 if (pitem) {
1416 }
1417
1418 return pitem;
1419}
1420
1421/****************************************************************************
1422 FIXME:
1423 Also, in principle these could be auto-generated once, inserted
1424 into pitem->text, and then don't need to keep re-generating them.
1425 Only thing to be careful of would be changeable data, but don't
1426 have that here (for ruleset change or spacerace change must
1427 re-boot helptexts anyway). Eg, genuinely dynamic information
1428 which could be useful would be if help system said which wonders
1429 have been built (or are being built and by who/where?)
1430****************************************************************************/
1431
1432/************************************************************************/
1439char *helptext_building(char *buf, size_t bufsz, struct player *pplayer,
1440 const char *user_text, const struct impr_type *pimprove)
1441{
1442 bool reqs = FALSE;
1443 struct universal source = {
1445 .value = {.building = pimprove}
1446 };
1447
1448 fc_assert_ret_val(NULL != buf && 0 < bufsz, NULL);
1449 buf[0] = '\0';
1450
1451 if (NULL == pimprove) {
1452 return buf;
1453 }
1454
1455 if (NULL != pimprove->helptext) {
1456 strvec_iterate(pimprove->helptext, text) {
1457 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
1459 }
1460
1461 /* Add requirement text for improvement itself */
1462 requirement_vector_iterate(&pimprove->reqs, preq) {
1463 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
1464 reqs = TRUE;
1465 }
1467 if (reqs) {
1468 fc_strlcat(buf, "\n", bufsz);
1469 }
1470
1472 if (VUT_ADVANCE == pobs->source.kind
1473 && pobs->present && !pobs->quiet) {
1475 _("%s The discovery of %s will make %s obsolete.\n"),
1476 BULLET,
1477 advance_name_translation(pobs->source.value.advance),
1479 }
1480 if (VUT_IMPROVEMENT == pobs->source.kind
1481 && pobs->present && !pobs->quiet) {
1483 /* TRANS: both %s are improvement names */
1484 _("%s The presence of %s in the city will make %s "
1485 "obsolete.\n"),
1486 BULLET,
1487 improvement_name_translation(pobs->source.value.building),
1489 }
1491
1492 if (is_small_wonder(pimprove)) {
1494 _("%s A 'small wonder': at most one of your cities may "
1495 "possess this improvement.\n"), BULLET);
1496 }
1497 /* (Great wonders are in their own help section explaining their
1498 * uniqueness, so we don't mention it here.) */
1499
1500 if (building_has_effect(pimprove, EFT_ENABLE_NUKE)) {
1502 struct unit_type *u = NULL;
1503
1504 {
1505 /* Find Manhattan dependent nuke actions */
1506 int i = 0;
1507
1510
1512 }
1513
1515 if (num_role_units(action_id_get_role(act_id)) > 0) {
1516 u = get_role_unit(action_id_get_role(act_id), 0);
1517 break;
1518 }
1520
1521 if (u) {
1522 struct advance *req = NULL;
1523 int count = 0;
1524
1526 req = preq;
1527 count++;
1529
1530 if (req != NULL) {
1531 if (count == 1) {
1533 /* TRANS: 'Allows all players with knowledge of atomic
1534 * power to build nuclear units.' */
1535 _("%s Allows all players with knowledge of %s "
1536 "to build %s units.\n"), BULLET,
1539 } else {
1540 /* Multiple tech requirements */
1542 /* TRANS: 'Allows all players with knowledge of required
1543 * techs to build nuclear units.' */
1544 _("%s Allows all players with knowledge of required "
1545 "techs to build %s units.\n"), BULLET,
1547 }
1548 } else {
1550 /* TRANS: 'Allows all players to build nuclear units.' */
1551 _("%s Allows all players to build %s units.\n"), BULLET,
1553 }
1554 }
1555 }
1556
1558 BULLET_SPACE);
1559
1560 /* Actions that requires the building to target a city. */
1561 action_iterate(act) {
1562 /* Nothing is found yet. */
1563 bool demanded = FALSE;
1565
1566 if (action_id_get_target_kind(act) != ATK_CITY) {
1567 /* Not relevant */
1568 continue;
1569 }
1570
1571 if (action_by_number(act)->quiet) {
1572 /* The ruleset it self documents this action. */
1573 continue;
1574 }
1575
1577 if (universal_fulfills_requirements(TRUE, &(enabler->target_reqs),
1578 &source)) {
1579 /* The building is needed by this action enabler. */
1580 demanded = TRUE;
1581
1582 /* See if this enabler gives the building a wider range. */
1583 requirement_vector_iterate(&(enabler->target_reqs), preq) {
1585 /* Not relevant. */
1586 continue;
1587 }
1588
1589 if (!preq->present) {
1590 /* A !present larger range requirement would make the present
1591 * lower range illegal. */
1592 continue;
1593 }
1594
1595 if (preq->range > max_range) {
1596 /* Found a larger range. */
1597 max_range = preq->range;
1598 /* Intentionally not breaking here. The requirement vector may
1599 * contain other requirements with a larger range.
1600 * Example: Building a GreatWonder in a city with a Palace. */
1601 }
1603 }
1605
1606 if (demanded) {
1607 switch (max_range) {
1608 case REQ_RANGE_LOCAL:
1609 /* At least one action enabler needed the building in its target
1610 * requirements. */
1612 /* TRANS: Help build Wonder */
1613 _("%s Makes it possible to target the city building it "
1614 "with the action \'%s\'.\n"), BULLET,
1616 break;
1617 case REQ_RANGE_CITY:
1618 /* At least one action enabler needed the building in its target
1619 * requirements. */
1621 /* TRANS: Help build Wonder */
1622 _("%s Makes it possible to target its city with the "
1623 "action \'%s\'.\n"), BULLET,
1625 break;
1627 /* At least one action enabler needed the building in its target
1628 * requirements. */
1630 /* TRANS: Help build Wonder */
1631 _("%s Makes it possible to target its city and its "
1632 "trade partners with the action \'%s\'.\n"),
1633 BULLET,
1635 break;
1637 /* At least one action enabler needed the building in its target
1638 * requirements. */
1640 /* TRANS: Help build Wonder */
1641 _("%s Makes it possible to target all cities with its "
1642 "owner on its continent with the action \'%s\'.\n"),
1643 BULLET,
1645 break;
1646 case REQ_RANGE_PLAYER:
1647 /* At least one action enabler needed the building in its target
1648 * requirements. */
1650 /* TRANS: Help build Wonder */
1651 _("%s Makes it possible to target all cities with its "
1652 "owner with the action \'%s\'.\n"),
1653 BULLET,
1655 break;
1656 case REQ_RANGE_TEAM:
1657 /* At least one action enabler needed the building in its target
1658 * requirements. */
1660 /* TRANS: Help build Wonder */
1661 _("%s Makes it possible to target all cities on the "
1662 "same team with the action \'%s\'.\n"),
1663 BULLET,
1665 break;
1666 case REQ_RANGE_ALLIANCE:
1667 /* At least one action enabler needed the building in its target
1668 * requirements. */
1670 /* TRANS: Help build Wonder */
1671 _("%s Makes it possible to target all cities owned by "
1672 "or allied to its owner with the action \'%s\'.\n"),
1673 BULLET,
1675 break;
1676 case REQ_RANGE_WORLD:
1677 /* At least one action enabler needed the building in its target
1678 * requirements. */
1680 /* TRANS: Help build Wonder */
1681 _("%s Makes it possible to target all cities with the "
1682 "action \'%s\'.\n"),
1683 BULLET,
1685 break;
1686 case REQ_RANGE_TILE:
1688 case REQ_RANGE_ADJACENT:
1689 case REQ_RANGE_COUNT:
1690 log_error("The range %s is invalid for buildings.",
1692 break;
1693 }
1694 }
1696
1697 /* Building protects against action. */
1698 action_iterate(act) {
1699 struct action *paction = action_by_number(act);
1700 /* Nothing is found yet. */
1701 bool vulnerable = FALSE;
1703
1704 if (action_id_get_target_kind(act) != ATK_CITY) {
1705 /* Not relevant */
1706 continue;
1707 }
1708
1709 if (!action_is_in_use(paction)) {
1710 /* This action isn't enabled at all. */
1711 continue;
1712 }
1713
1714 if (action_by_number(act)->quiet) {
1715 /* The ruleset it self documents this action. */
1716 continue;
1717 }
1718
1719 /* Must be immune in all cases. */
1722 &(enabler->target_reqs))) {
1723 vulnerable = TRUE;
1724 break;
1725 } else {
1727
1728 requirement_vector_iterate(&(enabler->target_reqs), preq) {
1730 /* Not relevant. */
1731 continue;
1732 }
1733
1734 if (preq->present) {
1735 /* Not what is looked for. */
1736 continue;
1737 }
1738
1739 if (preq->range > vector_max_range) {
1740 /* Found a larger range. */
1741 vector_max_range = preq->range;
1742 }
1744
1746 /* Found a smaller range. */
1748 }
1749 }
1751
1752 if (!vulnerable) {
1753 switch (min_range) {
1754 case REQ_RANGE_LOCAL:
1756 /* TRANS: Incite City */
1757 _("%s Makes it impossible to do the action \'%s\' to "
1758 "the city building it.\n"),
1759 BULLET,
1761 break;
1762 case REQ_RANGE_CITY:
1764 /* TRANS: Incite City */
1765 _("%s Makes it impossible to do the action \'%s\' to "
1766 "its city.\n"),
1767 BULLET,
1769 break;
1772 /* TRANS: Incite City */
1773 _("%s Makes it impossible to do the action \'%s\' to "
1774 "its city or to its city's trade partners.\n"),
1775 BULLET,
1777 break;
1780 /* TRANS: Incite City */
1781 _("%s Makes it impossible to do the action \'%s\' to "
1782 "any city with its owner on its continent.\n"),
1783 BULLET,
1785 break;
1786 case REQ_RANGE_PLAYER:
1788 /* TRANS: Incite City */
1789 _("%s Makes it impossible to do the action \'%s\' to "
1790 "any city with its owner.\n"),
1791 BULLET,
1793 break;
1794 case REQ_RANGE_TEAM:
1796 /* TRANS: Incite City */
1797 _("%s Makes it impossible to do the action \'%s\' to "
1798 "any city on the same team.\n"),
1799 BULLET,
1801 break;
1802 case REQ_RANGE_ALLIANCE:
1804 /* TRANS: Incite City */
1805 _("%s Makes it impossible to do the action \'%s\' to "
1806 "any city allied to or owned by its owner.\n"),
1807 BULLET,
1809 break;
1810 case REQ_RANGE_WORLD:
1812 /* TRANS: Incite City */
1813 _("%s Makes it impossible to do the action \'%s\' to "
1814 "any city in the game.\n"),
1815 BULLET,
1817 break;
1818 case REQ_RANGE_TILE:
1820 case REQ_RANGE_ADJACENT:
1821 case REQ_RANGE_COUNT:
1822 log_error("The range %s is invalid for buildings.",
1824 break;
1825 }
1826 }
1828
1829 {
1830 int i;
1831
1832 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
1834 if (n == B_LAST) {
1835 break;
1836 } else if (improvement_by_number(n) == pimprove) {
1838 _("%s All players start with this improvement in their "
1839 "first city.\n"), BULLET);
1840 break;
1841 }
1842 }
1843 }
1844
1845 /* Assume no-one will set the same building in both global and nation
1846 * init_buildings... */
1847 nations_iterate(pnation) {
1848 int i;
1849
1850 /* Avoid mentioning nations not in current set. */
1851 if (!show_help_for_nation(pnation)) {
1852 continue;
1853 }
1854 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
1855 Impr_type_id n = pnation->init_buildings[i];
1856 if (n == B_LAST) {
1857 break;
1858 } else if (improvement_by_number(n) == pimprove) {
1860 /* TRANS: %s is a nation plural */
1861 _("%s The %s start with this improvement in their "
1862 "first city.\n"), BULLET,
1863 nation_plural_translation(pnation));
1864 break;
1865 }
1866 }
1868
1871 /* TRANS: don't translate 'savepalace' */
1872 _("%s If you lose the city containing this improvement, "
1873 "it will be rebuilt for free in another of your cities "
1874 "(if the 'savepalace' server setting is enabled).\n"),
1875 BULLET);
1876 }
1877
1878 if (user_text && user_text[0] != '\0') {
1879 cat_snprintf(buf, bufsz, "\n\n%s", user_text);
1880 }
1881 return buf;
1882}
1883
1884/************************************************************************/
1890static bool utype_may_do_escape_action(const struct unit_type *utype)
1891{
1892 action_iterate(act_id) {
1893 struct action *paction = action_by_number(act_id);
1894
1896 /* Not relevant. */
1897 continue;
1898 }
1899
1900 if (!utype_can_do_action(utype, paction->id)) {
1901 /* Can't do it. */
1902 continue;
1903 }
1904
1906 /* No escape when dead. */
1907 continue;
1908 }
1909
1910 if (paction->actor.is_unit.moves_actor == MAK_ESCAPE) {
1911 /* Survives and escapes. */
1912 return TRUE;
1913 }
1915
1916 return FALSE;
1917}
1918
1919/************************************************************************/
1926void helptext_unitclass(struct unit_class *pclass, char *buf, size_t bufsz)
1927{
1928 int flagid;
1929
1930 if (pclass->helptext != NULL) {
1931 strvec_iterate(pclass->helptext, text) {
1932 cat_snprintf(buf, bufsz, "\n%s\n", _(text));
1934 } else {
1935 CATLSTR(buf, bufsz, "\n");
1936 }
1937
1939 /* TRANS: indented unit class property, preserve leading spaces */
1940 CATLSTR(buf, bufsz, _(" %s Speed is not affected by terrain.\n"),
1941 BULLET);
1942 }
1944 /* TRANS: indented unit class property, preserve leading spaces */
1945 CATLSTR(buf, bufsz, _(" %s Does not get defense bonuses from terrain.\n"),
1946 BULLET);
1947 }
1948
1950 /* TRANS: indented unit class property, preserve leading spaces */
1951 CATLSTR(buf, bufsz, _(" %s Not subject to zones of control.\n"),
1952 BULLET);
1953 }
1954
1956 /* TRANS: indented unit class property, preserve leading spaces */
1957 CATLSTR(buf, bufsz, _(" %s Slowed down while damaged.\n"), BULLET);
1958 }
1959
1961 CATLSTR(buf, bufsz,
1962 /* TRANS: Indented unit class property, preserve leading spaces */
1963 _(" %s Doesn't prevent enemy cities from working the tile it's on.\n"),
1964 BULLET);
1965 }
1966
1969 const char *helptxt = unit_class_flag_helptxt(flagid);
1970
1971 if (helptxt != NULL) {
1972 /* TRANS: Indented unit class property, preserve leading spaces */
1973 CATLSTR(buf, bufsz, " %s %s\n", BULLET, _(helptxt));
1974 }
1975 }
1976 }
1977}
1978
1979/************************************************************************/
1985char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
1986 const char *user_text, const struct unit_type *utype,
1987 bool class_help)
1988{
1989 bool has_vet_levels;
1990 int flagid;
1991 struct unit_class *pclass;
1992 int fuel;
1993
1994 fc_assert_ret_val(NULL != buf && 0 < bufsz && NULL != user_text, NULL);
1995
1996 if (!utype) {
1997 log_error("Unknown unit!");
1999 return buf;
2000 }
2001
2003
2004 buf[0] = '\0';
2005
2006 pclass = utype_class(utype);
2008 _("%s Belongs to %s unit class."),
2009 BULLET,
2011
2012 if (class_help) {
2014 } else {
2015 cat_snprintf(buf, bufsz, "\n");
2016 }
2017
2019 && !utype_has_flag(utype, UTYF_IGZOC)) {
2020 /* TRANS: Indented unit class property, preserve leading spaces */
2021 CATLSTR(buf, bufsz, _(" %s Subject to zones of control.\n"),
2022 BULLET);
2023 }
2024
2025 if (utype->defense_strength > 0) {
2026 struct universal unit_is_in_city[] = {
2027 { .kind = VUT_UTYPE, .value = { .utype = utype }},
2028 { .kind = VUT_CITYTILE, .value = { .citytile = CITYT_CENTER }},
2029 };
2030 int bonus = effect_value_from_universals(
2033
2034 if (bonus > 0) {
2036 /* TRANS: Indented unit class property, preserve leading
2037 * spaces */
2038 _(" %s Gets a %d%% defensive bonus while in cities.\n"),
2039 BULLET, bonus);
2040 }
2041 }
2043 CATLSTR(buf, bufsz,
2044 /* TRANS: indented unit class property, preserve leading spaces */
2045 _(" %s Is unreachable. Most units cannot attack this one.\n"),
2046 BULLET);
2047 if (utype_has_flag(utype, UTYF_NEVER_PROTECTS)) {
2048 CATLSTR(buf, bufsz,
2049 /* TRANS: Indented twice; preserve leading spaces */
2050 _(" %s Doesn't prevent enemy units from attacking other "
2051 "units on its tile.\n"), BULLET);
2052 }
2053 }
2054
2055 if (can_attack_non_native(utype)) {
2056 CATLSTR(buf, bufsz,
2057 /* TRANS: Indented unit class property, preserve leading spaces */
2058 _(" %s Can attack units on non-native tiles.\n"), BULLET);
2059 }
2060
2061 /* The unit's combat bonuses. Won't mention that another unit type has a
2062 * combat bonus against this unit type. Doesn't handle complex cases like
2063 * when a unit type has multiple combat bonuses of the same kind. */
2065 const char *against[utype_count()];
2066 int targets = 0;
2067
2068 if (cbonus->quiet) {
2069 /* Handled in the help text of the ruleset. */
2070 continue;
2071 }
2072
2073 /* Find the unit types of the bonus targets. */
2075 if (utype_has_flag(utype2, cbonus->flag)) {
2077 }
2079
2080 if (targets > 0) {
2081 struct astring list = ASTRING_INIT;
2082
2083 switch (cbonus->type) {
2086 /* TRANS: percentage ... or-list of unit types */
2087 _("%s %d%% defense bonus if attacked by %s.\n"),
2088 BULLET,
2089 cbonus->value * 100,
2090 astr_build_or_list(&list, against, targets));
2091 break;
2094 /* TRANS: defense divider ... or-list of unit types */
2095 _("%s Reduces target's defense to 1 / %d when "
2096 "attacking %s.\n"), BULLET,
2097 cbonus->value + 1,
2098 astr_build_or_list(&list, against, targets));
2099 break;
2102 /* TRANS: or-list of unit types */
2103 _("%s Reduces target's firepower to 1 when "
2104 "attacking %s.\n"), BULLET,
2105 astr_build_and_list(&list, against, targets));
2106 break;
2109 /* TRANS: percentage ... or-list of unit types */
2110 _("%s %d%% defense bonus if attacked by %s.\n"),
2111 BULLET, cbonus->value,
2112 astr_build_or_list(&list, against, targets));
2113 break;
2116 /* TRANS: defense divider ... or-list of unit types */
2117 _("%s Reduces target's defense to 1 / %.2f when "
2118 "attacking %s.\n"), BULLET,
2119 ((float) cbonus->value + 100.0f) / 100.0f,
2120 astr_build_or_list(&list, against, targets));
2121 break;
2124 /* TRANS: percentage ... or-list of unit types */
2125 _("%s %d%% defense bonus "
2126 "instead of any bonuses from city improvements "
2127 "if attacked by %s in a city.\n"),
2128 BULLET, cbonus->value,
2129 astr_build_or_list(&list, against, targets));
2130 break;
2131 }
2132
2133 astr_free(&list);
2134 }
2136
2137 /* Add requirement text for the unit type itself */
2140 BULLET_SPACE);
2142
2144 CATLSTR(buf, bufsz, _("%s Can pursue escaping units and kill them.\n"),
2145 BULLET);
2146 }
2147
2148 if (utype_has_flag(utype, UTYF_NOBUILD)) {
2149 CATLSTR(buf, bufsz, _("%s May not be built in cities.\n"), BULLET);
2150 }
2151 if (utype_has_flag(utype, UTYF_BARBARIAN_ONLY)) {
2152 CATLSTR(buf, bufsz, _("%s Only barbarians may build this.\n"), BULLET);
2153 }
2155 CATLSTR(buf, bufsz, _("%s Can only be built in games where new cities "
2156 "are allowed.\n"), BULLET);
2158 /* TRANS: indented; preserve leading spaces */
2159 CATLSTR(buf, bufsz, _(" %s New cities are not allowed in the current "
2160 "game.\n"), BULLET);
2161 } else {
2162 /* TRANS: indented; preserve leading spaces */
2163 CATLSTR(buf, bufsz, _(" %s New cities are allowed in the current "
2164 "game.\n"), BULLET);
2165 }
2166 }
2167 nations_iterate(pnation) {
2168 int i, count = 0;
2169
2170 /* Avoid mentioning nations not in current set. */
2171 if (!show_help_for_nation(pnation)) {
2172 continue;
2173 }
2174 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
2175 if (!pnation->init_units[i]) {
2176 break;
2177 } else if (pnation->init_units[i] == utype) {
2178 count++;
2179 }
2180 }
2181 if (count > 0) {
2183 /* TRANS: %s is a nation plural */
2184 PL_("%s The %s start the game with %d of these units.\n",
2185 "%s The %s start the game with %d of these units.\n",
2186 count), BULLET,
2187 nation_plural_translation(pnation), count);
2188 }
2190 {
2191 const char *types[utype_count()];
2192 int i = 0;
2193
2195 if (utype2->converted_to == utype
2197 types[i++] = utype_name_translation(utype2);
2198 }
2200 if (i > 0) {
2201 struct astring list = ASTRING_INIT;
2202
2203 astr_build_or_list(&list, types, i);
2205 /* TRANS: %s is a list of unit types separated by "or". */
2206 _("%s May be obtained by conversion of %s.\n"),
2207 BULLET, astr_str(&list));
2208 astr_free(&list);
2209 }
2210 }
2211 if (utype_has_flag(utype, UTYF_NOHOME)) {
2213 CATLSTR(buf, bufsz, _("%s Built without a home city.\n"), BULLET);
2214 } else {
2215 CATLSTR(buf, bufsz, _("%s Never has a home city.\n"), BULLET);
2216 }
2217 }
2218 if (utype_has_flag(utype, UTYF_GAMELOSS)) {
2219 CATLSTR(buf, bufsz, _("%s Losing this unit will lose you the game!\n"),
2220 BULLET);
2221 }
2222 if (utype_has_flag(utype, UTYF_UNIQUE)) {
2223 CATLSTR(buf, bufsz,
2224 _("%s Each player may only have one of this type of unit.\n"),
2225 BULLET);
2226 }
2228 if (utype_has_flag(utype, flagid)) {
2229 const char *helptxt = unit_type_flag_helptxt(flagid);
2230
2231 if (helptxt != NULL) {
2232 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
2233 }
2234 }
2235 }
2236 if (utype->pop_cost > 0) {
2238 PL_("%s Costs %d population to build.\n",
2239 "%s Costs %d population to build.\n", utype->pop_cost),
2240 BULLET, utype->pop_cost);
2241 }
2242 if (0 < utype->transport_capacity) {
2243 const char *classes[uclass_count()];
2244 int i = 0;
2245 struct astring list = ASTRING_INIT;
2246
2247 unit_class_iterate(uclass) {
2248 if (can_unit_type_transport(utype, uclass)) {
2249 classes[i++] = uclass_name_translation(uclass);
2250 }
2253
2255 /* TRANS: %s is a list of unit classes separated by "or". */
2256 PL_("%s Can carry and refuel %d %s unit.\n",
2257 "%s Can carry and refuel up to %d %s units.\n",
2258 utype->transport_capacity),
2260 astr_free(&list);
2262 /* Document restrictions on when units can load/unload */
2267 if (utype_can_freely_load(pcargo, utype)) {
2269 } else {
2271 }
2272 if (utype_can_freely_unload(pcargo, utype)) {
2274 } else {
2276 }
2277 }
2279 if (has_restricted_load) {
2281 /* At least one type of cargo can load onto us freely.
2282 * The specific exceptions will be documented in cargo help. */
2283 CATLSTR(buf, bufsz,
2284 /* TRANS: indented; preserve leading spaces */
2285 _(" %s Some cargo cannot be loaded except in a city or a "
2286 "base native to this transport.\n"), BULLET);
2287 } else {
2288 /* No exceptions */
2289 CATLSTR(buf, bufsz,
2290 /* TRANS: indented; preserve leading spaces */
2291 _(" %s Cargo cannot be loaded except in a city or a "
2292 "base native to this transport.\n"), BULLET);
2293 }
2294 } /* else, no restricted cargo exists; keep quiet */
2297 /* At least one type of cargo can unload from us freely. */
2298 CATLSTR(buf, bufsz,
2299 /* TRANS: indented; preserve leading spaces */
2300 _(" %s Some cargo cannot be unloaded except in a city or a "
2301 "base native to this transport.\n"), BULLET);
2302 } else {
2303 /* No exceptions */
2304 CATLSTR(buf, bufsz,
2305 /* TRANS: indented; preserve leading spaces */
2306 _(" %s Cargo cannot be unloaded except in a city or a "
2307 "base native to this transport.\n"), BULLET);
2308 }
2309 } /* else, no restricted cargo exists; keep quiet */
2310 }
2311 }
2312 if (utype_has_flag(utype, UTYF_COAST_STRICT)) {
2313 CATLSTR(buf, bufsz, _("%s Must stay next to safe coast.\n"), BULLET);
2314 }
2315 {
2316 /* Document exceptions to embark/disembark restrictions that we
2317 * have as cargo. */
2318 bv_unit_classes embarks, disembarks;
2319 BV_CLR_ALL(embarks);
2320 BV_CLR_ALL(disembarks);
2321 /* Determine which of our transport classes have restrictions in the first
2322 * place (that is, contain at least one transport which carries at least
2323 * one type of cargo which is restricted).
2324 * We'll suppress output for classes not in this set, since this cargo
2325 * type is not behaving exceptionally in such cases. */
2328 /* Don't waste time repeating checks on classes we've already checked,
2329 * or weren't under consideration in the first place */
2330 if (!BV_ISSET(embarks, trans_class)
2331 && BV_ISSET(utype->embarks, trans_class)) {
2335 /* At least one load restriction in transport class, which
2336 * we aren't subject to */
2337 BV_SET(embarks, trans_class);
2338 }
2339 } unit_type_iterate_end; /* cargo */
2340 }
2341 if (!BV_ISSET(disembarks, trans_class)
2342 && BV_ISSET(utype->disembarks, trans_class)) {
2346 /* At least one load restriction in transport class, which
2347 * we aren't subject to */
2348 BV_SET(disembarks, trans_class);
2349 }
2350 } unit_type_iterate_end; /* cargo */
2351 }
2352 } unit_class_iterate_end; /* transports */
2353
2354 if (BV_ISSET_ANY(embarks)) {
2355 /* Build list of embark exceptions */
2356 const char *eclasses[uclass_count()];
2357 int i = 0;
2358 struct astring elist = ASTRING_INIT;
2359
2360 unit_class_iterate(uclass) {
2361 if (BV_ISSET(embarks, uclass_index(uclass))) {
2362 eclasses[i++] = uclass_name_translation(uclass);
2363 }
2366 if (BV_ARE_EQUAL(embarks, disembarks)) {
2367 /* A common case: the list of disembark exceptions is identical */
2369 /* TRANS: %s is a list of unit classes separated
2370 * by "or". */
2371 _("%s May load onto and unload from %s transports even "
2372 "when underway.\n"),
2373 BULLET, astr_str(&elist));
2374 } else {
2376 /* TRANS: %s is a list of unit classes separated
2377 * by "or". */
2378 _("%s May load onto %s transports even when underway.\n"),
2379 BULLET, astr_str(&elist));
2380 }
2381 astr_free(&elist);
2382 }
2383 if (BV_ISSET_ANY(disembarks) && !BV_ARE_EQUAL(embarks, disembarks)) {
2384 /* Build list of disembark exceptions (if different from embarking) */
2385 const char *dclasses[uclass_count()];
2386 int i = 0;
2387 struct astring dlist = ASTRING_INIT;
2388
2389 unit_class_iterate(uclass) {
2390 if (BV_ISSET(disembarks, uclass_index(uclass))) {
2391 dclasses[i++] = uclass_name_translation(uclass);
2392 }
2396 /* TRANS: %s is a list of unit classes separated
2397 * by "or". */
2398 _("%s May unload from %s transports even when underway.\n"),
2399 BULLET, astr_str(&dlist));
2400 astr_free(&dlist);
2401 }
2402 }
2403
2404 if (utype_has_flag(utype, UTYF_SPY)) {
2405 CATLSTR(buf, bufsz, _("%s Strong in diplomatic battles.\n"), BULLET);
2406 }
2407 if (utype_has_flag(utype, UTYF_DIPLOMAT)
2408 || utype_has_flag(utype, UTYF_SUPERSPY)) {
2409 CATLSTR(buf, bufsz, _("%s Defends cities against diplomatic actions.\n"),
2410 BULLET);
2411 }
2412 if (utype_has_flag(utype, UTYF_SUPERSPY)) {
2413 CATLSTR(buf, bufsz, _("%s Will never lose a diplomat-versus-diplomat fight.\n"),
2414 BULLET);
2415 }
2417 && utype_has_flag(utype, UTYF_SUPERSPY)) {
2418 CATLSTR(buf, bufsz, _("%s Will always survive a spy mission.\n"), BULLET);
2419 }
2420 if (utype->vlayer == V_INVIS) {
2421 CATLSTR(buf, bufsz,
2422 _("%s Is invisible except when next to an enemy unit or city.\n"),
2423 BULLET);
2424 }
2426 CATLSTR(buf, bufsz,
2427 _("%s Can only attack units on native tiles.\n"), BULLET);
2428 }
2429 if (utype_has_flag(utype, UTYF_CITYBUSTER)) {
2430 CATLSTR(buf, bufsz,
2431 _("%s Gets double firepower when attacking cities.\n"), BULLET);
2432 }
2433 if (utype_has_flag(utype, UTYF_IGTER)) {
2435 /* TRANS: "MP" = movement points. %s may have a
2436 * fractional part. */
2437 _("%s Ignores terrain effects (moving costs at most %s MP "
2438 "per tile).\n"), BULLET,
2440 }
2441 if (utype_has_flag(utype, UTYF_NOZOC)) {
2442 CATLSTR(buf, bufsz, _("%s Never imposes a zone of control.\n"), BULLET);
2443 } else {
2444 CATLSTR(buf, bufsz, _("%s May impose a zone of control on its adjacent "
2445 "tiles.\n"), BULLET);
2446 }
2447 if (utype_has_flag(utype, UTYF_IGZOC)) {
2448 CATLSTR(buf, bufsz, _("%s Not subject to zones of control imposed "
2449 "by other units.\n"), BULLET);
2450 }
2451 if (utype_has_flag(utype, UTYF_CIVILIAN)) {
2452 CATLSTR(buf, bufsz,
2453 _("%s A non-military unit:\n"), BULLET);
2454 CATLSTR(buf, bufsz,
2455 /* TRANS: indented; preserve leading spaces */
2456 _(" %s Cannot attack.\n"), BULLET);
2457 CATLSTR(buf, bufsz,
2458 /* TRANS: indented; preserve leading spaces */
2459 _(" %s Doesn't impose martial law.\n"), BULLET);
2460 CATLSTR(buf, bufsz,
2461 /* TRANS: indented; preserve leading spaces */
2462 _(" %s Can enter foreign territory regardless of peace treaty.\n"),
2463 BULLET);
2464 CATLSTR(buf, bufsz,
2465 /* TRANS: indented; preserve leading spaces */
2466 _(" %s Doesn't prevent enemy cities from working the tile it's on.\n"),
2467 BULLET);
2468 }
2469 if (utype_has_flag(utype, UTYF_FIELDUNIT)) {
2470 CATLSTR(buf, bufsz,
2471 _("%s A field unit: one unhappiness applies even when non-aggressive.\n"),
2472 BULLET);
2473 }
2474 if (utype_has_flag(utype, UTYF_PROVOKING)
2476 server_setting_by_name("autoattack"))) {
2477 CATLSTR(buf, bufsz,
2478 _("%s An enemy unit considering to auto attack this unit will "
2479 "choose to do so even if it has better odds when defending "
2480 "against it than when attacking it.\n"), BULLET);
2481 }
2482
2483 unit_class_iterate(target) {
2484 if (uclass_has_flag(target, UCF_UNREACHABLE)
2485 && BV_ISSET(utype->targets, uclass_index(target))) {
2487 _("%s Can attack against %s units, which are usually not "
2488 "reachable.\n"), BULLET,
2489 uclass_name_translation(target));
2490 }
2492
2493 fuel = utype_fuel(utype);
2494 if (fuel > 0) {
2495 const char *types[utype_count()];
2496 int i = 0;
2497
2501 }
2503
2504 if (0 == i) {
2505 if (utype_has_flag(utype, UTYF_COAST)) {
2506 if (fuel == 1) {
2508 _("%s Unit has to end each turn next to safe coast or"
2509 " in a city or a base.\n"), BULLET);
2510 } else {
2512 /* Pluralization for the benefit of languages with
2513 * duals etc */
2514 /* TRANS: Never called for 'turns = 1' case */
2515 PL_("%s Unit has to be next to safe coast, in a city or a base"
2516 " after %d turn.\n",
2517 "%s Unit has to be next to safe coast, in a city or a base"
2518 " after %d turns.\n",
2519 fuel),
2520 BULLET, fuel);
2521 }
2522 } else {
2524 PL_("%s Unit has to be in a city or a base"
2525 " after %d turn.\n",
2526 "%s Unit has to be in a city or a base"
2527 " after %d turns.\n",
2528 fuel),
2529 BULLET, fuel);
2530 }
2531 } else {
2532 struct astring list = ASTRING_INIT;
2533
2534 if (utype_has_flag(utype, UTYF_COAST)) {
2536 /* TRANS: %s is a list of unit types separated by "or" */
2537 PL_("%s Unit has to be next to safe coast, in a city, a base, or on a %s"
2538 " after %d turn.\n",
2539 "%s Unit has to be next to safe coast, in a city, a base, or on a %s"
2540 " after %d turns.\n",
2541 fuel),
2542 BULLET, astr_build_or_list(&list, types, i), fuel);
2543 } else {
2545 /* TRANS: %s is a list of unit types separated by "or" */
2546 PL_("%s Unit has to be in a city, a base, or on a %s"
2547 " after %d turn.\n",
2548 "%s Unit has to be in a city, a base, or on a %s"
2549 " after %d turns.\n",
2550 fuel),
2551 BULLET, astr_build_or_list(&list, types, i), fuel);
2552 }
2553 astr_free(&list);
2554 }
2555 }
2556
2557 /* Auto attack immunity. (auto_attack.if_attacker ruleset setting) */
2559 server_setting_by_name("autoattack"))) {
2561
2563 if (auto_action->cause != AAPC_UNIT_MOVED_ADJ) {
2564 /* Not relevant for auto attack. */
2565 continue;
2566 }
2567
2569 /* Can be forced to auto attack. */
2571 break;
2572 }
2574
2576 CATLSTR(buf, bufsz,
2577 _("%s Will never be forced (by the autoattack server setting)"
2578 " to attack units moving to an adjacent tile.\n"), BULLET);
2579 }
2580 }
2581
2582 action_iterate(act) {
2583 struct action *paction = action_by_number(act);
2584
2585 if (action_by_number(act)->quiet) {
2586 /* The ruleset documents this action it self. */
2587 continue;
2588 }
2589
2590 if (utype_can_do_action(utype, act)) {
2591 const char *target_adjective;
2592 char sub_target_text[100];
2593 const char *blockers[MAX_NUM_ACTIONS];
2594 int i = 0;
2595
2596 /* Generic action information. */
2598 /* TRANS: %s is the action's ruleset defined ui name */
2599 _("%s Can do the action \'%s\'.\n"),
2601
2602 switch (action_id_get_target_kind(act)) {
2603 case ATK_SELF:
2604 /* No target. */
2605 break;
2606 default:
2607 if (!can_utype_do_act_if_tgt_diplrel(utype, act,
2608 DRO_FOREIGN, TRUE)) {
2609 /* TRANS: describes the target of an action. */
2610 target_adjective = _("domestic ");
2611 } else if (!can_utype_do_act_if_tgt_diplrel(utype, act,
2612 DRO_FOREIGN, FALSE)) {
2613 /* TRANS: describes the target of an action. */
2614 target_adjective = _("foreign ");
2615 } else {
2616 /* Both foreign and domestic targets are acceptable. */
2617 target_adjective = "";
2618 }
2619
2620 sub_target_text[0] = '\0';
2625 /* TRANS: action sub target extras with tile
2626 * extras target. */
2627 _("extras among "));
2628 } else {
2630 /* TRANS: action sub target kind. */
2631 _("%s "),
2634 }
2635 }
2636
2638 /* TRANS: First %s in %s%s%s is the sub target kind.
2639 * The next may be an adjective (that includes a space).
2640 * The next is the name of the target kind.
2641 * Example: "* is done to extras on foreign tiles." */
2642 _(" %s is done to %s%s%s.\n"), BULLET,
2646 }
2647
2650 /* TRANS: said about an action. %s is a unit type
2651 * name. */
2652 _(" %s uses up the %s.\n"), BULLET,
2653 utype_name_translation(utype));
2654 }
2655
2656 if (actres_get_battle_kind(paction->result) != ABK_NONE) {
2658 /* TRANS: The %s is a kind of battle defined in
2659 * actions.h. Example: "diplomatic battle". */
2660 _(" %s can lead to a %s against a defender.\n"),
2661 BULLET,
2664 }
2665
2666 {
2667 struct universal req_pattern[] = {
2668 { .kind = VUT_ACTION, .value.action = paction },
2669 { .kind = VUT_UTYPE, .value.utype = utype },
2670 };
2672
2677 ((100 - odds) * 100
2678 / odds))) {
2680 _(" %s may fail because of a dice throw.\n"),
2681 BULLET);
2682 }
2683 }
2684
2686 && paction->actor.is_unit.moves_actor == MAK_ESCAPE) {
2688 /* TRANS: said about an action. %s is a unit type
2689 * name. */
2690 _(" %s the %s may be captured while trying to"
2691 " escape after completing the mission.\n"),
2692 BULLET,
2693 utype_name_translation(utype));
2694 }
2695
2697 /* The dead don't care about movement loss. */
2698 } else if (utype_action_takes_all_mp(utype, paction)) {
2700 /* TRANS: Indented unit action property, preserve
2701 * leading spaces. */
2702 _(" %s ends this unit's turn.\n"),
2703 BULLET);
2705 USP_NATIVE_TILE)) {
2706 /* Used in the implementation of slow_invasion in many of the
2707 * bundled rulesets and in rulesets upgraded with rscompat from 3.0
2708 * to 3.1. */
2710 /* TRANS: Indented unit action property, preserve
2711 * leading spaces. */
2712 _(" %s ending up on a native tile"
2713 " after this action has been performed"
2714 " ends this unit's turn.\n"), BULLET);
2715 }
2716
2717 if (action_id_get_target_kind(act) != ATK_SELF) {
2718 /* Distance to target is relevant. */
2719
2720 /* FIXME: move paratroopers_range to the action and remove this
2721 * variable once actions are generalized. */
2725 MIN(paction->max_distance, utype->paratroopers_range) :
2726 paction->max_distance;
2727
2728 if (paction->min_distance == relative_max) {
2729 /* Only one distance to target is acceptable */
2730
2731 if (paction->min_distance == 0) {
2733 /* TRANS: distance between an actor unit and its
2734 * target when performing a specific action. */
2735 _(" %s target must be at the same tile.\n"),
2736 BULLET);
2737 } else {
2739 /* TRANS: distance between an actor unit and its
2740 * target when performing a specific action. */
2741 PL_(" %s target must be exactly %d tile away.\n",
2742 " %s target must be exactly %d tiles away.\n",
2743 paction->min_distance),
2744 BULLET, paction->min_distance);
2745 }
2747 /* No max distance */
2748
2749 if (paction->min_distance == 0) {
2751 /* TRANS: distance between an actor unit and its
2752 * target when performing a specific action. */
2753 _(" %s target can be anywhere.\n"), BULLET);
2754 } else {
2756 /* TRANS: distance between an actor unit and its
2757 * target when performing a specific action. */
2758 PL_(" %s target must be at least %d tile away.\n",
2759 " %s target must be at least %d tiles away.\n",
2760 paction->min_distance),
2761 BULLET, paction->min_distance);
2762 }
2763 } else if (paction->min_distance == 0) {
2764 /* No min distance */
2765
2767 /* TRANS: distance between an actor unit and its
2768 * target when performing a specific action. */
2769 PL_(" %s target can be max %d tile away.\n",
2770 " %s target can be max %d tiles away.\n",
2771 relative_max),
2773 } else {
2774 /* Full range. */
2775
2777 /* TRANS: distance between an actor unit and its
2778 * target when performing a specific action. */
2779 PL_(" %s target must be between %d and %d tile away.\n",
2780 " %s target must be between %d and %d tiles away.\n",
2781 relative_max),
2782 BULLET, paction->min_distance, relative_max);
2783 }
2784 }
2785
2786 /* The action may be a Casus Belli. */
2787 {
2788 const struct {
2789 const enum effect_type eft;
2790 const char *hlp_text;
2791 } casus_belli[] = {
2792 /* TRANS: ...performing this action ... Casus Belli */
2793 { EFT_CASUS_BELLI_SUCCESS, N_("successfully") },
2794 /* TRANS: ...performing this action ... Casus Belli */
2795 { EFT_CASUS_BELLI_CAUGHT, N_("getting caught before") },
2796 };
2797
2798 struct universal req_pattern[] = {
2799 { .kind = VUT_ACTION, .value.action = paction },
2800 { .kind = VUT_DIPLREL, /* value filled in later */ },
2801 };
2802
2803 /* First group by effect (currently getting caught and successfully
2804 * performing the action) */
2805 for (i = 0; i < ARRAY_SIZE(casus_belli); i++) {
2806 int diplrel;
2807
2808 /* DiplRel list of each Casus Belli size. */
2809 const char *victim_diplrel_names[DRO_LAST];
2810 const char *outrage_diplrel_names[DRO_LAST];
2811 int victim_diplrel_count = 0;
2812 int outrage_diplrel_count = 0;
2813
2814 /* Ignore Team and everything in diplrel_other. */
2815 for (diplrel = 0; diplrel < DS_NO_CONTACT; diplrel++) {
2817
2818 if (!can_utype_do_act_if_tgt_diplrel(utype, act,
2819 diplrel, TRUE)) {
2820 /* Can't do the action. Can't give Casus Belli. */
2821 continue;
2822 }
2823
2824 req_pattern[1].value.diplrel = diplrel;
2826 casus_belli[i].eft,
2828
2831 diplrel_name_translation(diplrel);
2832 } else if (CASUS_BELLI_VICTIM <= casus_belli_amount) {
2834 diplrel_name_translation(diplrel);
2835 }
2836 }
2837
2838 /* Then group by Casus Belli size (currently victim and
2839 * international outrage) */
2840 if (outrage_diplrel_count > 0) {
2841 struct astring list = ASTRING_INIT;
2843 /* TRANS: successfully ... Peace, or Alliance */
2844 _(" %s %s performing this action during %s causes"
2845 " international outrage: the whole world gets "
2846 "Casus Belli against you.\n"), BULLET,
2850 astr_free(&list);
2851 }
2852 if (victim_diplrel_count > 0) {
2853 struct astring list = ASTRING_INIT;
2855 /* TRANS: successfully ... Peace, or Alliance */
2856 _(" %s %s performing this action during %s gives"
2857 " the victim Casus Belli against you.\n"),
2858 BULLET,
2862 astr_free(&list);
2863 }
2864 }
2865 }
2866
2867 /* Custom action result specific information. */
2868 switch (paction->result) {
2869 case ACTRES_HELP_WONDER:
2871 /* TRANS: the %d is the number of shields the unit can
2872 * contribute. */
2873 _(" %s adds %d production.\n"), BULLET,
2875 break;
2876 case ACTRES_HEAL_UNIT:
2877 {
2878 struct universal req_pattern[] = {
2879 { .kind = VUT_ACTION, .value.action = paction },
2880 { .kind = VUT_UTYPE, .value.utype = utype },
2881 };
2882
2884 _(" %s restores up to %d%% of the target unit's"
2885 " hit points.\n"), BULLET,
2889 + 100);
2890 }
2891 break;
2892 case ACTRES_FOUND_CITY:
2895 /* TRANS: is talking about an action. */
2896 _(" %s is disabled in the current game.\n"),
2897 BULLET);
2898 }
2900 /* TRANS: the %d is initial population. */
2901 PL_(" %s initial population: %d.\n",
2902 " %s initial population: %d.\n",
2903 utype->city_size),
2904 BULLET, utype->city_size);
2905 if (is_super_specialist(utype->spec_type)) {
2907 /* FIXME: Here we'd better have a singular specialist
2908 * but the translated name is plural by definition. */
2909 /* TRANS: * ... Great Artist(s) ... */
2910 _(" %s the city starts with a %s superspecialist.\n"),
2912 }
2913 break;
2914 case ACTRES_JOIN_CITY:
2915 if (utype->pop_cost > 0 ){
2917 /* TRANS: the %d is population. */
2918 PL_(" %s max target size: %d.\n",
2919 " %s max target size: %d.\n",
2923 /* TRANS: the %d is the population added. */
2924 PL_(" %s adds %d population.\n",
2925 " %s adds %d population.\n",
2926 utype->pop_cost),
2927 BULLET, utype->pop_cost);
2928 }
2929 if (is_super_specialist(utype->spec_type)) {
2931 /* FIXME: Here we'd better have a singular specialist
2932 * but the translated name is plural by definition. */
2933 /* TRANS: * ... Great Artist(s) ... */
2934 _(" %s adds a %s superspecialist to the city.\n"),
2936 } else if (DEFAULT_SPECIALIST != specialist_index(utype->spec_type)) {
2938 /* TRANS: * ... Scientists */
2939 _(" %s adds to cities as %s.\n"),
2941 }
2942 break;
2943 case ACTRES_BOMBARD:
2945 /* TRANS: %d is bombard rate. */
2946 _(" %s %d per turn.\n"), BULLET,
2947 utype->bombard_rate);
2949 /* TRANS: talking about bombard */
2950 _(" %s Will damage all"
2951 " defenders on a tile, and have no risk for the"
2952 " attacker.\n"), BULLET);
2953 break;
2956 /* TRANS: %s is a unit type. */
2957 _(" %s upgraded to %s or, when possible, to the unit "
2958 "type it upgrades to.\n"), BULLET,
2960 break;
2961 case ACTRES_ATTACK:
2962 if (game.info.tired_attack) {
2964 _(" %s weaker when tired. If performed with less "
2965 "than a single move point left the attack power "
2966 "is reduced accordingly.\n"), BULLET);
2967 }
2968 break;
2969 case ACTRES_WIPE_UNITS:
2971 _(" %s can wipe stack of units with zero defense.\n"),
2972 BULLET);
2973 break;
2974 case ACTRES_CONVERT:
2976 /* TRANS: %s is a unit type. "MP" = movement points. */
2977 PL_(" %s is converted into %s (takes %d MP).\n",
2978 " %s is converted into %s (takes %d MP).\n",
2979 utype->convert_time),
2980 BULLET,
2982 utype->convert_time);
2983 break;
2984 case ACTRES_SPY_NUKE:
2985 case ACTRES_NUKE:
2986 case ACTRES_NUKE_UNITS:
2987 if (game.info.nuke_pop_loss_pct > 0) {
2989 /* TRANS: percentage */
2990 _(" %s %d%% of the population of each city inside"
2991 " the nuclear blast dies.\n"), BULLET,
2993 if (game.info.nuke_pop_loss_pct < 50) {
2995 _(" %s can never destroy city completely "
2996 "(%d%% of size 1 rounds down to 0).\n"), BULLET,
2998 } else {
3000 _(" %s can even destroy city completely "
3001 "(%d%% of size 1 rounds up to 1).\n"), BULLET,
3003 }
3004 }
3007 _(" %s all units caught in the open by the nuclear"
3008 " blast die.\n"), BULLET);
3010 /* TRANS: percentage */
3011 _(" %s a unit caught in the nuclear blast while"
3012 " inside a city has a %d%% chance of survival.\n"),
3013 BULLET,
3015 } else {
3017 _(" %s all units caught in the nuclear blast"
3018 " die.\n"), BULLET);
3019 }
3020 {
3021 struct universal req_pattern[] = {
3022 { .kind = VUT_ACTION, .value.action = paction },
3023 { .kind = VUT_UTYPE, .value.utype = utype },
3024 };
3025
3026 int blast_radius_1 =
3030
3032 _(" %s has a squared blast radius of %d.\n"),
3034 }
3035
3036 break;
3037 case ACTRES_PLANT:
3038 case ACTRES_CULTIVATE:
3041 _(" %s converts target tile terrain to another"
3042 " type.\n"), BULLET);
3043 break;
3044 case ACTRES_ROAD:
3045 case ACTRES_MINE:
3046 case ACTRES_IRRIGATE:
3047 case ACTRES_BASE:
3048 {
3050 struct strvec *extras_vec = strvec_new();
3051
3052 extra_type_iterate(pextra) {
3053 if (actres_creates_extra(paction->result, pextra)) {
3055 }
3057
3058 if (strvec_size(extras_vec) > 0) {
3060 /* TRANS: %s is list of extra types separated by ',' and 'and' */
3061 cat_snprintf(buf, bufsz, _(" %s builds %s on tiles.\n"),
3064 }
3065
3067 }
3068 break;
3069 case ACTRES_CLEAN:
3070 {
3072 struct strvec *extras_vec = strvec_new();
3073
3074 extra_type_iterate(pextra) {
3075 if (actres_removes_extra(paction->result, pextra)) {
3077 }
3079
3080 if (strvec_size(extras_vec) > 0) {
3082 /* TRANS: list of extras separated by "and" */
3083 cat_snprintf(buf, bufsz, _(" %s cleans %s from tiles.\n"),
3086 }
3087
3089 }
3090 break;
3091 case ACTRES_PILLAGE:
3092 {
3094 struct strvec *extras_vec = strvec_new();
3095
3096 extra_type_iterate(pextra) {
3097 if (actres_removes_extra(paction->result, pextra)) {
3099 }
3101
3102 if (strvec_size(extras_vec) > 0) {
3104 /* TRANS: list of extras separated by "and" */
3105 cat_snprintf(buf, bufsz, _(" %s pillages %s from tiles.\n"),
3108 }
3109
3111 }
3112 break;
3113 case ACTRES_FORTIFY:
3114 {
3115 struct universal unit_is_fortified[] = {
3116 { .kind = VUT_ACTIVITY,
3117 .value = { .activity = ACTIVITY_FORTIFIED }},
3118 { .kind = VUT_UTYPE, .value = { .utype = utype }},
3119 };
3120 int bonus = effect_value_from_universals(
3123
3124 if (utype->defense_strength <= 0
3126 &(struct universal){
3127 .kind = VUT_UTYPE,
3128 .value = { .utype = utype }},
3129 1)
3130 <= 0)) {
3132 /* TRANS: indented unit action property, preserve
3133 * leading spaces */
3134 _(" %s to stay put. No defensive bonus.\n"),
3135 BULLET);
3136 } else if (bonus > 0) {
3138 /* TRANS: indented unit action property, preserve
3139 * leading spaces */
3140 _(" %s granting a %d%% defensive bonus.\n"),
3141 BULLET, bonus);
3142 }
3143 }
3144 break;
3146 {
3147 const char *targets[extra_count()];
3148 int j = 0;
3149
3150 /* Extra being native one is a hard requirement
3151 * Not using unit class native_bases cache here.
3152 * Sometimes it's not initialized when we run this,
3153 * and as this is not performance critical, no point
3154 * in using it conditionally and having this only as
3155 * fallback implementation. */
3157 if (!is_native_extra_to_uclass(pextra, pclass)) {
3158 continue;
3159 }
3160
3161 if (!territory_claiming_base(pextra->data.base)) {
3162 continue;
3163 }
3164
3165 targets[j++] = extra_name_translation(pextra);
3167
3168 if (j > 0) {
3169 struct astring list = ASTRING_INIT;
3170 /* TRANS: indented unit action property, preserve
3171 * leading spaces.
3172 * %s is a list of extra types separated by "and". */
3173 cat_snprintf(buf, bufsz, _(" %s done to %s.\n"),
3174 BULLET,
3175 astr_build_and_list(&list, targets, j));
3176 astr_free(&list);
3177 }
3178 }
3179 break;
3180 default:
3181 /* No action specific details. */
3182 break;
3183 }
3184
3185 /* Custom action sub result specific information. */
3186 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_ENTER)) {
3188 /* TRANS: indented unit action property, preserve
3189 * leading spaces. */
3190 _(" %s if a suitable hut is at the targetet tile it"
3191 " will be entered.\n"), BULLET);
3192 }
3193 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_FRIGHTEN)) {
3195 /* TRANS: indented unit action property, preserve
3196 * leading spaces. */
3197 _(" %s if a suitable hut is at the targetet tile it"
3198 " will be frightened.\n"), BULLET);
3199 }
3200 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_MAY_EMBARK)) {
3202 /* TRANS: indented unit action property, preserve
3203 * leading spaces.
3204 * The %s is the unit type name */
3205 _(" %s the %s may end up loaded into a transport if it"
3206 " can't survive on its own at the target tile.\n"),
3208 }
3209 if (BV_ISSET(paction->sub_results, ACT_SUB_RES_NON_LETHAL)) {
3211 /* TRANS: talking about non lethal attacks */
3212 _(" %s These attacks will only damage (never kill)"
3213 " defenders.\n"), BULLET);
3214 }
3215
3216 i = 0;
3218 const struct action *blocker = action_by_number(blocker_id);
3219
3220 if (!utype_can_do_action(utype, blocker->id)) {
3221 /* Can't block since never legal. */
3222 continue;
3223 }
3224
3225 if (action_would_be_blocked_by(paction, blocker)) {
3226 /* action name alone can be MAX_LEN_NAME, leave space for extra
3227 * characters */
3228 int maxlen = MAX_LEN_NAME + 16;
3229 char *quoted = fc_malloc(maxlen);
3230
3232 /* TRANS: %s is an action that can block another. */
3233 _("\'%s\'"), action_name_translation(blocker));
3234 blockers[i] = quoted;
3235
3236 i++;
3237 }
3239
3240 if (i > 0) {
3241 struct astring blist = ASTRING_INIT;
3242
3244 /* TRANS: %s is a list of actions separated by "or". */
3245 _(" %s can't be done if %s is legal.\n"), BULLET,
3247
3248 astr_free(&blist);
3249
3250 for (; i > 0; i--) {
3251 /* The text was copied above. */
3252 free((char *)(blockers[i - 1]));
3253 }
3254 }
3255 }
3257 action_iterate(act) {
3258 struct action *paction = action_by_number(act);
3259 bool vulnerable;
3260
3261 if (action_by_number(act)->quiet) {
3262 /* The ruleset documents this action it self. */
3263 continue;
3264 }
3265
3266 /* Not relevant */
3270 continue;
3271 }
3272
3273 /* All units are immune to this since its not enabled */
3274 if (!action_is_in_use(paction)) {
3275 continue;
3276 }
3277
3278 /* Must be immune in all cases */
3279 vulnerable = FALSE;
3282 &(enabler->target_reqs))) {
3283 vulnerable = TRUE;
3284 break;
3285 }
3287
3288 if (!vulnerable) {
3290 _("%s Doing the action \'%s\' to this unit"
3291 " is impossible.\n"), BULLET,
3293 }
3295 if (!has_vet_levels) {
3296 /* Only mention this if the game generally does have veteran levels. */
3297 if (game.veteran->levels > 1) {
3298 CATLSTR(buf, bufsz, _("%s Will never achieve veteran status.\n"), BULLET);
3299 }
3300 } else {
3301 /* Not useful currently: */
3302#if 0
3303 /* Some units can never become veteran through combat in practice. */
3306 && utype->defense_strength == 0);
3307#endif
3308 /* FIXME: if we knew the raise chances on the client, we could be
3309 * more specific here about whether veteran status can be acquired
3310 * through combat/missions/work. */
3311 CATLSTR(buf, bufsz, _("%s May acquire veteran status.\n"), BULLET);
3312 if (utype_veteran_has_power_bonus(utype)) {
3314 || utype->defense_strength > 0) {
3315 CATLSTR(buf, bufsz,
3316 /* TRANS: indented; preserve leading spaces */
3317 _(" %s Veterans have increased strength in combat.\n"),
3318 BULLET);
3319 }
3320 /* SUPERSPY always wins/escapes */
3321 if (utype_has_flag(utype, UTYF_DIPLOMAT)
3322 && !utype_has_flag(utype, UTYF_SUPERSPY)) {
3323 CATLSTR(buf, bufsz,
3324 /* TRANS: indented; preserve leading spaces */
3325 _(" %s Veterans have improved chances in diplomatic "
3326 "contests.\n"), BULLET);
3327 if (utype_may_do_escape_action(utype)) {
3328 CATLSTR(buf, bufsz,
3329 /* TRANS: indented; preserve leading spaces */
3330 _(" %s Veterans are more likely to survive missions.\n"),
3331 BULLET);
3332 }
3333 }
3334 if (utype_has_flag(utype, UTYF_WORKERS)) {
3335 CATLSTR(buf, bufsz,
3336 /* TRANS: indented; preserve leading spaces */
3337 _(" %s Veterans work faster.\n"), BULLET);
3338 }
3339 }
3340 }
3341 if (strlen(buf) > 0) {
3342 CATLSTR(buf, bufsz, "\n");
3343 }
3344 if (has_vet_levels && utype->veteran) {
3345 /* The case where the unit has only a single veteran level has already
3346 * been handled above, so keep quiet here if that happens */
3347 if (insert_veteran_help(buf, bufsz, utype->veteran,
3348 _("This type of unit has its own veteran levels:"), NULL)) {
3349 CATLSTR(buf, bufsz, "\n\n");
3350 }
3351 }
3352 if (NULL != utype->helptext) {
3353 strvec_iterate(utype->helptext, text) {
3354 CATLSTR(buf, bufsz, "%s\n\n", _(text));
3356 }
3357 CATLSTR(buf, bufsz, "%s", user_text);
3358
3359 return buf;
3360}
3361
3362/************************************************************************/
3367void helptext_advance(char *buf, size_t bufsz, struct player *pplayer,
3368 const char *user_text, int i)
3369{
3370 struct astring astr = ASTRING_INIT;
3372 struct universal source = {
3373 .kind = VUT_ADVANCE,
3374 .value = {.advance = vap}
3375 };
3376 int flagid;
3377
3378 fc_assert_ret(NULL != buf && 0 < bufsz && NULL != user_text);
3380
3381 if (NULL == vap) {
3382 log_error("Unknown tech %d.", i);
3383 return;
3384 }
3385
3386 if (game.control.num_tech_classes > 0) {
3387 if (vap->tclass == NULL) {
3388 cat_snprintf(buf, bufsz, _("Belongs to the default tech class.\n\n"));
3389 } else {
3390 cat_snprintf(buf, bufsz, _("Belongs to tech class %s.\n\n"),
3392 }
3393 }
3394
3395 if (NULL != pplayer) {
3396 const struct research *presearch = research_get(pplayer);
3397
3401
3403 PL_("Starting now, researching %s would need %d bulb.",
3404 "Starting now, researching %s would need %d bulbs.",
3405 bulbs),
3408 /* Split string into two to allow localization of two pluralizations. */
3409 char buf2[MAX_LEN_MSG];
3411
3413 /* TRANS: appended to another sentence. Preserve the
3414 * leading space. */
3415 PL_(" The whole project will require %d bulb to complete.",
3416 " The whole project will require %d bulbs to complete.",
3417 bulbs),
3418 bulbs);
3420 /* TRANS: last %s is a sentence pluralized separately. */
3421 PL_("To research %s you need to research %d other"
3422 " technology first.%s",
3423 "To research %s you need to research %d other"
3424 " technologies first.%s",
3428 } else {
3429 CATLSTR(buf, bufsz,
3430 _("You cannot research this technology."));
3431 }
3434 CATLSTR(buf, bufsz,
3435 /* TRANS: preserve leading space */
3436 _(" This number may vary depending on what "
3437 "other players research.\n"));
3438 } else {
3439 CATLSTR(buf, bufsz, "\n");
3440 }
3441 }
3442
3443 CATLSTR(buf, bufsz, "\n");
3444 }
3445
3446 if (requirement_vector_size(&vap->research_reqs) > 0) {
3447 CATLSTR(buf, bufsz, _("Requirements to research:\n"));
3448 requirement_vector_iterate(&vap->research_reqs, preq) {
3449 (void) req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "");
3451 CATLSTR(buf, bufsz, "\n");
3452 }
3453
3455 BULLET_SPACE);
3456
3457 {
3458 int j;
3459
3460 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
3461 if (game.rgame.global_init_techs[j] == A_LAST) {
3462 break;
3463 } else if (game.rgame.global_init_techs[j] == i) {
3464 CATLSTR(buf, bufsz,
3465 _("%s All players start the game with knowledge of this "
3466 "technology.\n"), BULLET);
3467 break;
3468 }
3469 }
3470 }
3471
3472 /* Assume no-one will set the same tech in both global and nation
3473 * init_tech... */
3474 nations_iterate(pnation) {
3475 int j;
3476
3477 /* Avoid mentioning nations not in current set. */
3478 if (!show_help_for_nation(pnation)) {
3479 continue;
3480 }
3481 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
3482 if (pnation->init_techs[j] == A_LAST) {
3483 break;
3484 } else if (pnation->init_techs[j] == i) {
3486 /* TRANS: %s is a nation plural */
3487 _("%s The %s start the game with knowledge of this "
3488 "technology.\n"), BULLET,
3489 nation_plural_translation(pnation));
3490 break;
3491 }
3492 }
3494
3495 /* Explain the effects of root_reqs. */
3496 {
3498
3502 if (proot == vap) {
3503 /* Don't say anything at all if this tech is a self-root-req one;
3504 * assume that the ruleset help will explain how to get it. */
3506 break;
3507 }
3510 /* Now find out what roots each of this tech's root_req has, so that
3511 * we can suppress them. If tech A has roots B/C, and B has root C,
3512 * it's not worth saying that A needs C, and can lead to overwhelming
3513 * lists. */
3514 /* (Special case: don't do this if the root is a self-root-req tech,
3515 * since it would appear in its own root iteration; in the scenario
3516 * where S is a self-root tech that is root for T, this would prevent
3517 * S appearing in T's help.) */
3518 /* FIXME this is quite inefficient */
3522 }
3524
3525 /* Filter out all but the direct root reqs. */
3527
3528 if (BV_ISSET_ANY(roots)) {
3529 const char *root_techs[A_LAST];
3530 size_t n_roots = 0;
3532
3534 if (BV_ISSET(roots, root)) {
3537 }
3539 fc_assert(n_roots > 0);
3541 /* TRANS: 'and'-separated list of techs */
3542 _("%s Only those who know %s can acquire this "
3543 "technology (by any means).\n"),
3544 BULLET,
3547 }
3548 }
3549
3552 _("%s The first player to learn %s gets"
3553 " an immediate advance.\n"), BULLET,
3555 }
3556
3558 if (advance_has_flag(i, flagid)) {
3559 const char *helptxt = tech_flag_helptxt(flagid);
3560
3561 if (helptxt != NULL) {
3562 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
3563 }
3564 }
3565 }
3566
3568 CATLSTR(buf, bufsz,
3569 _("%s To preserve this technology for our nation some bulbs "
3570 "are needed each turn.\n"), BULLET);
3571 }
3572
3573 if (NULL != vap->helptext) {
3574 if (strlen(buf) > 0) {
3575 CATLSTR(buf, bufsz, "\n");
3576 }
3577 strvec_iterate(vap->helptext, text) {
3578 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3580 }
3581
3582 astr_free(&astr);
3583}
3584
3585/************************************************************************/
3588void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer,
3589 const char *user_text, struct terrain *pterrain)
3590{
3591 struct universal source = {
3592 .kind = VUT_TERRAIN,
3593 .value = {.terrain = pterrain}
3594 };
3595 int flagid;
3596
3597 fc_assert_ret(NULL != buf && 0 < bufsz);
3598 buf[0] = '\0';
3599
3600 if (!pterrain) {
3601 log_error("Unknown terrain!");
3602 return;
3603 }
3604
3606 BULLET_SPACE);
3607 if (terrain_has_flag(pterrain, TER_NO_CITIES)) {
3608 CATLSTR(buf, bufsz,
3609 _("%s You cannot build cities on this terrain.\n"),
3610 BULLET);
3611 }
3613 /* Can't build roads; only mention if ruleset has buildable roads */
3615 if (pextra->buildable) {
3616 CATLSTR(buf, bufsz,
3617 _("%s Paths cannot be built on this terrain.\n"),
3618 BULLET);
3619 break;
3620 }
3622 }
3624 /* Can't build bases; only mention if ruleset has buildable bases */
3626 if (pextra->buildable) {
3627 CATLSTR(buf, bufsz,
3628 _("%s Bases cannot be built on this terrain.\n"),
3629 BULLET);
3630 break;
3631 }
3633 }
3634 if (terrain_has_flag(pterrain, TER_UNSAFE_COAST)
3635 && terrain_type_terrain_class(pterrain) != TC_OCEAN) {
3636 CATLSTR(buf, bufsz,
3637 _("%s The coastline of this terrain is unsafe.\n"),
3638 BULLET);
3639 }
3640 {
3641 const char *classes[uclass_count()];
3642 int i = 0;
3643
3644 unit_class_iterate(uclass) {
3645 if (is_native_to_class(uclass, pterrain, NULL)) {
3646 classes[i++] = uclass_name_translation(uclass);
3647 }
3649
3650 if (0 < i) {
3651 struct astring list = ASTRING_INIT;
3652
3653 /* TRANS: %s is a list of unit classes separated by "and". */
3654 cat_snprintf(buf, bufsz, _("%s Can be traveled by %s units.\n"),
3656 astr_free(&list);
3657 }
3658 }
3659 if (terrain_has_flag(pterrain, TER_NO_ZOC)) {
3660 CATLSTR(buf, bufsz,
3661 _("%s Units on this terrain neither impose zones of control "
3662 "nor are restricted by them.\n"), BULLET);
3663 } else {
3664 CATLSTR(buf, bufsz,
3665 _("%s Units on this terrain may impose a zone of control, or "
3666 "be restricted by one.\n"), BULLET);
3667 }
3668 for (flagid = TER_USER_1 ; flagid <= TER_USER_LAST; flagid++) {
3669 if (terrain_has_flag(pterrain, flagid)) {
3670 const char *helptxt = terrain_flag_helptxt(flagid);
3671
3672 if (helptxt != NULL) {
3673 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
3674 }
3675 }
3676 }
3677
3678 if (NULL != pterrain->helptext) {
3679 if (buf[0] != '\0') {
3680 CATLSTR(buf, bufsz, "\n");
3681 }
3682 strvec_iterate(pterrain->helptext, text) {
3683 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3685 }
3686 if (user_text && user_text[0] != '\0') {
3687 CATLSTR(buf, bufsz, "\n\n%s", user_text);
3688 }
3689}
3690
3691/************************************************************************/
3698const char *helptext_road_bonus_str(const struct terrain *pterrain,
3699 const struct road_type *proad)
3700{
3701 static char str[64];
3702 bool has_effect = FALSE;
3703
3704 str[0] = '\0';
3706 switch (o) {
3707 case O_FOOD:
3708 case O_SHIELD:
3709 case O_TRADE:
3710 {
3711 int bonus = proad->tile_bonus[o];
3712 int incr = proad->tile_incr_const[o];
3713
3714 if (pterrain) {
3715 incr +=
3716 proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
3717 }
3718 if (str[0] != '\0') {
3719 CATLSTR(str, sizeof(str), "/");
3720 }
3721 if (incr == 0 && bonus == 0) {
3722 cat_snprintf(str, sizeof(str), "%d", incr);
3723 } else {
3724 has_effect = TRUE;
3725 if (incr != 0) {
3726 cat_snprintf(str, sizeof(str), "%+d", incr);
3727 }
3728 if (bonus != 0) {
3729 cat_snprintf(str, sizeof(str), "%+d%%", bonus);
3730 }
3731 }
3732 }
3733 break;
3734 default:
3735 /* FIXME: there's nothing actually stopping roads having gold, etc
3736 * bonuses */
3737 fc_assert(proad->tile_incr_const[o] == 0
3738 && proad->tile_incr[o] == 0
3739 && proad->tile_bonus[o] == 0);
3740 break;
3741 }
3743
3744 return has_effect ? str : NULL;
3745}
3746
3747/**********************************************************************/
3753static void extra_bonus_for_terrain(struct extra_type *pextra,
3754 struct terrain *pterrain,
3755 int *bonus)
3756{
3757 struct universal req_pattern[] = {
3758 { .kind = VUT_EXTRA, .value.extra = pextra },
3759 { .kind = VUT_TERRAIN, .value.terrain = pterrain },
3760 { .kind = VUT_OTYPE /* value filled in later */ }
3761 };
3762
3763 fc_assert_ret(bonus != NULL);
3764
3765 /* Irrigation-like food bonuses */
3766 bonus[0] = (pterrain->irrigation_food_incr
3768 2 /* just extra+terrain */)) / 100;
3769
3770 /* Mining-like shield bonuses */
3771 bonus[1] = (pterrain->mining_shield_incr
3773 2 /* just extra+terrain */)) / 100;
3774
3775 bonus[2] = 0; /* no trade bonuses so far */
3776
3777 /* Now add fixed bonuses from roads (but not percentage bonus) */
3778 if (extra_road_get(pextra)) {
3779 const struct road_type *proad = extra_road_get(pextra);
3780
3782 switch (o) {
3783 case O_FOOD:
3784 case O_SHIELD:
3785 case O_TRADE:
3786 bonus[o] += proad->tile_incr_const[o]
3787 + proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
3788 break;
3789 default:
3790 /* not dealing with other output types here */
3791 break;
3792 }
3794 }
3795
3796 /* Fixed bonuses for extra, possibly unrelated to terrain type */
3797
3799 /* Fill in rest of requirement template */
3800 req_pattern[2].value.outputtype = o;
3801 switch (o) {
3802 case O_FOOD:
3803 case O_SHIELD:
3804 case O_TRADE:
3808 /* Any of the above bonuses is sufficient to trigger
3809 * Output_Inc_Tile, if underlying terrain does not */
3810 if (bonus[o] > 0 || pterrain->output[o] > 0) {
3814 }
3815 break;
3816 default:
3817 break;
3818 }
3820}
3821
3822/**********************************************************************/
3829 struct terrain *pterrain,
3830 enum unit_activity act)
3831{
3832 static char buffer[256];
3833 int btime;
3834 int bonus[3];
3835
3836 btime = terrain_extra_build_time(pterrain, act, pextra);
3837 fc_snprintf(buffer, sizeof(buffer), PL_("%d turn", "%d turns", btime),
3838 btime);
3839 extra_bonus_for_terrain(pextra, pterrain, bonus);
3840 if (bonus[0] > 0) {
3841 cat_snprintf(buffer, sizeof(buffer),
3842 PL_(", +%d food", ", +%d food", bonus[0]), bonus[0]);
3843 }
3844 if (bonus[1] > 0) {
3845 cat_snprintf(buffer, sizeof(buffer),
3846 PL_(", +%d shield", ", +%d shields", bonus[1]), bonus[1]);
3847 }
3848 if (bonus[2] > 0) {
3849 cat_snprintf(buffer, sizeof(buffer),
3850 PL_(", +%d trade", ", +%d trade", bonus[2]), bonus[2]);
3851 }
3852
3853 return buffer;
3854}
3855
3856/************************************************************************/
3862void helptext_extra(char *buf, size_t bufsz, struct player *pplayer,
3863 const char *user_text, struct extra_type *pextra)
3864{
3865 size_t group_start;
3866 struct base_type *pbase;
3867 struct road_type *proad;
3868 struct universal source = {
3869 .kind = VUT_EXTRA,
3870 .value = {.extra = pextra}
3871 };
3872
3873 int flagid;
3874
3875 fc_assert_ret(NULL != buf && 0 < bufsz);
3876 buf[0] = '\0';
3877
3878 if (!pextra) {
3879 log_error("Unknown extra!");
3880 return;
3881 }
3882
3883 if (is_extra_caused_by(pextra, EC_BASE)) {
3884 pbase = pextra->data.base;
3885 } else {
3886 pbase = NULL;
3887 }
3888
3889 if (is_extra_caused_by(pextra, EC_ROAD)) {
3890 proad = pextra->data.road;
3891 } else {
3892 proad = NULL;
3893 }
3894
3895 if (pextra->helptext != NULL) {
3896 strvec_iterate(pextra->helptext, text) {
3897 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3899 }
3900
3901 /* Describe how extra is created and destroyed */
3902
3904
3905 if (pextra->buildable) {
3906 if (is_extra_caused_by(pextra, EC_IRRIGATION)) {
3907 CATLSTR(buf, bufsz,
3908 _("Build by issuing an \"irrigate\" order.\n"));
3909 }
3910 if (is_extra_caused_by(pextra, EC_MINE)) {
3911 CATLSTR(buf, bufsz,
3912 _("Build by issuing a \"mine\" order.\n"));
3913 }
3914 if (is_extra_caused_by(pextra, EC_ROAD)) {
3915 CATLSTR(buf, bufsz,
3916 _("Build by issuing a \"road\" order.\n"));
3917 }
3918 if (is_extra_caused_by(pextra, EC_BASE)) {
3919 fc_assert(pbase != NULL);
3920
3921 if (pbase->gui_type == BASE_GUI_OTHER) {
3923 _("Build by issuing a \"build base\" order.\n"));
3924 } else {
3925 const char *order = "";
3926
3927 switch (pbase->gui_type) {
3928 case BASE_GUI_FORTRESS:
3929 order = Q_(terrain_control.gui_type_base0);
3930 break;
3931 case BASE_GUI_AIRBASE:
3932 order = Q_(terrain_control.gui_type_base1);
3933 break;
3934 default:
3936 break;
3937 }
3939 /* TRANS: %s is a gui_type base string from a ruleset */
3940 _("Build by issuing a \"%s\" order.\n"), order);
3941 }
3942 }
3943 }
3944
3945 if (is_extra_caused_by(pextra, EC_POLLUTION)) {
3946 CATLSTR(buf, bufsz,
3947 _("May randomly appear around polluting city.\n"));
3948 }
3949
3950 if (is_extra_caused_by(pextra, EC_FALLOUT)) {
3951 CATLSTR(buf, bufsz,
3952 _("May randomly appear around nuclear blast.\n"));
3953 }
3954
3955 if (pextra->generated
3956 && (is_extra_caused_by(pextra, EC_HUT)
3958 || (proad != NULL && road_has_flag(proad, RF_RIVER)))) {
3959 CATLSTR(buf, bufsz,
3960 _("Placed by map generator.\n"));
3961 }
3962
3963 if (is_extra_removed_by(pextra, ERM_ENTER)) {
3964 CATLSTR(buf, bufsz,
3965 _("Can be explored by certain units.\n"));
3966 }
3967
3968 if (is_extra_caused_by(pextra, EC_APPEARANCE)) {
3969 CATLSTR(buf, bufsz,
3970 _("May appear spontaneously.\n"));
3971 }
3972
3973 if (requirement_vector_size(&pextra->reqs) > 0) {
3974 char reqsbuf[8192] = "";
3975 bool buildable = pextra->buildable
3977
3979 (void) req_text_insert_nl(reqsbuf, sizeof(reqsbuf), pplayer, preq,
3981 buildable ? BULLET_SPACE : "");
3983 if (reqsbuf[0] != '\0') {
3984 if (buildable) {
3985 CATLSTR(buf, bufsz, _("Requirements to build:\n"));
3986 }
3987 CATLSTR(buf, bufsz, "%s", reqsbuf);
3988 }
3989 }
3990
3991 if (pextra->infracost > 0) {
3992 cat_snprintf(buf, bufsz, _("Cost: %d\n"), pextra->infracost);
3993 }
3994
3995 if (buf[group_start] != '\0') {
3996 CATLSTR(buf, bufsz, "\n"); /* group separator */
3997 }
3998
4000
4001 if (is_extra_removed_by(pextra, ERM_PILLAGE)) {
4002 int pillage_time = -1;
4003
4004 if (pextra->removal_time != 0) {
4005 pillage_time = pextra->removal_time;
4006 } else {
4007 terrain_type_iterate(pterrain) {
4008 int terr_pillage_time = pterrain->pillage_time
4009 * pextra->removal_time_factor;
4010
4011 if (terr_pillage_time != 0) {
4012 if (pillage_time < 0) {
4013 pillage_time = terr_pillage_time;
4014 } else if (pillage_time != terr_pillage_time) {
4015 /* Give up */
4016 pillage_time = -1;
4017 break;
4018 }
4019 }
4021 }
4022 if (pillage_time < 0) {
4023 CATLSTR(buf, bufsz,
4024 _("Can be pillaged by units (time is terrain-dependent).\n"));
4025 } else if (pillage_time > 0) {
4027 PL_("Can be pillaged by units (takes %d turn).\n",
4028 "Can be pillaged by units (takes %d turns).\n",
4029 pillage_time), pillage_time);
4030 }
4031 }
4032 if (is_extra_removed_by(pextra, ERM_CLEAN)) {
4033 int clean_time = -1;
4034
4035 if (pextra->removal_time != 0) {
4036 clean_time = pextra->removal_time;
4037 } else {
4038 terrain_type_iterate(pterrain) {
4039 int terr_clean_time = -1;
4040 int rmtime = pterrain->extra_removal_times[extra_index(pextra)];
4041
4042 if (rmtime != 0) {
4044 }
4045
4046 if (clean_time < 0) {
4048 } else if (clean_time != terr_clean_time) {
4049 /* Give up */
4050 clean_time = -1;
4051 break;
4052 }
4054 }
4055
4056 if (clean_time < 0) {
4057 CATLSTR(buf, bufsz,
4058 _("Can be cleaned by units (time is terrain-dependent).\n"));
4059 } else if (clean_time > 0) {
4061 PL_("Can be cleaned by units (takes %d turn).\n",
4062 "Can be cleaned by units (takes %d turns).\n",
4064 }
4065 }
4066
4067 if (requirement_vector_size(&pextra->rmreqs) > 0) {
4068 char reqsbuf[8192] = "";
4069
4071 (void) req_text_insert_nl(reqsbuf, sizeof(reqsbuf), pplayer, preq,
4074 if (reqsbuf[0] != '\0') {
4075 CATLSTR(buf, bufsz, _("Requirements to remove:\n"));
4076 CATLSTR(buf, bufsz, "%s", reqsbuf);
4077 }
4078 }
4079
4080 if (buf[group_start] != '\0') {
4081 CATLSTR(buf, bufsz, "\n"); /* group separator */
4082 }
4083
4084 /* Describe what other elements are enabled by extra */
4085
4087
4089
4090 if (buf[group_start] != '\0') {
4091 CATLSTR(buf, bufsz, "\n"); /* group separator */
4092 }
4093
4094 /* Describe other properties of extras */
4095
4096 if (pextra->visibility_req != A_NONE) {
4097 char vrbuf[1024];
4098
4099 fc_snprintf(vrbuf, sizeof(vrbuf),
4100 _("%s Visible only if %s known.\n"), BULLET,
4102 CATLSTR(buf, bufsz, "%s", vrbuf);
4103 }
4104
4105 if (pextra->eus == EUS_HIDDEN) {
4106 CATLSTR(buf, bufsz,
4107 _("%s Units inside are hidden from non-allied players.\n"),
4108 BULLET);
4109 }
4110
4111 {
4112 const char *classes[uclass_count()];
4113 int i = 0;
4114
4115 unit_class_iterate(uclass) {
4116 if (is_native_extra_to_uclass(pextra, uclass)) {
4117 classes[i++] = uclass_name_translation(uclass);
4118 }
4120
4121 if (0 < i) {
4122 struct astring list = ASTRING_INIT;
4123
4124 if (proad != NULL) {
4125 /* TRANS: %s is a list of unit classes separated by "and". */
4126 cat_snprintf(buf, bufsz, _("%s Can be traveled by %s units.\n"),
4127 BULLET,
4129 } else {
4130 /* TRANS: %s is a list of unit classes separated by "and". */
4131 cat_snprintf(buf, bufsz, _("%s Native to %s units.\n"),
4132 BULLET,
4134 }
4135 astr_free(&list);
4136
4137 if (extra_has_flag(pextra, EF_NATIVE_TILE)) {
4138 CATLSTR(buf, bufsz,
4139 /* TRANS: indented; preserve leading spaces */
4140 _(" %s Such units can move onto this tile even if it would "
4141 "not normally be suitable terrain.\n"), BULLET);
4142 }
4143
4144 if (pextra->no_aggr_near_city >= 0) {
4145 CATLSTR(buf, bufsz,
4146 /* TRANS: indented; preserve leading spaces */
4147 PL_(" %s Such units situated here are not considered aggressive "
4148 "if this tile is within %d tile of a friendly city.\n",
4149 " %s Such units situated here are not considered aggressive "
4150 "if this tile is within %d tiles of a friendly city.\n",
4151 pextra->no_aggr_near_city),
4152 BULLET, pextra->no_aggr_near_city);
4153 }
4154
4155 if (pextra->defense_bonus) {
4157 /* TRANS: indented; preserve leading spaces */
4158 _(" %s Such units get a %d%% defense bonus on this "
4159 "tile.\n"), BULLET,
4160 pextra->defense_bonus);
4161 }
4162 }
4163 }
4164
4166 const char *conquerors[utype_count()];
4167 int i = 0;
4168
4173 }
4175
4176 if (i > 0) {
4177 struct astring list = ASTRING_INIT;
4179 /* TRANS: %s is a list of unit types separated by "and". */
4180 _("%s Can be conquered by %s.\n"), BULLET,
4182 astr_free(&list);
4183 }
4184 }
4185
4187 if (proad->move_cost == 0) {
4188 CATLSTR(buf, bufsz, _("%s Allows infinite movement.\n"), BULLET);
4189 } else {
4191 /* TRANS: "MP" = movement points. Second %s may have a
4192 * fractional part. */
4193 _("%s Movement cost along %s is %s MP.\n"),
4194 BULLET,
4195 extra_name_translation(pextra),
4196 move_points_text(proad->move_cost, TRUE));
4197 }
4198 }
4199
4200 if (game.info.killstack
4201 && extra_has_flag(pextra, EF_NO_STACK_DEATH)) {
4202 CATLSTR(buf, bufsz,
4203 _("%s Defeat of one unit does not cause death of all other units "
4204 "on this tile.\n"), BULLET);
4205 }
4206 if (pbase != NULL) {
4208 CATLSTR(buf, bufsz,
4209 _("%s Extends national borders of the building nation.\n"),
4210 BULLET);
4211 }
4212 if (pbase->vision_main_sq >= 0) {
4213 CATLSTR(buf, bufsz,
4214 _("%s Grants permanent vision of an area around the tile to "
4215 "its owner.\n"), BULLET);
4216 }
4217 if (pbase->vision_invis_sq >= 0) {
4218 CATLSTR(buf, bufsz,
4219 _("%s Allows the owner to see normally invisible stealth units "
4220 "in an area around the tile.\n"), BULLET);
4221 }
4222 if (pbase->vision_subs_sq >= 0) {
4223 CATLSTR(buf, bufsz,
4224 _("%s Allows the owner to see normally invisible subsurface units "
4225 "in an area around the tile.\n"), BULLET);
4226 }
4227 }
4229 if (extra_has_flag(pextra, flagid)) {
4230 const char *helptxt = extra_flag_helptxt(flagid);
4231
4232 if (helptxt != NULL) {
4233 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
4234 }
4235 }
4236 }
4237
4238 /* Table of terrain-specific attributes, if needed */
4239 if (proad != NULL || pbase != NULL) {
4240 bool road, do_time, do_bonus;
4241
4242 road = (proad != NULL);
4243 /* Terrain-dependent build time? */
4244 do_time = pextra->buildable && pextra->build_time == 0;
4245 if (road) {
4246 /* Terrain-dependent output bonus? */
4247 do_bonus = FALSE;
4249 if (proad->tile_incr[o] > 0) {
4250 do_bonus = TRUE;
4251 fc_assert(o == O_FOOD || o == O_SHIELD || o == O_TRADE);
4252 }
4254 } else {
4255 /* Bases don't have output bonuses */
4256 do_bonus = FALSE;
4257 }
4258
4259 if (do_time || do_bonus) {
4260 if (do_time && do_bonus) {
4261 CATLSTR(buf, bufsz,
4262 _("\nTime to build and output bonus depends on terrain:\n\n"));
4263 CATLSTR(buf, bufsz,
4264 /* TRANS: Header for fixed-width road properties table.
4265 * TRANS: Translators cannot change column widths :( */
4266 _("Terrain Time Bonus F/P/T\n"
4267 "----------------------------------\n"));
4268 } else if (do_time) {
4269 CATLSTR(buf, bufsz,
4270 _("\nTime to build depends on terrain:\n\n"));
4271 CATLSTR(buf, bufsz,
4272 /* TRANS: Header for fixed-width extra properties table.
4273 * TRANS: Translators cannot change column widths :( */
4274 _("Terrain Time\n"
4275 "------------------\n"));
4276 } else {
4278 CATLSTR(buf, bufsz,
4279 /* TRANS: Header for fixed-width road properties table.
4280 * TRANS: Translators cannot change column widths :( */
4281 _("\nYields an output bonus with some terrains:\n\n"));
4282 CATLSTR(buf, bufsz,
4283 _("Terrain Bonus F/P/T\n"
4284 "-------------------------\n"));
4285 }
4287 int turns = road ? terrain_extra_build_time(t, ACTIVITY_GEN_ROAD, pextra)
4289 const char *bonus_text
4291 if (turns > 0 || bonus_text) {
4292 const char *terrain = terrain_name_translation(t);
4294
4296 "%s%*s ", terrain,
4297 MAX(0, slen),
4298 "");
4299 if (do_time) {
4300 if (turns > 0) {
4301 cat_snprintf(buf, bufsz, "%3d ", turns);
4302 } else {
4303 CATLSTR(buf, bufsz, " - ");
4304 }
4305 }
4306 if (do_bonus) {
4307 fc_assert(proad != NULL);
4308 cat_snprintf(buf, bufsz, " %s", bonus_text ? bonus_text : "-");
4309 }
4310 CATLSTR(buf, bufsz, "\n");
4311 }
4313 } /* else rely on client-specific display */
4314 }
4315
4316 if (user_text && user_text[0] != '\0') {
4317 CATLSTR(buf, bufsz, "\n\n%s", user_text);
4318 }
4319}
4320
4321/************************************************************************/
4327void helptext_goods(char *buf, size_t bufsz, struct player *pplayer,
4328 const char *user_text, struct goods_type *pgood)
4329{
4330 bool reqs = FALSE;
4331
4332 fc_assert_ret(NULL != buf && 0 < bufsz);
4333 buf[0] = '\0';
4334
4335 if (NULL != pgood->helptext) {
4336 strvec_iterate(pgood->helptext, text) {
4337 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4339 }
4340
4341 if (pgood->onetime_pct == 0) {
4343 _("There's no bonuses paid when trade route gets established.\n\n"));
4344 } else if (pgood->onetime_pct != 100) {
4346 _("When trade route gets established, %d%% of the normal bonus is paid.\n"),
4347 pgood->onetime_pct);
4348 }
4349 cat_snprintf(buf, bufsz, _("Sending city enjoys %d%% income from the route.\n"),
4350 pgood->from_pct);
4351 cat_snprintf(buf, bufsz, _("Receiving city enjoys %d%% income from the route.\n\n"),
4352 pgood->to_pct);
4353
4354 /* Requirements for this good. */
4356 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
4357 reqs = TRUE;
4358 }
4360 if (reqs) {
4361 fc_strlcat(buf, "\n", bufsz);
4362 }
4363
4364 CATLSTR(buf, bufsz, "%s", user_text);
4365}
4366
4367/************************************************************************/
4373void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer,
4374 const char *user_text, struct specialist *pspec)
4375{
4376 bool reqs = FALSE;
4377
4378 fc_assert_ret(NULL != buf && 0 < bufsz);
4379 buf[0] = '\0';
4380
4381 if (NULL != pspec->helptext) {
4382 strvec_iterate(pspec->helptext, text) {
4383 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4385 }
4388 _("Superspecialist: is not counted within city population,"
4389 "\ncan not be assigned to or from another occupation.\n"));
4390 }
4391
4392 /* Requirements for this specialist. */
4394 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
4395 reqs = TRUE;
4396 }
4398 if (reqs) {
4399 fc_strlcat(buf, "\n", bufsz);
4400 }
4401
4402 CATLSTR(buf, bufsz, "%s", user_text);
4403}
4404
4405/************************************************************************/
4413void helptext_government(char *buf, size_t bufsz, struct player *pplayer,
4414 const char *user_text, struct government *gov)
4415{
4416 int flagid;
4417 bool has_flags = FALSE;
4418 bool reqs = FALSE;
4419 struct universal source = {
4421 .value = {.govern = gov}
4422 };
4423
4424 fc_assert_ret(NULL != buf && 0 < bufsz);
4425 buf[0] = '\0';
4426
4427 if (NULL != gov->helptext) {
4428 strvec_iterate(gov->helptext, text) {
4429 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4431 }
4432
4434 if (government_has_flag(gov, flagid)) {
4435 const char *helptxt = gov_flag_helptxt(flagid);
4436
4437 if (helptxt != nullptr) {
4438 CATLSTR(buf, bufsz, "%s %s\n", BULLET, _(helptxt));
4439
4440 has_flags = TRUE;
4441 }
4442 }
4443 }
4444
4445 if (has_flags) {
4446 fc_strlcat(buf, "\n", bufsz);
4447 }
4448
4449 /* Add requirement text for government itself */
4451 if (req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "")) {
4452 reqs = TRUE;
4453 }
4455 if (reqs) {
4456 fc_strlcat(buf, "\n", bufsz);
4457 }
4458
4459 /* Effects */
4460 CATLSTR(buf, bufsz, _("Features:\n"));
4462 BULLET_SPACE);
4465 struct unit_class *unitclass = NULL;
4466 const struct unit_type *unittype = NULL;
4468 struct strvec *outputs = strvec_new();
4471 bool too_complex = FALSE;
4472 bool world_value_valid = TRUE;
4473
4474 /* Grab output type, if there is one */
4476 /* Treat an effect with any negated requirements as too complex for
4477 * us to explain here.
4478 * Also don't try to explain an effect with any requirements explicitly
4479 * marked as 'quiet' by ruleset author. */
4480 if (!preq->present || preq->quiet) {
4481 too_complex = TRUE;
4482 continue;
4483 }
4484 switch (preq->source.kind) {
4485 case VUT_OTYPE:
4486 /* We should never have multiple outputtype requirements
4487 * in one list in the first place (it simply makes no sense,
4488 * output cannot be of multiple types)
4489 * Ruleset loading code should check against that. */
4491 output_type = preq->source.value.outputtype;
4493 break;
4494 case VUT_UCLASS:
4496 unitclass = preq->source.value.uclass;
4497 /* FIXME: can't easily get world bonus for unit class */
4499 break;
4500 case VUT_UTYPE:
4501 fc_assert(unittype == NULL);
4502 unittype = preq->source.value.utype;
4503 break;
4504 case VUT_UTFLAG:
4505 if (!unit_type_flag_id_is_valid(unitflag)) {
4506 unitflag = preq->source.value.unitflag;
4507 /* FIXME: can't easily get world bonus for unit type flag */
4509 } else {
4510 /* Already have a unit flag requirement. More than one is too
4511 * complex for us to explain, so say nothing. */
4512 /* FIXME: we could handle this */
4513 too_complex = TRUE;
4514 }
4515 break;
4516 case VUT_GOVERNMENT:
4517 /* This is government we are generating helptext for.
4518 * ...or if not, it's ruleset bug that should never make it
4519 * this far. Fix ruleset loading code. */
4520 fc_assert(preq->source.value.govern == gov);
4521 break;
4522 default:
4523 too_complex = TRUE;
4525 break;
4526 };
4528
4529 if (!too_complex) {
4530 /* Only list effects that don't have extra requirements too complex
4531 * for us to handle.
4532 * Anything more complicated will have to be documented by hand by the
4533 * ruleset author. */
4534
4535 /* Guard condition for simple player-wide effects descriptions.
4536 * (FIXME: in many cases, e.g. EFT_MAKE_CONTENT, additional requirements
4537 * like unittype will be ignored for gameplay, but will affect our
4538 * help here.) */
4539 const bool playerwide
4540 = world_value_valid && !unittype && (output_type == O_LAST);
4541 /* In some cases we give absolute values (world bonus + gov bonus).
4542 * We assume the fact that there's an effect with a gov requirement
4543 * is sufficient reason to list it in that gov's help.
4544 * Guard accesses to these with 'playerwide' or 'world_value_valid'. */
4545 int world_value = -999, net_value = -999;
4546
4547 if (world_value_valid) {
4548 /* Get government-independent world value of effect if the extra
4549 * requirements were simple enough. */
4550 struct output_type *potype =
4552
4553 world_value =
4555 &(const struct req_context) {
4556 .unittype = unittype,
4557 .output = potype,
4558 },
4559 NULL,
4560 peffect->type);
4561 net_value = peffect->value + world_value;
4562 }
4563
4564 if (output_type == O_LAST) {
4565 /* There was no outputtype requirement. Effect is active for all
4566 * output types. Generate lists for that. */
4567 bool harvested_only = TRUE; /* Consider only output types from fields */
4568
4569 if (peffect->type == EFT_UPKEEP_PCT
4571 || peffect->type == EFT_OUTPUT_BONUS
4572 || peffect->type == EFT_OUTPUT_BONUS_2) {
4573 /* Effect can use or require any kind of output */
4575 }
4576
4578 struct output_type *pot = get_output_type(ot);
4579
4580 if (!harvested_only || pot->harvested) {
4581 strvec_append(outputs, _(pot->name));
4582 }
4584 }
4585
4586 if (0 == strvec_size(outputs)) {
4587 /* TRANS: Empty output type list, should never happen. */
4588 astr_set(&outputs_or, "%s", Q_("?outputlist: Nothing "));
4589 astr_set(&outputs_and, "%s", Q_("?outputlist: Nothing "));
4590 } else {
4593 }
4594
4595 switch (peffect->type) {
4596 case EFT_UNHAPPY_FACTOR:
4597 if (playerwide) {
4598 /* FIXME: EFT_MAKE_CONTENT_MIL_PER would cancel this out. We assume
4599 * no-one will set both, so we don't bother handling it. */
4601 PL_("%s Military units away from home and field units"
4602 " will each cause %d citizen to become unhappy.\n",
4603 "%s Military units away from home and field units"
4604 " will each cause %d citizens to become unhappy.\n",
4605 net_value),
4606 BULLET, net_value);
4607 } /* else too complicated or silly ruleset */
4608 break;
4610 if (playerwide && net_value != world_value) {
4611 if (world_value > 0) {
4612 if (net_value > 0) {
4614 _("%s Unhappiness from foreign citizens due to "
4615 "war with their home state is %d%% the usual "
4616 "value.\n"), BULLET,
4617 (net_value * 100) / world_value);
4618 } else {
4619 CATLSTR(buf, bufsz,
4620 _("%s No unhappiness from foreign citizens even when "
4621 "at war with their home state.\n"), BULLET);
4622 }
4623 } else {
4625 /* TRANS: not pluralised as gettext doesn't support
4626 * fractional numbers, which this might be */
4627 _("%s Each foreign citizen causes %.2g unhappiness "
4628 "in their city while you are at war with their "
4629 "home state.\n"), BULLET,
4630 (double)net_value / 100);
4631 }
4632 }
4633 break;
4635 if (playerwide) {
4637 PL_("%s Each of your cities will avoid %d unhappiness"
4638 " caused by units.\n",
4639 "%s Each of your cities will avoid %d unhappiness"
4640 " caused by units.\n",
4641 peffect->value),
4642 BULLET, peffect->value);
4643 }
4644 break;
4645 case EFT_MAKE_CONTENT:
4646 if (playerwide) {
4648 PL_("%s Each of your cities will avoid %d unhappiness,"
4649 " not including that caused by aggression.\n",
4650 "%s Each of your cities will avoid %d unhappiness,"
4651 " not including that caused by aggression.\n",
4652 peffect->value),
4653 BULLET, peffect->value);
4654 }
4655 break;
4656 case EFT_FORCE_CONTENT:
4657 if (playerwide) {
4659 PL_("%s Each of your cities will avoid %d unhappiness,"
4660 " including that caused by aggression.\n",
4661 "%s Each of your cities will avoid %d unhappiness,"
4662 " including that caused by aggression.\n",
4663 peffect->value),
4664 BULLET, peffect->value);
4665 }
4666 break;
4667 case EFT_UPKEEP_PCT:
4668 if (world_value_valid && !unittype) {
4669 if (net_value == 0) {
4670 if (output_type != O_LAST) {
4672 /* TRANS: %s is the output type, like 'shield'
4673 * or 'gold'. */
4674 _("%s You pay no %s upkeep for your units.\n"),
4676 } else {
4677 CATLSTR(buf, bufsz,
4678 _("%s You pay no upkeep for your units.\n"),
4679 BULLET);
4680 }
4681 } else if (net_value != world_value) {
4682 double ratio = (double)net_value / world_value;
4683
4684 if (output_type != O_LAST) {
4686 /* TRANS: %s is the output type, like 'shield'
4687 * or 'gold'. */
4688 _("%s You pay %.2g times normal %s upkeep for your "
4689 "units.\n"), BULLET,
4691 } else {
4693 _("%s You pay %.2g times normal upkeep for your "
4694 "units.\n"), BULLET,
4695 ratio);
4696 }
4697 } /* else this effect somehow has no effect; keep quiet */
4698 } /* else there was some extra condition making it complicated */
4699 break;
4701 if (!unittype) {
4702 if (output_type != O_LAST) {
4704 /* TRANS: %s is the output type, like 'shield' or
4705 * 'gold'; pluralised in %d but there is currently
4706 * no way to control the singular/plural name of the
4707 * output type; sorry */
4708 PL_("%s Each of your cities will avoid paying %d %s"
4709 " upkeep for your units.\n",
4710 "%s Each of your cities will avoid paying %d %s"
4711 " upkeep for your units.\n", peffect->value),
4712 BULLET,
4713 peffect->value, astr_str(&outputs_and));
4714 } else {
4716 /* TRANS: Amount is subtracted from upkeep cost
4717 * for each upkeep type. */
4718 PL_("%s Each of your cities will avoid paying %d"
4719 " upkeep for your units.\n",
4720 "%s Each of your cities will avoid paying %d"
4721 " upkeep for your units.\n", peffect->value),
4722 BULLET, peffect->value);
4723 }
4724 } /* else too complicated */
4725 break;
4727 if (playerwide) {
4729 _("%s If you lose your capital,"
4730 " the base chance of civil war is %d%%.\n"),
4731 BULLET, net_value);
4732 }
4733 break;
4735 if (playerwide) {
4737 PL_("%s You can have %d city before an "
4738 "additional unhappy citizen appears in each city "
4739 "due to civilization size.\n",
4740 "%s You can have up to %d cities before an "
4741 "additional unhappy citizen appears in each city "
4742 "due to civilization size.\n", net_value),
4743 BULLET, net_value);
4744 }
4745 break;
4747 if (playerwide) {
4749 PL_("%s After the first unhappy citizen due to"
4750 " civilization size, for each %d additional city"
4751 " another unhappy citizen will appear.\n",
4752 "%s After the first unhappy citizen due to"
4753 " civilization size, for each %d additional cities"
4754 " another unhappy citizen will appear.\n",
4755 net_value),
4756 BULLET, net_value);
4757 }
4758 break;
4759 case EFT_MAX_RATES:
4761 if (net_value < 100) {
4763 _("%s The maximum rate you can set for science,"
4764 " gold, or luxuries is %d%%.\n"),
4765 BULLET, net_value);
4766 } else {
4767 CATLSTR(buf, bufsz,
4768 _("%s Has unlimited science/gold/luxuries rates.\n"),
4769 BULLET);
4770 }
4771 }
4772 break;
4774 if (playerwide) {
4776 PL_("%s Your units may impose martial law."
4777 " Each military unit inside a city will force %d"
4778 " unhappy citizen to become content.\n",
4779 "%s Your units may impose martial law."
4780 " Each military unit inside a city will force %d"
4781 " unhappy citizens to become content.\n",
4782 peffect->value),
4783 BULLET, peffect->value);
4784 }
4785 break;
4787 if (playerwide && net_value < 100) {
4789 PL_("%s A maximum of %d unit in each city can enforce"
4790 " martial law.\n",
4791 "%s A maximum of %d units in each city can enforce"
4792 " martial law.\n",
4793 net_value),
4794 BULLET, net_value);
4795 }
4796 break;
4797 case EFT_RAPTURE_GROW:
4798 if (playerwide && net_value > 0) {
4800 _("%s You may grow your cities by means of "
4801 "celebrations."), BULLET);
4802 if (game.info.celebratesize > 1) {
4804 /* TRANS: Preserve leading space. %d should always be
4805 * 2 or greater. */
4806 _(" (Cities below size %d cannot grow in this way.)"),
4808 }
4809 cat_snprintf(buf, bufsz, "\n");
4810 }
4811 break;
4813 if (playerwide) {
4815 PL_("%s If a city is in disorder for more than %d turn "
4816 "in a row, government will fall into anarchy.\n",
4817 "%s If a city is in disorder for more than %d turns "
4818 "in a row, government will fall into anarchy.\n",
4819 net_value),
4820 BULLET, net_value);
4821 }
4822 break;
4823 case EFT_HAS_SENATE:
4824 if (playerwide && net_value > 0) {
4825 CATLSTR(buf, bufsz,
4826 _("%s Has a senate that may prevent declaration of war.\n"),
4827 BULLET);
4828 }
4829 break;
4831 if (playerwide && net_value > 0) {
4832 CATLSTR(buf, bufsz,
4833 _("%s Allows partisans when cities are taken by the "
4834 "enemy.\n"), BULLET);
4835 }
4836 break;
4838 if (playerwide && net_value > 0) {
4839 CATLSTR(buf, bufsz,
4840 _("%s Buildings that normally confer bonuses against"
4841 " unhappiness will instead give gold.\n"), BULLET);
4842 }
4843 break;
4844 case EFT_FANATICS:
4845 if (playerwide && net_value > 0) {
4846 struct strvec *fanatics = strvec_new();
4848
4852 }
4855 /* TRANS: %s is list of unit types separated by 'or' */
4856 _("%s Pays no upkeep for %s.\n"), BULLET,
4860 }
4861 break;
4862 case EFT_NO_UNHAPPY:
4863 if (playerwide && net_value > 0) {
4864 CATLSTR(buf, bufsz, _("%s Has no unhappy citizens.\n"), BULLET);
4865 }
4866 break;
4867 case EFT_VETERAN_BUILD:
4868 {
4869 int conditions = 0;
4870 if (unitclass) {
4871 conditions++;
4872 }
4873 if (unittype) {
4874 conditions++;
4875 }
4876 if (unit_type_flag_id_is_valid(unitflag)) {
4877 conditions++;
4878 }
4879 if (conditions > 1) {
4880 /* More than one requirement on units, too complicated for us
4881 * to describe. */
4882 break;
4883 }
4884 if (unitclass) {
4885 /* FIXME: account for multiple veteran levels, or negative
4886 * values. This might lie for complicated rulesets! */
4888 /* TRANS: %s is a unit class */
4889 Q_("?unitclass:* New %s units will be veteran.\n"),
4891 } else if (unit_type_flag_id_is_valid(unitflag)) {
4892 /* FIXME: same problems as unitclass */
4894 /* TRANS: %s is a (translatable) unit type flag */
4895 Q_("?unitflag:* New %s units will be veteran.\n"),
4897 } else if (unittype != NULL) {
4898 if (world_value_valid && net_value > 0) {
4899 /* Here we can be specific about veteran level, and get
4900 * net value correct. */
4901 int maxlvl = utype_veteran_system(unittype)->levels - 1;
4902 const struct veteran_level *vlevel =
4905 /* TRANS: "* New Partisan units will have the rank
4906 * of elite." */
4907 Q_("?unittype:%s New %s units will have the rank "
4908 "of %s.\n"), BULLET,
4909 utype_name_translation(unittype),
4911 } /* else complicated */
4912 } else {
4913 /* No extra criteria. */
4914 /* FIXME: same problems as above */
4916 _("%s New units will be veteran.\n"), BULLET);
4917 }
4918 }
4919 break;
4921 if (world_value_valid) {
4923 /* TRANS: %s is list of output types, with 'or';
4924 * pluralised in %d but of course the output types
4925 * can't be pluralised; sorry */
4926 PL_("%s Each worked tile that gives more than %d %s will"
4927 " suffer a -1 penalty, unless the city working it"
4928 " is celebrating.",
4929 "%s Each worked tile that gives more than %d %s will"
4930 " suffer a -1 penalty, unless the city working it"
4931 " is celebrating.", net_value),
4933 if (game.info.celebratesize > 1) {
4935 /* TRANS: Preserve leading space. %d should always be
4936 * 2 or greater. */
4937 _(" (Cities below size %d will not celebrate.)"),
4939 }
4940 cat_snprintf(buf, bufsz, "\n");
4941 }
4942 break;
4945 /* TRANS: %s is list of output types, with 'or' */
4946 PL_("%s Each worked tile with at least 1 %s will yield"
4947 " %d more of it while the city working it is"
4948 " celebrating.",
4949 "%s Each worked tile with at least 1 %s will yield"
4950 " %d more of it while the city working it is"
4951 " celebrating.", peffect->value),
4952 BULLET, astr_str(&outputs_or), peffect->value);
4953 if (game.info.celebratesize > 1) {
4955 /* TRANS: Preserve leading space. %d should always be
4956 * 2 or greater. */
4957 _(" (Cities below size %d will not celebrate.)"),
4959 }
4960 cat_snprintf(buf, bufsz, "\n");
4961 break;
4964 /* TRANS: %s is list of output types, with 'or' */
4965 PL_("%s Each worked tile with at least 1 %s will yield"
4966 " %d more of it.\n",
4967 "%s Each worked tile with at least 1 %s will yield"
4968 " %d more of it.\n", peffect->value),
4969 BULLET, astr_str(&outputs_or), peffect->value);
4970 break;
4971 case EFT_OUTPUT_BONUS:
4972 case EFT_OUTPUT_BONUS_2:
4973 /* FIXME: makes most sense iff world_value == 0 */
4975 /* TRANS: %s is list of output types, with 'and' */
4976 _("%s %s production is increased %d%%.\n"),
4977 BULLET, astr_str(&outputs_and), peffect->value);
4978 break;
4979 case EFT_OUTPUT_WASTE:
4980 if (world_value_valid) {
4981 if (net_value > 30) {
4983 /* TRANS: %s is list of output types, with 'and' */
4984 _("%s %s production will suffer massive losses.\n"),
4986 } else if (net_value >= 15) {
4988 /* TRANS: %s is list of output types, with 'and' */
4989 _("%s %s production will suffer some losses.\n"),
4991 } else if (net_value > 0) {
4993 /* TRANS: %s is list of output types, with 'and' */
4994 _("%s %s production will suffer a small amount "
4995 "of losses.\n"),
4997 }
4998 }
4999 break;
5000 case EFT_HEALTH_PCT:
5001 if (playerwide) {
5002 if (peffect->value > 0) {
5003 CATLSTR(buf, bufsz, _("%s Increases the chance of plague"
5004 " within your cities.\n"), BULLET);
5005 } else if (peffect->value < 0) {
5006 CATLSTR(buf, bufsz, _("%s Decreases the chance of plague"
5007 " within your cities.\n"), BULLET);
5008 }
5009 }
5010 break;
5012 /* Semi-arbitrary scaling to get likely ruleset values in roughly
5013 * the same range as WASTE_BY_DISTANCE */
5014 /* FIXME: use different wording? */
5015 net_value = (net_value + 39) / 40; /* round up */
5016 fc__fallthrough; /* fall through to: */
5018 if (world_value_valid) {
5019 if (net_value >= 300) {
5021 /* TRANS: %s is list of output types, with 'and' */
5022 _("%s %s losses will increase quickly"
5023 " with distance from capital.\n"),
5025 } else if (net_value >= 200) {
5027 /* TRANS: %s is list of output types, with 'and' */
5028 _("%s %s losses will increase"
5029 " with distance from capital.\n"),
5031 } else if (net_value > 0) {
5033 /* TRANS: %s is list of output types, with 'and' */
5034 _("%s %s losses will increase slowly"
5035 " with distance from capital.\n"),
5037 }
5038 }
5039 break;
5040 case EFT_MIGRATION_PCT:
5041 if (playerwide) {
5042 if (peffect->value > 0) {
5043 CATLSTR(buf, bufsz, _("%s Increases the chance of migration"
5044 " into your cities.\n"), BULLET);
5045 } else if (peffect->value < 0) {
5046 CATLSTR(buf, bufsz, _("%s Decreases the chance of migration"
5047 " into your cities.\n"), BULLET);
5048 }
5049 }
5050 break;
5051 case EFT_BORDER_VISION:
5053 && playerwide && net_value > 0) {
5054 CATLSTR(buf, bufsz, _("%s All tiles inside your borders are"
5055 " monitored.\n"), BULLET);
5056 }
5057 break;
5058 default:
5059 break;
5060 };
5061 }
5062
5066
5068
5069 /* Action immunity */
5070 action_iterate(act) {
5071 if (action_by_number(act)->quiet) {
5072 /* The ruleset documents this action it self. */
5073 continue;
5074 }
5075
5076 if (action_immune_government(gov, act)) {
5078 /* TRANS: action name ... action target
5079 * ("individual units", etc) */
5080 _("%s Makes it impossible to do the action \'%s\'"
5081 " to your %s.\n"), BULLET,
5084 }
5086
5087 if (user_text && user_text[0] != '\0') {
5088 cat_snprintf(buf, bufsz, "\n%s", user_text);
5089 }
5090}
5091
5092/************************************************************************/
5095char *helptext_unit_upkeep_str(const struct unit_type *utype)
5096{
5097 static char buf[128];
5098 int any = 0;
5099
5100 if (!utype) {
5101 log_error("Unknown unit!");
5102 return "";
5103 }
5104
5105 buf[0] = '\0';
5107 if (utype->upkeep[o] > 0) {
5108 /* TRANS: "2 Food" or ", 1 Shield" */
5109 cat_snprintf(buf, sizeof(buf), _("%s%d %s"),
5110 (any > 0 ? Q_("?blistmore:, ") : ""), utype->upkeep[o],
5112 any++;
5113 }
5115 if (utype->happy_cost > 0) {
5116 /* TRANS: "2 Unhappy" or ", 1 Unhappy" */
5117 cat_snprintf(buf, sizeof(buf), _("%s%d Unhappy"),
5118 (any > 0 ? Q_("?blistmore:, ") : ""), utype->happy_cost);
5119 any++;
5120 }
5121
5122 if (any == 0) {
5123 /* strcpy(buf, _("None")); */
5124 fc_snprintf(buf, sizeof(buf), "%d", 0);
5125 }
5126 return buf;
5127}
5128
5129/************************************************************************/
5132void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation,
5133 const char *user_text)
5134{
5135 struct universal source = {
5136 .kind = VUT_NATION,
5137 .value = {.nation = pnation}
5138 };
5139 bool print_break = TRUE;
5140
5141#define PRINT_BREAK() do { \
5142 if (print_break) { \
5143 if (buf[0] != '\0') { \
5144 CATLSTR(buf, bufsz, "\n\n"); \
5145 } \
5146 print_break = FALSE; \
5147 } \
5148 } while (FALSE)
5149
5150 fc_assert_ret(NULL != buf && 0 < bufsz);
5151 buf[0] = '\0';
5152
5153 if (pnation->legend[0] != '\0') {
5154 /* Client side legend is stored already translated */
5155 cat_snprintf(buf, bufsz, "%s", pnation->legend);
5156 }
5157
5158 if (pnation->init_government) {
5159 PRINT_BREAK();
5161 _("Initial government is %s.\n"),
5163 }
5164 if (pnation->init_techs[0] != A_LAST) {
5165 const char *tech_names[MAX_NUM_TECH_LIST];
5166 int i;
5167 struct astring list = ASTRING_INIT;
5168
5169 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
5170 if (pnation->init_techs[i] == A_LAST) {
5171 break;
5172 }
5173 tech_names[i] =
5175 }
5177 PRINT_BREAK();
5178 if (game.rgame.global_init_techs[0] != A_LAST) {
5180 /* TRANS: %s is an and-separated list of techs */
5181 _("Starts with knowledge of %s in addition to the standard "
5182 "starting technologies.\n"), astr_str(&list));
5183 } else {
5185 /* TRANS: %s is an and-separated list of techs */
5186 _("Starts with knowledge of %s.\n"), astr_str(&list));
5187 }
5188 astr_free(&list);
5189 }
5190 if (pnation->init_units[0]) {
5191 const struct unit_type *utypes[MAX_NUM_UNIT_LIST];
5192 int count[MAX_NUM_UNIT_LIST];
5193 int i, j, n = 0, total = 0;
5194
5195 /* Count how many of each type there is. */
5196 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
5197 if (!pnation->init_units[i]) {
5198 break;
5199 }
5200 for (j = 0; j < n; j++) {
5201 if (pnation->init_units[i] == utypes[j]) {
5202 count[j]++;
5203 total++;
5204 break;
5205 }
5206 }
5207 if (j == n) {
5208 utypes[n] = pnation->init_units[i];
5209 count[n] = 1;
5210 total++;
5211 n++;
5212 }
5213 }
5214 {
5215 /* Construct the list of unit types and counts. */
5217 struct astring list = ASTRING_INIT;
5218
5219 for (i = 0; i < n; i++) {
5221 if (count[i] > 1) {
5222 /* TRANS: a unit type followed by a count. For instance,
5223 * "Fighter (2)" means two Fighters. Count is never 1.
5224 * Used in a list. */
5225 astr_set(&utype_names[i], _("%s (%d)"),
5226 utype_name_translation(utypes[i]), count[i]);
5227 } else {
5229 }
5230 }
5231 {
5233
5234 for (i = 0; i < n; i++) {
5236 }
5238 }
5239 for (i = 0; i < n; i++) {
5241 }
5242 PRINT_BREAK();
5244 /* TRANS: %s is an and-separated list of unit types
5245 * possibly with counts. Plurality is in total number of
5246 * units represented. */
5247 PL_("Starts with the following additional unit: %s.\n",
5248 "Starts with the following additional units: %s.\n",
5249 total), astr_str(&list));
5250 astr_free(&list);
5251 }
5252 }
5253 if (pnation->init_buildings[0] != B_LAST) {
5254 const char *impr_names[MAX_NUM_BUILDING_LIST];
5255 int i;
5256 struct astring list = ASTRING_INIT;
5257
5258 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
5259 if (pnation->init_buildings[i] == B_LAST) {
5260 break;
5261 }
5262 impr_names[i] =
5265 }
5267 PRINT_BREAK();
5270 /* TRANS: %s is an and-separated list of improvements */
5271 _("First city will get %s for free in addition to the "
5272 "standard improvements.\n"), astr_str(&list));
5273 } else {
5275 /* TRANS: %s is an and-separated list of improvements */
5276 _("First city will get %s for free.\n"), astr_str(&list));
5277 }
5278 astr_free(&list);
5279 }
5280
5281 if (buf[0] != '\0') {
5282 CATLSTR(buf, bufsz, "\n");
5283 }
5285
5286 if (user_text && user_text[0] != '\0') {
5287 if (buf[0] != '\0') {
5288 CATLSTR(buf, bufsz, "\n");
5289 }
5290 CATLSTR(buf, bufsz, "%s", user_text);
5291 }
5292#undef PRINT_BREAK
5293}
5294
5295/************************************************************************/
5299{
5300 if (req == NULL) {
5301 return HELP_LAST;
5302 }
5303
5304 if (req->source.kind == VUT_UTYPE) {
5305 return HELP_UNIT;
5306 }
5307 if (req->source.kind == VUT_IMPROVEMENT) {
5309 return HELP_WONDER;
5310 }
5311 return HELP_IMPROVEMENT;
5312 }
5313 if (req->source.kind == VUT_ADVANCE) {
5314 return HELP_TECH;
5315 }
5316 if (req->source.kind == VUT_TERRAIN) {
5317 return HELP_TERRAIN;
5318 }
5319 if (req->source.kind == VUT_EXTRA) {
5320 return HELP_EXTRA;
5321 }
5322 if (req->source.kind == VUT_GOOD) {
5323 return HELP_GOODS;
5324 }
5325 if (req->source.kind == VUT_SPECIALIST) {
5326 return HELP_SPECIALIST;
5327 }
5328 if (req->source.kind == VUT_GOVERNMENT) {
5329 return HELP_GOVERNMENT;
5330 }
5331
5332 if (req->source.kind == VUT_NATION) {
5333 return HELP_NATIONS;
5334 }
5335
5336 return HELP_LAST;
5337}
enum action_actor_kind action_get_actor_kind(const struct action *paction)
Definition actions.c:1119
const char * action_id_name_translation(action_id act_id)
Definition actions.c:1271
void action_array_add_all_by_result(action_id *act_array, int *position, enum action_result result)
Definition actions.c:6021
const char * action_name_translation(const struct action *paction)
Definition actions.c:1251
void action_array_end(action_id *act_array, int size)
Definition actions.c:6004
bool action_is_in_use(struct action *paction)
Definition actions.c:5910
enum action_sub_target_kind action_get_sub_target_kind(const struct action *paction)
Definition actions.c:1140
bool action_would_be_blocked_by(const struct action *blocked, const struct action *blocker)
Definition actions.c:1217
bool action_immune_government(struct government *gov, action_id act)
Definition actions.c:5673
int action_dice_roll_initial_odds(const struct action *paction)
Definition actions.c:5593
const char * action_target_kind_help(enum action_target_kind kind)
Definition actions.c:7530
enum action_target_kind action_get_target_kind(const struct action *paction)
Definition actions.c:1129
struct action_enabler_list * action_enablers_for_action(action_id action)
Definition actions.c:1580
#define action_id_univs_not_blocking(act_id, act_uni, tgt_uni)
Definition actions.h:740
#define action_auto_perf_iterate_end
Definition actions.h:350
static struct action * action_by_number(action_id act_id)
Definition actions.h:400
#define action_array_iterate(_act_array_, _act_id_)
Definition actions.h:261
#define action_has_result(_act_, _res_)
Definition actions.h:184
#define action_enabler_list_iterate_end
Definition actions.h:194
#define action_id_get_role(act_id)
Definition actions.h:461
#define ACTION_DISTANCE_UNLIMITED
Definition actions.h:105
#define action_array_iterate_end
Definition actions.h:273
#define action_iterate_end
Definition actions.h:218
#define MAX_NUM_ACTIONS
Definition actions.h:62
#define action_enabler_list_iterate(action_enabler_list, aenabler)
Definition actions.h:192
#define action_iterate(_act_)
Definition actions.h:214
#define action_id_get_target_kind(act_id)
Definition actions.h:417
#define action_auto_perf_iterate(_act_perf_)
Definition actions.h:338
#define ACTION_ODDS_PCT_DICE_ROLL_NA
Definition actions.h:724
bool actres_removes_extra(enum action_result result, const struct extra_type *pextra)
Definition actres.c:811
bool actres_creates_extra(enum action_result result, const struct extra_type *pextra)
Definition actres.c:790
enum action_battle_kind actres_get_battle_kind(enum action_result result)
Definition actres.c:273
void astr_free(struct astring *astr)
Definition astring.c:148
const char * astr_build_or_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:313
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:251
const char * astr_build_and_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:351
void astr_init(struct astring *astr)
Definition astring.c:139
#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:163
#define BV_CLR_ALL_FROM(vec_to, vec_from)
Definition bitvector.h:135
#define BV_CLR_ALL(bv)
Definition bitvector.h:103
#define BV_SET(bv, bit)
Definition bitvector.h:89
#define BV_ARE_EQUAL(vec1, vec2)
Definition bitvector.h:121
#define BV_ISSET(bv, bit)
Definition bitvector.h:86
#define BV_ISSET_ANY(vec)
Definition bitvector.h:117
struct output_type * get_output_type(Output_type_id output)
Definition city.c:640
const char * get_output_name(Output_type_id output)
Definition city.c:630
#define output_type_iterate(output)
Definition city.h:851
#define output_type_iterate_end
Definition city.h:857
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:1514
static struct fc_sockaddr_list * list
Definition clinet.c:102
char * utypes
Definition comments.c:35
char * incite_cost
Definition comments.c:77
#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:158
struct counter * counter_by_id(int id)
Definition counters.c:82
static void road(QVariant data1, QVariant data2)
Definition dialogs.cpp:2956
struct @22::@23 reqs
bool effect_universals_value_never_below(enum effect_type type, struct universal *unis, size_t n_unis, int min_value)
Definition effects.c:545
int effect_value_from_universals(enum effect_type type, struct universal *unis, size_t n_unis)
Definition effects.c:459
int get_target_bonus_effects(struct effect_list *plist, const struct req_context *context, const struct req_context *other_context, enum effect_type effect_type)
Definition effects.c:744
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:639
#define effect_list_iterate_end
Definition effects.h:81
#define effect_list_iterate(effect_list, peffect)
Definition effects.h:79
const char * extra_flag_helptxt(enum extra_flag_id id)
Definition extras.c:988
bool is_extra_caused_by_worker_action(const struct extra_type *pextra)
Definition extras.c:1046
bool extra_has_flag(const struct extra_type *pextra, enum extra_flag_id flag)
Definition extras.c:875
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:857
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:237
int Unit_Class_id
Definition fc_types.h:277
int action_id
Definition fc_types.h:250
#define CASUS_BELLI_OUTRAGE
Definition fc_types.h:343
#define CASUS_BELLI_VICTIM
Definition fc_types.h:337
#define MAX_NUM_UNIT_LIST
Definition fc_types.h:45
#define MAX_LEN_NAME
Definition fc_types.h:68
#define MAX_NUM_TECH_LIST
Definition fc_types.h:44
@ O_SHIELD
Definition fc_types.h:103
@ O_FOOD
Definition fc_types.h:103
@ O_TRADE
Definition fc_types.h:103
@ O_LAST
Definition fc_types.h:103
@ BORDERS_ENABLED
Definition fc_types.h:733
enum output_type_id Output_type_id
Definition fc_types.h:239
size_t get_internal_string_length(const char *text)
Definition fciconv.c:396
#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
struct world wld
Definition game.c:63
const char * government_name_translation(const struct government *pgovern)
Definition government.c:151
bool government_has_flag(const struct government *pgov, enum gov_flag_id flag)
Definition government.c:595
const char * gov_flag_helptxt(enum gov_flag_id id)
Definition government.c:668
#define governments_iterate(NAME_pgov)
Definition government.h:127
#define GOVF_LAST_USER_FLAG
Definition government.h:28
#define governments_iterate_end
Definition government.h:130
static struct tile * pos
Definition finddlg.c:53
static GtkWidget * source
Definition gotodlg.c:58
const char * client_string
Definition gui_main.c:106
void insert_client_build_info(char *outbuf, size_t outlen)
Definition gui_main.c:2537
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:3753
void help_iter_start(void)
Definition helpdata.c:1398
static int help_item_compar(const struct help_item *const *ppa, const struct help_item *const *ppb)
Definition helpdata.c:746
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:768
static void insert_allows(struct universal *psource, char *buf, size_t bufsz, const char *prefix)
Definition helpdata.c:629
#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:4413
void helptext_advance(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, int i)
Definition helpdata.c:3367
enum help_page_type help_type_by_requirement(const struct requirement *req)
Definition helpdata.c:5298
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:1985
void helptext_extra(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct extra_type *pextra)
Definition helpdata.c:3862
void helptext_unitclass(struct unit_class *pclass, char *buf, size_t bufsz)
Definition helpdata.c:1926
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:1323
int num_help_items(void)
Definition helpdata.c:1312
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:4327
#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:3698
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:5095
#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:3828
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:543
#define help_list_iterate_end
Definition helpdata.c:83
static bool utype_may_do_escape_action(const struct unit_type *utype)
Definition helpdata.c:1890
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:4373
const struct help_item * get_help_item_spec(const char *name, enum help_page_type htype, int *pos)
Definition helpdata.c:1346
#define help_list_iterate(helplist, phelp)
Definition helpdata.c:81
const struct help_item * help_iter_next(void)
Definition helpdata.c:1408
char * helptext_building(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, const struct impr_type *pimprove)
Definition helpdata.c:1439
void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct terrain *pterrain)
Definition helpdata.c:3588
void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation, const char *user_text)
Definition helpdata.c:5132
static struct help_item * new_help_item(int type)
Definition helpdata.c:730
#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:192
#define log_verbose(message,...)
Definition log.h:110
#define fc_assert(condition)
Definition log.h:177
#define fc_assert_ret_val(condition, val)
Definition log.h:195
#define log_error(message,...)
Definition log.h:104
struct terrain_misc terrain_control
Definition map.c:68
#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:1066
const char * move_points_text_full(int mp, bool reduce, const char *prefix, const char *none, bool align)
Definition movement.c:999
bool is_native_to_class(const struct unit_class *punitclass, const struct terrain *pterrain, const bv_extras *extras)
Definition movement.c:373
bool can_unit_type_transport(const struct unit_type *transporter, const struct unit_class *transported)
Definition movement.c:919
bool can_attack_non_native(const struct unit_type *utype)
Definition movement.c:213
#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:364
#define nations_iterate(NAME_pnation)
Definition nation.h:361
int len
Definition packhand.c:128
const char * diplrel_name_translation(int value)
Definition player.c:1614
struct section_file * secfile_load(const char *filename, bool allow_duplicates)
Definition registry.c:51
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:4787
@ 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:753
bool research_invention_reachable(const struct research *presearch, const Tech_type_id tech)
Definition research.c:671
int research_goal_bulbs_required(const struct research *presearch, Tech_type_id goal)
Definition research.c:775
int research_total_bulbs_required(const struct research *presearch, Tech_type_id tech, bool loss_value)
Definition research.c:872
struct research * research_get(const struct player *pplayer)
Definition research.c:130
enum tech_state research_invention_state(const struct research *presearch, Tech_type_id tech)
Definition research.c:622
bool road_has_flag(const struct road_type *proad, enum road_flag_id flag)
Definition road.c:416
bool road_provides_move_bonus(const struct road_type *proad)
Definition road.c:505
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:363
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1094
const struct strvec * get_data_dirs(void)
Definition shared.c:886
#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:110
Specialist_type_id specialist_index(const struct specialist *sp)
Definition specialist.c:90
const char * specialist_plural_translation(const struct specialist *sp)
Definition specialist.c:166
bool is_super_specialist(const struct specialist *sp)
Definition specialist.c:208
#define specialist_type_iterate_end
Definition specialist.h:85
#define specialist_type_iterate(sp)
Definition specialist.h:79
#define DEFAULT_SPECIALIST
Definition specialist.h:43
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:111
bool quiet
Definition actions.h:134
struct civ_game::@32::@35 client
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
struct civ_game::@31 rgame
bool ruleset_init
Definition game.h:118
struct veteran_system * veteran
Definition game.h:101
int num_valid_dirs
Definition map_types.h:76
struct strvec * helptext
Definition extras.h:149
struct road_type * road
Definition extras.h:155
int removal_time
Definition extras.h:120
bool generated
Definition extras.h:117
struct extra_type::@26 data
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:68
struct requirement_vector reqs
Definition government.h:64
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:126
struct unit_type * init_units[MAX_NUM_UNIT_LIST]
Definition nation.h:127
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:157
int irrigation_food_incr
Definition terrain.h:114
int output[O_LAST]
Definition terrain.h:95
int road_output_incr_pct[O_LAST]
Definition terrain.h:104
int mining_shield_incr
Definition terrain.h:117
int transport_capacity
Definition unittype.h:530
int pop_cost
Definition unittype.h:518
struct requirement_vector build_reqs
Definition unittype.h:527
int defense_strength
Definition unittype.h:523
int paratroopers_range
Definition unittype.h:548
int convert_time
Definition unittype.h:538
int city_size
Definition unittype.h:557
struct veteran_system * veteran
Definition unittype.h:551
const struct unit_type * obsoleted_by
Definition unittype.h:536
bv_unit_classes targets
Definition unittype.h:568
enum vision_layer vlayer
Definition unittype.h:576
struct strvec * helptext
Definition unittype.h:578
int bombard_rate
Definition unittype.h:554
int upkeep[O_LAST]
Definition unittype.h:545
bv_unit_classes disembarks
Definition unittype.h:574
const struct unit_type * converted_to
Definition unittype.h:537
bv_unit_classes embarks
Definition unittype.h:571
int happy_cost
Definition unittype.h:544
struct combat_bonus_list * bonuses
Definition unittype.h:533
struct specialist * spec_type
Definition unittype.h:520
enum universals_n kind
Definition fc_types.h:595
universals_u value
Definition fc_types.h:594
struct veteran_level * definitions
Definition unittype.h:502
struct civ_map map
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:960
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:777
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:186
size_t fc_strlcat(char *dest, const char *src, size_t n)
Definition support.c:822
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:986
#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:110
bool advance_has_flag(Tech_type_id tech, enum tech_flag_id flag)
Definition tech.c:221
const char * tech_class_name_translation(const struct tech_class *ptclass)
Definition tech.c:348
struct advance * advance_requires(const struct advance *padvance, enum tech_req require)
Definition tech.c:139
const char * advance_name_translation(const struct advance *padvance)
Definition tech.c:305
struct advance * valid_advance_by_number(const Tech_type_id id)
Definition tech.c:181
bool techs_have_fixed_costs(void)
Definition tech.c:465
const char * tech_flag_helptxt(enum tech_flag_id id)
Definition tech.c:450
Tech_type_id advance_number(const struct advance *padvance)
Definition tech.c:100
#define advance_index_iterate_end
Definition tech.h:246
@ AR_ROOT
Definition tech.h:110
#define TECH_USER_LAST
Definition tech.h:103
#define A_FIRST
Definition tech.h:44
#define A_NONE
Definition tech.h:43
#define advance_root_req_iterate_end
Definition tech.h:317
#define A_LAST
Definition tech.h:45
#define advance_index_iterate(_start, _index)
Definition tech.h:242
#define advance_root_req_iterate(_goal, _padvance)
Definition tech.h:312
const char * terrain_name_translation(const struct terrain *pterrain)
Definition terrain.c:240
const char * terrain_rule_name(const struct terrain *pterrain)
Definition terrain.c:249
enum terrain_class terrain_type_terrain_class(const struct terrain *pterrain)
Definition terrain.c:585
const char * terrain_flag_helptxt(enum terrain_flag_id id)
Definition terrain.c:832
int terrain_extra_build_time(const struct terrain *pterrain, enum unit_activity activity, const struct extra_type *tgt)
Definition terrain.c:702
#define terrain_type_iterate(_p)
Definition terrain.h:267
#define T_NONE
Definition terrain.h:61
#define terrain_type_iterate_end
Definition terrain.h:273
#define terrain_has_flag(terr, flag)
Definition terrain.h:177
const char * tileset_description(struct tileset *t)
Definition tilespec.c:7809
const char * tileset_summary(struct tileset *t)
Definition tilespec.c:7801
const char * tileset_name_get(struct tileset *t)
Definition tilespec.c:7785
const char * tileset_version(struct tileset *t)
Definition tilespec.c:7793
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:529
const char * uclass_name_translation(const struct unit_class *pclass)
Definition unittype.c:1658
bool utype_action_takes_all_mp(const struct unit_type *putype, struct action *paction)
Definition unittype.c:1216
bool utype_can_freely_unload(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Definition unittype.c:325
const char * unit_class_flag_helptxt(enum unit_class_flag_id id)
Definition unittype.c:1881
Unit_Class_id uclass_count(void)
Definition unittype.c:2480
struct unit_type * get_role_unit(int role, int role_index)
Definition unittype.c:2284
int utype_build_shield_cost_base(const struct unit_type *punittype)
Definition unittype.c:1493
const struct veteran_system * utype_veteran_system(const struct unit_type *punittype)
Definition unittype.c:2604
int num_role_units(int role)
Definition unittype.c:2234
bool utype_can_freely_load(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Definition unittype.c:313
Unit_type_id utype_count(void)
Definition unittype.c:82
int utype_veteran_levels(const struct unit_type *punittype)
Definition unittype.c:2662
bool utype_can_do_action_result(const struct unit_type *putype, enum action_result result)
Definition unittype.c:412
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:1232
const struct veteran_level * utype_veteran_level(const struct unit_type *punittype, int level)
Definition unittype.c:2634
bool utype_is_consumed_by_action(const struct action *paction, const struct unit_type *utype)
Definition unittype.c:1244
bool utype_veteran_has_power_bonus(const struct unit_type *punittype)
Definition unittype.c:2675
const char * unit_type_flag_helptxt(enum unit_type_flag_id id)
Definition unittype.c:1944
const char * utype_name_translation(const struct unit_type *punittype)
Definition unittype.c:1586
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:1042
bool utype_can_do_action(const struct unit_type *putype, const action_id act_id)
Definition unittype.c:396
#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:773
#define utype_class(_t_)
Definition unittype.h:756
#define utype_fuel(ptype)
Definition unittype.h:849
#define combat_bonus_list_iterate_end
Definition unittype.h:487
#define combat_bonus_list_iterate(bonuslist, pbonus)
Definition unittype.h:485
#define unit_tech_reqs_iterate_end
Definition unittype.h:891
#define unit_class_iterate(_p)
Definition unittype.h:918
#define unit_tech_reqs_iterate(_utype_, _p)
Definition unittype.h:885
static bool utype_has_flag(const struct unit_type *punittype, int flag)
Definition unittype.h:624
#define UTYF_LAST_USER_FLAG
Definition unittype.h:335
#define unit_type_iterate(_p)
Definition unittype.h:865
#define uclass_index(_c_)
Definition unittype.h:749
#define unit_class_iterate_end
Definition unittype.h:925
#define unit_type_iterate_end
Definition unittype.h:872
const char * freeciv_name_version(void)
Definition version.c:35