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