Freeciv-3.2: /fast/freeciv/nightly/S3_2/client/helpdata.c Source File

Freeciv-3.2

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