Freeciv-3.1: /fast/freeciv/nightly/S3_1/client/helpdata.c Source File

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 */
63 static 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
77 static const struct help_list_link *help_nodes_iterator;
78 static struct help_list *help_nodes;
79 static 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/************************************************************************/
87 void helpdata_init(void)
88{
89 help_nodes = help_list_new();
90}
91
92/************************************************************************/
95 void helpdata_done(void)
96{
97 help_list_destroy(help_nodes);
98}
99
100/************************************************************************/
106 static void check_help_nodes_init(void)
107{
108 if (!help_nodes_init) {
109 help_nodes_init = TRUE; /* before help_iter_start to avoid recursion! */
110 help_iter_start();
111 }
112}
113
114/************************************************************************/
117 void free_help_texts(void)
118{
119 check_help_nodes_init();
120 help_list_iterate(help_nodes, ptmp) {
121 free(ptmp->topic);
122 free(ptmp->text);
123 free(ptmp);
124 } help_list_iterate_end;
125 help_list_clear(help_nodes);
126}
127
128/************************************************************************/
131 static bool show_help_for_nation(const struct nation_type *pnation)
132{
133 return client_nation_is_in_current_set(pnation);
134}
135
136/************************************************************************/
141 static 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/************************************************************************/
200 static 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);
230 terrain = terrain_name_translation(pterrain);
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 }
294 } terrain_type_iterate_end;
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 }
317 } extra_type_by_rmcause_iterate_end;
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 {
335 fc_assert(FALSE);
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 }
357 } extra_type_by_rmcause_iterate_end;
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 {
375 fc_assert(FALSE);
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 }
397 } extra_type_by_rmcause_iterate_end;
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 {
415 fc_assert(FALSE);
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 }
428 } extra_type_by_cause_iterate_end;
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 }
435 } extra_type_by_cause_iterate_end;
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 }
471 } extra_type_by_cause_iterate_end;
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 }
483 } extra_type_by_cause_iterate_end;
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/************************************************************************/
517 static 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 }
544 } requirement_vector_iterate_end;
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 }
579 } requirement_vector_iterate_end;
580
581 strvec_destroy(coreqs);
582 strvec_destroy(conoreqs);
583 astr_free(&coreqstr);
584 astr_free(&conoreqstr);
585}
586
587/************************************************************************/
603 static 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);
624 } governments_iterate_end;
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 }
644 } improvement_iterate_end;
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);
662 } unit_type_iterate_end;
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);
680 } extra_type_iterate_end;
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);
698 } goods_type_iterate_end;
699}
700
701/************************************************************************/
704 static 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/************************************************************************/
719 static 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/************************************************************************/
741 void boot_help_texts(void)
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
754 check_help_nodes_init();
755
756 /* need to do something like this or bad things happen */
757 popdown_help_dialog();
758
759 if (!booted) {
760 log_verbose("Booting help texts");
761 } else {
762 /* free memory allocated last time booted */
763 free_help_texts();
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);
834 } unit_type_iterate_end;
835 break;
836 case HELP_TECH:
837 advance_index_iterate(A_FIRST, advi) {
838 if (valid_advance_by_number(advi)) {
839 pitem = new_help_item(current_type);
840 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
841 advance_name_translation(advance_by_number(advi)));
842 pitem->topic = fc_strdup(name);
843 pitem->text = fc_strdup("");
844 help_list_append(category_nodes, pitem);
845 }
846 } advance_index_iterate_end;
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 }
858 } terrain_type_iterate_end;
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);
889 } extra_type_iterate_end;
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, "",
897 goods_name_translation(pgood));
898 pitem->topic = fc_strdup(name);
899 pitem->text = fc_strdup("");
900 help_list_append(category_nodes, pitem);
901 } goods_type_iterate_end;
902 break;
903 case HELP_SPECIALIST:
904 specialist_type_iterate(sp) {
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, "",
909 specialist_plural_translation(pspec));
910 pitem->topic = fc_strdup(name);
911 pitem->text = fc_strdup("");
912 help_list_append(category_nodes, pitem);
913 } specialist_type_iterate_end;
914 break;
915 case HELP_GOVERNMENT:
916 governments_iterate(gov) {
917 pitem = new_help_item(current_type);
918 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
919 government_name_translation(gov));
920 pitem->topic = fc_strdup(name);
921 pitem->text = fc_strdup("");
922 help_list_append(category_nodes, pitem);
923 } governments_iterate_end;
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, "",
930 improvement_name_translation(pimprove));
931 pitem->topic = fc_strdup(name);
932 pitem->text = fc_strdup("");
933 help_list_append(category_nodes, pitem);
934 }
935 } improvement_iterate_end;
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, "",
942 improvement_name_translation(pimprove));
943 pitem->topic = fc_strdup(name);
944 pitem->text = fc_strdup("");
945 help_list_append(category_nodes, pitem);
946 }
947 } improvement_iterate_end;
948 break;
949 case HELP_RULESET:
950 {
951 int desc_len;
952 int len;
953
954 pitem = new_help_item(HELP_RULESET);
955 /* pitem->topic = fc_strdup(_(game.control.name)); */
956 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
957 Q_(HELP_RULESET_ITEM));
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",
975 _(game.control.name), game.control.version,
976 _(game.ruleset_summary));
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",
985 _(game.control.name), _(game.ruleset_summary));
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",
1000 _(game.control.name), game.control.version,
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, "",
1032 Q_(HELP_TILESET_ITEM));
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) {
1097 if (client_state() < C_S_RUNNING
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 }
1106 } nations_iterate_end;
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";
1121 } strvec_iterate_end;
1122 }
1123 pitem->text = fc_strdup(help_text_buffer);
1124 help_list_append(help_nodes, pitem);
1125 } multipliers_iterate_end;
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);
1134 } help_list_iterate_end;
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);
1168 } section_list_iterate_end;
1169
1170 section_list_destroy(sec);
1171 }
1172
1173 secfile_check_unused(sf);
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/************************************************************************/
1189 int num_help_items(void)
1190{
1191 check_help_nodes_init();
1192 return help_list_size(help_nodes);
1193}
1194
1195/************************************************************************/
1200 const struct help_item *get_help_item(int pos)
1201{
1202 int size;
1203
1204 check_help_nodes_init();
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
1231 check_help_nodes_init();
1232 idx = 0;
1233 help_list_iterate(help_nodes, ptmp) {
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 }
1245 help_list_iterate_end;
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/************************************************************************/
1275 void help_iter_start(void)
1276{
1277 check_help_nodes_init();
1278 help_nodes_iterator = help_list_head(help_nodes);
1279}
1280
1281/************************************************************************/
1285 const struct help_item *help_iter_next(void)
1286{
1287 const struct help_item *pitem;
1288
1289 check_help_nodes_init();
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/************************************************************************/
1316 char *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));
1335 } strvec_iterate_end;
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 }
1343 } requirement_vector_iterate_end;
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),
1353 improvement_name_translation(pimprove));
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),
1361 improvement_name_translation(pimprove));
1362 }
1363 } requirement_vector_iterate_end;
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 }
1392 } action_array_iterate_end;
1393
1394 if (u) {
1395 if (advance_number(u->require_advance) != A_NONE) {
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"),
1401 advance_name_translation(u->require_advance),
1402 utype_name_translation(u));
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"),
1407 utype_name_translation(u));
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
1432 action_enabler_list_iterate(action_enablers_for_action(act), enabler) {
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) {
1440 if (!universal_is_relevant_to_requirement(preq, &source)) {
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 }
1458 } requirement_vector_iterate_end;
1459 }
1460 } action_enabler_list_iterate_end;
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"),
1471 action_id_name_translation(act));
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"),
1480 action_id_name_translation(act));
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"),
1489 action_id_name_translation(act));
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"),
1498 action_id_name_translation(act));
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"),
1507 action_id_name_translation(act));
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"),
1516 action_id_name_translation(act));
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"),
1525 action_id_name_translation(act));
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"),
1534 action_id_name_translation(act));
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 }
1544 } action_iterate_end;
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. */
1569 action_enabler_list_iterate(action_enablers_for_action(act), enabler) {
1570 if (requirement_fulfilled_by_improvement(pimprove,
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) {
1578 if (!universal_is_relevant_to_requirement(preq, &source)) {
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 }
1592 } requirement_vector_iterate_end;
1593
1594 if (vector_max_range < min_range) {
1595 /* Found a smaller range. */
1596 min_range = vector_max_range;
1597 }
1598 }
1599 } action_enabler_list_iterate_end;
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"),
1608 action_id_name_translation(act));
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"),
1615 action_id_name_translation(act));
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"),
1622 action_id_name_translation(act));
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"),
1629 action_id_name_translation(act));
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"),
1636 action_id_name_translation(act));
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"),
1643 action_id_name_translation(act));
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"),
1650 action_id_name_translation(act));
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"),
1657 action_id_name_translation(act));
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 }
1667 } action_iterate_end;
1668
1669 {
1670 int i;
1671
1672 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
1673 Impr_type_id n = game.rgame.global_init_buildings[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 }
1706 } nations_iterate_end;
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/************************************************************************/
1728 static 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 }
1752 } action_iterate_end;
1753
1754 return FALSE;
1755}
1756
1757/************************************************************************/
1763 char *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));
1790 } strvec_iterate_end;
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 }
1882 } unit_type_iterate_end;
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 }
1938 } combat_bonus_list_iterate_end;
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:* "));
1944 } requirement_vector_iterate_end;
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"));
1962 if (game.scenario.prevent_new_cities) {
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 }
1994 } nations_iterate_end;
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 }
2004 } unit_type_iterate_end;
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 }
2057 } unit_class_iterate_end;
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 }
2084 } unit_type_iterate_end;
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 }
2170 } unit_class_iterate_end;
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 }
2199 } unit_class_iterate_end;
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 }
2220 if (utype_may_do_escape_action(utype)
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"),
2242 move_points_text(terrain_control.igter_cost, TRUE));
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)
2275 && server_setting_value_bool_get(
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 }
2299 } unit_class_iterate_end;
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 }
2310 } unit_type_iterate_end;
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) */
2366 if (server_setting_value_bool_get(
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 }
2381 } action_auto_perf_iterate_end;
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"),
2408 action_id_name_translation(act));
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,
2453 action_target_kind_help(action_id_get_target_kind(act)));
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
2480 if (odds != ACTION_ODDS_PCT_DICE_ROLL_NA
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"),
2676 utype_build_shield_cost_base(utype));
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"),
2688 effect_value_from_universals(
2689 EFT_HEAL_UNIT_PCT,
2690 req_pattern, ARRAY_SIZE(req_pattern))
2691 + 100);
2692 }
2693 break;
2694 case ACTRES_FOUND_CITY:
2695 if (game.scenario.prevent_new_cities) {
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",
2712 game.info.add_to_size_limit - utype->pop_cost),
2713 game.info.add_to_size_limit - utype->pop_cost);
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"),
2738 utype_name_translation(utype->obsoleted_by));
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),
2754 utype_name_translation(utype->converted_to),
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"),
2765 game.info.nuke_pop_loss_pct);
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"),
2770 game.info.nuke_pop_loss_pct);
2771 } else {
2772 cat_snprintf(buf, bufsz,
2773 _(" * can even destroy city completely "
2774 "(%d%% of size 1 rounds up to 1).\n"),
2775 game.info.nuke_pop_loss_pct);
2776 }
2777 }
2778 if (game.info.nuke_defender_survival_chance_pct > 0) {
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"),
2786 game.info.nuke_defender_survival_chance_pct);
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 }
2813 } extra_type_iterate_end;
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 }
2836 } extra_type_iterate_end;
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 }
2858 } extra_type_iterate_end;
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);
2923 } extra_type_by_cause_iterate_end;
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 }
2988 } action_iterate_end;
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 }
3006 } action_iterate_end;
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;
3030 action_enabler_list_iterate(action_enablers_for_action(act), enabler) {
3031 if (requirement_fulfilled_by_unit_type(utype,
3032 &(enabler->target_reqs))) {
3033 vulnerable = TRUE;
3034 break;
3035 }
3036 } action_enabler_list_iterate_end;
3037
3038 if (!vulnerable) {
3039 cat_snprintf(buf, bufsz,
3040 _("* Doing the action \'%s\' to this unit"
3041 " is impossible.\n"),
3042 action_id_name_translation(act));
3043 }
3044 } action_iterate_end;
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));
3104 } strvec_iterate_end;
3105 }
3106 CATLSTR(buf, bufsz, user_text);
3107 return buf;
3108}
3109
3110/************************************************************************/
3115 void 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"),
3139 tech_class_name_translation(vap->tclass));
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),
3174 advance_name_translation(vap),
3175 research_goal_unknown_techs(presearch, i) - 1, buf2);
3176 } else {
3177 CATLSTR(buf, bufsz,
3178 _("You cannot research this technology."));
3179 }
3180 if (!techs_have_fixed_costs()
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"));
3196 requirement_vector_iterate(&vap->research_reqs, preq) {
3197 (void) req_text_insert_nl(buf, bufsz, pplayer, preq, VERB_DEFAULT, "");
3198 } requirement_vector_iterate_end;
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 }
3241 } nations_iterate_end;
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));
3269 } advance_root_req_iterate_end;
3270 }
3271 } advance_root_req_iterate_end;
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
3281 advance_index_iterate(A_FIRST, root) {
3282 if (BV_ISSET(roots, root)) {
3283 root_techs[n_roots++]
3284 = advance_name_translation(advance_by_number(root));
3285 }
3286 } advance_index_iterate_end;
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"),
3301 advance_name_translation(vap));
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));
3329 } strvec_iterate_end;
3330 }
3331
3332 astr_free(&astr);
3333}
3334
3335/************************************************************************/
3338 void 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 }
3373 } extra_type_by_cause_iterate_end;
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 }
3385 } extra_type_by_cause_iterate_end;
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 }
3401 } unit_class_iterate_end;
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));
3440 } strvec_iterate_end;
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/************************************************************************/
3455 const 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';
3462 output_type_iterate(o) {
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 }
3499 } output_type_iterate_end;
3500
3501 return has_effect ? str : NULL;
3502}
3503
3504/**********************************************************************/
3510 static 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
3538 output_type_iterate(o) {
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 }
3550 } output_type_iterate_end;
3551 }
3552
3553 /* Fixed bonuses for extra, possibly unrelated to terrain type */
3554
3555 output_type_iterate(o) {
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 }
3576 } output_type_iterate_end;
3577}
3578
3579/**********************************************************************/
3585 const char *helptext_extra_for_terrain_str(struct extra_type *pextra,
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/************************************************************************/
3619 void 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));
3655 } strvec_iterate_end;
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:
3692 fc_assert(FALSE);
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
3733 && is_extra_caused_by_worker_action(pextra);
3734
3735 requirement_vector_iterate(&pextra->reqs, preq) {
3736 (void) req_text_insert_nl(reqsbuf, sizeof(reqsbuf), pplayer, preq,
3737 VERB_DEFAULT,
3738 /* TRANS: bullet point; note trailing space */
3739 buildable ? Q_("?bullet:* ") : "");
3740 } requirement_vector_iterate_end;
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 }
3778 } terrain_type_iterate_end;
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 }
3825 } terrain_type_iterate_end;
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:* "));
3844 } requirement_vector_iterate_end;
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"),
3872 advance_name_translation(advance_by_number(pextra->visibility_req)));
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 }
3889 } unit_class_iterate_end;
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 }
3939 } unit_type_iterate_end;
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),
3960 move_points_text(proad->move_cost, TRUE));
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;
4014 output_type_iterate(o) {
4015 if (proad->tile_incr[o] > 0) {
4016 do_bonus = TRUE;
4017 fc_assert(o == O_FOOD || o == O_SHIELD || o == O_TRADE);
4018 }
4019 } output_type_iterate_end;
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 }
4052 terrain_type_iterate(t) {
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 }
4078 } terrain_type_iterate_end;
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/************************************************************************/
4094 void 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));
4105 } strvec_iterate_end;
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 }
4126 } requirement_vector_iterate_end;
4127 if (reqs) {
4128 fc_strlcat(buf, "\n", bufsz);
4129 }
4130
4131 CATLSTR(buf, bufsz, user_text);
4132}
4133
4134/************************************************************************/
4140 void 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));
4151 } strvec_iterate_end;
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 }
4159 } requirement_vector_iterate_end;
4160 if (reqs) {
4161 fc_strlcat(buf, "\n", bufsz);
4162 }
4163
4164 CATLSTR(buf, bufsz, user_text);
4165}
4166
4167/************************************************************************/
4175 void 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));
4190 } strvec_iterate_end;
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 }
4198 } requirement_vector_iterate_end;
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:* "));
4208 effect_list_iterate(get_req_source_effects(&source), peffect) {
4209 Output_type_id output_type = O_LAST;
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. */
4235 fc_assert(output_type == O_LAST);
4236 output_type = preq->source.value.outputtype;
4237 strvec_append(outputs, get_output_name(output_type));
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 };
4272 } requirement_vector_iterate_end;
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 =
4295 output_type != O_LAST ? get_output_type(output_type) : NULL;
4296
4297 world_value =
4298 get_target_bonus_effects(NULL,
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
4321 output_type_iterate(ot) {
4322 struct output_type *pot = get_output_type(ot);
4323
4324 if (!harvested_only || pot->harvested) {
4325 strvec_append(outputs, _(pot->name));
4326 }
4327 } output_type_iterate_end;
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.)"),
4547 game.info.celebratesize);
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 }
4592 } unit_type_iterate_end;
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.)"),
4677 game.info.celebratesize);
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.)"),
4697 game.info.celebratesize);
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:
4791 if (game.info.borders == BORDERS_ENABLED
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
4806 } effect_list_iterate_end;
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"),
4821 action_id_name_translation(act),
4822 action_target_kind_help(action_id_get_target_kind(act)));
4823 }
4824 } action_iterate_end;
4825
4826 if (user_text && user_text[0] != '\0') {
4827 cat_snprintf(buf, bufsz, "\n%s", user_text);
4828 }
4829}
4830
4831/************************************************************************/
4834 char *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';
4845 output_type_iterate(o) {
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 }
4853 } output_type_iterate_end;
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/************************************************************************/
4871 void 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"),
4901 government_name_translation(pnation->init_government));
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] =
4913 advance_name_translation(advance_by_number(pnation->init_techs[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] =
5002 improvement_name_translation(
5003 improvement_by_number(pnation->init_buildings[i]));
5004 }
5005 astr_build_and_list(&list, impr_names, i);
5006 PRINT_BREAK();
5007 if (game.rgame.global_init_buildings[0] != B_LAST) {
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/************************************************************************/
5037 enum help_page_type help_type_by_requirement(const struct requirement *req)
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) {
5047 if (is_great_wonder(req->source.value.building)) {
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}
action_removes_extra
bool action_removes_extra(const struct action *paction, const struct extra_type *pextra)
Definition actions.c:2312
action_name_translation
const char * action_name_translation(const struct action *action)
Definition actions.c:1890
action_get_actor_kind
enum action_actor_kind action_get_actor_kind(const struct action *paction)
Definition actions.c:1730
action_id_name_translation
const char * action_id_name_translation(action_id act_id)
Definition actions.c:1910
action_list_end
void action_list_end(action_id *act_list, int size)
Definition actions.c:7403
action_is_in_use
bool action_is_in_use(struct action *paction)
Definition actions.c:7332
action_get_battle_kind
enum action_battle_kind action_get_battle_kind(const struct action *pact)
Definition actions.c:1762
action_get_sub_target_kind
enum action_sub_target_kind action_get_sub_target_kind(const struct action *paction)
Definition actions.c:1751
action_would_be_blocked_by
bool action_would_be_blocked_by(const struct action *blocked, const struct action *blocker)
Definition actions.c:1856
action_immune_government
bool action_immune_government(struct government *gov, action_id act)
Definition actions.c:7107
action_dice_roll_initial_odds
int action_dice_roll_initial_odds(const struct action *paction)
Definition actions.c:6985
action_list_add_all_by_result
void action_list_add_all_by_result(action_id *act_list, int *position, enum action_result result)
Definition actions.c:7420
action_target_kind_help
const char * action_target_kind_help(enum action_target_kind kind)
Definition actions.c:9344
action_get_target_kind
enum action_target_kind action_get_target_kind(const struct action *paction)
Definition actions.c:1740
action_creates_extra
bool action_creates_extra(const struct action *paction, const struct extra_type *pextra)
Definition actions.c:2224
action_enablers_for_action
struct action_enabler_list * action_enablers_for_action(action_id action)
Definition actions.c:2475
action_id_univs_not_blocking
#define action_id_univs_not_blocking(act_id, act_uni, tgt_uni)
Definition actions.h:947
action_auto_perf_iterate_end
#define action_auto_perf_iterate_end
Definition actions.h:593
action_by_number
static struct action * action_by_number(action_id act_id)
Definition actions.h:638
action_has_result
#define action_has_result(_act_, _res_)
Definition actions.h:448
action_enabler_list_iterate_end
#define action_enabler_list_iterate_end
Definition actions.h:457
action_id_get_role
#define action_id_get_role(act_id)
Definition actions.h:708
ACTION_DISTANCE_UNLIMITED
#define ACTION_DISTANCE_UNLIMITED
Definition actions.h:352
action_array_iterate
#define action_array_iterate(_act_list_, _act_id_)
Definition actions.h:504
action_array_iterate_end
#define action_array_iterate_end
Definition actions.h:516
action_iterate_end
#define action_iterate_end
Definition actions.h:472
MAX_NUM_ACTIONS
#define MAX_NUM_ACTIONS
Definition actions.h:296
action_enabler_list_iterate
#define action_enabler_list_iterate(action_enabler_list, aenabler)
Definition actions.h:455
action_iterate
#define action_iterate(_act_)
Definition actions.h:467
action_id_get_target_kind
#define action_id_get_target_kind(act_id)
Definition actions.h:655
action_auto_perf_iterate
#define action_auto_perf_iterate(_act_perf_)
Definition actions.h:581
ACTION_ODDS_PCT_DICE_ROLL_NA
#define ACTION_ODDS_PCT_DICE_ROLL_NA
Definition actions.h:931
astr_free
void astr_free(struct astring *astr)
Definition astring.c:153
astr_build_or_list
const char * astr_build_or_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:329
astr_set
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:267
astr_build_and_list
const char * astr_build_and_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:367
astr_init
void astr_init(struct astring *astr)
Definition astring.c:144
str
#define str
Definition astring.c:76
n
#define n
Definition astring.c:77
astring.h
astr_str
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:93
ASTRING_INIT
#define ASTRING_INIT
Definition astring.h:44
territory_claiming_base
bool territory_claiming_base(const struct base_type *pbase)
Definition base.c:158
bitvector.h
BV_CLR_ALL_FROM
#define BV_CLR_ALL_FROM(vec_to, vec_from)
Definition bitvector.h:127
BV_CLR_ALL
#define BV_CLR_ALL(bv)
Definition bitvector.h:95
BV_SET
#define BV_SET(bv, bit)
Definition bitvector.h:81
BV_ARE_EQUAL
#define BV_ARE_EQUAL(vec1, vec2)
Definition bitvector.h:113
BV_ISSET
#define BV_ISSET(bv, bit)
Definition bitvector.h:78
BV_ISSET_ANY
#define BV_ISSET_ANY(vec)
Definition bitvector.h:109
get_output_type
struct output_type * get_output_type(Output_type_id output)
Definition city.c:632
get_output_name
const char * get_output_name(Output_type_id output)
Definition city.c:623
output_type_iterate
#define output_type_iterate(output)
Definition city.h:809
output_type_iterate_end
#define output_type_iterate_end
Definition city.h:815
client_state
enum client_states client_state(void)
Definition client_main.c:1029
client_main.h
C_S_RUNNING
@ C_S_RUNNING
Definition client_main.h:47
client_nation_is_in_current_set
bool client_nation_is_in_current_set(const struct nation_type *pnation)
Definition climisc.c:1491
climisc.h
list
static struct fc_sockaddr_list * list
Definition clinet.c:102
utypes
char * utypes
Definition comments.c:33
MAX_LEN_PACKET
#define MAX_LEN_PACKET
Definition connection.h:56
road
static void road(QVariant data1, QVariant data2)
Definition dialogs.cpp:2818
effect_universals_value_never_below
bool effect_universals_value_never_below(enum effect_type type, struct universal *unis, size_t n_unis, int min_value)
Definition effects.c:488
get_req_source_effects
struct effect_list * get_req_source_effects(struct universal *psource)
Definition effects.c:146
reqs
struct @21::@22 reqs
get_target_bonus_effects
int get_target_bonus_effects(struct effect_list *plist, const struct req_context *context, const struct player *other_player, enum effect_type effect_type)
Definition effects.c:691
effect_value_from_universals
int effect_value_from_universals(enum effect_type type, struct universal *unis, size_t n_unis)
Definition effects.c:402
effect_cumulative_max
int effect_cumulative_max(enum effect_type type, struct universal *unis, size_t n_unis)
Definition effects.c:331
building_has_effect
bool building_has_effect(const struct impr_type *pimprove, enum effect_type effect_type)
Definition effects.c:587
effects.h
effect_list_iterate_end
#define effect_list_iterate_end
Definition effects.h:375
effect_list_iterate
#define effect_list_iterate(effect_list, peffect)
Definition effects.h:373
extra_flag_helptxt
const char * extra_flag_helptxt(enum extra_flag_id id)
Definition extras.c:923
is_extra_caused_by_worker_action
bool is_extra_caused_by_worker_action(const struct extra_type *pextra)
Definition extras.c:981
extra_has_flag
bool extra_has_flag(const struct extra_type *pextra, enum extra_flag_id flag)
Definition extras.c:810
is_extra_removed_by
bool is_extra_removed_by(const struct extra_type *pextra, enum extra_rmcause rmcause)
Definition extras.c:327
extra_count
int extra_count(void)
Definition extras.c:145
is_native_extra_to_uclass
bool is_native_extra_to_uclass(const struct extra_type *pextra, const struct unit_class *pclass)
Definition extras.c:792
extra_name_translation
const char * extra_name_translation(const struct extra_type *pextra)
Definition extras.c:186
extra_type_iterate
#define extra_type_iterate(_p)
Definition extras.h:291
extra_type_iterate_end
#define extra_type_iterate_end
Definition extras.h:297
extra_type_by_rmcause_iterate_end
#define extra_type_by_rmcause_iterate_end
Definition extras.h:334
is_extra_caused_by
#define is_extra_caused_by(e, c)
Definition extras.h:196
extra_type_by_rmcause_iterate
#define extra_type_by_rmcause_iterate(_rmcause, _extra)
Definition extras.h:329
extra_road_get
#define extra_road_get(_e_)
Definition extras.h:185
extra_type_by_cause_iterate_end
#define extra_type_by_cause_iterate_end
Definition extras.h:315
EF_LAST_USER_FLAG
#define EF_LAST_USER_FLAG
Definition extras.h:79
extra_type_by_cause_iterate
#define extra_type_by_cause_iterate(_cause, _extra)
Definition extras.h:309
MAX_NUM_BUILDING_LIST
#define MAX_NUM_BUILDING_LIST
Definition fc_types.h:46
Impr_type_id
int Impr_type_id
Definition fc_types.h:346
Unit_Class_id
int Unit_Class_id
Definition fc_types.h:388
action_id
int action_id
Definition fc_types.h:359
CASUS_BELLI_OUTRAGE
#define CASUS_BELLI_OUTRAGE
Definition fc_types.h:454
CASUS_BELLI_VICTIM
#define CASUS_BELLI_VICTIM
Definition fc_types.h:448
MAX_NUM_UNIT_LIST
#define MAX_NUM_UNIT_LIST
Definition fc_types.h:45
MAX_LEN_NAME
#define MAX_LEN_NAME
Definition fc_types.h:66
MAX_NUM_TECH_LIST
#define MAX_NUM_TECH_LIST
Definition fc_types.h:44
O_SHIELD
@ O_SHIELD
Definition fc_types.h:91
O_FOOD
@ O_FOOD
Definition fc_types.h:91
O_TRADE
@ O_TRADE
Definition fc_types.h:91
O_LAST
@ O_LAST
Definition fc_types.h:91
BORDERS_ENABLED
@ BORDERS_ENABLED
Definition fc_types.h:892
Output_type_id
enum output_type_id Output_type_id
Definition fc_types.h:348
get_internal_string_length
size_t get_internal_string_length(const char *text)
Definition fciconv.c:395
fciconv.h
fcintl.h
Q_
#define Q_(String)
Definition fcintl.h:70
PL_
#define PL_(String1, String2, n)
Definition fcintl.h:71
_
#define _(String)
Definition fcintl.h:67
N_
#define N_(String)
Definition fcintl.h:69
game
struct civ_game game
Definition game.c:57
game.h
government_name_translation
const char * government_name_translation(const struct government *pgovern)
Definition government.c:142
government.h
governments_iterate
#define governments_iterate(NAME_pgov)
Definition government.h:120
governments_iterate_end
#define governments_iterate_end
Definition government.h:123
pos
static struct tile * pos
Definition finddlg.c:53
source
static GtkWidget * source
Definition gotodlg.c:58
client_string
const char * client_string
Definition gui_main.c:104
insert_client_build_info
void insert_client_build_info(char *outbuf, size_t outlen)
Definition gui_main.c:2342
popdown_help_dialog
void popdown_help_dialog(void)
Definition helpdlg.c:185
type
GType type
Definition repodlgs.c:1312
bufsz
static const int bufsz
Definition helpdlg.c:70
gui_main_g.h
CATLSTR
#define CATLSTR(_b, _s, _t)
Definition helpdata.c:60
extra_bonus_for_terrain
static void extra_bonus_for_terrain(struct extra_type *pextra, struct terrain *pterrain, int *bonus)
Definition helpdata.c:3510
help_iter_start
void help_iter_start(void)
Definition helpdata.c:1275
help_item_compar
static int help_item_compar(const struct help_item *const *ppa, const struct help_item *const *ppb)
Definition helpdata.c:719
help_nodes_init
static bool help_nodes_init
Definition helpdata.c:79
free_help_texts
void free_help_texts(void)
Definition helpdata.c:117
boot_help_texts
void boot_help_texts(void)
Definition helpdata.c:741
insert_allows
static void insert_allows(struct universal *psource, char *buf, size_t bufsz, const char *prefix)
Definition helpdata.c:603
insert_generated_text
static bool insert_generated_text(char *outbuf, size_t outlen, const char *name)
Definition helpdata.c:200
help_nodes
static struct help_list * help_nodes
Definition helpdata.c:78
help_type_names
static const char *const help_type_names[]
Definition helpdata.c:63
helptext_government
void helptext_government(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct government *gov)
Definition helpdata.c:4175
helptext_advance
void helptext_advance(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, int i)
Definition helpdata.c:3115
help_type_by_requirement
enum help_page_type help_type_by_requirement(const struct requirement *req)
Definition helpdata.c:5037
helptext_extra
void helptext_extra(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct extra_type *pextra)
Definition helpdata.c:3619
helpdata_init
void helpdata_init(void)
Definition helpdata.c:87
check_help_nodes_init
static void check_help_nodes_init(void)
Definition helpdata.c:106
get_help_item
const struct help_item * get_help_item(int pos)
Definition helpdata.c:1200
num_help_items
int num_help_items(void)
Definition helpdata.c:1189
insert_veteran_help
static bool insert_veteran_help(char *outbuf, size_t outlen, const struct veteran_system *veteran, const char *intro, const char *nolevels)
Definition helpdata.c:141
helptext_goods
void helptext_goods(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct goods_type *pgood)
Definition helpdata.c:4094
show_help_for_nation
static bool show_help_for_nation(const struct nation_type *pnation)
Definition helpdata.c:131
PRINT_BREAK
#define PRINT_BREAK()
helptext_road_bonus_str
const char * helptext_road_bonus_str(const struct terrain *pterrain, const struct road_type *proad)
Definition helpdata.c:3455
help_nodes_iterator
static const struct help_list_link * help_nodes_iterator
Definition helpdata.c:77
helptext_unit_upkeep_str
char * helptext_unit_upkeep_str(const struct unit_type *utype)
Definition helpdata.c:4834
helptext_extra_for_terrain_str
const char * helptext_extra_for_terrain_str(struct extra_type *pextra, struct terrain *pterrain, enum unit_activity act)
Definition helpdata.c:3585
insert_allows_single
static void insert_allows_single(struct universal *psource, struct requirement_vector *psubjreqs, const char *subjstr, const char *const *strs, char *buf, size_t bufsz, const char *prefix)
Definition helpdata.c:517
help_list_iterate_end
#define help_list_iterate_end
Definition helpdata.c:75
utype_may_do_escape_action
static bool utype_may_do_escape_action(const struct unit_type *utype)
Definition helpdata.c:1728
helpdata_done
void helpdata_done(void)
Definition helpdata.c:95
helptext_specialist
void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct specialist *pspec)
Definition helpdata.c:4140
get_help_item_spec
const struct help_item * get_help_item_spec(const char *name, enum help_page_type htype, int *pos)
Definition helpdata.c:1223
help_list_iterate
#define help_list_iterate(helplist, phelp)
Definition helpdata.c:73
help_iter_next
const struct help_item * help_iter_next(void)
Definition helpdata.c:1285
helptext_building
char * helptext_building(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, const struct impr_type *pimprove)
Definition helpdata.c:1316
helptext_terrain
void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, struct terrain *pterrain)
Definition helpdata.c:3338
helptext_unit
char * helptext_unit(char *buf, size_t bufsz, struct player *pplayer, const char *user_text, const struct unit_type *utype)
Definition helpdata.c:1763
helptext_nation
void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation, const char *user_text)
Definition helpdata.c:4871
new_help_item
static struct help_item * new_help_item(int type)
Definition helpdata.c:704
helpdata.h
HELP_TILESET_ITEM
#define HELP_TILESET_ITEM
HELP_RULESET_ITEM
#define HELP_RULESET_ITEM
help_page_type
help_page_type
Definition helpdlg_g.h:20
HELP_ANY
@ HELP_ANY
Definition helpdlg_g.h:20
HELP_MULTIPLIER
@ HELP_MULTIPLIER
Definition helpdlg_g.h:24
HELP_TERRAIN
@ HELP_TERRAIN
Definition helpdlg_g.h:21
HELP_EXTRA
@ HELP_EXTRA
Definition helpdlg_g.h:21
HELP_NATIONS
@ HELP_NATIONS
Definition helpdlg_g.h:23
HELP_LAST
@ HELP_LAST
Definition helpdlg_g.h:24
HELP_IMPROVEMENT
@ HELP_IMPROVEMENT
Definition helpdlg_g.h:20
HELP_UNIT
@ HELP_UNIT
Definition helpdlg_g.h:20
HELP_SPECIALIST
@ HELP_SPECIALIST
Definition helpdlg_g.h:22
HELP_GOVERNMENT
@ HELP_GOVERNMENT
Definition helpdlg_g.h:22
HELP_GOODS
@ HELP_GOODS
Definition helpdlg_g.h:22
HELP_WONDER
@ HELP_WONDER
Definition helpdlg_g.h:21
HELP_TECH
@ HELP_TECH
Definition helpdlg_g.h:21
HELP_RULESET
@ HELP_RULESET
Definition helpdlg_g.h:23
HELP_TEXT
@ HELP_TEXT
Definition helpdlg_g.h:20
HELP_TILESET
@ HELP_TILESET
Definition helpdlg_g.h:23
valid_improvement
const struct impr_type * valid_improvement(const struct impr_type *pimprove)
Definition improvement.c:209
improvement_by_number
struct impr_type * improvement_by_number(const Impr_type_id id)
Definition improvement.c:192
is_great_wonder
bool is_great_wonder(const struct impr_type *pimprove)
Definition improvement.c:767
improvement_has_flag
bool improvement_has_flag(const struct impr_type *pimprove, enum impr_flag_id flag)
Definition improvement.c:381
improvement_name_translation
const char * improvement_name_translation(const struct impr_type *pimprove)
Definition improvement.c:241
is_small_wonder
bool is_small_wonder(const struct impr_type *pimprove)
Definition improvement.c:775
improvement_iterate_end
#define improvement_iterate_end
Definition improvement.h:211
improvement_iterate
#define improvement_iterate(_p)
Definition improvement.h:205
B_LAST
#define B_LAST
Definition improvement.h:42
name
const char * name
Definition inputfile.c:127
log.h
fc_assert_ret
#define fc_assert_ret(condition)
Definition log.h:191
log_verbose
#define log_verbose(message,...)
Definition log.h:109
fc_assert
#define fc_assert(condition)
Definition log.h:176
fc_assert_ret_val
#define fc_assert_ret_val(condition, val)
Definition log.h:194
log_error
#define log_error(message,...)
Definition log.h:103
terrain_control
struct terrain_misc terrain_control
Definition map.c:69
map.h
mem.h
FC_FREE
#define FC_FREE(ptr)
Definition mem.h:41
fc_strdup
#define fc_strdup(str)
Definition mem.h:43
fc_malloc
#define fc_malloc(sz)
Definition mem.h:34
move_points_text
const char * move_points_text(int mp, bool reduce)
Definition movement.c:973
move_points_text_full
const char * move_points_text_full(int mp, bool reduce, const char *prefix, const char *none, bool align)
Definition movement.c:906
is_native_to_class
bool is_native_to_class(const struct unit_class *punitclass, const struct terrain *pterrain, const bv_extras *extras)
Definition movement.c:327
can_unit_type_transport
bool can_unit_type_transport(const struct unit_type *transporter, const struct unit_class *transported)
Definition movement.c:826
can_attack_non_native
bool can_attack_non_native(const struct unit_type *utype)
Definition movement.c:202
movement.h
multipliers.h
multipliers_iterate
#define multipliers_iterate(_mul_)
Definition multipliers.h:61
multipliers_iterate_end
#define multipliers_iterate_end
Definition multipliers.h:67
name_translation_get
static const char * name_translation_get(const struct name_translation *ptrans)
Definition name_translation.h:121
nation_plural_translation
const char * nation_plural_translation(const struct nation_type *pnation)
Definition nation.c:158
nations_iterate_end
#define nations_iterate_end
Definition nation.h:335
nations_iterate
#define nations_iterate(NAME_pnation)
Definition nation.h:332
MAX_LEN_MSG
#define MAX_LEN_MSG
Definition packets.h:43
len
int len
Definition packhand.c:125
diplrel_name_translation
const char * diplrel_name_translation(int value)
Definition player.c:1591
secfile_load
struct section_file * secfile_load(const char *filename, bool allow_duplicates)
Definition registry.c:50
registry.h
secfile_error
const char * secfile_error(void)
Definition section_file.c:36
section_name
const char * section_name(const struct section *psection)
Definition section_file.c:65
secfile_destroy
void secfile_destroy(struct section_file *secfile)
Definition section_file.c:95
secfile_check_unused
void secfile_check_unused(const struct section_file *secfile)
Definition registry_ini.c:880
secfile_sections_by_name_prefix
struct section_list * secfile_sections_by_name_prefix(const struct section_file *secfile, const char *prefix)
Definition registry_ini.c:2808
secfile_lookup_str_vec
const char ** secfile_lookup_str_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
Definition registry_ini.c:2176
secfile_lookup_str
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
Definition registry_ini.c:2112
section_list_iterate
#define section_list_iterate(seclist, psection)
Definition registry_ini.h:45
section_list_iterate_end
#define section_list_iterate_end
Definition registry_ini.h:47
req_text_insert_nl
bool req_text_insert_nl(char *buf, size_t bufsz, struct player *pplayer, const struct requirement *preq, enum rt_verbosity verb, const char *prefix)
Definition reqtext.c:2879
reqtext.h
VERB_DEFAULT
@ VERB_DEFAULT
Definition reqtext.h:20
universal_is_relevant_to_requirement
bool universal_is_relevant_to_requirement(const struct requirement *req, const struct universal *source)
Definition requirements.c:6008
universal_fulfills_requirements
bool universal_fulfills_requirements(bool check_necessary, const struct requirement_vector *reqs, const struct universal *source)
Definition requirements.c:5971
are_universals_equal
bool are_universals_equal(const struct universal *psource1, const struct universal *psource2)
Definition requirements.c:5278
universal_name_translation
const char * universal_name_translation(const struct universal *psource, char *buf, size_t bufsz)
Definition requirements.c:5529
requirement_fulfilled_by_unit_type
#define requirement_fulfilled_by_unit_type(_ut_, _rqs_)
Definition requirements.h:418
requirement_fulfilled_by_improvement
#define requirement_fulfilled_by_improvement(_imp_, _rqs_)
Definition requirements.h:407
requirement_vector_iterate_end
#define requirement_vector_iterate_end
Definition requirements.h:93
requirement_vector_iterate
#define requirement_vector_iterate(req_vec, preq)
Definition requirements.h:91
research_goal_unknown_techs
int research_goal_unknown_techs(const struct research *presearch, Tech_type_id goal)
Definition research.c:747
research_invention_reachable
bool research_invention_reachable(const struct research *presearch, const Tech_type_id tech)
Definition research.c:665
research_goal_bulbs_required
int research_goal_bulbs_required(const struct research *presearch, Tech_type_id goal)
Definition research.c:769
research_total_bulbs_required
int research_total_bulbs_required(const struct research *presearch, Tech_type_id tech, bool loss_value)
Definition research.c:858
research_get
struct research * research_get(const struct player *pplayer)
Definition research.c:126
research_invention_state
enum tech_state research_invention_state(const struct research *presearch, Tech_type_id tech)
Definition research.c:616
research.h
road_has_flag
bool road_has_flag(const struct road_type *proad, enum road_flag_id flag)
Definition road.c:410
road_provides_move_bonus
bool road_provides_move_bonus(const struct road_type *proad)
Definition road.c:499
server_setting_by_name
server_setting_id server_setting_by_name(const char *name)
Definition server_settings.c:29
server_setting_value_bool_get
bool server_setting_value_bool_get(server_setting_id id)
Definition server_settings.c:70
server_settings.h
level
struct setting_list * level[OLEVELS_NUM]
Definition settings.c:183
compare_strings
int compare_strings(const void *first, const void *second)
Definition shared.c:358
fileinfoname
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1094
get_data_dirs
const struct strvec * get_data_dirs(void)
Definition shared.c:886
ARRAY_SIZE
#define ARRAY_SIZE(x)
Definition shared.h:85
MIN
#define MIN(x, y)
Definition shared.h:55
MAX
#define MAX(x, y)
Definition shared.h:54
specialist_by_number
struct specialist * specialist_by_number(const Specialist_type_id id)
Definition specialist.c:100
specialist_plural_translation
const char * specialist_plural_translation(const struct specialist *sp)
Definition specialist.c:155
specialist.h
specialist_type_iterate_end
#define specialist_type_iterate_end
Definition specialist.h:79
specialist_type_iterate
#define specialist_type_iterate(sp)
Definition specialist.h:73
speclist.h
size
size_t size
Definition specvec.h:72
strvec_destroy
void strvec_destroy(struct strvec *psv)
Definition string_vector.c:71
strvec_append
void strvec_append(struct strvec *psv, const char *string)
Definition string_vector.c:278
strvec_to_or_list
const char * strvec_to_or_list(const struct strvec *psv, struct astring *astr)
Definition string_vector.c:428
strvec_new
struct strvec * strvec_new(void)
Definition string_vector.c:58
strvec_clear
void strvec_clear(struct strvec *psv)
Definition string_vector.c:159
strvec_size
size_t strvec_size(const struct strvec *psv)
Definition string_vector.c:343
strvec_to_and_list
const char * strvec_to_and_list(const struct strvec *psv, struct astring *astr)
Definition string_vector.c:441
string_vector.h
strvec_iterate
#define strvec_iterate(psv, str)
Definition string_vector.h:61
strvec_iterate_end
#define strvec_iterate_end
Definition string_vector.h:68
action
Definition actions.h:379
action::id
action_id id
Definition actions.h:380
action::max_distance
int max_distance
Definition actions.h:395
action::quiet
bool quiet
Definition actions.h:402
action::moves_actor
enum moves_actor_kind moves_actor
Definition actions.h:432
action::is_unit
struct action::@12::@13 is_unit
action::result
enum action_result result
Definition actions.h:382
action::sub_results
bv_action_sub_results sub_results
Definition actions.h:383
action::actor
union action::@12 actor
action::min_distance
int min_distance
Definition actions.h:395
advance
Definition tech.h:124
advance::research_reqs
struct requirement_vector research_reqs
Definition tech.h:137
advance::tclass
struct tech_class * tclass
Definition tech.h:130
advance::helptext
struct strvec * helptext
Definition tech.h:140
astring
Definition astring.h:36
base_type
Definition base.h:41
base_type::vision_main_sq
int vision_main_sq
Definition base.h:45
base_type::gui_type
enum base_gui_type gui_type
Definition base.h:43
base_type::vision_invis_sq
int vision_invis_sq
Definition base.h:46
base_type::vision_subs_sq
int vision_subs_sq
Definition base.h:47
civ_game::rgame
struct civ_game::@29 rgame
civ_game::control
struct packet_ruleset_control control
Definition game.h:83
civ_game::ruleset_summary
char * ruleset_summary
Definition game.h:84
civ_game::global_init_techs
int global_init_techs[MAX_NUM_TECH_LIST]
Definition game.h:107
civ_game::info
struct packet_game_info info
Definition game.h:89
civ_game::client
struct civ_game::@30::@33 client
civ_game::global_init_buildings
int global_init_buildings[MAX_NUM_BUILDING_LIST]
Definition game.h:108
civ_game::scenario
struct packet_scenario_info scenario
Definition game.h:87
civ_game::ruleset_description
char * ruleset_description
Definition game.h:85
civ_game::ruleset_init
bool ruleset_init
Definition game.h:115
civ_game::veteran
struct veteran_system * veteran
Definition game.h:100
extra_type
Definition extras.h:85
extra_type::helptext
struct strvec * helptext
Definition extras.h:143
extra_type::road
struct road_type * road
Definition extras.h:149
extra_type::removal_time
int removal_time
Definition extras.h:116
extra_type::generated
bool generated
Definition extras.h:113
extra_type::rmreqs
struct requirement_vector rmreqs
Definition extras.h:103
extra_type::data
struct extra_type::@24 data
extra_type::visibility_req
Tech_type_id visibility_req
Definition extras.h:133
extra_type::removal_time_factor
int removal_time_factor
Definition extras.h:117
extra_type::reqs
struct requirement_vector reqs
Definition extras.h:102
extra_type::buildable
bool buildable
Definition extras.h:112
extra_type::eus
enum extra_unit_seen_type eus
Definition extras.h:124
extra_type::defense_bonus
int defense_bonus
Definition extras.h:120
extra_type::build_time
int build_time
Definition extras.h:114
extra_type::infracost
int infracost
Definition extras.h:118
extra_type::base
struct base_type * base
Definition extras.h:148
goods_type
Definition traderoutes.h:180
goods_type::to_pct
int to_pct
Definition traderoutes.h:188
goods_type::from_pct
int from_pct
Definition traderoutes.h:187
goods_type::reqs
struct requirement_vector reqs
Definition traderoutes.h:185
goods_type::onetime_pct
int onetime_pct
Definition traderoutes.h:189
goods_type::helptext
struct strvec * helptext
Definition traderoutes.h:193
government
Definition government.h:52
government::helptext
struct strvec * helptext
Definition government.h:61
government::reqs
struct requirement_vector reqs
Definition government.h:58
help_item
Definition helpdata.h:25
help_item::topic
char * topic
Definition helpdata.h:26
help_item::type
enum help_page_type type
Definition helpdata.h:27
help_item::text
char * text
Definition helpdata.h:26
impr_type
Definition improvement.h:68
impr_type::obsolete_by
struct requirement_vector obsolete_by
Definition improvement.h:76
impr_type::reqs
struct requirement_vector reqs
Definition improvement.h:75
impr_type::helptext
struct strvec * helptext
Definition improvement.h:82
nation_type
Definition nation.h:97
nation_type::init_buildings
int init_buildings[MAX_NUM_BUILDING_LIST]
Definition nation.h:122
nation_type::init_government
struct government * init_government
Definition nation.h:123
nation_type::init_units
struct unit_type * init_units[MAX_NUM_UNIT_LIST]
Definition nation.h:124
nation_type::legend
char * legend
Definition nation.h:106
nation_type::init_techs
int init_techs[MAX_NUM_TECH_LIST]
Definition nation.h:121
output_type
Definition city.h:251
output_type::harvested
bool harvested
Definition city.h:255
output_type::name
const char * name
Definition city.h:253
packet_game_info::celebratesize
int celebratesize
Definition packets_gen.h:140
packet_game_info::borders
enum borders_mode borders
Definition packets_gen.h:133
packet_game_info::add_to_size_limit
int add_to_size_limit
Definition packets_gen.h:120
packet_game_info::killstack
bool killstack
Definition packets_gen.h:188
packet_game_info::nuke_pop_loss_pct
int nuke_pop_loss_pct
Definition packets_gen.h:197
packet_game_info::nuke_defender_survival_chance_pct
int nuke_defender_survival_chance_pct
Definition packets_gen.h:198
packet_game_info::tech_upkeep_style
enum tech_upkeep_style tech_upkeep_style
Definition packets_gen.h:229
packet_game_info::changable_tax
bool changable_tax
Definition packets_gen.h:141
packet_game_info::tired_attack
bool tired_attack
Definition packets_gen.h:235
packet_ruleset_control::version
char version[MAX_LEN_NAME]
Definition packets_gen.h:1482
packet_ruleset_control::num_tech_classes
int num_tech_classes
Definition packets_gen.h:1458
packet_ruleset_control::name
char name[MAX_LEN_NAME]
Definition packets_gen.h:1481
packet_scenario_info::prevent_new_cities
bool prevent_new_cities
Definition packets_gen.h:1611
player
Definition player.h:249
req_context
Definition requirements.h:99
requirement
Definition requirements.h:80
requirement::source
struct universal source
Definition requirements.h:81
research
Definition research.h:39
road_type
Definition road.h:75
road_type::tile_bonus
int tile_bonus[O_LAST]
Definition road.h:82
road_type::tile_incr_const
int tile_incr_const[O_LAST]
Definition road.h:80
road_type::tile_incr
int tile_incr[O_LAST]
Definition road.h:81
road_type::move_cost
int move_cost
Definition road.h:78
section_file
Definition section_file.h:36
specialist
Definition specialist.h:29
specialist::reqs
struct requirement_vector reqs
Definition specialist.h:38
specialist::helptext
struct strvec * helptext
Definition specialist.h:40
strvec
Definition string_vector.c:29
terrain
Definition terrain.h:176
terrain::helptext
struct strvec * helptext
Definition terrain.h:249
terrain::road_time
int road_time
Definition terrain.h:201
terrain::irrigation_food_incr
int irrigation_food_incr
Definition terrain.h:209
terrain::output
int output[O_LAST]
Definition terrain.h:195
terrain::road_output_incr_pct
int road_output_incr_pct[O_LAST]
Definition terrain.h:199
terrain::base_time
int base_time
Definition terrain.h:200
terrain::mining_shield_incr
int mining_shield_incr
Definition terrain.h:212
tileset
Definition tilespec.c:459
unit_class
Definition unittype.h:136
unit_class::helptext
struct strvec * helptext
Definition unittype.h:146
unit_type
Definition unittype.h:481
unit_type::transport_capacity
int transport_capacity
Definition unittype.h:504
unit_type::pop_cost
int pop_cost
Definition unittype.h:493
unit_type::build_reqs
struct requirement_vector build_reqs
Definition unittype.h:501
unit_type::defense_strength
int defense_strength
Definition unittype.h:496
unit_type::paratroopers_range
int paratroopers_range
Definition unittype.h:522
unit_type::convert_time
int convert_time
Definition unittype.h:512
unit_type::city_size
int city_size
Definition unittype.h:531
unit_type::veteran
struct veteran_system * veteran
Definition unittype.h:525
unit_type::obsoleted_by
const struct unit_type * obsoleted_by
Definition unittype.h:510
unit_type::targets
bv_unit_classes targets
Definition unittype.h:542
unit_type::vlayer
enum vision_layer vlayer
Definition unittype.h:550
unit_type::require_advance
struct advance * require_advance
Definition unittype.h:500
unit_type::helptext
struct strvec * helptext
Definition unittype.h:552
unit_type::bombard_rate
int bombard_rate
Definition unittype.h:528
unit_type::upkeep
int upkeep[O_LAST]
Definition unittype.h:519
unit_type::disembarks
bv_unit_classes disembarks
Definition unittype.h:548
unit_type::converted_to
const struct unit_type * converted_to
Definition unittype.h:511
unit_type::embarks
bv_unit_classes embarks
Definition unittype.h:545
unit_type::happy_cost
int happy_cost
Definition unittype.h:518
unit_type::bonuses
struct combat_bonus_list * bonuses
Definition unittype.h:507
universal
Definition fc_types.h:756
universal::kind
enum universals_n kind
Definition fc_types.h:758
universal::value
universals_u value
Definition fc_types.h:757
veteran_level
Definition unittype.h:467
veteran_level::name
struct name_translation name
Definition unittype.h:468
veteran_system
Definition unittype.h:475
veteran_system::definitions
struct veteran_level * definitions
Definition unittype.h:478
veteran_system::levels
int levels
Definition unittype.h:476
fc_snprintf
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:969
fc_strlcpy
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:787
fc_strcasecmp
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:189
fc_strlcat
size_t fc_strlcat(char *dest, const char *src, size_t n)
Definition support.c:832
cat_snprintf
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:995
support.h
sz_strlcpy
#define sz_strlcpy(dest, src)
Definition support.h:161
TRUE
#define TRUE
Definition support.h:46
FALSE
#define FALSE
Definition support.h:47
sz_strlcat
#define sz_strlcat(dest, src)
Definition support.h:162
fc__fallthrough
#define fc__fallthrough
Definition support.h:109
advance_by_number
struct advance * advance_by_number(const Tech_type_id atype)
Definition tech.c:107
advance_has_flag
bool advance_has_flag(Tech_type_id tech, enum tech_flag_id flag)
Definition tech.c:216
tech_class_name_translation
const char * tech_class_name_translation(const struct tech_class *ptclass)
Definition tech.c:333
advance_requires
struct advance * advance_requires(const struct advance *padvance, enum tech_req require)
Definition tech.c:136
advance_name_translation
const char * advance_name_translation(const struct advance *padvance)
Definition tech.c:290
valid_advance_by_number
struct advance * valid_advance_by_number(const Tech_type_id id)
Definition tech.c:176
techs_have_fixed_costs
bool techs_have_fixed_costs(void)
Definition tech.c:450
tech_flag_helptxt
const char * tech_flag_helptxt(enum tech_flag_id id)
Definition tech.c:435
advance_number
Tech_type_id advance_number(const struct advance *padvance)
Definition tech.c:98
advance_index_iterate_end
#define advance_index_iterate_end
Definition tech.h:248
AR_ROOT
@ AR_ROOT
Definition tech.h:113
A_FIRST
#define A_FIRST
Definition tech.h:44
A_NONE
#define A_NONE
Definition tech.h:43
advance_root_req_iterate_end
#define advance_root_req_iterate_end
Definition tech.h:313
A_LAST
#define A_LAST
Definition tech.h:45
advance_index_iterate
#define advance_index_iterate(_start, _index)
Definition tech.h:244
advance_root_req_iterate
#define advance_root_req_iterate(_goal, _padvance)
Definition tech.h:308
terrain_name_translation
const char * terrain_name_translation(const struct terrain *pterrain)
Definition terrain.c:226
terrain_rule_name
const char * terrain_rule_name(const struct terrain *pterrain)
Definition terrain.c:235
terrain_type_terrain_class
enum terrain_class terrain_type_terrain_class(const struct terrain *pterrain)
Definition terrain.c:570
terrain_flag_helptxt
const char * terrain_flag_helptxt(enum terrain_flag_id id)
Definition terrain.c:817
terrain_extra_build_time
int terrain_extra_build_time(const struct terrain *pterrain, enum unit_activity activity, const struct extra_type *tgt)
Definition terrain.c:682
terrain_type_iterate
#define terrain_type_iterate(_p)
Definition terrain.h:358
T_NONE
#define T_NONE
Definition terrain.h:56
terrain_type_iterate_end
#define terrain_type_iterate_end
Definition terrain.h:364
terrain_has_flag
#define terrain_has_flag(terr, flag)
Definition terrain.h:269
tileset_description
const char * tileset_description(struct tileset *t)
Definition tilespec.c:7239
tileset_summary
const char * tileset_summary(struct tileset *t)
Definition tilespec.c:7231
tileset_name_get
const char * tileset_name_get(struct tileset *t)
Definition tilespec.c:7215
tileset_version
const char * tileset_version(struct tileset *t)
Definition tilespec.c:7223
tilespec.h
goods_name_translation
const char * goods_name_translation(struct goods_type *pgood)
Definition traderoutes.c:612
goods_type_iterate_end
#define goods_type_iterate_end
Definition traderoutes.h:223
goods_type_iterate
#define goods_type_iterate(_p)
Definition traderoutes.h:217
universals_u::diplrel
int diplrel
Definition fc_types.h:629
universals_u::building
const struct impr_type * building
Definition fc_types.h:598
universals_u::outputtype
Output_type_id outputtype
Definition fc_types.h:620
unit.h
uclass_name_translation
const char * uclass_name_translation(const struct unit_class *pclass)
Definition unittype.c:1684
utype_action_takes_all_mp
bool utype_action_takes_all_mp(const struct unit_type *putype, struct action *paction)
Definition unittype.c:1243
utype_can_freely_unload
bool utype_can_freely_unload(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Definition unittype.c:294
unit_class_flag_helptxt
const char * unit_class_flag_helptxt(enum unit_class_flag_id id)
Definition unittype.c:1906
uclass_count
Unit_Class_id uclass_count(void)
Definition unittype.c:2493
get_role_unit
struct unit_type * get_role_unit(int role, int role_index)
Definition unittype.c:2301
utype_build_shield_cost_base
int utype_build_shield_cost_base(const struct unit_type *punittype)
Definition unittype.c:1520
utype_veteran_system
const struct veteran_system * utype_veteran_system(const struct unit_type *punittype)
Definition unittype.c:2615
num_role_units
int num_role_units(int role)
Definition unittype.c:2251
utype_can_freely_load
bool utype_can_freely_load(const struct unit_type *pcargotype, const struct unit_type *ptranstype)
Definition unittype.c:282
utype_count
Unit_type_id utype_count(void)
Definition unittype.c:80
utype_veteran_levels
int utype_veteran_levels(const struct unit_type *punittype)
Definition unittype.c:2661
utype_can_do_action_result
bool utype_can_do_action_result(const struct unit_type *putype, enum action_result result)
Definition unittype.c:459
utype_action_takes_all_mp_if_ustate_is
bool utype_action_takes_all_mp_if_ustate_is(const struct unit_type *putype, struct action *paction, const enum ustate_prop prop)
Definition unittype.c:1259
utype_veteran_level
const struct veteran_level * utype_veteran_level(const struct unit_type *punittype, int level)
Definition unittype.c:2631
utype_is_consumed_by_action
bool utype_is_consumed_by_action(const struct action *paction, const struct unit_type *utype)
Definition unittype.c:1271
utype_veteran_has_power_bonus
bool utype_veteran_has_power_bonus(const struct unit_type *punittype)
Definition unittype.c:2674
unit_type_flag_helptxt
const char * unit_type_flag_helptxt(enum unit_type_flag_id id)
Definition unittype.c:1969
utype_name_translation
const char * utype_name_translation(const struct unit_type *punittype)
Definition unittype.c:1612
can_utype_do_act_if_tgt_diplrel
bool can_utype_do_act_if_tgt_diplrel(const struct unit_type *punit_type, const action_id act_id, const int prop, const bool is_there)
Definition unittype.c:1069
utype_can_do_action
bool utype_can_do_action(const struct unit_type *putype, const action_id act_id)
Definition unittype.c:443
UCF_LAST_USER_FLAG
#define UCF_LAST_USER_FLAG
Definition unittype.h:112
uclass_has_flag
static bool uclass_has_flag(const struct unit_class *punitclass, enum unit_class_flag_id flag)
Definition unittype.h:753
utype_class
#define utype_class(_t_)
Definition unittype.h:736
utype_fuel
#define utype_fuel(ptype)
Definition unittype.h:822
combat_bonus_list_iterate_end
#define combat_bonus_list_iterate_end
Definition unittype.h:463
combat_bonus_list_iterate
#define combat_bonus_list_iterate(bonuslist, pbonus)
Definition unittype.h:461
unit_class_iterate
#define unit_class_iterate(_p)
Definition unittype.h:876
utype_has_flag
static bool utype_has_flag(const struct unit_type *punittype, int flag)
Definition unittype.h:604
UTYF_LAST_USER_FLAG
#define UTYF_LAST_USER_FLAG
Definition unittype.h:315
unit_type_iterate
#define unit_type_iterate(_p)
Definition unittype.h:838
uclass_index
#define uclass_index(_c_)
Definition unittype.h:729
unit_class_iterate_end
#define unit_class_iterate_end
Definition unittype.h:883
unit_type_iterate_end
#define unit_type_iterate_end
Definition unittype.h:845
freeciv_name_version
const char * freeciv_name_version(void)
Definition version.c:35
version.h