Rulesave saves trade.type and trade.bonus correctly.
[freeciv.git] / client / helpdata.c
blobcf907767be994188e133a92a5b3cd86dc2ef8f8d
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.
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 ***********************************************************************/
14 /***********************************************************************
15 This module is for generic handling of help data, independent
16 of gui considerations.
17 ***********************************************************************/
19 #ifdef HAVE_CONFIG_H
20 #include <fc_config.h>
21 #endif
23 #include <stdio.h>
24 #include <string.h>
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"
37 /* common */
38 #include "achievements.h"
39 #include "actions.h"
40 #include "calendar.h"
41 #include "city.h"
42 #include "effects.h"
43 #include "game.h"
44 #include "government.h"
45 #include "map.h"
46 #include "movement.h"
47 #include "multipliers.h"
48 #include "requirements.h"
49 #include "research.h"
50 #include "road.h"
51 #include "specialist.h"
52 #include "tilespec.h"
53 #include "unit.h"
54 #include "version.h"
56 /* client */
57 #include "client_main.h"
58 #include "climisc.h"
59 #include "gui_main_g.h" /* client_string */
61 #include "helpdata.h"
63 /* helper macro for easy conversion from snprintf and cat_snprintf */
64 #define CATLSTR(_b, _s, _t) fc_strlcat(_b, _t, _s)
66 /* This must be in same order as enum in helpdlg_g.h */
67 static const char * const help_type_names[] = {
68 "(Any)", "(Text)", "Units", "Improvements", "Wonders",
69 "Techs", "Terrain", "Extras", "Specialists", "Governments",
70 "Ruleset", "Tileset", "Nations", "Multipliers", NULL
73 /*define MAX_LAST (MAX(MAX(MAX(A_LAST,B_LAST),U_LAST),terrain_count()))*/
75 #define SPECLIST_TAG help
76 #define SPECLIST_TYPE struct help_item
77 #include "speclist.h"
79 #define help_list_iterate(helplist, phelp) \
80 TYPED_LIST_ITERATE(struct help_item, helplist, phelp)
81 #define help_list_iterate_end LIST_ITERATE_END
83 static const struct help_list_link *help_nodes_iterator;
84 static struct help_list *help_nodes;
85 static bool help_nodes_init = FALSE;
86 /* helpnodes_init is not quite the same as booted in boot_help_texts();
87 latter can be 0 even after call, eg if couldn't find helpdata.txt.
90 /****************************************************************
91 Initialize.
92 *****************************************************************/
93 void helpdata_init(void)
95 help_nodes = help_list_new();
98 /****************************************************************
99 Clean up.
100 *****************************************************************/
101 void helpdata_done(void)
103 help_list_destroy(help_nodes);
106 /****************************************************************
107 Make sure help_nodes is initialised.
108 Should call this just about everywhere which uses help_nodes,
109 to be careful... or at least where called by external
110 (non-static) functions.
111 *****************************************************************/
112 static void check_help_nodes_init(void)
114 if (!help_nodes_init) {
115 help_nodes_init = TRUE; /* before help_iter_start to avoid recursion! */
116 help_iter_start();
120 /****************************************************************
121 Free all allocations associated with help_nodes.
122 *****************************************************************/
123 void free_help_texts(void)
125 check_help_nodes_init();
126 help_list_iterate(help_nodes, ptmp) {
127 free(ptmp->topic);
128 free(ptmp->text);
129 free(ptmp);
130 } help_list_iterate_end;
131 help_list_clear(help_nodes);
134 /****************************************************************************
135 Returns whether we should show help for this nation.
136 ****************************************************************************/
137 static bool show_help_for_nation(const struct nation_type *pnation)
139 return client_nation_is_in_current_set(pnation);
142 /****************************************************************************
143 Insert fixed-width table describing veteran system.
144 If only one veteran level, inserts 'nolevels' if non-NULL.
145 Otherwise, insert 'intro' then a table.
146 ****************************************************************************/
147 static bool insert_veteran_help(char *outbuf, size_t outlen,
148 const struct veteran_system *veteran,
149 const char *intro, const char *nolevels)
151 /* game.veteran can be NULL in pregame; if so, keep quiet about
152 * veteran levels */
153 if (!veteran) {
154 return FALSE;
157 fc_assert_ret_val(veteran->levels >= 1, FALSE);
159 if (veteran->levels == 1) {
160 /* Only a single veteran level. Don't bother to name it. */
161 if (nolevels) {
162 CATLSTR(outbuf, outlen, nolevels);
163 return TRUE;
164 } else {
165 return FALSE;
167 } else {
168 int i;
169 fc_assert_ret_val(veteran->definitions != NULL, FALSE);
170 if (intro) {
171 CATLSTR(outbuf, outlen, intro);
172 CATLSTR(outbuf, outlen, "\n\n");
174 /* raise_chance and work_raise_chance don't get to the client, so we
175 * can't report them */
176 CATLSTR(outbuf, outlen,
177 /* TRANS: Header for fixed-width veteran level table.
178 * TRANS: Translators cannot change column widths :(
179 * TRANS: "Level name" left-justified, other two right-justified */
180 _("Veteran level Power factor Move bonus\n"));
181 CATLSTR(outbuf, outlen,
182 /* TRANS: Part of header for veteran level table. */
183 _("--------------------------------------------"));
184 for (i = 0; i < veteran->levels; i++) {
185 const struct veteran_level *level = &veteran->definitions[i];
186 const char *name = name_translation_get(&level->name);
187 /* Use get_internal_string_length() for correct alignment with
188 * multibyte character encodings */
189 cat_snprintf(outbuf, outlen,
190 "\n%s%*s %4d%% %12s",
191 name, MAX(0, 25 - (int)get_internal_string_length(name)), "",
192 level->power_fact,
193 /* e.g. "- ", "+ 1/3", "+ 1 ", "+ 2 2/3" */
194 move_points_text_full(level->move_bonus, TRUE, "+ ", "-", TRUE));
196 return TRUE;
200 /****************************************************************************
201 Insert generated text for the helpdata "name".
202 Returns TRUE if anything was added.
203 ****************************************************************************/
204 static bool insert_generated_text(char *outbuf, size_t outlen, const char *name)
206 if (!game.client.ruleset_init) {
207 return FALSE;
210 if (0 == strcmp(name, "TerrainAlterations")) {
211 int clean_pollution_time = -1, clean_fallout_time = -1;
212 bool terrain_independent_extras = FALSE;
214 CATLSTR(outbuf, outlen,
215 /* TRANS: Header for fixed-width terrain alteration table.
216 * TRANS: Translators cannot change column widths :( */
217 _("Terrain Irrigation Mining Transform\n"));
218 CATLSTR(outbuf, outlen,
219 "----------------------------------------------------------------\n");
220 terrain_type_iterate(pterrain) {
221 if (0 != strlen(terrain_rule_name(pterrain))) {
222 char irrigation_time[4], mining_time[4], transform_time[4];
223 const char *terrain, *irrigation_result,
224 *mining_result,*transform_result;
225 struct universal for_terr = { .kind = VUT_TERRAIN, .value = { .terrain = pterrain }};
227 fc_snprintf(irrigation_time, sizeof(irrigation_time),
228 "%d", pterrain->irrigation_time);
229 fc_snprintf(mining_time, sizeof(mining_time),
230 "%d", pterrain->mining_time);
231 fc_snprintf(transform_time, sizeof(transform_time),
232 "%d", pterrain->transform_time);
233 terrain = terrain_name_translation(pterrain);
234 irrigation_result =
235 (pterrain->irrigation_result == pterrain
236 || pterrain->irrigation_result == T_NONE
237 || effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) <= 0) ? ""
238 : terrain_name_translation(pterrain->irrigation_result);
239 mining_result =
240 (pterrain->mining_result == pterrain
241 || pterrain->mining_result == T_NONE
242 || effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) <= 0) ? ""
243 : terrain_name_translation(pterrain->mining_result);
244 transform_result =
245 (pterrain->transform_result == pterrain
246 || pterrain->transform_result == T_NONE
247 || effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) <= 0) ? ""
248 : terrain_name_translation(pterrain->transform_result);
249 /* Use get_internal_string_length() for correct alignment with
250 * multibyte character encodings */
251 cat_snprintf(outbuf, outlen,
252 "%s%*s %3s %s%*s %3s %s%*s %3s %s\n",
253 terrain,
254 MAX(0, 12 - (int)get_internal_string_length(terrain)), "",
255 (pterrain->irrigation_result == T_NONE) ? "-" : irrigation_time,
256 irrigation_result,
257 MAX(0, 12 - (int)get_internal_string_length(irrigation_result)), "",
258 (pterrain->mining_result == T_NONE) ? "-" : mining_time,
259 mining_result,
260 MAX(0, 12 - (int)get_internal_string_length(mining_result)), "",
261 (pterrain->transform_result == T_NONE) ? "-" : transform_time,
262 transform_result);
264 if (clean_pollution_time != 0) {
265 if (clean_pollution_time < 0) {
266 clean_pollution_time = pterrain->clean_pollution_time;
267 } else {
268 if (clean_pollution_time != pterrain->clean_pollution_time) {
269 clean_pollution_time = 0; /* give up */
273 if (clean_fallout_time != 0) {
274 if (clean_fallout_time < 0) {
275 clean_fallout_time = pterrain->clean_fallout_time;
276 } else {
277 if (clean_fallout_time != pterrain->clean_fallout_time) {
278 clean_fallout_time = 0; /* give up */
283 } terrain_type_iterate_end;
285 extra_type_by_cause_iterate(EC_BASE, pextra) {
286 if (pextra->buildable && pextra->build_time > 0) {
287 terrain_independent_extras = TRUE;
288 break;
290 } extra_type_by_cause_iterate_end;
291 if (!terrain_independent_extras) {
292 extra_type_by_cause_iterate(EC_ROAD, pextra) {
293 if (pextra->buildable && pextra->build_time > 0) {
294 terrain_independent_extras = TRUE;
295 break;
297 } extra_type_by_cause_iterate_end;
300 if (clean_pollution_time > 0 || clean_fallout_time > 0
301 || terrain_independent_extras) {
302 CATLSTR(outbuf, outlen, "\n");
303 CATLSTR(outbuf, outlen,
304 _("Time taken for the following activities is independent of "
305 "terrain:\n"));
306 CATLSTR(outbuf, outlen, "\n");
307 CATLSTR(outbuf, outlen,
308 /* TRANS: Header for fixed-width terrain alteration table.
309 * TRANS: Translators cannot change column widths :( */
310 _("Activity Time\n"));
311 CATLSTR(outbuf, outlen,
312 "---------------------------");
313 if (clean_pollution_time > 0)
314 cat_snprintf(outbuf, outlen,
315 _("\nClean pollution %3d"), clean_pollution_time);
316 if (clean_fallout_time > 0)
317 cat_snprintf(outbuf, outlen,
318 _("\nClean fallout %3d"), clean_fallout_time);
319 extra_type_by_cause_iterate(EC_ROAD, pextra) {
320 if (pextra->buildable && pextra->build_time > 0) {
321 const char *rname = extra_name_translation(pextra);
323 cat_snprintf(outbuf, outlen,
324 "\n%s%*s %3d",
325 rname,
326 MAX(0, 18 - (int)get_internal_string_length(rname)), "",
327 pextra->build_time);
329 } extra_type_by_cause_iterate_end;
330 extra_type_by_cause_iterate(EC_BASE, pextra) {
331 if (pextra->buildable && pextra->build_time > 0) {
332 const char *bname = extra_name_translation(pextra);
334 cat_snprintf(outbuf, outlen,
335 "\n%s%*s %3d",
336 bname,
337 MAX(0, 18 - (int)get_internal_string_length(bname)), "",
338 pextra->build_time);
340 } extra_type_by_cause_iterate_end;
342 return TRUE;
343 } else if (0 == strcmp(name, "VeteranLevels")) {
344 return insert_veteran_help(outbuf, outlen, game.veteran,
345 _("In this ruleset, the following veteran levels are defined:"),
346 _("This ruleset has no default veteran levels defined."));
347 } else if (0 == strcmp(name, "FreecivVersion")) {
348 const char *ver = freeciv_name_version();
350 cat_snprintf(outbuf, outlen,
351 /* TRANS: First %s is version string, e.g.,
352 * "Freeciv version 2.3.0-beta1 (beta version)" (translated).
353 * Second %s is client_string, e.g., "gui-gtk-2.0". */
354 _("This is %s, %s client."), ver, client_string);
355 insert_client_build_info(outbuf, outlen);
357 return TRUE;
358 } else if (0 == strcmp(name, "DefaultMetaserver")) {
359 cat_snprintf(outbuf, outlen, " %s", FREECIV_META_URL);
361 return TRUE;
363 log_error("Unknown directive '$%s' in help", name);
364 return FALSE;
367 /****************************************************************
368 Append text for the requirement. Something like
370 "Requires knowledge of the technology Communism.\n"
372 pplayer may be NULL. Note that it must be updated everytime
373 a new requirement type or range is defined.
374 *****************************************************************/
375 static bool insert_requirement(char *buf, size_t bufsz,
376 struct player *pplayer,
377 const struct requirement *preq)
379 if (preq->quiet) {
380 return FALSE;
383 switch (preq->source.kind) {
384 case VUT_NONE:
385 return FALSE;
387 case VUT_ADVANCE:
388 switch (preq->range) {
389 case REQ_RANGE_PLAYER:
390 if (preq->present) {
391 cat_snprintf(buf, bufsz,
392 _("Requires knowledge of the technology %s.\n"),
393 advance_name_translation(preq->source.value.advance));
394 } else {
395 cat_snprintf(buf, bufsz,
396 _("Prevented by knowledge of the technology %s.\n"),
397 advance_name_translation(preq->source.value.advance));
399 return TRUE;
400 case REQ_RANGE_TEAM:
401 if (preq->present) {
402 cat_snprintf(buf, bufsz,
403 _("Requires that a player on your team knows the "
404 "technology %s.\n"),
405 advance_name_translation(preq->source.value.advance));
406 } else {
407 cat_snprintf(buf, bufsz,
408 _("Prevented if any player on your team knows the "
409 "technology %s.\n"),
410 advance_name_translation(preq->source.value.advance));
412 return TRUE;
413 case REQ_RANGE_ALLIANCE:
414 if (preq->present) {
415 cat_snprintf(buf, bufsz,
416 _("Requires that a player allied to you knows the "
417 "technology %s.\n"),
418 advance_name_translation(preq->source.value.advance));
419 } else {
420 cat_snprintf(buf, bufsz,
421 _("Prevented if any player allied to you knows the "
422 "technology %s.\n"),
423 advance_name_translation(preq->source.value.advance));
425 return TRUE;
426 case REQ_RANGE_WORLD:
427 if (preq->survives) {
428 if (preq->present) {
429 cat_snprintf(buf, bufsz,
430 _("Requires that someone has discovered the "
431 "technology %s.\n"),
432 advance_name_translation(preq->source.value.advance));
433 } else {
434 cat_snprintf(buf, bufsz,
435 _("Requires that no-one has yet discovered the "
436 "technology %s.\n"),
437 advance_name_translation(preq->source.value.advance));
439 } else {
440 if (preq->present) {
441 cat_snprintf(buf, bufsz,
442 _("Requires that some player knows the "
443 "technology %s.\n"),
444 advance_name_translation(preq->source.value.advance));
445 } else {
446 cat_snprintf(buf, bufsz,
447 _("Requires that no player knows the "
448 "technology %s.\n"),
449 advance_name_translation(preq->source.value.advance));
452 return TRUE;
453 case REQ_RANGE_LOCAL:
454 case REQ_RANGE_CADJACENT:
455 case REQ_RANGE_ADJACENT:
456 case REQ_RANGE_CITY:
457 case REQ_RANGE_TRADEROUTE:
458 case REQ_RANGE_CONTINENT:
459 case REQ_RANGE_COUNT:
460 /* Not supported. */
461 break;
463 break;
465 case VUT_TECHFLAG:
466 switch (preq->range) {
467 case REQ_RANGE_PLAYER:
468 if (preq->present) {
469 cat_snprintf(buf, bufsz,
470 /* TRANS: %s is a (translatable) tech flag. */
471 _("Requires knowledge of a technology with the "
472 "\"%s\" flag.\n"),
473 tech_flag_id_translated_name(preq->source.value.techflag));
474 } else {
475 cat_snprintf(buf, bufsz,
476 /* TRANS: %s is a (translatable) tech flag. */
477 _("Prevented by knowledge of any technology with the "
478 "\"%s\" flag.\n"),
479 tech_flag_id_translated_name(preq->source.value.techflag));
481 return TRUE;
482 case REQ_RANGE_TEAM:
483 if (preq->present) {
484 cat_snprintf(buf, bufsz,
485 /* TRANS: %s is a (translatable) tech flag. */
486 _("Requires that a player on your team knows "
487 "a technology with the \"%s\" flag.\n"),
488 tech_flag_id_translated_name(preq->source.value.techflag));
489 } else {
490 cat_snprintf(buf, bufsz,
491 /* TRANS: %s is a (translatable) tech flag. */
492 _("Prevented if any player on your team knows "
493 "any technology with the \"%s\" flag.\n"),
494 tech_flag_id_translated_name(preq->source.value.techflag));
496 return TRUE;
497 case REQ_RANGE_ALLIANCE:
498 if (preq->present) {
499 cat_snprintf(buf, bufsz,
500 /* TRANS: %s is a (translatable) tech flag. */
501 _("Requires that a player allied to you knows "
502 "a technology with the \"%s\" flag.\n"),
503 tech_flag_id_translated_name(preq->source.value.techflag));
504 } else {
505 cat_snprintf(buf, bufsz,
506 /* TRANS: %s is a (translatable) tech flag. */
507 _("Prevented if any player allied to you knows "
508 "any technology with the \"%s\" flag.\n"),
509 tech_flag_id_translated_name(preq->source.value.techflag));
511 return TRUE;
512 case REQ_RANGE_WORLD:
513 if (preq->present) {
514 cat_snprintf(buf, bufsz,
515 /* TRANS: %s is a (translatable) tech flag. */
516 _("Requires that some player knows a technology "
517 "with the \"%s\" flag.\n"),
518 tech_flag_id_translated_name(preq->source.value.techflag));
519 } else {
520 cat_snprintf(buf, bufsz,
521 /* TRANS: %s is a (translatable) tech flag. */
522 _("Requires that no player knows any technology with "
523 "the \"%s\" flag.\n"),
524 tech_flag_id_translated_name(preq->source.value.techflag));
526 return TRUE;
527 case REQ_RANGE_LOCAL:
528 case REQ_RANGE_CADJACENT:
529 case REQ_RANGE_ADJACENT:
530 case REQ_RANGE_CITY:
531 case REQ_RANGE_TRADEROUTE:
532 case REQ_RANGE_CONTINENT:
533 case REQ_RANGE_COUNT:
534 /* Not supported. */
535 break;
537 break;
539 case VUT_GOVERNMENT:
540 if (preq->range != REQ_RANGE_PLAYER) {
541 break;
543 if (preq->present) {
544 cat_snprintf(buf, bufsz, _("Requires the %s government.\n"),
545 government_name_translation(preq->source.value.govern));
546 } else {
547 cat_snprintf(buf, bufsz, _("Not available under the %s government.\n"),
548 government_name_translation(preq->source.value.govern));
550 return TRUE;
552 case VUT_ACHIEVEMENT:
553 switch (preq->range) {
554 case REQ_RANGE_PLAYER:
555 if (preq->present) {
556 cat_snprintf(buf, bufsz, _("Requires you to have achieved \"%s\".\n"),
557 achievement_name_translation(preq->source.value.achievement));
558 } else {
559 cat_snprintf(buf, bufsz, _("Not available once you have achieved "
560 "\"%s\".\n"),
561 achievement_name_translation(preq->source.value.achievement));
563 return TRUE;
564 case REQ_RANGE_TEAM:
565 if (preq->present) {
566 cat_snprintf(buf, bufsz, _("Requires that at least one of your "
567 "team-mates has achieved \"%s\".\n"),
568 achievement_name_translation(preq->source.value.achievement));
569 } else {
570 cat_snprintf(buf, bufsz, _("Not available if any of your team-mates "
571 "has achieved \"%s\".\n"),
572 achievement_name_translation(preq->source.value.achievement));
574 return TRUE;
575 case REQ_RANGE_ALLIANCE:
576 if (preq->present) {
577 cat_snprintf(buf, bufsz, _("Requires that at least one of your allies "
578 "has achieved \"%s\".\n"),
579 achievement_name_translation(preq->source.value.achievement));
580 } else {
581 cat_snprintf(buf, bufsz, _("Not available if any of your allies has "
582 "achieved \"%s\".\n"),
583 achievement_name_translation(preq->source.value.achievement));
585 return TRUE;
586 case REQ_RANGE_WORLD:
587 if (preq->present) {
588 cat_snprintf(buf, bufsz, _("Requires that at least one player "
589 "has achieved \"%s\".\n"),
590 achievement_name_translation(preq->source.value.achievement));
591 } else {
592 cat_snprintf(buf, bufsz, _("Not available if any player has "
593 "achieved \"%s\".\n"),
594 achievement_name_translation(preq->source.value.achievement));
596 return TRUE;
597 case REQ_RANGE_LOCAL:
598 case REQ_RANGE_CADJACENT:
599 case REQ_RANGE_ADJACENT:
600 case REQ_RANGE_CITY:
601 case REQ_RANGE_TRADEROUTE:
602 case REQ_RANGE_CONTINENT:
603 case REQ_RANGE_COUNT:
604 /* Not supported. */
605 break;
607 break;
609 case VUT_IMPROVEMENT:
610 switch (preq->range) {
611 case REQ_RANGE_WORLD:
612 if (is_great_wonder(preq->source.value.building)) {
613 if (preq->survives) {
614 if (preq->present) {
615 if (can_improvement_go_obsolete(preq->source.value.building)) {
616 cat_snprintf(buf, bufsz,
617 /* TRANS: %s is a wonder */
618 _("Requires that %s was built at some point, "
619 "and that it has not yet been rendered "
620 "obsolete.\n"),
621 improvement_name_translation
622 (preq->source.value.building));
623 } else {
624 cat_snprintf(buf, bufsz,
625 /* TRANS: %s is a wonder */
626 _("Requires that %s was built at some point.\n"),
627 improvement_name_translation
628 (preq->source.value.building));
630 } else {
631 if (can_improvement_go_obsolete(preq->source.value.building)) {
632 cat_snprintf(buf, bufsz,
633 /* TRANS: %s is a wonder */
634 _("Prevented if %s has ever been built, "
635 "unless it would be obsolete.\n"),
636 improvement_name_translation
637 (preq->source.value.building));
638 } else {
639 cat_snprintf(buf, bufsz,
640 /* TRANS: %s is a wonder */
641 _("Prevented if %s has ever been built.\n"),
642 improvement_name_translation
643 (preq->source.value.building));
646 } else {
647 /* Non-surviving requirement */
648 if (preq->present) {
649 if (can_improvement_go_obsolete(preq->source.value.building)) {
650 cat_snprintf(buf, bufsz,
651 /* TRANS: %s is a wonder */
652 _("Requires %s to be owned by any player "
653 "and not yet obsolete.\n"),
654 improvement_name_translation
655 (preq->source.value.building));
656 } else {
657 cat_snprintf(buf, bufsz,
658 /* TRANS: %s is a wonder */
659 _("Requires %s to be owned by any player.\n"),
660 improvement_name_translation
661 (preq->source.value.building));
663 } else {
664 if (can_improvement_go_obsolete(preq->source.value.building)) {
665 cat_snprintf(buf, bufsz,
666 /* TRANS: %s is a wonder */
667 _("Prevented if %s is currently owned by "
668 "any player, unless it is obsolete.\n"),
669 improvement_name_translation
670 (preq->source.value.building));
671 } else {
672 cat_snprintf(buf, bufsz,
673 /* TRANS: %s is a wonder */
674 _("Prevented if %s is currently owned by "
675 "any player.\n"),
676 improvement_name_translation
677 (preq->source.value.building));
681 return TRUE;
683 /* non-great-wonder world-ranged requirements not supported */
684 break;
685 case REQ_RANGE_ALLIANCE:
686 if (is_wonder(preq->source.value.building)) {
687 if (preq->survives) {
688 if (preq->present) {
689 if (can_improvement_go_obsolete(preq->source.value.building)) {
690 cat_snprintf(buf, bufsz,
691 /* TRANS: %s is a wonder */
692 _("Requires someone who is currently allied to "
693 "you to have built %s at some point, and for "
694 "it not to have been rendered obsolete.\n"),
695 improvement_name_translation
696 (preq->source.value.building));
697 } else {
698 cat_snprintf(buf, bufsz,
699 /* TRANS: %s is a wonder */
700 _("Requires someone who is currently allied to "
701 "you to have built %s at some point.\n"),
702 improvement_name_translation
703 (preq->source.value.building));
705 } else {
706 if (can_improvement_go_obsolete(preq->source.value.building)) {
707 cat_snprintf(buf, bufsz,
708 /* TRANS: %s is a wonder */
709 _("Prevented if someone currently allied to you "
710 "has ever built %s, unless it would be "
711 "obsolete.\n"),
712 improvement_name_translation
713 (preq->source.value.building));
714 } else {
715 cat_snprintf(buf, bufsz,
716 /* TRANS: %s is a wonder */
717 _("Prevented if someone currently allied to you "
718 "has ever built %s.\n"),
719 improvement_name_translation
720 (preq->source.value.building));
723 } else {
724 /* Non-surviving requirement */
725 if (preq->present) {
726 if (can_improvement_go_obsolete(preq->source.value.building)) {
727 cat_snprintf(buf, bufsz,
728 /* TRANS: %s is a wonder */
729 _("Requires someone allied to you to own %s, "
730 "and for it not to have been rendered "
731 "obsolete.\n"),
732 improvement_name_translation
733 (preq->source.value.building));
734 } else {
735 cat_snprintf(buf, bufsz,
736 /* TRANS: %s is a wonder */
737 _("Requires someone allied to you to own %s.\n"),
738 improvement_name_translation
739 (preq->source.value.building));
741 } else {
742 if (can_improvement_go_obsolete(preq->source.value.building)) {
743 cat_snprintf(buf, bufsz,
744 /* TRANS: %s is a wonder */
745 _("Prevented if someone allied to you owns %s, "
746 "unless it is obsolete.\n"),
747 improvement_name_translation
748 (preq->source.value.building));
749 } else {
750 cat_snprintf(buf, bufsz,
751 /* TRANS: %s is a wonder */
752 _("Prevented if someone allied to you owns %s.\n"),
753 improvement_name_translation
754 (preq->source.value.building));
758 return TRUE;
760 /* non-wonder alliance-ranged requirements not supported */
761 break;
762 case REQ_RANGE_TEAM:
763 if (is_wonder(preq->source.value.building)) {
764 if (preq->survives) {
765 if (preq->present) {
766 if (can_improvement_go_obsolete(preq->source.value.building)) {
767 cat_snprintf(buf, bufsz,
768 /* TRANS: %s is a wonder */
769 _("Requires someone on your team to have "
770 "built %s at some point, and for it not "
771 "to have been rendered obsolete.\n"),
772 improvement_name_translation
773 (preq->source.value.building));
774 } else {
775 cat_snprintf(buf, bufsz,
776 /* TRANS: %s is a wonder */
777 _("Requires someone on your team to have "
778 "built %s at some point.\n"),
779 improvement_name_translation
780 (preq->source.value.building));
782 } else {
783 if (can_improvement_go_obsolete(preq->source.value.building)) {
784 cat_snprintf(buf, bufsz,
785 /* TRANS: %s is a wonder */
786 _("Prevented if someone on your team has ever "
787 "built %s, unless it would be obsolete.\n"),
788 improvement_name_translation
789 (preq->source.value.building));
790 } else {
791 cat_snprintf(buf, bufsz,
792 /* TRANS: %s is a wonder */
793 _("Prevented if someone on your team has ever "
794 "built %s.\n"),
795 improvement_name_translation
796 (preq->source.value.building));
799 } else {
800 /* Non-surviving requirement */
801 if (preq->present) {
802 if (can_improvement_go_obsolete(preq->source.value.building)) {
803 cat_snprintf(buf, bufsz,
804 /* TRANS: %s is a wonder */
805 _("Requires someone on your team to own %s, "
806 "and for it not to have been rendered "
807 "obsolete.\n"),
808 improvement_name_translation
809 (preq->source.value.building));
810 } else {
811 cat_snprintf(buf, bufsz,
812 /* TRANS: %s is a wonder */
813 _("Requires someone on your team to own %s.\n"),
814 improvement_name_translation
815 (preq->source.value.building));
817 } else {
818 if (can_improvement_go_obsolete(preq->source.value.building)) {
819 cat_snprintf(buf, bufsz,
820 /* TRANS: %s is a wonder */
821 _("Prevented if someone on your team owns %s, "
822 "unless it is obsolete.\n"),
823 improvement_name_translation
824 (preq->source.value.building));
825 } else {
826 cat_snprintf(buf, bufsz,
827 /* TRANS: %s is a wonder */
828 _("Prevented if someone on your team owns %s.\n"),
829 improvement_name_translation
830 (preq->source.value.building));
834 return TRUE;
836 /* non-wonder team-ranged requirements not supported */
837 break;
838 case REQ_RANGE_PLAYER:
839 if (is_wonder(preq->source.value.building)) {
840 if (preq->survives) {
841 if (preq->present) {
842 if (can_improvement_go_obsolete(preq->source.value.building)) {
843 cat_snprintf(buf, bufsz,
844 /* TRANS: %s is a wonder */
845 _("Requires you to have built %s at some point, "
846 "and for it not to have been rendered "
847 "obsolete.\n"),
848 improvement_name_translation
849 (preq->source.value.building));
850 } else {
851 cat_snprintf(buf, bufsz,
852 /* TRANS: %s is a wonder */
853 _("Requires you to have built %s at some point.\n"),
854 improvement_name_translation
855 (preq->source.value.building));
857 } else {
858 if (can_improvement_go_obsolete(preq->source.value.building)) {
859 cat_snprintf(buf, bufsz,
860 /* TRANS: %s is a wonder */
861 _("Prevented if you have ever built %s, "
862 "unless it would be obsolete.\n"),
863 improvement_name_translation
864 (preq->source.value.building));
865 } else {
866 cat_snprintf(buf, bufsz,
867 /* TRANS: %s is a wonder */
868 _("Prevented if you have ever built %s.\n"),
869 improvement_name_translation
870 (preq->source.value.building));
873 } else {
874 /* Non-surviving requirement */
875 if (preq->present) {
876 if (can_improvement_go_obsolete(preq->source.value.building)) {
877 cat_snprintf(buf, bufsz,
878 /* TRANS: %s is a wonder */
879 _("Requires you to own %s, which must not "
880 "be obsolete.\n"),
881 improvement_name_translation
882 (preq->source.value.building));
883 } else {
884 cat_snprintf(buf, bufsz,
885 /* TRANS: %s is a wonder */
886 _("Requires you to own %s.\n"),
887 improvement_name_translation
888 (preq->source.value.building));
890 } else {
891 if (can_improvement_go_obsolete(preq->source.value.building)) {
892 cat_snprintf(buf, bufsz,
893 /* TRANS: %s is a wonder */
894 _("Prevented if you own %s, unless it is "
895 "obsolete.\n"),
896 improvement_name_translation
897 (preq->source.value.building));
898 } else {
899 cat_snprintf(buf, bufsz,
900 /* TRANS: %s is a wonder */
901 _("Prevented if you own %s.\n"),
902 improvement_name_translation
903 (preq->source.value.building));
907 return TRUE;
909 /* non-wonder player-ranged requirements not supported */
910 break;
911 case REQ_RANGE_CONTINENT:
912 if (is_wonder(preq->source.value.building)) {
913 if (preq->present) {
914 if (can_improvement_go_obsolete(preq->source.value.building)) {
915 cat_snprintf(buf, bufsz,
916 /* TRANS: %s is a wonder */
917 _("Requires %s in one of your cities on the same "
918 "continent, and not yet obsolete.\n"),
919 improvement_name_translation
920 (preq->source.value.building));
921 } else {
922 cat_snprintf(buf, bufsz,
923 /* TRANS: %s is a wonder */
924 _("Requires %s in one of your cities on the same "
925 "continent.\n"),
926 improvement_name_translation
927 (preq->source.value.building));
929 } else {
930 if (can_improvement_go_obsolete(preq->source.value.building)) {
931 cat_snprintf(buf, bufsz,
932 /* TRANS: %s is a wonder */
933 _("Prevented if %s is in one of your cities on the "
934 "same continent, unless it is obsolete.\n"),
935 improvement_name_translation
936 (preq->source.value.building));
937 } else {
938 cat_snprintf(buf, bufsz,
939 /* TRANS: %s is a wonder */
940 _("Prevented if %s is in one of your cities on the "
941 "same continent.\n"),
942 improvement_name_translation
943 (preq->source.value.building));
946 return TRUE;
948 /* surviving or non-wonder continent-ranged requirements not supported */
949 break;
950 case REQ_RANGE_TRADEROUTE:
951 if (preq->present) {
952 if (can_improvement_go_obsolete(preq->source.value.building)) {
953 /* Should only apply to wonders */
954 cat_snprintf(buf, bufsz,
955 /* TRANS: %s is a building or wonder */
956 _("Requires %s in the city or a trade partner "
957 "(and not yet obsolete).\n"),
958 improvement_name_translation
959 (preq->source.value.building));
960 } else {
961 cat_snprintf(buf, bufsz,
962 /* TRANS: %s is a building or wonder */
963 _("Requires %s in the city or a trade partner.\n"),
964 improvement_name_translation
965 (preq->source.value.building));
967 } else {
968 if (can_improvement_go_obsolete(preq->source.value.building)) {
969 /* Should only apply to wonders */
970 cat_snprintf(buf, bufsz,
971 /* TRANS: %s is a building or wonder */
972 _("Prevented by %s in the city or a trade partner "
973 "(unless it is obsolete).\n"),
974 improvement_name_translation
975 (preq->source.value.building));
976 } else {
977 cat_snprintf(buf, bufsz,
978 /* TRANS: %s is a building or wonder */
979 _("Prevented by %s in the city or a trade partner.\n"),
980 improvement_name_translation
981 (preq->source.value.building));
984 return TRUE;
985 case REQ_RANGE_CITY:
986 if (preq->present) {
987 if (can_improvement_go_obsolete(preq->source.value.building)) {
988 /* Should only apply to wonders */
989 cat_snprintf(buf, bufsz,
990 /* TRANS: %s is a building or wonder */
991 _("Requires %s in the city (and not yet obsolete).\n"),
992 improvement_name_translation
993 (preq->source.value.building));
994 } else {
995 cat_snprintf(buf, bufsz,
996 /* TRANS: %s is a building or wonder */
997 _("Requires %s in the city.\n"),
998 improvement_name_translation
999 (preq->source.value.building));
1001 } else {
1002 if (can_improvement_go_obsolete(preq->source.value.building)) {
1003 /* Should only apply to wonders */
1004 cat_snprintf(buf, bufsz,
1005 /* TRANS: %s is a building or wonder */
1006 _("Prevented by %s in the city (unless it is "
1007 "obsolete).\n"),
1008 improvement_name_translation
1009 (preq->source.value.building));
1010 } else {
1011 cat_snprintf(buf, bufsz,
1012 /* TRANS: %s is a building or wonder */
1013 _("Prevented by %s in the city.\n"),
1014 improvement_name_translation
1015 (preq->source.value.building));
1018 return TRUE;
1019 case REQ_RANGE_LOCAL:
1020 if (preq->present) {
1021 cat_snprintf(buf, bufsz,
1022 _("Only applies to \"%s\" buildings.\n"),
1023 improvement_name_translation
1024 (preq->source.value.building));
1025 } else {
1026 cat_snprintf(buf, bufsz,
1027 _("Does not apply to \"%s\" buildings.\n"),
1028 improvement_name_translation
1029 (preq->source.value.building));
1031 return TRUE;
1032 case REQ_RANGE_CADJACENT:
1033 case REQ_RANGE_ADJACENT:
1034 case REQ_RANGE_COUNT:
1035 /* Not supported. */
1036 break;
1038 break;
1040 case VUT_EXTRA:
1041 switch (preq->range) {
1042 case REQ_RANGE_LOCAL:
1043 if (preq->present) {
1044 cat_snprintf(buf, bufsz,
1045 Q_("?extra:Requires %s on the tile.\n"),
1046 extra_name_translation(preq->source.value.extra));
1047 } else {
1048 cat_snprintf(buf, bufsz,
1049 Q_("?extra:Prevented by %s on the tile.\n"),
1050 extra_name_translation(preq->source.value.extra));
1052 return TRUE;
1053 case REQ_RANGE_CADJACENT:
1054 if (preq->present) {
1055 cat_snprintf(buf, bufsz,
1056 Q_("?extra:Requires %s on the tile or a cardinally "
1057 "adjacent tile.\n"),
1058 extra_name_translation(preq->source.value.extra));
1059 } else {
1060 cat_snprintf(buf, bufsz,
1061 Q_("?extra:Prevented by %s on the tile or any cardinally "
1062 "adjacent tile.\n"),
1063 extra_name_translation(preq->source.value.extra));
1065 return TRUE;
1066 case REQ_RANGE_ADJACENT:
1067 if (preq->present) {
1068 cat_snprintf(buf, bufsz,
1069 Q_("?extra:Requires %s on the tile or an adjacent "
1070 "tile.\n"),
1071 extra_name_translation(preq->source.value.extra));
1072 } else {
1073 cat_snprintf(buf, bufsz,
1074 Q_("?extra:Prevented by %s on the tile or any adjacent "
1075 "tile.\n"),
1076 extra_name_translation(preq->source.value.extra));
1078 return TRUE;
1079 case REQ_RANGE_CITY:
1080 if (preq->present) {
1081 cat_snprintf(buf, bufsz,
1082 Q_("?extra:Requires %s on a tile within the city "
1083 "radius.\n"),
1084 extra_name_translation(preq->source.value.extra));
1085 } else {
1086 cat_snprintf(buf, bufsz,
1087 Q_("?extra:Prevented by %s on any tile within the city "
1088 "radius.\n"),
1089 extra_name_translation(preq->source.value.extra));
1091 return TRUE;
1092 case REQ_RANGE_TRADEROUTE:
1093 if (preq->present) {
1094 cat_snprintf(buf, bufsz,
1095 Q_("?extra:Requires %s on a tile within the city "
1096 "radius, or the city radius of a trade partner.\n"),
1097 extra_name_translation(preq->source.value.extra));
1098 } else {
1099 cat_snprintf(buf, bufsz,
1100 Q_("?extra:Prevented by %s on any tile within the city "
1101 "radius or the city radius of a trade partner.\n"),
1102 extra_name_translation(preq->source.value.extra));
1104 return TRUE;
1105 case REQ_RANGE_CONTINENT:
1106 case REQ_RANGE_PLAYER:
1107 case REQ_RANGE_TEAM:
1108 case REQ_RANGE_ALLIANCE:
1109 case REQ_RANGE_WORLD:
1110 case REQ_RANGE_COUNT:
1111 /* Not supported. */
1112 break;
1114 break;
1116 case VUT_TERRAIN:
1117 switch (preq->range) {
1118 case REQ_RANGE_LOCAL:
1119 if (preq->present) {
1120 cat_snprintf(buf, bufsz, Q_("?terrain:Requires %s on the tile.\n"),
1121 terrain_name_translation(preq->source.value.terrain));
1122 } else {
1123 cat_snprintf(buf, bufsz, Q_("?terrain:Prevented by %s on the tile.\n"),
1124 terrain_name_translation(preq->source.value.terrain));
1126 return TRUE;
1127 case REQ_RANGE_CADJACENT:
1128 if (preq->present) {
1129 cat_snprintf(buf, bufsz,
1130 Q_("?terrain:Requires %s on the tile or a cardinally "
1131 "adjacent tile.\n"),
1132 terrain_name_translation(preq->source.value.terrain));
1133 } else {
1134 cat_snprintf(buf, bufsz,
1135 Q_("?terrain:Prevented by %s on the tile or any "
1136 "cardinally adjacent tile.\n"),
1137 terrain_name_translation(preq->source.value.terrain));
1139 return TRUE;
1140 case REQ_RANGE_ADJACENT:
1141 if (preq->present) {
1142 cat_snprintf(buf, bufsz,
1143 Q_("?terrain:Requires %s on the tile or an adjacent "
1144 "tile.\n"),
1145 terrain_name_translation(preq->source.value.terrain));
1146 } else {
1147 cat_snprintf(buf, bufsz,
1148 Q_("?terrain:Prevented by %s on the tile or any "
1149 "adjacent tile.\n"),
1150 terrain_name_translation(preq->source.value.terrain));
1152 return TRUE;
1153 case REQ_RANGE_CITY:
1154 if (preq->present) {
1155 cat_snprintf(buf, bufsz,
1156 Q_("?terrain:Requires %s on a tile within the city "
1157 "radius.\n"),
1158 terrain_name_translation(preq->source.value.terrain));
1159 } else {
1160 cat_snprintf(buf, bufsz,
1161 Q_("?terrain:Prevented by %s on any tile within the city "
1162 "radius.\n"),
1163 terrain_name_translation(preq->source.value.terrain));
1165 return TRUE;
1166 case REQ_RANGE_TRADEROUTE:
1167 if (preq->present) {
1168 cat_snprintf(buf, bufsz,
1169 Q_("?terrain:Requires %s on a tile within the city "
1170 "radius, or the city radius of a trade partner.\n"),
1171 terrain_name_translation(preq->source.value.terrain));
1172 } else {
1173 cat_snprintf(buf, bufsz,
1174 Q_("?terrain:Prevented by %s on any tile within the city "
1175 "radius or the city radius of a trade partner.\n"),
1176 terrain_name_translation(preq->source.value.terrain));
1178 return TRUE;
1179 case REQ_RANGE_CONTINENT:
1180 case REQ_RANGE_PLAYER:
1181 case REQ_RANGE_TEAM:
1182 case REQ_RANGE_ALLIANCE:
1183 case REQ_RANGE_WORLD:
1184 case REQ_RANGE_COUNT:
1185 /* Not supported. */
1186 break;
1188 break;
1190 case VUT_RESOURCE:
1191 switch (preq->range) {
1192 case REQ_RANGE_LOCAL:
1193 if (preq->present) {
1194 cat_snprintf(buf, bufsz,
1195 Q_("?resource:Requires %s on the tile.\n"),
1196 resource_name_translation(preq->source.value.resource));
1197 } else {
1198 cat_snprintf(buf, bufsz,
1199 Q_("?resource:Prevented by %s on the tile.\n"),
1200 resource_name_translation(preq->source.value.resource));
1202 return TRUE;
1203 case REQ_RANGE_CADJACENT:
1204 if (preq->present) {
1205 cat_snprintf(buf, bufsz,
1206 Q_("?resource:Requires %s on the tile or a cardinally "
1207 "adjacent tile.\n"),
1208 resource_name_translation(preq->source.value.resource));
1209 } else {
1210 cat_snprintf(buf, bufsz,
1211 Q_("?resource:Prevented by %s on the tile or any "
1212 "cardinally adjacent tile.\n"),
1213 resource_name_translation(preq->source.value.resource));
1215 return TRUE;
1216 case REQ_RANGE_ADJACENT:
1217 if (preq->present) {
1218 cat_snprintf(buf, bufsz,
1219 Q_("?resource:Requires %s on the tile or an adjacent "
1220 "tile.\n"),
1221 resource_name_translation(preq->source.value.resource));
1222 } else {
1223 cat_snprintf(buf, bufsz,
1224 Q_("?resource:Prevented by %s on the tile or any "
1225 "adjacent tile.\n"),
1226 resource_name_translation(preq->source.value.resource));
1228 return TRUE;
1229 case REQ_RANGE_CITY:
1230 if (preq->present) {
1231 cat_snprintf(buf, bufsz,
1232 Q_("?resource:Requires %s on a tile within the "
1233 "city radius.\n"),
1234 resource_name_translation(preq->source.value.resource));
1235 } else {
1236 cat_snprintf(buf, bufsz,
1237 Q_("?resource:Prevented by %s on any tile within the "
1238 "city radius.\n"),
1239 resource_name_translation(preq->source.value.resource));
1241 return TRUE;
1242 case REQ_RANGE_TRADEROUTE:
1243 if (preq->present) {
1244 cat_snprintf(buf, bufsz,
1245 Q_("?resource:Requires %s on a tile within the "
1246 "city radius or the city radius of a trade partner.\n"),
1247 resource_name_translation(preq->source.value.resource));
1248 } else {
1249 cat_snprintf(buf, bufsz,
1250 Q_("?resource:Prevented by %s on any tile within the "
1251 "city radius or the city radius of a trade partner.\n"),
1252 resource_name_translation(preq->source.value.resource));
1254 return TRUE;
1255 case REQ_RANGE_CONTINENT:
1256 case REQ_RANGE_PLAYER:
1257 case REQ_RANGE_TEAM:
1258 case REQ_RANGE_ALLIANCE:
1259 case REQ_RANGE_WORLD:
1260 case REQ_RANGE_COUNT:
1261 /* Not supported. */
1262 break;
1264 break;
1266 case VUT_NATION:
1267 switch (preq->range) {
1268 case REQ_RANGE_PLAYER:
1269 if (preq->present) {
1270 cat_snprintf(buf, bufsz,
1271 /* TRANS: "... playing as the Swedes." */
1272 _("Requires that you are playing as the %s.\n"),
1273 nation_plural_translation(preq->source.value.nation));
1274 } else {
1275 cat_snprintf(buf, bufsz,
1276 /* TRANS: "... playing as the Turks." */
1277 _("Requires that you are not playing as the %s.\n"),
1278 nation_plural_translation(preq->source.value.nation));
1280 return TRUE;
1281 case REQ_RANGE_TEAM:
1282 if (preq->present) {
1283 cat_snprintf(buf, bufsz,
1284 /* TRANS: "... same team as the Indonesians." */
1285 _("Requires that you are on the same team as "
1286 "the %s.\n"),
1287 nation_plural_translation(preq->source.value.nation));
1288 } else {
1289 cat_snprintf(buf, bufsz,
1290 /* TRANS: "... same team as the Greeks." */
1291 _("Requires that you are not on the same team as "
1292 "the %s.\n"),
1293 nation_plural_translation(preq->source.value.nation));
1295 return TRUE;
1296 case REQ_RANGE_ALLIANCE:
1297 if (preq->present) {
1298 cat_snprintf(buf, bufsz,
1299 /* TRANS: "... allied with the Koreans." */
1300 _("Requires that you are allied with the %s.\n"),
1301 nation_plural_translation(preq->source.value.nation));
1302 } else {
1303 cat_snprintf(buf, bufsz,
1304 /* TRANS: "... allied with the Danes." */
1305 _("Requires that you are not allied with the %s.\n"),
1306 nation_plural_translation(preq->source.value.nation));
1308 return TRUE;
1309 case REQ_RANGE_WORLD:
1310 if (preq->survives) {
1311 if (preq->present) {
1312 cat_snprintf(buf, bufsz,
1313 /* TRANS: "Requires the Apaches to have ..." */
1314 _("Requires the %s to have been in the game.\n"),
1315 nation_plural_translation(preq->source.value.nation));
1316 } else {
1317 cat_snprintf(buf, bufsz,
1318 /* TRANS: "Requires the Celts never to have ..." */
1319 _("Requires the %s never to have been in the "
1320 "game.\n"),
1321 nation_plural_translation(preq->source.value.nation));
1323 } else {
1324 if (preq->present) {
1325 cat_snprintf(buf, bufsz,
1326 /* TRANS: "Requires the Belgians in the game." */
1327 _("Requires the %s in the game.\n"),
1328 nation_plural_translation(preq->source.value.nation));
1329 } else {
1330 cat_snprintf(buf, bufsz,
1331 /* TRANS: "Requires that the Russians are not ... */
1332 _("Requires that the %s are not in the game.\n"),
1333 nation_plural_translation(preq->source.value.nation));
1336 return TRUE;
1337 case REQ_RANGE_LOCAL:
1338 case REQ_RANGE_CADJACENT:
1339 case REQ_RANGE_ADJACENT:
1340 case REQ_RANGE_CITY:
1341 case REQ_RANGE_TRADEROUTE:
1342 case REQ_RANGE_CONTINENT:
1343 case REQ_RANGE_COUNT:
1344 /* Not supported. */
1345 break;
1347 break;
1349 case VUT_NATIONGROUP:
1350 switch (preq->range) {
1351 case REQ_RANGE_PLAYER:
1352 if (preq->present) {
1353 cat_snprintf(buf, bufsz,
1354 /* TRANS: nation group: "... playing African nation." */
1355 _("Requires that you are playing %s nation.\n"),
1356 nation_group_name_translation(preq->source.value.nationgroup));
1357 } else {
1358 cat_snprintf(buf, bufsz,
1359 /* TRANS: nation group: "... playing Imaginary nation." */
1360 _("Prevented if you are playing %s nation.\n"),
1361 nation_group_name_translation(preq->source.value.nationgroup));
1363 return TRUE;
1364 case REQ_RANGE_TEAM:
1365 if (preq->present) {
1366 cat_snprintf(buf, bufsz,
1367 /* TRANS: nation group: "Requires Medieval nation ..." */
1368 _("Requires %s nation on your team.\n"),
1369 nation_group_name_translation(preq->source.value.nationgroup));
1370 } else {
1371 cat_snprintf(buf, bufsz,
1372 /* TRANS: nation group: "Prevented by Medieval nation ..." */
1373 _("Prevented by %s nation on your team.\n"),
1374 nation_group_name_translation(preq->source.value.nationgroup));
1376 return TRUE;
1377 case REQ_RANGE_ALLIANCE:
1378 if (preq->present) {
1379 cat_snprintf(buf, bufsz,
1380 /* TRANS: nation group: "Requires Modern nation ..." */
1381 _("Requires %s nation in alliance with you.\n"),
1382 nation_group_name_translation(preq->source.value.nationgroup));
1383 } else {
1384 cat_snprintf(buf, bufsz,
1385 /* TRANS: nation group: "Prevented by Modern nation ..." */
1386 _("Prevented if %s nation is in alliance with you.\n"),
1387 nation_group_name_translation(preq->source.value.nationgroup));
1389 return TRUE;
1390 case REQ_RANGE_WORLD:
1391 if (preq->present) {
1392 cat_snprintf(buf, bufsz,
1393 /* TRANS: nation group: "Requires Asian nation ..." */
1394 _("Requires %s nation in the game.\n"),
1395 nation_group_name_translation(preq->source.value.nationgroup));
1396 } else {
1397 cat_snprintf(buf, bufsz,
1398 /* TRANS: nation group: "Prevented by Asian nation ..." */
1399 _("Prevented by %s nation in the game.\n"),
1400 nation_group_name_translation(preq->source.value.nationgroup));
1402 return TRUE;
1403 case REQ_RANGE_LOCAL:
1404 case REQ_RANGE_CADJACENT:
1405 case REQ_RANGE_ADJACENT:
1406 case REQ_RANGE_CITY:
1407 case REQ_RANGE_TRADEROUTE:
1408 case REQ_RANGE_CONTINENT:
1409 case REQ_RANGE_COUNT:
1410 /* Not supported. */
1411 break;
1413 break;
1415 case VUT_STYLE:
1416 if (preq->range != REQ_RANGE_PLAYER) {
1417 break;
1419 if (preq->present) {
1420 cat_snprintf(buf, bufsz,
1421 /* TRANS: "Requires that you are playing Asian style
1422 * nation." */
1423 _("Requires that you are playing %s style nation.\n"),
1424 style_name_translation(preq->source.value.style));
1425 } else {
1426 cat_snprintf(buf, bufsz,
1427 /* TRANS: "Requires that you are not playing Classical
1428 * style nation." */
1429 _("Requires that you are not playing %s style nation.\n"),
1430 style_name_translation(preq->source.value.style));
1432 return TRUE;
1434 case VUT_NATIONALITY:
1435 switch (preq->range) {
1436 case REQ_RANGE_TRADEROUTE:
1437 if (preq->present) {
1438 cat_snprintf(buf, bufsz,
1439 /* TRANS: "Requires at least one Barbarian citizen ..." */
1440 _("Requires at least one %s citizen in the city or a "
1441 "trade partner.\n"),
1442 nation_adjective_translation(preq->source.value.nationality));
1443 } else {
1444 cat_snprintf(buf, bufsz,
1445 /* TRANS: "... no Pirate citizens ..." */
1446 _("Requires that there are no %s citizens in "
1447 "the city or any trade partners.\n"),
1448 nation_adjective_translation(preq->source.value.nationality));
1450 return TRUE;
1451 case REQ_RANGE_CITY:
1452 if (preq->present) {
1453 cat_snprintf(buf, bufsz,
1454 /* TRANS: "Requires at least one Barbarian citizen ..." */
1455 _("Requires at least one %s citizen in the city.\n"),
1456 nation_adjective_translation(preq->source.value.nationality));
1457 } else {
1458 cat_snprintf(buf, bufsz,
1459 /* TRANS: "... no Pirate citizens ..." */
1460 _("Requires that there are no %s citizens in "
1461 "the city.\n"),
1462 nation_adjective_translation(preq->source.value.nationality));
1464 return TRUE;
1465 case REQ_RANGE_WORLD:
1466 case REQ_RANGE_ALLIANCE:
1467 case REQ_RANGE_TEAM:
1468 case REQ_RANGE_PLAYER:
1469 case REQ_RANGE_LOCAL:
1470 case REQ_RANGE_CADJACENT:
1471 case REQ_RANGE_ADJACENT:
1472 case REQ_RANGE_CONTINENT:
1473 case REQ_RANGE_COUNT:
1474 /* Not supported. */
1475 break;
1477 break;
1479 case VUT_DIPLREL:
1480 switch (preq->range) {
1481 case REQ_RANGE_PLAYER:
1482 if (preq->present) {
1483 cat_snprintf(buf, bufsz,
1484 /* TRANS: in this and following strings, '%s' can be one
1485 * of a wide range of relationships; e.g., 'Peace',
1486 * 'Never met', 'Is foreign', 'Hosts embassy',
1487 * 'Provided Casus Belli' */
1488 _("Requires that you have the relationship '%s' with at "
1489 "least one other living player.\n"),
1490 diplrel_name_translation(preq->source.value.diplrel));
1491 } else {
1492 cat_snprintf(buf, bufsz,
1493 _("Requires that you do not have the relationship '%s' "
1494 "with any living player.\n"),
1495 diplrel_name_translation(preq->source.value.diplrel));
1497 return TRUE;
1498 case REQ_RANGE_TEAM:
1499 if (preq->present) {
1500 cat_snprintf(buf, bufsz,
1501 _("Requires that somebody on your team has the "
1502 "relationship '%s' with at least one other living "
1503 "player.\n"),
1504 diplrel_name_translation(preq->source.value.diplrel));
1505 } else {
1506 cat_snprintf(buf, bufsz,
1507 _("Requires that nobody on your team has the "
1508 "relationship '%s' with any living player.\n"),
1509 diplrel_name_translation(preq->source.value.diplrel));
1511 return TRUE;
1512 case REQ_RANGE_ALLIANCE:
1513 if (preq->present) {
1514 cat_snprintf(buf, bufsz,
1515 _("Requires that somebody in your alliance has the "
1516 "relationship '%s' with at least one other living "
1517 "player.\n"),
1518 diplrel_name_translation(preq->source.value.diplrel));
1519 } else {
1520 cat_snprintf(buf, bufsz,
1521 _("Requires that nobody in your alliance has the "
1522 "relationship '%s' with any living player.\n"),
1523 diplrel_name_translation(preq->source.value.diplrel));
1525 return TRUE;
1526 case REQ_RANGE_WORLD:
1527 if (preq->present) {
1528 cat_snprintf(buf, bufsz,
1529 _("Requires the relationship '%s' between two living "
1530 "players.\n"),
1531 diplrel_name_translation(preq->source.value.diplrel));
1532 } else {
1533 cat_snprintf(buf, bufsz,
1534 _("Requires that no two living players have the "
1535 "relationship '%s'.\n"),
1536 diplrel_name_translation(preq->source.value.diplrel));
1538 return TRUE;
1539 case REQ_RANGE_LOCAL:
1540 if (preq->present) {
1541 cat_snprintf(buf, bufsz,
1542 _("Requires that you have the relationship '%s' with the "
1543 "other player.\n"),
1544 diplrel_name_translation(preq->source.value.diplrel));
1545 } else {
1546 cat_snprintf(buf, bufsz,
1547 _("Requires that you do not have the relationship '%s' "
1548 "with the other player.\n"),
1549 diplrel_name_translation(preq->source.value.diplrel));
1551 return TRUE;
1552 case REQ_RANGE_CADJACENT:
1553 case REQ_RANGE_ADJACENT:
1554 case REQ_RANGE_CITY:
1555 case REQ_RANGE_TRADEROUTE:
1556 case REQ_RANGE_CONTINENT:
1557 case REQ_RANGE_COUNT:
1558 /* Not supported. */
1559 break;
1561 break;
1563 case VUT_UTYPE:
1564 switch (preq->range) {
1565 case REQ_RANGE_LOCAL:
1566 if (preq->present) {
1567 /* TRANS: %s is a single kind of unit (e.g., "Settlers"). */
1568 cat_snprintf(buf, bufsz, Q_("?unit:Requires %s.\n"),
1569 utype_name_translation(preq->source.value.utype));
1570 } else {
1571 /* TRANS: %s is a single kind of unit (e.g., "Settlers"). */
1572 cat_snprintf(buf, bufsz, Q_("?unit:Does not apply to %s.\n"),
1573 utype_name_translation(preq->source.value.utype));
1575 return TRUE;
1576 case REQ_RANGE_CADJACENT:
1577 case REQ_RANGE_ADJACENT:
1578 case REQ_RANGE_CITY:
1579 case REQ_RANGE_TRADEROUTE:
1580 case REQ_RANGE_CONTINENT:
1581 case REQ_RANGE_PLAYER:
1582 case REQ_RANGE_TEAM:
1583 case REQ_RANGE_ALLIANCE:
1584 case REQ_RANGE_WORLD:
1585 case REQ_RANGE_COUNT:
1586 /* Not supported. */
1587 break;
1589 break;
1591 case VUT_UTFLAG:
1592 switch (preq->range) {
1593 case REQ_RANGE_LOCAL:
1595 struct astring astr = ASTRING_INIT;
1597 /* Unit type flags mean nothing to users. Explicitly list the unit
1598 * types with those flags. */
1599 if (role_units_translations(&astr, preq->source.value.unitflag,
1600 TRUE)) {
1601 if (preq->present) {
1602 /* TRANS: %s is a list of unit types separated by "or". */
1603 cat_snprintf(buf, bufsz, Q_("?ulist:Requires %s.\n"),
1604 astr_str(&astr));
1605 } else {
1606 /* TRANS: %s is a list of unit types separated by "or". */
1607 cat_snprintf(buf, bufsz, Q_("?ulist:Does not apply to %s.\n"),
1608 astr_str(&astr));
1610 astr_free(&astr);
1611 return TRUE;
1614 break;
1615 case REQ_RANGE_CADJACENT:
1616 case REQ_RANGE_ADJACENT:
1617 case REQ_RANGE_CITY:
1618 case REQ_RANGE_TRADEROUTE:
1619 case REQ_RANGE_CONTINENT:
1620 case REQ_RANGE_PLAYER:
1621 case REQ_RANGE_TEAM:
1622 case REQ_RANGE_ALLIANCE:
1623 case REQ_RANGE_WORLD:
1624 case REQ_RANGE_COUNT:
1625 /* Not supported. */
1626 break;
1628 break;
1630 case VUT_UCLASS:
1631 switch (preq->range) {
1632 case REQ_RANGE_LOCAL:
1633 if (preq->present) {
1634 /* TRANS: %s is a single unit class (e.g., "Air"). */
1635 cat_snprintf(buf, bufsz, Q_("?uclass:Requires %s units.\n"),
1636 uclass_name_translation(preq->source.value.uclass));
1637 } else {
1638 /* TRANS: %s is a single unit class (e.g., "Air"). */
1639 cat_snprintf(buf, bufsz, Q_("?uclass:Does not apply to %s units.\n"),
1640 uclass_name_translation(preq->source.value.uclass));
1642 return TRUE;
1643 case REQ_RANGE_CADJACENT:
1644 case REQ_RANGE_ADJACENT:
1645 case REQ_RANGE_CITY:
1646 case REQ_RANGE_TRADEROUTE:
1647 case REQ_RANGE_CONTINENT:
1648 case REQ_RANGE_PLAYER:
1649 case REQ_RANGE_TEAM:
1650 case REQ_RANGE_ALLIANCE:
1651 case REQ_RANGE_WORLD:
1652 case REQ_RANGE_COUNT:
1653 /* Not supported. */
1654 break;
1656 break;
1658 case VUT_UCFLAG:
1660 const char *classes[uclass_count()];
1661 int i = 0;
1662 bool done = FALSE;
1663 struct astring list = ASTRING_INIT;
1665 unit_class_iterate(uclass) {
1666 if (uclass_has_flag(uclass, preq->source.value.unitclassflag)) {
1667 classes[i++] = uclass_name_translation(uclass);
1669 } unit_class_iterate_end;
1670 astr_build_or_list(&list, classes, i);
1672 switch (preq->range) {
1673 case REQ_RANGE_LOCAL:
1674 if (preq->present) {
1675 /* TRANS: %s is a list of unit classes separated by "or". */
1676 cat_snprintf(buf, bufsz, Q_("?uclasslist:Requires %s units.\n"),
1677 astr_str(&list));
1678 } else {
1679 /* TRANS: %s is a list of unit classes separated by "or". */
1680 cat_snprintf(buf, bufsz, Q_("?uclasslist:Does not apply to "
1681 "%s units.\n"),
1682 astr_str(&list));
1684 done = TRUE;
1685 break;
1686 case REQ_RANGE_CADJACENT:
1687 case REQ_RANGE_ADJACENT:
1688 case REQ_RANGE_CITY:
1689 case REQ_RANGE_TRADEROUTE:
1690 case REQ_RANGE_CONTINENT:
1691 case REQ_RANGE_PLAYER:
1692 case REQ_RANGE_TEAM:
1693 case REQ_RANGE_ALLIANCE:
1694 case REQ_RANGE_WORLD:
1695 case REQ_RANGE_COUNT:
1696 /* Not supported. */
1697 break;
1699 astr_free(&list);
1700 if (done) {
1701 return TRUE;
1704 break;
1706 case VUT_UNITSTATE:
1708 switch (preq->range) {
1709 case REQ_RANGE_LOCAL:
1710 switch (preq->source.value.unit_state) {
1711 case USP_TRANSPORTED:
1712 if (preq->present) {
1713 cat_snprintf(buf, bufsz,
1714 _("Requires that the unit is transported.\n"));
1715 } else {
1716 cat_snprintf(buf, bufsz,
1717 _("Requires that the unit isn't transported.\n"));
1719 return TRUE;
1720 case USP_LIVABLE_TILE:
1721 if (preq->present) {
1722 cat_snprintf(buf, bufsz,
1723 _("Requires that the unit is on livable tile.\n"));
1724 } else {
1725 cat_snprintf(buf, bufsz,
1726 _("Requires that the unit isn't on livable tile.\n"));
1728 return TRUE;
1729 case USP_COUNT:
1730 fc_assert_msg(preq->source.value.unit_state != USP_COUNT,
1731 "Invalid unit state property.");
1733 break;
1734 case REQ_RANGE_CADJACENT:
1735 case REQ_RANGE_ADJACENT:
1736 case REQ_RANGE_CITY:
1737 case REQ_RANGE_TRADEROUTE:
1738 case REQ_RANGE_CONTINENT:
1739 case REQ_RANGE_PLAYER:
1740 case REQ_RANGE_TEAM:
1741 case REQ_RANGE_ALLIANCE:
1742 case REQ_RANGE_WORLD:
1743 case REQ_RANGE_COUNT:
1744 /* Not supported. */
1745 break;
1748 break;
1750 case VUT_MINMOVES:
1752 switch (preq->range) {
1753 case REQ_RANGE_LOCAL:
1754 if (preq->present) {
1755 cat_snprintf(buf, bufsz,
1756 /* %s is numeric move points; it may have a
1757 * fractional part ("1 1/3 MP"). */
1758 _("Requires that the unit has at least %s MP left.\n"),
1759 move_points_text(preq->source.value.minmoves, TRUE));
1760 } else {
1761 cat_snprintf(buf, bufsz,
1762 /* %s is numeric move points; it may have a
1763 * fractional part ("1 1/3 MP"). */
1764 _("Requires that the unit has less than %s MP left.\n"),
1765 move_points_text(preq->source.value.minmoves, TRUE));
1767 return TRUE;
1768 case REQ_RANGE_CADJACENT:
1769 case REQ_RANGE_ADJACENT:
1770 case REQ_RANGE_CITY:
1771 case REQ_RANGE_TRADEROUTE:
1772 case REQ_RANGE_CONTINENT:
1773 case REQ_RANGE_PLAYER:
1774 case REQ_RANGE_TEAM:
1775 case REQ_RANGE_ALLIANCE:
1776 case REQ_RANGE_WORLD:
1777 case REQ_RANGE_COUNT:
1778 /* Not supported. */
1779 break;
1782 break;
1784 case VUT_MINVETERAN:
1785 if (preq->range != REQ_RANGE_LOCAL) {
1786 break;
1788 /* FIXME: this would be better with veteran level names, but that's
1789 * potentially unit type dependent. */
1790 if (preq->present) {
1791 cat_snprintf(buf, bufsz,
1792 PL_("Requires a unit with at least %d veteran level.\n",
1793 "Requires a unit with at least %d veteran levels.\n",
1794 preq->source.value.minveteran),
1795 preq->source.value.minveteran);
1796 } else {
1797 cat_snprintf(buf, bufsz,
1798 PL_("Requires a unit with fewer than %d veteran level.\n",
1799 "Requires a unit with fewer than %d veteran levels.\n",
1800 preq->source.value.minveteran),
1801 preq->source.value.minveteran);
1803 return TRUE;
1805 case VUT_MINHP:
1806 if (preq->range != REQ_RANGE_LOCAL) {
1807 break;
1810 if (preq->present) {
1811 cat_snprintf(buf, bufsz,
1812 PL_("Requires a unit with at least %d hit point left.\n",
1813 "Requires a unit with at least %d hit points left.\n",
1814 preq->source.value.min_hit_points),
1815 preq->source.value.min_hit_points);
1816 } else {
1817 cat_snprintf(buf, bufsz,
1818 PL_("Requires a unit with fewer than %d hit point "
1819 "left.\n",
1820 "Requires a unit with fewer than %d hit points "
1821 "left.\n",
1822 preq->source.value.min_hit_points),
1823 preq->source.value.min_hit_points);
1825 return TRUE;
1827 case VUT_OTYPE:
1828 if (preq->range != REQ_RANGE_LOCAL) {
1829 break;
1831 if (preq->present) {
1832 /* TRANS: "Applies only to Food." */
1833 cat_snprintf(buf, bufsz, Q_("?output:Applies only to %s.\n"),
1834 get_output_name(preq->source.value.outputtype));
1835 } else {
1836 /* TRANS: "Does not apply to Food." */
1837 cat_snprintf(buf, bufsz, Q_("?output:Does not apply to %s.\n"),
1838 get_output_name(preq->source.value.outputtype));
1840 return TRUE;
1842 case VUT_SPECIALIST:
1843 if (preq->range != REQ_RANGE_LOCAL) {
1844 break;
1846 if (preq->present) {
1847 /* TRANS: "Applies only to Scientists." */
1848 cat_snprintf(buf, bufsz, Q_("?specialist:Applies only to %s.\n"),
1849 specialist_plural_translation(preq->source.value.specialist));
1850 } else {
1851 /* TRANS: "Does not apply to Scientists." */
1852 cat_snprintf(buf, bufsz, Q_("?specialist:Does not apply to %s.\n"),
1853 specialist_plural_translation(preq->source.value.specialist));
1855 return TRUE;
1857 case VUT_MINSIZE:
1858 switch (preq->range) {
1859 case REQ_RANGE_TRADEROUTE:
1860 if (preq->present) {
1861 cat_snprintf(buf, bufsz,
1862 PL_("Requires a minimum city size of %d for this "
1863 "city or a trade partner.\n",
1864 "Requires a minimum city size of %d for this "
1865 "city or a trade partner.\n",
1866 preq->source.value.minsize),
1867 preq->source.value.minsize);
1868 } else {
1869 cat_snprintf(buf, bufsz,
1870 PL_("Requires the city size to be less than %d "
1871 "for this city and all trade partners.\n",
1872 "Requires the city size to be less than %d "
1873 "for this city and all trade partners.\n",
1874 preq->source.value.minsize),
1875 preq->source.value.minsize);
1877 return TRUE;
1878 case REQ_RANGE_CITY:
1879 if (preq->present) {
1880 cat_snprintf(buf, bufsz,
1881 PL_("Requires a minimum city size of %d.\n",
1882 "Requires a minimum city size of %d.\n",
1883 preq->source.value.minsize),
1884 preq->source.value.minsize);
1885 } else {
1886 cat_snprintf(buf, bufsz,
1887 PL_("Requires the city size to be less than %d.\n",
1888 "Requires the city size to be less than %d.\n",
1889 preq->source.value.minsize),
1890 preq->source.value.minsize);
1892 return TRUE;
1893 case REQ_RANGE_LOCAL:
1894 case REQ_RANGE_CADJACENT:
1895 case REQ_RANGE_ADJACENT:
1896 case REQ_RANGE_CONTINENT:
1897 case REQ_RANGE_PLAYER:
1898 case REQ_RANGE_TEAM:
1899 case REQ_RANGE_ALLIANCE:
1900 case REQ_RANGE_WORLD:
1901 case REQ_RANGE_COUNT:
1902 /* Not supported. */
1903 break;
1906 case VUT_MINCULTURE:
1907 switch (preq->range) {
1908 case REQ_RANGE_CITY:
1909 if (preq->present) {
1910 cat_snprintf(buf, bufsz,
1911 PL_("Requires a minimum culture of %d in the city.\n",
1912 "Requires a minimum culture of %d in the city.\n",
1913 preq->source.value.minculture),
1914 preq->source.value.minculture);
1915 } else {
1916 cat_snprintf(buf, bufsz,
1917 PL_("Requires the culture in the city to be less "
1918 "than %d.\n",
1919 "Requires the culture in the city to be less "
1920 "than %d.\n",
1921 preq->source.value.minculture),
1922 preq->source.value.minculture);
1924 return TRUE;
1925 case REQ_RANGE_TRADEROUTE:
1926 if (preq->present) {
1927 cat_snprintf(buf, bufsz,
1928 PL_("Requires a minimum culture of %d in this city or "
1929 "a trade partner.\n",
1930 "Requires a minimum culture of %d in this city or "
1931 "a trade partner.\n",
1932 preq->source.value.minculture),
1933 preq->source.value.minculture);
1934 } else {
1935 cat_snprintf(buf, bufsz,
1936 PL_("Requires the culture in this city and all trade "
1937 "partners to be less than %d.\n",
1938 "Requires the culture in this city and all trade "
1939 "partners to be less than %d.\n",
1940 preq->source.value.minculture),
1941 preq->source.value.minculture);
1943 return TRUE;
1944 case REQ_RANGE_PLAYER:
1945 if (preq->present) {
1946 cat_snprintf(buf, bufsz,
1947 PL_("Requires your nation to have culture "
1948 "of at least %d.\n",
1949 "Requires your nation to have culture "
1950 "of at least %d.\n",
1951 preq->source.value.minculture),
1952 preq->source.value.minculture);
1953 } else {
1954 cat_snprintf(buf, bufsz,
1955 PL_("Prevented if your nation has culture of "
1956 "%d or more.\n",
1957 "Prevented if your nation has culture of "
1958 "%d or more.\n",
1959 preq->source.value.minculture),
1960 preq->source.value.minculture);
1962 return TRUE;
1963 case REQ_RANGE_TEAM:
1964 if (preq->present) {
1965 cat_snprintf(buf, bufsz,
1966 PL_("Requires someone on your team to have culture of "
1967 "at least %d.\n",
1968 "Requires someone on your team to have culture of "
1969 "at least %d.\n",
1970 preq->source.value.minculture),
1971 preq->source.value.minculture);
1972 } else {
1973 cat_snprintf(buf, bufsz,
1974 PL_("Prevented if anyone on your team has culture of "
1975 "%d or more.\n",
1976 "Prevented if anyone on your team has culture of "
1977 "%d or more.\n",
1978 preq->source.value.minculture),
1979 preq->source.value.minculture);
1981 return TRUE;
1982 case REQ_RANGE_ALLIANCE:
1983 if (preq->present) {
1984 cat_snprintf(buf, bufsz,
1985 PL_("Requires someone in your current alliance to "
1986 "have culture of at least %d.\n",
1987 "Requires someone in your current alliance to "
1988 "have culture of at least %d.\n",
1989 preq->source.value.minculture),
1990 preq->source.value.minculture);
1991 } else {
1992 cat_snprintf(buf, bufsz,
1993 PL_("Prevented if anyone in your current alliance has "
1994 "culture of %d or more.\n",
1995 "Prevented if anyone in your current alliance has "
1996 "culture of %d or more.\n",
1997 preq->source.value.minculture),
1998 preq->source.value.minculture);
2000 return TRUE;
2001 case REQ_RANGE_WORLD:
2002 if (preq->present) {
2003 cat_snprintf(buf, bufsz,
2004 PL_("Requires that some player has culture of at "
2005 "least %d.\n",
2006 "Requires that some player has culture of at "
2007 "least %d.\n",
2008 preq->source.value.minculture),
2009 preq->source.value.minculture);
2010 } else {
2011 cat_snprintf(buf, bufsz,
2012 PL_("Requires that no player has culture of %d "
2013 "or more.\n",
2014 "Requires that no player has culture of %d "
2015 "or more.\n",
2016 preq->source.value.minculture),
2017 preq->source.value.minculture);
2019 return TRUE;
2020 case REQ_RANGE_LOCAL:
2021 case REQ_RANGE_CADJACENT:
2022 case REQ_RANGE_ADJACENT:
2023 case REQ_RANGE_CONTINENT:
2024 case REQ_RANGE_COUNT:
2025 break;
2027 break;
2029 case VUT_MAXTILEUNITS:
2030 switch (preq->range) {
2031 case REQ_RANGE_LOCAL:
2032 if (preq->present) {
2033 cat_snprintf(buf, bufsz,
2034 PL_("At most %d unit may be present on the tile.\n",
2035 "At most %d units may be present on the tile.\n",
2036 preq->source.value.max_tile_units),
2037 preq->source.value.max_tile_units);
2038 } else {
2039 cat_snprintf(buf, bufsz,
2040 PL_("There must be more than %d unit present on "
2041 "the tile.\n",
2042 "There must be more than %d units present on "
2043 "the tile.\n",
2044 preq->source.value.max_tile_units),
2045 preq->source.value.max_tile_units);
2047 return TRUE;
2048 case REQ_RANGE_CADJACENT:
2049 if (preq->present) {
2050 cat_snprintf(buf, bufsz,
2051 PL_("The tile or at least one cardinally adjacent tile "
2052 "must have %d unit or fewer.\n",
2053 "The tile or at least one cardinally adjacent tile "
2054 "must have %d units or fewer.\n",
2055 preq->source.value.max_tile_units),
2056 preq->source.value.max_tile_units);
2057 } else {
2058 cat_snprintf(buf, bufsz,
2059 PL_("The tile and all cardinally adjacent tiles must "
2060 "have more than %d unit each.\n",
2061 "The tile and all cardinally adjacent tiles must "
2062 "have more than %d units each.\n",
2063 preq->source.value.max_tile_units),
2064 preq->source.value.max_tile_units);
2066 return TRUE;
2067 case REQ_RANGE_ADJACENT:
2068 if (preq->present) {
2069 cat_snprintf(buf, bufsz,
2070 PL_("The tile or at least one adjacent tile must have "
2071 "%d unit or fewer.\n",
2072 "The tile or at least one adjacent tile must have "
2073 "%d units or fewer.\n",
2074 preq->source.value.max_tile_units),
2075 preq->source.value.max_tile_units);
2076 } else {
2077 cat_snprintf(buf, bufsz,
2078 PL_("The tile and all adjacent tiles must have more "
2079 "than %d unit each.\n",
2080 "The tile and all adjacent tiles must have more "
2081 "than %d units each.\n",
2082 preq->source.value.max_tile_units),
2083 preq->source.value.max_tile_units);
2085 return TRUE;
2086 case REQ_RANGE_CITY:
2087 case REQ_RANGE_TRADEROUTE:
2088 case REQ_RANGE_CONTINENT:
2089 case REQ_RANGE_PLAYER:
2090 case REQ_RANGE_TEAM:
2091 case REQ_RANGE_ALLIANCE:
2092 case REQ_RANGE_WORLD:
2093 case REQ_RANGE_COUNT:
2094 /* Not supported. */
2095 break;
2098 case VUT_AI_LEVEL:
2099 if (preq->range != REQ_RANGE_PLAYER) {
2100 break;
2102 if (preq->present) {
2103 cat_snprintf(buf, bufsz,
2104 /* TRANS: AI level (e.g., "Handicapped") */
2105 _("Applies to %s AI players.\n"),
2106 ai_level_translated_name(preq->source.value.ai_level));
2107 } else {
2108 cat_snprintf(buf, bufsz,
2109 /* TRANS: AI level (e.g., "Cheating") */
2110 _("Does not apply to %s AI players.\n"),
2111 ai_level_translated_name(preq->source.value.ai_level));
2113 return TRUE;
2115 case VUT_TERRAINCLASS:
2116 switch (preq->range) {
2117 case REQ_RANGE_LOCAL:
2118 if (preq->present) {
2119 cat_snprintf(buf, bufsz,
2120 /* TRANS: %s is a terrain class */
2121 Q_("?terrainclass:Requires %s terrain on the tile.\n"),
2122 terrain_class_name_translation
2123 (preq->source.value.terrainclass));
2124 } else {
2125 cat_snprintf(buf, bufsz,
2126 /* TRANS: %s is a terrain class */
2127 Q_("?terrainclass:Prevented by %s terrain on the tile.\n"),
2128 terrain_class_name_translation
2129 (preq->source.value.terrainclass));
2131 return TRUE;
2132 case REQ_RANGE_CADJACENT:
2133 if (preq->present) {
2134 cat_snprintf(buf, bufsz,
2135 /* TRANS: %s is a terrain class */
2136 Q_("?terrainclass:Requires %s terrain on the tile or a "
2137 "cardinally adjacent tile.\n"),
2138 terrain_class_name_translation
2139 (preq->source.value.terrainclass));
2140 } else {
2141 cat_snprintf(buf, bufsz,
2142 /* TRANS: %s is a terrain class */
2143 Q_("?terrainclass:Prevented by %s terrain on the tile or "
2144 "any cardinally adjacent tile.\n"),
2145 terrain_class_name_translation
2146 (preq->source.value.terrainclass));
2148 return TRUE;
2149 case REQ_RANGE_ADJACENT:
2150 if (preq->present) {
2151 cat_snprintf(buf, bufsz,
2152 /* TRANS: %s is a terrain class */
2153 Q_("?terrainclass:Requires %s terrain on the tile or an "
2154 "adjacent tile.\n"),
2155 terrain_class_name_translation
2156 (preq->source.value.terrainclass));
2157 } else {
2158 cat_snprintf(buf, bufsz,
2159 /* TRANS: %s is a terrain class */
2160 Q_("?terrainclass:Prevented by %s terrain on the tile or "
2161 "any adjacent tile.\n"),
2162 terrain_class_name_translation
2163 (preq->source.value.terrainclass));
2165 return TRUE;
2166 case REQ_RANGE_CITY:
2167 if (preq->present) {
2168 cat_snprintf(buf, bufsz,
2169 /* TRANS: %s is a terrain class */
2170 Q_("?terrainclass:Requires %s terrain on a tile within "
2171 "the city radius.\n"),
2172 terrain_class_name_translation
2173 (preq->source.value.terrainclass));
2174 } else {
2175 cat_snprintf(buf, bufsz,
2176 /* TRANS: %s is a terrain class */
2177 Q_("?terrainclass:Prevented by %s terrain on any tile "
2178 "within the city radius.\n"),
2179 terrain_class_name_translation
2180 (preq->source.value.terrainclass));
2182 return TRUE;
2183 case REQ_RANGE_TRADEROUTE:
2184 if (preq->present) {
2185 cat_snprintf(buf, bufsz,
2186 /* TRANS: %s is a terrain class */
2187 Q_("?terrainclass:Requires %s terrain on a tile within "
2188 "the city radius or the city radius of a trade "
2189 "partner.\n"),
2190 terrain_class_name_translation
2191 (preq->source.value.terrainclass));
2192 } else {
2193 cat_snprintf(buf, bufsz,
2194 /* TRANS: %s is a terrain class */
2195 Q_("?terrainclass:Prevented by %s terrain on any tile "
2196 "within the city radius or the city radius of a trade "
2197 "partner.\n"),
2198 terrain_class_name_translation
2199 (preq->source.value.terrainclass));
2201 return TRUE;
2202 case REQ_RANGE_CONTINENT:
2203 case REQ_RANGE_PLAYER:
2204 case REQ_RANGE_TEAM:
2205 case REQ_RANGE_ALLIANCE:
2206 case REQ_RANGE_WORLD:
2207 case REQ_RANGE_COUNT:
2208 /* Not supported. */
2209 break;
2211 break;
2213 case VUT_TERRFLAG:
2214 switch (preq->range) {
2215 case REQ_RANGE_LOCAL:
2216 if (preq->present) {
2217 cat_snprintf(buf, bufsz,
2218 /* TRANS: %s is a (translatable) terrain flag. */
2219 _("Requires terrain with the \"%s\" flag on the tile.\n"),
2220 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2221 } else {
2222 cat_snprintf(buf, bufsz,
2223 /* TRANS: %s is a (translatable) terrain flag. */
2224 _("Prevented by terrain with the \"%s\" flag on the "
2225 "tile.\n"),
2226 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2228 return TRUE;
2229 case REQ_RANGE_CADJACENT:
2230 if (preq->present) {
2231 cat_snprintf(buf, bufsz,
2232 /* TRANS: %s is a (translatable) terrain flag. */
2233 _("Requires terrain with the \"%s\" flag on the "
2234 "tile or a cardinally adjacent tile.\n"),
2235 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2236 } else {
2237 cat_snprintf(buf, bufsz,
2238 /* TRANS: %s is a (translatable) terrain flag. */
2239 _("Prevented by terrain with the \"%s\" flag on "
2240 "the tile or any cardinally adjacent tile.\n"),
2241 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2243 return TRUE;
2244 case REQ_RANGE_ADJACENT:
2245 if (preq->present) {
2246 cat_snprintf(buf, bufsz,
2247 /* TRANS: %s is a (translatable) terrain flag. */
2248 _("Requires terrain with the \"%s\" flag on the "
2249 "tile or an adjacent tile.\n"),
2250 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2251 } else {
2252 cat_snprintf(buf, bufsz,
2253 /* TRANS: %s is a (translatable) terrain flag. */
2254 _("Prevented by terrain with the \"%s\" flag on "
2255 "the tile or any adjacent tile.\n"),
2256 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2258 return TRUE;
2259 case REQ_RANGE_CITY:
2260 if (preq->present) {
2261 cat_snprintf(buf, bufsz,
2262 /* TRANS: %s is a (translatable) terrain flag. */
2263 _("Requires terrain with the \"%s\" flag on a tile "
2264 "within the city radius.\n"),
2265 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2266 } else {
2267 cat_snprintf(buf, bufsz,
2268 /* TRANS: %s is a (translatable) terrain flag. */
2269 _("Prevented by terrain with the \"%s\" flag on any tile "
2270 "within the city radius.\n"),
2271 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2273 return TRUE;
2274 case REQ_RANGE_TRADEROUTE:
2275 if (preq->present) {
2276 cat_snprintf(buf, bufsz,
2277 /* TRANS: %s is a (translatable) terrain flag. */
2278 _("Requires terrain with the \"%s\" flag on a tile "
2279 "within the city radius or the city radius of "
2280 "a trade partner.\n"),
2281 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2282 } else {
2283 cat_snprintf(buf, bufsz,
2284 /* TRANS: %s is a (translatable) terrain flag. */
2285 _("Prevented by terrain with the \"%s\" flag on any tile "
2286 "within the city radius or the city radius of "
2287 "a trade partner.\n"),
2288 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2290 return TRUE;
2291 case REQ_RANGE_CONTINENT:
2292 case REQ_RANGE_PLAYER:
2293 case REQ_RANGE_TEAM:
2294 case REQ_RANGE_ALLIANCE:
2295 case REQ_RANGE_WORLD:
2296 case REQ_RANGE_COUNT:
2297 /* Not supported. */
2298 break;
2300 break;
2302 case VUT_BASEFLAG:
2303 switch (preq->range) {
2304 case REQ_RANGE_LOCAL:
2305 if (preq->present) {
2306 cat_snprintf(buf, bufsz,
2307 /* TRANS: %s is a (translatable) base flag. */
2308 _("Requires a base with the \"%s\" flag on the tile.\n"),
2309 base_flag_id_translated_name(preq->source.value.baseflag));
2310 } else {
2311 cat_snprintf(buf, bufsz,
2312 /* TRANS: %s is a (translatable) base flag. */
2313 _("Prevented by a base with the \"%s\" flag on the "
2314 "tile.\n"),
2315 base_flag_id_translated_name(preq->source.value.baseflag));
2317 return TRUE;
2318 case REQ_RANGE_CADJACENT:
2319 if (preq->present) {
2320 cat_snprintf(buf, bufsz,
2321 /* TRANS: %s is a (translatable) base flag. */
2322 _("Requires a base with the \"%s\" flag on the "
2323 "tile or a cardinally adjacent tile.\n"),
2324 base_flag_id_translated_name(preq->source.value.baseflag));
2325 } else {
2326 cat_snprintf(buf, bufsz,
2327 /* TRANS: %s is a (translatable) base flag. */
2328 _("Prevented by a base with the \"%s\" flag on "
2329 "the tile or any cardinally adjacent tile.\n"),
2330 base_flag_id_translated_name(preq->source.value.baseflag));
2332 return TRUE;
2333 case REQ_RANGE_ADJACENT:
2334 if (preq->present) {
2335 cat_snprintf(buf, bufsz,
2336 /* TRANS: %s is a (translatable) base flag. */
2337 _("Requires a base with the \"%s\" flag on the "
2338 "tile or an adjacent tile.\n"),
2339 base_flag_id_translated_name(preq->source.value.baseflag));
2340 } else {
2341 cat_snprintf(buf, bufsz,
2342 /* TRANS: %s is a (translatable) base flag. */
2343 _("Prevented by a base with the \"%s\" flag on "
2344 "the tile or any adjacent tile.\n"),
2345 base_flag_id_translated_name(preq->source.value.baseflag));
2347 return TRUE;
2348 case REQ_RANGE_CITY:
2349 if (preq->present) {
2350 cat_snprintf(buf, bufsz,
2351 /* TRANS: %s is a (translatable) base flag. */
2352 _("Requires a base with the \"%s\" flag on a tile "
2353 "within the city radius.\n"),
2354 base_flag_id_translated_name(preq->source.value.baseflag));
2355 } else {
2356 cat_snprintf(buf, bufsz,
2357 /* TRANS: %s is a (translatable) base flag. */
2358 _("Prevented by a base with the \"%s\" flag on any tile "
2359 "within the city radius.\n"),
2360 base_flag_id_translated_name(preq->source.value.baseflag));
2362 return TRUE;
2363 case REQ_RANGE_TRADEROUTE:
2364 if (preq->present) {
2365 cat_snprintf(buf, bufsz,
2366 /* TRANS: %s is a (translatable) base flag. */
2367 _("Requires a base with the \"%s\" flag on a tile "
2368 "within the city radius or the city radius of a "
2369 "trade partner.\n"),
2370 base_flag_id_translated_name(preq->source.value.baseflag));
2371 } else {
2372 cat_snprintf(buf, bufsz,
2373 /* TRANS: %s is a (translatable) base flag. */
2374 _("Prevented by a base with the \"%s\" flag on any tile "
2375 "within the city radius or the city radius of a "
2376 "trade partner.\n"),
2377 base_flag_id_translated_name(preq->source.value.baseflag));
2379 return TRUE;
2380 case REQ_RANGE_CONTINENT:
2381 case REQ_RANGE_PLAYER:
2382 case REQ_RANGE_TEAM:
2383 case REQ_RANGE_ALLIANCE:
2384 case REQ_RANGE_WORLD:
2385 case REQ_RANGE_COUNT:
2386 /* Not supported. */
2387 break;
2389 break;
2391 case VUT_ROADFLAG:
2392 switch (preq->range) {
2393 case REQ_RANGE_LOCAL:
2394 if (preq->present) {
2395 cat_snprintf(buf, bufsz,
2396 /* TRANS: %s is a (translatable) road flag. */
2397 _("Requires a road with the \"%s\" flag on the tile.\n"),
2398 road_flag_id_translated_name(preq->source.value.roadflag));
2399 } else {
2400 cat_snprintf(buf, bufsz,
2401 /* TRANS: %s is a (translatable) road flag. */
2402 _("Prevented by a road with the \"%s\" flag on the "
2403 "tile.\n"),
2404 road_flag_id_translated_name(preq->source.value.roadflag));
2406 return TRUE;
2407 case REQ_RANGE_CADJACENT:
2408 if (preq->present) {
2409 cat_snprintf(buf, bufsz,
2410 /* TRANS: %s is a (translatable) road flag. */
2411 _("Requires a road with the \"%s\" flag on the "
2412 "tile or a cardinally adjacent tile.\n"),
2413 road_flag_id_translated_name(preq->source.value.roadflag));
2414 } else {
2415 cat_snprintf(buf, bufsz,
2416 /* TRANS: %s is a (translatable) road flag. */
2417 _("Prevented by a road with the \"%s\" flag on "
2418 "the tile or any cardinally adjacent tile.\n"),
2419 road_flag_id_translated_name(preq->source.value.roadflag));
2421 return TRUE;
2422 case REQ_RANGE_ADJACENT:
2423 if (preq->present) {
2424 cat_snprintf(buf, bufsz,
2425 /* TRANS: %s is a (translatable) road flag. */
2426 _("Requires a road with the \"%s\" flag on the "
2427 "tile or an adjacent tile.\n"),
2428 road_flag_id_translated_name(preq->source.value.roadflag));
2429 } else {
2430 cat_snprintf(buf, bufsz,
2431 /* TRANS: %s is a (translatable) road flag. */
2432 _("Prevented by a road with the \"%s\" flag on "
2433 "the tile or any adjacent tile.\n"),
2434 road_flag_id_translated_name(preq->source.value.roadflag));
2436 return TRUE;
2437 case REQ_RANGE_CITY:
2438 if (preq->present) {
2439 cat_snprintf(buf, bufsz,
2440 /* TRANS: %s is a (translatable) road flag. */
2441 _("Requires a road with the \"%s\" flag on a tile "
2442 "within the city radius.\n"),
2443 road_flag_id_translated_name(preq->source.value.roadflag));
2444 } else {
2445 cat_snprintf(buf, bufsz,
2446 /* TRANS: %s is a (translatable) road flag. */
2447 _("Prevented by a road with the \"%s\" flag on any tile "
2448 "within the city radius.\n"),
2449 road_flag_id_translated_name(preq->source.value.roadflag));
2451 return TRUE;
2452 case REQ_RANGE_TRADEROUTE:
2453 if (preq->present) {
2454 cat_snprintf(buf, bufsz,
2455 /* TRANS: %s is a (translatable) road flag. */
2456 _("Requires a road with the \"%s\" flag on a tile "
2457 "within the city radius or the city radius of a "
2458 "trade partner.\n"),
2459 road_flag_id_translated_name(preq->source.value.roadflag));
2460 } else {
2461 cat_snprintf(buf, bufsz,
2462 /* TRANS: %s is a (translatable) road flag. */
2463 _("Prevented by a road with the \"%s\" flag on any tile "
2464 "within the city radius or the city radius of a "
2465 "trade partner.\n"),
2466 road_flag_id_translated_name(preq->source.value.roadflag));
2468 return TRUE;
2469 case REQ_RANGE_CONTINENT:
2470 case REQ_RANGE_PLAYER:
2471 case REQ_RANGE_TEAM:
2472 case REQ_RANGE_ALLIANCE:
2473 case REQ_RANGE_WORLD:
2474 case REQ_RANGE_COUNT:
2475 /* Not supported. */
2476 break;
2478 break;
2480 case VUT_EXTRAFLAG:
2481 switch (preq->range) {
2482 case REQ_RANGE_LOCAL:
2483 if (preq->present) {
2484 cat_snprintf(buf, bufsz,
2485 /* TRANS: %s is a (translatable) extra flag. */
2486 _("Requires an extra with the \"%s\" flag on the tile.\n"),
2487 extra_flag_id_translated_name(preq->source.value.extraflag));
2488 } else {
2489 cat_snprintf(buf, bufsz,
2490 /* TRANS: %s is a (translatable) extra flag. */
2491 _("Prevented by an extra with the \"%s\" flag on the "
2492 "tile.\n"),
2493 extra_flag_id_translated_name(preq->source.value.extraflag));
2495 return TRUE;
2496 case REQ_RANGE_CADJACENT:
2497 if (preq->present) {
2498 cat_snprintf(buf, bufsz,
2499 /* TRANS: %s is a (translatable) extra flag. */
2500 _("Requires an extra with the \"%s\" flag on the "
2501 "tile or a cardinally adjacent tile.\n"),
2502 extra_flag_id_translated_name(preq->source.value.extraflag));
2503 } else {
2504 cat_snprintf(buf, bufsz,
2505 /* TRANS: %s is a (translatable) extra flag. */
2506 _("Prevented by an extra with the \"%s\" flag on "
2507 "the tile or any cardinally adjacent tile.\n"),
2508 extra_flag_id_translated_name(preq->source.value.extraflag));
2510 return TRUE;
2511 case REQ_RANGE_ADJACENT:
2512 if (preq->present) {
2513 cat_snprintf(buf, bufsz,
2514 /* TRANS: %s is a (translatable) extra flag. */
2515 _("Requires an extra with the \"%s\" flag on the "
2516 "tile or an adjacent tile.\n"),
2517 extra_flag_id_translated_name(preq->source.value.extraflag));
2518 } else {
2519 cat_snprintf(buf, bufsz,
2520 /* TRANS: %s is a (translatable) extra flag. */
2521 _("Prevented by an extra with the \"%s\" flag on "
2522 "the tile or any adjacent tile.\n"),
2523 extra_flag_id_translated_name(preq->source.value.extraflag));
2525 return TRUE;
2526 case REQ_RANGE_CITY:
2527 if (preq->present) {
2528 cat_snprintf(buf, bufsz,
2529 /* TRANS: %s is a (translatable) extra flag. */
2530 _("Requires an extra with the \"%s\" flag on a tile "
2531 "within the city radius.\n"),
2532 extra_flag_id_translated_name(preq->source.value.extraflag));
2533 } else {
2534 cat_snprintf(buf, bufsz,
2535 /* TRANS: %s is a (translatable) extra flag. */
2536 _("Prevented by an extra with the \"%s\" flag on any tile "
2537 "within the city radius.\n"),
2538 extra_flag_id_translated_name(preq->source.value.extraflag));
2540 return TRUE;
2541 case REQ_RANGE_TRADEROUTE:
2542 if (preq->present) {
2543 cat_snprintf(buf, bufsz,
2544 /* TRANS: %s is a (translatable) extra flag. */
2545 _("Requires an extra with the \"%s\" flag on a tile "
2546 "within the city radius or the city radius of a "
2547 "trade partner.\n"),
2548 extra_flag_id_translated_name(preq->source.value.extraflag));
2549 } else {
2550 cat_snprintf(buf, bufsz,
2551 /* TRANS: %s is a (translatable) extra flag. */
2552 _("Prevented by an extra with the \"%s\" flag on any tile "
2553 "within the city radius or the city radius of a "
2554 "trade partner.\n"),
2555 extra_flag_id_translated_name(preq->source.value.extraflag));
2557 return TRUE;
2558 case REQ_RANGE_CONTINENT:
2559 case REQ_RANGE_PLAYER:
2560 case REQ_RANGE_TEAM:
2561 case REQ_RANGE_ALLIANCE:
2562 case REQ_RANGE_WORLD:
2563 case REQ_RANGE_COUNT:
2564 /* Not supported. */
2565 break;
2567 break;
2569 case VUT_MINYEAR:
2570 if (preq->range != REQ_RANGE_WORLD) {
2571 break;
2573 if (preq->present) {
2574 cat_snprintf(buf, bufsz,
2575 _("Requires the game to have reached the year %s.\n"),
2576 textyear(preq->source.value.minyear));
2577 } else {
2578 cat_snprintf(buf, bufsz,
2579 _("Requires that the game has not yet reached the "
2580 "year %s.\n"),
2581 textyear(preq->source.value.minyear));
2583 return TRUE;
2585 case VUT_TOPO:
2586 if (preq->range != REQ_RANGE_WORLD) {
2587 break;
2589 if (preq->present) {
2590 cat_snprintf(buf, bufsz,
2591 _("Requires %s map.\n"),
2592 _(topo_flag_name(preq->source.value.topo_property)));
2593 } else {
2594 cat_snprintf(buf, bufsz,
2595 _("Prevented on %s map.\n"),
2596 _(topo_flag_name(preq->source.value.topo_property)));
2598 return TRUE;
2600 case VUT_AGE:
2601 if (preq->present) {
2602 cat_snprintf(buf, bufsz,
2603 _("Requires age of %d turns.\n"),
2604 preq->source.value.age);
2605 } else {
2606 cat_snprintf(buf, bufsz,
2607 _("Prevented if age is over %d turns.\n"),
2608 preq->source.value.age);
2610 return TRUE;
2612 case VUT_TERRAINALTER:
2613 switch (preq->range) {
2614 case REQ_RANGE_LOCAL:
2615 if (preq->present) {
2616 cat_snprintf(buf, bufsz,
2617 _("Requires terrain on which alteration %s is "
2618 "possible.\n"),
2619 Q_(terrain_alteration_name(preq->source.value.terrainalter)));
2620 } else {
2621 cat_snprintf(buf, bufsz,
2622 _("Prevented by terrain on which alteration %s "
2623 "can be made.\n"),
2624 Q_(terrain_alteration_name(preq->source.value.terrainalter)));
2626 return TRUE;
2627 case REQ_RANGE_CADJACENT:
2628 case REQ_RANGE_ADJACENT:
2629 case REQ_RANGE_CITY:
2630 case REQ_RANGE_TRADEROUTE:
2631 case REQ_RANGE_CONTINENT:
2632 case REQ_RANGE_PLAYER:
2633 case REQ_RANGE_TEAM:
2634 case REQ_RANGE_ALLIANCE:
2635 case REQ_RANGE_WORLD:
2636 case REQ_RANGE_COUNT:
2637 /* Not supported. */
2638 break;
2640 break;
2642 case VUT_CITYTILE:
2643 if (preq->source.value.citytile == CITYT_LAST) {
2644 break;
2645 } else {
2646 static char *tile_property = NULL;
2648 switch (preq->source.value.citytile) {
2649 case CITYT_CENTER:
2650 tile_property = "city centers";
2651 break;
2652 case CITYT_CLAIMED:
2653 tile_property = "claimed tiles";
2654 break;
2655 case CITYT_LAST:
2656 fc_assert(preq->source.value.citytile != CITYT_LAST);
2657 break;
2660 switch (preq->range) {
2661 case REQ_RANGE_LOCAL:
2662 if (preq->present) {
2663 cat_snprintf(buf, bufsz,
2664 /* TRANS: tile property ("city centers", etc) */
2665 Q_("?tileprop:Applies only to %s.\n"), tile_property);
2666 } else {
2667 cat_snprintf(buf, bufsz,
2668 /* TRANS: tile property ("city centers", etc) */
2669 Q_("?tileprop:Does not apply to %s.\n"), tile_property);
2671 return TRUE;
2672 case REQ_RANGE_CADJACENT:
2673 if (preq->present) {
2674 /* TRANS: tile property ("city centers", etc) */
2675 cat_snprintf(buf, bufsz, Q_("?tileprop:Applies only to %s and "
2676 "cardinally adjacent tiles.\n"),
2677 tile_property);
2678 } else {
2679 /* TRANS: tile property ("city centers", etc) */
2680 cat_snprintf(buf, bufsz, Q_("?tileprop:Does not apply to %s or "
2681 "cardinally adjacent tiles.\n"),
2682 tile_property);
2684 return TRUE;
2685 case REQ_RANGE_ADJACENT:
2686 if (preq->present) {
2687 /* TRANS: tile property ("city centers", etc) */
2688 cat_snprintf(buf, bufsz, Q_("?tileprop:Applies only to %s and "
2689 "adjacent tiles.\n"), tile_property);
2690 } else {
2691 /* TRANS: tile property ("city centers", etc) */
2692 cat_snprintf(buf, bufsz, Q_("?tileprop:Does not apply to %s or "
2693 "adjacent tiles.\n"), tile_property);
2695 return TRUE;
2696 case REQ_RANGE_CITY:
2697 case REQ_RANGE_TRADEROUTE:
2698 case REQ_RANGE_CONTINENT:
2699 case REQ_RANGE_PLAYER:
2700 case REQ_RANGE_TEAM:
2701 case REQ_RANGE_ALLIANCE:
2702 case REQ_RANGE_WORLD:
2703 case REQ_RANGE_COUNT:
2704 /* Not supported. */
2705 break;
2709 case VUT_COUNT:
2710 break;
2714 char text[256];
2716 log_error("%s requirement %s in range %d is not supported in helpdata.c.",
2717 preq->present ? "Present" : "Absent",
2718 universal_name_translation(&preq->source, text, sizeof(text)),
2719 preq->range);
2722 return FALSE;
2725 /****************************************************************************
2726 Append text to 'buf' if the given requirements list 'subjreqs' contains
2727 'psource', implying that ability to build the subject 'subjstr' is
2728 affected by 'psource'.
2729 'strs' is an array of (possibly i18n-qualified) format strings covering
2730 the various cases where additional requirements apply.
2731 ****************************************************************************/
2732 static void insert_allows_single(struct universal *psource,
2733 struct requirement_vector *psubjreqs,
2734 const char *subjstr,
2735 const char *const *strs,
2736 char *buf, size_t bufsz) {
2737 struct strvec *coreqs = strvec_new();
2738 struct strvec *conoreqs = strvec_new();
2739 struct astring coreqstr = ASTRING_INIT;
2740 struct astring conoreqstr = ASTRING_INIT;
2741 char buf2[bufsz];
2743 /* FIXME: show other data like range and survives. */
2745 requirement_vector_iterate(psubjreqs, req) {
2746 if (!req->quiet && are_universals_equal(psource, &req->source)) {
2747 if (req->present) {
2748 /* psource enables the subject, but other sources may
2749 * also be required (or required to be absent). */
2750 requirement_vector_iterate(psubjreqs, coreq) {
2751 if (!coreq->quiet && !are_universals_equal(psource, &coreq->source)) {
2752 universal_name_translation(&coreq->source, buf2, sizeof(buf2));
2753 strvec_append(coreq->present ? coreqs : conoreqs, buf2);
2755 } requirement_vector_iterate_end;
2757 if (0 < strvec_size(coreqs)) {
2758 if (0 < strvec_size(conoreqs)) {
2759 cat_snprintf(buf, bufsz,
2760 Q_(strs[0]), /* "Allows %s (with %s but no %s)." */
2761 subjstr,
2762 strvec_to_and_list(coreqs, &coreqstr),
2763 strvec_to_or_list(conoreqs, &conoreqstr));
2764 } else {
2765 cat_snprintf(buf, bufsz,
2766 Q_(strs[1]), /* "Allows %s (with %s)." */
2767 subjstr,
2768 strvec_to_and_list(coreqs, &coreqstr));
2770 } else {
2771 if (0 < strvec_size(conoreqs)) {
2772 cat_snprintf(buf, bufsz,
2773 Q_(strs[2]), /* "Allows %s (absent %s)." */
2774 subjstr,
2775 strvec_to_and_list(conoreqs, &conoreqstr));
2776 } else {
2777 cat_snprintf(buf, bufsz,
2778 Q_(strs[3]), /* "Allows %s." */
2779 subjstr);
2782 } else {
2783 /* psource can, on its own, prevent the subject. */
2784 cat_snprintf(buf, bufsz,
2785 Q_(strs[4]), /* "Prevents %s." */
2786 subjstr);
2788 cat_snprintf(buf, bufsz, "\n");
2790 } requirement_vector_iterate_end;
2792 strvec_destroy(coreqs);
2793 strvec_destroy(conoreqs);
2794 astr_free(&coreqstr);
2795 astr_free(&conoreqstr);
2798 /****************************************************************************
2799 Generate text for what this requirement source allows. Something like
2801 "Allows Communism (with University).\n"
2802 "Allows Mfg. Plant (with Factory).\n"
2803 "Allows Library (absent Fundamentalism).\n"
2804 "Prevents Harbor.\n"
2806 This should be called to generate helptext for every possible source
2807 type. Note this doesn't handle effects but rather requirements to
2808 create/maintain things (currently only building/government reqs).
2810 NB: This function overwrites any existing buffer contents by writing the
2811 generated text to the start of the given 'buf' pointer (i.e. it does
2812 NOT append like cat_snprintf).
2813 ****************************************************************************/
2814 static void insert_allows(struct universal *psource,
2815 char *buf, size_t bufsz)
2817 buf[0] = '\0';
2819 governments_iterate(pgov) {
2820 static const char *const govstrs[] = {
2821 /* TRANS: First %s is a government name. */
2822 N_("?gov:* Allows %s (with %s but no %s)."),
2823 /* TRANS: First %s is a government name. */
2824 N_("?gov:* Allows %s (with %s)."),
2825 /* TRANS: First %s is a government name. */
2826 N_("?gov:* Allows %s (absent %s)."),
2827 /* TRANS: %s is a government name. */
2828 N_("?gov:* Allows %s."),
2829 /* TRANS: %s is a government name. */
2830 N_("?gov:* Prevents %s.")
2832 insert_allows_single(psource, &pgov->reqs,
2833 government_name_translation(pgov), govstrs,
2834 buf, bufsz);
2835 } governments_iterate_end;
2837 improvement_iterate(pimprove) {
2838 static const char *const imprstrs[] = {
2839 /* TRANS: First %s is a building name. */
2840 N_("?improvement:* Allows %s (with %s but no %s)."),
2841 /* TRANS: First %s is a building name. */
2842 N_("?improvement:* Allows %s (with %s)."),
2843 /* TRANS: First %s is a building name. */
2844 N_("?improvement:* Allows %s (absent %s)."),
2845 /* TRANS: %s is a building name. */
2846 N_("?improvement:* Allows %s."),
2847 /* TRANS: %s is a building name. */
2848 N_("?improvement:* Prevents %s.")
2850 insert_allows_single(psource, &pimprove->reqs,
2851 improvement_name_translation(pimprove), imprstrs,
2852 buf, bufsz);
2853 } improvement_iterate_end;
2856 /****************************************************************
2857 Allocate and initialize new help item
2858 *****************************************************************/
2859 static struct help_item *new_help_item(int type)
2861 struct help_item *pitem;
2863 pitem = fc_malloc(sizeof(struct help_item));
2864 pitem->topic = NULL;
2865 pitem->text = NULL;
2866 pitem->type = type;
2867 return pitem;
2870 /****************************************************************
2871 for help_list_sort(); sort by topic via compare_strings()
2872 (sort topics with more leading spaces after those with fewer)
2873 *****************************************************************/
2874 static int help_item_compar(const struct help_item *const *ppa,
2875 const struct help_item *const *ppb)
2877 const struct help_item *ha, *hb;
2878 char *ta, *tb;
2879 ha = *ppa;
2880 hb = *ppb;
2881 for (ta = ha->topic, tb = hb->topic; *ta != '\0' && *tb != '\0'; ta++, tb++) {
2882 if (*ta != ' ') {
2883 if (*tb == ' ') return -1;
2884 break;
2885 } else if (*tb != ' ') {
2886 if (*ta == ' ') return 1;
2887 break;
2890 return compare_strings(ta, tb);
2893 /****************************************************************
2894 pplayer may be NULL.
2895 *****************************************************************/
2896 void boot_help_texts(void)
2898 static bool booted = FALSE;
2900 struct section_file *sf;
2901 const char *filename;
2902 struct help_item *pitem;
2903 int i;
2904 struct section_list *sec;
2905 const char **paras;
2906 size_t npara;
2907 char long_buffer[64000]; /* HACK: this may be overrun. */
2909 check_help_nodes_init();
2911 /* need to do something like this or bad things happen */
2912 popdown_help_dialog();
2914 if (!booted) {
2915 log_verbose("Booting help texts");
2916 } else {
2917 /* free memory allocated last time booted */
2918 free_help_texts();
2919 log_verbose("Rebooting help texts");
2922 filename = fileinfoname(get_data_dirs(), "helpdata.txt");
2923 if (!filename) {
2924 log_error("Did not read help texts");
2925 return;
2927 /* after following call filename may be clobbered; use sf->filename instead */
2928 if (!(sf = secfile_load(filename, FALSE))) {
2929 /* this is now unlikely to happen */
2930 log_error("failed reading help-texts from '%s':\n%s", filename,
2931 secfile_error());
2932 return;
2935 sec = secfile_sections_by_name_prefix(sf, "help_");
2937 if (NULL != sec) {
2938 section_list_iterate(sec, psection) {
2939 char help_text_buffer[MAX_LEN_PACKET];
2940 const char *sec_name = section_name(psection);
2941 const char *gen_str = secfile_lookup_str(sf, "%s.generate", sec_name);
2943 if (gen_str) {
2944 enum help_page_type current_type = HELP_ANY;
2945 int level = strspn(gen_str, " ");
2947 gen_str += level;
2949 for (i = 2; help_type_names[i]; i++) {
2950 if (strcmp(gen_str, help_type_names[i]) == 0) {
2951 current_type = i;
2952 break;
2955 if (current_type == HELP_ANY) {
2956 log_error("bad help-generate category \"%s\"", gen_str);
2957 continue;
2960 if (!booted) {
2961 if (current_type == HELP_EXTRA) {
2962 size_t ncats;
2964 /* Avoid warnings about entries unused on this round,
2965 * when the entries in question are valid once help system has been booted */
2966 (void) secfile_lookup_str_vec(sf, &ncats,
2967 "%s.categories", sec_name);
2969 continue; /* on initial boot data tables are empty */
2973 /* Note these should really fill in pitem->text from auto-gen
2974 data instead of doing it later on the fly, but I don't want
2975 to change that now. --dwp
2977 char name[2048];
2978 struct help_list *category_nodes = help_list_new();
2980 switch (current_type) {
2981 case HELP_UNIT:
2982 unit_type_iterate(punittype) {
2983 pitem = new_help_item(current_type);
2984 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
2985 utype_name_translation(punittype));
2986 pitem->topic = fc_strdup(name);
2987 pitem->text = fc_strdup("");
2988 help_list_append(category_nodes, pitem);
2989 } unit_type_iterate_end;
2990 break;
2991 case HELP_TECH:
2992 advance_index_iterate(A_FIRST, advi) {
2993 if (valid_advance_by_number(advi)) {
2994 pitem = new_help_item(current_type);
2995 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
2996 advance_name_translation(advance_by_number(advi)));
2997 pitem->topic = fc_strdup(name);
2998 pitem->text = fc_strdup("");
2999 help_list_append(category_nodes, pitem);
3001 } advance_index_iterate_end;
3002 break;
3003 case HELP_TERRAIN:
3004 terrain_type_iterate(pterrain) {
3005 if (0 != strlen(terrain_rule_name(pterrain))) {
3006 pitem = new_help_item(current_type);
3007 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3008 terrain_name_translation(pterrain));
3009 pitem->topic = fc_strdup(name);
3010 pitem->text = fc_strdup("");
3011 help_list_append(category_nodes, pitem);
3013 } terrain_type_iterate_end;
3014 break;
3015 case HELP_EXTRA:
3017 const char **cats;
3018 size_t ncats;
3019 cats = secfile_lookup_str_vec(sf, &ncats,
3020 "%s.categories", sec_name);
3021 extra_type_iterate(pextra) {
3022 /* If categories not specified, don't filter */
3023 if (cats) {
3024 bool include = FALSE;
3025 const char *cat = extra_category_name(pextra->category);
3026 int ci;
3028 for (ci = 0; ci < ncats; ci++) {
3029 if (fc_strcasecmp(cats[ci], cat) == 0) {
3030 include = TRUE;
3031 break;
3034 if (!include) {
3035 continue;
3038 pitem = new_help_item(current_type);
3039 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3040 extra_name_translation(pextra));
3041 pitem->topic = fc_strdup(name);
3042 pitem->text = fc_strdup("");
3043 help_list_append(category_nodes, pitem);
3044 } extra_type_iterate_end;
3045 FC_FREE(cats);
3047 break;
3048 case HELP_SPECIALIST:
3049 specialist_type_iterate(sp) {
3050 struct specialist *pspec = specialist_by_number(sp);
3051 pitem = new_help_item(current_type);
3052 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3053 specialist_plural_translation(pspec));
3054 pitem->topic = fc_strdup(name);
3055 pitem->text = fc_strdup("");
3056 help_list_append(category_nodes, pitem);
3057 } specialist_type_iterate_end;
3058 break;
3059 case HELP_GOVERNMENT:
3060 governments_iterate(gov) {
3061 pitem = new_help_item(current_type);
3062 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3063 government_name_translation(gov));
3064 pitem->topic = fc_strdup(name);
3065 pitem->text = fc_strdup("");
3066 help_list_append(category_nodes, pitem);
3067 } governments_iterate_end;
3068 break;
3069 case HELP_IMPROVEMENT:
3070 improvement_iterate(pimprove) {
3071 if (valid_improvement(pimprove) && !is_great_wonder(pimprove)) {
3072 pitem = new_help_item(current_type);
3073 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3074 improvement_name_translation(pimprove));
3075 pitem->topic = fc_strdup(name);
3076 pitem->text = fc_strdup("");
3077 help_list_append(category_nodes, pitem);
3079 } improvement_iterate_end;
3080 break;
3081 case HELP_WONDER:
3082 improvement_iterate(pimprove) {
3083 if (valid_improvement(pimprove) && is_great_wonder(pimprove)) {
3084 pitem = new_help_item(current_type);
3085 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3086 improvement_name_translation(pimprove));
3087 pitem->topic = fc_strdup(name);
3088 pitem->text = fc_strdup("");
3089 help_list_append(category_nodes, pitem);
3091 } improvement_iterate_end;
3092 break;
3093 case HELP_RULESET:
3095 int desc_len;
3096 int len;
3098 pitem = new_help_item(HELP_RULESET);
3099 /* pitem->topic = fc_strdup(_(game.control.name)); */
3100 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3101 Q_(HELP_RULESET_ITEM));
3102 pitem->topic = fc_strdup(name);
3103 if (game.ruleset_description != NULL) {
3104 desc_len = strlen("\n\n") + strlen(game.ruleset_description);
3105 } else {
3106 desc_len = 0;
3108 if (game.ruleset_summary != NULL) {
3109 if (game.control.version[0] != '\0') {
3110 len = strlen(_(game.control.name))
3111 + strlen(" ")
3112 + strlen(game.control.version)
3113 + strlen("\n\n")
3114 + strlen(_(game.ruleset_summary))
3115 + 1;
3117 pitem->text = fc_malloc(len + desc_len);
3118 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3119 _(game.control.name), game.control.version,
3120 _(game.ruleset_summary));
3121 } else {
3122 len = strlen(_(game.control.name))
3123 + strlen("\n\n")
3124 + strlen(_(game.ruleset_summary))
3125 + 1;
3127 pitem->text = fc_malloc(len + desc_len);
3128 fc_snprintf(pitem->text, len, "%s\n\n%s",
3129 _(game.control.name), _(game.ruleset_summary));
3131 } else {
3132 const char *nodesc = _("Current ruleset contains no summary.");
3134 if (game.control.version[0] != '\0') {
3135 len = strlen(_(game.control.name))
3136 + strlen(" ")
3137 + strlen(game.control.version)
3138 + strlen("\n\n")
3139 + strlen(nodesc)
3140 + 1;
3142 pitem->text = fc_malloc(len + desc_len);
3143 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3144 _(game.control.name), game.control.version,
3145 nodesc);
3146 } else {
3147 len = strlen(_(game.control.name))
3148 + strlen("\n\n")
3149 + strlen(nodesc)
3150 + 1;
3152 pitem->text = fc_malloc(len + desc_len);
3153 fc_snprintf(pitem->text, len, "%s\n\n%s",
3154 _(game.control.name),
3155 nodesc);
3158 if (game.ruleset_description != NULL) {
3159 fc_strlcat(pitem->text, "\n\n", len + desc_len);
3160 fc_strlcat(pitem->text, game.ruleset_description, len + desc_len);
3162 help_list_append(help_nodes, pitem);
3164 break;
3165 case HELP_TILESET:
3167 int desc_len;
3168 int len;
3169 const char *ts_name = tileset_name_get(tileset);
3170 const char *version = tileset_version(tileset);
3171 const char *summary = tileset_summary(tileset);
3172 const char *description = tileset_description(tileset);
3174 pitem = new_help_item(HELP_TILESET);
3175 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3176 Q_(HELP_TILESET_ITEM));
3177 pitem->topic = fc_strdup(name);
3178 if (description != NULL) {
3179 desc_len = strlen("\n\n") + strlen(description);
3180 } else {
3181 desc_len = 0;
3183 if (summary != NULL) {
3184 if (version[0] != '\0') {
3185 len = strlen(_(ts_name))
3186 + strlen(" ")
3187 + strlen(version)
3188 + strlen("\n\n")
3189 + strlen(_(summary))
3190 + 1;
3192 pitem->text = fc_malloc(len + desc_len);
3193 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3194 _(ts_name), version, _(summary));
3195 } else {
3196 len = strlen(_(ts_name))
3197 + strlen("\n\n")
3198 + strlen(_(summary))
3199 + 1;
3201 pitem->text = fc_malloc(len + desc_len);
3202 fc_snprintf(pitem->text, len, "%s\n\n%s",
3203 _(ts_name), _(summary));
3205 } else {
3206 const char *nodesc = _("Current tileset contains no summary.");
3208 if (version[0] != '\0') {
3209 len = strlen(_(ts_name))
3210 + strlen(" ")
3211 + strlen(version)
3212 + strlen("\n\n")
3213 + strlen(nodesc)
3214 + 1;
3216 pitem->text = fc_malloc(len + desc_len);
3217 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3218 _(ts_name), version,
3219 nodesc);
3220 } else {
3221 len = strlen(_(ts_name))
3222 + strlen("\n\n")
3223 + strlen(nodesc)
3224 + 1;
3226 pitem->text = fc_malloc(len + desc_len);
3227 fc_snprintf(pitem->text, len, "%s\n\n%s",
3228 _(ts_name),
3229 nodesc);
3232 if (description != NULL) {
3233 fc_strlcat(pitem->text, "\n\n", len + desc_len);
3234 fc_strlcat(pitem->text, description, len + desc_len);
3236 help_list_append(help_nodes, pitem);
3238 break;
3239 case HELP_NATIONS:
3240 nations_iterate(pnation) {
3241 if (client_state() < C_S_RUNNING
3242 || show_help_for_nation(pnation)) {
3243 pitem = new_help_item(current_type);
3244 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3245 nation_plural_translation(pnation));
3246 pitem->topic = fc_strdup(name);
3247 pitem->text = fc_strdup("");
3248 help_list_append(category_nodes, pitem);
3250 } nations_iterate_end;
3251 break;
3252 case HELP_MULTIPLIER:
3253 multipliers_iterate(pmul) {
3254 help_text_buffer[0] = '\0';
3255 pitem = new_help_item(current_type);
3256 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3257 name_translation_get(&pmul->name));
3258 pitem->topic = fc_strdup(name);
3259 if (pmul->helptext) {
3260 const char *sep = "";
3261 strvec_iterate(pmul->helptext, text) {
3262 cat_snprintf(help_text_buffer, sizeof(help_text_buffer),
3263 "%s%s", sep, text);
3264 sep = "\n\n";
3265 } strvec_iterate_end;
3267 pitem->text = fc_strdup(help_text_buffer);
3268 help_list_append(help_nodes, pitem);
3269 } multipliers_iterate_end;
3270 break;
3271 default:
3272 log_error("Bad current_type: %d.", current_type);
3273 break;
3275 help_list_sort(category_nodes, help_item_compar);
3276 help_list_iterate(category_nodes, ptmp) {
3277 help_list_append(help_nodes, ptmp);
3278 } help_list_iterate_end;
3279 help_list_destroy(category_nodes);
3280 continue;
3284 /* It wasn't a "generate" node: */
3286 pitem = new_help_item(HELP_TEXT);
3287 pitem->topic = fc_strdup(Q_(secfile_lookup_str(sf, "%s.name",
3288 sec_name)));
3290 paras = secfile_lookup_str_vec(sf, &npara, "%s.text", sec_name);
3292 long_buffer[0] = '\0';
3293 for (i=0; i<npara; i++) {
3294 bool inserted;
3295 const char *para = paras[i];
3296 if(strncmp(para, "$", 1)==0) {
3297 inserted =
3298 insert_generated_text(long_buffer, sizeof(long_buffer), para+1);
3299 } else {
3300 sz_strlcat(long_buffer, _(para));
3301 inserted = TRUE;
3303 if (inserted && i!=npara-1) {
3304 sz_strlcat(long_buffer, "\n\n");
3307 free(paras);
3308 paras = NULL;
3309 pitem->text=fc_strdup(long_buffer);
3310 help_list_append(help_nodes, pitem);
3311 } section_list_iterate_end;
3313 section_list_destroy(sec);
3316 secfile_check_unused(sf);
3317 secfile_destroy(sf);
3318 booted = TRUE;
3319 log_verbose("Booted help texts ok");
3322 /****************************************************************
3323 The following few functions are essentially wrappers for the
3324 help_nodes help_list. This allows us to avoid exporting the
3325 help_list, and instead only access it through a controlled
3326 interface.
3327 *****************************************************************/
3329 /****************************************************************
3330 Number of help items.
3331 *****************************************************************/
3332 int num_help_items(void)
3334 check_help_nodes_init();
3335 return help_list_size(help_nodes);
3338 /****************************************************************
3339 Return pointer to given help_item.
3340 Returns NULL for 1 past end.
3341 Returns NULL and prints error message for other out-of bounds.
3342 *****************************************************************/
3343 const struct help_item *get_help_item(int pos)
3345 int size;
3347 check_help_nodes_init();
3348 size = help_list_size(help_nodes);
3349 if (pos < 0 || pos > size) {
3350 log_error("Bad index %d to get_help_item (size %d)", pos, size);
3351 return NULL;
3353 if (pos == size) {
3354 return NULL;
3356 return help_list_get(help_nodes, pos);
3359 /****************************************************************
3360 Find help item by name and type.
3361 Returns help item, and sets (*pos) to position in list.
3362 If no item, returns pointer to static internal item with
3363 some faked data, and sets (*pos) to -1.
3364 *****************************************************************/
3365 const struct help_item*
3366 get_help_item_spec(const char *name, enum help_page_type htype, int *pos)
3368 int idx;
3369 const struct help_item *pitem = NULL;
3370 static struct help_item vitem; /* v = virtual */
3371 static char vtopic[128];
3372 static char vtext[256];
3374 check_help_nodes_init();
3375 idx = 0;
3376 help_list_iterate(help_nodes, ptmp) {
3377 char *p=ptmp->topic;
3378 while (*p == ' ') {
3379 p++;
3381 if(strcmp(name, p)==0 && (htype==HELP_ANY || htype==ptmp->type)) {
3382 pitem = ptmp;
3383 break;
3385 idx++;
3387 help_list_iterate_end;
3389 if(!pitem) {
3390 idx = -1;
3391 vitem.topic = vtopic;
3392 sz_strlcpy(vtopic, name);
3393 vitem.text = vtext;
3394 if(htype==HELP_ANY || htype==HELP_TEXT) {
3395 fc_snprintf(vtext, sizeof(vtext),
3396 _("Sorry, no help topic for %s.\n"), vitem.topic);
3397 vitem.type = HELP_TEXT;
3398 } else {
3399 fc_snprintf(vtext, sizeof(vtext),
3400 _("Sorry, no help topic for %s.\n"
3401 "This page was auto-generated.\n\n"),
3402 vitem.topic);
3403 vitem.type = htype;
3405 pitem = &vitem;
3407 *pos = idx;
3408 return pitem;
3411 /****************************************************************
3412 Start iterating through help items;
3413 that is, reset iterator to start position.
3414 (Could iterate using get_help_item(), but that would be
3415 less efficient due to scanning to find pos.)
3416 *****************************************************************/
3417 void help_iter_start(void)
3419 check_help_nodes_init();
3420 help_nodes_iterator = help_list_head(help_nodes);
3423 /****************************************************************
3424 Returns next help item; after help_iter_start(), this is
3425 the first item. At end, returns NULL.
3426 *****************************************************************/
3427 const struct help_item *help_iter_next(void)
3429 const struct help_item *pitem;
3431 check_help_nodes_init();
3432 pitem = help_list_link_data(help_nodes_iterator);
3433 if (pitem) {
3434 help_nodes_iterator = help_list_link_next(help_nodes_iterator);
3437 return pitem;
3441 /****************************************************************
3442 FIXME:
3443 Also, in principle these could be auto-generated once, inserted
3444 into pitem->text, and then don't need to keep re-generating them.
3445 Only thing to be careful of would be changeable data, but don't
3446 have that here (for ruleset change or spacerace change must
3447 re-boot helptexts anyway). Eg, genuinely dynamic information
3448 which could be useful would be if help system said which wonders
3449 have been built (or are being built and by who/where?)
3450 *****************************************************************/
3452 /**************************************************************************
3453 Write dynamic text for buildings (including wonders). This includes
3454 the ruleset helptext as well as any automatically generated text.
3456 pplayer may be NULL.
3457 user_text, if non-NULL, will be appended to the text.
3458 **************************************************************************/
3459 char *helptext_building(char *buf, size_t bufsz, struct player *pplayer,
3460 const char *user_text, struct impr_type *pimprove)
3462 bool reqs = FALSE;
3463 struct universal source = {
3464 .kind = VUT_IMPROVEMENT,
3465 .value = {.building = pimprove}
3468 fc_assert_ret_val(NULL != buf && 0 < bufsz, NULL);
3469 buf[0] = '\0';
3471 if (NULL == pimprove) {
3472 return buf;
3475 if (NULL != pimprove->helptext) {
3476 strvec_iterate(pimprove->helptext, text) {
3477 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3478 } strvec_iterate_end;
3481 /* Add requirement text for improvement itself */
3482 requirement_vector_iterate(&pimprove->reqs, preq) {
3483 if (insert_requirement(buf, bufsz, pplayer, preq)) {
3484 reqs = TRUE;
3486 } requirement_vector_iterate_end;
3487 if (reqs) {
3488 fc_strlcat(buf, "\n", bufsz);
3491 requirement_vector_iterate(&pimprove->obsolete_by, pobs) {
3492 if (VUT_ADVANCE == pobs->source.kind && pobs->present) {
3493 cat_snprintf(buf, bufsz,
3494 _("* The discovery of %s will make %s obsolete.\n"),
3495 advance_name_translation(pobs->source.value.advance),
3496 improvement_name_translation(pimprove));
3498 if (VUT_IMPROVEMENT == pobs->source.kind && pobs->present) {
3499 cat_snprintf(buf, bufsz,
3500 /* TRANS: both %s are improvement names */
3501 _("* The presence of %s in the city will make %s "
3502 "obsolete.\n"),
3503 improvement_name_translation(pobs->source.value.building),
3504 improvement_name_translation(pimprove));
3506 } requirement_vector_iterate_end;
3508 if (is_small_wonder(pimprove)) {
3509 cat_snprintf(buf, bufsz,
3510 _("* A 'small wonder': at most one of your cities may "
3511 "possess this improvement.\n"));
3513 /* (Great wonders are in their own help section explaining their
3514 * uniqueness, so we don't mention it here.) */
3516 if (building_has_effect(pimprove, EFT_ENABLE_NUKE)
3517 && num_role_units(UTYF_NUCLEAR) > 0) {
3518 struct unit_type *u = get_role_unit(UTYF_NUCLEAR, 0);
3520 cat_snprintf(buf, bufsz,
3521 /* TRANS: 'Allows all players with knowledge of atomic
3522 * power to build nuclear units.' */
3523 _("* Allows all players with knowledge of %s "
3524 "to build %s units.\n"),
3525 advance_name_translation(u->require_advance),
3526 utype_name_translation(u));
3529 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
3531 unit_type_iterate(u) {
3532 if (u->need_improvement == pimprove) {
3533 if (A_NEVER != u->require_advance) {
3534 cat_snprintf(buf, bufsz, _("* Allows %s (with %s).\n"),
3535 utype_name_translation(u),
3536 advance_name_translation(u->require_advance));
3537 } else {
3538 cat_snprintf(buf, bufsz, _("* Allows %s.\n"),
3539 utype_name_translation(u));
3542 } unit_type_iterate_end;
3545 int i;
3547 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
3548 Impr_type_id n = game.rgame.global_init_buildings[i];
3549 if (n == B_LAST) {
3550 break;
3551 } else if (improvement_by_number(n) == pimprove) {
3552 cat_snprintf(buf, bufsz,
3553 _("* All players start with this improvement in their "
3554 "first city.\n"));
3555 break;
3560 /* Assume no-one will set the same building in both global and nation
3561 * init_buildings... */
3562 nations_iterate(pnation) {
3563 int i;
3565 /* Avoid mentioning nations not in current set. */
3566 if (!show_help_for_nation(pnation)) {
3567 continue;
3569 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
3570 Impr_type_id n = pnation->init_buildings[i];
3571 if (n == B_LAST) {
3572 break;
3573 } else if (improvement_by_number(n) == pimprove) {
3574 cat_snprintf(buf, bufsz,
3575 /* TRANS: %s is a nation plural */
3576 _("* The %s start with this improvement in their "
3577 "first city.\n"), nation_plural_translation(pnation));
3578 break;
3581 } nations_iterate_end;
3583 if (improvement_has_flag(pimprove, IF_SAVE_SMALL_WONDER)) {
3584 cat_snprintf(buf, bufsz,
3585 /* TRANS: don't translate 'savepalace' */
3586 _("* If you lose the city containing this improvement, "
3587 "it will be rebuilt for free in another of your cities "
3588 "(if the 'savepalace' server setting is enabled).\n"));
3591 if (user_text && user_text[0] != '\0') {
3592 cat_snprintf(buf, bufsz, "\n\n%s", user_text);
3594 return buf;
3597 /****************************************************************
3598 Is unit type ever able to build an extra
3599 *****************************************************************/
3600 static bool help_is_extra_buildable(struct extra_type *pextra,
3601 struct unit_type *ptype)
3603 if (!pextra->buildable) {
3604 return FALSE;
3607 return are_reqs_active(NULL, NULL, NULL, NULL, NULL,
3608 NULL, ptype, NULL, NULL, &pextra->reqs,
3609 RPT_POSSIBLE);
3612 /****************************************************************
3613 Is unit type ever able to clean out an extra
3614 *****************************************************************/
3615 static bool help_is_extra_cleanable(struct extra_type *pextra,
3616 struct unit_type *ptype)
3618 return are_reqs_active(NULL, NULL, NULL, NULL, NULL,
3619 NULL, ptype, NULL, NULL, &pextra->rmreqs,
3620 RPT_POSSIBLE);
3623 /****************************************************************
3624 Append misc dynamic text for units.
3625 Transport capacity, unit flags, fuel.
3627 pplayer may be NULL.
3628 *****************************************************************/
3629 char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
3630 const char *user_text, struct unit_type *utype)
3632 bool has_vet_levels;
3633 int flagid;
3634 struct unit_class *pclass;
3636 fc_assert_ret_val(NULL != buf && 0 < bufsz && NULL != user_text, NULL);
3638 if (!utype) {
3639 log_error("Unknown unit!");
3640 fc_strlcpy(buf, user_text, bufsz);
3641 return buf;
3644 has_vet_levels = utype_veteran_levels(utype) > 1;
3646 buf[0] = '\0';
3648 pclass = utype_class(utype);
3649 cat_snprintf(buf, bufsz,
3650 _("* Belongs to %s unit class."),
3651 uclass_name_translation(pclass));
3652 if (NULL != pclass->helptext) {
3653 strvec_iterate(pclass->helptext, text) {
3654 cat_snprintf(buf, bufsz, "\n%s\n", _(text));
3655 } strvec_iterate_end;
3656 } else {
3657 CATLSTR(buf, bufsz, "\n");
3659 if (uclass_has_flag(pclass, UCF_CAN_OCCUPY_CITY)
3660 && !utype_has_flag(utype, UTYF_CIVILIAN)) {
3661 CATLSTR(buf, bufsz, _(" * Can occupy empty enemy cities.\n"));
3663 if (!uclass_has_flag(pclass, UCF_TERRAIN_SPEED)) {
3664 CATLSTR(buf, bufsz, _(" * Speed is not affected by terrain.\n"));
3666 if (!uclass_has_flag(pclass, UCF_TERRAIN_DEFENSE)) {
3667 CATLSTR(buf, bufsz, _(" * Does not get defense bonuses from terrain.\n"));
3669 if (!uclass_has_flag(pclass, UCF_ZOC)) {
3670 CATLSTR(buf, bufsz, _(" * Not subject to zones of control.\n"));
3671 } else if (!utype_has_flag(utype, UTYF_IGZOC)) {
3672 CATLSTR(buf, bufsz, _(" * Subject to zones of control.\n"));
3674 if (uclass_has_flag(pclass, UCF_DAMAGE_SLOWS)) {
3675 CATLSTR(buf, bufsz, _(" * Slowed down while damaged.\n"));
3677 if (uclass_has_flag(pclass, UCF_MISSILE)) {
3678 CATLSTR(buf, bufsz, _(" * Gets used up in making an attack.\n"));
3680 if (uclass_has_flag(pclass, UCF_CAN_FORTIFY)
3681 && !utype_has_flag(utype, UTYF_CANT_FORTIFY)) {
3682 if (utype->defense_strength > 0) {
3683 CATLSTR(buf, bufsz,
3684 /* xgettext:no-c-format */
3685 _(" * Gets a 50% defensive bonus while in cities.\n"));
3686 CATLSTR(buf, bufsz,
3687 /* xgettext:no-c-format */
3688 _(" * May fortify, granting a 50% defensive bonus when not in "
3689 "a city.\n"));
3690 } else {
3691 CATLSTR(buf, bufsz,
3692 _(" * May fortify to stay put.\n"));
3695 if (uclass_has_flag(pclass, UCF_UNREACHABLE)) {
3696 CATLSTR(buf, bufsz,
3697 _(" * Is unreachable. Most units cannot attack this one.\n"));
3699 if (uclass_has_flag(pclass, UCF_CAN_PILLAGE)) {
3700 CATLSTR(buf, bufsz,
3701 _(" * Can pillage tile improvements.\n"));
3703 if (uclass_has_flag(pclass, UCF_DOESNT_OCCUPY_TILE)
3704 && !utype_has_flag(utype, UTYF_CIVILIAN)) {
3705 CATLSTR(buf, bufsz,
3706 _(" * Doesn't prevent enemy cities from working the tile it's on.\n"));
3708 if (can_attack_non_native(utype)) {
3709 CATLSTR(buf, bufsz,
3710 _(" * Can attack units on non-native tiles.\n"));
3712 /* Must use flag to distinguish from UTYF_MARINES text. */
3713 if (utype->attack_strength > 0
3714 && uclass_has_flag(pclass, UCF_ATT_FROM_NON_NATIVE)) {
3715 CATLSTR(buf, bufsz,
3716 _(" * Can launch attack from non-native tiles.\n"));
3718 if (uclass_has_flag(pclass, UCF_AIRLIFTABLE)) {
3719 CATLSTR(buf, bufsz,
3720 _(" * Can be airlifted from a suitable city.\n"));
3723 /* The unit's combat bonuses. Won't mention that another unit type has a
3724 * combat bonus against this unit type. Doesn't handle complex cases like
3725 * when a unit type has multiple combat bonuses of the same kind. */
3726 combat_bonus_list_iterate(utype->bonuses, cbonus) {
3727 const char *against[utype_count()];
3728 int targets = 0;
3730 if (cbonus->quiet) {
3731 /* Handled in the help text of the ruleset. */
3732 continue;
3735 /* Find the unit types of the bonus targets. */
3736 unit_type_iterate(utype2) {
3737 if (utype_has_flag(utype2, cbonus->flag)) {
3738 against[targets++] = utype_name_translation(utype2);
3740 } unit_type_iterate_end;
3742 if (targets > 0) {
3743 struct astring list = ASTRING_INIT;
3745 switch (cbonus->type) {
3746 case CBONUS_DEFENSE_MULTIPLIER:
3747 cat_snprintf(buf, bufsz,
3748 /* TRANS: multipied by ... or-list of unit types */
3749 _("* %dx defense bonus if attacked by %s.\n"),
3750 cbonus->value + 1,
3751 astr_build_or_list(&list, against, targets));
3752 break;
3753 case CBONUS_DEFENSE_DIVIDER:
3754 cat_snprintf(buf, bufsz,
3755 /* TRANS: defense divider ... or-list of unit types */
3756 _("* reduces target's defense to 1 / %d when "
3757 "attacking %s.\n"),
3758 cbonus->value + 1,
3759 astr_build_or_list(&list, against, targets));
3760 break;
3761 case CBONUS_FIREPOWER1:
3762 cat_snprintf(buf, bufsz,
3763 /* TRANS: or-list of unit types */
3764 _("* reduces target's fire power to 1 when "
3765 "attacking %s.\n"),
3766 astr_build_and_list(&list, against, targets));
3767 break;
3770 astr_free(&list);
3772 } combat_bonus_list_iterate_end;
3774 if (utype->need_improvement) {
3775 cat_snprintf(buf, bufsz,
3776 _("* Can only be built if there is %s in the city.\n"),
3777 improvement_name_translation(utype->need_improvement));
3780 if (utype->need_government) {
3781 cat_snprintf(buf, bufsz,
3782 _("* Can only be built with %s as government.\n"),
3783 government_name_translation(utype->need_government));
3786 if (utype_has_flag(utype, UTYF_NOBUILD)) {
3787 CATLSTR(buf, bufsz, _("* May not be built in cities.\n"));
3789 if (utype_has_flag(utype, UTYF_BARBARIAN_ONLY)) {
3790 CATLSTR(buf, bufsz, _("* Only barbarians may build this.\n"));
3792 if (utype_has_flag(utype, UTYF_NEWCITY_GAMES_ONLY)) {
3793 CATLSTR(buf, bufsz, _("* Can only be built in games where new cities "
3794 "are allowed.\n"));
3795 if (game.scenario.prevent_new_cities) {
3796 CATLSTR(buf, bufsz, _(" - New cities are not allowed in the current "
3797 "game.\n"));
3798 } else {
3799 CATLSTR(buf, bufsz, _(" - New cities are allowed in the current "
3800 "game.\n"));
3803 nations_iterate(pnation) {
3804 int i, count = 0;
3806 /* Avoid mentioning nations not in current set. */
3807 if (!show_help_for_nation(pnation)) {
3808 continue;
3810 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
3811 if (!pnation->init_units[i]) {
3812 break;
3813 } else if (pnation->init_units[i] == utype) {
3814 count++;
3817 if (count > 0) {
3818 cat_snprintf(buf, bufsz,
3819 /* TRANS: %s is a nation plural */
3820 PL_("* The %s start the game with %d of these units.\n",
3821 "* The %s start the game with %d of these units.\n",
3822 count),
3823 nation_plural_translation(pnation), count);
3825 } nations_iterate_end;
3827 const char *types[utype_count()];
3828 int i = 0;
3829 unit_type_iterate(utype2) {
3830 if (utype2->converted_to == utype) {
3831 types[i++] = utype_name_translation(utype2);
3833 } unit_type_iterate_end;
3834 if (i > 0) {
3835 struct astring list = ASTRING_INIT;
3836 astr_build_or_list(&list, types, i);
3837 cat_snprintf(buf, bufsz,
3838 /* TRANS: %s is a list of unit types separated by "or". */
3839 _("* May be obtained by conversion of %s.\n"),
3840 astr_str(&list));
3841 astr_free(&list);
3844 if (NULL != utype->converted_to) {
3845 cat_snprintf(buf, bufsz,
3846 /* TRANS: %s is a unit type. "MP" = movement points. */
3847 PL_("* May be converted into %s (takes %d MP).\n",
3848 "* May be converted into %s (takes %d MP).\n",
3849 utype->convert_time),
3850 utype_name_translation(utype->converted_to),
3851 utype->convert_time);
3853 if (utype_has_flag(utype, UTYF_NOHOME)) {
3854 CATLSTR(buf, bufsz, _("* Never has a home city.\n"));
3856 if (utype_has_flag(utype, UTYF_GAMELOSS)) {
3857 CATLSTR(buf, bufsz, _("* Losing this unit will lose you the game!\n"));
3859 if (utype_has_flag(utype, UTYF_UNIQUE)) {
3860 CATLSTR(buf, bufsz,
3861 _("* Each player may only have one of this type of unit.\n"));
3863 for (flagid = UTYF_USER_FLAG_1 ; flagid <= UTYF_LAST_USER_FLAG; flagid++) {
3864 if (utype_has_flag(utype, flagid)) {
3865 const char *helptxt = unit_type_flag_helptxt(flagid);
3867 if (helptxt != NULL) {
3868 CATLSTR(buf, bufsz, Q_("?bullet:* "));
3869 CATLSTR(buf, bufsz, _(helptxt));
3870 CATLSTR(buf, bufsz, "\n");
3874 if (utype->pop_cost > 0) {
3875 cat_snprintf(buf, bufsz,
3876 PL_("* Costs %d population to build.\n",
3877 "* Costs %d population to build.\n", utype->pop_cost),
3878 utype->pop_cost);
3880 if (0 < utype->transport_capacity) {
3881 const char *classes[uclass_count()];
3882 int i = 0;
3883 struct astring list = ASTRING_INIT;
3885 unit_class_iterate(uclass) {
3886 if (can_unit_type_transport(utype, uclass)) {
3887 classes[i++] = uclass_name_translation(uclass);
3889 } unit_class_iterate_end;
3890 astr_build_or_list(&list, classes, i);
3892 cat_snprintf(buf, bufsz,
3893 /* TRANS: %s is a list of unit classes separated by "or". */
3894 PL_("* Can carry and refuel %d %s unit.\n",
3895 "* Can carry and refuel up to %d %s units.\n",
3896 utype->transport_capacity),
3897 utype->transport_capacity, astr_str(&list));
3898 astr_free(&list);
3899 if (uclass_has_flag(utype_class(utype), UCF_UNREACHABLE)) {
3900 /* Document restrictions on when units can load/unload */
3901 bool has_restricted_load = FALSE, has_unrestricted_load = FALSE,
3902 has_restricted_unload = FALSE, has_unrestricted_unload = FALSE;
3903 unit_type_iterate(pcargo) {
3904 if (can_unit_type_transport(utype, utype_class(pcargo))) {
3905 if (utype_can_freely_load(pcargo, utype)) {
3906 has_unrestricted_load = TRUE;
3907 } else {
3908 has_restricted_load = TRUE;
3910 if (utype_can_freely_unload(pcargo, utype)) {
3911 has_unrestricted_unload = TRUE;
3912 } else {
3913 has_restricted_unload = TRUE;
3916 } unit_type_iterate_end;
3917 if (has_restricted_load) {
3918 if (has_unrestricted_load) {
3919 /* At least one type of cargo can load onto us freely.
3920 * The specific exceptions will be documented in cargo help. */
3921 CATLSTR(buf, bufsz,
3922 _(" * Some cargo cannot be loaded except in a city or a "
3923 "base native to this transport.\n"));
3924 } else {
3925 /* No exceptions */
3926 CATLSTR(buf, bufsz,
3927 _(" * Cargo cannot be loaded except in a city or a "
3928 "base native to this transport.\n"));
3930 } /* else, no restricted cargo exists; keep quiet */
3931 if (has_restricted_unload) {
3932 if (has_unrestricted_unload) {
3933 /* At least one type of cargo can unload from us freely. */
3934 CATLSTR(buf, bufsz,
3935 _(" * Some cargo cannot be unloaded except in a city or a "
3936 "base native to this transport.\n"));
3937 } else {
3938 /* No exceptions */
3939 CATLSTR(buf, bufsz,
3940 _(" * Cargo cannot be unloaded except in a city or a "
3941 "base native to this transport.\n"));
3943 } /* else, no restricted cargo exists; keep quiet */
3946 if (utype_has_flag(utype, UTYF_TRIREME)) {
3947 CATLSTR(buf, bufsz, _("* Must stay next to coast.\n"));
3950 /* Document exceptions to embark/disembark restrictions that we
3951 * have as cargo. */
3952 bv_unit_classes embarks, disembarks;
3953 BV_CLR_ALL(embarks);
3954 BV_CLR_ALL(disembarks);
3955 /* Determine which of our transport classes have restrictions in the first
3956 * place (that is, contain at least one transport which carries at least
3957 * one type of cargo which is restricted).
3958 * We'll suppress output for classes not in this set, since this cargo
3959 * type is not behaving exceptionally in such cases. */
3960 unit_type_iterate(utrans) {
3961 const Unit_Class_id trans_class = uclass_index(utype_class(utrans));
3962 /* Don't waste time repeating checks on classes we've already checked,
3963 * or weren't under consideration in the first place */
3964 if (!BV_ISSET(embarks, trans_class)
3965 && BV_ISSET(utype->embarks, trans_class)) {
3966 unit_type_iterate(other_cargo) {
3967 if (can_unit_type_transport(utrans, utype_class(other_cargo))
3968 && !utype_can_freely_load(other_cargo, utrans)) {
3969 /* At least one load restriction in transport class, which
3970 * we aren't subject to */
3971 BV_SET(embarks, trans_class);
3973 } unit_type_iterate_end; /* cargo */
3975 if (!BV_ISSET(disembarks, trans_class)
3976 && BV_ISSET(utype->disembarks, trans_class)) {
3977 unit_type_iterate(other_cargo) {
3978 if (can_unit_type_transport(utrans, utype_class(other_cargo))
3979 && !utype_can_freely_unload(other_cargo, utrans)) {
3980 /* At least one load restriction in transport class, which
3981 * we aren't subject to */
3982 BV_SET(disembarks, trans_class);
3984 } unit_type_iterate_end; /* cargo */
3986 } unit_class_iterate_end; /* transports */
3988 if (BV_ISSET_ANY(embarks)) {
3989 /* Build list of embark exceptions */
3990 const char *eclasses[uclass_count()];
3991 int i = 0;
3992 struct astring elist = ASTRING_INIT;
3994 unit_class_iterate(uclass) {
3995 if (BV_ISSET(embarks, uclass_index(uclass))) {
3996 eclasses[i++] = uclass_name_translation(uclass);
3998 } unit_class_iterate_end;
3999 astr_build_or_list(&elist, eclasses, i);
4000 if (BV_ARE_EQUAL(embarks, disembarks)) {
4001 /* A common case: the list of disembark exceptions is identical */
4002 cat_snprintf(buf, bufsz,
4003 /* TRANS: %s is a list of unit classes separated
4004 * by "or". */
4005 _("* May load onto and unload from %s transports even "
4006 "when underway.\n"),
4007 astr_str(&elist));
4008 } else {
4009 cat_snprintf(buf, bufsz,
4010 /* TRANS: %s is a list of unit classes separated
4011 * by "or". */
4012 _("* May load onto %s transports even when underway.\n"),
4013 astr_str(&elist));
4015 astr_free(&elist);
4017 if (BV_ISSET_ANY(disembarks) && !BV_ARE_EQUAL(embarks, disembarks)) {
4018 /* Build list of disembark exceptions (if different from embarking) */
4019 const char *dclasses[uclass_count()];
4020 int i = 0;
4021 struct astring dlist = ASTRING_INIT;
4023 unit_class_iterate(uclass) {
4024 if (BV_ISSET(disembarks, uclass_index(uclass))) {
4025 dclasses[i++] = uclass_name_translation(uclass);
4027 } unit_class_iterate_end;
4028 astr_build_or_list(&dlist, dclasses, i);
4029 cat_snprintf(buf, bufsz,
4030 /* TRANS: %s is a list of unit classes separated
4031 * by "or". */
4032 _("* May unload from %s transports even when underway.\n"),
4033 astr_str(&dlist));
4034 astr_free(&dlist);
4037 if (utype_has_flag(utype, UTYF_UNDISBANDABLE)) {
4038 CATLSTR(buf, bufsz, _("* May not be disbanded.\n"));
4039 } else {
4040 CATLSTR(buf, bufsz,
4041 /* xgettext:no-c-format */
4042 _("* May be disbanded in a city to recover 50% of the"
4043 " production cost.\n"));
4045 if (utype_is_cityfounder(utype)) {
4046 cat_snprintf(buf, bufsz,
4047 PL_("* Can build new cities (initial population %d).\n",
4048 "* Can build new cities (initial population %d).\n",
4049 utype->city_size),
4050 utype->city_size);
4052 if (utype_has_flag(utype, UTYF_ADD_TO_CITY)) {
4053 cat_snprintf(buf, bufsz,
4054 /* TRANS: Plural in "%d population", not "size %d". */
4055 PL_("* Can add on %d population to cities of no more than"
4056 " size %d.\n",
4057 "* Can add on %d population to cities of no more than"
4058 " size %d.\n", utype_pop_value(utype)),
4059 utype_pop_value(utype),
4060 game.info.add_to_size_limit - utype_pop_value(utype));
4062 if (utype_has_flag(utype, UTYF_SETTLERS)) {
4063 struct universal for_utype = { .kind = VUT_UTYPE, .value = { .utype = utype }};
4064 struct astring extras_and = ASTRING_INIT;
4065 struct strvec *extras_vec = strvec_new();
4067 /* Roads, rail, mines, irrigation. */
4068 extra_type_by_cause_iterate(EC_ROAD, pextra) {
4069 if (help_is_extra_buildable(pextra, utype)) {
4070 strvec_append(extras_vec, extra_name_translation(pextra));
4072 } extra_type_by_cause_iterate_end;
4073 if (strvec_size(extras_vec) > 0) {
4074 strvec_to_and_list(extras_vec, &extras_and);
4075 /* TRANS: %s is list of extra types separated by ',' and 'and' */
4076 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4077 astr_str(&extras_and));
4078 strvec_clear(extras_vec);
4081 if (effect_cumulative_max(EFT_MINING_POSSIBLE, &for_utype) > 0) {
4082 extra_type_by_cause_iterate(EC_MINE, pextra) {
4083 if (help_is_extra_buildable(pextra, utype)) {
4084 strvec_append(extras_vec, extra_name_translation(pextra));
4086 } extra_type_by_cause_iterate_end;
4088 if (strvec_size(extras_vec) > 0) {
4089 strvec_to_and_list(extras_vec, &extras_and);
4090 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4091 astr_str(&extras_and));
4092 strvec_clear(extras_vec);
4095 if (effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_utype) > 0) {
4096 CATLSTR(buf, bufsz, _("* Can convert terrain to another type by "
4097 "mining.\n"));
4100 if (effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_utype) > 0) {
4101 extra_type_by_cause_iterate(EC_IRRIGATION, pextra) {
4102 if (help_is_extra_buildable(pextra, utype)) {
4103 strvec_append(extras_vec, extra_name_translation(pextra));
4105 } extra_type_by_cause_iterate_end;
4107 if (strvec_size(extras_vec) > 0) {
4108 strvec_to_and_list(extras_vec, &extras_and);
4109 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4110 astr_str(&extras_and));
4111 strvec_clear(extras_vec);
4114 if (effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_utype) > 0) {
4115 CATLSTR(buf, bufsz, _("* Can convert terrain to another type by "
4116 "irrigation.\n"));
4118 if (effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_utype) > 0) {
4119 CATLSTR(buf, bufsz, _("* Can transform terrain to another type.\n"));
4122 extra_type_by_cause_iterate(EC_BASE, pextra) {
4123 if (help_is_extra_buildable(pextra, utype)) {
4124 strvec_append(extras_vec, extra_name_translation(pextra));
4126 } extra_type_by_cause_iterate_end;
4128 if (strvec_size(extras_vec) > 0) {
4129 strvec_to_and_list(extras_vec, &extras_and);
4130 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4131 astr_str(&extras_and));
4132 strvec_clear(extras_vec);
4135 /* Pollution, fallout. */
4136 extra_type_by_rmcause_iterate(ERM_CLEANPOLLUTION, pextra) {
4137 if (help_is_extra_cleanable(pextra, utype)) {
4138 strvec_append(extras_vec, extra_name_translation(pextra));
4140 } extra_type_by_rmcause_iterate_end;
4142 if (strvec_size(extras_vec) > 0) {
4143 strvec_to_and_list(extras_vec, &extras_and);
4144 /* TRANS: list of extras separated by "and" */
4145 cat_snprintf(buf, bufsz, _("* Can clean %s from tiles.\n"),
4146 astr_str(&extras_and));
4147 strvec_clear(extras_vec);
4150 extra_type_by_rmcause_iterate(ERM_CLEANFALLOUT, pextra) {
4151 if (help_is_extra_cleanable(pextra, utype)) {
4152 strvec_append(extras_vec, extra_name_translation(pextra));
4154 } extra_type_by_rmcause_iterate_end;
4156 if (strvec_size(extras_vec) > 0) {
4157 strvec_to_and_list(extras_vec, &extras_and);
4158 /* TRANS: list of extras separated by "and" */
4159 cat_snprintf(buf, bufsz, _("* Can clean %s from tiles.\n"),
4160 astr_str(&extras_and));
4161 strvec_clear(extras_vec);
4164 strvec_destroy(extras_vec);
4167 if (utype_has_flag(utype, UTYF_SPY)) {
4168 CATLSTR(buf, bufsz, _("* Performs better diplomatic actions.\n"));
4170 if (utype_has_flag(utype, UTYF_DIPLOMAT)
4171 || utype_has_flag(utype, UTYF_SUPERSPY)) {
4172 CATLSTR(buf, bufsz, _("* Defends cities against diplomatic actions.\n"));
4174 if (utype_has_flag(utype, UTYF_SUPERSPY)) {
4175 CATLSTR(buf, bufsz, _("* Will never lose a diplomat-versus-diplomat fight.\n"));
4177 if (utype_has_flag(utype, UTYF_SPY)
4178 && utype_has_flag(utype, UTYF_SUPERSPY)) {
4179 CATLSTR(buf, bufsz, _("* Will always survive a spy mission.\n"));
4181 if (utype_has_flag(utype, UTYF_PARTIAL_INVIS)) {
4182 CATLSTR(buf, bufsz,
4183 _("* Is invisible except when next to an enemy unit or city.\n"));
4185 if (utype_has_flag(utype, UTYF_ONLY_NATIVE_ATTACK)) {
4186 CATLSTR(buf, bufsz,
4187 _("* Can only attack units on native tiles.\n"));
4189 /* Must use flag to distinguish from UCF_ATT_FROM_NON_NATIVE text. */
4190 if (utype_has_flag(utype, UTYF_MARINES)) {
4191 CATLSTR(buf, bufsz,
4192 _("* Can launch attack from non-native tiles.\n"));
4194 if (utype_has_flag(utype, UTYF_PARATROOPERS)) {
4195 cat_snprintf(buf, bufsz,
4196 PL_("* Can be paradropped from a friendly city or suitable "
4197 "base (range: %d tile).\n",
4198 "* Can be paradropped from a friendly city or suitable "
4199 "base (range: %d tiles).\n",
4200 utype->paratroopers_range),
4201 utype->paratroopers_range);
4203 if (!uclass_has_flag(utype_class(utype), UCF_MISSILE)
4204 && utype_has_flag(utype, UTYF_ONEATTACK)) {
4205 CATLSTR(buf, bufsz,
4206 _("* Making an attack ends this unit's turn.\n"));
4208 if (utype_has_flag(utype, UTYF_NUCLEAR)) {
4209 CATLSTR(buf, bufsz,
4210 _("* This unit's attack causes a nuclear explosion!\n"));
4212 if (utype_has_flag(utype, UTYF_CITYBUSTER)) {
4213 CATLSTR(buf, bufsz,
4214 _("* Gets double firepower when attacking cities.\n"));
4216 if (utype_has_flag(utype, UTYF_BOMBARDER)) {
4217 /* FIXME: also they only happen against land units. We leave the
4218 * ruleset author to document this. */
4219 cat_snprintf(buf, bufsz,
4220 _("* Does bombard attacks (%d per turn). These attacks will"
4221 " only damage (never kill) defenders, but damage all"
4222 " defenders on a tile, and have no risk for the"
4223 " attacker.\n"),
4224 utype->bombard_rate);
4226 if (utype_has_flag(utype, UTYF_IGTER)) {
4227 cat_snprintf(buf, bufsz,
4228 /* TRANS: "MP" = movement points. %s may have a
4229 * fractional part. */
4230 _("* Ignores terrain effects (moving costs at most %s MP "
4231 "per tile).\n"),
4232 move_points_text(terrain_control.igter_cost, TRUE));
4234 if (utype_has_flag(utype, UTYF_NOZOC)) {
4235 CATLSTR(buf, bufsz, _("* Never imposes a zone of control.\n"));
4236 } else {
4237 CATLSTR(buf, bufsz, _("* May impose a zone of control on its adjacent "
4238 "tiles.\n"));
4240 if (utype_has_flag(utype, UTYF_IGZOC)) {
4241 CATLSTR(buf, bufsz, _("* Not subject to zones of control imposed "
4242 "by other units.\n"));
4244 if (utype_has_flag(utype, UTYF_CIVILIAN)) {
4245 CATLSTR(buf, bufsz,
4246 _("* A non-military unit:\n"));
4247 CATLSTR(buf, bufsz,
4248 _(" * Cannot attack.\n"));
4249 CATLSTR(buf, bufsz,
4250 _(" * Doesn't impose martial law.\n"));
4251 CATLSTR(buf, bufsz,
4252 _(" * Can enter foreign territory regardless of peace treaty.\n"));
4253 CATLSTR(buf, bufsz,
4254 _(" * Doesn't prevent enemy cities from working the tile it's on.\n"));
4256 if (utype_has_flag(utype, UTYF_FIELDUNIT)) {
4257 CATLSTR(buf, bufsz,
4258 _("* A field unit: one unhappiness applies even when non-aggressive.\n"));
4260 if (utype_has_flag(utype, UTYF_CAPTURER)) {
4261 CATLSTR(buf, bufsz, _("* Can capture some enemy units.\n"));
4263 if (utype_has_flag(utype, UTYF_CAPTURABLE)) {
4264 CATLSTR(buf, bufsz, _("* Can be captured by some enemy units.\n"));
4266 if (utype_has_flag(utype, UTYF_SHIELD2GOLD)) {
4267 /* FIXME: the conversion shield => gold is activated if
4268 * EFT_SHIELD2GOLD_FACTOR is not equal null; how to determine
4269 * possible sources? */
4270 CATLSTR(buf, bufsz,
4271 _("* Under certain conditions the shield upkeep of this unit can "
4272 "be converted to gold upkeep.\n"));
4275 unit_class_iterate(target) {
4276 if (uclass_has_flag(target, UCF_UNREACHABLE)
4277 && BV_ISSET(utype->targets, uclass_index(target))) {
4278 cat_snprintf(buf, bufsz,
4279 _("* Can attack against %s units, which are usually not "
4280 "reachable.\n"),
4281 uclass_name_translation(target));
4283 } unit_class_iterate_end;
4284 if (utype_fuel(utype)) {
4285 const char *types[utype_count()];
4286 int i = 0;
4288 unit_type_iterate(transport) {
4289 if (can_unit_type_transport(transport, utype_class(utype))) {
4290 types[i++] = utype_name_translation(transport);
4292 } unit_type_iterate_end;
4294 if (0 == i) {
4295 cat_snprintf(buf, bufsz,
4296 PL_("* Unit has to be in a city or a base"
4297 " after %d turn.\n",
4298 "* Unit has to be in a city or a base"
4299 " after %d turns.\n",
4300 utype_fuel(utype)),
4301 utype_fuel(utype));
4302 } else {
4303 struct astring list = ASTRING_INIT;
4305 cat_snprintf(buf, bufsz,
4306 /* TRANS: %s is a list of unit types separated by "or" */
4307 PL_("* Unit has to be in a city, a base, or on a %s"
4308 " after %d turn.\n",
4309 "* Unit has to be in a city, a base, or on a %s"
4310 " after %d turns.\n",
4311 utype_fuel(utype)),
4312 astr_build_or_list(&list, types, i), utype_fuel(utype));
4313 astr_free(&list);
4316 action_iterate(act) {
4317 if (action_by_number(act)->quiet) {
4318 /* The ruleset documents this action it self. */
4319 continue;
4322 if (action_id_get_actor_kind(act) != AAK_UNIT) {
4323 continue;
4326 action_enabler_list_iterate(action_enablers_for_action(act), enabler) {
4327 if (requirement_fulfilled_by_unit_type(utype,
4328 &(enabler->actor_reqs))) {
4329 switch (act) {
4330 case ACTION_HELP_WONDER:
4331 cat_snprintf(buf, bufsz,
4332 /* TRANS: the first %s is the ruleset defined ui
4333 * name of the "Help Wonder" action, the next %s is
4334 * the name of its target kind ("individual cities")
4335 * and the %d is the number of shields the unit can
4336 * contribute. */
4337 _("* Can do the action \'%s\' to some %s"
4338 " (adds %d production).\n"),
4339 /* The action may have a ruleset defined ui name. */
4340 action_id_name_translation(act),
4341 /* Keep the style consistent with the help for the
4342 * other actions. */
4343 _(action_target_kind_name(
4344 action_id_get_target_kind(act))),
4345 /* The custom information. */
4346 utype_build_shield_cost(utype));
4347 break;
4348 default:
4349 /* Generic action information. */
4350 cat_snprintf(buf, bufsz,
4351 /* TRANS: the first %s is the action's ruleset
4352 * defined ui name and the next %s is the name of
4353 * its target kind. */
4354 _("* Can do the action \'%s\' to some %s.\n"),
4355 action_id_name_translation(act),
4356 _(action_target_kind_name(
4357 action_id_get_target_kind(act))));
4358 break;
4361 /* The unit's ability to perform this action was just documented.
4362 * Move on to check if the unit can perform the next action too. */
4363 break;
4365 } action_enabler_list_iterate_end;
4366 } action_iterate_end;
4367 action_iterate(act) {
4368 bool vulnerable;
4370 if (action_by_number(act)->quiet) {
4371 /* The ruleset documents this action it self. */
4372 continue;
4375 /* Not relevant */
4376 if (action_id_get_target_kind(act) != ATK_UNIT) {
4377 continue;
4380 /* All units are immune to this since its not enabled */
4381 if (action_enabler_list_size(action_enablers_for_action(act)) == 0) {
4382 continue;
4385 /* Must be immune in all cases */
4386 vulnerable = FALSE;
4387 action_enabler_list_iterate(action_enablers_for_action(act), enabler) {
4388 if (requirement_fulfilled_by_unit_type(utype,
4389 &(enabler->target_reqs))) {
4390 vulnerable = TRUE;
4391 break;
4393 } action_enabler_list_iterate_end;
4395 if (!vulnerable) {
4396 cat_snprintf(buf, bufsz,
4397 _("* Doing the action \'%s\' to this unit"
4398 " is impossible.\n"),
4399 action_id_name_translation(act));
4401 } action_iterate_end;
4402 if (!has_vet_levels) {
4403 /* Only mention this if the game generally does have veteran levels. */
4404 if (game.veteran->levels > 1) {
4405 CATLSTR(buf, bufsz, _("* Will never achieve veteran status.\n"));
4407 } else {
4408 /* Not useful currently: */
4409 #if 0
4410 /* Some units can never become veteran through combat in practice. */
4411 bool veteran_through_combat =
4412 !((utype->attack_strength == 0
4413 || uclass_has_flag(utype_class(utype), UCF_MISSILE))
4414 && utype->defense_strength == 0);
4415 #endif
4416 /* FIXME: if we knew the raise chances on the client, we could be
4417 * more specific here about whether veteran status can be acquired
4418 * through combat/missions/work. Should also take into account
4419 * UTYF_NO_VETERAN when writing this text. (Gna patch #4794) */
4420 CATLSTR(buf, bufsz, _("* May acquire veteran status.\n"));
4421 if (utype_veteran_has_power_bonus(utype)) {
4422 if ((!utype_has_flag(utype, UTYF_NUCLEAR) && utype->attack_strength > 0)
4423 || utype->defense_strength > 0) {
4424 CATLSTR(buf, bufsz,
4425 _(" * Veterans have increased strength in combat.\n"));
4427 /* SUPERSPY always wins/escapes */
4428 if ((utype_has_flag(utype, UTYF_DIPLOMAT)
4429 || utype_has_flag(utype, UTYF_SPY))
4430 && !utype_has_flag(utype, UTYF_SUPERSPY)) {
4431 CATLSTR(buf, bufsz,
4432 _(" * Veterans have improved chances in diplomatic "
4433 "contests.\n"));
4434 if (utype_has_flag(utype, UTYF_SPY)) {
4435 CATLSTR(buf, bufsz,
4436 _(" * Veterans are more likely to survive missions.\n"));
4439 if (utype_has_flag(utype, UTYF_SETTLERS)) {
4440 CATLSTR(buf, bufsz,
4441 _(" * Veterans work faster.\n"));
4445 if (strlen(buf) > 0) {
4446 CATLSTR(buf, bufsz, "\n");
4448 if (has_vet_levels && utype->veteran) {
4449 /* The case where the unit has only a single veteran level has already
4450 * been handled above, so keep quiet here if that happens */
4451 if (insert_veteran_help(buf, bufsz, utype->veteran,
4452 _("This type of unit has its own veteran levels:"), NULL)) {
4453 CATLSTR(buf, bufsz, "\n\n");
4456 if (NULL != utype->helptext) {
4457 strvec_iterate(utype->helptext, text) {
4458 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4459 } strvec_iterate_end;
4461 CATLSTR(buf, bufsz, user_text);
4462 return buf;
4465 /****************************************************************************
4466 Append misc dynamic text for advance/technology.
4468 pplayer may be NULL.
4469 ****************************************************************************/
4470 void helptext_advance(char *buf, size_t bufsz, struct player *pplayer,
4471 const char *user_text, int i)
4473 struct astring astr = ASTRING_INIT;
4474 struct advance *vap = valid_advance_by_number(i);
4475 struct universal source = {
4476 .kind = VUT_ADVANCE,
4477 .value = {.advance = vap}
4479 int flagid;
4481 fc_assert_ret(NULL != buf && 0 < bufsz && NULL != user_text);
4482 fc_strlcpy(buf, user_text, bufsz);
4484 if (NULL == vap) {
4485 log_error("Unknown tech %d.", i);
4486 return;
4489 if (NULL != pplayer) {
4490 const struct research *presearch = research_get(pplayer);
4492 if (research_invention_state(presearch, i) != TECH_KNOWN) {
4493 if (research_invention_state(presearch, i) == TECH_PREREQS_KNOWN) {
4494 int bulbs = research_total_bulbs_required(presearch, i, FALSE);
4496 cat_snprintf(buf, bufsz,
4497 PL_("Starting now, researching %s would need %d bulb.",
4498 "Starting now, researching %s would need %d bulbs.",
4499 bulbs),
4500 advance_name_translation(vap), bulbs);
4501 } else if (research_invention_reachable(presearch, i)) {
4502 /* Split string into two to allow localization of two pluralizations. */
4503 char buf2[MAX_LEN_MSG];
4504 int bulbs = research_goal_bulbs_required(presearch, i);
4506 fc_snprintf(buf2, ARRAY_SIZE(buf2),
4507 /* TRANS: appended to another sentence. Preserve the
4508 * leading space. */
4509 PL_(" The whole project will require %d bulb to complete.",
4510 " The whole project will require %d bulbs to complete.",
4511 bulbs),
4512 bulbs);
4513 cat_snprintf(buf, bufsz,
4514 /* TRANS: last %s is a sentence pluralized separately. */
4515 PL_("To reach %s you need to obtain %d other"
4516 " technology first.%s",
4517 "To reach %s you need to obtain %d other"
4518 " technologies first.%s",
4519 research_goal_unknown_techs(presearch, i) - 1),
4520 advance_name_translation(vap),
4521 research_goal_unknown_techs(presearch, i) - 1, buf2);
4522 } else {
4523 CATLSTR(buf, bufsz,
4524 _("You cannot research this technology."));
4526 if (!techs_have_fixed_costs()
4527 && research_invention_reachable(presearch, i)) {
4528 CATLSTR(buf, bufsz,
4529 _(" This number may vary depending on what "
4530 "other players research.\n"));
4531 } else {
4532 CATLSTR(buf, bufsz, "\n");
4536 CATLSTR(buf, bufsz, "\n");
4539 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
4542 int j;
4544 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
4545 if (game.rgame.global_init_techs[j] == A_LAST) {
4546 break;
4547 } else if (game.rgame.global_init_techs[j] == i) {
4548 CATLSTR(buf, bufsz,
4549 _("* All players start the game with knowledge of this "
4550 "technology.\n"));
4551 break;
4556 /* Assume no-one will set the same tech in both global and nation
4557 * init_tech... */
4558 nations_iterate(pnation) {
4559 int j;
4561 /* Avoid mentioning nations not in current set. */
4562 if (!show_help_for_nation(pnation)) {
4563 continue;
4565 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
4566 if (pnation->init_techs[j] == A_LAST) {
4567 break;
4568 } else if (pnation->init_techs[j] == i) {
4569 cat_snprintf(buf, bufsz,
4570 /* TRANS: %s is a nation plural */
4571 _("* The %s start the game with knowledge of this "
4572 "technology.\n"), nation_plural_translation(pnation));
4573 break;
4576 } nations_iterate_end;
4578 if (advance_has_flag(i, TF_BONUS_TECH)) {
4579 cat_snprintf(buf, bufsz,
4580 _("* The first player to research %s gets"
4581 " an immediate advance.\n"),
4582 advance_name_translation(vap));
4585 for (flagid = TECH_USER_1 ; flagid <= TECH_USER_LAST; flagid++) {
4586 if (advance_has_flag(i, flagid)) {
4587 const char *helptxt = tech_flag_helptxt(flagid);
4589 if (helptxt != NULL) {
4590 CATLSTR(buf, bufsz, Q_("?bullet:* "));
4591 CATLSTR(buf, bufsz, _(helptxt));
4592 CATLSTR(buf, bufsz, "\n");
4597 /* FIXME: bases -- but there is no good way to find out which bases a tech
4598 * can enable currently, so we have to remain silent. */
4600 if (game.info.tech_upkeep_style != TECH_UPKEEP_NONE) {
4601 CATLSTR(buf, bufsz,
4602 _("* To preserve this technology for our nation some bulbs "
4603 "are needed each turn.\n"));
4606 if (NULL != vap->helptext) {
4607 if (strlen(buf) > 0) {
4608 CATLSTR(buf, bufsz, "\n");
4610 strvec_iterate(vap->helptext, text) {
4611 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4612 } strvec_iterate_end;
4615 astr_free(&astr);
4618 /****************************************************************
4619 Append text for terrain.
4620 *****************************************************************/
4621 void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer,
4622 const char *user_text, struct terrain *pterrain)
4624 struct universal source = {
4625 .kind = VUT_TERRAIN,
4626 .value = {.terrain = pterrain}
4628 int flagid;
4630 fc_assert_ret(NULL != buf && 0 < bufsz);
4631 buf[0] = '\0';
4633 if (!pterrain) {
4634 log_error("Unknown terrain!");
4635 return;
4638 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
4639 if (terrain_has_flag(pterrain, TER_NO_CITIES)) {
4640 CATLSTR(buf, bufsz,
4641 _("* You cannot build cities on this terrain."));
4642 CATLSTR(buf, bufsz, "\n");
4644 if (pterrain->road_time == 0) {
4645 /* Can't build roads; only mention if ruleset has buildable roads */
4646 extra_type_by_cause_iterate(EC_ROAD, pextra) {
4647 if (pextra->buildable) {
4648 CATLSTR(buf, bufsz,
4649 _("* Paths cannot be built on this terrain."));
4650 CATLSTR(buf, bufsz, "\n");
4651 break;
4653 } extra_type_by_cause_iterate_end;
4655 if (pterrain->base_time == 0) {
4656 /* Can't build bases; only mention if ruleset has buildable bases */
4657 extra_type_by_cause_iterate(EC_BASE, pextra) {
4658 if (pextra->buildable) {
4659 CATLSTR(buf, bufsz,
4660 _("* Bases cannot be built on this terrain."));
4661 CATLSTR(buf, bufsz, "\n");
4662 break;
4664 } extra_type_by_cause_iterate_end;
4666 if (terrain_has_flag(pterrain, TER_UNSAFE_COAST)
4667 && terrain_type_terrain_class(pterrain) != TC_OCEAN) {
4668 CATLSTR(buf, bufsz,
4669 _("* The coastline of this terrain is unsafe."));
4670 CATLSTR(buf, bufsz, "\n");
4673 const char *classes[uclass_count()];
4674 int i = 0;
4676 unit_class_iterate(uclass) {
4677 if (is_native_to_class(uclass, pterrain, NULL)) {
4678 classes[i++] = uclass_name_translation(uclass);
4680 } unit_class_iterate_end;
4682 if (0 < i) {
4683 struct astring list = ASTRING_INIT;
4685 /* TRANS: %s is a list of unit classes separated by "and". */
4686 cat_snprintf(buf, bufsz, _("* Can be traveled by %s units.\n"),
4687 astr_build_and_list(&list, classes, i));
4688 astr_free(&list);
4691 if (terrain_has_flag(pterrain, TER_NO_ZOC)) {
4692 CATLSTR(buf, bufsz,
4693 _("* Units on this terrain neither impose zones of control "
4694 "nor are restricted by them.\n"));
4695 } else {
4696 CATLSTR(buf, bufsz,
4697 _("* Units on this terrain may impose a zone of control, or "
4698 "be restricted by one.\n"));
4700 if (terrain_has_flag(pterrain, TER_NO_FORTIFY)) {
4701 CATLSTR(buf, bufsz,
4702 _("* No units can fortify on this terrain.\n"));
4703 } else {
4704 CATLSTR(buf, bufsz,
4705 _("* Units able to fortify may do so on this terrain.\n"));
4707 for (flagid = TER_USER_1 ; flagid <= TER_USER_LAST; flagid++) {
4708 if (terrain_has_flag(pterrain, flagid)) {
4709 const char *helptxt = terrain_flag_helptxt(flagid);
4711 if (helptxt != NULL) {
4712 CATLSTR(buf, bufsz, Q_("?bullet:* "));
4713 CATLSTR(buf, bufsz, _(helptxt));
4714 CATLSTR(buf, bufsz, "\n");
4719 if (NULL != pterrain->helptext) {
4720 if (buf[0] != '\0') {
4721 CATLSTR(buf, bufsz, "\n");
4723 strvec_iterate(pterrain->helptext, text) {
4724 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4725 } strvec_iterate_end;
4727 if (user_text && user_text[0] != '\0') {
4728 CATLSTR(buf, bufsz, "\n\n");
4729 CATLSTR(buf, bufsz, user_text);
4733 /****************************************************************************
4734 Return a textual representation of the F/P/T bonus a road provides to a
4735 terrain if supplied, or the terrain-independent bonus if pterrain==NULL.
4736 e.g. "0/0/+1", "0/+50%/0", or for a complex road "+2/+1+50%/0".
4737 Returns a pointer to a static string, so caller should not free
4738 (or NULL if there is no effect at all).
4739 ****************************************************************************/
4740 const char *helptext_road_bonus_str(const struct terrain *pterrain,
4741 const struct road_type *proad)
4743 static char str[64];
4744 str[0] = '\0';
4745 bool has_effect = FALSE;
4746 output_type_iterate(o) {
4747 switch (o) {
4748 case O_FOOD:
4749 case O_SHIELD:
4750 case O_TRADE:
4752 int bonus = proad->tile_bonus[o];
4753 int incr = proad->tile_incr_const[o];
4754 if (pterrain) {
4755 incr +=
4756 proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
4758 if (str[0] != '\0') {
4759 CATLSTR(str, sizeof(str), "/");
4761 if (incr == 0 && bonus == 0) {
4762 cat_snprintf(str, sizeof(str), "%d", incr);
4763 } else {
4764 has_effect = TRUE;
4765 if (incr != 0) {
4766 cat_snprintf(str, sizeof(str), "%+d", incr);
4768 if (bonus != 0) {
4769 cat_snprintf(str, sizeof(str), "%+d%%", bonus);
4773 break;
4774 default:
4775 /* FIXME: there's nothing actually stopping roads having gold, etc
4776 * bonuses */
4777 fc_assert(proad->tile_incr_const[o] == 0
4778 && proad->tile_incr[o] == 0
4779 && proad->tile_bonus[o] == 0);
4780 break;
4782 } output_type_iterate_end;
4784 return has_effect ? str : NULL;
4787 /****************************************************************************
4788 Append misc dynamic text for extras.
4789 Assumes build time and conflicts are handled in the GUI front-end.
4791 pplayer may be NULL.
4792 ****************************************************************************/
4793 void helptext_extra(char *buf, size_t bufsz, struct player *pplayer,
4794 const char *user_text, struct extra_type *pextra)
4796 struct base_type *pbase;
4797 struct road_type *proad;
4798 struct universal source = {
4799 .kind = VUT_EXTRA,
4800 .value = {.extra = pextra}
4803 fc_assert_ret(NULL != buf && 0 < bufsz);
4804 buf[0] = '\0';
4806 if (!pextra) {
4807 log_error("Unknown extra!");
4808 return;
4811 if (is_extra_caused_by(pextra, EC_BASE)) {
4812 pbase = pextra->data.base;
4813 } else {
4814 pbase = NULL;
4817 if (is_extra_caused_by(pextra, EC_ROAD)) {
4818 proad = pextra->data.road;
4819 } else {
4820 proad = NULL;
4823 if (pextra->helptext != NULL) {
4824 strvec_iterate(pextra->helptext, text) {
4825 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4826 } strvec_iterate_end;
4829 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
4831 if (is_extra_caused_by(pextra, EC_POLLUTION)) {
4832 CATLSTR(buf, bufsz,
4833 _("* May randomly appear around polluting city.\n"));
4836 if (is_extra_caused_by(pextra, EC_FALLOUT)) {
4837 CATLSTR(buf, bufsz,
4838 _("* May randomly appear around nuclear blast.\n"));
4841 if (is_extra_caused_by(pextra, EC_HUT)
4842 || (proad != NULL && road_has_flag(proad, RF_RIVER))) {
4843 CATLSTR(buf, bufsz,
4844 _("* Placed by map generator.\n"));
4847 /* XXX Non-zero requirement vector is not a good test of whether
4848 * insert_requirement() will give any output. */
4849 if (requirement_vector_size(&pextra->reqs) > 0) {
4850 if (pextra->buildable && is_extra_caused_by_worker_action(pextra)) {
4851 CATLSTR(buf, bufsz, _("Requirements to build:\n"));
4853 requirement_vector_iterate(&pextra->reqs, preq) {
4854 (void) insert_requirement(buf, bufsz, pplayer, preq);
4855 } requirement_vector_iterate_end;
4856 CATLSTR(buf, bufsz, "\n");
4860 const char *classes[uclass_count()];
4861 int i = 0;
4863 unit_class_iterate(uclass) {
4864 if (is_native_extra_to_uclass(pextra, uclass)) {
4865 classes[i++] = uclass_name_translation(uclass);
4867 } unit_class_iterate_end;
4869 if (0 < i) {
4870 struct astring list = ASTRING_INIT;
4872 if (proad != NULL) {
4873 /* TRANS: %s is a list of unit classes separated by "and". */
4874 cat_snprintf(buf, bufsz, _("* Can be traveled by %s units.\n"),
4875 astr_build_and_list(&list, classes, i));
4876 } else {
4877 /* TRANS: %s is a list of unit classes separated by "and". */
4878 cat_snprintf(buf, bufsz, _("* Native to %s units.\n"),
4879 astr_build_and_list(&list, classes, i));
4881 astr_free(&list);
4883 if (extra_has_flag(pextra, EF_NATIVE_TILE)) {
4884 CATLSTR(buf, bufsz,
4885 _(" * Such units can move onto this tile even if it would "
4886 "not normally be suitable terrain.\n"));
4888 if (pbase != NULL) {
4889 if (base_has_flag(pbase, BF_NOT_AGGRESSIVE)) {
4890 /* "3 tiles" is hardcoded in is_friendly_city_near() */
4891 CATLSTR(buf, bufsz,
4892 _(" * Such units situated here are not considered aggressive "
4893 "if this tile is within 3 tiles of a friendly city.\n"));
4895 if (territory_claiming_base(pbase)) {
4896 CATLSTR(buf, bufsz,
4897 _(" * Can be captured by such units if at war with the "
4898 "nation that currently owns it.\n"));
4900 if (base_has_flag(pbase, BF_DIPLOMAT_DEFENSE)) {
4901 CATLSTR(buf, bufsz,
4902 /* xgettext:no-c-format */
4903 _(" * Diplomatic units get a 25% defense bonus in "
4904 "diplomatic fights.\n"));
4907 if (pextra->defense_bonus) {
4908 cat_snprintf(buf, bufsz,
4909 _(" * Such units get a %d%% defense bonus on this "
4910 "tile.\n"),
4911 pextra->defense_bonus);
4916 if (proad != NULL && road_provides_move_bonus(proad)) {
4917 if (proad->move_cost == 0) {
4918 CATLSTR(buf, bufsz, _("* Allows infinite movement.\n"));
4919 } else {
4920 cat_snprintf(buf, bufsz,
4921 /* TRANS: "MP" = movement points. Second %s may have a
4922 * fractional part. */
4923 _("* Movement cost along %s is %s MP.\n"),
4924 extra_name_translation(pextra),
4925 move_points_text(proad->move_cost, TRUE));
4929 if (is_extra_removed_by(pextra, ERM_PILLAGE)) {
4930 CATLSTR(buf, bufsz,
4931 _("* Can be pillaged by units.\n"));
4933 if (is_extra_removed_by(pextra, ERM_CLEANPOLLUTION) || is_extra_removed_by(pextra, ERM_CLEANFALLOUT)) {
4934 CATLSTR(buf, bufsz,
4935 _("* Can be cleaned by units.\n"));
4937 if (pbase != NULL) {
4938 if (game.info.killstack
4939 && base_has_flag(pbase, BF_NO_STACK_DEATH)) {
4940 CATLSTR(buf, bufsz,
4941 _("* Defeat of one unit does not cause death of all other units "
4942 "on this tile.\n"));
4944 if (base_has_flag(pbase, BF_PARADROP_FROM)) {
4945 CATLSTR(buf, bufsz,
4946 _("* Units can paradrop from this tile.\n"));
4948 if (territory_claiming_base(pbase)) {
4949 CATLSTR(buf, bufsz,
4950 _("* Extends national borders of the building nation.\n"));
4952 if (pbase->vision_main_sq >= 0) {
4953 CATLSTR(buf, bufsz,
4954 _("* Grants permanent vision of an area around the tile to "
4955 "its owner.\n"));
4957 if (pbase->vision_invis_sq >= 0) {
4958 CATLSTR(buf, bufsz,
4959 _("* Allows the owner to see normally invisible units in an "
4960 "area around the tile.\n"));
4964 /* Table of terrain-specific attributes, if needed */
4965 if (proad != NULL || pbase != NULL) {
4966 bool road, do_time, do_bonus;
4968 road = (proad != NULL);
4969 /* Terrain-dependent build time? */
4970 do_time = pextra->buildable && pextra->build_time == 0;
4971 if (road) {
4972 /* Terrain-dependent output bonus? */
4973 do_bonus = FALSE;
4974 output_type_iterate(o) {
4975 if (proad->tile_incr[o] > 0) {
4976 do_bonus = TRUE;
4977 fc_assert(o == O_FOOD || o == O_SHIELD || o == O_TRADE);
4979 } output_type_iterate_end;
4980 } else {
4981 /* Bases don't have output bonuses */
4982 do_bonus = FALSE;
4985 if (do_time || do_bonus) {
4986 if (do_time && do_bonus) {
4987 CATLSTR(buf, bufsz,
4988 _("\nTime to build and output bonus depends on terrain:\n\n"));
4989 CATLSTR(buf, bufsz,
4990 /* TRANS: Header for fixed-width road properties table.
4991 * TRANS: Translators cannot change column widths :( */
4992 _("Terrain Time Bonus F/P/T\n"
4993 "----------------------------------\n"));
4994 } else if (do_time) {
4995 CATLSTR(buf, bufsz,
4996 _("\nTime to build depends on terrain:\n\n"));
4997 CATLSTR(buf, bufsz,
4998 /* TRANS: Header for fixed-width extra properties table.
4999 * TRANS: Translators cannot change column widths :( */
5000 _("Terrain Time\n"
5001 "------------------\n"));
5002 } else {
5003 fc_assert(do_bonus);
5004 CATLSTR(buf, bufsz,
5005 /* TRANS: Header for fixed-width road properties table.
5006 * TRANS: Translators cannot change column widths :( */
5007 _("\nYields an output bonus with some terrains:\n\n"));
5008 CATLSTR(buf, bufsz,
5009 _("Terrain Bonus F/P/T\n"
5010 "-------------------------\n"));;
5012 terrain_type_iterate(t) {
5013 int turns = road ? terrain_extra_build_time(t, ACTIVITY_GEN_ROAD, pextra)
5014 : terrain_extra_build_time(t, ACTIVITY_BASE, pextra);
5015 const char *bonus_text
5016 = road ? helptext_road_bonus_str(t, proad) : NULL;
5017 if (turns > 0 || bonus_text) {
5018 const char *terrain = terrain_name_translation(t);
5020 cat_snprintf(buf, bufsz,
5021 "%s%*s ", terrain,
5022 MAX(0, 12 - (int)get_internal_string_length(terrain)),
5023 "");
5024 if (do_time) {
5025 if (turns > 0) {
5026 cat_snprintf(buf, bufsz, "%3d ", turns);
5027 } else {
5028 CATLSTR(buf, bufsz, " - ");
5031 if (do_bonus) {
5032 fc_assert(proad != NULL);
5033 cat_snprintf(buf, bufsz, " %s", bonus_text ? bonus_text : "-");
5035 CATLSTR(buf, bufsz, "\n");
5037 } terrain_type_iterate_end;
5038 } /* else rely on client-specific display */
5041 if (user_text && user_text[0] != '\0') {
5042 CATLSTR(buf, bufsz, "\n\n");
5043 CATLSTR(buf, bufsz, user_text);
5047 /****************************************************************************
5048 Append misc dynamic text for specialists.
5049 Assumes effects are described in the help text.
5051 pplayer may be NULL.
5052 ****************************************************************************/
5053 void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer,
5054 const char *user_text, struct specialist *pspec)
5056 bool reqs = FALSE;
5058 fc_assert_ret(NULL != buf && 0 < bufsz);
5059 buf[0] = '\0';
5061 if (NULL != pspec->helptext) {
5062 strvec_iterate(pspec->helptext, text) {
5063 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
5064 } strvec_iterate_end;
5067 /* Requirements for this specialist. */
5068 requirement_vector_iterate(&pspec->reqs, preq) {
5069 if (insert_requirement(buf, bufsz, pplayer, preq)) {
5070 reqs = TRUE;
5072 } requirement_vector_iterate_end;
5073 if (reqs) {
5074 fc_strlcat(buf, "\n", bufsz);
5077 CATLSTR(buf, bufsz, user_text);
5080 /****************************************************************
5081 Append text for government.
5083 pplayer may be NULL.
5085 TODO: Generalize the effects code for use elsewhere. Add
5086 other requirements.
5087 *****************************************************************/
5088 void helptext_government(char *buf, size_t bufsz, struct player *pplayer,
5089 const char *user_text, struct government *gov)
5091 bool reqs = FALSE;
5092 struct universal source = {
5093 .kind = VUT_GOVERNMENT,
5094 .value = {.govern = gov}
5097 fc_assert_ret(NULL != buf && 0 < bufsz);
5098 buf[0] = '\0';
5100 if (NULL != gov->helptext) {
5101 strvec_iterate(gov->helptext, text) {
5102 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
5103 } strvec_iterate_end;
5106 /* Add requirement text for government itself */
5107 requirement_vector_iterate(&gov->reqs, preq) {
5108 if (insert_requirement(buf, bufsz, pplayer, preq)) {
5109 reqs = TRUE;
5111 } requirement_vector_iterate_end;
5112 if (reqs) {
5113 fc_strlcat(buf, "\n", bufsz);
5116 /* Effects */
5117 CATLSTR(buf, bufsz, _("Features:\n"));
5118 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
5119 effect_list_iterate(get_req_source_effects(&source), peffect) {
5120 Output_type_id output_type = O_LAST;
5121 struct unit_class *unitclass = NULL;
5122 struct unit_type *unittype = NULL;
5123 enum unit_type_flag_id unitflag = unit_type_flag_id_invalid();
5124 struct strvec *outputs = strvec_new();
5125 struct astring outputs_or = ASTRING_INIT;
5126 struct astring outputs_and = ASTRING_INIT;
5127 bool too_complex = FALSE;
5128 bool world_value_valid = TRUE;
5130 /* Grab output type, if there is one */
5131 requirement_vector_iterate(&peffect->reqs, preq) {
5132 /* Treat an effect with any negated requirements as too complex for
5133 * us to explain here.
5134 * Also don't try to explain an effect with any requirements explicitly
5135 * marked as 'quiet' by ruleset author. */
5136 if (!preq->present || preq->quiet) {
5137 too_complex = TRUE;
5138 continue;
5140 switch (preq->source.kind) {
5141 case VUT_OTYPE:
5142 /* We should never have multiple outputtype requirements
5143 * in one list in the first place (it simply makes no sense,
5144 * output cannot be of multiple types)
5145 * Ruleset loading code should check against that. */
5146 fc_assert(output_type == O_LAST);
5147 output_type = preq->source.value.outputtype;
5148 strvec_append(outputs, get_output_name(output_type));
5149 break;
5150 case VUT_UCLASS:
5151 fc_assert(unitclass == NULL);
5152 unitclass = preq->source.value.uclass;
5153 /* FIXME: can't easily get world bonus for unit class */
5154 world_value_valid = FALSE;
5155 break;
5156 case VUT_UTYPE:
5157 fc_assert(unittype == NULL);
5158 unittype = preq->source.value.utype;
5159 break;
5160 case VUT_UTFLAG:
5161 if (!unit_type_flag_id_is_valid(unitflag)) {
5162 unitflag = preq->source.value.unitflag;
5163 /* FIXME: can't easily get world bonus for unit type flag */
5164 world_value_valid = FALSE;
5165 } else {
5166 /* Already have a unit flag requirement. More than one is too
5167 * complex for us to explain, so say nothing. */
5168 /* FIXME: we could handle this */
5169 too_complex = TRUE;
5171 break;
5172 case VUT_GOVERNMENT:
5173 /* This is government we are generating helptext for.
5174 * ...or if not, it's ruleset bug that should never make it
5175 * this far. Fix ruleset loading code. */
5176 fc_assert(preq->source.value.govern == gov);
5177 break;
5178 default:
5179 too_complex = TRUE;
5180 world_value_valid = FALSE;
5181 break;
5183 } requirement_vector_iterate_end;
5185 if (!too_complex) {
5186 /* Only list effects that don't have extra requirements too complex
5187 * for us to handle.
5188 * Anything more complicated will have to be documented by hand by the
5189 * ruleset author. */
5191 /* Guard condition for simple player-wide effects descriptions.
5192 * (FIXME: in many cases, e.g. EFT_MAKE_CONTENT, additional requirements
5193 * like unittype will be ignored for gameplay, but will affect our
5194 * help here.) */
5195 const bool playerwide
5196 = world_value_valid && !unittype && (output_type == O_LAST);
5197 /* In some cases we give absolute values (world bonus + gov bonus).
5198 * We assume the fact that there's an effect with a gov requirement
5199 * is sufficient reason to list it in that gov's help.
5200 * Guard accesses to these with 'playerwide' or 'world_value_valid'. */
5201 int world_value = -999, net_value = -999;
5202 if (world_value_valid) {
5203 /* Get government-independent world value of effect if the extra
5204 * requirements were simple enough. */
5205 struct output_type *potype =
5206 output_type != O_LAST ? get_output_type(output_type) : NULL;
5207 world_value =
5208 get_target_bonus_effects(NULL, NULL, NULL, NULL, NULL, NULL,
5209 NULL, unittype, potype, NULL,
5210 peffect->type);
5211 net_value = peffect->value + world_value;
5214 if (output_type == O_LAST) {
5215 /* There was no outputtype requirement. Effect is active for all
5216 * output types. Generate lists for that. */
5217 bool harvested_only = TRUE; /* Consider only output types from fields */
5219 if (peffect->type == EFT_UPKEEP_FACTOR
5220 || peffect->type == EFT_UNIT_UPKEEP_FREE_PER_CITY
5221 || peffect->type == EFT_OUTPUT_BONUS
5222 || peffect->type == EFT_OUTPUT_BONUS_2) {
5223 /* Effect can use or require any kind of output */
5224 harvested_only = FALSE;
5227 output_type_iterate(ot) {
5228 struct output_type *pot = get_output_type(ot);
5230 if (!harvested_only || pot->harvested) {
5231 strvec_append(outputs, _(pot->name));
5233 } output_type_iterate_end;
5236 if (0 == strvec_size(outputs)) {
5237 /* TRANS: Empty output type list, should never happen. */
5238 astr_set(&outputs_or, "%s", Q_("?outputlist: Nothing "));
5239 astr_set(&outputs_and, "%s", Q_("?outputlist: Nothing "));
5240 } else {
5241 strvec_to_or_list(outputs, &outputs_or);
5242 strvec_to_and_list(outputs, &outputs_and);
5245 switch (peffect->type) {
5246 case EFT_UNHAPPY_FACTOR:
5247 if (playerwide) {
5248 /* FIXME: EFT_MAKE_CONTENT_MIL_PER would cancel this out. We assume
5249 * no-one will set both, so we don't bother handling it. */
5250 cat_snprintf(buf, bufsz,
5251 PL_("* Military units away from home and field units"
5252 " will each cause %d citizen to become unhappy.\n",
5253 "* Military units away from home and field units"
5254 " will each cause %d citizens to become unhappy.\n",
5255 net_value),
5256 net_value);
5257 } /* else too complicated or silly ruleset */
5258 break;
5259 case EFT_ENEMY_CITIZEN_UNHAPPY_PCT:
5260 if (playerwide && net_value != world_value) {
5261 if (world_value > 0) {
5262 if (net_value > 0) {
5263 cat_snprintf(buf, bufsz,
5264 _("* Unhappiness from foreign citizens due to "
5265 "war with their home state is %d%% the usual "
5266 "value.\n"),
5267 (net_value * 100) / world_value);
5268 } else {
5269 CATLSTR(buf, bufsz,
5270 _("* No unhappiness from foreign citizens even when "
5271 "at war with their home state.\n"));
5273 } else {
5274 cat_snprintf(buf, bufsz,
5275 /* TRANS: not pluralised as gettext doesn't support
5276 * fractional numbers, which this might be */
5277 _("* Each foreign citizen causes %.2g unhappiness "
5278 "in their city while you are at war with their "
5279 "home state.\n"),
5280 (double)net_value / 100);
5283 break;
5284 case EFT_MAKE_CONTENT_MIL:
5285 if (playerwide) {
5286 cat_snprintf(buf, bufsz,
5287 PL_("* Each of your cities will avoid %d unhappiness"
5288 " caused by units.\n",
5289 "* Each of your cities will avoid %d unhappiness"
5290 " caused by units.\n",
5291 peffect->value),
5292 peffect->value);
5294 break;
5295 case EFT_MAKE_CONTENT:
5296 if (playerwide) {
5297 cat_snprintf(buf, bufsz,
5298 PL_("* Each of your cities will avoid %d unhappiness,"
5299 " not including that caused by aggression.\n",
5300 "* Each of your cities will avoid %d unhappiness,"
5301 " not including that caused by aggression.\n",
5302 peffect->value),
5303 peffect->value);
5305 break;
5306 case EFT_FORCE_CONTENT:
5307 if (playerwide) {
5308 cat_snprintf(buf, bufsz,
5309 PL_("* Each of your cities will avoid %d unhappiness,"
5310 " including that caused by aggression.\n",
5311 "* Each of your cities will avoid %d unhappiness,"
5312 " including that caused by aggression.\n",
5313 peffect->value),
5314 peffect->value);
5316 break;
5317 case EFT_UPKEEP_FACTOR:
5318 if (world_value_valid && !unittype) {
5319 if (net_value == 0) {
5320 if (output_type != O_LAST) {
5321 cat_snprintf(buf, bufsz,
5322 /* TRANS: %s is the output type, like 'shield'
5323 * or 'gold'. */
5324 _("* You pay no %s upkeep for your units.\n"),
5325 astr_str(&outputs_or));
5326 } else {
5327 CATLSTR(buf, bufsz,
5328 _("* You pay no upkeep for your units.\n"));
5330 } else if (net_value != world_value) {
5331 double ratio = (double)net_value / world_value;
5332 if (output_type != O_LAST) {
5333 cat_snprintf(buf, bufsz,
5334 /* TRANS: %s is the output type, like 'shield'
5335 * or 'gold'. */
5336 _("* You pay %.2g times normal %s upkeep for your "
5337 "units.\n"),
5338 ratio, astr_str(&outputs_and));
5339 } else {
5340 cat_snprintf(buf, bufsz,
5341 _("* You pay %.2g times normal upkeep for your "
5342 "units.\n"),
5343 ratio);
5345 } /* else this effect somehow has no effect; keep quiet */
5346 } /* else there was some extra condition making it complicated */
5347 break;
5348 case EFT_UNIT_UPKEEP_FREE_PER_CITY:
5349 if (!unittype) {
5350 if (output_type != O_LAST) {
5351 cat_snprintf(buf, bufsz,
5352 /* TRANS: %s is the output type, like 'shield' or
5353 * 'gold'; pluralised in %d but there is currently
5354 * no way to control the singular/plural name of the
5355 * output type; sorry */
5356 PL_("* Each of your cities will avoid paying %d %s"
5357 " upkeep for your units.\n",
5358 "* Each of your cities will avoid paying %d %s"
5359 " upkeep for your units.\n", peffect->value),
5360 peffect->value, astr_str(&outputs_and));
5361 } else {
5362 cat_snprintf(buf, bufsz,
5363 /* TRANS: Amount is subtracted from upkeep cost
5364 * for each upkeep type. */
5365 PL_("* Each of your cities will avoid paying %d"
5366 " upkeep for your units.\n",
5367 "* Each of your cities will avoid paying %d"
5368 " upkeep for your units.\n", peffect->value),
5369 peffect->value);
5371 } /* else too complicated */
5372 break;
5373 case EFT_CIVIL_WAR_CHANCE:
5374 if (playerwide) {
5375 cat_snprintf(buf, bufsz,
5376 _("* If you lose your capital,"
5377 " the chance of civil war is %d%%.\n"),
5378 net_value);
5380 break;
5381 case EFT_EMPIRE_SIZE_BASE:
5382 if (playerwide) {
5383 cat_snprintf(buf, bufsz,
5384 PL_("* You can have %d city before an "
5385 "additional unhappy citizen appears in each city "
5386 "due to civilization size.\n",
5387 "* You can have up to %d cities before an "
5388 "additional unhappy citizen appears in each city "
5389 "due to civilization size.\n", net_value),
5390 net_value);
5392 break;
5393 case EFT_EMPIRE_SIZE_STEP:
5394 if (playerwide) {
5395 cat_snprintf(buf, bufsz,
5396 PL_("* After the first unhappy citizen due to"
5397 " civilization size, for each %d additional city"
5398 " another unhappy citizen will appear.\n",
5399 "* After the first unhappy citizen due to"
5400 " civilization size, for each %d additional cities"
5401 " another unhappy citizen will appear.\n",
5402 net_value),
5403 net_value);
5405 break;
5406 case EFT_MAX_RATES:
5407 if (playerwide && game.info.changable_tax) {
5408 if (net_value < 100) {
5409 cat_snprintf(buf, bufsz,
5410 _("* The maximum rate you can set for science,"
5411 " gold, or luxuries is %d%%.\n"),
5412 net_value);
5413 } else {
5414 CATLSTR(buf, bufsz,
5415 _("* Has unlimited science/gold/luxuries rates.\n"));
5418 break;
5419 case EFT_MARTIAL_LAW_EACH:
5420 if (playerwide) {
5421 cat_snprintf(buf, bufsz,
5422 PL_("* Your units may impose martial law."
5423 " Each military unit inside a city will force %d"
5424 " unhappy citizen to become content.\n",
5425 "* Your units may impose martial law."
5426 " Each military unit inside a city will force %d"
5427 " unhappy citizens to become content.\n",
5428 peffect->value),
5429 peffect->value);
5431 break;
5432 case EFT_MARTIAL_LAW_MAX:
5433 if (playerwide && net_value < 100) {
5434 cat_snprintf(buf, bufsz,
5435 PL_("* A maximum of %d unit in each city can enforce"
5436 " martial law.\n",
5437 "* A maximum of %d units in each city can enforce"
5438 " martial law.\n",
5439 net_value),
5440 net_value);
5442 break;
5443 case EFT_RAPTURE_GROW:
5444 if (playerwide && net_value > 0) {
5445 cat_snprintf(buf, bufsz,
5446 _("* You may grow your cities by means of "
5447 "celebrations."));
5448 if (game.info.celebratesize > 1) {
5449 cat_snprintf(buf, bufsz,
5450 /* TRANS: Preserve leading space. %d should always be
5451 * 2 or greater. */
5452 _(" (Cities below size %d cannot grow in this way.)"),
5453 game.info.celebratesize);
5455 cat_snprintf(buf, bufsz, "\n");
5457 break;
5458 case EFT_REVOLUTION_UNHAPPINESS:
5459 if (playerwide) {
5460 cat_snprintf(buf, bufsz,
5461 PL_("* If a city is in disorder for more than %d turn "
5462 "in a row, government will fall into anarchy.\n",
5463 "* If a city is in disorder for more than %d turns "
5464 "in a row, government will fall into anarchy.\n",
5465 net_value),
5466 net_value);
5468 break;
5469 case EFT_HAS_SENATE:
5470 if (playerwide && net_value > 0) {
5471 CATLSTR(buf, bufsz,
5472 _("* Has a senate that may prevent declaration of war.\n"));
5474 break;
5475 case EFT_INSPIRE_PARTISANS:
5476 if (playerwide && net_value > 0) {
5477 CATLSTR(buf, bufsz,
5478 _("* Allows partisans when cities are taken by the "
5479 "enemy.\n"));
5481 break;
5482 case EFT_HAPPINESS_TO_GOLD:
5483 if (playerwide && net_value > 0) {
5484 CATLSTR(buf, bufsz,
5485 _("* Buildings that normally confer bonuses against"
5486 " unhappiness will instead give gold.\n"));
5488 break;
5489 case EFT_FANATICS:
5490 if (playerwide && net_value > 0) {
5491 struct strvec *fanatics = strvec_new();
5492 struct astring fanaticstr = ASTRING_INIT;
5494 unit_type_iterate(putype) {
5495 if (utype_has_flag(putype, UTYF_FANATIC)) {
5496 strvec_append(fanatics, utype_name_translation(putype));
5498 } unit_type_iterate_end;
5499 cat_snprintf(buf, bufsz,
5500 /* TRANS: %s is list of unit types separated by 'or' */
5501 _("* Pays no upkeep for %s.\n"),
5502 strvec_to_or_list(fanatics, &fanaticstr));
5503 strvec_destroy(fanatics);
5504 astr_free(&fanaticstr);
5506 break;
5507 case EFT_NO_UNHAPPY:
5508 if (playerwide && net_value > 0) {
5509 CATLSTR(buf, bufsz, _("* Has no unhappy citizens.\n"));
5511 break;
5512 case EFT_VETERAN_BUILD:
5514 int conditions = 0;
5515 if (unitclass) {
5516 conditions++;
5518 if (unittype) {
5519 conditions++;
5521 if (unit_type_flag_id_is_valid(unitflag)) {
5522 conditions++;
5524 if (conditions > 1) {
5525 /* More than one requirement on units, too complicated for us
5526 * to describe. */
5527 break;
5529 if (unitclass) {
5530 /* FIXME: account for multiple veteran levels, or negative
5531 * values. This might lie for complicated rulesets! */
5532 cat_snprintf(buf, bufsz,
5533 /* TRANS: %s is a unit class */
5534 Q_("?unitclass:* New %s units will be veteran.\n"),
5535 uclass_name_translation(unitclass));
5536 } else if (unit_type_flag_id_is_valid(unitflag)) {
5537 /* FIXME: same problems as unitclass */
5538 cat_snprintf(buf, bufsz,
5539 /* TRANS: %s is a (translatable) unit type flag */
5540 Q_("?unitflag:* New %s units will be veteran.\n"),
5541 unit_type_flag_id_translated_name(unitflag));
5542 } else if (unittype != NULL) {
5543 if (world_value_valid && net_value > 0) {
5544 /* Here we can be specific about veteran level, and get
5545 * net value correct. */
5546 int maxlvl = utype_veteran_system(unittype)->levels - 1;
5547 const struct veteran_level *vlevel =
5548 utype_veteran_level(unittype, MIN(net_value, maxlvl));
5549 cat_snprintf(buf, bufsz,
5550 /* TRANS: "* New Partisan units will have the rank
5551 * of elite." */
5552 Q_("?unittype:* New %s units will have the rank "
5553 "of %s.\n"),
5554 utype_name_translation(unittype),
5555 name_translation_get(&vlevel->name));
5556 } /* else complicated */
5557 } else {
5558 /* No extra criteria. */
5559 /* FIXME: same problems as above */
5560 cat_snprintf(buf, bufsz,
5561 _("* New units will be veteran.\n"));
5564 break;
5565 case EFT_OUTPUT_PENALTY_TILE:
5566 if (world_value_valid) {
5567 cat_snprintf(buf, bufsz,
5568 /* TRANS: %s is list of output types, with 'or';
5569 * pluralised in %d but of course the output types
5570 * can't be pluralised; sorry */
5571 PL_("* Each worked tile that gives more than %d %s will"
5572 " suffer a -1 penalty, unless the city working it"
5573 " is celebrating.",
5574 "* Each worked tile that gives more than %d %s will"
5575 " suffer a -1 penalty, unless the city working it"
5576 " is celebrating.", net_value),
5577 net_value, astr_str(&outputs_or));
5578 if (game.info.celebratesize > 1) {
5579 cat_snprintf(buf, bufsz,
5580 /* TRANS: Preserve leading space. %d should always be
5581 * 2 or greater. */
5582 _(" (Cities below size %d will not celebrate.)"),
5583 game.info.celebratesize);
5585 cat_snprintf(buf, bufsz, "\n");
5587 break;
5588 case EFT_OUTPUT_INC_TILE_CELEBRATE:
5589 cat_snprintf(buf, bufsz,
5590 /* TRANS: %s is list of output types, with 'or' */
5591 PL_("* Each worked tile with at least 1 %s will yield"
5592 " %d more of it while the city working it is"
5593 " celebrating.",
5594 "* Each worked tile with at least 1 %s will yield"
5595 " %d more of it while the city working it is"
5596 " celebrating.", peffect->value),
5597 astr_str(&outputs_or), peffect->value);
5598 if (game.info.celebratesize > 1) {
5599 cat_snprintf(buf, bufsz,
5600 /* TRANS: Preserve leading space. %d should always be
5601 * 2 or greater. */
5602 _(" (Cities below size %d will not celebrate.)"),
5603 game.info.celebratesize);
5605 cat_snprintf(buf, bufsz, "\n");
5606 break;
5607 case EFT_OUTPUT_INC_TILE:
5608 cat_snprintf(buf, bufsz,
5609 /* TRANS: %s is list of output types, with 'or' */
5610 PL_("* Each worked tile with at least 1 %s will yield"
5611 " %d more of it.\n",
5612 "* Each worked tile with at least 1 %s will yield"
5613 " %d more of it.\n", peffect->value),
5614 astr_str(&outputs_or), peffect->value);
5615 break;
5616 case EFT_OUTPUT_BONUS:
5617 case EFT_OUTPUT_BONUS_2:
5618 /* FIXME: makes most sense iff world_value == 0 */
5619 cat_snprintf(buf, bufsz,
5620 /* TRANS: %s is list of output types, with 'and' */
5621 _("* %s production is increased %d%%.\n"),
5622 astr_str(&outputs_and), peffect->value);
5623 break;
5624 case EFT_OUTPUT_WASTE:
5625 if (world_value_valid) {
5626 if (net_value > 30) {
5627 cat_snprintf(buf, bufsz,
5628 /* TRANS: %s is list of output types, with 'and' */
5629 _("* %s production will suffer massive losses.\n"),
5630 astr_str(&outputs_and));
5631 } else if (net_value >= 15) {
5632 cat_snprintf(buf, bufsz,
5633 /* TRANS: %s is list of output types, with 'and' */
5634 _("* %s production will suffer some losses.\n"),
5635 astr_str(&outputs_and));
5636 } else if (net_value > 0) {
5637 cat_snprintf(buf, bufsz,
5638 /* TRANS: %s is list of output types, with 'and' */
5639 _("* %s production will suffer a small amount "
5640 "of losses.\n"),
5641 astr_str(&outputs_and));
5644 break;
5645 case EFT_HEALTH_PCT:
5646 if (playerwide) {
5647 if (peffect->value > 0) {
5648 CATLSTR(buf, bufsz, _("* Increases the chance of plague"
5649 " within your cities.\n"));
5650 } else if (peffect->value < 0) {
5651 CATLSTR(buf, bufsz, _("* Decreases the chance of plague"
5652 " within your cities.\n"));
5655 break;
5656 case EFT_OUTPUT_WASTE_BY_DISTANCE:
5657 if (world_value_valid) {
5658 if (net_value >= 3) {
5659 cat_snprintf(buf, bufsz,
5660 /* TRANS: %s is list of output types, with 'and' */
5661 _("* %s losses will increase quickly"
5662 " with distance from capital.\n"),
5663 astr_str(&outputs_and));
5664 } else if (net_value == 2) {
5665 cat_snprintf(buf, bufsz,
5666 /* TRANS: %s is list of output types, with 'and' */
5667 _("* %s losses will increase"
5668 " with distance from capital.\n"),
5669 astr_str(&outputs_and));
5670 } else if (net_value > 0) {
5671 cat_snprintf(buf, bufsz,
5672 /* TRANS: %s is list of output types, with 'and' */
5673 _("* %s losses will increase slowly"
5674 " with distance from capital.\n"),
5675 astr_str(&outputs_and));
5678 break;
5679 case EFT_MIGRATION_PCT:
5680 if (playerwide) {
5681 if (peffect->value > 0) {
5682 CATLSTR(buf, bufsz, _("* Increases the chance of migration"
5683 " into your cities.\n"));
5684 } else if (peffect->value < 0) {
5685 CATLSTR(buf, bufsz, _("* Decreases the chance of migration"
5686 " into your cities.\n"));
5689 break;
5690 default:
5691 break;
5695 strvec_destroy(outputs);
5696 astr_free(&outputs_or);
5697 astr_free(&outputs_and);
5699 } effect_list_iterate_end;
5701 unit_type_iterate(utype) {
5702 if (utype->need_government == gov) {
5703 cat_snprintf(buf, bufsz,
5704 _("* Allows you to build %s.\n"),
5705 utype_name_translation(utype));
5707 } unit_type_iterate_end;
5709 /* Action immunity */
5710 action_iterate(act) {
5711 if (action_by_number(act)->quiet) {
5712 /* The ruleset documents this action it self. */
5713 continue;
5716 if (action_immune_government(gov, act)) {
5717 cat_snprintf(buf, bufsz,
5718 _("* Makes it impossible to do the action \'%s\'"
5719 " to your %s.\n"),
5720 action_id_name_translation(act),
5721 _(action_target_kind_name(
5722 action_id_get_target_kind(act))));
5724 } action_iterate_end;
5726 if (user_text && user_text[0] != '\0') {
5727 cat_snprintf(buf, bufsz, "\n%s", user_text);
5731 /****************************************************************
5732 Returns pointer to static string with eg: "1 shield, 1 unhappy"
5733 *****************************************************************/
5734 char *helptext_unit_upkeep_str(struct unit_type *utype)
5736 static char buf[128];
5737 int any = 0;
5739 if (!utype) {
5740 log_error("Unknown unit!");
5741 return "";
5745 buf[0] = '\0';
5746 output_type_iterate(o) {
5747 if (utype->upkeep[o] > 0) {
5748 /* TRANS: "2 Food" or ", 1 Shield" */
5749 cat_snprintf(buf, sizeof(buf), _("%s%d %s"),
5750 (any > 0 ? Q_("?blistmore:, ") : ""), utype->upkeep[o],
5751 get_output_name(o));
5752 any++;
5754 } output_type_iterate_end;
5755 if (utype->happy_cost > 0) {
5756 /* TRANS: "2 Unhappy" or ", 1 Unhappy" */
5757 cat_snprintf(buf, sizeof(buf), _("%s%d Unhappy"),
5758 (any > 0 ? Q_("?blistmore:, ") : ""), utype->happy_cost);
5759 any++;
5762 if (any == 0) {
5763 /* strcpy(buf, _("None")); */
5764 fc_snprintf(buf, sizeof(buf), "%d", 0);
5766 return buf;
5769 /****************************************************************************
5770 Returns nation legend and characteristics
5771 ****************************************************************************/
5772 void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation,
5773 const char *user_text)
5775 struct universal source = {
5776 .kind = VUT_NATION,
5777 .value = {.nation = pnation}
5779 bool print_break = TRUE;
5780 #define PRINT_BREAK() do { \
5781 if (print_break) { \
5782 if (buf[0] != '\0') { \
5783 CATLSTR(buf, bufsz, "\n\n"); \
5785 print_break = FALSE; \
5787 } while(0)
5789 fc_assert_ret(NULL != buf && 0 < bufsz);
5790 buf[0] = '\0';
5792 if (pnation->legend[0] != '\0') {
5793 /* Client side legend is stored already translated */
5794 cat_snprintf(buf, bufsz, "%s", pnation->legend);
5797 if (pnation->init_government) {
5798 PRINT_BREAK();
5799 cat_snprintf(buf, bufsz,
5800 _("Initial government is %s.\n"),
5801 government_name_translation(pnation->init_government));
5803 if (pnation->init_techs[0] != A_LAST) {
5804 const char *tech_names[MAX_NUM_TECH_LIST];
5805 int i;
5806 struct astring list = ASTRING_INIT;
5807 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
5808 if (pnation->init_techs[i] == A_LAST) {
5809 break;
5811 tech_names[i] =
5812 advance_name_translation(advance_by_number(pnation->init_techs[i]));
5814 astr_build_and_list(&list, tech_names, i);
5815 PRINT_BREAK();
5816 if (game.rgame.global_init_techs[0] != A_LAST) {
5817 cat_snprintf(buf, bufsz,
5818 /* TRANS: %s is an and-separated list of techs */
5819 _("Starts with knowledge of %s in addition to the standard "
5820 "starting technologies.\n"), astr_str(&list));
5821 } else {
5822 cat_snprintf(buf, bufsz,
5823 /* TRANS: %s is an and-separated list of techs */
5824 _("Starts with knowledge of %s.\n"), astr_str(&list));
5826 astr_free(&list);
5828 if (pnation->init_units[0]) {
5829 const struct unit_type *utypes[MAX_NUM_UNIT_LIST];
5830 int count[MAX_NUM_UNIT_LIST];
5831 int i, j, n = 0, total = 0;
5832 /* Count how many of each type there is. */
5833 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
5834 if (!pnation->init_units[i]) {
5835 break;
5837 for (j = 0; j < n; j++) {
5838 if (pnation->init_units[i] == utypes[j]) {
5839 count[j]++;
5840 total++;
5841 break;
5844 if (j == n) {
5845 utypes[n] = pnation->init_units[i];
5846 count[n] = 1;
5847 total++;
5848 n++;
5852 /* Construct the list of unit types and counts. */
5853 struct astring utype_names[MAX_NUM_UNIT_LIST];
5854 struct astring list = ASTRING_INIT;
5855 for (i = 0; i < n; i++) {
5856 astr_init(&utype_names[i]);
5857 if (count[i] > 1) {
5858 /* TRANS: a unit type followed by a count. For instance,
5859 * "Fighter (2)" means two Fighters. Count is never 1.
5860 * Used in a list. */
5861 astr_set(&utype_names[i], _("%s (%d)"),
5862 utype_name_translation(utypes[i]), count[i]);
5863 } else {
5864 astr_set(&utype_names[i], "%s", utype_name_translation(utypes[i]));
5868 const char *utype_name_strs[MAX_NUM_UNIT_LIST];
5869 for (i = 0; i < n; i++) {
5870 utype_name_strs[i] = astr_str(&utype_names[i]);
5872 astr_build_and_list(&list, utype_name_strs, n);
5874 for (i = 0; i < n; i++) {
5875 astr_free(&utype_names[i]);
5877 PRINT_BREAK();
5878 cat_snprintf(buf, bufsz,
5879 /* TRANS: %s is an and-separated list of unit types
5880 * possibly with counts. Plurality is in total number of
5881 * units represented. */
5882 PL_("Starts with the following additional unit: %s.\n",
5883 "Starts with the following additional units: %s.\n",
5884 total), astr_str(&list));
5885 astr_free(&list);
5888 if (pnation->init_buildings[0] != B_LAST) {
5889 const char *impr_names[MAX_NUM_BUILDING_LIST];
5890 int i;
5891 struct astring list = ASTRING_INIT;
5892 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
5893 if (pnation->init_buildings[i] == B_LAST) {
5894 break;
5896 impr_names[i] =
5897 improvement_name_translation(
5898 improvement_by_number(pnation->init_buildings[i]));
5900 astr_build_and_list(&list, impr_names, i);
5901 PRINT_BREAK();
5902 if (game.rgame.global_init_buildings[0] != B_LAST) {
5903 cat_snprintf(buf, bufsz,
5904 /* TRANS: %s is an and-separated list of improvements */
5905 _("First city will get %s for free in addition to the "
5906 "standard improvements.\n"), astr_str(&list));
5907 } else {
5908 cat_snprintf(buf, bufsz,
5909 /* TRANS: %s is an and-separated list of improvements */
5910 _("First city will get %s for free.\n"), astr_str(&list));
5912 astr_free(&list);
5915 if (buf[0] != '\0') {
5916 CATLSTR(buf, bufsz, "\n");
5918 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
5920 if (user_text && user_text[0] != '\0') {
5921 if (buf[0] != '\0') {
5922 CATLSTR(buf, bufsz, "\n");
5924 CATLSTR(buf, bufsz, user_text);
5926 #undef PRINT_BREAK