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