1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 /***********************************************************************
15 This module is for generic handling of help data, independent
16 of gui considerations.
17 ***********************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include <fc_config.h>
21 #endif
22
23 #include <stdio.h>
24 #include <string.h>
25
26 /* utility */
27 #include "astring.h"
28 #include "bitvector.h"
29 #include "fciconv.h"
30 #include "fcintl.h"
31 #include "log.h"
32 #include "mem.h"
33 #include "registry.h"
34 #include "string_vector.h"
35 #include "support.h"
36
37 /* common */
38 #include "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"
55
56 /* client */
57 #include "client_main.h"
58 #include "climisc.h"
59 #include "gui_main_g.h" /* client_string */
60
61 #include "helpdata.h"
62
63 /* helper macro for easy conversion from snprintf and cat_snprintf */
64 #define CATLSTR(_b, _s, _t) fc_strlcat(_b, _t, _s)
65
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
71 };
72
73 #define SPECLIST_TAG help
74 #define SPECLIST_TYPE struct help_item
75 #include "speclist.h"
76
77 #define help_list_iterate(helplist, phelp) \
78 TYPED_LIST_ITERATE(struct help_item, helplist, phelp)
79 #define help_list_iterate_end LIST_ITERATE_END
80
81 static const struct help_list_link *help_nodes_iterator;
82 static struct help_list *help_nodes;
83 static bool help_nodes_init = FALSE;
84 /* help_nodes_init is not quite the same as booted in boot_help_texts();
85 latter can be FALSE even after call, eg if couldn't find helpdata.txt.
86 */
87
88 /****************************************************************
89 Initialize.
90 *****************************************************************/
helpdata_init(void)91 void helpdata_init(void)
92 {
93 help_nodes = help_list_new();
94 }
95
96 /****************************************************************
97 Clean up.
98 *****************************************************************/
helpdata_done(void)99 void helpdata_done(void)
100 {
101 help_list_destroy(help_nodes);
102 }
103
104 /****************************************************************
105 Make sure help_nodes is initialised.
106 Should call this just about everywhere which uses help_nodes,
107 to be careful... or at least where called by external
108 (non-static) functions.
109 *****************************************************************/
check_help_nodes_init(void)110 static void check_help_nodes_init(void)
111 {
112 if (!help_nodes_init) {
113 help_nodes_init = TRUE; /* before help_iter_start to avoid recursion! */
114 help_iter_start();
115 }
116 }
117
118 /****************************************************************
119 Free all allocations associated with help_nodes.
120 *****************************************************************/
free_help_texts(void)121 void free_help_texts(void)
122 {
123 check_help_nodes_init();
124 help_list_iterate(help_nodes, ptmp) {
125 free(ptmp->topic);
126 free(ptmp->text);
127 free(ptmp);
128 } help_list_iterate_end;
129 help_list_clear(help_nodes);
130 }
131
132 /****************************************************************************
133 Returns whether we should show help for this nation.
134 ****************************************************************************/
show_help_for_nation(const struct nation_type * pnation)135 static bool show_help_for_nation(const struct nation_type *pnation)
136 {
137 return client_nation_is_in_current_set(pnation);
138 }
139
140 /****************************************************************************
141 Insert fixed-width table describing veteran system.
142 If only one veteran level, inserts 'nolevels' if non-NULL.
143 Otherwise, insert 'intro' then a table.
144 ****************************************************************************/
insert_veteran_help(char * outbuf,size_t outlen,const struct veteran_system * veteran,const char * intro,const char * nolevels)145 static bool insert_veteran_help(char *outbuf, size_t outlen,
146 const struct veteran_system *veteran,
147 const char *intro, const char *nolevels)
148 {
149 /* game.veteran can be NULL in pregame; if so, keep quiet about
150 * veteran levels */
151 if (!veteran) {
152 return FALSE;
153 }
154
155 fc_assert_ret_val(veteran->levels >= 1, FALSE);
156
157 if (veteran->levels == 1) {
158 /* Only a single veteran level. Don't bother to name it. */
159 if (nolevels) {
160 CATLSTR(outbuf, outlen, nolevels);
161 return TRUE;
162 } else {
163 return FALSE;
164 }
165 } else {
166 int i;
167 fc_assert_ret_val(veteran->definitions != NULL, FALSE);
168 if (intro) {
169 CATLSTR(outbuf, outlen, intro);
170 CATLSTR(outbuf, outlen, "\n\n");
171 }
172 /* raise_chance and work_raise_chance don't get to the client, so we
173 * can't report them */
174 CATLSTR(outbuf, outlen,
175 /* TRANS: Header for fixed-width veteran level table.
176 * TRANS: Translators cannot change column widths :(
177 * TRANS: "Level name" left-justified, other two right-justified */
178 _("Veteran level Power factor Move bonus\n"));
179 CATLSTR(outbuf, outlen,
180 /* TRANS: Part of header for veteran level table. */
181 _("--------------------------------------------"));
182 for (i = 0; i < veteran->levels; i++) {
183 const struct veteran_level *level = &veteran->definitions[i];
184 const char *name = name_translation_get(&level->name);
185 /* Use get_internal_string_length() for correct alignment with
186 * multibyte character encodings */
187 cat_snprintf(outbuf, outlen,
188 "\n%s%*s %4d%% %12s",
189 name, MAX(0, 25 - (int)get_internal_string_length(name)), "",
190 level->power_fact,
191 /* e.g. "- ", "+ 1/3", "+ 1 ", "+ 2 2/3" */
192 move_points_text_full(level->move_bonus, TRUE, "+ ", "-", TRUE));
193 }
194 return TRUE;
195 }
196 }
197
198 /****************************************************************************
199 Insert generated text for the helpdata "name".
200 Returns TRUE if anything was added.
201 ****************************************************************************/
insert_generated_text(char * outbuf,size_t outlen,const char * name)202 static bool insert_generated_text(char *outbuf, size_t outlen, const char *name)
203 {
204 if (!game.client.ruleset_init) {
205 return FALSE;
206 }
207
208 if (0 == strcmp(name, "TerrainAlterations")) {
209 int clean_pollution_time = -1, clean_fallout_time = -1, pillage_time = -1;
210 bool terrain_independent_extras = FALSE;
211
212 CATLSTR(outbuf, outlen,
213 /* TRANS: Header for fixed-width terrain alteration table.
214 * TRANS: Translators cannot change column widths :( */
215 _("Terrain Irrigation Mining Transform\n"));
216 CATLSTR(outbuf, outlen,
217 "----------------------------------------------------------------\n");
218 terrain_type_iterate(pterrain) {
219 if (0 != strlen(terrain_rule_name(pterrain))) {
220 char irrigation_time[4], mining_time[4], transform_time[4];
221 const char *terrain, *irrigation_result,
222 *mining_result,*transform_result;
223 struct universal for_terr = { .kind = VUT_TERRAIN, .value = { .terrain = pterrain }};
224
225 fc_snprintf(irrigation_time, sizeof(irrigation_time),
226 "%d", pterrain->irrigation_time);
227 fc_snprintf(mining_time, sizeof(mining_time),
228 "%d", pterrain->mining_time);
229 fc_snprintf(transform_time, sizeof(transform_time),
230 "%d", pterrain->transform_time);
231 terrain = terrain_name_translation(pterrain);
232 irrigation_result =
233 (pterrain->irrigation_result == pterrain
234 || pterrain->irrigation_result == T_NONE
235 || effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) <= 0) ? ""
236 : terrain_name_translation(pterrain->irrigation_result);
237 mining_result =
238 (pterrain->mining_result == pterrain
239 || pterrain->mining_result == T_NONE
240 || effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) <= 0) ? ""
241 : terrain_name_translation(pterrain->mining_result);
242 transform_result =
243 (pterrain->transform_result == pterrain
244 || pterrain->transform_result == T_NONE
245 || effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) <= 0) ? ""
246 : terrain_name_translation(pterrain->transform_result);
247 /* Use get_internal_string_length() for correct alignment with
248 * multibyte character encodings */
249 cat_snprintf(outbuf, outlen,
250 "%s%*s %3s %s%*s %3s %s%*s %3s %s\n",
251 terrain,
252 MAX(0, 12 - (int)get_internal_string_length(terrain)), "",
253 (pterrain->irrigation_result == T_NONE) ? "-" : irrigation_time,
254 irrigation_result,
255 MAX(0, 12 - (int)get_internal_string_length(irrigation_result)), "",
256 (pterrain->mining_result == T_NONE) ? "-" : mining_time,
257 mining_result,
258 MAX(0, 12 - (int)get_internal_string_length(mining_result)), "",
259 (pterrain->transform_result == T_NONE) ? "-" : transform_time,
260 transform_result);
261
262 if (clean_pollution_time != 0 && pterrain->clean_pollution_time != 0) {
263 if (clean_pollution_time < 0) {
264 clean_pollution_time = pterrain->clean_pollution_time;
265 } else {
266 if (clean_pollution_time != pterrain->clean_pollution_time) {
267 clean_pollution_time = 0; /* give up */
268 }
269 }
270 }
271 if (clean_fallout_time != 0 && pterrain->clean_fallout_time != 0) {
272 if (clean_fallout_time < 0) {
273 clean_fallout_time = pterrain->clean_fallout_time;
274 } else {
275 if (clean_fallout_time != pterrain->clean_fallout_time) {
276 clean_fallout_time = 0; /* give up */
277 }
278 }
279 }
280 if (pillage_time != 0 && pterrain->pillage_time != 0) {
281 if (pillage_time < 0) {
282 pillage_time = pterrain->pillage_time;
283 } else {
284 if (pillage_time != pterrain->pillage_time) {
285 pillage_time = 0; /* give up */
286 }
287 }
288 }
289 }
290 } terrain_type_iterate_end;
291
292 /* Examine extras to see if time of removal activities really is
293 * terrain-independent, and take into account removal_time_factor.
294 * XXX: this is rather overwrought to handle cases which the ruleset
295 * author could express much more simply for the same result */
296 {
297 int time = -1, factor = -1;
298 extra_type_by_rmcause_iterate(ERM_CLEANPOLLUTION, pextra) {
299 if (pextra->removal_time == 0) {
300 if (factor < 0) {
301 factor = pextra->removal_time_factor;
302 } else if (factor != pextra->removal_time_factor) {
303 factor = 0; /* give up */
304 }
305 } else {
306 if (time < 0) {
307 time = pextra->removal_time;
308 } else if (time != pextra->removal_time) {
309 time = 0; /* give up */
310 }
311 }
312 } extra_type_by_rmcause_iterate_end;
313 if (factor < 0) {
314 /* No extra has terrain-dependent clean time; use extra's time */
315 if (time >= 0) {
316 clean_pollution_time = time;
317 } else {
318 clean_pollution_time = 0;
319 }
320 } else if (clean_pollution_time != 0) {
321 /* At least one extra's time depends on terrain */
322 fc_assert(clean_pollution_time > 0);
323 if (time > 0 && factor > 0 && time != clean_pollution_time * factor) {
324 clean_pollution_time = 0;
325 } else if (time >= 0) {
326 clean_pollution_time = time;
327 } else if (factor >= 0) {
328 clean_pollution_time = clean_pollution_time * factor;
329 } else {
330 fc_assert(FALSE);
331 }
332 }
333 }
334
335 {
336 int time = -1, factor = -1;
337 extra_type_by_rmcause_iterate(ERM_CLEANFALLOUT, pextra) {
338 if (pextra->removal_time == 0) {
339 if (factor < 0) {
340 factor = pextra->removal_time_factor;
341 } else if (factor != pextra->removal_time_factor) {
342 factor = 0; /* give up */
343 }
344 } else {
345 if (time < 0) {
346 time = pextra->removal_time;
347 } else if (time != pextra->removal_time) {
348 time = 0; /* give up */
349 }
350 }
351 } extra_type_by_rmcause_iterate_end;
352 if (factor < 0) {
353 /* No extra has terrain-dependent clean time; use extra's time */
354 if (time >= 0) {
355 clean_fallout_time = time;
356 } else {
357 clean_fallout_time = 0;
358 }
359 } else if (clean_fallout_time != 0) {
360 /* At least one extra's time depends on terrain */
361 fc_assert(clean_fallout_time > 0);
362 if (time > 0 && factor > 0 && time != clean_fallout_time * factor) {
363 clean_fallout_time = 0;
364 } else if (time >= 0) {
365 clean_fallout_time = time;
366 } else if (factor >= 0) {
367 clean_fallout_time = clean_fallout_time * factor;
368 } else {
369 fc_assert(FALSE);
370 }
371 }
372 }
373
374 {
375 int time = -1, factor = -1;
376 extra_type_by_rmcause_iterate(ERM_PILLAGE, pextra) {
377 if (pextra->removal_time == 0) {
378 if (factor < 0) {
379 factor = pextra->removal_time_factor;
380 } else if (factor != pextra->removal_time_factor) {
381 factor = 0; /* give up */
382 }
383 } else {
384 if (time < 0) {
385 time = pextra->removal_time;
386 } else if (time != pextra->removal_time) {
387 time = 0; /* give up */
388 }
389 }
390 } extra_type_by_rmcause_iterate_end;
391 if (factor < 0) {
392 /* No extra has terrain-dependent pillage time; use extra's time */
393 if (time >= 0) {
394 pillage_time = time;
395 } else {
396 pillage_time = 0;
397 }
398 } else if (pillage_time != 0) {
399 /* At least one extra's time depends on terrain */
400 fc_assert(pillage_time > 0);
401 if (time > 0 && factor > 0 && time != pillage_time * factor) {
402 pillage_time = 0;
403 } else if (time >= 0) {
404 pillage_time = time;
405 } else if (factor >= 0) {
406 pillage_time = pillage_time * factor;
407 } else {
408 fc_assert(FALSE);
409 }
410 }
411 }
412
413 /* Check whether there are any bases or roads whose build time is
414 * independent of terrain */
415
416 extra_type_by_cause_iterate(EC_BASE, pextra) {
417 if (pextra->buildable && pextra->build_time > 0) {
418 terrain_independent_extras = TRUE;
419 break;
420 }
421 } extra_type_by_cause_iterate_end;
422 if (!terrain_independent_extras) {
423 extra_type_by_cause_iterate(EC_ROAD, pextra) {
424 if (pextra->buildable && pextra->build_time > 0) {
425 terrain_independent_extras = TRUE;
426 break;
427 }
428 } extra_type_by_cause_iterate_end;
429 }
430
431 if (clean_pollution_time > 0 || clean_fallout_time > 0 || pillage_time > 0
432 || terrain_independent_extras) {
433 CATLSTR(outbuf, outlen, "\n");
434 CATLSTR(outbuf, outlen,
435 _("Time taken for the following activities is independent of "
436 "terrain:\n"));
437 CATLSTR(outbuf, outlen, "\n");
438 CATLSTR(outbuf, outlen,
439 /* TRANS: Header for fixed-width terrain alteration table.
440 * TRANS: Translators cannot change column widths :( */
441 _("Activity Time\n"));
442 CATLSTR(outbuf, outlen,
443 "---------------------------");
444 if (clean_pollution_time > 0)
445 cat_snprintf(outbuf, outlen,
446 _("\nClean pollution %3d"), clean_pollution_time);
447 if (clean_fallout_time > 0)
448 cat_snprintf(outbuf, outlen,
449 _("\nClean fallout %3d"), clean_fallout_time);
450 if (pillage_time > 0)
451 cat_snprintf(outbuf, outlen,
452 _("\nPillage %3d"), pillage_time);
453 extra_type_by_cause_iterate(EC_ROAD, pextra) {
454 if (pextra->buildable && pextra->build_time > 0) {
455 const char *rname = extra_name_translation(pextra);
456
457 cat_snprintf(outbuf, outlen,
458 "\n%s%*s %3d",
459 rname,
460 MAX(0, 18 - (int)get_internal_string_length(rname)), "",
461 pextra->build_time);
462 }
463 } extra_type_by_cause_iterate_end;
464 extra_type_by_cause_iterate(EC_BASE, pextra) {
465 if (pextra->buildable && pextra->build_time > 0) {
466 const char *bname = extra_name_translation(pextra);
467
468 cat_snprintf(outbuf, outlen,
469 "\n%s%*s %3d",
470 bname,
471 MAX(0, 18 - (int)get_internal_string_length(bname)), "",
472 pextra->build_time);
473 }
474 } extra_type_by_cause_iterate_end;
475 }
476 return TRUE;
477 } else if (0 == strcmp(name, "VeteranLevels")) {
478 return insert_veteran_help(outbuf, outlen, game.veteran,
479 _("In this ruleset, the following veteran levels are defined:"),
480 _("This ruleset has no default veteran levels defined."));
481 } else if (0 == strcmp(name, "FreecivVersion")) {
482 const char *ver = freeciv_name_version();
483
484 cat_snprintf(outbuf, outlen,
485 /* TRANS: First %s is version string, e.g.,
486 * "Freeciv version 2.3.0-beta1 (beta version)" (translated).
487 * Second %s is client_string, e.g., "gui-gtk-2.0". */
488 _("This is %s, %s client."), ver, client_string);
489 insert_client_build_info(outbuf, outlen);
490
491 return TRUE;
492 } else if (0 == strcmp(name, "DefaultMetaserver")) {
493 cat_snprintf(outbuf, outlen, " %s", FREECIV_META_URL);
494
495 return TRUE;
496 }
497 log_error("Unknown directive '$%s' in help", name);
498 return FALSE;
499 }
500
501 /****************************************************************
502 Append text for the requirement. Something like
503
504 "Requires knowledge of the technology Communism.\n"
505
506 pplayer may be NULL. Note that it must be updated everytime
507 a new requirement type or range is defined.
508 *****************************************************************/
insert_requirement(char * buf,size_t bufsz,struct player * pplayer,const struct requirement * preq,const char * prefix)509 static bool insert_requirement(char *buf, size_t bufsz,
510 struct player *pplayer,
511 const struct requirement *preq,
512 const char *prefix)
513 {
514 if (preq->quiet) {
515 return FALSE;
516 }
517
518 switch (preq->source.kind) {
519 case VUT_NONE:
520 return FALSE;
521
522 case VUT_ADVANCE:
523 switch (preq->range) {
524 case REQ_RANGE_PLAYER:
525 CATLSTR(buf, bufsz, prefix);
526 if (preq->present) {
527 cat_snprintf(buf, bufsz,
528 _("Requires knowledge of the technology %s.\n"),
529 advance_name_translation(preq->source.value.advance));
530 } else {
531 cat_snprintf(buf, bufsz,
532 _("Prevented by knowledge of the technology %s.\n"),
533 advance_name_translation(preq->source.value.advance));
534 }
535 return TRUE;
536 case REQ_RANGE_TEAM:
537 CATLSTR(buf, bufsz, prefix);
538 if (preq->present) {
539 cat_snprintf(buf, bufsz,
540 _("Requires that a player on your team knows the "
541 "technology %s.\n"),
542 advance_name_translation(preq->source.value.advance));
543 } else {
544 cat_snprintf(buf, bufsz,
545 _("Prevented if any player on your team knows the "
546 "technology %s.\n"),
547 advance_name_translation(preq->source.value.advance));
548 }
549 return TRUE;
550 case REQ_RANGE_ALLIANCE:
551 CATLSTR(buf, bufsz, prefix);
552 if (preq->present) {
553 cat_snprintf(buf, bufsz,
554 _("Requires that a player allied to you knows the "
555 "technology %s.\n"),
556 advance_name_translation(preq->source.value.advance));
557 } else {
558 cat_snprintf(buf, bufsz,
559 _("Prevented if any player allied to you knows the "
560 "technology %s.\n"),
561 advance_name_translation(preq->source.value.advance));
562 }
563 return TRUE;
564 case REQ_RANGE_WORLD:
565 CATLSTR(buf, bufsz, prefix);
566 if (preq->survives) {
567 if (preq->present) {
568 cat_snprintf(buf, bufsz,
569 _("Requires that someone has discovered the "
570 "technology %s.\n"),
571 advance_name_translation(preq->source.value.advance));
572 } else {
573 cat_snprintf(buf, bufsz,
574 _("Requires that no-one has yet discovered the "
575 "technology %s.\n"),
576 advance_name_translation(preq->source.value.advance));
577 }
578 } else {
579 if (preq->present) {
580 cat_snprintf(buf, bufsz,
581 _("Requires that some player knows the "
582 "technology %s.\n"),
583 advance_name_translation(preq->source.value.advance));
584 } else {
585 cat_snprintf(buf, bufsz,
586 _("Requires that no player knows the "
587 "technology %s.\n"),
588 advance_name_translation(preq->source.value.advance));
589 }
590 }
591 return TRUE;
592 case REQ_RANGE_LOCAL:
593 case REQ_RANGE_CADJACENT:
594 case REQ_RANGE_ADJACENT:
595 case REQ_RANGE_CITY:
596 case REQ_RANGE_TRADEROUTE:
597 case REQ_RANGE_CONTINENT:
598 case REQ_RANGE_COUNT:
599 /* Not supported. */
600 break;
601 }
602 break;
603
604 case VUT_TECHFLAG:
605 switch (preq->range) {
606 case REQ_RANGE_PLAYER:
607 CATLSTR(buf, bufsz, prefix);
608 if (preq->present) {
609 cat_snprintf(buf, bufsz,
610 /* TRANS: %s is a (translatable) tech flag. */
611 _("Requires knowledge of a technology with the "
612 "\"%s\" flag.\n"),
613 tech_flag_id_translated_name(preq->source.value.techflag));
614 } else {
615 cat_snprintf(buf, bufsz,
616 /* TRANS: %s is a (translatable) tech flag. */
617 _("Prevented by knowledge of any technology with the "
618 "\"%s\" flag.\n"),
619 tech_flag_id_translated_name(preq->source.value.techflag));
620 }
621 return TRUE;
622 case REQ_RANGE_TEAM:
623 CATLSTR(buf, bufsz, prefix);
624 if (preq->present) {
625 cat_snprintf(buf, bufsz,
626 /* TRANS: %s is a (translatable) tech flag. */
627 _("Requires that a player on your team knows "
628 "a technology with the \"%s\" flag.\n"),
629 tech_flag_id_translated_name(preq->source.value.techflag));
630 } else {
631 cat_snprintf(buf, bufsz,
632 /* TRANS: %s is a (translatable) tech flag. */
633 _("Prevented if any player on your team knows "
634 "any technology with the \"%s\" flag.\n"),
635 tech_flag_id_translated_name(preq->source.value.techflag));
636 }
637 return TRUE;
638 case REQ_RANGE_ALLIANCE:
639 CATLSTR(buf, bufsz, prefix);
640 if (preq->present) {
641 cat_snprintf(buf, bufsz,
642 /* TRANS: %s is a (translatable) tech flag. */
643 _("Requires that a player allied to you knows "
644 "a technology with the \"%s\" flag.\n"),
645 tech_flag_id_translated_name(preq->source.value.techflag));
646 } else {
647 cat_snprintf(buf, bufsz,
648 /* TRANS: %s is a (translatable) tech flag. */
649 _("Prevented if any player allied to you knows "
650 "any technology with the \"%s\" flag.\n"),
651 tech_flag_id_translated_name(preq->source.value.techflag));
652 }
653 return TRUE;
654 case REQ_RANGE_WORLD:
655 CATLSTR(buf, bufsz, prefix);
656 if (preq->present) {
657 cat_snprintf(buf, bufsz,
658 /* TRANS: %s is a (translatable) tech flag. */
659 _("Requires that some player knows a technology "
660 "with the \"%s\" flag.\n"),
661 tech_flag_id_translated_name(preq->source.value.techflag));
662 } else {
663 cat_snprintf(buf, bufsz,
664 /* TRANS: %s is a (translatable) tech flag. */
665 _("Requires that no player knows any technology with "
666 "the \"%s\" flag.\n"),
667 tech_flag_id_translated_name(preq->source.value.techflag));
668 }
669 return TRUE;
670 case REQ_RANGE_LOCAL:
671 case REQ_RANGE_CADJACENT:
672 case REQ_RANGE_ADJACENT:
673 case REQ_RANGE_CITY:
674 case REQ_RANGE_TRADEROUTE:
675 case REQ_RANGE_CONTINENT:
676 case REQ_RANGE_COUNT:
677 /* Not supported. */
678 break;
679 }
680 break;
681
682 case VUT_GOVERNMENT:
683 if (preq->range != REQ_RANGE_PLAYER) {
684 break;
685 }
686 CATLSTR(buf, bufsz, prefix);
687 if (preq->present) {
688 cat_snprintf(buf, bufsz, _("Requires the %s government.\n"),
689 government_name_translation(preq->source.value.govern));
690 } else {
691 cat_snprintf(buf, bufsz, _("Not available under the %s government.\n"),
692 government_name_translation(preq->source.value.govern));
693 }
694 return TRUE;
695
696 case VUT_ACHIEVEMENT:
697 switch (preq->range) {
698 case REQ_RANGE_PLAYER:
699 CATLSTR(buf, bufsz, prefix);
700 if (preq->present) {
701 cat_snprintf(buf, bufsz, _("Requires you to have achieved \"%s\".\n"),
702 achievement_name_translation(preq->source.value.achievement));
703 } else {
704 cat_snprintf(buf, bufsz, _("Not available once you have achieved "
705 "\"%s\".\n"),
706 achievement_name_translation(preq->source.value.achievement));
707 }
708 return TRUE;
709 case REQ_RANGE_TEAM:
710 CATLSTR(buf, bufsz, prefix);
711 if (preq->present) {
712 cat_snprintf(buf, bufsz, _("Requires that at least one of your "
713 "team-mates has achieved \"%s\".\n"),
714 achievement_name_translation(preq->source.value.achievement));
715 } else {
716 cat_snprintf(buf, bufsz, _("Not available if any of your team-mates "
717 "has achieved \"%s\".\n"),
718 achievement_name_translation(preq->source.value.achievement));
719 }
720 return TRUE;
721 case REQ_RANGE_ALLIANCE:
722 CATLSTR(buf, bufsz, prefix);
723 if (preq->present) {
724 cat_snprintf(buf, bufsz, _("Requires that at least one of your allies "
725 "has achieved \"%s\".\n"),
726 achievement_name_translation(preq->source.value.achievement));
727 } else {
728 cat_snprintf(buf, bufsz, _("Not available if any of your allies has "
729 "achieved \"%s\".\n"),
730 achievement_name_translation(preq->source.value.achievement));
731 }
732 return TRUE;
733 case REQ_RANGE_WORLD:
734 CATLSTR(buf, bufsz, prefix);
735 if (preq->present) {
736 cat_snprintf(buf, bufsz, _("Requires that at least one player "
737 "has achieved \"%s\".\n"),
738 achievement_name_translation(preq->source.value.achievement));
739 } else {
740 cat_snprintf(buf, bufsz, _("Not available if any player has "
741 "achieved \"%s\".\n"),
742 achievement_name_translation(preq->source.value.achievement));
743 }
744 return TRUE;
745 case REQ_RANGE_LOCAL:
746 case REQ_RANGE_CADJACENT:
747 case REQ_RANGE_ADJACENT:
748 case REQ_RANGE_CITY:
749 case REQ_RANGE_TRADEROUTE:
750 case REQ_RANGE_CONTINENT:
751 case REQ_RANGE_COUNT:
752 /* Not supported. */
753 break;
754 }
755 break;
756
757 case VUT_IMPROVEMENT:
758 switch (preq->range) {
759 case REQ_RANGE_WORLD:
760 if (is_great_wonder(preq->source.value.building)) {
761 CATLSTR(buf, bufsz, prefix);
762 if (preq->survives) {
763 if (preq->present) {
764 if (can_improvement_go_obsolete(preq->source.value.building)) {
765 cat_snprintf(buf, bufsz,
766 /* TRANS: %s is a wonder */
767 _("Requires that %s was built at some point, "
768 "and that it has not yet been rendered "
769 "obsolete.\n"),
770 improvement_name_translation
771 (preq->source.value.building));
772 } else {
773 cat_snprintf(buf, bufsz,
774 /* TRANS: %s is a wonder */
775 _("Requires that %s was built at some point.\n"),
776 improvement_name_translation
777 (preq->source.value.building));
778 }
779 } else {
780 if (can_improvement_go_obsolete(preq->source.value.building)) {
781 cat_snprintf(buf, bufsz,
782 /* TRANS: %s is a wonder */
783 _("Prevented if %s has ever been built, "
784 "unless it would be obsolete.\n"),
785 improvement_name_translation
786 (preq->source.value.building));
787 } else {
788 cat_snprintf(buf, bufsz,
789 /* TRANS: %s is a wonder */
790 _("Prevented if %s has ever been built.\n"),
791 improvement_name_translation
792 (preq->source.value.building));
793 }
794 }
795 } else {
796 /* Non-surviving requirement */
797 if (preq->present) {
798 if (can_improvement_go_obsolete(preq->source.value.building)) {
799 cat_snprintf(buf, bufsz,
800 /* TRANS: %s is a wonder */
801 _("Requires %s to be owned by any player "
802 "and not yet obsolete.\n"),
803 improvement_name_translation
804 (preq->source.value.building));
805 } else {
806 cat_snprintf(buf, bufsz,
807 /* TRANS: %s is a wonder */
808 _("Requires %s to be owned by any player.\n"),
809 improvement_name_translation
810 (preq->source.value.building));
811 }
812 } else {
813 if (can_improvement_go_obsolete(preq->source.value.building)) {
814 cat_snprintf(buf, bufsz,
815 /* TRANS: %s is a wonder */
816 _("Prevented if %s is currently owned by "
817 "any player, unless it is obsolete.\n"),
818 improvement_name_translation
819 (preq->source.value.building));
820 } else {
821 cat_snprintf(buf, bufsz,
822 /* TRANS: %s is a wonder */
823 _("Prevented if %s is currently owned by "
824 "any player.\n"),
825 improvement_name_translation
826 (preq->source.value.building));
827 }
828 }
829 }
830 return TRUE;
831 }
832 /* non-great-wonder world-ranged requirements not supported */
833 break;
834 case REQ_RANGE_ALLIANCE:
835 if (is_wonder(preq->source.value.building)) {
836 CATLSTR(buf, bufsz, prefix);
837 if (preq->survives) {
838 if (preq->present) {
839 if (can_improvement_go_obsolete(preq->source.value.building)) {
840 cat_snprintf(buf, bufsz,
841 /* TRANS: %s is a wonder */
842 _("Requires someone who is currently allied to "
843 "you to have built %s at some point, and for "
844 "it not to have been rendered obsolete.\n"),
845 improvement_name_translation
846 (preq->source.value.building));
847 } else {
848 cat_snprintf(buf, bufsz,
849 /* TRANS: %s is a wonder */
850 _("Requires someone who is currently allied to "
851 "you to have built %s at some point.\n"),
852 improvement_name_translation
853 (preq->source.value.building));
854 }
855 } else {
856 if (can_improvement_go_obsolete(preq->source.value.building)) {
857 cat_snprintf(buf, bufsz,
858 /* TRANS: %s is a wonder */
859 _("Prevented if someone currently allied to you "
860 "has ever built %s, unless it would be "
861 "obsolete.\n"),
862 improvement_name_translation
863 (preq->source.value.building));
864 } else {
865 cat_snprintf(buf, bufsz,
866 /* TRANS: %s is a wonder */
867 _("Prevented if someone currently allied to you "
868 "has ever built %s.\n"),
869 improvement_name_translation
870 (preq->source.value.building));
871 }
872 }
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 someone allied to you to own %s, "
880 "and for it not to have been rendered "
881 "obsolete.\n"),
882 improvement_name_translation
883 (preq->source.value.building));
884 } else {
885 cat_snprintf(buf, bufsz,
886 /* TRANS: %s is a wonder */
887 _("Requires someone allied to you to own %s.\n"),
888 improvement_name_translation
889 (preq->source.value.building));
890 }
891 } else {
892 if (can_improvement_go_obsolete(preq->source.value.building)) {
893 cat_snprintf(buf, bufsz,
894 /* TRANS: %s is a wonder */
895 _("Prevented if someone allied to you owns %s, "
896 "unless it is obsolete.\n"),
897 improvement_name_translation
898 (preq->source.value.building));
899 } else {
900 cat_snprintf(buf, bufsz,
901 /* TRANS: %s is a wonder */
902 _("Prevented if someone allied to you owns %s.\n"),
903 improvement_name_translation
904 (preq->source.value.building));
905 }
906 }
907 }
908 return TRUE;
909 }
910 /* non-wonder alliance-ranged requirements not supported */
911 break;
912 case REQ_RANGE_TEAM:
913 if (is_wonder(preq->source.value.building)) {
914 CATLSTR(buf, bufsz, prefix);
915 if (preq->survives) {
916 if (preq->present) {
917 if (can_improvement_go_obsolete(preq->source.value.building)) {
918 cat_snprintf(buf, bufsz,
919 /* TRANS: %s is a wonder */
920 _("Requires someone on your team to have "
921 "built %s at some point, and for it not "
922 "to have been rendered obsolete.\n"),
923 improvement_name_translation
924 (preq->source.value.building));
925 } else {
926 cat_snprintf(buf, bufsz,
927 /* TRANS: %s is a wonder */
928 _("Requires someone on your team to have "
929 "built %s at some point.\n"),
930 improvement_name_translation
931 (preq->source.value.building));
932 }
933 } else {
934 if (can_improvement_go_obsolete(preq->source.value.building)) {
935 cat_snprintf(buf, bufsz,
936 /* TRANS: %s is a wonder */
937 _("Prevented if someone on your team has ever "
938 "built %s, unless it would be obsolete.\n"),
939 improvement_name_translation
940 (preq->source.value.building));
941 } else {
942 cat_snprintf(buf, bufsz,
943 /* TRANS: %s is a wonder */
944 _("Prevented if someone on your team has ever "
945 "built %s.\n"),
946 improvement_name_translation
947 (preq->source.value.building));
948 }
949 }
950 } else {
951 /* Non-surviving requirement */
952 if (preq->present) {
953 if (can_improvement_go_obsolete(preq->source.value.building)) {
954 cat_snprintf(buf, bufsz,
955 /* TRANS: %s is a wonder */
956 _("Requires someone on your team to own %s, "
957 "and for it not to have been rendered "
958 "obsolete.\n"),
959 improvement_name_translation
960 (preq->source.value.building));
961 } else {
962 cat_snprintf(buf, bufsz,
963 /* TRANS: %s is a wonder */
964 _("Requires someone on your team to own %s.\n"),
965 improvement_name_translation
966 (preq->source.value.building));
967 }
968 } else {
969 if (can_improvement_go_obsolete(preq->source.value.building)) {
970 cat_snprintf(buf, bufsz,
971 /* TRANS: %s is a wonder */
972 _("Prevented if someone on your team owns %s, "
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 wonder */
979 _("Prevented if someone on your team owns %s.\n"),
980 improvement_name_translation
981 (preq->source.value.building));
982 }
983 }
984 }
985 return TRUE;
986 }
987 /* non-wonder team-ranged requirements not supported */
988 break;
989 case REQ_RANGE_PLAYER:
990 if (is_wonder(preq->source.value.building)) {
991 CATLSTR(buf, bufsz, prefix);
992 if (preq->survives) {
993 if (preq->present) {
994 if (can_improvement_go_obsolete(preq->source.value.building)) {
995 cat_snprintf(buf, bufsz,
996 /* TRANS: %s is a wonder */
997 _("Requires you to have built %s at some point, "
998 "and for it not to have been rendered "
999 "obsolete.\n"),
1000 improvement_name_translation
1001 (preq->source.value.building));
1002 } else {
1003 cat_snprintf(buf, bufsz,
1004 /* TRANS: %s is a wonder */
1005 _("Requires you to have built %s at some point.\n"),
1006 improvement_name_translation
1007 (preq->source.value.building));
1008 }
1009 } else {
1010 if (can_improvement_go_obsolete(preq->source.value.building)) {
1011 cat_snprintf(buf, bufsz,
1012 /* TRANS: %s is a wonder */
1013 _("Prevented if you have ever built %s, "
1014 "unless it would be obsolete.\n"),
1015 improvement_name_translation
1016 (preq->source.value.building));
1017 } else {
1018 cat_snprintf(buf, bufsz,
1019 /* TRANS: %s is a wonder */
1020 _("Prevented if you have ever built %s.\n"),
1021 improvement_name_translation
1022 (preq->source.value.building));
1023 }
1024 }
1025 } else {
1026 /* Non-surviving requirement */
1027 if (preq->present) {
1028 if (can_improvement_go_obsolete(preq->source.value.building)) {
1029 cat_snprintf(buf, bufsz,
1030 /* TRANS: %s is a wonder */
1031 _("Requires you to own %s, which must not "
1032 "be obsolete.\n"),
1033 improvement_name_translation
1034 (preq->source.value.building));
1035 } else {
1036 cat_snprintf(buf, bufsz,
1037 /* TRANS: %s is a wonder */
1038 _("Requires you to own %s.\n"),
1039 improvement_name_translation
1040 (preq->source.value.building));
1041 }
1042 } else {
1043 if (can_improvement_go_obsolete(preq->source.value.building)) {
1044 cat_snprintf(buf, bufsz,
1045 /* TRANS: %s is a wonder */
1046 _("Prevented if you own %s, unless it is "
1047 "obsolete.\n"),
1048 improvement_name_translation
1049 (preq->source.value.building));
1050 } else {
1051 cat_snprintf(buf, bufsz,
1052 /* TRANS: %s is a wonder */
1053 _("Prevented if you own %s.\n"),
1054 improvement_name_translation
1055 (preq->source.value.building));
1056 }
1057 }
1058 }
1059 return TRUE;
1060 }
1061 /* non-wonder player-ranged requirements not supported */
1062 break;
1063 case REQ_RANGE_CONTINENT:
1064 if (is_wonder(preq->source.value.building)) {
1065 CATLSTR(buf, bufsz, prefix);
1066 if (preq->present) {
1067 if (can_improvement_go_obsolete(preq->source.value.building)) {
1068 cat_snprintf(buf, bufsz,
1069 /* TRANS: %s is a wonder */
1070 _("Requires %s in one of your cities on the same "
1071 "continent, and not yet obsolete.\n"),
1072 improvement_name_translation
1073 (preq->source.value.building));
1074 } else {
1075 cat_snprintf(buf, bufsz,
1076 /* TRANS: %s is a wonder */
1077 _("Requires %s in one of your cities on the same "
1078 "continent.\n"),
1079 improvement_name_translation
1080 (preq->source.value.building));
1081 }
1082 } else {
1083 if (can_improvement_go_obsolete(preq->source.value.building)) {
1084 cat_snprintf(buf, bufsz,
1085 /* TRANS: %s is a wonder */
1086 _("Prevented if %s is in one of your cities on the "
1087 "same continent, unless it is obsolete.\n"),
1088 improvement_name_translation
1089 (preq->source.value.building));
1090 } else {
1091 cat_snprintf(buf, bufsz,
1092 /* TRANS: %s is a wonder */
1093 _("Prevented if %s is in one of your cities on the "
1094 "same continent.\n"),
1095 improvement_name_translation
1096 (preq->source.value.building));
1097 }
1098 }
1099 return TRUE;
1100 }
1101 /* surviving or non-wonder continent-ranged requirements not supported */
1102 break;
1103 case REQ_RANGE_TRADEROUTE:
1104 CATLSTR(buf, bufsz, prefix);
1105 if (preq->present) {
1106 if (can_improvement_go_obsolete(preq->source.value.building)) {
1107 /* Should only apply to wonders */
1108 cat_snprintf(buf, bufsz,
1109 /* TRANS: %s is a building or wonder */
1110 _("Requires %s in the city or a trade partner "
1111 "(and not yet obsolete).\n"),
1112 improvement_name_translation
1113 (preq->source.value.building));
1114 } else {
1115 cat_snprintf(buf, bufsz,
1116 /* TRANS: %s is a building or wonder */
1117 _("Requires %s in the city or a trade partner.\n"),
1118 improvement_name_translation
1119 (preq->source.value.building));
1120 }
1121 } else {
1122 if (can_improvement_go_obsolete(preq->source.value.building)) {
1123 /* Should only apply to wonders */
1124 cat_snprintf(buf, bufsz,
1125 /* TRANS: %s is a building or wonder */
1126 _("Prevented by %s in the city or a trade partner "
1127 "(unless it is obsolete).\n"),
1128 improvement_name_translation
1129 (preq->source.value.building));
1130 } else {
1131 cat_snprintf(buf, bufsz,
1132 /* TRANS: %s is a building or wonder */
1133 _("Prevented by %s in the city or a trade partner.\n"),
1134 improvement_name_translation
1135 (preq->source.value.building));
1136 }
1137 }
1138 return TRUE;
1139 case REQ_RANGE_CITY:
1140 CATLSTR(buf, bufsz, prefix);
1141 if (preq->present) {
1142 if (can_improvement_go_obsolete(preq->source.value.building)) {
1143 /* Should only apply to wonders */
1144 cat_snprintf(buf, bufsz,
1145 /* TRANS: %s is a building or wonder */
1146 _("Requires %s in the city (and not yet obsolete).\n"),
1147 improvement_name_translation
1148 (preq->source.value.building));
1149 } else {
1150 cat_snprintf(buf, bufsz,
1151 /* TRANS: %s is a building or wonder */
1152 _("Requires %s in the city.\n"),
1153 improvement_name_translation
1154 (preq->source.value.building));
1155 }
1156 } else {
1157 if (can_improvement_go_obsolete(preq->source.value.building)) {
1158 /* Should only apply to wonders */
1159 cat_snprintf(buf, bufsz,
1160 /* TRANS: %s is a building or wonder */
1161 _("Prevented by %s in the city (unless it is "
1162 "obsolete).\n"),
1163 improvement_name_translation
1164 (preq->source.value.building));
1165 } else {
1166 cat_snprintf(buf, bufsz,
1167 /* TRANS: %s is a building or wonder */
1168 _("Prevented by %s in the city.\n"),
1169 improvement_name_translation
1170 (preq->source.value.building));
1171 }
1172 }
1173 return TRUE;
1174 case REQ_RANGE_LOCAL:
1175 CATLSTR(buf, bufsz, prefix);
1176 if (preq->present) {
1177 cat_snprintf(buf, bufsz,
1178 _("Only applies to \"%s\" buildings.\n"),
1179 improvement_name_translation
1180 (preq->source.value.building));
1181 } else {
1182 cat_snprintf(buf, bufsz,
1183 _("Does not apply to \"%s\" buildings.\n"),
1184 improvement_name_translation
1185 (preq->source.value.building));
1186 }
1187 return TRUE;
1188 case REQ_RANGE_CADJACENT:
1189 case REQ_RANGE_ADJACENT:
1190 case REQ_RANGE_COUNT:
1191 /* Not supported. */
1192 break;
1193 }
1194 break;
1195
1196 case VUT_EXTRA:
1197 switch (preq->range) {
1198 case REQ_RANGE_LOCAL:
1199 CATLSTR(buf, bufsz, prefix);
1200 if (preq->present) {
1201 cat_snprintf(buf, bufsz,
1202 Q_("?extra:Requires %s on the tile.\n"),
1203 extra_name_translation(preq->source.value.extra));
1204 } else {
1205 cat_snprintf(buf, bufsz,
1206 Q_("?extra:Prevented by %s on the tile.\n"),
1207 extra_name_translation(preq->source.value.extra));
1208 }
1209 return TRUE;
1210 case REQ_RANGE_CADJACENT:
1211 CATLSTR(buf, bufsz, prefix);
1212 if (preq->present) {
1213 cat_snprintf(buf, bufsz,
1214 Q_("?extra:Requires %s on the tile or a cardinally "
1215 "adjacent tile.\n"),
1216 extra_name_translation(preq->source.value.extra));
1217 } else {
1218 cat_snprintf(buf, bufsz,
1219 Q_("?extra:Prevented by %s on the tile or any cardinally "
1220 "adjacent tile.\n"),
1221 extra_name_translation(preq->source.value.extra));
1222 }
1223 return TRUE;
1224 case REQ_RANGE_ADJACENT:
1225 CATLSTR(buf, bufsz, prefix);
1226 if (preq->present) {
1227 cat_snprintf(buf, bufsz,
1228 Q_("?extra:Requires %s on the tile or an adjacent "
1229 "tile.\n"),
1230 extra_name_translation(preq->source.value.extra));
1231 } else {
1232 cat_snprintf(buf, bufsz,
1233 Q_("?extra:Prevented by %s on the tile or any adjacent "
1234 "tile.\n"),
1235 extra_name_translation(preq->source.value.extra));
1236 }
1237 return TRUE;
1238 case REQ_RANGE_CITY:
1239 CATLSTR(buf, bufsz, prefix);
1240 if (preq->present) {
1241 cat_snprintf(buf, bufsz,
1242 Q_("?extra:Requires %s on a tile within the city "
1243 "radius.\n"),
1244 extra_name_translation(preq->source.value.extra));
1245 } else {
1246 cat_snprintf(buf, bufsz,
1247 Q_("?extra:Prevented by %s on any tile within the city "
1248 "radius.\n"),
1249 extra_name_translation(preq->source.value.extra));
1250 }
1251 return TRUE;
1252 case REQ_RANGE_TRADEROUTE:
1253 CATLSTR(buf, bufsz, prefix);
1254 if (preq->present) {
1255 cat_snprintf(buf, bufsz,
1256 Q_("?extra:Requires %s on a tile within the city "
1257 "radius, or the city radius of a trade partner.\n"),
1258 extra_name_translation(preq->source.value.extra));
1259 } else {
1260 cat_snprintf(buf, bufsz,
1261 Q_("?extra:Prevented by %s on any tile within the city "
1262 "radius or the city radius of a trade partner.\n"),
1263 extra_name_translation(preq->source.value.extra));
1264 }
1265 return TRUE;
1266 case REQ_RANGE_CONTINENT:
1267 case REQ_RANGE_PLAYER:
1268 case REQ_RANGE_TEAM:
1269 case REQ_RANGE_ALLIANCE:
1270 case REQ_RANGE_WORLD:
1271 case REQ_RANGE_COUNT:
1272 /* Not supported. */
1273 break;
1274 }
1275 break;
1276
1277 case VUT_TERRAIN:
1278 switch (preq->range) {
1279 case REQ_RANGE_LOCAL:
1280 CATLSTR(buf, bufsz, prefix);
1281 if (preq->present) {
1282 cat_snprintf(buf, bufsz, Q_("?terrain:Requires %s on the tile.\n"),
1283 terrain_name_translation(preq->source.value.terrain));
1284 } else {
1285 cat_snprintf(buf, bufsz, Q_("?terrain:Prevented by %s on the tile.\n"),
1286 terrain_name_translation(preq->source.value.terrain));
1287 }
1288 return TRUE;
1289 case REQ_RANGE_CADJACENT:
1290 CATLSTR(buf, bufsz, prefix);
1291 if (preq->present) {
1292 cat_snprintf(buf, bufsz,
1293 Q_("?terrain:Requires %s on the tile or a cardinally "
1294 "adjacent tile.\n"),
1295 terrain_name_translation(preq->source.value.terrain));
1296 } else {
1297 cat_snprintf(buf, bufsz,
1298 Q_("?terrain:Prevented by %s on the tile or any "
1299 "cardinally adjacent tile.\n"),
1300 terrain_name_translation(preq->source.value.terrain));
1301 }
1302 return TRUE;
1303 case REQ_RANGE_ADJACENT:
1304 CATLSTR(buf, bufsz, prefix);
1305 if (preq->present) {
1306 cat_snprintf(buf, bufsz,
1307 Q_("?terrain:Requires %s on the tile or an adjacent "
1308 "tile.\n"),
1309 terrain_name_translation(preq->source.value.terrain));
1310 } else {
1311 cat_snprintf(buf, bufsz,
1312 Q_("?terrain:Prevented by %s on the tile or any "
1313 "adjacent tile.\n"),
1314 terrain_name_translation(preq->source.value.terrain));
1315 }
1316 return TRUE;
1317 case REQ_RANGE_CITY:
1318 CATLSTR(buf, bufsz, prefix);
1319 if (preq->present) {
1320 cat_snprintf(buf, bufsz,
1321 Q_("?terrain:Requires %s on a tile within the city "
1322 "radius.\n"),
1323 terrain_name_translation(preq->source.value.terrain));
1324 } else {
1325 cat_snprintf(buf, bufsz,
1326 Q_("?terrain:Prevented by %s on any tile within the city "
1327 "radius.\n"),
1328 terrain_name_translation(preq->source.value.terrain));
1329 }
1330 return TRUE;
1331 case REQ_RANGE_TRADEROUTE:
1332 CATLSTR(buf, bufsz, prefix);
1333 if (preq->present) {
1334 cat_snprintf(buf, bufsz,
1335 Q_("?terrain:Requires %s on a tile within the city "
1336 "radius, or the city radius of a trade partner.\n"),
1337 terrain_name_translation(preq->source.value.terrain));
1338 } else {
1339 cat_snprintf(buf, bufsz,
1340 Q_("?terrain:Prevented by %s on any tile within the city "
1341 "radius or the city radius of a trade partner.\n"),
1342 terrain_name_translation(preq->source.value.terrain));
1343 }
1344 return TRUE;
1345 case REQ_RANGE_CONTINENT:
1346 case REQ_RANGE_PLAYER:
1347 case REQ_RANGE_TEAM:
1348 case REQ_RANGE_ALLIANCE:
1349 case REQ_RANGE_WORLD:
1350 case REQ_RANGE_COUNT:
1351 /* Not supported. */
1352 break;
1353 }
1354 break;
1355
1356 case VUT_RESOURCE:
1357 switch (preq->range) {
1358 case REQ_RANGE_LOCAL:
1359 CATLSTR(buf, bufsz, prefix);
1360 if (preq->present) {
1361 cat_snprintf(buf, bufsz,
1362 Q_("?resource:Requires %s on the tile.\n"),
1363 resource_name_translation(preq->source.value.resource));
1364 } else {
1365 cat_snprintf(buf, bufsz,
1366 Q_("?resource:Prevented by %s on the tile.\n"),
1367 resource_name_translation(preq->source.value.resource));
1368 }
1369 return TRUE;
1370 case REQ_RANGE_CADJACENT:
1371 CATLSTR(buf, bufsz, prefix);
1372 if (preq->present) {
1373 cat_snprintf(buf, bufsz,
1374 Q_("?resource:Requires %s on the tile or a cardinally "
1375 "adjacent tile.\n"),
1376 resource_name_translation(preq->source.value.resource));
1377 } else {
1378 cat_snprintf(buf, bufsz,
1379 Q_("?resource:Prevented by %s on the tile or any "
1380 "cardinally adjacent tile.\n"),
1381 resource_name_translation(preq->source.value.resource));
1382 }
1383 return TRUE;
1384 case REQ_RANGE_ADJACENT:
1385 CATLSTR(buf, bufsz, prefix);
1386 if (preq->present) {
1387 cat_snprintf(buf, bufsz,
1388 Q_("?resource:Requires %s on the tile or an adjacent "
1389 "tile.\n"),
1390 resource_name_translation(preq->source.value.resource));
1391 } else {
1392 cat_snprintf(buf, bufsz,
1393 Q_("?resource:Prevented by %s on the tile or any "
1394 "adjacent tile.\n"),
1395 resource_name_translation(preq->source.value.resource));
1396 }
1397 return TRUE;
1398 case REQ_RANGE_CITY:
1399 CATLSTR(buf, bufsz, prefix);
1400 if (preq->present) {
1401 cat_snprintf(buf, bufsz,
1402 Q_("?resource:Requires %s on a tile within the "
1403 "city radius.\n"),
1404 resource_name_translation(preq->source.value.resource));
1405 } else {
1406 cat_snprintf(buf, bufsz,
1407 Q_("?resource:Prevented by %s on any tile within the "
1408 "city radius.\n"),
1409 resource_name_translation(preq->source.value.resource));
1410 }
1411 return TRUE;
1412 case REQ_RANGE_TRADEROUTE:
1413 CATLSTR(buf, bufsz, prefix);
1414 if (preq->present) {
1415 cat_snprintf(buf, bufsz,
1416 Q_("?resource:Requires %s on a tile within the "
1417 "city radius or the city radius of a trade partner.\n"),
1418 resource_name_translation(preq->source.value.resource));
1419 } else {
1420 cat_snprintf(buf, bufsz,
1421 Q_("?resource:Prevented by %s on any tile within the "
1422 "city radius or the city radius of a trade partner.\n"),
1423 resource_name_translation(preq->source.value.resource));
1424 }
1425 return TRUE;
1426 case REQ_RANGE_CONTINENT:
1427 case REQ_RANGE_PLAYER:
1428 case REQ_RANGE_TEAM:
1429 case REQ_RANGE_ALLIANCE:
1430 case REQ_RANGE_WORLD:
1431 case REQ_RANGE_COUNT:
1432 /* Not supported. */
1433 break;
1434 }
1435 break;
1436
1437 case VUT_NATION:
1438 switch (preq->range) {
1439 case REQ_RANGE_PLAYER:
1440 CATLSTR(buf, bufsz, prefix);
1441 if (preq->present) {
1442 cat_snprintf(buf, bufsz,
1443 /* TRANS: "... playing as the Swedes." */
1444 _("Requires that you are playing as the %s.\n"),
1445 nation_plural_translation(preq->source.value.nation));
1446 } else {
1447 cat_snprintf(buf, bufsz,
1448 /* TRANS: "... playing as the Turks." */
1449 _("Requires that you are not playing as the %s.\n"),
1450 nation_plural_translation(preq->source.value.nation));
1451 }
1452 return TRUE;
1453 case REQ_RANGE_TEAM:
1454 CATLSTR(buf, bufsz, prefix);
1455 if (preq->present) {
1456 cat_snprintf(buf, bufsz,
1457 /* TRANS: "... same team as the Indonesians." */
1458 _("Requires that you are on the same team as "
1459 "the %s.\n"),
1460 nation_plural_translation(preq->source.value.nation));
1461 } else {
1462 cat_snprintf(buf, bufsz,
1463 /* TRANS: "... same team as the Greeks." */
1464 _("Requires that you are not on the same team as "
1465 "the %s.\n"),
1466 nation_plural_translation(preq->source.value.nation));
1467 }
1468 return TRUE;
1469 case REQ_RANGE_ALLIANCE:
1470 CATLSTR(buf, bufsz, prefix);
1471 if (preq->present) {
1472 cat_snprintf(buf, bufsz,
1473 /* TRANS: "... allied with the Koreans." */
1474 _("Requires that you are allied with the %s.\n"),
1475 nation_plural_translation(preq->source.value.nation));
1476 } else {
1477 cat_snprintf(buf, bufsz,
1478 /* TRANS: "... allied with the Danes." */
1479 _("Requires that you are not allied with the %s.\n"),
1480 nation_plural_translation(preq->source.value.nation));
1481 }
1482 return TRUE;
1483 case REQ_RANGE_WORLD:
1484 CATLSTR(buf, bufsz, prefix);
1485 if (preq->survives) {
1486 if (preq->present) {
1487 cat_snprintf(buf, bufsz,
1488 /* TRANS: "Requires the Apaches to have ..." */
1489 _("Requires the %s to have been in the game.\n"),
1490 nation_plural_translation(preq->source.value.nation));
1491 } else {
1492 cat_snprintf(buf, bufsz,
1493 /* TRANS: "Requires the Celts never to have ..." */
1494 _("Requires the %s never to have been in the "
1495 "game.\n"),
1496 nation_plural_translation(preq->source.value.nation));
1497 }
1498 } else {
1499 if (preq->present) {
1500 cat_snprintf(buf, bufsz,
1501 /* TRANS: "Requires the Belgians in the game." */
1502 _("Requires the %s in the game.\n"),
1503 nation_plural_translation(preq->source.value.nation));
1504 } else {
1505 cat_snprintf(buf, bufsz,
1506 /* TRANS: "Requires that the Russians are not ... */
1507 _("Requires that the %s are not in the game.\n"),
1508 nation_plural_translation(preq->source.value.nation));
1509 }
1510 }
1511 return TRUE;
1512 case REQ_RANGE_LOCAL:
1513 case REQ_RANGE_CADJACENT:
1514 case REQ_RANGE_ADJACENT:
1515 case REQ_RANGE_CITY:
1516 case REQ_RANGE_TRADEROUTE:
1517 case REQ_RANGE_CONTINENT:
1518 case REQ_RANGE_COUNT:
1519 /* Not supported. */
1520 break;
1521 }
1522 break;
1523
1524 case VUT_NATIONGROUP:
1525 switch (preq->range) {
1526 case REQ_RANGE_PLAYER:
1527 CATLSTR(buf, bufsz, prefix);
1528 if (preq->present) {
1529 cat_snprintf(buf, bufsz,
1530 /* TRANS: nation group: "... playing African nation." */
1531 _("Requires that you are playing %s nation.\n"),
1532 nation_group_name_translation(preq->source.value.nationgroup));
1533 } else {
1534 cat_snprintf(buf, bufsz,
1535 /* TRANS: nation group: "... playing Imaginary nation." */
1536 _("Prevented if you are playing %s nation.\n"),
1537 nation_group_name_translation(preq->source.value.nationgroup));
1538 }
1539 return TRUE;
1540 case REQ_RANGE_TEAM:
1541 CATLSTR(buf, bufsz, prefix);
1542 if (preq->present) {
1543 cat_snprintf(buf, bufsz,
1544 /* TRANS: nation group: "Requires Medieval nation ..." */
1545 _("Requires %s nation on your team.\n"),
1546 nation_group_name_translation(preq->source.value.nationgroup));
1547 } else {
1548 cat_snprintf(buf, bufsz,
1549 /* TRANS: nation group: "Prevented by Medieval nation ..." */
1550 _("Prevented by %s nation on your team.\n"),
1551 nation_group_name_translation(preq->source.value.nationgroup));
1552 }
1553 return TRUE;
1554 case REQ_RANGE_ALLIANCE:
1555 CATLSTR(buf, bufsz, prefix);
1556 if (preq->present) {
1557 cat_snprintf(buf, bufsz,
1558 /* TRANS: nation group: "Requires Modern nation ..." */
1559 _("Requires %s nation in alliance with you.\n"),
1560 nation_group_name_translation(preq->source.value.nationgroup));
1561 } else {
1562 cat_snprintf(buf, bufsz,
1563 /* TRANS: nation group: "Prevented by Modern nation ..." */
1564 _("Prevented if %s nation is in alliance with you.\n"),
1565 nation_group_name_translation(preq->source.value.nationgroup));
1566 }
1567 return TRUE;
1568 case REQ_RANGE_WORLD:
1569 CATLSTR(buf, bufsz, prefix);
1570 if (preq->present) {
1571 cat_snprintf(buf, bufsz,
1572 /* TRANS: nation group: "Requires Asian nation ..." */
1573 _("Requires %s nation in the game.\n"),
1574 nation_group_name_translation(preq->source.value.nationgroup));
1575 } else {
1576 cat_snprintf(buf, bufsz,
1577 /* TRANS: nation group: "Prevented by Asian nation ..." */
1578 _("Prevented by %s nation in the game.\n"),
1579 nation_group_name_translation(preq->source.value.nationgroup));
1580 }
1581 return TRUE;
1582 case REQ_RANGE_LOCAL:
1583 case REQ_RANGE_CADJACENT:
1584 case REQ_RANGE_ADJACENT:
1585 case REQ_RANGE_CITY:
1586 case REQ_RANGE_TRADEROUTE:
1587 case REQ_RANGE_CONTINENT:
1588 case REQ_RANGE_COUNT:
1589 /* Not supported. */
1590 break;
1591 }
1592 break;
1593
1594 case VUT_STYLE:
1595 if (preq->range != REQ_RANGE_PLAYER) {
1596 break;
1597 }
1598 CATLSTR(buf, bufsz, prefix);
1599 if (preq->present) {
1600 cat_snprintf(buf, bufsz,
1601 /* TRANS: "Requires that you are playing Asian style
1602 * nation." */
1603 _("Requires that you are playing %s style nation.\n"),
1604 style_name_translation(preq->source.value.style));
1605 } else {
1606 cat_snprintf(buf, bufsz,
1607 /* TRANS: "Requires that you are not playing Classical
1608 * style nation." */
1609 _("Requires that you are not playing %s style nation.\n"),
1610 style_name_translation(preq->source.value.style));
1611 }
1612 return TRUE;
1613
1614 case VUT_NATIONALITY:
1615 switch (preq->range) {
1616 case REQ_RANGE_TRADEROUTE:
1617 CATLSTR(buf, bufsz, prefix);
1618 if (preq->present) {
1619 cat_snprintf(buf, bufsz,
1620 /* TRANS: "Requires at least one Barbarian citizen ..." */
1621 _("Requires at least one %s citizen in the city or a "
1622 "trade partner.\n"),
1623 nation_adjective_translation(preq->source.value.nationality));
1624 } else {
1625 cat_snprintf(buf, bufsz,
1626 /* TRANS: "... no Pirate citizens ..." */
1627 _("Requires that there are no %s citizens in "
1628 "the city or any trade partners.\n"),
1629 nation_adjective_translation(preq->source.value.nationality));
1630 }
1631 return TRUE;
1632 case REQ_RANGE_CITY:
1633 CATLSTR(buf, bufsz, prefix);
1634 if (preq->present) {
1635 cat_snprintf(buf, bufsz,
1636 /* TRANS: "Requires at least one Barbarian citizen ..." */
1637 _("Requires at least one %s citizen in the city.\n"),
1638 nation_adjective_translation(preq->source.value.nationality));
1639 } else {
1640 cat_snprintf(buf, bufsz,
1641 /* TRANS: "... no Pirate citizens ..." */
1642 _("Requires that there are no %s citizens in "
1643 "the city.\n"),
1644 nation_adjective_translation(preq->source.value.nationality));
1645 }
1646 return TRUE;
1647 case REQ_RANGE_WORLD:
1648 case REQ_RANGE_ALLIANCE:
1649 case REQ_RANGE_TEAM:
1650 case REQ_RANGE_PLAYER:
1651 case REQ_RANGE_LOCAL:
1652 case REQ_RANGE_CADJACENT:
1653 case REQ_RANGE_ADJACENT:
1654 case REQ_RANGE_CONTINENT:
1655 case REQ_RANGE_COUNT:
1656 /* Not supported. */
1657 break;
1658 }
1659 break;
1660
1661 case VUT_DIPLREL:
1662 switch (preq->range) {
1663 case REQ_RANGE_PLAYER:
1664 CATLSTR(buf, bufsz, prefix);
1665 if (preq->present) {
1666 cat_snprintf(buf, bufsz,
1667 /* TRANS: in this and following strings, '%s' can be one
1668 * of a wide range of relationships; e.g., 'Peace',
1669 * 'Never met', 'Is foreign', 'Hosts embassy',
1670 * 'Provided Casus Belli' */
1671 _("Requires that you have the relationship '%s' with at "
1672 "least one other living player.\n"),
1673 diplrel_name_translation(preq->source.value.diplrel));
1674 } else {
1675 cat_snprintf(buf, bufsz,
1676 _("Requires that you do not have the relationship '%s' "
1677 "with any living player.\n"),
1678 diplrel_name_translation(preq->source.value.diplrel));
1679 }
1680 return TRUE;
1681 case REQ_RANGE_TEAM:
1682 CATLSTR(buf, bufsz, prefix);
1683 if (preq->present) {
1684 cat_snprintf(buf, bufsz,
1685 _("Requires that somebody on your team has the "
1686 "relationship '%s' with at least one other living "
1687 "player.\n"),
1688 diplrel_name_translation(preq->source.value.diplrel));
1689 } else {
1690 cat_snprintf(buf, bufsz,
1691 _("Requires that nobody on your team has the "
1692 "relationship '%s' with any living player.\n"),
1693 diplrel_name_translation(preq->source.value.diplrel));
1694 }
1695 return TRUE;
1696 case REQ_RANGE_ALLIANCE:
1697 CATLSTR(buf, bufsz, prefix);
1698 if (preq->present) {
1699 cat_snprintf(buf, bufsz,
1700 _("Requires that somebody in your alliance has the "
1701 "relationship '%s' with at least one other living "
1702 "player.\n"),
1703 diplrel_name_translation(preq->source.value.diplrel));
1704 } else {
1705 cat_snprintf(buf, bufsz,
1706 _("Requires that nobody in your alliance has the "
1707 "relationship '%s' with any living player.\n"),
1708 diplrel_name_translation(preq->source.value.diplrel));
1709 }
1710 return TRUE;
1711 case REQ_RANGE_WORLD:
1712 CATLSTR(buf, bufsz, prefix);
1713 if (preq->present) {
1714 cat_snprintf(buf, bufsz,
1715 _("Requires the relationship '%s' between two living "
1716 "players.\n"),
1717 diplrel_name_translation(preq->source.value.diplrel));
1718 } else {
1719 cat_snprintf(buf, bufsz,
1720 _("Requires that no two living players have the "
1721 "relationship '%s'.\n"),
1722 diplrel_name_translation(preq->source.value.diplrel));
1723 }
1724 return TRUE;
1725 case REQ_RANGE_LOCAL:
1726 CATLSTR(buf, bufsz, prefix);
1727 if (preq->present) {
1728 cat_snprintf(buf, bufsz,
1729 _("Requires that you have the relationship '%s' with the "
1730 "other player.\n"),
1731 diplrel_name_translation(preq->source.value.diplrel));
1732 } else {
1733 cat_snprintf(buf, bufsz,
1734 _("Requires that you do not have the relationship '%s' "
1735 "with the other player.\n"),
1736 diplrel_name_translation(preq->source.value.diplrel));
1737 }
1738 return TRUE;
1739 case REQ_RANGE_CADJACENT:
1740 case REQ_RANGE_ADJACENT:
1741 case REQ_RANGE_CITY:
1742 case REQ_RANGE_TRADEROUTE:
1743 case REQ_RANGE_CONTINENT:
1744 case REQ_RANGE_COUNT:
1745 /* Not supported. */
1746 break;
1747 }
1748 break;
1749
1750 case VUT_UTYPE:
1751 switch (preq->range) {
1752 case REQ_RANGE_LOCAL:
1753 CATLSTR(buf, bufsz, prefix);
1754 if (preq->present) {
1755 /* TRANS: %s is a single kind of unit (e.g., "Settlers"). */
1756 cat_snprintf(buf, bufsz, Q_("?unit:Requires %s.\n"),
1757 utype_name_translation(preq->source.value.utype));
1758 } else {
1759 /* TRANS: %s is a single kind of unit (e.g., "Settlers"). */
1760 cat_snprintf(buf, bufsz, Q_("?unit:Does not apply to %s.\n"),
1761 utype_name_translation(preq->source.value.utype));
1762 }
1763 return TRUE;
1764 case REQ_RANGE_CADJACENT:
1765 case REQ_RANGE_ADJACENT:
1766 case REQ_RANGE_CITY:
1767 case REQ_RANGE_TRADEROUTE:
1768 case REQ_RANGE_CONTINENT:
1769 case REQ_RANGE_PLAYER:
1770 case REQ_RANGE_TEAM:
1771 case REQ_RANGE_ALLIANCE:
1772 case REQ_RANGE_WORLD:
1773 case REQ_RANGE_COUNT:
1774 /* Not supported. */
1775 break;
1776 }
1777 break;
1778
1779 case VUT_UTFLAG:
1780 switch (preq->range) {
1781 case REQ_RANGE_LOCAL:
1782 {
1783 struct astring astr = ASTRING_INIT;
1784
1785 /* Unit type flags mean nothing to users. Explicitly list the unit
1786 * types with those flags. */
1787 if (role_units_translations(&astr, preq->source.value.unitflag,
1788 TRUE)) {
1789 CATLSTR(buf, bufsz, prefix);
1790 if (preq->present) {
1791 /* TRANS: %s is a list of unit types separated by "or". */
1792 cat_snprintf(buf, bufsz, Q_("?ulist:Requires %s.\n"),
1793 astr_str(&astr));
1794 } else {
1795 /* TRANS: %s is a list of unit types separated by "or". */
1796 cat_snprintf(buf, bufsz, Q_("?ulist:Does not apply to %s.\n"),
1797 astr_str(&astr));
1798 }
1799 astr_free(&astr);
1800 return TRUE;
1801 }
1802 }
1803 break;
1804 case REQ_RANGE_CADJACENT:
1805 case REQ_RANGE_ADJACENT:
1806 case REQ_RANGE_CITY:
1807 case REQ_RANGE_TRADEROUTE:
1808 case REQ_RANGE_CONTINENT:
1809 case REQ_RANGE_PLAYER:
1810 case REQ_RANGE_TEAM:
1811 case REQ_RANGE_ALLIANCE:
1812 case REQ_RANGE_WORLD:
1813 case REQ_RANGE_COUNT:
1814 /* Not supported. */
1815 break;
1816 }
1817 break;
1818
1819 case VUT_UCLASS:
1820 switch (preq->range) {
1821 case REQ_RANGE_LOCAL:
1822 CATLSTR(buf, bufsz, prefix);
1823 if (preq->present) {
1824 /* TRANS: %s is a single unit class (e.g., "Air"). */
1825 cat_snprintf(buf, bufsz, Q_("?uclass:Requires %s units.\n"),
1826 uclass_name_translation(preq->source.value.uclass));
1827 } else {
1828 /* TRANS: %s is a single unit class (e.g., "Air"). */
1829 cat_snprintf(buf, bufsz, Q_("?uclass:Does not apply to %s units.\n"),
1830 uclass_name_translation(preq->source.value.uclass));
1831 }
1832 return TRUE;
1833 case REQ_RANGE_CADJACENT:
1834 case REQ_RANGE_ADJACENT:
1835 case REQ_RANGE_CITY:
1836 case REQ_RANGE_TRADEROUTE:
1837 case REQ_RANGE_CONTINENT:
1838 case REQ_RANGE_PLAYER:
1839 case REQ_RANGE_TEAM:
1840 case REQ_RANGE_ALLIANCE:
1841 case REQ_RANGE_WORLD:
1842 case REQ_RANGE_COUNT:
1843 /* Not supported. */
1844 break;
1845 }
1846 break;
1847
1848 case VUT_UCFLAG:
1849 {
1850 const char *classes[uclass_count()];
1851 int i = 0;
1852 bool done = FALSE;
1853 struct astring list = ASTRING_INIT;
1854
1855 unit_class_iterate(uclass) {
1856 if (uclass_has_flag(uclass, preq->source.value.unitclassflag)) {
1857 classes[i++] = uclass_name_translation(uclass);
1858 }
1859 } unit_class_iterate_end;
1860 astr_build_or_list(&list, classes, i);
1861
1862 switch (preq->range) {
1863 case REQ_RANGE_LOCAL:
1864 CATLSTR(buf, bufsz, prefix);
1865 if (preq->present) {
1866 /* TRANS: %s is a list of unit classes separated by "or". */
1867 cat_snprintf(buf, bufsz, Q_("?uclasslist:Requires %s units.\n"),
1868 astr_str(&list));
1869 } else {
1870 /* TRANS: %s is a list of unit classes separated by "or". */
1871 cat_snprintf(buf, bufsz, Q_("?uclasslist:Does not apply to "
1872 "%s units.\n"),
1873 astr_str(&list));
1874 }
1875 done = TRUE;
1876 break;
1877 case REQ_RANGE_CADJACENT:
1878 case REQ_RANGE_ADJACENT:
1879 case REQ_RANGE_CITY:
1880 case REQ_RANGE_TRADEROUTE:
1881 case REQ_RANGE_CONTINENT:
1882 case REQ_RANGE_PLAYER:
1883 case REQ_RANGE_TEAM:
1884 case REQ_RANGE_ALLIANCE:
1885 case REQ_RANGE_WORLD:
1886 case REQ_RANGE_COUNT:
1887 /* Not supported. */
1888 break;
1889 }
1890 astr_free(&list);
1891 if (done) {
1892 return TRUE;
1893 }
1894 }
1895 break;
1896
1897 case VUT_UNITSTATE:
1898 {
1899 switch (preq->range) {
1900 case REQ_RANGE_LOCAL:
1901 switch (preq->source.value.unit_state) {
1902 case USP_TRANSPORTED:
1903 CATLSTR(buf, bufsz, prefix);
1904 if (preq->present) {
1905 cat_snprintf(buf, bufsz,
1906 _("Requires that the unit is transported.\n"));
1907 } else {
1908 cat_snprintf(buf, bufsz,
1909 _("Requires that the unit isn't transported.\n"));
1910 }
1911 return TRUE;
1912 case USP_LIVABLE_TILE:
1913 CATLSTR(buf, bufsz, prefix);
1914 if (preq->present) {
1915 cat_snprintf(buf, bufsz,
1916 _("Requires that the unit is on livable tile.\n"));
1917 } else {
1918 cat_snprintf(buf, bufsz,
1919 _("Requires that the unit isn't on livable tile.\n"));
1920 }
1921 return TRUE;
1922 case USP_COUNT:
1923 fc_assert_msg(preq->source.value.unit_state != USP_COUNT,
1924 "Invalid unit state property.");
1925 }
1926 break;
1927 case REQ_RANGE_CADJACENT:
1928 case REQ_RANGE_ADJACENT:
1929 case REQ_RANGE_CITY:
1930 case REQ_RANGE_TRADEROUTE:
1931 case REQ_RANGE_CONTINENT:
1932 case REQ_RANGE_PLAYER:
1933 case REQ_RANGE_TEAM:
1934 case REQ_RANGE_ALLIANCE:
1935 case REQ_RANGE_WORLD:
1936 case REQ_RANGE_COUNT:
1937 /* Not supported. */
1938 break;
1939 }
1940 }
1941 break;
1942
1943 case VUT_MINMOVES:
1944 {
1945 switch (preq->range) {
1946 case REQ_RANGE_LOCAL:
1947 CATLSTR(buf, bufsz, prefix);
1948 if (preq->present) {
1949 cat_snprintf(buf, bufsz,
1950 /* %s is numeric move points; it may have a
1951 * fractional part ("1 1/3 MP"). */
1952 _("Requires that the unit has at least %s MP left.\n"),
1953 move_points_text(preq->source.value.minmoves, TRUE));
1954 } else {
1955 cat_snprintf(buf, bufsz,
1956 /* %s is numeric move points; it may have a
1957 * fractional part ("1 1/3 MP"). */
1958 _("Requires that the unit has less than %s MP left.\n"),
1959 move_points_text(preq->source.value.minmoves, TRUE));
1960 }
1961 return TRUE;
1962 case REQ_RANGE_CADJACENT:
1963 case REQ_RANGE_ADJACENT:
1964 case REQ_RANGE_CITY:
1965 case REQ_RANGE_TRADEROUTE:
1966 case REQ_RANGE_CONTINENT:
1967 case REQ_RANGE_PLAYER:
1968 case REQ_RANGE_TEAM:
1969 case REQ_RANGE_ALLIANCE:
1970 case REQ_RANGE_WORLD:
1971 case REQ_RANGE_COUNT:
1972 /* Not supported. */
1973 break;
1974 }
1975 }
1976 break;
1977
1978 case VUT_MINVETERAN:
1979 if (preq->range != REQ_RANGE_LOCAL) {
1980 break;
1981 }
1982 CATLSTR(buf, bufsz, prefix);
1983 /* FIXME: this would be better with veteran level names, but that's
1984 * potentially unit type dependent. */
1985 if (preq->present) {
1986 cat_snprintf(buf, bufsz,
1987 PL_("Requires a unit with at least %d veteran level.\n",
1988 "Requires a unit with at least %d veteran levels.\n",
1989 preq->source.value.minveteran),
1990 preq->source.value.minveteran);
1991 } else {
1992 cat_snprintf(buf, bufsz,
1993 PL_("Requires a unit with fewer than %d veteran level.\n",
1994 "Requires a unit with fewer than %d veteran levels.\n",
1995 preq->source.value.minveteran),
1996 preq->source.value.minveteran);
1997 }
1998 return TRUE;
1999
2000 case VUT_MINHP:
2001 if (preq->range != REQ_RANGE_LOCAL) {
2002 break;
2003 }
2004
2005 CATLSTR(buf, bufsz, prefix);
2006 if (preq->present) {
2007 cat_snprintf(buf, bufsz,
2008 PL_("Requires a unit with at least %d hit point left.\n",
2009 "Requires a unit with at least %d hit points left.\n",
2010 preq->source.value.min_hit_points),
2011 preq->source.value.min_hit_points);
2012 } else {
2013 cat_snprintf(buf, bufsz,
2014 PL_("Requires a unit with fewer than %d hit point "
2015 "left.\n",
2016 "Requires a unit with fewer than %d hit points "
2017 "left.\n",
2018 preq->source.value.min_hit_points),
2019 preq->source.value.min_hit_points);
2020 }
2021 return TRUE;
2022
2023 case VUT_OTYPE:
2024 if (preq->range != REQ_RANGE_LOCAL) {
2025 break;
2026 }
2027 CATLSTR(buf, bufsz, prefix);
2028 if (preq->present) {
2029 /* TRANS: "Applies only to Food." */
2030 cat_snprintf(buf, bufsz, Q_("?output:Applies only to %s.\n"),
2031 get_output_name(preq->source.value.outputtype));
2032 } else {
2033 /* TRANS: "Does not apply to Food." */
2034 cat_snprintf(buf, bufsz, Q_("?output:Does not apply to %s.\n"),
2035 get_output_name(preq->source.value.outputtype));
2036 }
2037 return TRUE;
2038
2039 case VUT_SPECIALIST:
2040 if (preq->range != REQ_RANGE_LOCAL) {
2041 break;
2042 }
2043 CATLSTR(buf, bufsz, prefix);
2044 if (preq->present) {
2045 /* TRANS: "Applies only to Scientists." */
2046 cat_snprintf(buf, bufsz, Q_("?specialist:Applies only to %s.\n"),
2047 specialist_plural_translation(preq->source.value.specialist));
2048 } else {
2049 /* TRANS: "Does not apply to Scientists." */
2050 cat_snprintf(buf, bufsz, Q_("?specialist:Does not apply to %s.\n"),
2051 specialist_plural_translation(preq->source.value.specialist));
2052 }
2053 return TRUE;
2054
2055 case VUT_MINSIZE:
2056 switch (preq->range) {
2057 case REQ_RANGE_TRADEROUTE:
2058 CATLSTR(buf, bufsz, prefix);
2059 if (preq->present) {
2060 cat_snprintf(buf, bufsz,
2061 PL_("Requires a minimum city size of %d for this "
2062 "city or a trade partner.\n",
2063 "Requires a minimum city size of %d for this "
2064 "city or a trade partner.\n",
2065 preq->source.value.minsize),
2066 preq->source.value.minsize);
2067 } else {
2068 cat_snprintf(buf, bufsz,
2069 PL_("Requires the city size to be less than %d "
2070 "for this city and all trade partners.\n",
2071 "Requires the city size to be less than %d "
2072 "for this city and all trade partners.\n",
2073 preq->source.value.minsize),
2074 preq->source.value.minsize);
2075 }
2076 return TRUE;
2077 case REQ_RANGE_CITY:
2078 CATLSTR(buf, bufsz, prefix);
2079 if (preq->present) {
2080 cat_snprintf(buf, bufsz,
2081 PL_("Requires a minimum city size of %d.\n",
2082 "Requires a minimum city size of %d.\n",
2083 preq->source.value.minsize),
2084 preq->source.value.minsize);
2085 } else {
2086 cat_snprintf(buf, bufsz,
2087 PL_("Requires the city size to be less than %d.\n",
2088 "Requires the city size to be less than %d.\n",
2089 preq->source.value.minsize),
2090 preq->source.value.minsize);
2091 }
2092 return TRUE;
2093 case REQ_RANGE_LOCAL:
2094 case REQ_RANGE_CADJACENT:
2095 case REQ_RANGE_ADJACENT:
2096 case REQ_RANGE_CONTINENT:
2097 case REQ_RANGE_PLAYER:
2098 case REQ_RANGE_TEAM:
2099 case REQ_RANGE_ALLIANCE:
2100 case REQ_RANGE_WORLD:
2101 case REQ_RANGE_COUNT:
2102 /* Not supported. */
2103 break;
2104 }
2105 break;
2106
2107 case VUT_MINCULTURE:
2108 switch (preq->range) {
2109 case REQ_RANGE_CITY:
2110 CATLSTR(buf, bufsz, prefix);
2111 if (preq->present) {
2112 cat_snprintf(buf, bufsz,
2113 PL_("Requires a minimum culture of %d in the city.\n",
2114 "Requires a minimum culture of %d in the city.\n",
2115 preq->source.value.minculture),
2116 preq->source.value.minculture);
2117 } else {
2118 cat_snprintf(buf, bufsz,
2119 PL_("Requires the culture in the city to be less "
2120 "than %d.\n",
2121 "Requires the culture in the city to be less "
2122 "than %d.\n",
2123 preq->source.value.minculture),
2124 preq->source.value.minculture);
2125 }
2126 return TRUE;
2127 case REQ_RANGE_TRADEROUTE:
2128 CATLSTR(buf, bufsz, prefix);
2129 if (preq->present) {
2130 cat_snprintf(buf, bufsz,
2131 PL_("Requires a minimum culture of %d in this city or "
2132 "a trade partner.\n",
2133 "Requires a minimum culture of %d in this city or "
2134 "a trade partner.\n",
2135 preq->source.value.minculture),
2136 preq->source.value.minculture);
2137 } else {
2138 cat_snprintf(buf, bufsz,
2139 PL_("Requires the culture in this city and all trade "
2140 "partners to be less than %d.\n",
2141 "Requires the culture in this city and all trade "
2142 "partners to be less than %d.\n",
2143 preq->source.value.minculture),
2144 preq->source.value.minculture);
2145 }
2146 return TRUE;
2147 case REQ_RANGE_PLAYER:
2148 CATLSTR(buf, bufsz, prefix);
2149 if (preq->present) {
2150 cat_snprintf(buf, bufsz,
2151 PL_("Requires your nation to have culture "
2152 "of at least %d.\n",
2153 "Requires your nation to have culture "
2154 "of at least %d.\n",
2155 preq->source.value.minculture),
2156 preq->source.value.minculture);
2157 } else {
2158 cat_snprintf(buf, bufsz,
2159 PL_("Prevented if your nation has culture of "
2160 "%d or more.\n",
2161 "Prevented if your nation has culture of "
2162 "%d or more.\n",
2163 preq->source.value.minculture),
2164 preq->source.value.minculture);
2165 }
2166 return TRUE;
2167 case REQ_RANGE_TEAM:
2168 CATLSTR(buf, bufsz, prefix);
2169 if (preq->present) {
2170 cat_snprintf(buf, bufsz,
2171 PL_("Requires someone on your team to have culture of "
2172 "at least %d.\n",
2173 "Requires someone on your team to have culture of "
2174 "at least %d.\n",
2175 preq->source.value.minculture),
2176 preq->source.value.minculture);
2177 } else {
2178 cat_snprintf(buf, bufsz,
2179 PL_("Prevented if anyone on your team has culture of "
2180 "%d or more.\n",
2181 "Prevented if anyone on your team has culture of "
2182 "%d or more.\n",
2183 preq->source.value.minculture),
2184 preq->source.value.minculture);
2185 }
2186 return TRUE;
2187 case REQ_RANGE_ALLIANCE:
2188 CATLSTR(buf, bufsz, prefix);
2189 if (preq->present) {
2190 cat_snprintf(buf, bufsz,
2191 PL_("Requires someone in your current alliance to "
2192 "have culture of at least %d.\n",
2193 "Requires someone in your current alliance to "
2194 "have culture of at least %d.\n",
2195 preq->source.value.minculture),
2196 preq->source.value.minculture);
2197 } else {
2198 cat_snprintf(buf, bufsz,
2199 PL_("Prevented if anyone in your current alliance has "
2200 "culture of %d or more.\n",
2201 "Prevented if anyone in your current alliance has "
2202 "culture of %d or more.\n",
2203 preq->source.value.minculture),
2204 preq->source.value.minculture);
2205 }
2206 return TRUE;
2207 case REQ_RANGE_WORLD:
2208 CATLSTR(buf, bufsz, prefix);
2209 if (preq->present) {
2210 cat_snprintf(buf, bufsz,
2211 PL_("Requires that some player has culture of at "
2212 "least %d.\n",
2213 "Requires that some player has culture of at "
2214 "least %d.\n",
2215 preq->source.value.minculture),
2216 preq->source.value.minculture);
2217 } else {
2218 cat_snprintf(buf, bufsz,
2219 PL_("Requires that no player has culture of %d "
2220 "or more.\n",
2221 "Requires that no player has culture of %d "
2222 "or more.\n",
2223 preq->source.value.minculture),
2224 preq->source.value.minculture);
2225 }
2226 return TRUE;
2227 case REQ_RANGE_LOCAL:
2228 case REQ_RANGE_CADJACENT:
2229 case REQ_RANGE_ADJACENT:
2230 case REQ_RANGE_CONTINENT:
2231 case REQ_RANGE_COUNT:
2232 break;
2233 }
2234 break;
2235
2236 case VUT_MAXTILEUNITS:
2237 switch (preq->range) {
2238 case REQ_RANGE_LOCAL:
2239 CATLSTR(buf, bufsz, prefix);
2240 if (preq->present) {
2241 cat_snprintf(buf, bufsz,
2242 PL_("At most %d unit may be present on the tile.\n",
2243 "At most %d units may be present on the tile.\n",
2244 preq->source.value.max_tile_units),
2245 preq->source.value.max_tile_units);
2246 } else {
2247 cat_snprintf(buf, bufsz,
2248 PL_("There must be more than %d unit present on "
2249 "the tile.\n",
2250 "There must be more than %d units present on "
2251 "the tile.\n",
2252 preq->source.value.max_tile_units),
2253 preq->source.value.max_tile_units);
2254 }
2255 return TRUE;
2256 case REQ_RANGE_CADJACENT:
2257 CATLSTR(buf, bufsz, prefix);
2258 if (preq->present) {
2259 cat_snprintf(buf, bufsz,
2260 PL_("The tile or at least one cardinally adjacent tile "
2261 "must have %d unit or fewer.\n",
2262 "The tile or at least one cardinally adjacent tile "
2263 "must have %d units or fewer.\n",
2264 preq->source.value.max_tile_units),
2265 preq->source.value.max_tile_units);
2266 } else {
2267 cat_snprintf(buf, bufsz,
2268 PL_("The tile and all cardinally adjacent tiles must "
2269 "have more than %d unit each.\n",
2270 "The tile and all cardinally adjacent tiles must "
2271 "have more than %d units each.\n",
2272 preq->source.value.max_tile_units),
2273 preq->source.value.max_tile_units);
2274 }
2275 return TRUE;
2276 case REQ_RANGE_ADJACENT:
2277 CATLSTR(buf, bufsz, prefix);
2278 if (preq->present) {
2279 cat_snprintf(buf, bufsz,
2280 PL_("The tile or at least one adjacent tile must have "
2281 "%d unit or fewer.\n",
2282 "The tile or at least one adjacent tile must have "
2283 "%d units or fewer.\n",
2284 preq->source.value.max_tile_units),
2285 preq->source.value.max_tile_units);
2286 } else {
2287 cat_snprintf(buf, bufsz,
2288 PL_("The tile and all adjacent tiles must have more "
2289 "than %d unit each.\n",
2290 "The tile and all adjacent tiles must have more "
2291 "than %d units each.\n",
2292 preq->source.value.max_tile_units),
2293 preq->source.value.max_tile_units);
2294 }
2295 return TRUE;
2296 case REQ_RANGE_CITY:
2297 case REQ_RANGE_TRADEROUTE:
2298 case REQ_RANGE_CONTINENT:
2299 case REQ_RANGE_PLAYER:
2300 case REQ_RANGE_TEAM:
2301 case REQ_RANGE_ALLIANCE:
2302 case REQ_RANGE_WORLD:
2303 case REQ_RANGE_COUNT:
2304 /* Not supported. */
2305 break;
2306 }
2307 break;
2308
2309 case VUT_AI_LEVEL:
2310 if (preq->range != REQ_RANGE_PLAYER) {
2311 break;
2312 }
2313 CATLSTR(buf, bufsz, prefix);
2314 if (preq->present) {
2315 cat_snprintf(buf, bufsz,
2316 /* TRANS: AI level (e.g., "Handicapped") */
2317 _("Applies to %s AI players.\n"),
2318 ai_level_translated_name(preq->source.value.ai_level));
2319 } else {
2320 cat_snprintf(buf, bufsz,
2321 /* TRANS: AI level (e.g., "Cheating") */
2322 _("Does not apply to %s AI players.\n"),
2323 ai_level_translated_name(preq->source.value.ai_level));
2324 }
2325 return TRUE;
2326
2327 case VUT_TERRAINCLASS:
2328 switch (preq->range) {
2329 case REQ_RANGE_LOCAL:
2330 CATLSTR(buf, bufsz, prefix);
2331 if (preq->present) {
2332 cat_snprintf(buf, bufsz,
2333 /* TRANS: %s is a terrain class */
2334 Q_("?terrainclass:Requires %s terrain on the tile.\n"),
2335 terrain_class_name_translation
2336 (preq->source.value.terrainclass));
2337 } else {
2338 cat_snprintf(buf, bufsz,
2339 /* TRANS: %s is a terrain class */
2340 Q_("?terrainclass:Prevented by %s terrain on the tile.\n"),
2341 terrain_class_name_translation
2342 (preq->source.value.terrainclass));
2343 }
2344 return TRUE;
2345 case REQ_RANGE_CADJACENT:
2346 CATLSTR(buf, bufsz, prefix);
2347 if (preq->present) {
2348 cat_snprintf(buf, bufsz,
2349 /* TRANS: %s is a terrain class */
2350 Q_("?terrainclass:Requires %s terrain on the tile or a "
2351 "cardinally adjacent tile.\n"),
2352 terrain_class_name_translation
2353 (preq->source.value.terrainclass));
2354 } else {
2355 cat_snprintf(buf, bufsz,
2356 /* TRANS: %s is a terrain class */
2357 Q_("?terrainclass:Prevented by %s terrain on the tile or "
2358 "any cardinally adjacent tile.\n"),
2359 terrain_class_name_translation
2360 (preq->source.value.terrainclass));
2361 }
2362 return TRUE;
2363 case REQ_RANGE_ADJACENT:
2364 CATLSTR(buf, bufsz, prefix);
2365 if (preq->present) {
2366 cat_snprintf(buf, bufsz,
2367 /* TRANS: %s is a terrain class */
2368 Q_("?terrainclass:Requires %s terrain on the tile or an "
2369 "adjacent tile.\n"),
2370 terrain_class_name_translation
2371 (preq->source.value.terrainclass));
2372 } else {
2373 cat_snprintf(buf, bufsz,
2374 /* TRANS: %s is a terrain class */
2375 Q_("?terrainclass:Prevented by %s terrain on the tile or "
2376 "any adjacent tile.\n"),
2377 terrain_class_name_translation
2378 (preq->source.value.terrainclass));
2379 }
2380 return TRUE;
2381 case REQ_RANGE_CITY:
2382 CATLSTR(buf, bufsz, prefix);
2383 if (preq->present) {
2384 cat_snprintf(buf, bufsz,
2385 /* TRANS: %s is a terrain class */
2386 Q_("?terrainclass:Requires %s terrain on a tile within "
2387 "the city radius.\n"),
2388 terrain_class_name_translation
2389 (preq->source.value.terrainclass));
2390 } else {
2391 cat_snprintf(buf, bufsz,
2392 /* TRANS: %s is a terrain class */
2393 Q_("?terrainclass:Prevented by %s terrain on any tile "
2394 "within the city radius.\n"),
2395 terrain_class_name_translation
2396 (preq->source.value.terrainclass));
2397 }
2398 return TRUE;
2399 case REQ_RANGE_TRADEROUTE:
2400 CATLSTR(buf, bufsz, prefix);
2401 if (preq->present) {
2402 cat_snprintf(buf, bufsz,
2403 /* TRANS: %s is a terrain class */
2404 Q_("?terrainclass:Requires %s terrain on a tile within "
2405 "the city radius or the city radius of a trade "
2406 "partner.\n"),
2407 terrain_class_name_translation
2408 (preq->source.value.terrainclass));
2409 } else {
2410 cat_snprintf(buf, bufsz,
2411 /* TRANS: %s is a terrain class */
2412 Q_("?terrainclass:Prevented by %s terrain on any tile "
2413 "within the city radius or the city radius of a trade "
2414 "partner.\n"),
2415 terrain_class_name_translation
2416 (preq->source.value.terrainclass));
2417 }
2418 return TRUE;
2419 case REQ_RANGE_CONTINENT:
2420 case REQ_RANGE_PLAYER:
2421 case REQ_RANGE_TEAM:
2422 case REQ_RANGE_ALLIANCE:
2423 case REQ_RANGE_WORLD:
2424 case REQ_RANGE_COUNT:
2425 /* Not supported. */
2426 break;
2427 }
2428 break;
2429
2430 case VUT_TERRFLAG:
2431 switch (preq->range) {
2432 case REQ_RANGE_LOCAL:
2433 CATLSTR(buf, bufsz, prefix);
2434 if (preq->present) {
2435 cat_snprintf(buf, bufsz,
2436 /* TRANS: %s is a (translatable) terrain flag. */
2437 _("Requires terrain with the \"%s\" flag on the tile.\n"),
2438 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2439 } else {
2440 cat_snprintf(buf, bufsz,
2441 /* TRANS: %s is a (translatable) terrain flag. */
2442 _("Prevented by terrain with the \"%s\" flag on the "
2443 "tile.\n"),
2444 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2445 }
2446 return TRUE;
2447 case REQ_RANGE_CADJACENT:
2448 CATLSTR(buf, bufsz, prefix);
2449 if (preq->present) {
2450 cat_snprintf(buf, bufsz,
2451 /* TRANS: %s is a (translatable) terrain flag. */
2452 _("Requires terrain with the \"%s\" flag on the "
2453 "tile or a cardinally adjacent tile.\n"),
2454 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2455 } else {
2456 cat_snprintf(buf, bufsz,
2457 /* TRANS: %s is a (translatable) terrain flag. */
2458 _("Prevented by terrain with the \"%s\" flag on "
2459 "the tile or any cardinally adjacent tile.\n"),
2460 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2461 }
2462 return TRUE;
2463 case REQ_RANGE_ADJACENT:
2464 CATLSTR(buf, bufsz, prefix);
2465 if (preq->present) {
2466 cat_snprintf(buf, bufsz,
2467 /* TRANS: %s is a (translatable) terrain flag. */
2468 _("Requires terrain with the \"%s\" flag on the "
2469 "tile or an adjacent tile.\n"),
2470 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2471 } else {
2472 cat_snprintf(buf, bufsz,
2473 /* TRANS: %s is a (translatable) terrain flag. */
2474 _("Prevented by terrain with the \"%s\" flag on "
2475 "the tile or any adjacent tile.\n"),
2476 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2477 }
2478 return TRUE;
2479 case REQ_RANGE_CITY:
2480 CATLSTR(buf, bufsz, prefix);
2481 if (preq->present) {
2482 cat_snprintf(buf, bufsz,
2483 /* TRANS: %s is a (translatable) terrain flag. */
2484 _("Requires terrain with the \"%s\" flag on a tile "
2485 "within the city radius.\n"),
2486 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2487 } else {
2488 cat_snprintf(buf, bufsz,
2489 /* TRANS: %s is a (translatable) terrain flag. */
2490 _("Prevented by terrain with the \"%s\" flag on any tile "
2491 "within the city radius.\n"),
2492 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2493 }
2494 return TRUE;
2495 case REQ_RANGE_TRADEROUTE:
2496 CATLSTR(buf, bufsz, prefix);
2497 if (preq->present) {
2498 cat_snprintf(buf, bufsz,
2499 /* TRANS: %s is a (translatable) terrain flag. */
2500 _("Requires terrain with the \"%s\" flag on a tile "
2501 "within the city radius or the city radius of "
2502 "a trade partner.\n"),
2503 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2504 } else {
2505 cat_snprintf(buf, bufsz,
2506 /* TRANS: %s is a (translatable) terrain flag. */
2507 _("Prevented by terrain with the \"%s\" flag on any tile "
2508 "within the city radius or the city radius of "
2509 "a trade partner.\n"),
2510 terrain_flag_id_translated_name(preq->source.value.terrainflag));
2511 }
2512 return TRUE;
2513 case REQ_RANGE_CONTINENT:
2514 case REQ_RANGE_PLAYER:
2515 case REQ_RANGE_TEAM:
2516 case REQ_RANGE_ALLIANCE:
2517 case REQ_RANGE_WORLD:
2518 case REQ_RANGE_COUNT:
2519 /* Not supported. */
2520 break;
2521 }
2522 break;
2523
2524 case VUT_BASEFLAG:
2525 switch (preq->range) {
2526 case REQ_RANGE_LOCAL:
2527 CATLSTR(buf, bufsz, prefix);
2528 if (preq->present) {
2529 cat_snprintf(buf, bufsz,
2530 /* TRANS: %s is a (translatable) base flag. */
2531 _("Requires a base with the \"%s\" flag on the tile.\n"),
2532 base_flag_id_translated_name(preq->source.value.baseflag));
2533 } else {
2534 cat_snprintf(buf, bufsz,
2535 /* TRANS: %s is a (translatable) base flag. */
2536 _("Prevented by a base with the \"%s\" flag on the "
2537 "tile.\n"),
2538 base_flag_id_translated_name(preq->source.value.baseflag));
2539 }
2540 return TRUE;
2541 case REQ_RANGE_CADJACENT:
2542 CATLSTR(buf, bufsz, prefix);
2543 if (preq->present) {
2544 cat_snprintf(buf, bufsz,
2545 /* TRANS: %s is a (translatable) base flag. */
2546 _("Requires a base with the \"%s\" flag on the "
2547 "tile or a cardinally adjacent tile.\n"),
2548 base_flag_id_translated_name(preq->source.value.baseflag));
2549 } else {
2550 cat_snprintf(buf, bufsz,
2551 /* TRANS: %s is a (translatable) base flag. */
2552 _("Prevented by a base with the \"%s\" flag on "
2553 "the tile or any cardinally adjacent tile.\n"),
2554 base_flag_id_translated_name(preq->source.value.baseflag));
2555 }
2556 return TRUE;
2557 case REQ_RANGE_ADJACENT:
2558 CATLSTR(buf, bufsz, prefix);
2559 if (preq->present) {
2560 cat_snprintf(buf, bufsz,
2561 /* TRANS: %s is a (translatable) base flag. */
2562 _("Requires a base with the \"%s\" flag on the "
2563 "tile or an adjacent tile.\n"),
2564 base_flag_id_translated_name(preq->source.value.baseflag));
2565 } else {
2566 cat_snprintf(buf, bufsz,
2567 /* TRANS: %s is a (translatable) base flag. */
2568 _("Prevented by a base with the \"%s\" flag on "
2569 "the tile or any adjacent tile.\n"),
2570 base_flag_id_translated_name(preq->source.value.baseflag));
2571 }
2572 return TRUE;
2573 case REQ_RANGE_CITY:
2574 CATLSTR(buf, bufsz, prefix);
2575 if (preq->present) {
2576 cat_snprintf(buf, bufsz,
2577 /* TRANS: %s is a (translatable) base flag. */
2578 _("Requires a base with the \"%s\" flag on a tile "
2579 "within the city radius.\n"),
2580 base_flag_id_translated_name(preq->source.value.baseflag));
2581 } else {
2582 cat_snprintf(buf, bufsz,
2583 /* TRANS: %s is a (translatable) base flag. */
2584 _("Prevented by a base with the \"%s\" flag on any tile "
2585 "within the city radius.\n"),
2586 base_flag_id_translated_name(preq->source.value.baseflag));
2587 }
2588 return TRUE;
2589 case REQ_RANGE_TRADEROUTE:
2590 CATLSTR(buf, bufsz, prefix);
2591 if (preq->present) {
2592 cat_snprintf(buf, bufsz,
2593 /* TRANS: %s is a (translatable) base flag. */
2594 _("Requires a base with the \"%s\" flag on a tile "
2595 "within the city radius or the city radius of a "
2596 "trade partner.\n"),
2597 base_flag_id_translated_name(preq->source.value.baseflag));
2598 } else {
2599 cat_snprintf(buf, bufsz,
2600 /* TRANS: %s is a (translatable) base flag. */
2601 _("Prevented by a base with the \"%s\" flag on any tile "
2602 "within the city radius or the city radius of a "
2603 "trade partner.\n"),
2604 base_flag_id_translated_name(preq->source.value.baseflag));
2605 }
2606 return TRUE;
2607 case REQ_RANGE_CONTINENT:
2608 case REQ_RANGE_PLAYER:
2609 case REQ_RANGE_TEAM:
2610 case REQ_RANGE_ALLIANCE:
2611 case REQ_RANGE_WORLD:
2612 case REQ_RANGE_COUNT:
2613 /* Not supported. */
2614 break;
2615 }
2616 break;
2617
2618 case VUT_ROADFLAG:
2619 switch (preq->range) {
2620 case REQ_RANGE_LOCAL:
2621 CATLSTR(buf, bufsz, prefix);
2622 if (preq->present) {
2623 cat_snprintf(buf, bufsz,
2624 /* TRANS: %s is a (translatable) road flag. */
2625 _("Requires a road with the \"%s\" flag on the tile.\n"),
2626 road_flag_id_translated_name(preq->source.value.roadflag));
2627 } else {
2628 cat_snprintf(buf, bufsz,
2629 /* TRANS: %s is a (translatable) road flag. */
2630 _("Prevented by a road with the \"%s\" flag on the "
2631 "tile.\n"),
2632 road_flag_id_translated_name(preq->source.value.roadflag));
2633 }
2634 return TRUE;
2635 case REQ_RANGE_CADJACENT:
2636 CATLSTR(buf, bufsz, prefix);
2637 if (preq->present) {
2638 cat_snprintf(buf, bufsz,
2639 /* TRANS: %s is a (translatable) road flag. */
2640 _("Requires a road with the \"%s\" flag on the "
2641 "tile or a cardinally adjacent tile.\n"),
2642 road_flag_id_translated_name(preq->source.value.roadflag));
2643 } else {
2644 cat_snprintf(buf, bufsz,
2645 /* TRANS: %s is a (translatable) road flag. */
2646 _("Prevented by a road with the \"%s\" flag on "
2647 "the tile or any cardinally adjacent tile.\n"),
2648 road_flag_id_translated_name(preq->source.value.roadflag));
2649 }
2650 return TRUE;
2651 case REQ_RANGE_ADJACENT:
2652 CATLSTR(buf, bufsz, prefix);
2653 if (preq->present) {
2654 cat_snprintf(buf, bufsz,
2655 /* TRANS: %s is a (translatable) road flag. */
2656 _("Requires a road with the \"%s\" flag on the "
2657 "tile or an adjacent tile.\n"),
2658 road_flag_id_translated_name(preq->source.value.roadflag));
2659 } else {
2660 cat_snprintf(buf, bufsz,
2661 /* TRANS: %s is a (translatable) road flag. */
2662 _("Prevented by a road with the \"%s\" flag on "
2663 "the tile or any adjacent tile.\n"),
2664 road_flag_id_translated_name(preq->source.value.roadflag));
2665 }
2666 return TRUE;
2667 case REQ_RANGE_CITY:
2668 CATLSTR(buf, bufsz, prefix);
2669 if (preq->present) {
2670 cat_snprintf(buf, bufsz,
2671 /* TRANS: %s is a (translatable) road flag. */
2672 _("Requires a road with the \"%s\" flag on a tile "
2673 "within the city radius.\n"),
2674 road_flag_id_translated_name(preq->source.value.roadflag));
2675 } else {
2676 cat_snprintf(buf, bufsz,
2677 /* TRANS: %s is a (translatable) road flag. */
2678 _("Prevented by a road with the \"%s\" flag on any tile "
2679 "within the city radius.\n"),
2680 road_flag_id_translated_name(preq->source.value.roadflag));
2681 }
2682 return TRUE;
2683 case REQ_RANGE_TRADEROUTE:
2684 CATLSTR(buf, bufsz, prefix);
2685 if (preq->present) {
2686 cat_snprintf(buf, bufsz,
2687 /* TRANS: %s is a (translatable) road flag. */
2688 _("Requires a road with the \"%s\" flag on a tile "
2689 "within the city radius or the city radius of a "
2690 "trade partner.\n"),
2691 road_flag_id_translated_name(preq->source.value.roadflag));
2692 } else {
2693 cat_snprintf(buf, bufsz,
2694 /* TRANS: %s is a (translatable) road flag. */
2695 _("Prevented by a road with the \"%s\" flag on any tile "
2696 "within the city radius or the city radius of a "
2697 "trade partner.\n"),
2698 road_flag_id_translated_name(preq->source.value.roadflag));
2699 }
2700 return TRUE;
2701 case REQ_RANGE_CONTINENT:
2702 case REQ_RANGE_PLAYER:
2703 case REQ_RANGE_TEAM:
2704 case REQ_RANGE_ALLIANCE:
2705 case REQ_RANGE_WORLD:
2706 case REQ_RANGE_COUNT:
2707 /* Not supported. */
2708 break;
2709 }
2710 break;
2711
2712 case VUT_EXTRAFLAG:
2713 switch (preq->range) {
2714 case REQ_RANGE_LOCAL:
2715 CATLSTR(buf, bufsz, prefix);
2716 if (preq->present) {
2717 cat_snprintf(buf, bufsz,
2718 /* TRANS: %s is a (translatable) extra flag. */
2719 _("Requires an extra with the \"%s\" flag on the tile.\n"),
2720 extra_flag_id_translated_name(preq->source.value.extraflag));
2721 } else {
2722 cat_snprintf(buf, bufsz,
2723 /* TRANS: %s is a (translatable) extra flag. */
2724 _("Prevented by an extra with the \"%s\" flag on the "
2725 "tile.\n"),
2726 extra_flag_id_translated_name(preq->source.value.extraflag));
2727 }
2728 return TRUE;
2729 case REQ_RANGE_CADJACENT:
2730 CATLSTR(buf, bufsz, prefix);
2731 if (preq->present) {
2732 cat_snprintf(buf, bufsz,
2733 /* TRANS: %s is a (translatable) extra flag. */
2734 _("Requires an extra with the \"%s\" flag on the "
2735 "tile or a cardinally adjacent tile.\n"),
2736 extra_flag_id_translated_name(preq->source.value.extraflag));
2737 } else {
2738 cat_snprintf(buf, bufsz,
2739 /* TRANS: %s is a (translatable) extra flag. */
2740 _("Prevented by an extra with the \"%s\" flag on "
2741 "the tile or any cardinally adjacent tile.\n"),
2742 extra_flag_id_translated_name(preq->source.value.extraflag));
2743 }
2744 return TRUE;
2745 case REQ_RANGE_ADJACENT:
2746 CATLSTR(buf, bufsz, prefix);
2747 if (preq->present) {
2748 cat_snprintf(buf, bufsz,
2749 /* TRANS: %s is a (translatable) extra flag. */
2750 _("Requires an extra with the \"%s\" flag on the "
2751 "tile or an adjacent tile.\n"),
2752 extra_flag_id_translated_name(preq->source.value.extraflag));
2753 } else {
2754 cat_snprintf(buf, bufsz,
2755 /* TRANS: %s is a (translatable) extra flag. */
2756 _("Prevented by an extra with the \"%s\" flag on "
2757 "the tile or any adjacent tile.\n"),
2758 extra_flag_id_translated_name(preq->source.value.extraflag));
2759 }
2760 return TRUE;
2761 case REQ_RANGE_CITY:
2762 CATLSTR(buf, bufsz, prefix);
2763 if (preq->present) {
2764 cat_snprintf(buf, bufsz,
2765 /* TRANS: %s is a (translatable) extra flag. */
2766 _("Requires an extra with the \"%s\" flag on a tile "
2767 "within the city radius.\n"),
2768 extra_flag_id_translated_name(preq->source.value.extraflag));
2769 } else {
2770 cat_snprintf(buf, bufsz,
2771 /* TRANS: %s is a (translatable) extra flag. */
2772 _("Prevented by an extra with the \"%s\" flag on any tile "
2773 "within the city radius.\n"),
2774 extra_flag_id_translated_name(preq->source.value.extraflag));
2775 }
2776 return TRUE;
2777 case REQ_RANGE_TRADEROUTE:
2778 CATLSTR(buf, bufsz, prefix);
2779 if (preq->present) {
2780 cat_snprintf(buf, bufsz,
2781 /* TRANS: %s is a (translatable) extra flag. */
2782 _("Requires an extra with the \"%s\" flag on a tile "
2783 "within the city radius or the city radius of a "
2784 "trade partner.\n"),
2785 extra_flag_id_translated_name(preq->source.value.extraflag));
2786 } else {
2787 cat_snprintf(buf, bufsz,
2788 /* TRANS: %s is a (translatable) extra flag. */
2789 _("Prevented by an extra with the \"%s\" flag on any tile "
2790 "within the city radius or the city radius of a "
2791 "trade partner.\n"),
2792 extra_flag_id_translated_name(preq->source.value.extraflag));
2793 }
2794 return TRUE;
2795 case REQ_RANGE_CONTINENT:
2796 case REQ_RANGE_PLAYER:
2797 case REQ_RANGE_TEAM:
2798 case REQ_RANGE_ALLIANCE:
2799 case REQ_RANGE_WORLD:
2800 case REQ_RANGE_COUNT:
2801 /* Not supported. */
2802 break;
2803 }
2804 break;
2805
2806 case VUT_MINYEAR:
2807 if (preq->range != REQ_RANGE_WORLD) {
2808 break;
2809 }
2810 CATLSTR(buf, bufsz, prefix);
2811 if (preq->present) {
2812 cat_snprintf(buf, bufsz,
2813 _("Requires the game to have reached the year %s.\n"),
2814 textyear(preq->source.value.minyear));
2815 } else {
2816 cat_snprintf(buf, bufsz,
2817 _("Requires that the game has not yet reached the "
2818 "year %s.\n"),
2819 textyear(preq->source.value.minyear));
2820 }
2821 return TRUE;
2822
2823 case VUT_TOPO:
2824 if (preq->range != REQ_RANGE_WORLD) {
2825 break;
2826 }
2827 CATLSTR(buf, bufsz, prefix);
2828 if (preq->present) {
2829 cat_snprintf(buf, bufsz,
2830 /* TRANS: topology flag name ("WrapX", "ISO", etc) */
2831 _("Requires %s map.\n"),
2832 _(topo_flag_name(preq->source.value.topo_property)));
2833 } else {
2834 cat_snprintf(buf, bufsz,
2835 /* TRANS: topology flag name ("WrapX", "ISO", etc) */
2836 _("Prevented on %s map.\n"),
2837 _(topo_flag_name(preq->source.value.topo_property)));
2838 }
2839 return TRUE;
2840
2841 case VUT_AGE:
2842 CATLSTR(buf, bufsz, prefix);
2843 if (preq->present) {
2844 cat_snprintf(buf, bufsz,
2845 _("Requires age of %d turns.\n"),
2846 preq->source.value.age);
2847 } else {
2848 cat_snprintf(buf, bufsz,
2849 _("Prevented if age is over %d turns.\n"),
2850 preq->source.value.age);
2851 }
2852 return TRUE;
2853
2854 case VUT_TERRAINALTER:
2855 switch (preq->range) {
2856 case REQ_RANGE_LOCAL:
2857 CATLSTR(buf, bufsz, prefix);
2858 if (preq->present) {
2859 cat_snprintf(buf, bufsz,
2860 _("Requires terrain on which alteration %s is "
2861 "possible.\n"),
2862 Q_(terrain_alteration_name(preq->source.value.terrainalter)));
2863 } else {
2864 cat_snprintf(buf, bufsz,
2865 _("Prevented by terrain on which alteration %s "
2866 "can be made.\n"),
2867 Q_(terrain_alteration_name(preq->source.value.terrainalter)));
2868 }
2869 return TRUE;
2870 case REQ_RANGE_CADJACENT:
2871 case REQ_RANGE_ADJACENT:
2872 case REQ_RANGE_CITY:
2873 case REQ_RANGE_TRADEROUTE:
2874 case REQ_RANGE_CONTINENT:
2875 case REQ_RANGE_PLAYER:
2876 case REQ_RANGE_TEAM:
2877 case REQ_RANGE_ALLIANCE:
2878 case REQ_RANGE_WORLD:
2879 case REQ_RANGE_COUNT:
2880 /* Not supported. */
2881 break;
2882 }
2883 break;
2884
2885 case VUT_CITYTILE:
2886 if (preq->source.value.citytile == CITYT_LAST) {
2887 break;
2888 } else {
2889 static char *tile_property = NULL;
2890
2891 switch (preq->source.value.citytile) {
2892 case CITYT_CENTER:
2893 tile_property = _("city centers");
2894 break;
2895 case CITYT_CLAIMED:
2896 tile_property = _("claimed tiles");
2897 break;
2898 case CITYT_LAST:
2899 fc_assert(preq->source.value.citytile != CITYT_LAST);
2900 break;
2901 }
2902
2903 switch (preq->range) {
2904 case REQ_RANGE_LOCAL:
2905 CATLSTR(buf, bufsz, prefix);
2906 if (preq->present) {
2907 cat_snprintf(buf, bufsz,
2908 /* TRANS: tile property ("city centers", etc) */
2909 Q_("?tileprop:Applies only to %s.\n"), tile_property);
2910 } else {
2911 cat_snprintf(buf, bufsz,
2912 /* TRANS: tile property ("city centers", etc) */
2913 Q_("?tileprop:Does not apply to %s.\n"), tile_property);
2914 }
2915 return TRUE;
2916 case REQ_RANGE_CADJACENT:
2917 CATLSTR(buf, bufsz, prefix);
2918 if (preq->present) {
2919 /* TRANS: tile property ("city centers", etc) */
2920 cat_snprintf(buf, bufsz, Q_("?tileprop:Applies only to %s and "
2921 "cardinally adjacent tiles.\n"),
2922 tile_property);
2923 } else {
2924 /* TRANS: tile property ("city centers", etc) */
2925 cat_snprintf(buf, bufsz, Q_("?tileprop:Does not apply to %s or "
2926 "cardinally adjacent tiles.\n"),
2927 tile_property);
2928 }
2929 return TRUE;
2930 case REQ_RANGE_ADJACENT:
2931 CATLSTR(buf, bufsz, prefix);
2932 if (preq->present) {
2933 /* TRANS: tile property ("city centers", etc) */
2934 cat_snprintf(buf, bufsz, Q_("?tileprop:Applies only to %s and "
2935 "adjacent tiles.\n"), tile_property);
2936 } else {
2937 /* TRANS: tile property ("city centers", etc) */
2938 cat_snprintf(buf, bufsz, Q_("?tileprop:Does not apply to %s or "
2939 "adjacent tiles.\n"), tile_property);
2940 }
2941 return TRUE;
2942 case REQ_RANGE_CITY:
2943 case REQ_RANGE_TRADEROUTE:
2944 case REQ_RANGE_CONTINENT:
2945 case REQ_RANGE_PLAYER:
2946 case REQ_RANGE_TEAM:
2947 case REQ_RANGE_ALLIANCE:
2948 case REQ_RANGE_WORLD:
2949 case REQ_RANGE_COUNT:
2950 /* Not supported. */
2951 break;
2952 }
2953 }
2954
2955 case VUT_COUNT:
2956 break;
2957 }
2958
2959 {
2960 char text[256];
2961
2962 log_error("%s requirement %s in range %d is not supported in helpdata.c.",
2963 preq->present ? "Present" : "Absent",
2964 universal_name_translation(&preq->source, text, sizeof(text)),
2965 preq->range);
2966 }
2967
2968 return FALSE;
2969 }
2970
2971 /****************************************************************************
2972 Append text to 'buf' if the given requirements list 'subjreqs' contains
2973 'psource', implying that ability to build the subject 'subjstr' is
2974 affected by 'psource'.
2975 'strs' is an array of (possibly i18n-qualified) format strings covering
2976 the various cases where additional requirements apply.
2977 ****************************************************************************/
insert_allows_single(struct universal * psource,struct requirement_vector * psubjreqs,const char * subjstr,const char * const * strs,char * buf,size_t bufsz,const char * prefix)2978 static void insert_allows_single(struct universal *psource,
2979 struct requirement_vector *psubjreqs,
2980 const char *subjstr,
2981 const char *const *strs,
2982 char *buf, size_t bufsz,
2983 const char *prefix)
2984 {
2985 struct strvec *coreqs = strvec_new();
2986 struct strvec *conoreqs = strvec_new();
2987 struct astring coreqstr = ASTRING_INIT;
2988 struct astring conoreqstr = ASTRING_INIT;
2989 char buf2[bufsz];
2990
2991 /* FIXME: show other data like range and survives. */
2992
2993 requirement_vector_iterate(psubjreqs, req) {
2994 if (!req->quiet && are_universals_equal(psource, &req->source)) {
2995 /* We're definitely going to print _something_. */
2996 CATLSTR(buf, bufsz, prefix);
2997 if (req->present) {
2998 /* psource enables the subject, but other sources may
2999 * also be required (or required to be absent). */
3000 requirement_vector_iterate(psubjreqs, coreq) {
3001 if (!coreq->quiet && !are_universals_equal(psource, &coreq->source)) {
3002 universal_name_translation(&coreq->source, buf2, sizeof(buf2));
3003 strvec_append(coreq->present ? coreqs : conoreqs, buf2);
3004 }
3005 } requirement_vector_iterate_end;
3006
3007 if (0 < strvec_size(coreqs)) {
3008 if (0 < strvec_size(conoreqs)) {
3009 cat_snprintf(buf, bufsz,
3010 Q_(strs[0]), /* "Allows %s (with %s but no %s)." */
3011 subjstr,
3012 strvec_to_and_list(coreqs, &coreqstr),
3013 strvec_to_or_list(conoreqs, &conoreqstr));
3014 } else {
3015 cat_snprintf(buf, bufsz,
3016 Q_(strs[1]), /* "Allows %s (with %s)." */
3017 subjstr,
3018 strvec_to_and_list(coreqs, &coreqstr));
3019 }
3020 } else {
3021 if (0 < strvec_size(conoreqs)) {
3022 cat_snprintf(buf, bufsz,
3023 Q_(strs[2]), /* "Allows %s (absent %s)." */
3024 subjstr,
3025 strvec_to_and_list(conoreqs, &conoreqstr));
3026 } else {
3027 cat_snprintf(buf, bufsz,
3028 Q_(strs[3]), /* "Allows %s." */
3029 subjstr);
3030 }
3031 }
3032 } else {
3033 /* psource can, on its own, prevent the subject. */
3034 cat_snprintf(buf, bufsz,
3035 Q_(strs[4]), /* "Prevents %s." */
3036 subjstr);
3037 }
3038 cat_snprintf(buf, bufsz, "\n");
3039 }
3040 } requirement_vector_iterate_end;
3041
3042 strvec_destroy(coreqs);
3043 strvec_destroy(conoreqs);
3044 astr_free(&coreqstr);
3045 astr_free(&conoreqstr);
3046 }
3047
3048 /****************************************************************************
3049 Generate text for what this requirement source allows. Something like
3050
3051 "Allows Communism (with University).\n"
3052 "Allows Mfg. Plant (with Factory).\n"
3053 "Allows Library (absent Fundamentalism).\n"
3054 "Prevents Harbor.\n"
3055
3056 This should be called to generate helptext for every possible source
3057 type. Note this doesn't handle effects but rather requirements to
3058 create/maintain things (currently only building/government reqs).
3059
3060 NB: This function overwrites any existing buffer contents by writing the
3061 generated text to the start of the given 'buf' pointer (i.e. it does
3062 NOT append like cat_snprintf).
3063 ****************************************************************************/
insert_allows(struct universal * psource,char * buf,size_t bufsz,const char * prefix)3064 static void insert_allows(struct universal *psource,
3065 char *buf, size_t bufsz, const char *prefix)
3066 {
3067 buf[0] = '\0';
3068
3069 governments_iterate(pgov) {
3070 static const char *const govstrs[] = {
3071 /* TRANS: First %s is a government name. */
3072 N_("?gov:Allows %s (with %s but no %s)."),
3073 /* TRANS: First %s is a government name. */
3074 N_("?gov:Allows %s (with %s)."),
3075 /* TRANS: First %s is a government name. */
3076 N_("?gov:Allows %s (absent %s)."),
3077 /* TRANS: %s is a government name. */
3078 N_("?gov:Allows %s."),
3079 /* TRANS: %s is a government name. */
3080 N_("?gov:Prevents %s.")
3081 };
3082 insert_allows_single(psource, &pgov->reqs,
3083 government_name_translation(pgov), govstrs,
3084 buf, bufsz, prefix);
3085 } governments_iterate_end;
3086
3087 improvement_iterate(pimprove) {
3088 if (valid_improvement(pimprove)) {
3089 static const char *const imprstrs[] = {
3090 /* TRANS: First %s is a building name. */
3091 N_("?improvement:Allows %s (with %s but no %s)."),
3092 /* TRANS: First %s is a building name. */
3093 N_("?improvement:Allows %s (with %s)."),
3094 /* TRANS: First %s is a building name. */
3095 N_("?improvement:Allows %s (absent %s)."),
3096 /* TRANS: %s is a building name. */
3097 N_("?improvement:Allows %s."),
3098 /* TRANS: %s is a building name. */
3099 N_("?improvement:Prevents %s.")
3100 };
3101 insert_allows_single(psource, &pimprove->reqs,
3102 improvement_name_translation(pimprove), imprstrs,
3103 buf, bufsz, prefix);
3104 }
3105 } improvement_iterate_end;
3106 }
3107
3108 /****************************************************************
3109 Allocate and initialize new help item
3110 *****************************************************************/
new_help_item(int type)3111 static struct help_item *new_help_item(int type)
3112 {
3113 struct help_item *pitem;
3114
3115 pitem = fc_malloc(sizeof(struct help_item));
3116 pitem->topic = NULL;
3117 pitem->text = NULL;
3118 pitem->type = type;
3119 return pitem;
3120 }
3121
3122 /****************************************************************
3123 for help_list_sort(); sort by topic via compare_strings()
3124 (sort topics with more leading spaces after those with fewer)
3125 *****************************************************************/
help_item_compar(const struct help_item * const * ppa,const struct help_item * const * ppb)3126 static int help_item_compar(const struct help_item *const *ppa,
3127 const struct help_item *const *ppb)
3128 {
3129 const struct help_item *ha, *hb;
3130 char *ta, *tb;
3131 ha = *ppa;
3132 hb = *ppb;
3133 for (ta = ha->topic, tb = hb->topic; *ta != '\0' && *tb != '\0'; ta++, tb++) {
3134 if (*ta != ' ') {
3135 if (*tb == ' ') return -1;
3136 break;
3137 } else if (*tb != ' ') {
3138 if (*ta == ' ') return 1;
3139 break;
3140 }
3141 }
3142 return compare_strings(ta, tb);
3143 }
3144
3145 /****************************************************************
3146 pplayer may be NULL.
3147 *****************************************************************/
boot_help_texts(void)3148 void boot_help_texts(void)
3149 {
3150 static bool booted = FALSE;
3151
3152 struct section_file *sf;
3153 const char *filename;
3154 struct help_item *pitem;
3155 int i;
3156 struct section_list *sec;
3157 const char **paras;
3158 size_t npara;
3159 char long_buffer[64000]; /* HACK: this may be overrun. */
3160
3161 check_help_nodes_init();
3162
3163 /* need to do something like this or bad things happen */
3164 popdown_help_dialog();
3165
3166 if (!booted) {
3167 log_verbose("Booting help texts");
3168 } else {
3169 /* free memory allocated last time booted */
3170 free_help_texts();
3171 log_verbose("Rebooting help texts");
3172 }
3173
3174 filename = fileinfoname(get_data_dirs(), "helpdata.txt");
3175 if (!filename) {
3176 log_error("Did not read help texts");
3177 return;
3178 }
3179 /* after following call filename may be clobbered; use sf->filename instead */
3180 if (!(sf = secfile_load(filename, FALSE))) {
3181 /* this is now unlikely to happen */
3182 log_error("failed reading help-texts from '%s':\n%s", filename,
3183 secfile_error());
3184 return;
3185 }
3186
3187 sec = secfile_sections_by_name_prefix(sf, "help_");
3188
3189 if (NULL != sec) {
3190 section_list_iterate(sec, psection) {
3191 char help_text_buffer[MAX_LEN_PACKET];
3192 const char *sec_name = section_name(psection);
3193 const char *gen_str = secfile_lookup_str(sf, "%s.generate", sec_name);
3194
3195 if (gen_str) {
3196 enum help_page_type current_type = HELP_ANY;
3197 int level = strspn(gen_str, " ");
3198
3199 gen_str += level;
3200
3201 for (i = 2; help_type_names[i]; i++) {
3202 if (strcmp(gen_str, help_type_names[i]) == 0) {
3203 current_type = i;
3204 break;
3205 }
3206 }
3207 if (current_type == HELP_ANY) {
3208 log_error("bad help-generate category \"%s\"", gen_str);
3209 continue;
3210 }
3211
3212 if (!booted) {
3213 if (current_type == HELP_EXTRA) {
3214 size_t ncats;
3215
3216 /* Avoid warnings about entries unused on this round,
3217 * when the entries in question are valid once help system has been booted */
3218 (void) secfile_lookup_str_vec(sf, &ncats,
3219 "%s.categories", sec_name);
3220 }
3221 continue; /* on initial boot data tables are empty */
3222 }
3223
3224 {
3225 /* Note these should really fill in pitem->text from auto-gen
3226 data instead of doing it later on the fly, but I don't want
3227 to change that now. --dwp
3228 */
3229 char name[2048];
3230 struct help_list *category_nodes = help_list_new();
3231
3232 switch (current_type) {
3233 case HELP_UNIT:
3234 unit_type_iterate(punittype) {
3235 pitem = new_help_item(current_type);
3236 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3237 utype_name_translation(punittype));
3238 pitem->topic = fc_strdup(name);
3239 pitem->text = fc_strdup("");
3240 help_list_append(category_nodes, pitem);
3241 } unit_type_iterate_end;
3242 break;
3243 case HELP_TECH:
3244 advance_index_iterate(A_FIRST, advi) {
3245 if (valid_advance_by_number(advi)) {
3246 pitem = new_help_item(current_type);
3247 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3248 advance_name_translation(advance_by_number(advi)));
3249 pitem->topic = fc_strdup(name);
3250 pitem->text = fc_strdup("");
3251 help_list_append(category_nodes, pitem);
3252 }
3253 } advance_index_iterate_end;
3254 break;
3255 case HELP_TERRAIN:
3256 terrain_type_iterate(pterrain) {
3257 if (0 != strlen(terrain_rule_name(pterrain))) {
3258 pitem = new_help_item(current_type);
3259 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3260 terrain_name_translation(pterrain));
3261 pitem->topic = fc_strdup(name);
3262 pitem->text = fc_strdup("");
3263 help_list_append(category_nodes, pitem);
3264 }
3265 } terrain_type_iterate_end;
3266 break;
3267 case HELP_EXTRA:
3268 {
3269 const char **cats;
3270 size_t ncats;
3271 cats = secfile_lookup_str_vec(sf, &ncats,
3272 "%s.categories", sec_name);
3273 extra_type_iterate(pextra) {
3274 /* If categories not specified, don't filter */
3275 if (cats) {
3276 bool include = FALSE;
3277 const char *cat = extra_category_name(pextra->category);
3278 int ci;
3279
3280 for (ci = 0; ci < ncats; ci++) {
3281 if (fc_strcasecmp(cats[ci], cat) == 0) {
3282 include = TRUE;
3283 break;
3284 }
3285 }
3286 if (!include) {
3287 continue;
3288 }
3289 }
3290 pitem = new_help_item(current_type);
3291 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3292 extra_name_translation(pextra));
3293 pitem->topic = fc_strdup(name);
3294 pitem->text = fc_strdup("");
3295 help_list_append(category_nodes, pitem);
3296 } extra_type_iterate_end;
3297 FC_FREE(cats);
3298 }
3299 break;
3300 case HELP_SPECIALIST:
3301 specialist_type_iterate(sp) {
3302 struct specialist *pspec = specialist_by_number(sp);
3303 pitem = new_help_item(current_type);
3304 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3305 specialist_plural_translation(pspec));
3306 pitem->topic = fc_strdup(name);
3307 pitem->text = fc_strdup("");
3308 help_list_append(category_nodes, pitem);
3309 } specialist_type_iterate_end;
3310 break;
3311 case HELP_GOVERNMENT:
3312 governments_iterate(gov) {
3313 pitem = new_help_item(current_type);
3314 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3315 government_name_translation(gov));
3316 pitem->topic = fc_strdup(name);
3317 pitem->text = fc_strdup("");
3318 help_list_append(category_nodes, pitem);
3319 } governments_iterate_end;
3320 break;
3321 case HELP_IMPROVEMENT:
3322 improvement_iterate(pimprove) {
3323 if (valid_improvement(pimprove) && !is_great_wonder(pimprove)) {
3324 pitem = new_help_item(current_type);
3325 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3326 improvement_name_translation(pimprove));
3327 pitem->topic = fc_strdup(name);
3328 pitem->text = fc_strdup("");
3329 help_list_append(category_nodes, pitem);
3330 }
3331 } improvement_iterate_end;
3332 break;
3333 case HELP_WONDER:
3334 improvement_iterate(pimprove) {
3335 if (valid_improvement(pimprove) && is_great_wonder(pimprove)) {
3336 pitem = new_help_item(current_type);
3337 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3338 improvement_name_translation(pimprove));
3339 pitem->topic = fc_strdup(name);
3340 pitem->text = fc_strdup("");
3341 help_list_append(category_nodes, pitem);
3342 }
3343 } improvement_iterate_end;
3344 break;
3345 case HELP_RULESET:
3346 {
3347 int desc_len;
3348 int len;
3349
3350 pitem = new_help_item(HELP_RULESET);
3351 /* pitem->topic = fc_strdup(_(game.control.name)); */
3352 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3353 Q_(HELP_RULESET_ITEM));
3354 pitem->topic = fc_strdup(name);
3355 if (game.ruleset_description != NULL) {
3356 desc_len = strlen("\n\n") + strlen(game.ruleset_description);
3357 } else {
3358 desc_len = 0;
3359 }
3360 if (game.ruleset_summary != NULL) {
3361 if (game.control.version[0] != '\0') {
3362 len = strlen(_(game.control.name))
3363 + strlen(" ")
3364 + strlen(game.control.version)
3365 + strlen("\n\n")
3366 + strlen(_(game.ruleset_summary))
3367 + 1;
3368
3369 pitem->text = fc_malloc(len + desc_len);
3370 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3371 _(game.control.name), game.control.version,
3372 _(game.ruleset_summary));
3373 } else {
3374 len = strlen(_(game.control.name))
3375 + strlen("\n\n")
3376 + strlen(_(game.ruleset_summary))
3377 + 1;
3378
3379 pitem->text = fc_malloc(len + desc_len);
3380 fc_snprintf(pitem->text, len, "%s\n\n%s",
3381 _(game.control.name), _(game.ruleset_summary));
3382 }
3383 } else {
3384 const char *nodesc = _("Current ruleset contains no summary.");
3385
3386 if (game.control.version[0] != '\0') {
3387 len = strlen(_(game.control.name))
3388 + strlen(" ")
3389 + strlen(game.control.version)
3390 + strlen("\n\n")
3391 + strlen(nodesc)
3392 + 1;
3393
3394 pitem->text = fc_malloc(len + desc_len);
3395 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3396 _(game.control.name), game.control.version,
3397 nodesc);
3398 } else {
3399 len = strlen(_(game.control.name))
3400 + strlen("\n\n")
3401 + strlen(nodesc)
3402 + 1;
3403
3404 pitem->text = fc_malloc(len + desc_len);
3405 fc_snprintf(pitem->text, len, "%s\n\n%s",
3406 _(game.control.name),
3407 nodesc);
3408 }
3409 }
3410 if (game.ruleset_description != NULL) {
3411 fc_strlcat(pitem->text, "\n\n", len + desc_len);
3412 fc_strlcat(pitem->text, game.ruleset_description, len + desc_len);
3413 }
3414 help_list_append(help_nodes, pitem);
3415 }
3416 break;
3417 case HELP_TILESET:
3418 {
3419 int desc_len;
3420 int len;
3421 const char *ts_name = tileset_name_get(tileset);
3422 const char *version = tileset_version(tileset);
3423 const char *summary = tileset_summary(tileset);
3424 const char *description = tileset_description(tileset);
3425
3426 pitem = new_help_item(HELP_TILESET);
3427 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3428 Q_(HELP_TILESET_ITEM));
3429 pitem->topic = fc_strdup(name);
3430 if (description != NULL) {
3431 desc_len = strlen("\n\n") + strlen(description);
3432 } else {
3433 desc_len = 0;
3434 }
3435 if (summary != NULL) {
3436 if (version[0] != '\0') {
3437 len = strlen(_(ts_name))
3438 + strlen(" ")
3439 + strlen(version)
3440 + strlen("\n\n")
3441 + strlen(_(summary))
3442 + 1;
3443
3444 pitem->text = fc_malloc(len + desc_len);
3445 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3446 _(ts_name), version, _(summary));
3447 } else {
3448 len = strlen(_(ts_name))
3449 + strlen("\n\n")
3450 + strlen(_(summary))
3451 + 1;
3452
3453 pitem->text = fc_malloc(len + desc_len);
3454 fc_snprintf(pitem->text, len, "%s\n\n%s",
3455 _(ts_name), _(summary));
3456 }
3457 } else {
3458 const char *nodesc = _("Current tileset contains no summary.");
3459
3460 if (version[0] != '\0') {
3461 len = strlen(_(ts_name))
3462 + strlen(" ")
3463 + strlen(version)
3464 + strlen("\n\n")
3465 + strlen(nodesc)
3466 + 1;
3467
3468 pitem->text = fc_malloc(len + desc_len);
3469 fc_snprintf(pitem->text, len, "%s %s\n\n%s",
3470 _(ts_name), version,
3471 nodesc);
3472 } else {
3473 len = strlen(_(ts_name))
3474 + strlen("\n\n")
3475 + strlen(nodesc)
3476 + 1;
3477
3478 pitem->text = fc_malloc(len + desc_len);
3479 fc_snprintf(pitem->text, len, "%s\n\n%s",
3480 _(ts_name),
3481 nodesc);
3482 }
3483 }
3484 if (description != NULL) {
3485 fc_strlcat(pitem->text, "\n\n", len + desc_len);
3486 fc_strlcat(pitem->text, description, len + desc_len);
3487 }
3488 help_list_append(help_nodes, pitem);
3489 }
3490 break;
3491 case HELP_NATIONS:
3492 nations_iterate(pnation) {
3493 if (client_state() < C_S_RUNNING
3494 || show_help_for_nation(pnation)) {
3495 pitem = new_help_item(current_type);
3496 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3497 nation_plural_translation(pnation));
3498 pitem->topic = fc_strdup(name);
3499 pitem->text = fc_strdup("");
3500 help_list_append(category_nodes, pitem);
3501 }
3502 } nations_iterate_end;
3503 break;
3504 case HELP_MULTIPLIER:
3505 multipliers_iterate(pmul) {
3506 help_text_buffer[0] = '\0';
3507 pitem = new_help_item(current_type);
3508 fc_snprintf(name, sizeof(name), "%*s%s", level, "",
3509 name_translation_get(&pmul->name));
3510 pitem->topic = fc_strdup(name);
3511 if (pmul->helptext) {
3512 const char *sep = "";
3513 strvec_iterate(pmul->helptext, text) {
3514 cat_snprintf(help_text_buffer, sizeof(help_text_buffer),
3515 "%s%s", sep, text);
3516 sep = "\n\n";
3517 } strvec_iterate_end;
3518 }
3519 pitem->text = fc_strdup(help_text_buffer);
3520 help_list_append(help_nodes, pitem);
3521 } multipliers_iterate_end;
3522 break;
3523 default:
3524 log_error("Bad current_type: %d.", current_type);
3525 break;
3526 }
3527 help_list_sort(category_nodes, help_item_compar);
3528 help_list_iterate(category_nodes, ptmp) {
3529 help_list_append(help_nodes, ptmp);
3530 } help_list_iterate_end;
3531 help_list_destroy(category_nodes);
3532 continue;
3533 }
3534 }
3535
3536 /* It wasn't a "generate" node: */
3537
3538 pitem = new_help_item(HELP_TEXT);
3539 pitem->topic = fc_strdup(Q_(secfile_lookup_str(sf, "%s.name",
3540 sec_name)));
3541
3542 paras = secfile_lookup_str_vec(sf, &npara, "%s.text", sec_name);
3543
3544 long_buffer[0] = '\0';
3545 for (i=0; i<npara; i++) {
3546 bool inserted;
3547 const char *para = paras[i];
3548 if(strncmp(para, "$", 1)==0) {
3549 inserted =
3550 insert_generated_text(long_buffer, sizeof(long_buffer), para+1);
3551 } else {
3552 sz_strlcat(long_buffer, _(para));
3553 inserted = TRUE;
3554 }
3555 if (inserted && i!=npara-1) {
3556 sz_strlcat(long_buffer, "\n\n");
3557 }
3558 }
3559 free(paras);
3560 paras = NULL;
3561 pitem->text=fc_strdup(long_buffer);
3562 help_list_append(help_nodes, pitem);
3563 } section_list_iterate_end;
3564
3565 section_list_destroy(sec);
3566 }
3567
3568 secfile_check_unused(sf);
3569 secfile_destroy(sf);
3570 booted = TRUE;
3571 log_verbose("Booted help texts ok");
3572 }
3573
3574 /****************************************************************
3575 The following few functions are essentially wrappers for the
3576 help_nodes help_list. This allows us to avoid exporting the
3577 help_list, and instead only access it through a controlled
3578 interface.
3579 *****************************************************************/
3580
3581 /****************************************************************
3582 Number of help items.
3583 *****************************************************************/
num_help_items(void)3584 int num_help_items(void)
3585 {
3586 check_help_nodes_init();
3587 return help_list_size(help_nodes);
3588 }
3589
3590 /****************************************************************
3591 Return pointer to given help_item.
3592 Returns NULL for 1 past end.
3593 Returns NULL and prints error message for other out-of bounds.
3594 *****************************************************************/
get_help_item(int pos)3595 const struct help_item *get_help_item(int pos)
3596 {
3597 int size;
3598
3599 check_help_nodes_init();
3600 size = help_list_size(help_nodes);
3601 if (pos < 0 || pos > size) {
3602 log_error("Bad index %d to get_help_item (size %d)", pos, size);
3603 return NULL;
3604 }
3605 if (pos == size) {
3606 return NULL;
3607 }
3608 return help_list_get(help_nodes, pos);
3609 }
3610
3611 /****************************************************************
3612 Find help item by name and type.
3613 Returns help item, and sets (*pos) to position in list.
3614 If no item, returns pointer to static internal item with
3615 some faked data, and sets (*pos) to -1.
3616 *****************************************************************/
3617 const struct help_item*
get_help_item_spec(const char * name,enum help_page_type htype,int * pos)3618 get_help_item_spec(const char *name, enum help_page_type htype, int *pos)
3619 {
3620 int idx;
3621 const struct help_item *pitem = NULL;
3622 static struct help_item vitem; /* v = virtual */
3623 static char vtopic[128];
3624 static char vtext[256];
3625
3626 check_help_nodes_init();
3627 idx = 0;
3628 help_list_iterate(help_nodes, ptmp) {
3629 char *p=ptmp->topic;
3630 while (*p == ' ') {
3631 p++;
3632 }
3633 if(strcmp(name, p)==0 && (htype==HELP_ANY || htype==ptmp->type)) {
3634 pitem = ptmp;
3635 break;
3636 }
3637 idx++;
3638 }
3639 help_list_iterate_end;
3640
3641 if(!pitem) {
3642 idx = -1;
3643 vitem.topic = vtopic;
3644 sz_strlcpy(vtopic, name);
3645 vitem.text = vtext;
3646 if(htype==HELP_ANY || htype==HELP_TEXT) {
3647 fc_snprintf(vtext, sizeof(vtext),
3648 _("Sorry, no help topic for %s.\n"), vitem.topic);
3649 vitem.type = HELP_TEXT;
3650 } else {
3651 fc_snprintf(vtext, sizeof(vtext),
3652 _("Sorry, no help topic for %s.\n"
3653 "This page was auto-generated.\n\n"),
3654 vitem.topic);
3655 vitem.type = htype;
3656 }
3657 pitem = &vitem;
3658 }
3659 *pos = idx;
3660 return pitem;
3661 }
3662
3663 /****************************************************************
3664 Start iterating through help items;
3665 that is, reset iterator to start position.
3666 (Could iterate using get_help_item(), but that would be
3667 less efficient due to scanning to find pos.)
3668 *****************************************************************/
help_iter_start(void)3669 void help_iter_start(void)
3670 {
3671 check_help_nodes_init();
3672 help_nodes_iterator = help_list_head(help_nodes);
3673 }
3674
3675 /****************************************************************
3676 Returns next help item; after help_iter_start(), this is
3677 the first item. At end, returns NULL.
3678 *****************************************************************/
help_iter_next(void)3679 const struct help_item *help_iter_next(void)
3680 {
3681 const struct help_item *pitem;
3682
3683 check_help_nodes_init();
3684 pitem = help_list_link_data(help_nodes_iterator);
3685 if (pitem) {
3686 help_nodes_iterator = help_list_link_next(help_nodes_iterator);
3687 }
3688
3689 return pitem;
3690 }
3691
3692
3693 /****************************************************************
3694 FIXME:
3695 Also, in principle these could be auto-generated once, inserted
3696 into pitem->text, and then don't need to keep re-generating them.
3697 Only thing to be careful of would be changeable data, but don't
3698 have that here (for ruleset change or spacerace change must
3699 re-boot helptexts anyway). Eg, genuinely dynamic information
3700 which could be useful would be if help system said which wonders
3701 have been built (or are being built and by who/where?)
3702 *****************************************************************/
3703
3704 /**************************************************************************
3705 Write dynamic text for buildings (including wonders). This includes
3706 the ruleset helptext as well as any automatically generated text.
3707
3708 pplayer may be NULL.
3709 user_text, if non-NULL, will be appended to the text.
3710 **************************************************************************/
helptext_building(char * buf,size_t bufsz,struct player * pplayer,const char * user_text,struct impr_type * pimprove)3711 char *helptext_building(char *buf, size_t bufsz, struct player *pplayer,
3712 const char *user_text, struct impr_type *pimprove)
3713 {
3714 bool reqs = FALSE;
3715 struct universal source = {
3716 .kind = VUT_IMPROVEMENT,
3717 .value = {.building = pimprove}
3718 };
3719
3720 fc_assert_ret_val(NULL != buf && 0 < bufsz, NULL);
3721 buf[0] = '\0';
3722
3723 if (NULL == pimprove) {
3724 return buf;
3725 }
3726
3727 if (NULL != pimprove->helptext) {
3728 strvec_iterate(pimprove->helptext, text) {
3729 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
3730 } strvec_iterate_end;
3731 }
3732
3733 /* Add requirement text for improvement itself */
3734 requirement_vector_iterate(&pimprove->reqs, preq) {
3735 if (insert_requirement(buf, bufsz, pplayer, preq, "")) {
3736 reqs = TRUE;
3737 }
3738 } requirement_vector_iterate_end;
3739 if (reqs) {
3740 fc_strlcat(buf, "\n", bufsz);
3741 }
3742
3743 requirement_vector_iterate(&pimprove->obsolete_by, pobs) {
3744 if (VUT_ADVANCE == pobs->source.kind && pobs->present) {
3745 cat_snprintf(buf, bufsz,
3746 _("* The discovery of %s will make %s obsolete.\n"),
3747 advance_name_translation(pobs->source.value.advance),
3748 improvement_name_translation(pimprove));
3749 }
3750 if (VUT_IMPROVEMENT == pobs->source.kind && pobs->present) {
3751 cat_snprintf(buf, bufsz,
3752 /* TRANS: both %s are improvement names */
3753 _("* The presence of %s in the city will make %s "
3754 "obsolete.\n"),
3755 improvement_name_translation(pobs->source.value.building),
3756 improvement_name_translation(pimprove));
3757 }
3758 } requirement_vector_iterate_end;
3759
3760 if (is_small_wonder(pimprove)) {
3761 cat_snprintf(buf, bufsz,
3762 _("* A 'small wonder': at most one of your cities may "
3763 "possess this improvement.\n"));
3764 }
3765 /* (Great wonders are in their own help section explaining their
3766 * uniqueness, so we don't mention it here.) */
3767
3768 if (building_has_effect(pimprove, EFT_ENABLE_NUKE)
3769 && num_role_units(UTYF_NUCLEAR) > 0) {
3770 struct unit_type *u = get_role_unit(UTYF_NUCLEAR, 0);
3771
3772 cat_snprintf(buf, bufsz,
3773 /* TRANS: 'Allows all players with knowledge of atomic
3774 * power to build nuclear units.' */
3775 _("* Allows all players with knowledge of %s "
3776 "to build %s units.\n"),
3777 advance_name_translation(u->require_advance),
3778 utype_name_translation(u));
3779 }
3780
3781 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf),
3782 /* TRANS: bullet point; note trailing space */
3783 Q_("?bullet:* "));
3784
3785 unit_type_iterate(u) {
3786 if (u->need_improvement == pimprove) {
3787 if (valid_advance(u->require_advance)
3788 && advance_by_number(A_NONE) != u->require_advance) {
3789 cat_snprintf(buf, bufsz, _("* Allows %s (with %s).\n"),
3790 utype_name_translation(u),
3791 advance_name_translation(u->require_advance));
3792 } else {
3793 cat_snprintf(buf, bufsz, _("* Allows %s.\n"),
3794 utype_name_translation(u));
3795 }
3796 }
3797 } unit_type_iterate_end;
3798
3799 {
3800 int i;
3801
3802 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
3803 Impr_type_id n = game.rgame.global_init_buildings[i];
3804 if (n == B_LAST) {
3805 break;
3806 } else if (improvement_by_number(n) == pimprove) {
3807 cat_snprintf(buf, bufsz,
3808 _("* All players start with this improvement in their "
3809 "first city.\n"));
3810 break;
3811 }
3812 }
3813 }
3814
3815 /* Assume no-one will set the same building in both global and nation
3816 * init_buildings... */
3817 nations_iterate(pnation) {
3818 int i;
3819
3820 /* Avoid mentioning nations not in current set. */
3821 if (!show_help_for_nation(pnation)) {
3822 continue;
3823 }
3824 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
3825 Impr_type_id n = pnation->init_buildings[i];
3826 if (n == B_LAST) {
3827 break;
3828 } else if (improvement_by_number(n) == pimprove) {
3829 cat_snprintf(buf, bufsz,
3830 /* TRANS: %s is a nation plural */
3831 _("* The %s start with this improvement in their "
3832 "first city.\n"), nation_plural_translation(pnation));
3833 break;
3834 }
3835 }
3836 } nations_iterate_end;
3837
3838 if (improvement_has_flag(pimprove, IF_SAVE_SMALL_WONDER)) {
3839 cat_snprintf(buf, bufsz,
3840 /* TRANS: don't translate 'savepalace' */
3841 _("* If you lose the city containing this improvement, "
3842 "it will be rebuilt for free in another of your cities "
3843 "(if the 'savepalace' server setting is enabled).\n"));
3844 }
3845
3846 if (user_text && user_text[0] != '\0') {
3847 cat_snprintf(buf, bufsz, "\n\n%s", user_text);
3848 }
3849 return buf;
3850 }
3851
3852 /****************************************************************
3853 Is unit type ever able to build an extra
3854 *****************************************************************/
help_is_extra_buildable(struct extra_type * pextra,struct unit_type * ptype)3855 static bool help_is_extra_buildable(struct extra_type *pextra,
3856 struct unit_type *ptype)
3857 {
3858 if (!pextra->buildable) {
3859 return FALSE;
3860 }
3861
3862 return are_reqs_active(NULL, NULL, NULL, NULL, NULL,
3863 NULL, ptype, NULL, NULL, &pextra->reqs,
3864 RPT_POSSIBLE);
3865 }
3866
3867 /****************************************************************
3868 Is unit type ever able to clean out an extra
3869 *****************************************************************/
help_is_extra_cleanable(struct extra_type * pextra,struct unit_type * ptype)3870 static bool help_is_extra_cleanable(struct extra_type *pextra,
3871 struct unit_type *ptype)
3872 {
3873 return are_reqs_active(NULL, NULL, NULL, NULL, NULL,
3874 NULL, ptype, NULL, NULL, &pextra->rmreqs,
3875 RPT_POSSIBLE);
3876 }
3877
3878 /****************************************************************
3879 Append misc dynamic text for units.
3880 Transport capacity, unit flags, fuel.
3881
3882 pplayer may be NULL.
3883 *****************************************************************/
helptext_unit(char * buf,size_t bufsz,struct player * pplayer,const char * user_text,struct unit_type * utype)3884 char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
3885 const char *user_text, struct unit_type *utype)
3886 {
3887 bool has_vet_levels;
3888 int flagid;
3889 struct unit_class *pclass;
3890
3891 fc_assert_ret_val(NULL != buf && 0 < bufsz && NULL != user_text, NULL);
3892
3893 if (!utype) {
3894 log_error("Unknown unit!");
3895 fc_strlcpy(buf, user_text, bufsz);
3896 return buf;
3897 }
3898
3899 has_vet_levels = utype_veteran_levels(utype) > 1;
3900
3901 buf[0] = '\0';
3902
3903 pclass = utype_class(utype);
3904 cat_snprintf(buf, bufsz,
3905 _("* Belongs to %s unit class."),
3906 uclass_name_translation(pclass));
3907 if (NULL != pclass->helptext) {
3908 strvec_iterate(pclass->helptext, text) {
3909 cat_snprintf(buf, bufsz, "\n%s\n", _(text));
3910 } strvec_iterate_end;
3911 } else {
3912 CATLSTR(buf, bufsz, "\n");
3913 }
3914 if (uclass_has_flag(pclass, UCF_CAN_OCCUPY_CITY)
3915 && !utype_has_flag(utype, UTYF_CIVILIAN)) {
3916 /* TRANS: indented unit class property, preserve leading spaces */
3917 CATLSTR(buf, bufsz, _(" * Can occupy empty enemy cities.\n"));
3918 }
3919 if (!uclass_has_flag(pclass, UCF_TERRAIN_SPEED)) {
3920 /* TRANS: indented unit class property, preserve leading spaces */
3921 CATLSTR(buf, bufsz, _(" * Speed is not affected by terrain.\n"));
3922 }
3923 if (!uclass_has_flag(pclass, UCF_TERRAIN_DEFENSE)) {
3924 /* TRANS: indented unit class property, preserve leading spaces */
3925 CATLSTR(buf, bufsz, _(" * Does not get defense bonuses from terrain.\n"));
3926 }
3927 if (!uclass_has_flag(pclass, UCF_ZOC)) {
3928 /* TRANS: indented unit class property, preserve leading spaces */
3929 CATLSTR(buf, bufsz, _(" * Not subject to zones of control.\n"));
3930 } else if (!utype_has_flag(utype, UTYF_IGZOC)) {
3931 /* TRANS: indented unit class property, preserve leading spaces */
3932 CATLSTR(buf, bufsz, _(" * Subject to zones of control.\n"));
3933 }
3934 if (uclass_has_flag(pclass, UCF_DAMAGE_SLOWS)) {
3935 /* TRANS: indented unit class property, preserve leading spaces */
3936 CATLSTR(buf, bufsz, _(" * Slowed down while damaged.\n"));
3937 }
3938 if (uclass_has_flag(pclass, UCF_MISSILE)) {
3939 /* TRANS: indented unit class property, preserve leading spaces */
3940 CATLSTR(buf, bufsz, _(" * Gets used up in making an attack.\n"));
3941 }
3942 if (uclass_has_flag(pclass, UCF_CAN_FORTIFY)
3943 && !utype_has_flag(utype, UTYF_CANT_FORTIFY)) {
3944 if (utype->defense_strength > 0) {
3945 CATLSTR(buf, bufsz,
3946 /* TRANS: indented unit class property, preserve leading spaces */
3947 /* xgettext:no-c-format */
3948 _(" * Gets a 50% defensive bonus while in cities.\n"));
3949 CATLSTR(buf, bufsz,
3950 /* TRANS: indented unit class property, preserve leading spaces */
3951 /* xgettext:no-c-format */
3952 _(" * May fortify, granting a 50% defensive bonus when not in "
3953 "a city.\n"));
3954 } else {
3955 CATLSTR(buf, bufsz,
3956 /* TRANS: indented unit class property, preserve leading spaces */
3957 _(" * May fortify to stay put.\n"));
3958 }
3959 }
3960 if (uclass_has_flag(pclass, UCF_UNREACHABLE)) {
3961 CATLSTR(buf, bufsz,
3962 /* TRANS: indented unit class property, preserve leading spaces */
3963 _(" * Is unreachable. Most units cannot attack this one.\n"));
3964 }
3965 if (uclass_has_flag(pclass, UCF_CAN_PILLAGE)) {
3966 CATLSTR(buf, bufsz,
3967 /* TRANS: indented unit class property, preserve leading spaces */
3968 _(" * Can pillage tile improvements.\n"));
3969 }
3970 if (uclass_has_flag(pclass, UCF_DOESNT_OCCUPY_TILE)
3971 && !utype_has_flag(utype, UTYF_CIVILIAN)) {
3972 CATLSTR(buf, bufsz,
3973 /* TRANS: indented unit class property, preserve leading spaces */
3974 _(" * Doesn't prevent enemy cities from working the tile it's on.\n"));
3975 }
3976 if (can_attack_non_native(utype)) {
3977 CATLSTR(buf, bufsz,
3978 /* TRANS: indented unit class property, preserve leading spaces */
3979 _(" * Can attack units on non-native tiles.\n"));
3980 }
3981 /* Must use flag to distinguish from UTYF_MARINES text. */
3982 if (utype->attack_strength > 0
3983 && uclass_has_flag(pclass, UCF_ATT_FROM_NON_NATIVE)) {
3984 CATLSTR(buf, bufsz,
3985 /* TRANS: indented unit class property, preserve leading spaces */
3986 _(" * Can launch attack from non-native tiles.\n"));
3987 }
3988 if (uclass_has_flag(pclass, UCF_AIRLIFTABLE)) {
3989 CATLSTR(buf, bufsz,
3990 /* TRANS: indented unit class property, preserve leading spaces */
3991 _(" * Can be airlifted from a suitable city.\n"));
3992 }
3993
3994 /* The unit's combat bonuses. Won't mention that another unit type has a
3995 * combat bonus against this unit type. Doesn't handle complex cases like
3996 * when a unit type has multiple combat bonuses of the same kind. */
3997 combat_bonus_list_iterate(utype->bonuses, cbonus) {
3998 const char *against[utype_count()];
3999 int targets = 0;
4000
4001 if (cbonus->quiet) {
4002 /* Handled in the help text of the ruleset. */
4003 continue;
4004 }
4005
4006 /* Find the unit types of the bonus targets. */
4007 unit_type_iterate(utype2) {
4008 if (utype_has_flag(utype2, cbonus->flag)) {
4009 against[targets++] = utype_name_translation(utype2);
4010 }
4011 } unit_type_iterate_end;
4012
4013 if (targets > 0) {
4014 struct astring list = ASTRING_INIT;
4015
4016 switch (cbonus->type) {
4017 case CBONUS_DEFENSE_MULTIPLIER:
4018 cat_snprintf(buf, bufsz,
4019 /* TRANS: multipied by ... or-list of unit types */
4020 _("* %dx defense bonus if attacked by %s.\n"),
4021 cbonus->value + 1,
4022 astr_build_or_list(&list, against, targets));
4023 break;
4024 case CBONUS_DEFENSE_DIVIDER:
4025 cat_snprintf(buf, bufsz,
4026 /* TRANS: defense divider ... or-list of unit types */
4027 _("* Reduces target's defense to 1 / %d when "
4028 "attacking %s.\n"),
4029 cbonus->value + 1,
4030 astr_build_or_list(&list, against, targets));
4031 break;
4032 case CBONUS_FIREPOWER1:
4033 cat_snprintf(buf, bufsz,
4034 /* TRANS: or-list of unit types */
4035 _("* Reduces target's firepower to 1 when "
4036 "attacking %s.\n"),
4037 astr_build_and_list(&list, against, targets));
4038 break;
4039 }
4040
4041 astr_free(&list);
4042 }
4043 } combat_bonus_list_iterate_end;
4044
4045 if (utype->need_improvement) {
4046 cat_snprintf(buf, bufsz,
4047 _("* Can only be built if there is %s in the city.\n"),
4048 improvement_name_translation(utype->need_improvement));
4049 }
4050
4051 if (utype->need_government) {
4052 cat_snprintf(buf, bufsz,
4053 _("* Can only be built with %s as government.\n"),
4054 government_name_translation(utype->need_government));
4055 }
4056
4057 if (utype_has_flag(utype, UTYF_NOBUILD)) {
4058 CATLSTR(buf, bufsz, _("* May not be built in cities.\n"));
4059 }
4060 if (utype_has_flag(utype, UTYF_BARBARIAN_ONLY)) {
4061 CATLSTR(buf, bufsz, _("* Only barbarians may build this.\n"));
4062 }
4063 if (utype_has_flag(utype, UTYF_NEWCITY_GAMES_ONLY)) {
4064 CATLSTR(buf, bufsz, _("* Can only be built in games where new cities "
4065 "are allowed.\n"));
4066 if (game.scenario.prevent_new_cities) {
4067 /* TRANS: indented; preserve leading spaces */
4068 CATLSTR(buf, bufsz, _(" - New cities are not allowed in the current "
4069 "game.\n"));
4070 } else {
4071 /* TRANS: indented; preserve leading spaces */
4072 CATLSTR(buf, bufsz, _(" - New cities are allowed in the current "
4073 "game.\n"));
4074 }
4075 }
4076 nations_iterate(pnation) {
4077 int i, count = 0;
4078
4079 /* Avoid mentioning nations not in current set. */
4080 if (!show_help_for_nation(pnation)) {
4081 continue;
4082 }
4083 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
4084 if (!pnation->init_units[i]) {
4085 break;
4086 } else if (pnation->init_units[i] == utype) {
4087 count++;
4088 }
4089 }
4090 if (count > 0) {
4091 cat_snprintf(buf, bufsz,
4092 /* TRANS: %s is a nation plural */
4093 PL_("* The %s start the game with %d of these units.\n",
4094 "* The %s start the game with %d of these units.\n",
4095 count),
4096 nation_plural_translation(pnation), count);
4097 }
4098 } nations_iterate_end;
4099 {
4100 const char *types[utype_count()];
4101 int i = 0;
4102 unit_type_iterate(utype2) {
4103 if (utype2->converted_to == utype) {
4104 types[i++] = utype_name_translation(utype2);
4105 }
4106 } unit_type_iterate_end;
4107 if (i > 0) {
4108 struct astring list = ASTRING_INIT;
4109 astr_build_or_list(&list, types, i);
4110 cat_snprintf(buf, bufsz,
4111 /* TRANS: %s is a list of unit types separated by "or". */
4112 _("* May be obtained by conversion of %s.\n"),
4113 astr_str(&list));
4114 astr_free(&list);
4115 }
4116 }
4117 if (NULL != utype->converted_to) {
4118 cat_snprintf(buf, bufsz,
4119 /* TRANS: %s is a unit type. "MP" = movement points. */
4120 PL_("* May be converted into %s (takes %d MP).\n",
4121 "* May be converted into %s (takes %d MP).\n",
4122 utype->convert_time),
4123 utype_name_translation(utype->converted_to),
4124 utype->convert_time);
4125 }
4126 if (utype_has_flag(utype, UTYF_NOHOME)) {
4127 CATLSTR(buf, bufsz, _("* Never has a home city.\n"));
4128 }
4129 if (utype_has_flag(utype, UTYF_GAMELOSS)) {
4130 CATLSTR(buf, bufsz, _("* Losing this unit will lose you the game!\n"));
4131 }
4132 if (utype_has_flag(utype, UTYF_UNIQUE)) {
4133 CATLSTR(buf, bufsz,
4134 _("* Each player may only have one of this type of unit.\n"));
4135 }
4136 for (flagid = UTYF_USER_FLAG_1 ; flagid <= UTYF_LAST_USER_FLAG; flagid++) {
4137 if (utype_has_flag(utype, flagid)) {
4138 const char *helptxt = unit_type_flag_helptxt(flagid);
4139
4140 if (helptxt != NULL) {
4141 /* TRANS: bullet point; note trailing space */
4142 CATLSTR(buf, bufsz, Q_("?bullet:* "));
4143 CATLSTR(buf, bufsz, _(helptxt));
4144 CATLSTR(buf, bufsz, "\n");
4145 }
4146 }
4147 }
4148 if (utype->pop_cost > 0) {
4149 cat_snprintf(buf, bufsz,
4150 PL_("* Costs %d population to build.\n",
4151 "* Costs %d population to build.\n", utype->pop_cost),
4152 utype->pop_cost);
4153 }
4154 if (0 < utype->transport_capacity) {
4155 const char *classes[uclass_count()];
4156 int i = 0;
4157 struct astring list = ASTRING_INIT;
4158
4159 unit_class_iterate(uclass) {
4160 if (can_unit_type_transport(utype, uclass)) {
4161 classes[i++] = uclass_name_translation(uclass);
4162 }
4163 } unit_class_iterate_end;
4164 astr_build_or_list(&list, classes, i);
4165
4166 cat_snprintf(buf, bufsz,
4167 /* TRANS: %s is a list of unit classes separated by "or". */
4168 PL_("* Can carry and refuel %d %s unit.\n",
4169 "* Can carry and refuel up to %d %s units.\n",
4170 utype->transport_capacity),
4171 utype->transport_capacity, astr_str(&list));
4172 astr_free(&list);
4173 if (uclass_has_flag(utype_class(utype), UCF_UNREACHABLE)) {
4174 /* Document restrictions on when units can load/unload */
4175 bool has_restricted_load = FALSE, has_unrestricted_load = FALSE,
4176 has_restricted_unload = FALSE, has_unrestricted_unload = FALSE;
4177 unit_type_iterate(pcargo) {
4178 if (can_unit_type_transport(utype, utype_class(pcargo))) {
4179 if (utype_can_freely_load(pcargo, utype)) {
4180 has_unrestricted_load = TRUE;
4181 } else {
4182 has_restricted_load = TRUE;
4183 }
4184 if (utype_can_freely_unload(pcargo, utype)) {
4185 has_unrestricted_unload = TRUE;
4186 } else {
4187 has_restricted_unload = TRUE;
4188 }
4189 }
4190 } unit_type_iterate_end;
4191 if (has_restricted_load) {
4192 if (has_unrestricted_load) {
4193 /* At least one type of cargo can load onto us freely.
4194 * The specific exceptions will be documented in cargo help. */
4195 CATLSTR(buf, bufsz,
4196 /* TRANS: indented; preserve leading spaces */
4197 _(" * Some cargo cannot be loaded except in a city or a "
4198 "base native to this transport.\n"));
4199 } else {
4200 /* No exceptions */
4201 CATLSTR(buf, bufsz,
4202 /* TRANS: indented; preserve leading spaces */
4203 _(" * Cargo cannot be loaded except in a city or a "
4204 "base native to this transport.\n"));
4205 }
4206 } /* else, no restricted cargo exists; keep quiet */
4207 if (has_restricted_unload) {
4208 if (has_unrestricted_unload) {
4209 /* At least one type of cargo can unload from us freely. */
4210 CATLSTR(buf, bufsz,
4211 /* TRANS: indented; preserve leading spaces */
4212 _(" * Some cargo cannot be unloaded except in a city or a "
4213 "base native to this transport.\n"));
4214 } else {
4215 /* No exceptions */
4216 CATLSTR(buf, bufsz,
4217 /* TRANS: indented; preserve leading spaces */
4218 _(" * Cargo cannot be unloaded except in a city or a "
4219 "base native to this transport.\n"));
4220 }
4221 } /* else, no restricted cargo exists; keep quiet */
4222 }
4223 }
4224 if (utype_has_flag(utype, UTYF_TRIREME)) {
4225 CATLSTR(buf, bufsz, _("* Must stay next to coast.\n"));
4226 }
4227 {
4228 /* Document exceptions to embark/disembark restrictions that we
4229 * have as cargo. */
4230 bv_unit_classes embarks, disembarks;
4231 BV_CLR_ALL(embarks);
4232 BV_CLR_ALL(disembarks);
4233 /* Determine which of our transport classes have restrictions in the first
4234 * place (that is, contain at least one transport which carries at least
4235 * one type of cargo which is restricted).
4236 * We'll suppress output for classes not in this set, since this cargo
4237 * type is not behaving exceptionally in such cases. */
4238 unit_type_iterate(utrans) {
4239 const Unit_Class_id trans_class = uclass_index(utype_class(utrans));
4240 /* Don't waste time repeating checks on classes we've already checked,
4241 * or weren't under consideration in the first place */
4242 if (!BV_ISSET(embarks, trans_class)
4243 && BV_ISSET(utype->embarks, trans_class)) {
4244 unit_type_iterate(other_cargo) {
4245 if (can_unit_type_transport(utrans, utype_class(other_cargo))
4246 && !utype_can_freely_load(other_cargo, utrans)) {
4247 /* At least one load restriction in transport class, which
4248 * we aren't subject to */
4249 BV_SET(embarks, trans_class);
4250 }
4251 } unit_type_iterate_end; /* cargo */
4252 }
4253 if (!BV_ISSET(disembarks, trans_class)
4254 && BV_ISSET(utype->disembarks, trans_class)) {
4255 unit_type_iterate(other_cargo) {
4256 if (can_unit_type_transport(utrans, utype_class(other_cargo))
4257 && !utype_can_freely_unload(other_cargo, utrans)) {
4258 /* At least one load restriction in transport class, which
4259 * we aren't subject to */
4260 BV_SET(disembarks, trans_class);
4261 }
4262 } unit_type_iterate_end; /* cargo */
4263 }
4264 } unit_class_iterate_end; /* transports */
4265
4266 if (BV_ISSET_ANY(embarks)) {
4267 /* Build list of embark exceptions */
4268 const char *eclasses[uclass_count()];
4269 int i = 0;
4270 struct astring elist = ASTRING_INIT;
4271
4272 unit_class_iterate(uclass) {
4273 if (BV_ISSET(embarks, uclass_index(uclass))) {
4274 eclasses[i++] = uclass_name_translation(uclass);
4275 }
4276 } unit_class_iterate_end;
4277 astr_build_or_list(&elist, eclasses, i);
4278 if (BV_ARE_EQUAL(embarks, disembarks)) {
4279 /* A common case: the list of disembark exceptions is identical */
4280 cat_snprintf(buf, bufsz,
4281 /* TRANS: %s is a list of unit classes separated
4282 * by "or". */
4283 _("* May load onto and unload from %s transports even "
4284 "when underway.\n"),
4285 astr_str(&elist));
4286 } else {
4287 cat_snprintf(buf, bufsz,
4288 /* TRANS: %s is a list of unit classes separated
4289 * by "or". */
4290 _("* May load onto %s transports even when underway.\n"),
4291 astr_str(&elist));
4292 }
4293 astr_free(&elist);
4294 }
4295 if (BV_ISSET_ANY(disembarks) && !BV_ARE_EQUAL(embarks, disembarks)) {
4296 /* Build list of disembark exceptions (if different from embarking) */
4297 const char *dclasses[uclass_count()];
4298 int i = 0;
4299 struct astring dlist = ASTRING_INIT;
4300
4301 unit_class_iterate(uclass) {
4302 if (BV_ISSET(disembarks, uclass_index(uclass))) {
4303 dclasses[i++] = uclass_name_translation(uclass);
4304 }
4305 } unit_class_iterate_end;
4306 astr_build_or_list(&dlist, dclasses, i);
4307 cat_snprintf(buf, bufsz,
4308 /* TRANS: %s is a list of unit classes separated
4309 * by "or". */
4310 _("* May unload from %s transports even when underway.\n"),
4311 astr_str(&dlist));
4312 astr_free(&dlist);
4313 }
4314 }
4315 if (utype_has_flag(utype, UTYF_UNDISBANDABLE)) {
4316 CATLSTR(buf, bufsz, _("* May not be disbanded.\n"));
4317 } else {
4318 CATLSTR(buf, bufsz,
4319 /* xgettext:no-c-format */
4320 _("* May be disbanded in a city to recover 50% of the"
4321 " production cost.\n"));
4322 }
4323 if (utype_is_cityfounder(utype)) {
4324 cat_snprintf(buf, bufsz,
4325 PL_("* Can build new cities (initial population %d).\n",
4326 "* Can build new cities (initial population %d).\n",
4327 utype->city_size),
4328 utype->city_size);
4329 }
4330 if (utype_has_flag(utype, UTYF_ADD_TO_CITY)) {
4331 cat_snprintf(buf, bufsz,
4332 /* TRANS: Plural in "%d population", not "size %d". */
4333 PL_("* Can add on %d population to cities of no more than"
4334 " size %d.\n",
4335 "* Can add on %d population to cities of no more than"
4336 " size %d.\n", utype_pop_value(utype)),
4337 utype_pop_value(utype),
4338 game.info.add_to_size_limit - utype_pop_value(utype));
4339 }
4340 if (utype_has_flag(utype, UTYF_SETTLERS)) {
4341 struct universal for_utype = { .kind = VUT_UTYPE, .value = { .utype = utype }};
4342 struct astring extras_and = ASTRING_INIT;
4343 struct strvec *extras_vec = strvec_new();
4344
4345 /* Roads, rail, mines, irrigation. */
4346 extra_type_by_cause_iterate(EC_ROAD, pextra) {
4347 if (help_is_extra_buildable(pextra, utype)) {
4348 strvec_append(extras_vec, extra_name_translation(pextra));
4349 }
4350 } extra_type_by_cause_iterate_end;
4351 if (strvec_size(extras_vec) > 0) {
4352 strvec_to_and_list(extras_vec, &extras_and);
4353 /* TRANS: %s is list of extra types separated by ',' and 'and' */
4354 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4355 astr_str(&extras_and));
4356 strvec_clear(extras_vec);
4357 }
4358
4359 if (effect_cumulative_max(EFT_MINING_POSSIBLE, &for_utype) > 0) {
4360 extra_type_by_cause_iterate(EC_MINE, pextra) {
4361 if (help_is_extra_buildable(pextra, utype)) {
4362 strvec_append(extras_vec, extra_name_translation(pextra));
4363 }
4364 } extra_type_by_cause_iterate_end;
4365
4366 if (strvec_size(extras_vec) > 0) {
4367 strvec_to_and_list(extras_vec, &extras_and);
4368 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4369 astr_str(&extras_and));
4370 strvec_clear(extras_vec);
4371 }
4372 }
4373 if (effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_utype) > 0) {
4374 CATLSTR(buf, bufsz, _("* Can convert terrain to another type by "
4375 "mining.\n"));
4376 }
4377
4378 if (effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_utype) > 0) {
4379 extra_type_by_cause_iterate(EC_IRRIGATION, pextra) {
4380 if (help_is_extra_buildable(pextra, utype)) {
4381 strvec_append(extras_vec, extra_name_translation(pextra));
4382 }
4383 } extra_type_by_cause_iterate_end;
4384
4385 if (strvec_size(extras_vec) > 0) {
4386 strvec_to_and_list(extras_vec, &extras_and);
4387 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4388 astr_str(&extras_and));
4389 strvec_clear(extras_vec);
4390 }
4391 }
4392 if (effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_utype) > 0) {
4393 CATLSTR(buf, bufsz, _("* Can convert terrain to another type by "
4394 "irrigation.\n"));
4395 }
4396 if (effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_utype) > 0) {
4397 CATLSTR(buf, bufsz, _("* Can transform terrain to another type.\n"));
4398 }
4399
4400 extra_type_by_cause_iterate(EC_BASE, pextra) {
4401 if (help_is_extra_buildable(pextra, utype)) {
4402 strvec_append(extras_vec, extra_name_translation(pextra));
4403 }
4404 } extra_type_by_cause_iterate_end;
4405
4406 if (strvec_size(extras_vec) > 0) {
4407 strvec_to_and_list(extras_vec, &extras_and);
4408 cat_snprintf(buf, bufsz, _("* Can build %s on tiles.\n"),
4409 astr_str(&extras_and));
4410 strvec_clear(extras_vec);
4411 }
4412
4413 /* Pollution, fallout. */
4414 extra_type_by_rmcause_iterate(ERM_CLEANPOLLUTION, pextra) {
4415 if (help_is_extra_cleanable(pextra, utype)) {
4416 strvec_append(extras_vec, extra_name_translation(pextra));
4417 }
4418 } extra_type_by_rmcause_iterate_end;
4419
4420 if (strvec_size(extras_vec) > 0) {
4421 strvec_to_and_list(extras_vec, &extras_and);
4422 /* TRANS: list of extras separated by "and" */
4423 cat_snprintf(buf, bufsz, _("* Can clean %s from tiles.\n"),
4424 astr_str(&extras_and));
4425 strvec_clear(extras_vec);
4426 }
4427
4428 extra_type_by_rmcause_iterate(ERM_CLEANFALLOUT, pextra) {
4429 if (help_is_extra_cleanable(pextra, utype)) {
4430 strvec_append(extras_vec, extra_name_translation(pextra));
4431 }
4432 } extra_type_by_rmcause_iterate_end;
4433
4434 if (strvec_size(extras_vec) > 0) {
4435 strvec_to_and_list(extras_vec, &extras_and);
4436 /* TRANS: list of extras separated by "and" */
4437 cat_snprintf(buf, bufsz, _("* Can clean %s from tiles.\n"),
4438 astr_str(&extras_and));
4439 strvec_clear(extras_vec);
4440 }
4441
4442 strvec_destroy(extras_vec);
4443 }
4444
4445 if (utype_has_flag(utype, UTYF_SPY)) {
4446 CATLSTR(buf, bufsz, _("* Performs better diplomatic actions.\n"));
4447 }
4448 if (utype_has_flag(utype, UTYF_DIPLOMAT)
4449 || utype_has_flag(utype, UTYF_SUPERSPY)) {
4450 CATLSTR(buf, bufsz, _("* Defends cities against diplomatic actions.\n"));
4451 }
4452 if (utype_has_flag(utype, UTYF_SUPERSPY)) {
4453 CATLSTR(buf, bufsz, _("* Will never lose a diplomat-versus-diplomat fight.\n"));
4454 }
4455 if (utype_has_flag(utype, UTYF_SPY)
4456 && utype_has_flag(utype, UTYF_SUPERSPY)) {
4457 CATLSTR(buf, bufsz, _("* Will always survive a spy mission.\n"));
4458 }
4459 if (utype_has_flag(utype, UTYF_PARTIAL_INVIS)) {
4460 CATLSTR(buf, bufsz,
4461 _("* Is invisible except when next to an enemy unit or city.\n"));
4462 }
4463 if (utype_has_flag(utype, UTYF_ONLY_NATIVE_ATTACK)) {
4464 CATLSTR(buf, bufsz,
4465 _("* Can only attack units on native tiles.\n"));
4466 }
4467 /* Must use flag to distinguish from UCF_ATT_FROM_NON_NATIVE text. */
4468 if (utype_has_flag(utype, UTYF_MARINES)) {
4469 CATLSTR(buf, bufsz,
4470 _("* Can launch attack from non-native tiles.\n"));
4471 }
4472 if (utype_has_flag(utype, UTYF_PARATROOPERS)) {
4473 cat_snprintf(buf, bufsz,
4474 PL_("* Can be paradropped from a friendly city or suitable "
4475 "base (range: %d tile).\n",
4476 "* Can be paradropped from a friendly city or suitable "
4477 "base (range: %d tiles).\n",
4478 utype->paratroopers_range),
4479 utype->paratroopers_range);
4480 }
4481 if (!uclass_has_flag(utype_class(utype), UCF_MISSILE)
4482 && utype_has_flag(utype, UTYF_ONEATTACK)) {
4483 CATLSTR(buf, bufsz,
4484 _("* Making an attack ends this unit's turn.\n"));
4485 }
4486 if (utype_has_flag(utype, UTYF_NUCLEAR)) {
4487 CATLSTR(buf, bufsz,
4488 _("* This unit's attack causes a nuclear explosion!\n"));
4489 }
4490 if (utype_has_flag(utype, UTYF_CITYBUSTER)) {
4491 CATLSTR(buf, bufsz,
4492 _("* Gets double firepower when attacking cities.\n"));
4493 }
4494 if (utype_has_flag(utype, UTYF_BOMBARDER)) {
4495 /* FIXME: also they only happen against land units. We leave the
4496 * ruleset author to document this. */
4497 cat_snprintf(buf, bufsz,
4498 _("* Does bombard attacks (%d per turn). These attacks will"
4499 " only damage (never kill) defenders, but damage all"
4500 " defenders on a tile, and have no risk for the"
4501 " attacker.\n"),
4502 utype->bombard_rate);
4503 }
4504 if (utype_has_flag(utype, UTYF_IGTER)) {
4505 cat_snprintf(buf, bufsz,
4506 /* TRANS: "MP" = movement points. %s may have a
4507 * fractional part. */
4508 _("* Ignores terrain effects (moving costs at most %s MP "
4509 "per tile).\n"),
4510 move_points_text(terrain_control.igter_cost, TRUE));
4511 }
4512 if (utype_has_flag(utype, UTYF_NOZOC)) {
4513 CATLSTR(buf, bufsz, _("* Never imposes a zone of control.\n"));
4514 } else {
4515 CATLSTR(buf, bufsz, _("* May impose a zone of control on its adjacent "
4516 "tiles.\n"));
4517 }
4518 if (utype_has_flag(utype, UTYF_IGZOC)) {
4519 CATLSTR(buf, bufsz, _("* Not subject to zones of control imposed "
4520 "by other units.\n"));
4521 }
4522 if (utype_has_flag(utype, UTYF_CIVILIAN)) {
4523 CATLSTR(buf, bufsz,
4524 _("* A non-military unit:\n"));
4525 CATLSTR(buf, bufsz,
4526 /* TRANS: indented; preserve leading spaces */
4527 _(" * Cannot attack.\n"));
4528 CATLSTR(buf, bufsz,
4529 /* TRANS: indented; preserve leading spaces */
4530 _(" * Doesn't impose martial law.\n"));
4531 CATLSTR(buf, bufsz,
4532 /* TRANS: indented; preserve leading spaces */
4533 _(" * Can enter foreign territory regardless of peace treaty.\n"));
4534 CATLSTR(buf, bufsz,
4535 /* TRANS: indented; preserve leading spaces */
4536 _(" * Doesn't prevent enemy cities from working the tile it's on.\n"));
4537 }
4538 if (utype_has_flag(utype, UTYF_FIELDUNIT)) {
4539 CATLSTR(buf, bufsz,
4540 _("* A field unit: one unhappiness applies even when non-aggressive.\n"));
4541 }
4542 if (utype_has_flag(utype, UTYF_CAPTURER)) {
4543 CATLSTR(buf, bufsz, _("* Can capture some enemy units.\n"));
4544 }
4545 if (utype_has_flag(utype, UTYF_CAPTURABLE)) {
4546 CATLSTR(buf, bufsz, _("* Can be captured by some enemy units.\n"));
4547 }
4548 if (utype_has_flag(utype, UTYF_SHIELD2GOLD)) {
4549 /* FIXME: the conversion shield => gold is activated if
4550 * EFT_SHIELD2GOLD_FACTOR is not equal null; how to determine
4551 * possible sources? */
4552 CATLSTR(buf, bufsz,
4553 _("* Under certain conditions the shield upkeep of this unit can "
4554 "be converted to gold upkeep.\n"));
4555 }
4556
4557 unit_class_iterate(target) {
4558 if (uclass_has_flag(target, UCF_UNREACHABLE)
4559 && BV_ISSET(utype->targets, uclass_index(target))) {
4560 cat_snprintf(buf, bufsz,
4561 _("* Can attack against %s units, which are usually not "
4562 "reachable.\n"),
4563 uclass_name_translation(target));
4564 }
4565 } unit_class_iterate_end;
4566 if (utype_fuel(utype)) {
4567 const char *types[utype_count()];
4568 int i = 0;
4569
4570 unit_type_iterate(transport) {
4571 if (can_unit_type_transport(transport, utype_class(utype))) {
4572 types[i++] = utype_name_translation(transport);
4573 }
4574 } unit_type_iterate_end;
4575
4576 if (0 == i) {
4577 cat_snprintf(buf, bufsz,
4578 PL_("* Unit has to be in a city or a base"
4579 " after %d turn.\n",
4580 "* Unit has to be in a city or a base"
4581 " after %d turns.\n",
4582 utype_fuel(utype)),
4583 utype_fuel(utype));
4584 } else {
4585 struct astring list = ASTRING_INIT;
4586
4587 cat_snprintf(buf, bufsz,
4588 /* TRANS: %s is a list of unit types separated by "or" */
4589 PL_("* Unit has to be in a city, a base, or on a %s"
4590 " after %d turn.\n",
4591 "* Unit has to be in a city, a base, or on a %s"
4592 " after %d turns.\n",
4593 utype_fuel(utype)),
4594 astr_build_or_list(&list, types, i), utype_fuel(utype));
4595 astr_free(&list);
4596 }
4597 }
4598 action_iterate(act) {
4599 if (action_by_number(act)->quiet) {
4600 /* The ruleset documents this action it self. */
4601 continue;
4602 }
4603
4604 if (action_id_get_actor_kind(act) != AAK_UNIT) {
4605 continue;
4606 }
4607
4608 action_enabler_list_iterate(action_enablers_for_action(act), enabler) {
4609 if (requirement_fulfilled_by_unit_type(utype,
4610 &(enabler->actor_reqs))) {
4611 switch (act) {
4612 case ACTION_HELP_WONDER:
4613 cat_snprintf(buf, bufsz,
4614 /* TRANS: the first %s is the ruleset defined ui
4615 * name of the "Help Wonder" action, the next %s is
4616 * the name of its target kind ("individual cities")
4617 * and the %d is the number of shields the unit can
4618 * contribute. */
4619 _("* Can do the action \'%s\' to some %s"
4620 " (adds %d production).\n"),
4621 /* The action may have a ruleset defined ui name. */
4622 action_id_name_translation(act),
4623 /* Keep the style consistent with the help for the
4624 * other actions. */
4625 _(action_target_kind_name(
4626 action_id_get_target_kind(act))),
4627 /* The custom information. */
4628 utype_build_shield_cost(utype));
4629 break;
4630 default:
4631 /* Generic action information. */
4632 cat_snprintf(buf, bufsz,
4633 /* TRANS: the first %s is the action's ruleset
4634 * defined ui name and the next %s is the name of
4635 * its target kind. */
4636 _("* Can do the action \'%s\' to some %s.\n"),
4637 action_id_name_translation(act),
4638 _(action_target_kind_name(
4639 action_id_get_target_kind(act))));
4640 break;
4641 }
4642
4643 /* The unit's ability to perform this action was just documented.
4644 * Move on to check if the unit can perform the next action too. */
4645 break;
4646 }
4647 } action_enabler_list_iterate_end;
4648 } action_iterate_end;
4649 action_iterate(act) {
4650 bool vulnerable;
4651
4652 if (action_by_number(act)->quiet) {
4653 /* The ruleset documents this action it self. */
4654 continue;
4655 }
4656
4657 /* Not relevant */
4658 if (action_id_get_target_kind(act) != ATK_UNIT) {
4659 continue;
4660 }
4661
4662 /* All units are immune to this since its not enabled */
4663 if (action_enabler_list_size(action_enablers_for_action(act)) == 0) {
4664 continue;
4665 }
4666
4667 /* Must be immune in all cases */
4668 vulnerable = FALSE;
4669 action_enabler_list_iterate(action_enablers_for_action(act), enabler) {
4670 if (requirement_fulfilled_by_unit_type(utype,
4671 &(enabler->target_reqs))) {
4672 vulnerable = TRUE;
4673 break;
4674 }
4675 } action_enabler_list_iterate_end;
4676
4677 if (!vulnerable) {
4678 cat_snprintf(buf, bufsz,
4679 _("* Doing the action \'%s\' to this unit"
4680 " is impossible.\n"),
4681 action_id_name_translation(act));
4682 }
4683 } action_iterate_end;
4684 if (!has_vet_levels) {
4685 /* Only mention this if the game generally does have veteran levels. */
4686 if (game.veteran->levels > 1) {
4687 CATLSTR(buf, bufsz, _("* Will never achieve veteran status.\n"));
4688 }
4689 } else {
4690 /* Not useful currently: */
4691 #if 0
4692 /* Some units can never become veteran through combat in practice. */
4693 bool veteran_through_combat =
4694 !((utype->attack_strength == 0
4695 || uclass_has_flag(utype_class(utype), UCF_MISSILE))
4696 && utype->defense_strength == 0);
4697 #endif
4698 /* FIXME: if we knew the raise chances on the client, we could be
4699 * more specific here about whether veteran status can be acquired
4700 * through combat/missions/work. Should also take into account
4701 * UTYF_NO_VETERAN when writing this text. (Gna patch #4794) */
4702 CATLSTR(buf, bufsz, _("* May acquire veteran status.\n"));
4703 if (utype_veteran_has_power_bonus(utype)) {
4704 if ((!utype_has_flag(utype, UTYF_NUCLEAR) && utype->attack_strength > 0)
4705 || utype->defense_strength > 0) {
4706 CATLSTR(buf, bufsz,
4707 /* TRANS: indented; preserve leading spaces */
4708 _(" * Veterans have increased strength in combat.\n"));
4709 }
4710 /* SUPERSPY always wins/escapes */
4711 if ((utype_has_flag(utype, UTYF_DIPLOMAT)
4712 || utype_has_flag(utype, UTYF_SPY))
4713 && !utype_has_flag(utype, UTYF_SUPERSPY)) {
4714 CATLSTR(buf, bufsz,
4715 /* TRANS: indented; preserve leading spaces */
4716 _(" * Veterans have improved chances in diplomatic "
4717 "contests.\n"));
4718 if (utype_has_flag(utype, UTYF_SPY) &&
4719 (utype_can_do_action(utype, ACTION_SPY_POISON)
4720 || utype_can_do_action(utype, ACTION_SPY_SABOTAGE_UNIT)
4721 || utype_can_do_action(utype, ACTION_SPY_STEAL_TECH)
4722 || utype_can_do_action(utype, ACTION_SPY_TARGETED_STEAL_TECH)
4723 || utype_can_do_action(utype, ACTION_SPY_INCITE_CITY)
4724 || utype_can_do_action(utype, ACTION_SPY_SABOTAGE_CITY)
4725 || utype_can_do_action(utype, ACTION_SPY_TARGETED_SABOTAGE_CITY)
4726 || utype_can_do_action(utype, ACTION_SPY_STEAL_GOLD))) {
4727 CATLSTR(buf, bufsz,
4728 /* TRANS: indented; preserve leading spaces */
4729 _(" * Veterans are more likely to survive missions.\n"));
4730 }
4731 }
4732 if (utype_has_flag(utype, UTYF_SETTLERS)) {
4733 CATLSTR(buf, bufsz,
4734 /* TRANS: indented; preserve leading spaces */
4735 _(" * Veterans work faster.\n"));
4736 }
4737 }
4738 }
4739 if (strlen(buf) > 0) {
4740 CATLSTR(buf, bufsz, "\n");
4741 }
4742 if (has_vet_levels && utype->veteran) {
4743 /* The case where the unit has only a single veteran level has already
4744 * been handled above, so keep quiet here if that happens */
4745 if (insert_veteran_help(buf, bufsz, utype->veteran,
4746 _("This type of unit has its own veteran levels:"), NULL)) {
4747 CATLSTR(buf, bufsz, "\n\n");
4748 }
4749 }
4750 if (NULL != utype->helptext) {
4751 strvec_iterate(utype->helptext, text) {
4752 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4753 } strvec_iterate_end;
4754 }
4755 CATLSTR(buf, bufsz, user_text);
4756 return buf;
4757 }
4758
4759 /****************************************************************************
4760 Append misc dynamic text for advance/technology.
4761
4762 pplayer may be NULL.
4763 ****************************************************************************/
helptext_advance(char * buf,size_t bufsz,struct player * pplayer,const char * user_text,int i)4764 void helptext_advance(char *buf, size_t bufsz, struct player *pplayer,
4765 const char *user_text, int i)
4766 {
4767 struct astring astr = ASTRING_INIT;
4768 struct advance *vap = valid_advance_by_number(i);
4769 struct universal source = {
4770 .kind = VUT_ADVANCE,
4771 .value = {.advance = vap}
4772 };
4773 int flagid;
4774
4775 fc_assert_ret(NULL != buf && 0 < bufsz && NULL != user_text);
4776 fc_strlcpy(buf, user_text, bufsz);
4777
4778 if (NULL == vap) {
4779 log_error("Unknown tech %d.", i);
4780 return;
4781 }
4782
4783 if (NULL != pplayer) {
4784 const struct research *presearch = research_get(pplayer);
4785
4786 if (research_invention_state(presearch, i) != TECH_KNOWN) {
4787 if (research_invention_state(presearch, i) == TECH_PREREQS_KNOWN) {
4788 int bulbs = research_total_bulbs_required(presearch, i, FALSE);
4789
4790 cat_snprintf(buf, bufsz,
4791 PL_("Starting now, researching %s would need %d bulb.",
4792 "Starting now, researching %s would need %d bulbs.",
4793 bulbs),
4794 advance_name_translation(vap), bulbs);
4795 } else if (research_invention_reachable(presearch, i)) {
4796 /* Split string into two to allow localization of two pluralizations. */
4797 char buf2[MAX_LEN_MSG];
4798 int bulbs = research_goal_bulbs_required(presearch, i);
4799
4800 fc_snprintf(buf2, ARRAY_SIZE(buf2),
4801 /* TRANS: appended to another sentence. Preserve the
4802 * leading space. */
4803 PL_(" The whole project will require %d bulb to complete.",
4804 " The whole project will require %d bulbs to complete.",
4805 bulbs),
4806 bulbs);
4807 cat_snprintf(buf, bufsz,
4808 /* TRANS: last %s is a sentence pluralized separately. */
4809 PL_("To research %s you need to research %d other"
4810 " technology first.%s",
4811 "To research %s you need to research %d other"
4812 " technologies first.%s",
4813 research_goal_unknown_techs(presearch, i) - 1),
4814 advance_name_translation(vap),
4815 research_goal_unknown_techs(presearch, i) - 1, buf2);
4816 } else {
4817 CATLSTR(buf, bufsz,
4818 _("You cannot research this technology."));
4819 }
4820 if (!techs_have_fixed_costs()
4821 && research_invention_reachable(presearch, i)) {
4822 CATLSTR(buf, bufsz,
4823 /* TRANS: preserve leading space */
4824 _(" This number may vary depending on what "
4825 "other players research.\n"));
4826 } else {
4827 CATLSTR(buf, bufsz, "\n");
4828 }
4829 }
4830
4831 CATLSTR(buf, bufsz, "\n");
4832 }
4833
4834 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf),
4835 /* TRANS: bullet point; note trailing space */
4836 Q_("?bullet:* "));
4837
4838 {
4839 int j;
4840
4841 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
4842 if (game.rgame.global_init_techs[j] == A_LAST) {
4843 break;
4844 } else if (game.rgame.global_init_techs[j] == i) {
4845 CATLSTR(buf, bufsz,
4846 _("* All players start the game with knowledge of this "
4847 "technology.\n"));
4848 break;
4849 }
4850 }
4851 }
4852
4853 /* Assume no-one will set the same tech in both global and nation
4854 * init_tech... */
4855 nations_iterate(pnation) {
4856 int j;
4857
4858 /* Avoid mentioning nations not in current set. */
4859 if (!show_help_for_nation(pnation)) {
4860 continue;
4861 }
4862 for (j = 0; j < MAX_NUM_TECH_LIST; j++) {
4863 if (pnation->init_techs[j] == A_LAST) {
4864 break;
4865 } else if (pnation->init_techs[j] == i) {
4866 cat_snprintf(buf, bufsz,
4867 /* TRANS: %s is a nation plural */
4868 _("* The %s start the game with knowledge of this "
4869 "technology.\n"), nation_plural_translation(pnation));
4870 break;
4871 }
4872 }
4873 } nations_iterate_end;
4874
4875 /* Explain the effects of root_reqs. */
4876 {
4877 bv_techs roots, rootsofroots;
4878
4879 BV_CLR_ALL(roots);
4880 BV_CLR_ALL(rootsofroots);
4881 advance_root_req_iterate(vap, proot) {
4882 if (proot == vap) {
4883 /* Don't say anything at all if this tech is a self-root-req one;
4884 * assume that the ruleset help will explain how to get it. */
4885 BV_CLR_ALL(roots);
4886 break;
4887 }
4888 BV_SET(roots, advance_number(proot));
4889 if (advance_requires(proot, AR_ROOT) != proot) {
4890 /* Now find out what roots each of this tech's root_req has, so that
4891 * we can suppress them. If tech A has roots B/C, and B has root C,
4892 * it's not worth saying that A needs C, and can lead to overwhelming
4893 * lists. */
4894 /* (Special case: don't do this if the root is a self-root-req tech,
4895 * since it would appear in its own root iteration; in the scenario
4896 * where S is a self-root tech that is root for T, this would prevent
4897 * S appearing in T's help.) */
4898 /* FIXME this is quite inefficient */
4899 advance_root_req_iterate(proot, prootroot) {
4900 BV_SET(rootsofroots, advance_number(prootroot));
4901 } advance_root_req_iterate_end;
4902 }
4903 } advance_root_req_iterate_end;
4904
4905 /* Filter out all but the direct root reqs. */
4906 BV_CLR_ALL_FROM(roots, rootsofroots);
4907
4908 if (BV_ISSET_ANY(roots)) {
4909 const char *root_techs[A_LAST];
4910 size_t n_roots = 0;
4911 struct astring root_list = ASTRING_INIT;
4912
4913 advance_index_iterate(A_FIRST, root) {
4914 if (BV_ISSET(roots, root)) {
4915 root_techs[n_roots++]
4916 = advance_name_translation(advance_by_number(root));
4917 }
4918 } advance_index_iterate_end;
4919 fc_assert(n_roots > 0);
4920 cat_snprintf(buf, bufsz,
4921 /* TRANS: 'and'-separated list of techs */
4922 _("* Only those who know %s can acquire this "
4923 "technology (by any means).\n"),
4924 astr_build_and_list(&root_list, root_techs, n_roots));
4925 astr_free(&root_list);
4926 }
4927 }
4928
4929 if (advance_has_flag(i, TF_BONUS_TECH)) {
4930 cat_snprintf(buf, bufsz,
4931 _("* The first player to learn %s gets"
4932 " an immediate advance.\n"),
4933 advance_name_translation(vap));
4934 }
4935
4936 for (flagid = TECH_USER_1 ; flagid <= TECH_USER_LAST; flagid++) {
4937 if (advance_has_flag(i, flagid)) {
4938 const char *helptxt = tech_flag_helptxt(flagid);
4939
4940 if (helptxt != NULL) {
4941 /* TRANS: bullet point; note trailing space */
4942 CATLSTR(buf, bufsz, Q_("?bullet:* "));
4943 CATLSTR(buf, bufsz, _(helptxt));
4944 CATLSTR(buf, bufsz, "\n");
4945 }
4946 }
4947 }
4948
4949 /* FIXME: bases -- but there is no good way to find out which bases a tech
4950 * can enable currently, so we have to remain silent. */
4951
4952 if (game.info.tech_upkeep_style != TECH_UPKEEP_NONE) {
4953 CATLSTR(buf, bufsz,
4954 _("* To preserve this technology for our nation some bulbs "
4955 "are needed each turn.\n"));
4956 }
4957
4958 if (NULL != vap->helptext) {
4959 if (strlen(buf) > 0) {
4960 CATLSTR(buf, bufsz, "\n");
4961 }
4962 strvec_iterate(vap->helptext, text) {
4963 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
4964 } strvec_iterate_end;
4965 }
4966
4967 astr_free(&astr);
4968 }
4969
4970 /****************************************************************
4971 Append text for terrain.
4972 *****************************************************************/
helptext_terrain(char * buf,size_t bufsz,struct player * pplayer,const char * user_text,struct terrain * pterrain)4973 void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer,
4974 const char *user_text, struct terrain *pterrain)
4975 {
4976 struct universal source = {
4977 .kind = VUT_TERRAIN,
4978 .value = {.terrain = pterrain}
4979 };
4980 int flagid;
4981
4982 fc_assert_ret(NULL != buf && 0 < bufsz);
4983 buf[0] = '\0';
4984
4985 if (!pterrain) {
4986 log_error("Unknown terrain!");
4987 return;
4988 }
4989
4990 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf),
4991 /* TRANS: bullet point; note trailing space */
4992 Q_("?bullet:* "));
4993 if (terrain_has_flag(pterrain, TER_NO_CITIES)) {
4994 CATLSTR(buf, bufsz,
4995 _("* You cannot build cities on this terrain."));
4996 CATLSTR(buf, bufsz, "\n");
4997 }
4998 if (pterrain->road_time == 0) {
4999 /* Can't build roads; only mention if ruleset has buildable roads */
5000 extra_type_by_cause_iterate(EC_ROAD, pextra) {
5001 if (pextra->buildable) {
5002 CATLSTR(buf, bufsz,
5003 _("* Paths cannot be built on this terrain."));
5004 CATLSTR(buf, bufsz, "\n");
5005 break;
5006 }
5007 } extra_type_by_cause_iterate_end;
5008 }
5009 if (pterrain->base_time == 0) {
5010 /* Can't build bases; only mention if ruleset has buildable bases */
5011 extra_type_by_cause_iterate(EC_BASE, pextra) {
5012 if (pextra->buildable) {
5013 CATLSTR(buf, bufsz,
5014 _("* Bases cannot be built on this terrain."));
5015 CATLSTR(buf, bufsz, "\n");
5016 break;
5017 }
5018 } extra_type_by_cause_iterate_end;
5019 }
5020 if (terrain_has_flag(pterrain, TER_UNSAFE_COAST)
5021 && terrain_type_terrain_class(pterrain) != TC_OCEAN) {
5022 CATLSTR(buf, bufsz,
5023 _("* The coastline of this terrain is unsafe."));
5024 CATLSTR(buf, bufsz, "\n");
5025 }
5026 {
5027 const char *classes[uclass_count()];
5028 int i = 0;
5029
5030 unit_class_iterate(uclass) {
5031 if (is_native_to_class(uclass, pterrain, NULL)) {
5032 classes[i++] = uclass_name_translation(uclass);
5033 }
5034 } unit_class_iterate_end;
5035
5036 if (0 < i) {
5037 struct astring list = ASTRING_INIT;
5038
5039 /* TRANS: %s is a list of unit classes separated by "and". */
5040 cat_snprintf(buf, bufsz, _("* Can be traveled by %s units.\n"),
5041 astr_build_and_list(&list, classes, i));
5042 astr_free(&list);
5043 }
5044 }
5045 if (terrain_has_flag(pterrain, TER_NO_ZOC)) {
5046 CATLSTR(buf, bufsz,
5047 _("* Units on this terrain neither impose zones of control "
5048 "nor are restricted by them.\n"));
5049 } else {
5050 CATLSTR(buf, bufsz,
5051 _("* Units on this terrain may impose a zone of control, or "
5052 "be restricted by one.\n"));
5053 }
5054 if (terrain_has_flag(pterrain, TER_NO_FORTIFY)) {
5055 CATLSTR(buf, bufsz,
5056 _("* No units can fortify on this terrain.\n"));
5057 } else {
5058 CATLSTR(buf, bufsz,
5059 _("* Units able to fortify may do so on this terrain.\n"));
5060 }
5061 for (flagid = TER_USER_1 ; flagid <= TER_USER_LAST; flagid++) {
5062 if (terrain_has_flag(pterrain, flagid)) {
5063 const char *helptxt = terrain_flag_helptxt(flagid);
5064
5065 if (helptxt != NULL) {
5066 /* TRANS: bullet point; note trailing space */
5067 CATLSTR(buf, bufsz, Q_("?bullet:* "));
5068 CATLSTR(buf, bufsz, _(helptxt));
5069 CATLSTR(buf, bufsz, "\n");
5070 }
5071 }
5072 }
5073
5074 if (NULL != pterrain->helptext) {
5075 if (buf[0] != '\0') {
5076 CATLSTR(buf, bufsz, "\n");
5077 }
5078 strvec_iterate(pterrain->helptext, text) {
5079 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
5080 } strvec_iterate_end;
5081 }
5082 if (user_text && user_text[0] != '\0') {
5083 CATLSTR(buf, bufsz, "\n\n");
5084 CATLSTR(buf, bufsz, user_text);
5085 }
5086 }
5087
5088 /****************************************************************************
5089 Return a textual representation of the F/P/T bonus a road provides to a
5090 terrain if supplied, or the terrain-independent bonus if pterrain==NULL.
5091 e.g. "0/0/+1", "0/+50%/0", or for a complex road "+2/+1+50%/0".
5092 Returns a pointer to a static string, so caller should not free
5093 (or NULL if there is no effect at all).
5094 ****************************************************************************/
helptext_road_bonus_str(const struct terrain * pterrain,const struct road_type * proad)5095 const char *helptext_road_bonus_str(const struct terrain *pterrain,
5096 const struct road_type *proad)
5097 {
5098 static char str[64];
5099 str[0] = '\0';
5100 bool has_effect = FALSE;
5101 output_type_iterate(o) {
5102 switch (o) {
5103 case O_FOOD:
5104 case O_SHIELD:
5105 case O_TRADE:
5106 {
5107 int bonus = proad->tile_bonus[o];
5108 int incr = proad->tile_incr_const[o];
5109 if (pterrain) {
5110 incr +=
5111 proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
5112 }
5113 if (str[0] != '\0') {
5114 CATLSTR(str, sizeof(str), "/");
5115 }
5116 if (incr == 0 && bonus == 0) {
5117 cat_snprintf(str, sizeof(str), "%d", incr);
5118 } else {
5119 has_effect = TRUE;
5120 if (incr != 0) {
5121 cat_snprintf(str, sizeof(str), "%+d", incr);
5122 }
5123 if (bonus != 0) {
5124 cat_snprintf(str, sizeof(str), "%+d%%", bonus);
5125 }
5126 }
5127 }
5128 break;
5129 default:
5130 /* FIXME: there's nothing actually stopping roads having gold, etc
5131 * bonuses */
5132 fc_assert(proad->tile_incr_const[o] == 0
5133 && proad->tile_incr[o] == 0
5134 && proad->tile_bonus[o] == 0);
5135 break;
5136 }
5137 } output_type_iterate_end;
5138
5139 return has_effect ? str : NULL;
5140 }
5141
5142 /**********************************************************************//**
5143 Calculate any fixed food/prod/trade bonus that 'pextra' will always add
5144 to terrain type, independent of any other modifications. Does not
5145 consider percentage bonuses.
5146 Result written into 'bonus' which should hold 3 ints (F/P/T).
5147 **************************************************************************/
extra_bonus_for_terrain(struct extra_type * pextra,struct terrain * pterrain,int * bonus)5148 static void extra_bonus_for_terrain(struct extra_type *pextra,
5149 struct terrain *pterrain,
5150 int *bonus)
5151 {
5152 struct universal req_pattern[] = {
5153 { .kind = VUT_EXTRA, .value.extra = pextra },
5154 { .kind = VUT_TERRAIN, .value.terrain = pterrain },
5155 { .kind = VUT_OTYPE /* value filled in later */ }
5156 };
5157
5158 fc_assert_ret(bonus != NULL);
5159
5160 /* Irrigation-like food bonuses */
5161 bonus[0] = (pterrain->irrigation_food_incr
5162 * effect_value_from_universals(EFT_IRRIGATION_PCT, req_pattern,
5163 2 /* just extra+terrain */)) / 100;
5164
5165 /* Mining-like shield bonuses */
5166 bonus[1] = (pterrain->mining_shield_incr
5167 * effect_value_from_universals(EFT_MINING_PCT, req_pattern,
5168 2 /* just extra+terrain */)) / 100;
5169
5170 bonus[2] = 0; /* no trade bonuses so far */
5171
5172 /* Now add fixed bonuses from roads (but not percentage bonus) */
5173 if (extra_road_get(pextra)) {
5174 const struct road_type *proad = extra_road_get(pextra);
5175
5176 output_type_iterate(o) {
5177 switch (o) {
5178 case O_FOOD:
5179 case O_SHIELD:
5180 case O_TRADE:
5181 bonus[o] += proad->tile_incr_const[o]
5182 + proad->tile_incr[o] * pterrain->road_output_incr_pct[o] / 100;
5183 break;
5184 default:
5185 /* not dealing with other output types here */
5186 break;
5187 }
5188 } output_type_iterate_end;
5189 }
5190
5191 /* Fixed bonuses for extra, possibly unrelated to terrain type */
5192
5193 output_type_iterate(o) {
5194 /* Fill in rest of requirement template */
5195 req_pattern[2].value.outputtype = o;
5196 switch (o) {
5197 case O_FOOD:
5198 case O_SHIELD:
5199 case O_TRADE:
5200 bonus[o] += effect_value_from_universals(EFT_OUTPUT_ADD_TILE,
5201 req_pattern,
5202 ARRAY_SIZE(req_pattern));
5203 /* Any of the above bonuses is sufficient to trigger
5204 * Output_Inc_Tile, if underlying terrain does not */
5205 if (bonus[o] > 0 || pterrain->output[o] > 0) {
5206 bonus[o] += effect_value_from_universals(EFT_OUTPUT_INC_TILE,
5207 req_pattern,
5208 ARRAY_SIZE(req_pattern));
5209 }
5210 break;
5211 default:
5212 break;
5213 }
5214 } output_type_iterate_end;
5215 }
5216
5217 /**********************************************************************//**
5218 Return a brief description specific to the extra and terrain, when
5219 extra is built by cause 'act'.
5220 Returns number of turns to build, and selected bonuses.
5221 Returns a pointer to a static string, so caller should not free.
5222 **************************************************************************/
helptext_extra_for_terrain_str(struct extra_type * pextra,struct terrain * pterrain,enum unit_activity act)5223 const char *helptext_extra_for_terrain_str(struct extra_type *pextra,
5224 struct terrain *pterrain,
5225 enum unit_activity act)
5226 {
5227 static char buffer[256];
5228 int btime;
5229 int bonus[3];
5230
5231 btime = terrain_extra_build_time(pterrain, act, pextra);
5232 fc_snprintf(buffer, sizeof(buffer), PL_("%d turn", "%d turns", btime),
5233 btime);
5234 extra_bonus_for_terrain(pextra, pterrain, bonus);
5235 if (bonus[0] > 0) {
5236 cat_snprintf(buffer, sizeof(buffer),
5237 PL_(", +%d food", ", +%d food", bonus[0]), bonus[0]);
5238 }
5239 if (bonus[1] > 0) {
5240 cat_snprintf(buffer, sizeof(buffer),
5241 PL_(", +%d shield", ", +%d shields", bonus[1]), bonus[1]);
5242 }
5243 if (bonus[2] > 0) {
5244 cat_snprintf(buffer, sizeof(buffer),
5245 PL_(", +%d trade", ", +%d trade", bonus[2]), bonus[2]);
5246 }
5247
5248 return buffer;
5249 }
5250
5251 /****************************************************************************
5252 Append misc dynamic text for extras.
5253 Assumes build time and conflicts are handled in the GUI front-end.
5254
5255 pplayer may be NULL.
5256 ****************************************************************************/
helptext_extra(char * buf,size_t bufsz,struct player * pplayer,const char * user_text,struct extra_type * pextra)5257 void helptext_extra(char *buf, size_t bufsz, struct player *pplayer,
5258 const char *user_text, struct extra_type *pextra)
5259 {
5260 size_t group_start;
5261 struct base_type *pbase;
5262 struct road_type *proad;
5263 struct universal source = {
5264 .kind = VUT_EXTRA,
5265 .value = {.extra = pextra}
5266 };
5267
5268 fc_assert_ret(NULL != buf && 0 < bufsz);
5269 buf[0] = '\0';
5270
5271 if (!pextra) {
5272 log_error("Unknown extra!");
5273 return;
5274 }
5275
5276 if (is_extra_caused_by(pextra, EC_BASE)) {
5277 pbase = pextra->data.base;
5278 } else {
5279 pbase = NULL;
5280 }
5281
5282 if (is_extra_caused_by(pextra, EC_ROAD)) {
5283 proad = pextra->data.road;
5284 } else {
5285 proad = NULL;
5286 }
5287
5288 if (pextra->helptext != NULL) {
5289 strvec_iterate(pextra->helptext, text) {
5290 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
5291 } strvec_iterate_end;
5292 }
5293
5294 /* Describe how extra is created and destroyed */
5295
5296 group_start = strlen(buf);
5297
5298 if (pextra->buildable) {
5299 if (is_extra_caused_by(pextra, EC_IRRIGATION)) {
5300 CATLSTR(buf, bufsz,
5301 _("Build by issuing an \"irrigate\" order.\n"));
5302 }
5303 if (is_extra_caused_by(pextra, EC_MINE)) {
5304 CATLSTR(buf, bufsz,
5305 _("Build by issuing a \"mine\" order.\n"));
5306 }
5307 if (is_extra_caused_by(pextra, EC_ROAD)) {
5308 CATLSTR(buf, bufsz,
5309 _("Build by issuing a \"road\" order.\n"));
5310 }
5311 if (is_extra_caused_by(pextra, EC_BASE)) {
5312 fc_assert(pbase);
5313 if (pbase->gui_type == BASE_GUI_OTHER) {
5314 cat_snprintf(buf, bufsz,
5315 _("Build by issuing a \"build base\" order.\n"));
5316 } else {
5317 const char *order = "";
5318
5319 switch (pbase->gui_type) {
5320 case BASE_GUI_FORTRESS:
5321 order = Q_(terrain_control.gui_type_base0);
5322 break;
5323 case BASE_GUI_AIRBASE:
5324 order = Q_(terrain_control.gui_type_base1);
5325 break;
5326 default:
5327 fc_assert(FALSE);
5328 break;
5329 }
5330 cat_snprintf(buf, bufsz,
5331 /* TRANS: %s is a gui_type base string from a ruleset */
5332 _("Build by issuing a \"%s\" order.\n"), order);
5333 }
5334 }
5335 }
5336
5337 if (is_extra_caused_by(pextra, EC_POLLUTION)) {
5338 CATLSTR(buf, bufsz,
5339 _("May randomly appear around polluting city.\n"));
5340 }
5341
5342 if (is_extra_caused_by(pextra, EC_FALLOUT)) {
5343 CATLSTR(buf, bufsz,
5344 _("May randomly appear around nuclear blast.\n"));
5345 }
5346
5347 if (is_extra_caused_by(pextra, EC_HUT)
5348 || (proad != NULL && road_has_flag(proad, RF_RIVER))) {
5349 CATLSTR(buf, bufsz,
5350 _("Placed by map generator.\n"));
5351 }
5352
5353 if (requirement_vector_size(&pextra->reqs) > 0) {
5354 char reqsbuf[8192] = "";
5355 bool buildable = pextra->buildable
5356 && is_extra_caused_by_worker_action(pextra);
5357
5358 requirement_vector_iterate(&pextra->reqs, preq) {
5359 (void) insert_requirement(reqsbuf, sizeof(reqsbuf), pplayer, preq,
5360 /* TRANS: bullet point; note trailing space */
5361 buildable ? Q_("?bullet:* ") : "");
5362 } requirement_vector_iterate_end;
5363 if (reqsbuf[0] != '\0') {
5364 if (buildable) {
5365 CATLSTR(buf, bufsz, _("Requirements to build:\n"));
5366 }
5367 CATLSTR(buf, bufsz, reqsbuf);
5368 }
5369 }
5370
5371 if (buf[group_start] != '\0') {
5372 CATLSTR(buf, bufsz, "\n"); /* group separator */
5373 }
5374
5375 group_start = strlen(buf);
5376
5377 if (is_extra_removed_by(pextra, ERM_PILLAGE)) {
5378 int pillage_time = -1;
5379
5380 if (pextra->removal_time != 0) {
5381 pillage_time = pextra->removal_time;
5382 } else {
5383 terrain_type_iterate(pterrain) {
5384 int terr_pillage_time = pterrain->pillage_time
5385 * pextra->removal_time_factor;
5386
5387 if (terr_pillage_time != 0) {
5388 if (pillage_time < 0) {
5389 pillage_time = terr_pillage_time;
5390 } else if (pillage_time != terr_pillage_time) {
5391 /* Give up */
5392 pillage_time = -1;
5393 break;
5394 }
5395 }
5396 } terrain_type_iterate_end;
5397 }
5398 if (pillage_time < 0) {
5399 CATLSTR(buf, bufsz,
5400 _("Can be pillaged by units (time is terrain-dependent).\n"));
5401 } else if (pillage_time > 0) {
5402 cat_snprintf(buf, bufsz,
5403 PL_("Can be pillaged by units (takes %d turn).\n",
5404 "Can be pillaged by units (takes %d turns).\n",
5405 pillage_time), pillage_time);
5406 }
5407 }
5408 if (is_extra_removed_by(pextra, ERM_CLEANPOLLUTION)
5409 || is_extra_removed_by(pextra, ERM_CLEANFALLOUT)) {
5410 int clean_time = -1;
5411
5412 if (pextra->removal_time != 0) {
5413 clean_time = pextra->removal_time;
5414 } else {
5415 terrain_type_iterate(pterrain) {
5416 int terr_clean_time = -1;
5417
5418 if (is_extra_removed_by(pextra, ERM_CLEANPOLLUTION)
5419 && pterrain->clean_pollution_time != 0) {
5420 terr_clean_time = pterrain->clean_pollution_time
5421 * pextra->removal_time_factor;
5422 }
5423 if (is_extra_removed_by(pextra, ERM_CLEANFALLOUT)
5424 && pterrain->clean_fallout_time != 0) {
5425 int terr_clean_fall_time = pterrain->clean_fallout_time
5426 * pextra->removal_time_factor;
5427 if (terr_clean_time > 0
5428 && terr_clean_time != terr_clean_fall_time) {
5429 /* Pollution/fallout cleaning activities taking different time
5430 * on same terrain. Give up. */
5431 clean_time = -1;
5432 break;
5433 }
5434 terr_clean_time = terr_clean_fall_time;
5435 }
5436 if (clean_time < 0) {
5437 clean_time = terr_clean_time;
5438 } else if (clean_time != terr_clean_time) {
5439 /* Give up */
5440 clean_time = -1;
5441 break;
5442 }
5443 } terrain_type_iterate_end;
5444 }
5445 if (clean_time < 0) {
5446 CATLSTR(buf, bufsz,
5447 _("Can be cleaned by units (time is terrain-dependent).\n"));
5448 } else if (clean_time > 0) {
5449 cat_snprintf(buf, bufsz,
5450 PL_("Can be cleaned by units (takes %d turn).\n",
5451 "Can be cleaned by units (takes %d turns).\n",
5452 clean_time), clean_time);
5453 }
5454 }
5455
5456 if (buf[group_start] != '\0') {
5457 CATLSTR(buf, bufsz, "\n"); /* group separator */
5458 }
5459
5460 /* Describe what other elements are enabled by extra */
5461
5462 group_start = strlen(buf);
5463
5464 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf), "");
5465
5466 if (buf[group_start] != '\0') {
5467 CATLSTR(buf, bufsz, "\n"); /* group separator */
5468 }
5469
5470 /* Describe other properties of extras */
5471
5472 {
5473 const char *classes[uclass_count()];
5474 int i = 0;
5475
5476 unit_class_iterate(uclass) {
5477 if (is_native_extra_to_uclass(pextra, uclass)) {
5478 classes[i++] = uclass_name_translation(uclass);
5479 }
5480 } unit_class_iterate_end;
5481
5482 if (0 < i) {
5483 struct astring list = ASTRING_INIT;
5484
5485 if (proad != NULL) {
5486 /* TRANS: %s is a list of unit classes separated by "and". */
5487 cat_snprintf(buf, bufsz, _("* Can be traveled by %s units.\n"),
5488 astr_build_and_list(&list, classes, i));
5489 } else {
5490 /* TRANS: %s is a list of unit classes separated by "and". */
5491 cat_snprintf(buf, bufsz, _("* Native to %s units.\n"),
5492 astr_build_and_list(&list, classes, i));
5493 }
5494 astr_free(&list);
5495
5496 if (extra_has_flag(pextra, EF_NATIVE_TILE)) {
5497 CATLSTR(buf, bufsz,
5498 /* TRANS: indented; preserve leading spaces */
5499 _(" * Such units can move onto this tile even if it would "
5500 "not normally be suitable terrain.\n"));
5501 }
5502 if (pbase != NULL) {
5503 if (base_has_flag(pbase, BF_NOT_AGGRESSIVE)) {
5504 /* "3 tiles" is hardcoded in is_friendly_city_near() */
5505 CATLSTR(buf, bufsz,
5506 /* TRANS: indented; preserve leading spaces */
5507 _(" * Such units situated here are not considered aggressive "
5508 "if this tile is within 3 tiles of a friendly city.\n"));
5509 }
5510 if (territory_claiming_base(pbase)) {
5511 CATLSTR(buf, bufsz,
5512 /* TRANS: indented; preserve leading spaces */
5513 _(" * Can be captured by such units if at war with the "
5514 "nation that currently owns it.\n"));
5515 }
5516 }
5517 if (pextra->defense_bonus) {
5518 cat_snprintf(buf, bufsz,
5519 /* TRANS: indented; preserve leading spaces */
5520 _(" * Such units get a %d%% defense bonus on this "
5521 "tile.\n"),
5522 pextra->defense_bonus);
5523 }
5524 if (pbase != NULL) {
5525 if (base_has_flag(pbase, BF_DIPLOMAT_DEFENSE)) {
5526 CATLSTR(buf, bufsz,
5527 /* TRANS: indented; preserve leading spaces */
5528 /* xgettext:no-c-format */
5529 _(" * Diplomatic units get a 25% defense bonus in "
5530 "diplomatic fights.\n"));
5531 }
5532 }
5533 }
5534 }
5535
5536 if (proad != NULL && road_provides_move_bonus(proad)) {
5537 if (proad->move_cost == 0) {
5538 CATLSTR(buf, bufsz, _("* Allows infinite movement.\n"));
5539 } else {
5540 cat_snprintf(buf, bufsz,
5541 /* TRANS: "MP" = movement points. Second %s may have a
5542 * fractional part. */
5543 _("* Movement cost along %s is %s MP.\n"),
5544 extra_name_translation(pextra),
5545 move_points_text(proad->move_cost, TRUE));
5546 }
5547 }
5548
5549 if (pbase != NULL) {
5550 if (game.info.killstack
5551 && base_has_flag(pbase, BF_NO_STACK_DEATH)) {
5552 CATLSTR(buf, bufsz,
5553 _("* Defeat of one unit does not cause death of all other units "
5554 "on this tile.\n"));
5555 }
5556 if (base_has_flag(pbase, BF_PARADROP_FROM)) {
5557 CATLSTR(buf, bufsz,
5558 _("* Units can paradrop from this tile.\n"));
5559 }
5560 if (territory_claiming_base(pbase)) {
5561 CATLSTR(buf, bufsz,
5562 _("* Extends national borders of the building nation.\n"));
5563 }
5564 if (pbase->vision_main_sq >= 0) {
5565 CATLSTR(buf, bufsz,
5566 _("* Grants permanent vision of an area around the tile to "
5567 "its owner.\n"));
5568 }
5569 if (pbase->vision_invis_sq >= 0) {
5570 CATLSTR(buf, bufsz,
5571 _("* Allows the owner to see normally invisible units in an "
5572 "area around the tile.\n"));
5573 }
5574 }
5575
5576 /* Table of terrain-specific attributes, if needed */
5577 if (proad != NULL || pbase != NULL) {
5578 bool road, do_time, do_bonus;
5579
5580 road = (proad != NULL);
5581 /* Terrain-dependent build time? */
5582 do_time = pextra->buildable && pextra->build_time == 0;
5583 if (road) {
5584 /* Terrain-dependent output bonus? */
5585 do_bonus = FALSE;
5586 output_type_iterate(o) {
5587 if (proad->tile_incr[o] > 0) {
5588 do_bonus = TRUE;
5589 fc_assert(o == O_FOOD || o == O_SHIELD || o == O_TRADE);
5590 }
5591 } output_type_iterate_end;
5592 } else {
5593 /* Bases don't have output bonuses */
5594 do_bonus = FALSE;
5595 }
5596
5597 if (do_time || do_bonus) {
5598 if (do_time && do_bonus) {
5599 CATLSTR(buf, bufsz,
5600 _("\nTime to build and output bonus depends on terrain:\n\n"));
5601 CATLSTR(buf, bufsz,
5602 /* TRANS: Header for fixed-width road properties table.
5603 * TRANS: Translators cannot change column widths :( */
5604 _("Terrain Time Bonus F/P/T\n"
5605 "----------------------------------\n"));
5606 } else if (do_time) {
5607 CATLSTR(buf, bufsz,
5608 _("\nTime to build depends on terrain:\n\n"));
5609 CATLSTR(buf, bufsz,
5610 /* TRANS: Header for fixed-width extra properties table.
5611 * TRANS: Translators cannot change column widths :( */
5612 _("Terrain Time\n"
5613 "------------------\n"));
5614 } else {
5615 fc_assert(do_bonus);
5616 CATLSTR(buf, bufsz,
5617 /* TRANS: Header for fixed-width road properties table.
5618 * TRANS: Translators cannot change column widths :( */
5619 _("\nYields an output bonus with some terrains:\n\n"));
5620 CATLSTR(buf, bufsz,
5621 _("Terrain Bonus F/P/T\n"
5622 "-------------------------\n"));;
5623 }
5624 terrain_type_iterate(t) {
5625 int turns = road ? terrain_extra_build_time(t, ACTIVITY_GEN_ROAD, pextra)
5626 : terrain_extra_build_time(t, ACTIVITY_BASE, pextra);
5627 const char *bonus_text
5628 = road ? helptext_road_bonus_str(t, proad) : NULL;
5629 if (turns > 0 || bonus_text) {
5630 const char *terrain = terrain_name_translation(t);
5631
5632 cat_snprintf(buf, bufsz,
5633 "%s%*s ", terrain,
5634 MAX(0, 12 - (int)get_internal_string_length(terrain)),
5635 "");
5636 if (do_time) {
5637 if (turns > 0) {
5638 cat_snprintf(buf, bufsz, "%3d ", turns);
5639 } else {
5640 CATLSTR(buf, bufsz, " - ");
5641 }
5642 }
5643 if (do_bonus) {
5644 fc_assert(proad != NULL);
5645 cat_snprintf(buf, bufsz, " %s", bonus_text ? bonus_text : "-");
5646 }
5647 CATLSTR(buf, bufsz, "\n");
5648 }
5649 } terrain_type_iterate_end;
5650 } /* else rely on client-specific display */
5651 }
5652
5653 if (user_text && user_text[0] != '\0') {
5654 CATLSTR(buf, bufsz, "\n\n");
5655 CATLSTR(buf, bufsz, user_text);
5656 }
5657 }
5658
5659 /****************************************************************************
5660 Append misc dynamic text for specialists.
5661 Assumes effects are described in the help text.
5662
5663 pplayer may be NULL.
5664 ****************************************************************************/
helptext_specialist(char * buf,size_t bufsz,struct player * pplayer,const char * user_text,struct specialist * pspec)5665 void helptext_specialist(char *buf, size_t bufsz, struct player *pplayer,
5666 const char *user_text, struct specialist *pspec)
5667 {
5668 bool reqs = FALSE;
5669
5670 fc_assert_ret(NULL != buf && 0 < bufsz);
5671 buf[0] = '\0';
5672
5673 if (NULL != pspec->helptext) {
5674 strvec_iterate(pspec->helptext, text) {
5675 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
5676 } strvec_iterate_end;
5677 }
5678
5679 /* Requirements for this specialist. */
5680 requirement_vector_iterate(&pspec->reqs, preq) {
5681 if (insert_requirement(buf, bufsz, pplayer, preq, "")) {
5682 reqs = TRUE;
5683 }
5684 } requirement_vector_iterate_end;
5685 if (reqs) {
5686 fc_strlcat(buf, "\n", bufsz);
5687 }
5688
5689 CATLSTR(buf, bufsz, user_text);
5690 }
5691
5692 /****************************************************************
5693 Append text for government.
5694
5695 pplayer may be NULL.
5696
5697 TODO: Generalize the effects code for use elsewhere. Add
5698 other requirements.
5699 *****************************************************************/
helptext_government(char * buf,size_t bufsz,struct player * pplayer,const char * user_text,struct government * gov)5700 void helptext_government(char *buf, size_t bufsz, struct player *pplayer,
5701 const char *user_text, struct government *gov)
5702 {
5703 bool reqs = FALSE;
5704 struct universal source = {
5705 .kind = VUT_GOVERNMENT,
5706 .value = {.govern = gov}
5707 };
5708
5709 fc_assert_ret(NULL != buf && 0 < bufsz);
5710 buf[0] = '\0';
5711
5712 if (NULL != gov->helptext) {
5713 strvec_iterate(gov->helptext, text) {
5714 cat_snprintf(buf, bufsz, "%s\n\n", _(text));
5715 } strvec_iterate_end;
5716 }
5717
5718 /* Add requirement text for government itself */
5719 requirement_vector_iterate(&gov->reqs, preq) {
5720 if (insert_requirement(buf, bufsz, pplayer, preq, "")) {
5721 reqs = TRUE;
5722 }
5723 } requirement_vector_iterate_end;
5724 if (reqs) {
5725 fc_strlcat(buf, "\n", bufsz);
5726 }
5727
5728 /* Effects */
5729 CATLSTR(buf, bufsz, _("Features:\n"));
5730 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf),
5731 /* TRANS: bullet point; note trailing space */
5732 Q_("?bullet:* "));
5733 effect_list_iterate(get_req_source_effects(&source), peffect) {
5734 Output_type_id output_type = O_LAST;
5735 struct unit_class *unitclass = NULL;
5736 struct unit_type *unittype = NULL;
5737 enum unit_type_flag_id unitflag = unit_type_flag_id_invalid();
5738 struct strvec *outputs = strvec_new();
5739 struct astring outputs_or = ASTRING_INIT;
5740 struct astring outputs_and = ASTRING_INIT;
5741 bool too_complex = FALSE;
5742 bool world_value_valid = TRUE;
5743
5744 /* Grab output type, if there is one */
5745 requirement_vector_iterate(&peffect->reqs, preq) {
5746 /* Treat an effect with any negated requirements as too complex for
5747 * us to explain here.
5748 * Also don't try to explain an effect with any requirements explicitly
5749 * marked as 'quiet' by ruleset author. */
5750 if (!preq->present || preq->quiet) {
5751 too_complex = TRUE;
5752 continue;
5753 }
5754 switch (preq->source.kind) {
5755 case VUT_OTYPE:
5756 /* We should never have multiple outputtype requirements
5757 * in one list in the first place (it simply makes no sense,
5758 * output cannot be of multiple types)
5759 * Ruleset loading code should check against that. */
5760 fc_assert(output_type == O_LAST);
5761 output_type = preq->source.value.outputtype;
5762 strvec_append(outputs, get_output_name(output_type));
5763 break;
5764 case VUT_UCLASS:
5765 fc_assert(unitclass == NULL);
5766 unitclass = preq->source.value.uclass;
5767 /* FIXME: can't easily get world bonus for unit class */
5768 world_value_valid = FALSE;
5769 break;
5770 case VUT_UTYPE:
5771 fc_assert(unittype == NULL);
5772 unittype = preq->source.value.utype;
5773 break;
5774 case VUT_UTFLAG:
5775 if (!unit_type_flag_id_is_valid(unitflag)) {
5776 unitflag = preq->source.value.unitflag;
5777 /* FIXME: can't easily get world bonus for unit type flag */
5778 world_value_valid = FALSE;
5779 } else {
5780 /* Already have a unit flag requirement. More than one is too
5781 * complex for us to explain, so say nothing. */
5782 /* FIXME: we could handle this */
5783 too_complex = TRUE;
5784 }
5785 break;
5786 case VUT_GOVERNMENT:
5787 /* This is government we are generating helptext for.
5788 * ...or if not, it's ruleset bug that should never make it
5789 * this far. Fix ruleset loading code. */
5790 fc_assert(preq->source.value.govern == gov);
5791 break;
5792 default:
5793 too_complex = TRUE;
5794 world_value_valid = FALSE;
5795 break;
5796 };
5797 } requirement_vector_iterate_end;
5798
5799 if (!too_complex) {
5800 /* Only list effects that don't have extra requirements too complex
5801 * for us to handle.
5802 * Anything more complicated will have to be documented by hand by the
5803 * ruleset author. */
5804
5805 /* Guard condition for simple player-wide effects descriptions.
5806 * (FIXME: in many cases, e.g. EFT_MAKE_CONTENT, additional requirements
5807 * like unittype will be ignored for gameplay, but will affect our
5808 * help here.) */
5809 const bool playerwide
5810 = world_value_valid && !unittype && (output_type == O_LAST);
5811 /* In some cases we give absolute values (world bonus + gov bonus).
5812 * We assume the fact that there's an effect with a gov requirement
5813 * is sufficient reason to list it in that gov's help.
5814 * Guard accesses to these with 'playerwide' or 'world_value_valid'. */
5815 int world_value = -999, net_value = -999;
5816 if (world_value_valid) {
5817 /* Get government-independent world value of effect if the extra
5818 * requirements were simple enough. */
5819 struct output_type *potype =
5820 output_type != O_LAST ? get_output_type(output_type) : NULL;
5821 world_value =
5822 get_target_bonus_effects(NULL, NULL, NULL, NULL, NULL, NULL,
5823 NULL, unittype, potype, NULL,
5824 peffect->type);
5825 net_value = peffect->value + world_value;
5826 }
5827
5828 if (output_type == O_LAST) {
5829 /* There was no outputtype requirement. Effect is active for all
5830 * output types. Generate lists for that. */
5831 bool harvested_only = TRUE; /* Consider only output types from fields */
5832
5833 if (peffect->type == EFT_UPKEEP_FACTOR
5834 || peffect->type == EFT_UNIT_UPKEEP_FREE_PER_CITY
5835 || peffect->type == EFT_OUTPUT_BONUS
5836 || peffect->type == EFT_OUTPUT_BONUS_2) {
5837 /* Effect can use or require any kind of output */
5838 harvested_only = FALSE;
5839 }
5840
5841 output_type_iterate(ot) {
5842 struct output_type *pot = get_output_type(ot);
5843
5844 if (!harvested_only || pot->harvested) {
5845 strvec_append(outputs, _(pot->name));
5846 }
5847 } output_type_iterate_end;
5848 }
5849
5850 if (0 == strvec_size(outputs)) {
5851 /* TRANS: Empty output type list, should never happen. */
5852 astr_set(&outputs_or, "%s", Q_("?outputlist: Nothing "));
5853 astr_set(&outputs_and, "%s", Q_("?outputlist: Nothing "));
5854 } else {
5855 strvec_to_or_list(outputs, &outputs_or);
5856 strvec_to_and_list(outputs, &outputs_and);
5857 }
5858
5859 switch (peffect->type) {
5860 case EFT_UNHAPPY_FACTOR:
5861 if (playerwide) {
5862 /* FIXME: EFT_MAKE_CONTENT_MIL_PER would cancel this out. We assume
5863 * no-one will set both, so we don't bother handling it. */
5864 cat_snprintf(buf, bufsz,
5865 PL_("* Military units away from home and field units"
5866 " will each cause %d citizen to become unhappy.\n",
5867 "* Military units away from home and field units"
5868 " will each cause %d citizens to become unhappy.\n",
5869 net_value),
5870 net_value);
5871 } /* else too complicated or silly ruleset */
5872 break;
5873 case EFT_ENEMY_CITIZEN_UNHAPPY_PCT:
5874 if (playerwide && net_value != world_value) {
5875 if (world_value > 0) {
5876 if (net_value > 0) {
5877 cat_snprintf(buf, bufsz,
5878 _("* Unhappiness from foreign citizens due to "
5879 "war with their home state is %d%% the usual "
5880 "value.\n"),
5881 (net_value * 100) / world_value);
5882 } else {
5883 CATLSTR(buf, bufsz,
5884 _("* No unhappiness from foreign citizens even when "
5885 "at war with their home state.\n"));
5886 }
5887 } else {
5888 cat_snprintf(buf, bufsz,
5889 /* TRANS: not pluralised as gettext doesn't support
5890 * fractional numbers, which this might be */
5891 _("* Each foreign citizen causes %.2g unhappiness "
5892 "in their city while you are at war with their "
5893 "home state.\n"),
5894 (double)net_value / 100);
5895 }
5896 }
5897 break;
5898 case EFT_MAKE_CONTENT_MIL:
5899 if (playerwide) {
5900 cat_snprintf(buf, bufsz,
5901 PL_("* Each of your cities will avoid %d unhappiness"
5902 " caused by units.\n",
5903 "* Each of your cities will avoid %d unhappiness"
5904 " caused by units.\n",
5905 peffect->value),
5906 peffect->value);
5907 }
5908 break;
5909 case EFT_MAKE_CONTENT:
5910 if (playerwide) {
5911 cat_snprintf(buf, bufsz,
5912 PL_("* Each of your cities will avoid %d unhappiness,"
5913 " not including that caused by aggression.\n",
5914 "* Each of your cities will avoid %d unhappiness,"
5915 " not including that caused by aggression.\n",
5916 peffect->value),
5917 peffect->value);
5918 }
5919 break;
5920 case EFT_FORCE_CONTENT:
5921 if (playerwide) {
5922 cat_snprintf(buf, bufsz,
5923 PL_("* Each of your cities will avoid %d unhappiness,"
5924 " including that caused by aggression.\n",
5925 "* Each of your cities will avoid %d unhappiness,"
5926 " including that caused by aggression.\n",
5927 peffect->value),
5928 peffect->value);
5929 }
5930 break;
5931 case EFT_UPKEEP_FACTOR:
5932 if (world_value_valid && !unittype) {
5933 if (net_value == 0) {
5934 if (output_type != O_LAST) {
5935 cat_snprintf(buf, bufsz,
5936 /* TRANS: %s is the output type, like 'shield'
5937 * or 'gold'. */
5938 _("* You pay no %s upkeep for your units.\n"),
5939 astr_str(&outputs_or));
5940 } else {
5941 CATLSTR(buf, bufsz,
5942 _("* You pay no upkeep for your units.\n"));
5943 }
5944 } else if (net_value != world_value) {
5945 double ratio = (double)net_value / world_value;
5946 if (output_type != O_LAST) {
5947 cat_snprintf(buf, bufsz,
5948 /* TRANS: %s is the output type, like 'shield'
5949 * or 'gold'. */
5950 _("* You pay %.2g times normal %s upkeep for your "
5951 "units.\n"),
5952 ratio, astr_str(&outputs_and));
5953 } else {
5954 cat_snprintf(buf, bufsz,
5955 _("* You pay %.2g times normal upkeep for your "
5956 "units.\n"),
5957 ratio);
5958 }
5959 } /* else this effect somehow has no effect; keep quiet */
5960 } /* else there was some extra condition making it complicated */
5961 break;
5962 case EFT_UNIT_UPKEEP_FREE_PER_CITY:
5963 if (!unittype) {
5964 if (output_type != O_LAST) {
5965 cat_snprintf(buf, bufsz,
5966 /* TRANS: %s is the output type, like 'shield' or
5967 * 'gold'; pluralised in %d but there is currently
5968 * no way to control the singular/plural name of the
5969 * output type; sorry */
5970 PL_("* Each of your cities will avoid paying %d %s"
5971 " upkeep for your units.\n",
5972 "* Each of your cities will avoid paying %d %s"
5973 " upkeep for your units.\n", peffect->value),
5974 peffect->value, astr_str(&outputs_and));
5975 } else {
5976 cat_snprintf(buf, bufsz,
5977 /* TRANS: Amount is subtracted from upkeep cost
5978 * for each upkeep type. */
5979 PL_("* Each of your cities will avoid paying %d"
5980 " upkeep for your units.\n",
5981 "* Each of your cities will avoid paying %d"
5982 " upkeep for your units.\n", peffect->value),
5983 peffect->value);
5984 }
5985 } /* else too complicated */
5986 break;
5987 case EFT_CIVIL_WAR_CHANCE:
5988 if (playerwide) {
5989 cat_snprintf(buf, bufsz,
5990 _("* If you lose your capital,"
5991 " the base chance of civil war is %d%%.\n"),
5992 net_value);
5993 }
5994 break;
5995 case EFT_EMPIRE_SIZE_BASE:
5996 if (playerwide) {
5997 cat_snprintf(buf, bufsz,
5998 PL_("* You can have %d city before an "
5999 "additional unhappy citizen appears in each city "
6000 "due to civilization size.\n",
6001 "* You can have up to %d cities before an "
6002 "additional unhappy citizen appears in each city "
6003 "due to civilization size.\n", net_value),
6004 net_value);
6005 }
6006 break;
6007 case EFT_EMPIRE_SIZE_STEP:
6008 if (playerwide) {
6009 cat_snprintf(buf, bufsz,
6010 PL_("* After the first unhappy citizen due to"
6011 " civilization size, for each %d additional city"
6012 " another unhappy citizen will appear.\n",
6013 "* After the first unhappy citizen due to"
6014 " civilization size, for each %d additional cities"
6015 " another unhappy citizen will appear.\n",
6016 net_value),
6017 net_value);
6018 }
6019 break;
6020 case EFT_MAX_RATES:
6021 if (playerwide && game.info.changable_tax) {
6022 if (net_value < 100) {
6023 cat_snprintf(buf, bufsz,
6024 _("* The maximum rate you can set for science,"
6025 " gold, or luxuries is %d%%.\n"),
6026 net_value);
6027 } else {
6028 CATLSTR(buf, bufsz,
6029 _("* Has unlimited science/gold/luxuries rates.\n"));
6030 }
6031 }
6032 break;
6033 case EFT_MARTIAL_LAW_EACH:
6034 if (playerwide) {
6035 cat_snprintf(buf, bufsz,
6036 PL_("* Your units may impose martial law."
6037 " Each military unit inside a city will force %d"
6038 " unhappy citizen to become content.\n",
6039 "* Your units may impose martial law."
6040 " Each military unit inside a city will force %d"
6041 " unhappy citizens to become content.\n",
6042 peffect->value),
6043 peffect->value);
6044 }
6045 break;
6046 case EFT_MARTIAL_LAW_MAX:
6047 if (playerwide && net_value < 100) {
6048 cat_snprintf(buf, bufsz,
6049 PL_("* A maximum of %d unit in each city can enforce"
6050 " martial law.\n",
6051 "* A maximum of %d units in each city can enforce"
6052 " martial law.\n",
6053 net_value),
6054 net_value);
6055 }
6056 break;
6057 case EFT_RAPTURE_GROW:
6058 if (playerwide && net_value > 0) {
6059 cat_snprintf(buf, bufsz,
6060 _("* You may grow your cities by means of "
6061 "celebrations."));
6062 if (game.info.celebratesize > 1) {
6063 cat_snprintf(buf, bufsz,
6064 /* TRANS: Preserve leading space. %d should always be
6065 * 2 or greater. */
6066 _(" (Cities below size %d cannot grow in this way.)"),
6067 game.info.celebratesize);
6068 }
6069 cat_snprintf(buf, bufsz, "\n");
6070 }
6071 break;
6072 case EFT_REVOLUTION_UNHAPPINESS:
6073 if (playerwide) {
6074 cat_snprintf(buf, bufsz,
6075 PL_("* If a city is in disorder for more than %d turn "
6076 "in a row, government will fall into anarchy.\n",
6077 "* If a city is in disorder for more than %d turns "
6078 "in a row, government will fall into anarchy.\n",
6079 net_value),
6080 net_value);
6081 }
6082 break;
6083 case EFT_HAS_SENATE:
6084 if (playerwide && net_value > 0) {
6085 CATLSTR(buf, bufsz,
6086 _("* Has a senate that may prevent declaration of war.\n"));
6087 }
6088 break;
6089 case EFT_INSPIRE_PARTISANS:
6090 if (playerwide && net_value > 0) {
6091 CATLSTR(buf, bufsz,
6092 _("* Allows partisans when cities are taken by the "
6093 "enemy.\n"));
6094 }
6095 break;
6096 case EFT_HAPPINESS_TO_GOLD:
6097 if (playerwide && net_value > 0) {
6098 CATLSTR(buf, bufsz,
6099 _("* Buildings that normally confer bonuses against"
6100 " unhappiness will instead give gold.\n"));
6101 }
6102 break;
6103 case EFT_FANATICS:
6104 if (playerwide && net_value > 0) {
6105 struct strvec *fanatics = strvec_new();
6106 struct astring fanaticstr = ASTRING_INIT;
6107
6108 unit_type_iterate(putype) {
6109 if (utype_has_flag(putype, UTYF_FANATIC)) {
6110 strvec_append(fanatics, utype_name_translation(putype));
6111 }
6112 } unit_type_iterate_end;
6113 cat_snprintf(buf, bufsz,
6114 /* TRANS: %s is list of unit types separated by 'or' */
6115 _("* Pays no upkeep for %s.\n"),
6116 strvec_to_or_list(fanatics, &fanaticstr));
6117 strvec_destroy(fanatics);
6118 astr_free(&fanaticstr);
6119 }
6120 break;
6121 case EFT_NO_UNHAPPY:
6122 if (playerwide && net_value > 0) {
6123 CATLSTR(buf, bufsz, _("* Has no unhappy citizens.\n"));
6124 }
6125 break;
6126 case EFT_VETERAN_BUILD:
6127 {
6128 int conditions = 0;
6129 if (unitclass) {
6130 conditions++;
6131 }
6132 if (unittype) {
6133 conditions++;
6134 }
6135 if (unit_type_flag_id_is_valid(unitflag)) {
6136 conditions++;
6137 }
6138 if (conditions > 1) {
6139 /* More than one requirement on units, too complicated for us
6140 * to describe. */
6141 break;
6142 }
6143 if (unitclass) {
6144 /* FIXME: account for multiple veteran levels, or negative
6145 * values. This might lie for complicated rulesets! */
6146 cat_snprintf(buf, bufsz,
6147 /* TRANS: %s is a unit class */
6148 Q_("?unitclass:* New %s units will be veteran.\n"),
6149 uclass_name_translation(unitclass));
6150 } else if (unit_type_flag_id_is_valid(unitflag)) {
6151 /* FIXME: same problems as unitclass */
6152 cat_snprintf(buf, bufsz,
6153 /* TRANS: %s is a (translatable) unit type flag */
6154 Q_("?unitflag:* New %s units will be veteran.\n"),
6155 unit_type_flag_id_translated_name(unitflag));
6156 } else if (unittype != NULL) {
6157 if (world_value_valid && net_value > 0) {
6158 /* Here we can be specific about veteran level, and get
6159 * net value correct. */
6160 int maxlvl = utype_veteran_system(unittype)->levels - 1;
6161 const struct veteran_level *vlevel =
6162 utype_veteran_level(unittype, MIN(net_value, maxlvl));
6163 cat_snprintf(buf, bufsz,
6164 /* TRANS: "* New Partisan units will have the rank
6165 * of elite." */
6166 Q_("?unittype:* New %s units will have the rank "
6167 "of %s.\n"),
6168 utype_name_translation(unittype),
6169 name_translation_get(&vlevel->name));
6170 } /* else complicated */
6171 } else {
6172 /* No extra criteria. */
6173 /* FIXME: same problems as above */
6174 cat_snprintf(buf, bufsz,
6175 _("* New units will be veteran.\n"));
6176 }
6177 }
6178 break;
6179 case EFT_OUTPUT_PENALTY_TILE:
6180 if (world_value_valid) {
6181 cat_snprintf(buf, bufsz,
6182 /* TRANS: %s is list of output types, with 'or';
6183 * pluralised in %d but of course the output types
6184 * can't be pluralised; sorry */
6185 PL_("* Each worked tile that gives more than %d %s will"
6186 " suffer a -1 penalty, unless the city working it"
6187 " is celebrating.",
6188 "* Each worked tile that gives more than %d %s will"
6189 " suffer a -1 penalty, unless the city working it"
6190 " is celebrating.", net_value),
6191 net_value, astr_str(&outputs_or));
6192 if (game.info.celebratesize > 1) {
6193 cat_snprintf(buf, bufsz,
6194 /* TRANS: Preserve leading space. %d should always be
6195 * 2 or greater. */
6196 _(" (Cities below size %d will not celebrate.)"),
6197 game.info.celebratesize);
6198 }
6199 cat_snprintf(buf, bufsz, "\n");
6200 }
6201 break;
6202 case EFT_OUTPUT_INC_TILE_CELEBRATE:
6203 cat_snprintf(buf, bufsz,
6204 /* TRANS: %s is list of output types, with 'or' */
6205 PL_("* Each worked tile with at least 1 %s will yield"
6206 " %d more of it while the city working it is"
6207 " celebrating.",
6208 "* Each worked tile with at least 1 %s will yield"
6209 " %d more of it while the city working it is"
6210 " celebrating.", peffect->value),
6211 astr_str(&outputs_or), peffect->value);
6212 if (game.info.celebratesize > 1) {
6213 cat_snprintf(buf, bufsz,
6214 /* TRANS: Preserve leading space. %d should always be
6215 * 2 or greater. */
6216 _(" (Cities below size %d will not celebrate.)"),
6217 game.info.celebratesize);
6218 }
6219 cat_snprintf(buf, bufsz, "\n");
6220 break;
6221 case EFT_OUTPUT_INC_TILE:
6222 cat_snprintf(buf, bufsz,
6223 /* TRANS: %s is list of output types, with 'or' */
6224 PL_("* Each worked tile with at least 1 %s will yield"
6225 " %d more of it.\n",
6226 "* Each worked tile with at least 1 %s will yield"
6227 " %d more of it.\n", peffect->value),
6228 astr_str(&outputs_or), peffect->value);
6229 break;
6230 case EFT_OUTPUT_BONUS:
6231 case EFT_OUTPUT_BONUS_2:
6232 /* FIXME: makes most sense iff world_value == 0 */
6233 cat_snprintf(buf, bufsz,
6234 /* TRANS: %s is list of output types, with 'and' */
6235 _("* %s production is increased %d%%.\n"),
6236 astr_str(&outputs_and), peffect->value);
6237 break;
6238 case EFT_OUTPUT_WASTE:
6239 if (world_value_valid) {
6240 if (net_value > 30) {
6241 cat_snprintf(buf, bufsz,
6242 /* TRANS: %s is list of output types, with 'and' */
6243 _("* %s production will suffer massive losses.\n"),
6244 astr_str(&outputs_and));
6245 } else if (net_value >= 15) {
6246 cat_snprintf(buf, bufsz,
6247 /* TRANS: %s is list of output types, with 'and' */
6248 _("* %s production will suffer some losses.\n"),
6249 astr_str(&outputs_and));
6250 } else if (net_value > 0) {
6251 cat_snprintf(buf, bufsz,
6252 /* TRANS: %s is list of output types, with 'and' */
6253 _("* %s production will suffer a small amount "
6254 "of losses.\n"),
6255 astr_str(&outputs_and));
6256 }
6257 }
6258 break;
6259 case EFT_HEALTH_PCT:
6260 if (playerwide) {
6261 if (peffect->value > 0) {
6262 CATLSTR(buf, bufsz, _("* Increases the chance of plague"
6263 " within your cities.\n"));
6264 } else if (peffect->value < 0) {
6265 CATLSTR(buf, bufsz, _("* Decreases the chance of plague"
6266 " within your cities.\n"));
6267 }
6268 }
6269 break;
6270 case EFT_OUTPUT_WASTE_BY_DISTANCE:
6271 if (world_value_valid) {
6272 if (net_value >= 3) {
6273 cat_snprintf(buf, bufsz,
6274 /* TRANS: %s is list of output types, with 'and' */
6275 _("* %s losses will increase quickly"
6276 " with distance from capital.\n"),
6277 astr_str(&outputs_and));
6278 } else if (net_value == 2) {
6279 cat_snprintf(buf, bufsz,
6280 /* TRANS: %s is list of output types, with 'and' */
6281 _("* %s losses will increase"
6282 " with distance from capital.\n"),
6283 astr_str(&outputs_and));
6284 } else if (net_value > 0) {
6285 cat_snprintf(buf, bufsz,
6286 /* TRANS: %s is list of output types, with 'and' */
6287 _("* %s losses will increase slowly"
6288 " with distance from capital.\n"),
6289 astr_str(&outputs_and));
6290 }
6291 }
6292 break;
6293 case EFT_MIGRATION_PCT:
6294 if (playerwide) {
6295 if (peffect->value > 0) {
6296 CATLSTR(buf, bufsz, _("* Increases the chance of migration"
6297 " into your cities.\n"));
6298 } else if (peffect->value < 0) {
6299 CATLSTR(buf, bufsz, _("* Decreases the chance of migration"
6300 " into your cities.\n"));
6301 }
6302 }
6303 break;
6304 default:
6305 break;
6306 };
6307 }
6308
6309 strvec_destroy(outputs);
6310 astr_free(&outputs_or);
6311 astr_free(&outputs_and);
6312
6313 } effect_list_iterate_end;
6314
6315 unit_type_iterate(utype) {
6316 if (utype->need_government == gov) {
6317 cat_snprintf(buf, bufsz,
6318 _("* Allows you to build %s.\n"),
6319 utype_name_translation(utype));
6320 }
6321 } unit_type_iterate_end;
6322
6323 /* Action immunity */
6324 action_iterate(act) {
6325 if (action_by_number(act)->quiet) {
6326 /* The ruleset documents this action it self. */
6327 continue;
6328 }
6329
6330 if (action_immune_government(gov, act)) {
6331 cat_snprintf(buf, bufsz,
6332 /* TRANS: action name ... action target
6333 * ("individual units", etc) */
6334 _("* Makes it impossible to do the action \'%s\'"
6335 " to your %s.\n"),
6336 action_id_name_translation(act),
6337 _(action_target_kind_name(
6338 action_id_get_target_kind(act))));
6339 }
6340 } action_iterate_end;
6341
6342 if (user_text && user_text[0] != '\0') {
6343 cat_snprintf(buf, bufsz, "\n%s", user_text);
6344 }
6345 }
6346
6347 /****************************************************************
6348 Returns pointer to static string with eg: "1 shield, 1 unhappy"
6349 *****************************************************************/
helptext_unit_upkeep_str(struct unit_type * utype)6350 char *helptext_unit_upkeep_str(struct unit_type *utype)
6351 {
6352 static char buf[128];
6353 int any = 0;
6354
6355 if (!utype) {
6356 log_error("Unknown unit!");
6357 return "";
6358 }
6359
6360
6361 buf[0] = '\0';
6362 output_type_iterate(o) {
6363 if (utype->upkeep[o] > 0) {
6364 /* TRANS: "2 Food" or ", 1 Shield" */
6365 cat_snprintf(buf, sizeof(buf), _("%s%d %s"),
6366 (any > 0 ? Q_("?blistmore:, ") : ""), utype->upkeep[o],
6367 get_output_name(o));
6368 any++;
6369 }
6370 } output_type_iterate_end;
6371 if (utype->happy_cost > 0) {
6372 /* TRANS: "2 Unhappy" or ", 1 Unhappy" */
6373 cat_snprintf(buf, sizeof(buf), _("%s%d Unhappy"),
6374 (any > 0 ? Q_("?blistmore:, ") : ""), utype->happy_cost);
6375 any++;
6376 }
6377
6378 if (any == 0) {
6379 /* strcpy(buf, _("None")); */
6380 fc_snprintf(buf, sizeof(buf), "%d", 0);
6381 }
6382 return buf;
6383 }
6384
6385 /****************************************************************************
6386 Returns nation legend and characteristics
6387 ****************************************************************************/
helptext_nation(char * buf,size_t bufsz,struct nation_type * pnation,const char * user_text)6388 void helptext_nation(char *buf, size_t bufsz, struct nation_type *pnation,
6389 const char *user_text)
6390 {
6391 struct universal source = {
6392 .kind = VUT_NATION,
6393 .value = {.nation = pnation}
6394 };
6395 bool print_break = TRUE;
6396 #define PRINT_BREAK() do { \
6397 if (print_break) { \
6398 if (buf[0] != '\0') { \
6399 CATLSTR(buf, bufsz, "\n\n"); \
6400 } \
6401 print_break = FALSE; \
6402 } \
6403 } while(0)
6404
6405 fc_assert_ret(NULL != buf && 0 < bufsz);
6406 buf[0] = '\0';
6407
6408 if (pnation->legend[0] != '\0') {
6409 /* Client side legend is stored already translated */
6410 cat_snprintf(buf, bufsz, "%s", pnation->legend);
6411 }
6412
6413 if (pnation->init_government) {
6414 PRINT_BREAK();
6415 cat_snprintf(buf, bufsz,
6416 _("Initial government is %s.\n"),
6417 government_name_translation(pnation->init_government));
6418 }
6419 if (pnation->init_techs[0] != A_LAST) {
6420 const char *tech_names[MAX_NUM_TECH_LIST];
6421 int i;
6422 struct astring list = ASTRING_INIT;
6423 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
6424 if (pnation->init_techs[i] == A_LAST) {
6425 break;
6426 }
6427 tech_names[i] =
6428 advance_name_translation(advance_by_number(pnation->init_techs[i]));
6429 }
6430 astr_build_and_list(&list, tech_names, i);
6431 PRINT_BREAK();
6432 if (game.rgame.global_init_techs[0] != A_LAST) {
6433 cat_snprintf(buf, bufsz,
6434 /* TRANS: %s is an and-separated list of techs */
6435 _("Starts with knowledge of %s in addition to the standard "
6436 "starting technologies.\n"), astr_str(&list));
6437 } else {
6438 cat_snprintf(buf, bufsz,
6439 /* TRANS: %s is an and-separated list of techs */
6440 _("Starts with knowledge of %s.\n"), astr_str(&list));
6441 }
6442 astr_free(&list);
6443 }
6444 if (pnation->init_units[0]) {
6445 const struct unit_type *utypes[MAX_NUM_UNIT_LIST];
6446 int count[MAX_NUM_UNIT_LIST];
6447 int i, j, n = 0, total = 0;
6448 /* Count how many of each type there is. */
6449 for (i = 0; i < MAX_NUM_UNIT_LIST; i++) {
6450 if (!pnation->init_units[i]) {
6451 break;
6452 }
6453 for (j = 0; j < n; j++) {
6454 if (pnation->init_units[i] == utypes[j]) {
6455 count[j]++;
6456 total++;
6457 break;
6458 }
6459 }
6460 if (j == n) {
6461 utypes[n] = pnation->init_units[i];
6462 count[n] = 1;
6463 total++;
6464 n++;
6465 }
6466 }
6467 {
6468 /* Construct the list of unit types and counts. */
6469 struct astring utype_names[MAX_NUM_UNIT_LIST];
6470 struct astring list = ASTRING_INIT;
6471 for (i = 0; i < n; i++) {
6472 astr_init(&utype_names[i]);
6473 if (count[i] > 1) {
6474 /* TRANS: a unit type followed by a count. For instance,
6475 * "Fighter (2)" means two Fighters. Count is never 1.
6476 * Used in a list. */
6477 astr_set(&utype_names[i], _("%s (%d)"),
6478 utype_name_translation(utypes[i]), count[i]);
6479 } else {
6480 astr_set(&utype_names[i], "%s", utype_name_translation(utypes[i]));
6481 }
6482 }
6483 {
6484 const char *utype_name_strs[MAX_NUM_UNIT_LIST];
6485 for (i = 0; i < n; i++) {
6486 utype_name_strs[i] = astr_str(&utype_names[i]);
6487 }
6488 astr_build_and_list(&list, utype_name_strs, n);
6489 }
6490 for (i = 0; i < n; i++) {
6491 astr_free(&utype_names[i]);
6492 }
6493 PRINT_BREAK();
6494 cat_snprintf(buf, bufsz,
6495 /* TRANS: %s is an and-separated list of unit types
6496 * possibly with counts. Plurality is in total number of
6497 * units represented. */
6498 PL_("Starts with the following additional unit: %s.\n",
6499 "Starts with the following additional units: %s.\n",
6500 total), astr_str(&list));
6501 astr_free(&list);
6502 }
6503 }
6504 if (pnation->init_buildings[0] != B_LAST) {
6505 const char *impr_names[MAX_NUM_BUILDING_LIST];
6506 int i;
6507 struct astring list = ASTRING_INIT;
6508 for (i = 0; i < MAX_NUM_BUILDING_LIST; i++) {
6509 if (pnation->init_buildings[i] == B_LAST) {
6510 break;
6511 }
6512 impr_names[i] =
6513 improvement_name_translation(
6514 improvement_by_number(pnation->init_buildings[i]));
6515 }
6516 astr_build_and_list(&list, impr_names, i);
6517 PRINT_BREAK();
6518 if (game.rgame.global_init_buildings[0] != B_LAST) {
6519 cat_snprintf(buf, bufsz,
6520 /* TRANS: %s is an and-separated list of improvements */
6521 _("First city will get %s for free in addition to the "
6522 "standard improvements.\n"), astr_str(&list));
6523 } else {
6524 cat_snprintf(buf, bufsz,
6525 /* TRANS: %s is an and-separated list of improvements */
6526 _("First city will get %s for free.\n"), astr_str(&list));
6527 }
6528 astr_free(&list);
6529 }
6530
6531 if (buf[0] != '\0') {
6532 CATLSTR(buf, bufsz, "\n");
6533 }
6534 insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf), "");
6535
6536 if (user_text && user_text[0] != '\0') {
6537 if (buf[0] != '\0') {
6538 CATLSTR(buf, bufsz, "\n");
6539 }
6540 CATLSTR(buf, bufsz, user_text);
6541 }
6542 #undef PRINT_BREAK
6543 }
6544