1 /* Online help support for Xconq.
2 Copyright (C) 1987-1989, 1991-2000 Stanley T. Shebs.
3 Copyright (C) 2004-2005 Eric A. McDonald.
4
5 Xconq is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version. See the file COPYING. */
9
10 /* This is basically support code for interfaces, which handle the
11 actual help interaction themselves. */
12
13 /* This file must also be translated (mostly) for non-English Xconq. */
14
15 #include "conq.h"
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 #include <fcntl.h>
20 #ifdef __cplusplus
21 }
22 #endif
23
24 /* Extra functions. */
25
26 int may_detonate(int u);
27 int any_ut_capacity_x(int u);
28 int any_mp_to_enter_unit(int u);
29 int any_mp_to_leave_unit(int u);
30 int any_enter_indep(int u);
31
32 /* Help system internal variables. */
33
34 static int help_output_cc = HELP_OUTPUT_CC_NONE;
35 static HelpOutputMode help_output_mode = HELP_OUTPUT_PLAIN_TEXT;
36 static char *help_output_dir = NULL;
37 static FILE *help_output_tmp_filep = NULL;
38 static FILE *help_output_toc_filep = NULL;
39
40 static HelpPageDefn hpagedefns [] = {
41 /* HELP_PAGE_NONE */
42 { NULL, NULL, HELP_PAGE_NONE, HELP_PAGE_NONE, HELP_PAGE_NONE },
43 /* HELP_PAGE_MASTER_INDEX */
44 { NULL, "Master Game Index", HELP_PAGE_NONE, HELP_PAGE_NONE,
45 HELP_PAGE_NONE },
46 /* HELP_PAGE_TOC */
47 { "index", "Table of Contents", HELP_PAGE_MASTER_INDEX, HELP_PAGE_NONE,
48 HELP_PAGE_NONE },
49 /* HELP_PAGE_COPYRIGHT */
50 { "copyleft", "Copyright", HELP_PAGE_TOC, HELP_PAGE_CONCEPTS,
51 HELP_PAGE_WARRANTY },
52 /* HELP_PAGE_WARRANTY */
53 { "warranty", "Warranty (or lack thereof)", HELP_PAGE_TOC,
54 HELP_PAGE_COPYRIGHT, HELP_PAGE_NEWS },
55 /* HELP_PAGE_NEWS */
56 { "news", "News (or lack thereof)", HELP_PAGE_TOC, HELP_PAGE_WARRANTY,
57 HELP_PAGE_INSTRUCTIONS },
58 /* HELP_PAGE_INSTRUCTIONS */
59 { "isns", "Instructions", HELP_PAGE_TOC, HELP_PAGE_NEWS,
60 HELP_PAGE_GAME_OVERVIEW },
61 /* HELP_PAGE_GAME_OVERVIEW */
62 { "overview", "Game Overview", HELP_PAGE_TOC, HELP_PAGE_INSTRUCTIONS,
63 HELP_PAGE_SCORING },
64 /* HELP_PAGE_SCORING */
65 { "scoring", "Scoring", HELP_PAGE_TOC, HELP_PAGE_GAME_OVERVIEW,
66 HELP_PAGE_MODULES },
67 /* HELP_PAGE_MODULES */
68 { "modules", "Modules", HELP_PAGE_TOC, HELP_PAGE_SCORING,
69 HELP_PAGE_GAME_SETUP },
70 /* HELP_PAGE_GAME_SETUP */
71 { "config", "Game Setup", HELP_PAGE_TOC, HELP_PAGE_MODULES,
72 HELP_PAGE_WORLD },
73 /* HELP_PAGE_WORLD */
74 { "world", "World", HELP_PAGE_TOC, HELP_PAGE_GAME_SETUP, HELP_PAGE_UTYPE },
75 /* HELP_PAGE_UTYPE */
76 { "utype", "Unit", HELP_PAGE_TOC, HELP_PAGE_WORLD, HELP_PAGE_TTYPE },
77 /* HELP_PAGE_TTYPE */
78 { "ttype", "Terrain", HELP_PAGE_TOC, HELP_PAGE_UTYPE, HELP_PAGE_MTYPE },
79 /* HELP_PAGE_MTYPE */
80 { "mtype", "Material", HELP_PAGE_TOC, HELP_PAGE_TTYPE, HELP_PAGE_ATYPE },
81 /* HELP_PAGE_ATYPE */
82 { "atype", "Advance", HELP_PAGE_TOC, HELP_PAGE_MTYPE, HELP_PAGE_NONE },
83 /* HELP_PAGE_CONCEPTS */
84 { "concepts", "Game Concepts", HELP_PAGE_TOC, HELP_PAGE_ATYPE,
85 HELP_PAGE_COPYRIGHT }
86 };
87 #define HELP_PAGE_LAST HELP_PAGE_CONCEPTS
88
89 /* Obstack allocation and deallocation routines. */
90
91 #define obstack_chunk_alloc xmalloc
92 #define obstack_chunk_free free
93
94 /* Help system internal functions. */
95
96 static void describe_help_system(int arg, char *key, TextBuffer *buf);
97 static void describe_instructions(int arg, char *key, TextBuffer *buf);
98 static void describe_synth_run(TextBuffer *buf, int methkey);
99 static void describe_world(int arg, char *key, TextBuffer *buf);
100 static int histogram_compare(const void *h1, const void *h2);
101 static void describe_news(int arg, char *key, TextBuffer *buf);
102 static void describe_concepts(int arg, char *key, TextBuffer *buf);
103 static void describe_game_design(int arg, char *key, TextBuffer *buf);
104 static void describe_utype(int u, char *key, TextBuffer *buf);
105 static void describe_utype_movement(int u, char *key, TextBuffer *buf);
106 static void describe_utype_actions(int u, char *key, TextBuffer *buf);
107 static void describe_utype_side_attributes(int u, char *key, TextBuffer *buf);
108 static void describe_utype_ai_attributes(int u, char *key, TextBuffer *buf);
109 static void describe_mtype(int m, char *key, TextBuffer *buf);
110 static void describe_ttype(int t, char *key, TextBuffer *buf);
111 static void describe_atype(int t, char *key, TextBuffer *buf);
112
113 static void describe_scorekeepers(int arg, char *key, TextBuffer *buf);
114 static void describe_setup(int arg, char *key, TextBuffer *buf);
115 static void describe_game_modules(int arg, char *key, TextBuffer *buf);
116 static void describe_game_module_aux(TextBuffer *buf, Module *module,
117 int level);
118 static void describe_module_notes(TextBuffer *buf, Module *module);
119
120 static int u_property_not_default(int (*fn)(int i), int dflt);
121 static int t_property_not_default(int (*fn)(int i), int dflt);
122 static int uu_table_row_not_default(int u, int (*fn)(int i, int j), int dflt);
123 static int ut_table_row_not_default(int u, int (*fn)(int i, int j), int dflt);
124 static int um_table_row_not_default(int u, int (*fn)(int i, int j), int dflt);
125 static int ua_table_row_not_default(int u, int (*fn)(int i, int j), int dflt);
126 static int tt_table_row_not_default(int t, int (*fn)(int i, int j), int dflt);
127 #if 0
128 static int tm_table_row_not_default(int t, int (*fn)(int i, int j), int dflt);
129 #endif
130 static int aa_table_row_not_default(int a1, int (*fn)(int i, int j), int dflt);
131 static int uu_table_column_not_default(int u, int (*fn)(int i, int j),
132 int dflt);
133 static int aa_table_column_not_default(int a1, int (*fn)(int i, int j),
134 int dflt);
135 static void u_property_desc(TextBuffer *buf, int (*fn)(int),
136 void (*formatter)(TextBuffer *, int));
137 static void t_property_desc(TextBuffer *buf, int (*fn)(int),
138 void (*formatter)(TextBuffer *, int));
139 static void uu_table_row_desc(TextBuffer *buf, int u, int (*fn)(int, int),
140 void (*formatter)(TextBuffer *, int),
141 char *connect);
142 static void uu_table_column_desc(TextBuffer *buf, int u, int (*fn)(int, int),
143 void (*formatter)(TextBuffer *, int),
144 char *connect);
145 static void uu_table_rowcol_desc(TextBuffer *buf, int u, int (*fn)(int, int),
146 void (*formatter)(TextBuffer *, int),
147 char *connect, int rowcol);
148 static void ut_table_row_desc(TextBuffer *buf, int u, int (*fn)(int, int),
149 void (*formatter)(TextBuffer *, int),
150 char *connect);
151 static void um_table_row_desc(TextBuffer *buf, int u, int (*fn)(int, int),
152 void (*formatter)(TextBuffer *, int));
153 static void ua_table_row_desc(TextBuffer *buf, int u, int (*fn)(int, int),
154 void (*formatter)(TextBuffer *, int));
155 static void tt_table_row_desc(TextBuffer *buf, int t1, int (*fn)(int, int),
156 void (*formatter)(TextBuffer *, int));
157 #if 0
158 static void tm_table_row_desc(TextBuffer *buf, int t, int (*fn)(int, int),
159 void (*formatter)(TextBuffer *, int));
160 #endif
161 static void aa_table_row_desc(TextBuffer *buf, int a1, int (*fn)(int, int),
162 void (*formatter)(TextBuffer *, int));
163 static void aa_table_column_desc(TextBuffer *buf, int a1, int (*fn)(int, int),
164 void (*formatter)(TextBuffer *, int));
165 static void aa_table_rowcol_desc(TextBuffer *buf, int a1, int (*fn)(int, int),
166 void (*formatter)(TextBuffer *, int),
167 int rowcol);
168 static void tb_value_desc(TextBuffer *buf, int val);
169 static void tb_fraction_desc(TextBuffer *buf, int val);
170 static void tb_percent_desc(TextBuffer *buf, int val);
171 static void tb_percent_100th_desc(TextBuffer *buf, int val);
172 static void tb_probfraction_desc(TextBuffer *buf, int val);
173 static void tb_dice_desc(TextBuffer *buf, int val);
174 static void tb_mult_desc(TextBuffer *buf, int val);
175 static void tb_bool_desc(TextBuffer *buf, int val);
176 #if 0
177 static void append_number(TextBuffer *buf, int value, int dflt);
178 #endif
179 static void append_help_phrase(TextBuffer *buf, char *phrase);
180 static void append_notes(TextBuffer *buf, Obj *notes);
181
182 static void init_help_pages(void);
183 static void meta_prep_help_cc(HelpPage hpage, int hpageidx);
184 static void prep_help_cc_toc(void);
185 static void prep_help_cc_any(char *ccfilename, char *sectionname);
186 static void flush_help_cc_tmp(HelpNode *helpnode);
187 static void meta_finish_help_cc(HelpPage hpage, int hpageidx,
188 HelpNode *helpnode);
189 static void finish_help_cc_toc(void);
190 static void flush_help_cc(FILE *ccfile, HelpNode *helpnode);
191 static void write_help_file_navbar(FILE *ccfilep, HelpPage hpage, int hpageidx);
192 static void write_help_section_header(FILE *ccfile, char *headerdata);
193 static void write_help_section_footer(FILE *ccfilep, char *footerdata);
194 static void write_help_subsection_header(FILE *ccfile, char *headerdata);
195 static void write_help_subsection_footer(FILE *ccfile, char *footerdata);
196 static void write_help_subsection_break(FILE *ccfile);
197
198 static char *help_newline(void);
199 static char *help_indent(unsigned int spaces);
200
201 /* The first help node in the chain. */
202
203 HelpNode *first_help_node;
204
205 /* The last help node. */
206
207 HelpNode *last_help_node;
208
209 /* The help node with help system info. */
210
211 HelpNode *help_system_node;
212
213 /* The help node with copying and copyright info. */
214
215 HelpNode *copying_help_node;
216
217 /* The help node with (non-)warranty info. */
218
219 HelpNode *warranty_help_node;
220
221 HelpNode *default_prev_help_node;
222
223 /* Set the help output carbon copy. */
224 void
set_help_output_cc(int cctarget)225 set_help_output_cc(int cctarget)
226 {
227 switch(cctarget) {
228 case HELP_OUTPUT_CC_NONE: case HELP_OUTPUT_CC_FILES:
229 help_output_cc = cctarget;
230 break;
231 default:
232 run_error("Attempted to set an invalid help carbon copy target.");
233 }
234 }
235
236 /* Set the help output mode. */
237 void
set_help_output_mode(HelpOutputMode houtmode)238 set_help_output_mode(HelpOutputMode houtmode)
239 {
240 switch(houtmode) {
241 case HELP_OUTPUT_PLAIN_TEXT: case HELP_OUTPUT_HTML:
242 help_output_mode = houtmode;
243 break;
244 default:
245 run_error("Attempted to set an unsupported help output mode.");
246 }
247 }
248
249 /* Set the help output directory. */
250 void
set_help_output_dir(char * dir)251 set_help_output_dir(char *dir)
252 {
253 #if (!defined MAC)
254 if (access(dir, F_OK))
255 run_error("Attempted to access a missing directory.");
256 #endif
257 help_output_dir = dir;
258 }
259
260 /* Set the help TOC file stream pointer. */
261 void
set_help_toc_filep(FILE * htocfilep)262 set_help_toc_filep(FILE *htocfilep)
263 {
264 help_output_toc_filep = htocfilep;
265 }
266
267 /* Create the initial help node and link it to itself. Subsequent
268 nodes will be inserted later, after a game has been loaded. */
269
270 void
init_help(void)271 init_help(void)
272 {
273 HelpNode *node = NULL;
274
275 init_help_pages();
276 prep_help_cc_toc();
277 /* Note that we can't use add_help_node to set up the first help
278 node. */
279 first_help_node = create_help_node();
280 first_help_node->key = "--- BASIC HELP ---";
281 first_help_node->prev = first_help_node->next = first_help_node;
282 last_help_node = first_help_node;
283 help_system_node =
284 add_help_node("help system", describe_help_system, 0, first_help_node);
285 meta_prep_help_cc(HELP_PAGE_COPYRIGHT, -1);
286 copying_help_node =
287 add_help_node("copyright", describe_copyright, 0, help_system_node);
288 meta_finish_help_cc(HELP_PAGE_COPYRIGHT, -1, copying_help_node);
289 meta_prep_help_cc(HELP_PAGE_WARRANTY, -1);
290 warranty_help_node =
291 add_help_node("warranty", describe_warranty, 0, copying_help_node);
292 meta_finish_help_cc(HELP_PAGE_WARRANTY, -1, warranty_help_node);
293 /* Set the place for new nodes to appear normally. */
294 default_prev_help_node = copying_help_node;
295 meta_prep_help_cc(HELP_PAGE_NEWS, -1);
296 node = add_help_node("news", describe_news, 0, NULL);
297 meta_finish_help_cc(HELP_PAGE_NEWS, -1, node);
298 }
299
300 /* This function creates the actual set of help nodes for the kernel. */
301
302 void
create_game_help_nodes(void)303 create_game_help_nodes(void)
304 {
305 int u, m, t, a;
306 char *name, *longname;
307 HelpNode *node;
308
309 add_help_node("--- MODULE INFO ---", NULL, 0, NULL);
310 meta_prep_help_cc(HELP_PAGE_INSTRUCTIONS, -1);
311 node = add_help_node("instructions", describe_instructions, 0, NULL);
312 meta_finish_help_cc(HELP_PAGE_INSTRUCTIONS, -1, node);
313 meta_prep_help_cc(HELP_PAGE_GAME_OVERVIEW, -1);
314 node = add_help_node("game overview", describe_game_design, 0, NULL);
315 meta_finish_help_cc(HELP_PAGE_GAME_OVERVIEW, -1, node);
316 meta_prep_help_cc(HELP_PAGE_SCORING, -1);
317 node = add_help_node("scoring", describe_scorekeepers, 0, NULL);
318 meta_finish_help_cc(HELP_PAGE_SCORING, -1, node);
319 meta_prep_help_cc(HELP_PAGE_MODULES, -1);
320 node = add_help_node("modules", describe_game_modules, 0, NULL);
321 meta_finish_help_cc(HELP_PAGE_MODULES, -1, node);
322 meta_prep_help_cc(HELP_PAGE_GAME_SETUP, -1);
323 node = add_help_node("game setup", describe_setup, 0, NULL);
324 meta_finish_help_cc(HELP_PAGE_GAME_SETUP, -1, node);
325 meta_prep_help_cc(HELP_PAGE_WORLD, -1);
326 node = add_help_node("world", describe_world, 0, NULL);
327 meta_finish_help_cc(HELP_PAGE_WORLD, -1, node);
328 if (numutypes) {
329 add_help_node("--- UNIT TYPES ---", NULL, 0, NULL);
330 write_help_toc_entry(NULL, "Unit Types", 0);
331 }
332 for_all_unit_types(u) {
333 longname = u_long_name(u);
334 if (!empty_string(longname)) {
335 sprintf(spbuf, "%s (%s)", longname, u_type_name(u));
336 name = copy_string(spbuf);
337 } else {
338 name = u_type_name(u);
339 }
340 meta_prep_help_cc(HELP_PAGE_UTYPE, u);
341 node = add_help_node(name, describe_utype, u, NULL);
342 node->nclass = utypenode;
343 meta_finish_help_cc(HELP_PAGE_UTYPE, u, node);
344 }
345 if (nummtypes) {
346 add_help_node("--- MATERIALS ---", NULL, 0, NULL);
347 write_help_toc_entry(NULL, "Material Types", 0);
348 }
349 for_all_material_types(m) {
350 meta_prep_help_cc(HELP_PAGE_MTYPE, m);
351 node = add_help_node(m_type_name(m), describe_mtype, m, NULL);
352 node->nclass = mtypenode;
353 meta_finish_help_cc(HELP_PAGE_MTYPE, m, node);
354 }
355 if (numttypes) {
356 add_help_node("--- TERRAIN TYPES ---", NULL, 0, NULL);
357 write_help_toc_entry(NULL, "Terrain Types", 0);
358 }
359 for_all_terrain_types(t) {
360 meta_prep_help_cc(HELP_PAGE_TTYPE, t);
361 node = add_help_node(t_type_name(t), describe_ttype, t, NULL);
362 node->nclass = ttypenode;
363 meta_finish_help_cc(HELP_PAGE_TTYPE, t, node);
364 }
365 if (numatypes) {
366 add_help_node("--- ADVANCES ---", NULL, 0, NULL);
367 write_help_toc_entry(NULL, "Advances", 0);
368 }
369 for_all_advance_types(a) {
370 meta_prep_help_cc(HELP_PAGE_ATYPE, a);
371 node = add_help_node(a_type_name(a), describe_atype, a, NULL);
372 node->nclass = atypenode;
373 meta_finish_help_cc(HELP_PAGE_ATYPE, a, node);
374 }
375 add_help_node("--- GENERAL INFO ---", NULL, 0, NULL);
376 meta_prep_help_cc(HELP_PAGE_CONCEPTS, -1);
377 node = add_help_node("general concepts", describe_concepts, 0, NULL);
378 meta_finish_help_cc(HELP_PAGE_CONCEPTS, -1, node);
379 /* Invalidate any existing topics node. */
380 first_help_node->text = NULL;
381 finish_help_cc_toc();
382 }
383
384 /* Create an empty help node. */
385
386 HelpNode *
create_help_node(void)387 create_help_node(void)
388 {
389 HelpNode *node = (HelpNode *) xmalloc(sizeof(HelpNode));
390
391 node->key = NULL;
392 node->fn = NULL;
393 node->nclass = miscnode;
394 node->arg = 0;
395 node->text = NULL;
396 node->prev = node->next = NULL;
397 return node;
398 }
399
400 /* Add a help node after the given node. */
401
402 HelpNode *
add_help_node(char * key,void (* fn)(int t,char * key,TextBuffer * buf),int arg,HelpNode * prevnode)403 add_help_node(char *key, void (*fn)(int t, char *key, TextBuffer *buf),
404 int arg, HelpNode *prevnode)
405 {
406 HelpNode *node, *nextnode;
407
408 if (empty_string(key)) {
409 run_error("empty help key");
410 }
411 node = create_help_node();
412 node->key = key;
413 node->fn = fn;
414 node->arg = arg;
415 if (prevnode == NULL)
416 prevnode = default_prev_help_node->prev;
417 nextnode = prevnode->next;
418 node->prev = prevnode;
419 node->next = nextnode;
420 prevnode->next = node;
421 nextnode->prev = node;
422 /* Might need to fix last help node. */
423 last_help_node = first_help_node->prev;
424 return node;
425 }
426
427 /* Given a string and node, find the next node whose key matches. */
428
429 HelpNode *
find_help_node(HelpNode * node,char * str)430 find_help_node(HelpNode *node, char *str)
431 {
432 HelpNode *tmp;
433
434 /* Note that the search wraps around. */
435 for (tmp = node->next; tmp != node; tmp = tmp->next) {
436 if (strcmp(tmp->key, str) == 0)
437 return tmp;
438 }
439 return NULL;
440 }
441
442 /* Return the string containing the text of the help node, possibly
443 computing it first. */
444
445 char *
get_help_text(HelpNode * node)446 get_help_text(HelpNode *node)
447 {
448 TextBuffer tbuf;
449
450 if (node != NULL) {
451 /* Maybe calculate the text to display. */
452 if (node->text == NULL) {
453 if (node->fn != NULL) {
454 /* (should allow for variable-size allocation) */
455 obstack_begin(&(tbuf.ostack), 200);
456 if (1) {
457 node->textend = 0;
458 (*(node->fn))(node->arg, node->key, &tbuf);
459 obstack_1grow(&(tbuf.ostack), '\0');
460 node->text = copy_string((char *)obstack_finish(&(tbuf.ostack)));
461 obstack_free(&(tbuf.ostack), 0);
462 node->textend = strlen(node->text);
463 } else {
464 /* Ran out of memory... (would never get here though!) */
465 }
466 } else if (strspn(node->key, "-") > 2) {
467 /* Generate an empty separator. */
468 sprintf(spbuf, " ");
469 node->text = copy_string(spbuf);
470 node->textend = strlen(node->text);
471 } else {
472 /* Generate a default message if nothing to compute help. */
473 sprintf(spbuf, "%s: No info available.", node->key);
474 node->text = copy_string(spbuf);
475 node->textend = strlen(node->text);
476 }
477 Dprintf("Size of help node \"%s\" text is %d\n", node->key, node->textend);
478 }
479 return node->text;
480 } else {
481 return NULL;
482 }
483 }
484
485 static void
describe_help_system(int arg,char * key,TextBuffer * buf)486 describe_help_system(int arg, char *key, TextBuffer *buf)
487 {
488 tbcatline(buf, "This is the header node of the Xconq help system.");
489 tbcatline(buf, "Go forward or backward from here to see the online help.");
490 }
491
492 /* Create a raw list of help topics by just iterating through all the nodes,
493 except for the topics node itself. */
494
495 void
describe_topics(int arg,char * key,TextBuffer * buf)496 describe_topics(int arg, char *key, TextBuffer *buf)
497 {
498 HelpNode *topics, *tmp;
499
500 topics = find_help_node(first_help_node, "topics");
501 /* Unlikely that we'll call this without the topics node existing
502 already, but just in case... */
503 if (topics == NULL)
504 return;
505 for (tmp = topics->next; tmp != topics; tmp = tmp->next) {
506 tbprintf(buf, "%s", tmp->key);
507 tbcat(buf, "\n");
508 }
509 }
510
511 /* Get the news file and put it into text buffer. */
512
513 static void
describe_news(int arg,char * key,TextBuffer * buf)514 describe_news(int arg, char *key, TextBuffer *buf)
515 {
516 FILE *fp;
517
518 fp = open_file(news_filename(), "r");
519 if (fp != NULL) {
520 tbcat(buf, "XCONQ NEWS\n\n");
521 while (fgets(spbuf, BUFSIZE-1, fp) != NULL) {
522 tbcat(buf, spbuf);
523 }
524 fclose(fp);
525 } else {
526 tbcat(buf, "(no news)");
527 }
528 }
529
530 /* Describe general game concepts in a general way. If a concept does
531 not apply to the game in effect, then just say it's not part of
532 this game (would be confusing if the online doc described things
533 irrelevant to the specific game). */
534
535 static void
describe_concepts(int arg,char * key,TextBuffer * buf)536 describe_concepts(int arg, char *key, TextBuffer *buf)
537 {
538 tbcat(buf, "Hit points (HP) represent the overall condition of ");
539 tbcatline(buf, "the unit.");
540 tbcat(buf, "Action points (ACP) are what a unit needs to be able ");
541 tbcat(buf, "to do anything at all. Typically a unit will use 1 ACP ");
542 tbcatline(buf, "to move 1 cell.");
543 tbcat(buf, "Movement points (MP) represent varying costs of movement ");
544 tbcat(buf, "actions, such as a difficult-to-cross border. The number ");
545 tbcat(buf, "of movement points is added up then divided by unit's speed ");
546 tbcatline(buf, "to get the total number of acp used up by a move.");
547 #if (0)
548 if (0) {
549 } else {
550 tbcatline(buf, "No combat experience (CXP) in this game.");
551 }
552 if (0) {
553 } else {
554 tbcatline(buf, "No morale (MO) in this game.");
555 }
556 #endif
557 tbcat(buf, "Each unit that can do anything has a plan, and a list of ");
558 tbcatline(buf, "tasks to perform.");
559 /* (should describe more general concepts) */
560 }
561
562 static void
describe_instructions(int arg,char * key,TextBuffer * buf)563 describe_instructions(int arg, char *key, TextBuffer *buf)
564 {
565 Obj *instructions = mainmodule->instructions;
566
567 if (instructions != lispnil) {
568 append_notes(buf, instructions);
569 } else {
570 tbcat(buf, "(no instructions supplied)");
571 }
572 }
573
574 /* Spit out all the general game_design parameters in a readable fashion. */
575
576 static void
describe_game_design(int arg,char * key,TextBuffer * buf)577 describe_game_design(int arg, char *key, TextBuffer *buf)
578 {
579 int u, m, t, a;
580
581 /* Replicate title and blurb? (should put title at head of
582 windows, and pages if printed) */
583 tbprintf(buf, "*** %s ***%s",
584 (mainmodule->title ? mainmodule->title : mainmodule->name),
585 help_newline());
586 tbcatline(buf, "");
587 tbprintf(buf, "This game includes %d unit types and %d terrain types",
588 numutypes, numttypes);
589 if (nummtypes > 0) {
590 tbprintf(buf, ", along with %d material types", nummtypes);
591 }
592 if (numatypes > 0) {
593 tbprintf(buf, ", and it has %d types of advances", numatypes);
594 }
595 tbcatline(buf, ".");
596 if (g_sides_min() == g_sides_max()) {
597 tbprintf(buf, "Exactly %d sides may play.%s", g_sides_min(),
598 help_newline());
599 } else {
600 tbprintf(buf,
601 "Number of sides to play may range from %d to %d, defaulting to %d.%s",
602 g_sides_min(), g_sides_max(), g_sides_wanted(),
603 help_newline());
604 }
605 tbcatline(buf, "");
606 if (g_advantage_min() == g_advantage_max())
607 tbprintf(buf, "Side advantages are fixed.%s", help_newline());
608 else
609 tbprintf(buf,
610 "Side advantages may range from %d to %d, defaulting to %d.%s",
611 g_advantage_min(), g_advantage_max(), g_advantage_default(),
612 help_newline());
613 tbcatline(buf, "");
614 if (g_see_all()) {
615 tbcatline(buf, "Everything is always seen by all sides.");
616 } else {
617 if (g_see_terrain_always()) {
618 tbcatline(buf, "Terrain view is always accurate once seen.");
619 }
620 /* (should only have if any weather to be seen) */
621 if ((any_temp_variation || any_wind_variation || any_clouds)
622 && g_see_weather_always()) {
623 tbcatline(buf,
624 "Weather view is always accurate once terrain seen.");
625 }
626 if (g_terrain_seen()) {
627 tbcatline(buf, "World terrain is already seen by all sides.");
628 }
629 }
630 tbcatline(buf, "");
631 if (g_last_turn() != gvar_i_default(g_last_turn)) {
632 tbprintf(buf, "Game can go for up to %d turns", g_last_turn());
633 if (g_extra_turn() > 0) {
634 tbprintf(buf, ", with %d%% chance of additional turn thereafter.",
635 g_extra_turn());
636 }
637 tbcatline(buf, ".");
638 }
639 if (g_rt_for_game() > 0) {
640 tbprintf(buf, "Entire game can last up to %d minutes.%s",
641 g_rt_for_game() / 60, help_newline());
642 }
643 if (g_rt_per_turn() > 0) {
644 tbprintf(buf, "Each turn can last up to %d minutes.%s",
645 g_rt_per_turn() / 60, help_newline());
646 }
647 if (g_rt_per_side() > 0) {
648 tbprintf(buf, "Each side gets a total %d minutes to act.%s",
649 g_rt_per_side() / 60, help_newline());
650 }
651 if (g_units_in_game_max() >= 0) {
652 tbprintf(buf, "Limited to no more than %d units in all.%s",
653 g_units_in_game_max(), help_newline());
654 }
655 if (g_units_per_side_max() >= 0) {
656 tbprintf(buf, "Limited to no more than %d units per side.%s",
657 g_units_per_side_max(), help_newline());
658 }
659 if (g_use_side_priority()) {
660 tbcatline(buf, "Sides move sequentially, in priority order.");
661 } else {
662 tbcatline(buf, "Sides move simultaneously.");
663 }
664 if (any_temp_variation) {
665 tbprintf(buf,
666 "Lowest possible temperature is %d, at an elevation of %d.%s",
667 g_temp_floor(), g_temp_floor_elev(), help_newline());
668 tbprintf(buf, "Temperatures averaged to range %d.%s",
669 g_temp_mod_range(), help_newline());
670 }
671 tbprintf(buf, "%sUnit Types:%s", help_newline(), help_newline());
672 for_all_unit_types(u) {
673 tbprintf(buf, "%s%s", help_indent(2), u_type_name(u));
674 if (!empty_string(u_help(u)))
675 tbprintf(buf, " (%s)", u_help(u));
676 tbcatline(buf, "");
677 #ifdef DESIGNERS
678 /* Show designers a bit more. */
679 if (numdesigners > 0) {
680 tbcat(buf, " [");
681 if (!empty_string(u_uchar(u)))
682 tbprintf(buf, "char '%s'", u_uchar(u));
683 else
684 tbcat(buf, "no char");
685 if (!empty_string(u_gchar(u)))
686 tbprintf(buf, "generic char '%s'", u_gchar(u));
687 else
688 tbcat(buf, "no generic char");
689 if (!empty_string(get_string(u_image_name(u))))
690 tbprintf(buf, ", image \"%s\"", get_string(u_image_name(u)));
691 if (!empty_string(u_generic_name(u)))
692 tbprintf(buf, ", generic name \"%s\"", u_generic_name(u));
693 if (u_desc_format(u) != lispnil) {
694 tbcat(buf, ", special format");
695 }
696 tbcatline(buf, "]");
697 }
698 #endif /* DESIGNERS */
699 }
700 tbprintf(buf, "%sTerrain Types:%s", help_newline(), help_newline());
701 for_all_terrain_types(t) {
702 tbprintf(buf, "%s%s", help_indent(2), t_type_name(t));
703 if (!empty_string(t_help(t)))
704 tbprintf(buf, " (%s)", t_help(t));
705 tbcatline(buf, "");
706 #ifdef DESIGNERS
707 /* Show designers a bit more. */
708 if (numdesigners > 0) {
709 tbcat(buf, " [");
710 if (!empty_string(t_char(t)))
711 tbprintf(buf, "char '%s'", t_char(t));
712 else
713 tbcat(buf, "no char");
714 if (!empty_string(t_image_name(t)))
715 tbprintf(buf, ", image \"%s\"", t_image_name(t));
716 tbcatline(buf, "]");
717 }
718 #endif /* DESIGNERS */
719 }
720 if (nummtypes > 0) {
721 tbprintf(buf, "%sMaterial Types:%s", help_newline(), help_newline());
722 for_all_material_types(m) {
723 tbprintf(buf, "%s%s", help_indent(2), m_type_name(m));
724 if (!empty_string(m_help(m)))
725 tbprintf(buf, " (%s)", m_help(m));
726 tbcatline(buf, "");
727 #ifdef DESIGNERS
728 /* Show designers a bit more. */
729 if (numdesigners > 0) {
730 tbcat(buf, " [");
731 if (!empty_string(m_char(m)))
732 tbprintf(buf, "char '%s'", m_char(m));
733 else
734 tbcat(buf, "no char");
735 if (!empty_string(m_image_name(m)))
736 tbprintf(buf, ", image \"%s\"", m_image_name(m));
737 tbcatline(buf, "]");
738 }
739 #endif /* DESIGNERS */
740 }
741 }
742 if (numatypes > 0) {
743 tbprintf(buf, "%sAdvances:%s", help_newline(), help_newline());
744 for_all_advance_types(a) {
745 tbprintf(buf, "%s%s", help_indent(2), a_type_name(a));
746 if (!empty_string(a_help(a)))
747 tbprintf(buf, " (%s)", a_help(a));
748 tbcatline(buf, "");
749 #ifdef DESIGNERS
750 /* Show designers a bit more. */
751 if (numdesigners > 0) {
752 tbcat(buf, " [");
753 if (!empty_string(a_image_name(a)))
754 tbprintf(buf, ", image \"%s\"", a_image_name(a));
755 tbcatline(buf, "]");
756 }
757 #endif /* DESIGNERS */
758 }
759 }
760 #ifdef DESIGNERS
761 /* Show designers a bit more. */
762 if (numdesigners > 0) {
763 tbcat(buf, "FOR DESIGNERS:\n");
764 tbprintf(buf, "Unseen terrain char is \"%s\".%s", g_unseen_char(),
765 help_newline());
766 tbprintf(buf, "Scorefile name is \"%s\".%s", g_scorefile_name(),
767 help_newline());
768 }
769 #endif /* DESIGNERS */
770 }
771
772 /* Display game module info to a side. */
773
774 static void
describe_game_modules(int arg,char * key,TextBuffer * buf)775 describe_game_modules(int arg, char *key, TextBuffer *buf)
776 {
777 if (mainmodule != NULL) {
778 /* First put out basic module info. */
779 describe_game_module_aux(buf, mainmodule, 0);
780 /* Now do the lengthy module notes (with no indentation). */
781 describe_module_notes(buf, mainmodule);
782 } else {
783 tbcat(buf, "(No game module information is available.)");
784 }
785 }
786
787 /* Recurse down through included modules to display docs on each.
788 Indents each file by inclusion level. Note that modules cannot
789 be loaded more than once, so each will be described only once here. */
790
791 static void
describe_game_module_aux(TextBuffer * buf,Module * module,int level)792 describe_game_module_aux(TextBuffer *buf, Module *module, int level)
793 {
794 int i;
795 char indentbuf[BUFSIZE];
796 char dashbuf[BUFSIZE];
797 int indentsz = 0;
798 Module *submodule;
799
800 dashbuf[0] = '\0';
801 indentbuf[0] = '\0';
802 indentsz = strlen(help_indent(3));
803 for (i = 0; i < level; ++i) {
804 strcat(dashbuf, "-- ");
805 strncat(indentbuf, help_indent(3), BUFSIZE - (indentsz * i));
806 }
807 tbprintf(buf, "%s\"%s\"", dashbuf,
808 (module->title ? module->title : module->name));
809 /* Display the true name of the module if not the same as the title. */
810 if (module->title != NULL && strcmp(module->title, module->name) != 0) {
811 tbprintf(buf, " (\"%s\")", module->name);
812 }
813 if (module->version != NULL) {
814 tbprintf(buf, " (version \"%s\")", module->version);
815 }
816 tbcatline(buf, help_indent(10));
817 if (module->blurb != lispnil) {
818 tbcat(buf, indentbuf);
819 append_notes(buf, module->blurb);
820 tbcatline(buf, "");
821 } else {
822 tbprintf(buf, "%s(no description)%s", indentbuf, help_newline());
823 }
824 if (module->notes != lispnil) {
825 tbprintf(buf, "%sNotes to \"%s\":%s", indentbuf, module->name,
826 help_newline());
827 append_notes(buf, module->notes);
828 tbcatline(buf, "");
829 }
830 tbcatline(buf, "");
831 /* Now describe any included modules. */
832 for_all_includes(module, submodule) {
833 describe_game_module_aux(buf, submodule, level + 1);
834 }
835 }
836
837 /* Dump the module designer's notes into the given buffer. When doing
838 submodules, don't indent. */
839
840 static void
describe_module_notes(TextBuffer * buf,Module * module)841 describe_module_notes(TextBuffer *buf, Module *module)
842 {
843 Module *submodule;
844
845 #ifdef DESIGNERS
846 /* Only show design notes if any designers around. */
847 if (numdesigners > 0 && module->designnotes != lispnil) {
848 tbprintf(buf, "Design Notes to \"%s\":%s", module->name,
849 help_newline());
850 append_notes(buf, module->designnotes);
851 tbcatline(buf, "");
852 }
853 #endif /* DESIGNERS */
854 for_all_includes(module, submodule) {
855 describe_module_notes(buf, submodule);
856 }
857 }
858
859 int
any_ut_capacity_x(int u)860 any_ut_capacity_x(int u)
861 {
862 int t;
863
864 for_all_terrain_types(t) {
865 if (ut_capacity_x(u, t) != 0)
866 return TRUE;
867 }
868 return FALSE;
869 }
870
871 int
any_mp_to_enter_unit(int u)872 any_mp_to_enter_unit(int u)
873 {
874 int u2;
875
876 for_all_unit_types(u2) {
877 if (uu_mp_to_enter(u, u2) != 0)
878 return TRUE;
879 }
880 return FALSE;
881 }
882
883 int
any_mp_to_leave_unit(int u)884 any_mp_to_leave_unit(int u)
885 {
886 int u2;
887
888 for_all_unit_types(u2) {
889 if (uu_mp_to_leave(u, u2) != 0)
890 return TRUE;
891 }
892 return FALSE;
893 }
894
895 int
any_enter_indep(int u)896 any_enter_indep(int u)
897 {
898 int u2;
899
900 for_all_unit_types(u2) {
901 if (uu_can_enter_indep(u, u2))
902 return TRUE;
903 }
904 return FALSE;
905 }
906
907 /* Full details on the given type of unit. */
908
909 static void
describe_utype(int u,char * key,TextBuffer * buf)910 describe_utype(int u, char *key, TextBuffer *buf)
911 {
912 int m, usesm, a;
913
914 append_help_phrase(buf, u_help(u));
915 /* Display the point value of the unit. */
916 if (u_point_value(u) > 0) {
917 tbprintf(buf, "Point Value: %d%s%s", u_point_value(u),
918 help_newline(), help_newline());
919 }
920 /* Display the designer's notes for this type. */
921 if (u_notes(u) != lispnil) {
922 tbcatline(buf, "Notes:");
923 append_notes(buf, u_notes(u));
924 tbprintf(buf, "%s%s", help_newline(), help_newline());
925 }
926 /* Display side attributes. */
927 describe_utype_side_attributes(u, key, buf);
928 /* Display action attributes. */
929 describe_utype_actions(u, key, buf);
930 /* Display movement attributes. */
931 describe_utype_movement(u, key, buf);
932 tbprintf(buf, "Hit Points (HP): %d.", u_hp_max(u));
933 if (u_parts(u) > 1) {
934 tbprintf(buf, " Parts: %d.", u_parts(u));
935 }
936 if (u_hp_recovery(u) != 0) {
937 tbprintf(buf, "%sRecovers by ", help_indent(2));
938 tb_fraction_desc(buf, u_hp_recovery(u));
939 tbprintf(buf, " HP each turn");
940 if (u_hp_to_recover(u) > 0)
941 tbprintf(buf, ", if over %d HP", u_hp_to_recover(u));
942 tbcat(buf, ".");
943 }
944 tbcatline(buf, "");
945 if (ut_table_row_not_default(u, ut_vanishes_on, 0)) {
946 tbprintf(buf, "Vanishes if in: ");
947 ut_table_row_desc(buf, u, ut_vanishes_on, tb_bool_desc, "");
948 tbcatline(buf, ".");
949 }
950 if (ut_table_row_not_default(u, ut_wrecks_on, 0)) {
951 tbprintf(buf, "Immediate wreck if in: ");
952 ut_table_row_desc(buf, u, ut_wrecks_on, tb_bool_desc, "");
953 tbcatline(buf, ".");
954 }
955 /* Describe unit's transport capabilities. */
956 if (u_capacity(u) > 0
957 || uu_table_row_not_default(u, uu_capacity_x, 0)) {
958 if (u_capacity(u) > 0) {
959 tbprintf(buf, "Generic capacity for units is %d.%s",
960 u_capacity(u), help_newline());
961 if (uu_table_column_not_default(u, uu_size, 1)) {
962 tbcat(buf, "Relative sizes of occupants: ");
963 uu_table_column_desc(buf, u, uu_size, NULL, NULL);
964 tbcatline(buf, ".");
965 }
966 }
967 if (uu_table_row_not_default(u, uu_capacity_x, 0)) {
968 tbcat(buf, "Dedicated space for units: ");
969 uu_table_row_desc(buf, u, uu_capacity_x, NULL, NULL);
970 tbcatline(buf, ".");
971 }
972 /* (should only display if < capacities would allow?) */
973 if (uu_table_row_not_default(u, uu_occ_max, -1)) {
974 tbcat(buf, "Maximum number of occupants: ");
975 uu_table_row_desc(buf, u, uu_occ_max, NULL, NULL);
976 tbcatline(buf, ".");
977 }
978 if (u_occ_total_max(u) >= 0) {
979 tbprintf(buf, "Maximum total of %d for all types together.%s",
980 u_occ_total_max(u), help_newline());
981 }
982 }
983 if (any_ut_capacity_x(u)) {
984 tbcat(buf, "Exclusive terrain capacity: ");
985 ut_table_row_desc(buf, u, ut_capacity_x, NULL, NULL);
986 tbcatline(buf, ".");
987 }
988 if (uu_table_row_not_default(u, uu_zoc_range, 0)) {
989 tbcat(buf, "Exerts ZOC out to: ");
990 uu_table_row_desc(buf, u, uu_zoc_range, NULL, NULL);
991 tbcatline(buf, ".");
992 if (ut_table_row_not_default(u, ut_zoc_into, 1)) {
993 tbcat(buf, "Exerts ZOC into: ");
994 ut_table_row_desc(buf, u, ut_zoc_into, tb_bool_desc, "in");
995 tbcatline(buf, ".");
996 }
997 if (ut_table_row_not_default(u, ut_zoc_from_terrain, 100)) {
998 tbcat(buf, "Effect of own terrain: ");
999 ut_table_row_desc(buf, u, ut_zoc_from_terrain, tb_mult_desc, "in");
1000 tbcatline(buf, ".");
1001 }
1002 if (uu_table_row_not_default(u, uu_mp_to_enter_zoc, -1)) {
1003 tbcat(buf, "MP to enter ZOC: ");
1004 uu_table_row_desc(buf, u, uu_mp_to_enter_zoc, NULL, NULL);
1005 tbcatline(buf, ".");
1006 }
1007 if (uu_table_row_not_default(u, uu_mp_to_leave_zoc, 0)) {
1008 tbcat(buf, "MP to leave ZOC: ");
1009 uu_table_row_desc(buf, u, uu_mp_to_leave_zoc, NULL, NULL);
1010 tbcatline(buf, ".");
1011 }
1012 if (uu_table_row_not_default(u, uu_mp_to_traverse_zoc, 0)) {
1013 tbcat(buf, "MP to traverse ZOC: ");
1014 uu_table_row_desc(buf, u, uu_mp_to_traverse_zoc, NULL, NULL);
1015 tbcatline(buf, ".");
1016 }
1017 }
1018 if (u_cxp_max(u) != 0) {
1019 tbprintf(buf, "Combat experience (CXP) maximum: %d.%s", u_cxp_max(u),
1020 help_newline());
1021 }
1022 if (u_morale_max(u) != 0) {
1023 tbprintf(buf, "Morale maximum: %d", u_morale_max(u));
1024 if (u_morale_recovery(u) != 0)
1025 tbprintf(buf, ", recover %s each turn", u_morale_recovery(u));
1026 tbcatline(buf, ".");
1027 }
1028 if (u_cp(u) != 1) {
1029 tbprintf(buf, "Construction points (CP): %d.%s", u_cp(u),
1030 help_newline());
1031 }
1032 if (u_tech_to_see(u) != 0) {
1033 tbprintf(buf, "Tech to see: %d.%s", u_tech_to_see(u),
1034 help_newline());
1035 }
1036 if (u_tech_to_own(u) != 0) {
1037 tbprintf(buf, "Tech to own: %d.%s", u_tech_to_own(u),
1038 help_newline());
1039 }
1040 if (u_tech_to_use(u) != 0) {
1041 tbprintf(buf, "Tech to use: %d.%s", u_tech_to_use(u),
1042 help_newline());
1043 }
1044 if (u_tech_to_build(u) != 0) {
1045 tbprintf(buf, "Tech to build: %d.%s", u_tech_to_build(u),
1046 help_newline());
1047 }
1048 if (u_tech_to_change_type_to(u)) {
1049 tbprintf(buf, "Tech to change type to: %d.%s",
1050 u_tech_to_change_type_to(u), help_newline());
1051 }
1052 if (u_tech_max(u) != 0) {
1053 tbprintf(buf, "Tech max: %d.%s", u_tech_max(u), help_newline());
1054 }
1055 if (u_tech_max(u) != 0 && u_tech_per_turn_max(u) != PROPHI) {
1056 tbprintf(buf, "Tech increase per turn max: %d.%s",
1057 u_tech_per_turn_max(u), help_newline());
1058 }
1059 if (u_tech_from_ownership(u) != 0) {
1060 tbprintf(buf, "Tech guaranteed by ownership: %d.%s",
1061 u_tech_from_ownership(u), help_newline());
1062 }
1063 if (u_tech_leakage(u) != 0) {
1064 tbprintf(buf, "Tech leakage: %d.%s", u_tech_leakage(u),
1065 help_newline());
1066 }
1067 if (type_max_acp(u) > 0
1068 && type_can_develop(u) > 0
1069 ) {
1070 tbprintf(buf, "%sDevelop:%s", help_newline(), help_newline());
1071 tbcat(buf, "ACP to develop: ");
1072 uu_table_row_desc(buf, u, uu_acp_to_develop, NULL, NULL);
1073 tbcatline(buf, ".");
1074 tbcat_si(buf, "Tech gained: ");
1075 uu_table_row_desc(buf, u, uu_tech_per_develop, tb_fraction_desc, NULL);
1076 tbcatline(buf, ".");
1077 }
1078 if (type_max_acp(u) > 0
1079 && (could_create_any(u) || could_build_any(u))) {
1080 tbprintf(buf, "%sConstruction:%s", help_newline(), help_newline());
1081 if (could_create_any(u)) {
1082 tbcat(buf, "ACP to create: ");
1083 uu_table_row_desc(buf, u, uu_acp_to_create, NULL, NULL);
1084 tbcatline(buf, ".");
1085 if (uu_table_row_not_default(u, uu_create_range, 0)) {
1086 tbcat_si(buf, "Creation distance max: ");
1087 uu_table_row_desc(buf, u, uu_create_range, NULL, NULL);
1088 tbcatline(buf, ".");
1089 }
1090 if (uu_table_row_not_default(u, uu_creation_cp, 1)) {
1091 tbcat_si(buf, "CP upon creation: ");
1092 uu_table_row_desc(buf, u, uu_creation_cp, NULL, NULL);
1093 tbcatline(buf, ".");
1094 }
1095 }
1096 if (could_build_any(u)) {
1097 tbcat(buf, "ACP to build: ");
1098 uu_table_row_desc(buf, u, uu_acp_to_build, NULL, NULL);
1099 tbcatline(buf, ".");
1100 if (uu_table_row_not_default(u, uu_cp_per_build, 1)) {
1101 tbcat_si(buf, "CP added per build: ");
1102 uu_table_row_desc(buf, u, uu_cp_per_build, NULL, NULL);
1103 tbcatline(buf, ".");
1104 }
1105 }
1106 if (u_cp_per_self_build(u) > 0) {
1107 tbprintf(buf,
1108 "Can finish building self at %d cp, will add %d cp per action.%s",
1109 u_cp_to_self_build(u), u_cp_per_self_build(u),
1110 help_newline());
1111 }
1112 if (uu_table_row_not_default(u, uu_build_range, 0)) {
1113 tbcat(buf, "Range at which can build: ");
1114 uu_table_row_desc(buf, u, uu_build_range, NULL, NULL);
1115 tbcatline(buf, ".");
1116 } else {
1117 tbcatline(buf, "Can build at own location.");
1118 }
1119 /* Toolup help. */
1120 if (could_toolup_for_any(u)) {
1121 tbcat(buf, "ACP to toolup: ");
1122 uu_table_row_desc(buf, u, uu_acp_to_toolup, NULL, NULL);
1123 tbcatline(buf, ".");
1124 tbcat_si(buf, "TP/toolup action: ");
1125 uu_table_row_desc(buf, u, uu_tp_per_toolup, NULL, NULL);
1126 tbcatline(buf, ".");
1127 /* (should put these with type beING built...) */
1128 tbcat_si(buf, "TP to build: ");
1129 uu_table_row_desc(buf, u, uu_tp_to_build, NULL, NULL);
1130 tbcatline(buf, ".");
1131 tbcat_si(buf, "TP max: ");
1132 uu_table_row_desc(buf, u, uu_tp_max, NULL, NULL);
1133 tbcatline(buf, ".");
1134 }
1135 }
1136 if ((type_max_acp(u) > 0
1137 && (could_attack_any(u)
1138 || could_fire_at_any(u) > 0
1139 || could_capture_any(u) > 0
1140 ))
1141 || may_detonate(u)
1142 || uu_table_row_not_default(u, uu_protection, 100)
1143 || uu_table_row_not_default(u, uu_retreat_chance, 0)
1144 || uu_table_row_not_default(u, uu_acp_retreat, 0)
1145 || u_wrecked_type(u) != NONUTYPE
1146 ) {
1147 tbprintf(buf, "%sCombat:%s", help_newline(), help_newline());
1148 if (could_attack_any(u)) {
1149 tbcat(buf, "Can attack (ACP ");
1150 uu_table_row_desc(buf, u, uu_acp_to_attack, NULL, "vs");
1151 tbcatline(buf, ").");
1152 if (uu_table_row_not_default(u, uu_attack_range, 1)) {
1153 tbcat(buf, "Attack range is ");
1154 uu_table_row_desc(buf, u, uu_attack_range, NULL, "vs");
1155 tbcatline(buf, ".");
1156 tbcat(buf, "Attack range min is ");
1157 uu_table_row_desc(buf, u, uu_attack_range_min, NULL, "vs");
1158 tbcatline(buf, ".");
1159 }
1160 }
1161 if (u_acp_to_fire(u) > 0) {
1162 tbprintf(buf, "Can fire (%d ACP), at ranges", u_acp_to_fire(u));
1163 if (u_range_min(u) > 0) {
1164 tbprintf(buf, " from %d", u_range_min(u));
1165 }
1166 tbprintf(buf, " up to %d", u_range(u));
1167 tbcatline(buf, ".");
1168 }
1169 tbcat(buf, "Hit chances are ");
1170 uu_table_row_desc(buf, u, uu_hit, tb_percent_desc, "vs");
1171 tbcatline(buf, ".");
1172 if (u_acp_to_fire(u) > 0) {
1173 if (uu_table_row_not_default(u, uu_fire_hit, -1)) {
1174 tbcat(buf, "Hit chances if firing are ");
1175 uu_table_row_desc(buf, u, uu_fire_hit, tb_percent_desc, "vs");
1176 tbcatline(buf, ".");
1177 } else {
1178 tbcatline(buf,
1179 "Hit chances if firing same as for regular combat.");
1180 }
1181 }
1182 if (ut_table_row_not_default(u, ut_attack_terrain_effect, 100)) {
1183 tbcat(buf, "Effect of attacker's terrain is ");
1184 ut_table_row_desc(buf, u, ut_attack_terrain_effect,
1185 tb_mult_desc, "in");
1186 tbcatline(buf, ".");
1187 }
1188 tbcat(buf, "Damage is ");
1189 uu_table_row_desc(buf, u, uu_damage, tb_dice_desc, "vs");
1190 tbcatline(buf, ".");
1191 if (uu_table_row_not_default(u, uu_tp_damage, 0)) {
1192 tbcat(buf, "Tooling damage is ");
1193 uu_table_row_desc(buf, u, uu_tp_damage, tb_dice_desc, NULL);
1194 tbcatline(buf, ".");
1195 }
1196 if (u_acp_to_fire(u) > 0) {
1197 if (uu_table_row_not_default(u, uu_fire_damage, -1)) {
1198 tbcat(buf, "Damage if firing is ");
1199 uu_table_row_desc(buf, u, uu_fire_damage, tb_dice_desc, "vs");
1200 tbcatline(buf, ".");
1201 } else {
1202 tbcatline(buf, "Damages if firing same as for regular combat.");
1203 }
1204 }
1205 if (uu_table_row_not_default(u, uu_acp_to_defend, 1)) {
1206 tbcat(buf, "If attacked, ACP to defend is ");
1207 uu_table_row_desc(buf, u, uu_acp_to_defend, NULL, "vs");
1208 tbcatline(buf, ".");
1209 }
1210 if (ut_table_row_not_default(u, ut_defend_terrain_effect, 100)) {
1211 tbcat(buf, "If attacked, effect of own terrain is ");
1212 ut_table_row_desc(buf, u, ut_defend_terrain_effect,
1213 tb_mult_desc, "in");
1214 tbcatline(buf, ".");
1215 }
1216 if (um_table_row_not_default(u, um_hit_by, 0)) {
1217 tbcat(buf, "Ammo needed to hit unit: ");
1218 um_table_row_desc(buf, u, um_hit_by, NULL);
1219 tbcatline(buf, ".");
1220 }
1221 if (could_capture_any(u) > 0) {
1222 tbcat(buf, "Can capture (ACP ");
1223 uu_table_row_desc(buf, u, uu_acp_to_capture, NULL, "vs");
1224 tbcatline(buf, ").");
1225 tbcat(buf, "Chance to capture: ");
1226 uu_table_row_desc(buf, u, uu_capture, tb_percent_desc, "vs");
1227 tbcatline(buf, ".");
1228 if (uu_table_row_not_default(u, uu_indep_capture, -1)) {
1229 tbcat(buf, "Chance to capture indep: ");
1230 uu_table_row_desc(buf, u, uu_indep_capture,
1231 tb_percent_desc, "vs");
1232 tbcatline(buf, ".");
1233 }
1234 }
1235 if (may_detonate(u)) {
1236 /* Display all the different ways that a unit might detonate. */
1237 if (u_acp_to_detonate(u) > 0) {
1238 tbprintf(buf, "Can detonate self (%d ACP).%s",
1239 u_acp_to_detonate(u), help_newline());
1240 }
1241 if (u_detonate_on_death(u)) {
1242 tbprintf(buf,
1243 "%d%% chance to detonate if destroyed in combat.%s",
1244 u_detonate_on_death(u), help_newline());
1245 }
1246 if (uu_table_row_not_default(u, uu_detonate_on_hit, 0)) {
1247 tbcat(buf, "Chance to detonate upon being hit: ");
1248 uu_table_row_desc(buf, u, uu_detonate_on_hit, tb_percent_desc,
1249 NULL);
1250 tbcatline(buf, ".");
1251 }
1252 if (uu_table_row_not_default(u, uu_detonate_on_capture, 0)) {
1253 tbcat(buf, "Chance to detonate upon capture: ");
1254 uu_table_row_desc(buf, u, uu_detonate_on_capture,
1255 tb_percent_desc, NULL);
1256 tbcatline(buf, ".");
1257 }
1258 if (uu_table_row_not_default(u, uu_detonate_approach_range, -1)) {
1259 tbcat(buf, "Will detonate upon approach within range: ");
1260 uu_table_row_desc(buf, u, uu_detonate_approach_range,
1261 NULL, NULL);
1262 tbcatline(buf, ".");
1263 }
1264 if (ut_table_row_not_default(u, ut_detonation_accident, 0)) {
1265 tbcat(buf, "Chance of accidental detonation: ");
1266 ut_table_row_desc(buf, u, ut_detonation_accident,
1267 tb_percent_desc, "in");
1268 tbcatline(buf, ".");
1269 }
1270 tbcat(buf, "Detonation damage at ground zero is ");
1271 uu_table_row_desc(buf, u, uu_detonation_damage_at, tb_dice_desc,
1272 NULL);
1273 tbcatline(buf, ".");
1274 /* (should only display if effect range > 0) */
1275 tbcat(buf, "Detonation damage to adjacent units is ");
1276 uu_table_row_desc(buf, u, uu_detonation_damage_adj, tb_dice_desc,
1277 NULL);
1278 tbcatline(buf, ".");
1279 tbcat(buf, "Range of detonation effect on units is ");
1280 uu_table_row_desc(buf, u, uu_detonation_range, NULL, NULL);
1281 tbcatline(buf, ".");
1282 /* Damage decreases as inverse square of distance. */
1283 if (ut_table_row_not_default(u, ut_detonation_damage, 0)) {
1284 tbcat(buf, "Chance of detonation damage to terrain is ");
1285 ut_table_row_desc(buf, u, ut_detonation_damage, NULL, NULL);
1286 tbcatline(buf, ".");
1287 tbcat(buf, "Range of detonation effect on terrain is ");
1288 ut_table_row_desc(buf, u, ut_detonation_range, NULL, NULL);
1289 tbcatline(buf, ".");
1290 }
1291 if (u_hp_per_detonation(u) < u_hp_max(u)) {
1292 tbprintf(buf, "Loses %d HP per detonation.%s",
1293 u_hp_per_detonation(u), help_newline());
1294 } else {
1295 tbcatline(buf, "Always destroyed by detonation.");
1296 }
1297 }
1298 if (uu_table_row_not_default(u, uu_hp_min, 0)) {
1299 tbcat(buf, "Combat never reduces defender's HP below ");
1300 uu_table_row_desc(buf, u, uu_hp_min, NULL, "vs");
1301 tbcatline(buf, ".");
1302 }
1303 if (uu_table_row_not_default(u, uu_protection, 100)) {
1304 tbcat(buf, "Protection of occupants/transport is ");
1305 uu_table_row_desc(buf, u, uu_protection, tb_mult_desc, NULL);
1306 tbcatline(buf, ".");
1307 }
1308 if (uu_table_row_not_default(u, uu_occ_combat, 100)) {
1309 tbcat(buf, "Combat effectiveness as occupant is ");
1310 uu_table_row_desc(buf, u, uu_occ_combat, tb_percent_desc, "in");
1311 tbcatline(buf, ".");
1312 }
1313 if (uu_table_row_not_default(u, uu_retreat_chance, 0)) {
1314 tbcat(buf, "Chance to retreat from combat is ");
1315 uu_table_row_desc(buf, u, uu_retreat_chance, tb_percent_desc,
1316 "vs");
1317 tbcatline(buf, ".");
1318 }
1319 if (uu_table_row_not_default(u, uu_acp_retreat, 0)) {
1320 tbcat(buf, "Extra ACP for retreating is ");
1321 uu_table_row_desc(buf, u, uu_acp_retreat, NULL, "vs");
1322 tbcatline(buf, ".");
1323 }
1324 if (u_wrecked_type(u) != NONUTYPE) {
1325 tbprintf(buf, "Becomes %s when destroyed.%s",
1326 u_type_name(u_wrecked_type(u)), help_newline());
1327 }
1328 }
1329 if (type_max_acp(u) > 0
1330 && (u_acp_to_change_side(u) > 0
1331 || u_acp_to_disband(u) > 0
1332 || u_acp_to_transfer_part(u) > 0
1333 || uu_table_row_not_default(u, uu_acp_to_repair, 0)
1334 || (uu_table_row_not_default(u, uu_change_type_to, 0)
1335 && uu_table_row_not_default(u, uu_acp_to_change_type, 0))
1336 || ut_table_row_not_default(u, ut_acp_to_add_terrain, 0)
1337 || ut_table_row_not_default(u, ut_acp_to_remove_terrain, 0)
1338 )) {
1339 tbprintf(buf, "%sOther Actions:%s", help_newline(), help_newline());
1340 if (u_acp_to_change_side(u) > 0) {
1341 tbprintf(buf, "Can be given to another side (%d ACP).%s",
1342 u_acp_to_change_side(u), help_newline());
1343 }
1344 if (u_acp_to_disband(u) > 0) {
1345 tbprintf(buf, "Can be disbanded (%d ACP)", u_acp_to_disband(u));
1346 if (u_hp_per_disband(u) < u_hp_max(u)) {
1347 tbprintf(buf, ", losing %d HP per action",
1348 u_hp_per_disband(u));
1349 }
1350 tbcatline(buf, ".");
1351 if (um_table_row_not_default(u, um_supply_per_disband, 0)) {
1352 tbprintf(buf, "Supply yield per disband action: ");
1353 um_table_row_desc(buf, u, um_supply_per_disband, NULL);
1354 tbcatline(buf, ".");
1355 }
1356 if (um_table_row_not_default(u, um_recycleable, 0)) {
1357 tbprintf(buf, "Additional material when unit is gone: ");
1358 um_table_row_desc(buf, u, um_recycleable, NULL);
1359 tbcatline(buf, ".");
1360 }
1361 }
1362 if (u_acp_to_transfer_part(u) > 0) {
1363 tbprintf(buf, "Can transfer parts (%d ACP).%s",
1364 u_acp_to_transfer_part(u), help_newline());
1365 }
1366 if (uu_table_row_not_default(u, uu_acp_to_repair, 0)) {
1367 tbcat(buf, "ACP for explicit repair is ");
1368 uu_table_row_desc(buf, u, uu_acp_to_repair, NULL, NULL);
1369 tbcatline(buf, ".");
1370 tbcat(buf, "Explicit repair performance is ");
1371 uu_table_row_desc(buf, u, uu_hp_per_repair, NULL, NULL);
1372 tbcatline(buf, ".");
1373 }
1374 /* (More work still needs to be done, so that we can accomodate
1375 'u_auto_upgrade_to' and ACP-less 'change-type' possibilities.) */
1376 if (uu_table_row_not_default(u, uu_change_type_to, FALSE)) {
1377 if (uu_table_row_not_default(u, uu_acp_to_change_type, 0)) {
1378 tbcat(buf, "ACP to change type is ");
1379 uu_table_row_desc(buf, u, uu_acp_to_change_type, NULL, NULL);
1380 tbcatline(buf, ".");
1381 if (um_table_row_not_default(u, um_to_change_type, 0)) {
1382 tbcat(buf, "Material to change type is ");
1383 um_table_row_desc(buf, u, um_to_change_type, NULL);
1384 tbcatline(buf, ".");
1385 }
1386 if (ua_table_row_not_default(u, ua_to_change_type, FALSE)) {
1387 tbcat(buf, "Advance to change type is ");
1388 ua_table_row_desc(buf, u, ua_to_change_type, tb_bool_desc);
1389 tbcatline(buf, ".");
1390 }
1391 if (uu_table_row_not_default(u, uu_size_to_change_type, 0)) {
1392 tbcat(buf, "Size to change type is ");
1393 uu_table_row_desc(buf, u, uu_size_to_change_type, NULL,
1394 NULL);
1395 tbcatline(buf, ".");
1396 }
1397 if (uu_table_row_not_default(u, uu_cxp_to_change_type, 0)) {
1398 tbcat(buf, "Combat experience to change type is ");
1399 uu_table_row_desc(buf, u, uu_cxp_to_change_type, NULL,
1400 NULL);
1401 tbcatline(buf, ".");
1402 }
1403 }
1404 }
1405 if (ut_table_row_not_default(u, ut_acp_to_add_terrain, 0)) {
1406 tbcat(buf, "ACP to add/change terrain is ");
1407 ut_table_row_desc(buf, u, ut_acp_to_add_terrain, NULL, NULL);
1408 tbcatline(buf, ".");
1409 if (ut_table_row_not_default(u, ut_alter_range, 0)) {
1410 tbcat_si(buf, "Can alter up to range ");
1411 ut_table_row_desc(buf, u, ut_alter_range, NULL, NULL);
1412 tbcatline(buf, ".");
1413 }
1414 }
1415 if (ut_table_row_not_default(u, ut_acp_to_remove_terrain, 0)) {
1416 tbcat(buf, "ACP to remove terrain is ");
1417 ut_table_row_desc(buf, u, ut_acp_to_remove_terrain, NULL, NULL);
1418 tbcatline(buf, ".");
1419 if (ut_table_row_not_default(u, ut_alter_range, 0)) {
1420 tbcat_si(buf, "Can alter up to range ");
1421 ut_table_row_desc(buf, u, ut_alter_range, NULL, NULL);
1422 tbcatline(buf, ".");
1423 }
1424 }
1425 }
1426 if (!g_see_all()) {
1427 tbprintf(buf, "%sVision:%s", help_newline(), help_newline());
1428 tbprintf(buf, "%d%% chance to be seen at outset of game.%s",
1429 u_already_seen(u), help_newline());
1430 tbprintf(buf,
1431 "%d%% chance to be seen at outset of game if independent.%s",
1432 u_already_seen_indep(u), help_newline());
1433 if (u_see_always(u))
1434 tbcatline(buf, "Always seen if terrain has been seen.");
1435 else
1436 tbcatline(buf, "Not always seen even if terrain has been seen.");
1437 /* (should only say if can be an occupant) */
1438 if (uu_table_row_not_default(u, uu_occ_vision, 100)) {
1439 tbcat(buf, "Vision effect when occupying: ");
1440 uu_table_row_desc(buf, u, uu_occ_vision, NULL, "in");
1441 tbcatline(buf, ".");
1442 }
1443 /* (should only say if unit can have occupants) */
1444 if (u_see_occupants(u))
1445 tbcatline(buf, "Occupants seen if unit has been seen.");
1446 else
1447 tbcatline(buf, "Occupants not seen even if unit has been seen.");
1448 switch (u_vision_range(u)) {
1449 case -1:
1450 tbcatline(buf, "Can never see other units.");
1451 break;
1452 case 0:
1453 tbcatline(buf, "Can see other units at own location.");
1454 break;
1455 case 1:
1456 /* Default range, no need to say anything. */
1457 break;
1458 default:
1459 tbprintf(buf,
1460 "Can see units up to %d cells away.%s", u_vision_range(u),
1461 help_newline());
1462 break;
1463 }
1464 if (u_vision_range(u) > 0 && !u_can_see_behind(u)) {
1465 tbcat(buf, "Vision is line-of-sight");
1466 if (u_can_see_behind(u) > 0)
1467 tbprintf(buf, ", seeing behind obstacles.");
1468 tbcatline(buf, ".");
1469 if (ut_table_row_not_default(u, ut_eye_height, 0)) {
1470 tbcat(buf, "Effective eye height is ");
1471 ut_table_row_desc(buf, u, ut_eye_height, NULL, "in");
1472 tbcatline(buf, ".");
1473 }
1474 }
1475 if (ut_table_row_not_default(u, ut_body_height, 0)) {
1476 tbcat(buf, "Effective body height is ");
1477 ut_table_row_desc(buf, u, ut_body_height, NULL, "in");
1478 tbcatline(buf, ".");
1479 }
1480 if (ut_table_row_not_default(u, ut_weapon_height, 0)) {
1481 tbcat(buf, "Effective weapon height is ");
1482 ut_table_row_desc(buf, u, ut_weapon_height, NULL, "in");
1483 tbcatline(buf, ".");
1484 }
1485 if (u_vision_range(u) >= 0
1486 && uu_table_row_not_default(u, uu_see_at, 100)) {
1487 tbcat(buf, "Chance to see if in same cell is ");
1488 uu_table_row_desc(buf, u, uu_see_at, tb_percent_desc, NULL);
1489 tbcatline(buf, ".");
1490 }
1491 if (u_vision_range(u) >= 1
1492 && uu_table_row_not_default(u, uu_see_adj, 100)) {
1493 tbcat(buf, "Chance to see if adjacent is ");
1494 uu_table_row_desc(buf, u, uu_see_adj, tb_percent_desc, NULL);
1495 tbcatline(buf, ".");
1496 }
1497 if (u_vision_range(u) >= 2
1498 && uu_table_row_not_default(u, uu_see, 100)) {
1499 tbcat(buf, "Chance to see in general is ");
1500 uu_table_row_desc(buf, u, uu_see, tb_percent_desc, NULL);
1501 tbcatline(buf, ".");
1502 }
1503 if (u_see_terrain_captured(u) > 0)
1504 tbprintf(buf,
1505 "%d%% chance for enemy to see your terrain view if this type captured.%s",
1506 u_see_terrain_captured(u), help_newline());
1507 }
1508 if (ut_table_row_not_default(u, ut_accident_hit, 0)
1509 || ut_table_row_not_default(u, ut_accident_vanish, 0)) {
1510 tbprintf(buf, "%sAccidents:%s", help_newline(), help_newline());
1511 if (ut_table_row_not_default(u, ut_accident_hit, 0)) {
1512 tbcat(buf, "Chance to be damaged in an accident: ");
1513 ut_table_row_desc(buf, u, ut_accident_hit, tb_percent_100th_desc,
1514 "in");
1515 tbcatline(buf, ".");
1516 tbcat(buf, "Amount of damage: ");
1517 ut_table_row_desc(buf, u, ut_accident_damage, tb_dice_desc, "in");
1518 tbcatline(buf, ".");
1519 }
1520 if (ut_table_row_not_default(u, ut_accident_vanish, 0)) {
1521 tbcat(buf, "Chance to vanish in an accident: ");
1522 ut_table_row_desc(buf, u, ut_accident_vanish,
1523 tb_percent_100th_desc, "in");
1524 tbcatline(buf, ".");
1525 }
1526 }
1527 if (ut_table_row_not_default(u, ut_attrition, 0)) {
1528 tbprintf(buf, "%sAttrition in HP:%s", help_newline(), help_newline());
1529 ut_table_row_desc(buf, u, ut_attrition, tb_probfraction_desc, "in");
1530 tbcatline(buf, ".");
1531 }
1532 /* Don't bother with this if economy code is not running. */
1533 if (nummtypes > 0 && g_economy()) {
1534 tbprintf(buf, "%sMaterial Handling:%s", help_newline(), help_newline());
1535 for_all_material_types(m) {
1536 usesm = FALSE;
1537 tbprintf(buf, "%s%s", help_indent(2), m_type_name(m));
1538 if (um_base_production(u, m) > 0) {
1539 tbprintf(buf, ", %d basic production",
1540 um_base_production(u, m));
1541 usesm = TRUE;
1542 }
1543 if (um_occ_production(u, m) >= 0) {
1544 tbprintf(buf, ", %d basic production if occupant",
1545 um_occ_production(u, m));
1546 usesm = TRUE;
1547 }
1548 if (um_acp_to_extract(u, m) > 0) {
1549 tbprintf(buf, ", %d ACP to extract", um_acp_to_extract(u, m));
1550 usesm = TRUE;
1551 }
1552 if (um_storage_x(u, m) > 0) {
1553 tbprintf(buf, ", %d storage", um_storage_x(u, m));
1554 if (um_initial(u, m) > 0) {
1555 tbprintf(buf, " (%d at start of game)",
1556 min(um_initial(u, m), um_storage_x(u, m)));
1557 }
1558 usesm = TRUE;
1559 }
1560 if (um_base_consumption(u, m) > 0) {
1561 tbprintf(buf, ", %d basic consumption",
1562 um_base_consumption(u, m));
1563 if (um_consumption_as_occupant(u, m) != 100) {
1564 tbprintf(buf, ", times %d%% if occupant",
1565 um_consumption_as_occupant(u, m));
1566 }
1567 usesm = TRUE;
1568 }
1569 if (um_to_act(u, m) != 0) {
1570 tbprintf(buf, ", needs %d to act at all",
1571 um_to_act(u, m));
1572 usesm = TRUE;
1573 }
1574 if (um_to_move(u, m) != 0) {
1575 tbprintf(buf, ", needs %d to move",
1576 um_to_move(u, m));
1577 usesm = TRUE;
1578 }
1579 if (um_consumption_per_move(u, m) != 0) {
1580 tbprintf(buf, ", %d consumed per move",
1581 um_consumption_per_move(u, m));
1582 usesm = TRUE;
1583 }
1584 if (um_to_attack(u, m) != 0) {
1585 tbprintf(buf, ", needs %d (weapons or equipment) to attack",
1586 um_to_attack(u, m));
1587 usesm = TRUE;
1588 }
1589 if (um_consumption_per_attack(u, m) != 0) {
1590 tbprintf(buf, ", %d (ammo) consumed per attack",
1591 um_consumption_per_attack(u, m));
1592 usesm = TRUE;
1593 }
1594 if (u_acp_to_fire(u) > 0 && um_to_fire(u, m) != 0) {
1595 tbprintf(buf, ", needs %d (weapons or equipment) to fire",
1596 um_to_fire(u, m));
1597 usesm = TRUE;
1598 }
1599 if (u_acp_to_fire(u) > 0 && um_consumption_per_fire(u, m) != 0) {
1600 tbprintf(buf, ", %d (ammo) consumed when firing",
1601 um_consumption_per_fire(u, m));
1602 usesm = TRUE;
1603 }
1604 if (um_to_create(u, m) != 0) {
1605 tbprintf(buf, ", builder needs %d to create",
1606 um_to_create(u, m));
1607 usesm = TRUE;
1608 }
1609 if (um_consumption_on_creation(u, m) != 0) {
1610 tbprintf(buf, ", %d used in creation",
1611 um_consumption_on_creation(u, m));
1612 usesm = TRUE;
1613 }
1614 if (um_to_build(u, m) != 0) {
1615 tbprintf(buf, ", builder needs %d to build anything",
1616 um_to_build(u, m));
1617 usesm = TRUE;
1618 }
1619 if (um_consumption_per_build(u, m) != 0) {
1620 tbprintf(buf, ", %d used to build anything",
1621 um_consumption_per_build(u, m));
1622 usesm = TRUE;
1623 }
1624 if (um_consumption_per_built(u, m) != 0) {
1625 tbprintf(buf, ", %d used to perform a build upon",
1626 um_consumption_per_built(u, m));
1627 usesm = TRUE;
1628 }
1629 if (um_consumption_per_cp(u, m) != 0) {
1630 tbprintf(buf, ", %d used to add 1 CP to",
1631 um_consumption_per_cp(u, m));
1632 usesm = TRUE;
1633 }
1634 if (um_to_repair(u, m) != 0) {
1635 tbprintf(buf, ", repairer needs %d to repair",
1636 um_to_repair(u, m));
1637 usesm = TRUE;
1638 }
1639 if (um_consumption_per_repair(u, m) != 0) {
1640 tbprintf(buf, ", %d used to restore 1 HP",
1641 um_consumption_per_repair(u, m));
1642 usesm = TRUE;
1643 }
1644 if (usesm) {
1645 if (um_inlength(u, m) > 0) {
1646 tbprintf(buf, ", receive from up to %d cells away",
1647 um_inlength(u, m));
1648 }
1649 if (um_outlength(u, m) > 0) {
1650 tbprintf(buf, ", send up to %d cells away",
1651 um_outlength(u, m));
1652 }
1653 } else {
1654 tbcat(buf, " (none)");
1655 }
1656 tbcatline(buf, "");
1657 }
1658 /* Productivity adjustment due to terrain applies to all
1659 material types equally, but only display if unit
1660 produces. */
1661 if (um_table_row_not_default(u, um_base_production, 0)) {
1662 if (ut_table_row_not_default(u, ut_productivity, 100)) {
1663 tbcat(buf, "Productivity adjustment for terrain is ");
1664 ut_table_row_desc(buf, u, ut_productivity, tb_mult_desc, "in");
1665 tbcatline(buf, ".");
1666 }
1667 if (ut_table_row_not_default(u, ut_productivity_adj, 0)) {
1668 tbcat(buf, "Productivity adjustment for adjacent terrain is ");
1669 ut_table_row_desc(buf, u, ut_productivity_adj, tb_mult_desc,
1670 "in");
1671 tbcatline(buf, ".");
1672 }
1673 if (um_table_row_not_default(u, um_productivity_min, 0)) {
1674 tbcat(buf, "Minimum net adjustment is ");
1675 um_table_row_desc(buf, u, um_productivity_min, tb_mult_desc);
1676 tbcatline(buf, ".");
1677 }
1678 if (um_table_row_not_default(u, um_productivity_max, TABHI)) {
1679 tbcat(buf, "Maximum net adjustment is ");
1680 um_table_row_desc(buf, u, um_productivity_max, tb_mult_desc);
1681 tbcatline(buf, ".");
1682 }
1683 }
1684 }
1685 if (numatypes > 0) {
1686 /* Now also prints "None" if no advances are required. */
1687 int found = FALSE;
1688
1689 tbprintf(buf, "%sRequired advances to build: ", help_newline());
1690 for_all_advance_types(a) {
1691 if (ua_needed_to_build(u, a) > 0) {
1692 if (found)
1693 tbprintf(buf, ", ");
1694 tbprintf(buf, "%s", a_type_name(a));
1695 found = TRUE;
1696 }
1697 }
1698 if (found)
1699 tbcatline(buf, ".");
1700 else
1701 tbcatline(buf, "None.");
1702 }
1703 tbcatline(buf, "");
1704 /* (should display weather interaction here) */
1705 if (uu_table_row_not_default(u, uu_auto_repair, 0)) {
1706 tbcat(buf, "Can auto-repair lost HP of other units: ");
1707 uu_table_row_desc(buf, u, uu_auto_repair, tb_probfraction_desc, NULL);
1708 tbcatline(buf, ".");
1709 if (uu_table_row_not_default(u, uu_auto_repair_range, 0)) {
1710 tbcat(buf, "Range of auto-repair: ");
1711 uu_table_row_desc(buf, u, uu_auto_repair_range, NULL, NULL);
1712 tbcatline(buf, ".");
1713 }
1714 }
1715 if (u_spy_chance(u) > 0 /* and random event in use */) {
1716 tbprintf(buf, "%d%% chance to spy, on units up to %d away.%s",
1717 u_spy_chance(u), u_spy_range(u), help_newline());
1718 }
1719 if (u_revolt(u) > 0 /* and random event in use */) {
1720 tb_fraction_desc(buf, u_revolt(u));
1721 tbcatline(buf, "% chance of revolt.");
1722 }
1723 if (u_lost_wreck(u) > 0
1724 || u_lost_vanish(u) > 0
1725 || u_lost_revolt(u) > 0
1726 || uu_table_row_not_default(u, uu_lost_surrender, 0)) {
1727 tbprintf(buf, "%sFate if side loses:", help_newline());
1728 if (u_lost_wreck(u) > 0) {
1729 tbcat(buf, " ");
1730 tb_percent_100th_desc(buf, u_lost_wreck(u));
1731 tbcat(buf, " chance to wreck");
1732 }
1733 if (u_lost_wreck(u) < 10000 && u_lost_vanish(u) > 0) {
1734 tbcat(buf, " ");
1735 tb_percent_100th_desc(buf, u_lost_vanish(u));
1736 tbcat(buf, " chance to vanish");
1737 }
1738 if (u_lost_wreck(u) < 10000
1739 && u_lost_vanish(u) < 10000
1740 && u_lost_revolt(u) > 0) {
1741 tbcat(buf, " ");
1742 tb_percent_100th_desc(buf, u_lost_revolt(u));
1743 tbcat(buf, " chance to revolt");
1744 }
1745 if (u_lost_wreck(u) < 10000
1746 && u_lost_vanish(u) < 10000
1747 && u_lost_revolt(u) < 10000
1748 && uu_table_row_not_default(u, uu_lost_surrender, 0)) {
1749 tbcat(buf, " chance to surrender to nearby unit, ");
1750 uu_table_row_desc(buf, u, uu_lost_surrender,
1751 tb_percent_100th_desc, "to");
1752 }
1753 if (u_lost_wreck(u) == 0
1754 && u_lost_vanish(u) == 0
1755 && u_lost_revolt(u) == 0
1756 && !uu_table_row_not_default(u, uu_lost_surrender, 0))
1757 tbcat(buf, " survival");
1758 tbprintf(buf, ".%s%s", help_newline(), help_newline());
1759 }
1760 if (u_type_in_game_max(u) >= 0) {
1761 tbprintf(buf, "At most %d allowed in a game.%s", u_type_in_game_max(u),
1762 help_newline());
1763 }
1764 #ifdef DESIGNERS
1765 if (numdesigners > 0) {
1766 tbcat(buf, "FOR DESIGNERS:\n");
1767 tbprintf(buf, "Internal name is \"%s\".%s", u_internal_name(u),
1768 help_newline());
1769 tbprintf(buf, "Short name is \"%s\".%s", u_short_name(u),
1770 help_newline());
1771 if (u_assign_number(u))
1772 tbcatline(buf, "New units get assigned a number.");
1773 tbcatline(buf, "");
1774 }
1775 #endif /* DESIGNERS */
1776 describe_utype_ai_attributes(u, key, buf);
1777 }
1778
1779 /* Describe the unit type's ability to move. */
1780
1781 static void
describe_utype_movement(int u,char * key,TextBuffer * buf)1782 describe_utype_movement(int u, char *key, TextBuffer *buf)
1783 {
1784 int speedvaries = FALSE;
1785
1786 tbcatline(buf,
1787 "Movement, Movement Points (MP), and Speeds (MP/ACP Ratios):");
1788 if (type_max_speed(u) <= 0) {
1789 tbprintf(buf, "%sIs always immobile.%s", help_indent(2),
1790 help_newline());
1791 }
1792 else {
1793 if (u_acp_to_move(u) > 0) {
1794 tbprintf(buf, "%sUses a minimum of %d ACP per move.%s",
1795 help_indent(2), u_acp_to_move(u), help_newline());
1796 } else {
1797 tbprintf(buf, "%sCannot move by its own action.%s",
1798 help_indent(2), help_newline());
1799 }
1800 if (u_speed(u) != uprop_i_default(u_speed)) {
1801 tbprintf(buf, "%sUnadjusted speed is ", help_indent(2));
1802 tb_percent_desc(buf, u_speed(u));
1803 tbcatline(buf, ".");
1804 }
1805 if (uu_table_row_not_default(u, uu_occ_adds_speed,
1806 table_default(uu_occ_adds_speed))) {
1807 tbprintf(buf, "%sOccupant adds speed: ", help_indent(2));
1808 uu_table_row_desc(buf, u, uu_occ_adds_speed, tb_percent_desc, NULL);
1809 tbcatline(buf, ".");
1810 speedvaries = TRUE;
1811 }
1812 if (uu_table_row_not_default(u, uu_occ_multiplies_speed,
1813 table_default(uu_occ_multiplies_speed))) {
1814 tbprintf(buf, "%sOccupant multiplies speed: ", help_indent(2));
1815 uu_table_row_desc(buf, u, uu_occ_multiplies_speed, tb_mult_desc,
1816 NULL);
1817 tbcatline(buf, ".");
1818 speedvaries = TRUE;
1819 }
1820 if (u_speed_wind_effect(u) != uprop_l_default(u_speed_wind_effect)) {
1821 /* (should add mech to describe in detail) */
1822 tbprintf(buf, "%sWind affects speed.%s", help_indent(2),
1823 help_newline());
1824 speedvaries = TRUE;
1825 }
1826 if (u_speed_damage_effect(u) !=
1827 uprop_l_default(u_speed_damage_effect)) {
1828 /* (should add mech to describe in detail) */
1829 tbprintf(buf, "%sDamage affects speed.%s", help_indent(2),
1830 help_newline());
1831 speedvaries = TRUE;
1832 }
1833 /* (should only list variation limits if actually needed to clip) */
1834 if (speedvaries) {
1835 tbprintf(buf, "%sSpeed variation limited to between ",
1836 help_indent(2));
1837 tb_percent_desc(buf, u_speed_min(u));
1838 tbcat(buf, " and ");
1839 tb_percent_desc(buf, u_speed_max(u));
1840 tbcatline(buf, ".");
1841 }
1842 tbprintf(buf, "%sNeeds MP to enter terrain: ", help_indent(2));
1843 ut_table_row_desc(buf, u, ut_mp_to_enter, NULL, "into");
1844 tbcatline(buf, ".");
1845 if (ut_table_row_not_default(u, ut_mp_to_leave,
1846 table_default(ut_mp_to_leave))) {
1847 tbprintf(buf, "%sNeeds MP to leave terrain: ", help_indent(2));
1848 ut_table_row_desc(buf, u, ut_mp_to_leave, NULL, "in");
1849 tbcatline(buf, ".");
1850 }
1851 if (ut_table_row_not_default(u, ut_mp_to_traverse,
1852 table_default(ut_mp_to_traverse))) {
1853 tbprintf(buf, "%sNeeds MP to traverse terrain: ", help_indent(2));
1854 ut_table_row_desc(buf, u, ut_mp_to_traverse, NULL, "across");
1855 tbcatline(buf, ".");
1856 }
1857 if (any_mp_to_enter_unit(u)) {
1858 tbprintf(buf, "%sMP to enter unit: ", help_indent(2));
1859 uu_table_row_desc(buf, u, uu_mp_to_enter, NULL, "into");
1860 tbcatline(buf, ".");
1861 }
1862 if (any_mp_to_leave_unit(u)) {
1863 tbprintf(buf, "%sMP to leave unit: ", help_indent(2));
1864 uu_table_row_desc(buf, u, uu_mp_to_leave, NULL, "in");
1865 tbcatline(buf, ".");
1866 }
1867 if (any_enter_indep(u)) {
1868 tbprintf(buf, "%sCan enter independent units: ", help_indent(2));
1869 uu_table_row_desc(buf, u, uu_can_enter_indep, tb_bool_desc, NULL);
1870 tbcatline(buf, ".");
1871 }
1872 if (u_mp_to_leave_world(u) > uprop_i_default(u_mp_to_leave_world)) {
1873 tbprintf(buf, "%sNeeds %d MP to leave the world entirely.%s",
1874 help_indent(2), u_mp_to_leave_world(u), help_newline());
1875 }
1876 if (u_free_mp(u) > 0) {
1877 tbprintf(buf,
1878 "%sGets up to %d free MP if needed to finish a move.%s",
1879 help_indent(2), u_free_mp(u), help_newline());
1880 }
1881 }
1882 tbcatline(buf, "");
1883 }
1884
1885 /* Describe the unit type's ability to act. */
1886
1887 static void
describe_utype_actions(int u,char * key,TextBuffer * buf)1888 describe_utype_actions(int u, char *key, TextBuffer *buf)
1889 {
1890 tbcatline(buf, "Actions and Action Points (ACP):");
1891 /* (Should enumerate possible actions here.) */
1892 /* ACP independent. */
1893 if (u_acp_independent(u)) {
1894 tbprintf(buf, "%sActs independently of ACP restrictions.%s",
1895 help_indent(2), help_newline());
1896 }
1897 /* Actionless. */
1898 else if (type_max_acp(u) <= 0) {
1899 tbprintf(buf, "%sDoes not act.%s", help_indent(2), help_newline());
1900 }
1901 /* ACP-dependent actions. */
1902 else {
1903 /* Limits on total ACP. */
1904 if (u_acp_min(u) != uprop_i_default(u_acp_min))
1905 tbprintf(buf, "%sCan act until %d ACP left.%s", help_indent(2),
1906 u_acp_min(u), help_newline());
1907 if (u_acp_max(u) != uprop_i_default(u_acp_max)) {
1908 tbprintf(buf, "%sWill never exceed %d ACP.%s", help_indent(2),
1909 u_acp_max(u), help_newline());
1910 }
1911 /* Limits on new ACP per turn. */
1912 if (u_acp_turn_min(u) != uprop_i_default(u_acp_turn_min))
1913 tbprintf(buf, "%sGuaranteed to gain at least %d new ACP per turn.%s",
1914 help_indent(2), u_acp_turn_min(u), help_newline());
1915 if (u_acp_turn_max(u) != uprop_i_default(u_acp_turn_max))
1916 tbprintf(buf, "%sWill never exceed %d new ACP per turn.%s",
1917 help_indent(2), u_acp_turn_max(u), help_newline());
1918 /* Basic new ACP per turn. */
1919 if (u_acp(u) != uprop_i_default(u_acp))
1920 tbprintf(buf, "%sReceives basic allotment of %d new ACP per turn.%s",
1921 help_indent(2), u_acp(u), help_newline());
1922 /* Possible added ACP per turn. */
1923 if (uu_table_row_not_default(u, uu_occ_adds_acp,
1924 table_default(uu_occ_adds_acp))) {
1925 tbprintf(buf, "%sOccupant adds ACP: ", help_indent(2));
1926 uu_table_row_desc(buf, u, uu_occ_adds_acp, NULL, NULL);
1927 tbcatline(buf, ".");
1928 }
1929 if (ut_table_row_not_default(u, ut_night_adds_acp,
1930 table_default(ut_night_adds_acp))) {
1931 tbprintf(buf, "%sNight adds ACP: ", help_indent(2));
1932 ut_table_row_desc(buf, u, ut_night_adds_acp, NULL, "in");
1933 tbcatline(buf, ".");
1934 }
1935 if (u_acp_damage_effect(u) != uprop_l_default(u_acp_damage_effect)) {
1936 /* (Should write out ACP damage interpolation list.) */
1937 tbprintf(buf, "%sDamage affects ACP.%s", help_indent(2),
1938 help_newline());
1939 }
1940 /* Possible ACP multipliers. */
1941 if (uu_table_row_not_default(u, uu_occ_multiplies_acp,
1942 table_default(uu_occ_multiplies_acp))) {
1943 tbprintf(buf, "%sOccupant multiplies ACP: ", help_indent(2));
1944 uu_table_row_desc(buf, u, uu_occ_multiplies_acp, tb_mult_desc,
1945 NULL);
1946 tbcatline(buf, ".");
1947 }
1948 if (ut_table_row_not_default(u, ut_night_multiplies_acp,
1949 table_default(ut_night_multiplies_acp))) {
1950 tbprintf(buf, "%sNight multiplies ACP: ", help_indent(2));
1951 ut_table_row_desc(buf, u, ut_night_multiplies_acp, tb_mult_desc,
1952 "in");
1953 tbcatline(buf, ".");
1954 }
1955 /* (Should consider other multipliers such as temperature.) */
1956 /* Free ACP. */
1957 if (u_free_acp(u) != uprop_i_default(u_free_acp)) {
1958 tbprintf(buf, "%sAllowed %d free ACP to complete an action.%s",
1959 help_indent(2), u_free_acp(u), help_newline());
1960 }
1961 }
1962 tbcatline(buf, "");
1963 }
1964
1965 /* Describe the unit type's relevance to the sides. */
1966
1967 static void
describe_utype_side_attributes(int u,char * key,TextBuffer * buf)1968 describe_utype_side_attributes(int u, char *key, TextBuffer *buf)
1969 {
1970 int hasattribs = FALSE, first = TRUE;
1971 Side *side = NULL;
1972 char sidetmpbuf[BUFSIZE];
1973
1974 tbcatline(buf, "Side Attributes:");
1975 /* Possible sides. */
1976 if (u_possible_sides(u) != lispnil) {
1977 tbprintf(buf, "%sCan belong to the following sides: ", help_indent(2));
1978 first = TRUE;
1979 for_all_sides(side) {
1980 if (type_allowed_on_side(u, side)) {
1981 if (first)
1982 first = FALSE;
1983 else
1984 tbcat(buf, ", ");
1985 tbcat(buf, shortest_side_title(side, sidetmpbuf));
1986 }
1987 }
1988 tbcatline(buf, ".");
1989 hasattribs = TRUE;
1990 }
1991 /* (Should mention whether the unit can ever be available to the
1992 side of the player viewing the help.) */
1993 /* Self unit. */
1994 if (u_can_be_self(u)) {
1995 tbcatline_si(buf, "Can be a side leader/capital (aka. \"self\") unit.");
1996 if (u_self_changeable(u))
1997 tbcatline_si(buf, "Side can opt for a different self unit instead.");
1998 if (u_self_resurrects(u))
1999 tbcatline_si(buf, "Upon death, annoint a new self unit.");
2000 /* (Should move the following test to side help.) */
2001 if (g_self_required())
2002 tbcatline_si(buf, "(Side must always have a self unit during game.)");
2003 /* (If an unit of this type is the side's self unit, then should
2004 mention its name and location.) */
2005 hasattribs = TRUE;
2006 }
2007 /* How many per side? */
2008 if (u_type_per_side_max(u) >= 0) {
2009 tbprintf(buf, "%sAt most %d allowed on each side in a game.%s",
2010 help_indent(2), u_type_per_side_max(u), help_newline());
2011 hasattribs = TRUE;
2012 }
2013 /* (Should tell how many the side of the player currently owns. And
2014 perhaps how many allied sides own in addition.) */
2015 if (!u_direct_control(u)) {
2016 tbcatline_si(buf, "Cannot be controlled directly by side.");
2017 hasattribs = TRUE;
2018 }
2019 if (!hasattribs)
2020 tbcatline_si(buf, "None.");
2021 tbcatline(buf, "");
2022 }
2023
2024 /* Describe the unit type's AI attributes. */
2025
2026 static void
describe_utype_ai_attributes(int u,char * key,TextBuffer * buf)2027 describe_utype_ai_attributes(int u, char *key, TextBuffer *buf)
2028 {
2029 int hasattribs = FALSE;
2030
2031 tbcatline(buf, "AI Attributes:");
2032 if (u_colonizer(u)) {
2033 tbcatline_si(buf, "Can build advanced units.");
2034 hasattribs = TRUE;
2035 }
2036 if (u_facility(u)) {
2037 tbcatline_si(buf, "Considered to be an immobile facility.");
2038 hasattribs = TRUE;
2039 }
2040 if (u_ground_mobile(u)) {
2041 tbcatline_si(buf, "Considered to be a mobile ground unit.");
2042 hasattribs = TRUE;
2043 }
2044 if (u_naval_mobile(u)) {
2045 tbcatline_si(buf, "Considered to be a mobile naval unit.");
2046 hasattribs = TRUE;
2047 }
2048 if (u_air_mobile(u)) {
2049 tbcatline_si(buf, "Considered to be a mobile air unit.");
2050 hasattribs = TRUE;
2051 }
2052 if (u_minimal_sea_for_docks(u) < PROPHI) {
2053 tbprintf(buf,
2054 "%sRequires >= %d accessible, liquid cells to build naval units.%s",
2055 help_indent(2), u_minimal_sea_for_docks(u), help_newline());
2056 hasattribs = TRUE;
2057 }
2058 if (u_ai_peace_garrison(u) > 0) {
2059 tbprintf(buf, "%sRequests a total peacetime garrison of %d units.%s",
2060 help_indent(2), u_ai_peace_garrison(u), help_newline());
2061 hasattribs = TRUE;
2062 }
2063 if (u_ai_war_garrison(u) > 0) {
2064 tbprintf(buf, "%sRequests a total wartime garrison of %d units.%s",
2065 help_indent(2), u_ai_war_garrison(u), help_newline());
2066 hasattribs = TRUE;
2067 }
2068 if (u_ai_enemy_alert_range(u) > 0) {
2069 tbprintf(buf,
2070 "%sRange within which enemies will cause an alert is %d.%s",
2071 help_indent(2), u_ai_enemy_alert_range(u), help_newline());
2072 hasattribs = TRUE;
2073 }
2074 if (u_ai_tactical_range(u) > 0) {
2075 tbprintf(buf, "%sBasic tactical computation range is %d.%s",
2076 help_indent(2), u_ai_tactical_range(u), help_newline());
2077 hasattribs = TRUE;
2078 }
2079 if (!hasattribs)
2080 tbcatline_si(buf, "None.");
2081 tbcatline(buf, "");
2082 }
2083
2084 int
may_detonate(int u)2085 may_detonate(int u)
2086 {
2087 return ((type_max_acp(u) > 0 && u_acp_to_detonate(u) > 0)
2088 || u_detonate_on_death(u) > 0
2089 || uu_table_row_not_default(u, uu_detonate_on_hit, 0)
2090 || uu_table_row_not_default(u, uu_detonate_on_capture, 0)
2091 || uu_table_row_not_default(u, uu_detonate_approach_range, -1)
2092 || ut_table_row_not_default(u, ut_detonation_accident, 0)
2093 );
2094 }
2095
2096 static void
dump_material_unit_summary(TextBuffer * buf,const char * category,int (* func)(int,int),int m,int no,int fire_f)2097 dump_material_unit_summary(TextBuffer * buf,const char * category,
2098 int (*func)(int,int), int m, int no, int fire_f)
2099 {
2100 int cnt = 0;
2101 int u;
2102 int p;
2103
2104 tbprintf(buf,"%s: ",category);
2105 for_all_unit_types(u) {
2106 p = (*func)(u,m);
2107 if(fire_f && !(u_acp_to_fire(u) > 0))
2108 continue;
2109 if(p != no) {
2110 cnt++;
2111 tbprintf(buf,"%s:%d ",u_type_name(u),p);
2112 }
2113 }
2114 if(cnt == 0)
2115 tbprintf(buf,"(none)%s%s", help_newline(), help_newline());
2116 else
2117 tbprintf(buf,"%s%s", help_newline(), help_newline());
2118 return;
2119 }
2120
2121 static void
describe_mtype(int m,char * key,TextBuffer * buf)2122 describe_mtype(int m, char *key, TextBuffer *buf)
2123 {
2124 int u;
2125 int cnt;
2126 int o;
2127 int i;
2128 int s;
2129
2130 append_help_phrase(buf, m_help(m));
2131 /* Display the designer's notes for this type. */
2132 if (m_notes(m) != lispnil) {
2133 tbprintf(buf, "%sNotes:%s", help_newline(), help_newline());
2134 append_notes(buf, m_notes(m));
2135 tbprintf(buf, "%s%s", help_newline(), help_newline());
2136 }
2137 if (m_people(m) > 0) {
2138 tbprintf(buf, "1 of this represents %d individuals.", m_people(m));
2139 }
2140 /* (should display unit columns here) */
2141 /* And here is a summary */
2142 tbprintf(buf, "%s%s", help_newline(), help_newline());
2143 dump_material_unit_summary(
2144 buf,"Base production", um_base_production, m, 0, 0);
2145 dump_material_unit_summary(
2146 buf,"Base consumption", um_base_consumption, m, 0, 0);
2147 dump_material_unit_summary(buf,"Storage", um_storage_x, m, 0, 0);
2148 dump_material_unit_summary(buf,"Consumption percentage as occupant",
2149 um_consumption_as_occupant, m, 100, 0);
2150 dump_material_unit_summary(buf,"Production as occupant",
2151 um_occ_production, m, -1, 0);
2152 dump_material_unit_summary(
2153 buf,"ACP to extract", um_acp_to_extract, m, 0, 0);
2154 tbprintf(
2155 buf, "%sMinimum required by a unit for the following activities.%s%s",
2156 help_newline(), help_newline(), help_newline());
2157 dump_material_unit_summary(buf, "To act", um_to_act, m, 0, 0);
2158 dump_material_unit_summary(buf, "To move", um_to_move, m, 0, 0);
2159 dump_material_unit_summary(buf, "To attack", um_to_attack, m, 0, 0);
2160 dump_material_unit_summary(buf, "To fire", um_to_fire, m, 0, 1);
2161 tbprintf(buf, "%sIn addition to basic consumption.%s%s", help_newline(),
2162 help_newline(), help_newline(), help_newline());
2163 dump_material_unit_summary(buf, "Consumption per move",
2164 um_consumption_per_move, m, 0, 0);
2165 dump_material_unit_summary(buf, "Consumption per attack",
2166 um_consumption_per_attack, m, 0, 0);
2167 dump_material_unit_summary(buf, "Consumption per fire",
2168 um_consumption_per_fire, m, 0, 1);
2169 tbprintf(buf, "%sAny unit building another unit,%s", help_newline(),
2170 help_newline());
2171 tbcatline(buf, "needs (but does not necessarily use)");
2172 tbcatline(buf, "the following minimum amounts for the given unit.");
2173 dump_material_unit_summary(
2174 buf, "Needed to create ", um_to_create, m, 0, 0);
2175 dump_material_unit_summary(
2176 buf, "Needed to build ", um_to_build, m, 0, 0);
2177 tbprintf(buf, "%sAny building unit consumes as follows%s%s",
2178 help_newline(), help_newline(), help_newline());
2179 dump_material_unit_summary(buf,"On creation of",
2180 um_consumption_on_creation, m, 0, 0);
2181 dump_material_unit_summary(buf,"Per build of",
2182 um_consumption_per_build, m, 0, 0);
2183 tbprintf(
2184 buf,"%sAny unit repairing other units needs/uses these amounts%s%s",
2185 help_newline(), help_newline(), help_newline());
2186 dump_material_unit_summary(
2187 buf, "To be able to repair ", um_to_repair, m, 0, 0);
2188 dump_material_unit_summary(buf, "Consumption per repair of 1 HP for",
2189 um_consumption_per_repair, m, 0, 0);
2190 /*
2191 * Select a minimal set of functions for the test,
2192 * after all, if it doesnt store any, it cant use the material.
2193 */
2194 tbcatline(
2195 buf, "Distances that a unit can send or receive this material.");
2196 tbcatline(buf, "Format is unit:send:receive.");
2197 tbcatline(buf, "(NB Distances are free of terrain effects)");
2198 for_all_unit_types(u) {
2199 /* (Should consider the case where a material is sent directly
2200 to the side treasury.) */
2201 if (um_base_production(u,m) > 0
2202 || um_base_consumption(u,m) > 0
2203 || um_storage_x(u,m) > 0) {
2204 o = um_outlength(u,m);
2205 i = um_inlength(u,m);
2206 if (o >= 0 && i >= 0)
2207 tbprintf(buf, "%s:%d:%d ", u_type_name(u), o, i);
2208 else if (i >= 0)
2209 tbprintf(buf, "%s:-:%d ", u_type_name(u), i);
2210 }
2211 }
2212 tbprintf(buf, "%s%s", help_newline(), help_newline());
2213 tbprintf(buf, "Initial unit quantities at game start:");
2214 cnt = 0;
2215 for_all_unit_types(u) {
2216 i = um_initial(u,m);
2217 s = um_storage_x(u,m);
2218 if( s > 0 && i >= 0 && i < s) {
2219 tbprintf(buf, "%s:%d ", u_type_name(u), i);
2220 cnt++;
2221 }
2222 }
2223 if (!cnt)
2224 tbprintf(buf,"(none)");
2225 tbprintf(buf,"%s%s", help_newline(), help_newline());
2226 return;
2227 }
2228
2229 static void
describe_ttype(int t,char * key,TextBuffer * buf)2230 describe_ttype(int t, char *key, TextBuffer *buf)
2231 {
2232 int m, ct;
2233 int u;
2234 int e;
2235 int speed;
2236 int acp;
2237 int mp;
2238 int l;
2239 int eff;
2240
2241 append_help_phrase(buf, t_help(t));
2242 /* Display the subtype. */
2243 switch (t_subtype(t)) {
2244 case cellsubtype:
2245 break;
2246 case bordersubtype:
2247 tbcatline(buf, " (a border type)");
2248 break;
2249 case connectionsubtype:
2250 tbcatline(buf, " (a connection type)");
2251 break;
2252 case coatingsubtype:
2253 tbcatline(buf, " (a coating type)");
2254 break;
2255 }
2256 /* Display the designer's notes for this type. */
2257 if (t_notes(t) != lispnil) {
2258 tbprintf(buf, "%sNotes:%s", help_newline(), help_newline());
2259 append_notes(buf, t_notes(t));
2260 tbprintf(buf, "%s%s", help_newline(), help_newline());
2261 }
2262 if (t_liquid(t))
2263 tbcatline(buf, "Represents water or other liquid.");
2264 tbprintf(buf, "Generic unit capacity is %d.%s", t_capacity(t),
2265 help_newline());
2266 if (t_people_max(t) >= 0) {
2267 tbprintf(buf, "Up to %d people may live in this type of terrain.%s",
2268 t_people_max(t), help_newline());
2269 }
2270 if (any_elev_variation) {
2271 if (t_elev_min(t) == t_elev_max(t)) {
2272 tbprintf(buf, "Elevation is always %d.%s",
2273 t_elev_min(t), help_newline());
2274 } else {
2275 tbprintf(buf, "Elevations fall between %d and %d.%s",
2276 t_elev_min(t), t_elev_max(t), help_newline());
2277 }
2278 }
2279 if (t_thickness(t) > 0) {
2280 tbprintf(buf, "Thickness is %d.%s", t_thickness(t), help_newline());
2281 }
2282 if (any_temp_variation) {
2283 if (t_temp_min(t) == t_temp_max(t)) {
2284 tbprintf(buf, "Temperature is always %d.%s",
2285 t_temp_min(t), help_newline());
2286 } else {
2287 tbprintf(buf,
2288 "Temperatures fall between %d and %d, averaging %d.%s",
2289 t_temp_min(t), t_temp_max(t), t_temp_avg(t),
2290 help_newline());
2291 }
2292 if (t_temp_variability(t) > 0) {
2293 tbprintf(buf, "Temperature varies randomly, up to %d each turn.%s",
2294 t_temp_variability(t), help_newline());
2295 }
2296 }
2297 if (any_wind_variation) {
2298 if (t_wind_force_min(t) == t_wind_force_max(t)) {
2299 tbprintf(buf, "Wind force is always %d.%s",
2300 t_wind_force_min(t), help_newline());
2301 } else {
2302 tbprintf(buf,
2303 "Wind forces fall between %d and %d, averaging %d.%s",
2304 t_wind_force_min(t), t_wind_force_max(t),
2305 t_wind_force_avg(t), help_newline());
2306 }
2307 if (t_wind_force_variability(t) > 0) {
2308 tbprintf(buf,
2309 "%d%% chance each turn that wind force will change.%s",
2310 t_wind_force_variability(t), help_newline());
2311 }
2312 if (t_wind_variability(t) > 0) {
2313 tbprintf(buf,
2314 "%d%% chance each turn that wind direction will change.%s",
2315 t_wind_variability(t), help_newline());
2316 }
2317 }
2318 if (any_clouds) {
2319 if (t_clouds_min(t) == t_clouds_max(t)) {
2320 tbprintf(buf, "Cloud cover is always %d.%s",
2321 t_clouds_min(t), help_newline());
2322 } else {
2323 tbprintf(buf, "Cloud cover falls between %d and %d%s",
2324 t_clouds_min(t), t_clouds_max(t), help_newline());
2325 }
2326 }
2327 /* Display relationships with materials. */
2328 if (nummtypes > 0) {
2329 for_all_material_types(m) {
2330 if (tm_storage_x(t, m) > 0
2331 || tm_production(t, m) > 0
2332 || tm_consumption(t, m) > 0
2333 || tm_prod_from_terrain(t, m) > 0) {
2334 tbprintf(buf, "%s:", m_type_name(m));
2335 }
2336 if (tm_storage_x(t, m) > 0) {
2337 tbprintf(buf, "%sCan store up to %d", help_indent(2),
2338 tm_storage_x(t, m));
2339 tbprintf(buf, " (normally starts game with %d)",
2340 min(tm_initial(t, m), tm_storage_x(t, m)));
2341 tbcatline(buf, ".");
2342 tbprintf(buf,
2343 "%sSides will%s always know current amount accurately.%s",
2344 help_indent(2), (tm_see_always(t, m) ? "" : " not"),
2345 help_newline());
2346 }
2347 if (tm_production(t, m) > 0 || tm_consumption(t, m) > 0) {
2348 tbprintf(buf, "%sProduces %d and consumes %d each turn.%s",
2349 help_indent(2), tm_production(t, m),
2350 tm_consumption(t, m), help_newline());
2351 }
2352 if (tm_prod_from_terrain(t, m) > 0) {
2353 tbprintf(buf,
2354 "%sAdvanced units can use this terrain to produce %d each turn.%s",
2355 help_indent(2), tm_prod_from_terrain(t, m),
2356 help_newline());
2357 }
2358 }
2359 }
2360 /* Display relationships with any coating terrain types. */
2361 if (numcoattypes > 0) {
2362 tbcatline(buf, "Coatings:");
2363 for_all_terrain_types(ct) {
2364 if (t_is_coating(ct)) {
2365 tbprintf(buf, "%s coats, depths %d up to %d",
2366 t_type_name(ct),
2367 tt_coat_min(ct, t), tt_coat_max(ct, t));
2368 }
2369 }
2370 }
2371 /* Display damaged types. */
2372 if (tt_table_row_not_default(t, tt_damaged_type, 0)) {
2373 tbcat_si(buf, "Type after being damaged: ");
2374 tt_table_row_desc(buf, t, tt_damaged_type, NULL);
2375 tbcatline(buf, ".");
2376 }
2377 /* Display exhaustion types. */
2378 if (nummtypes > 0) {
2379 for_all_material_types(m) {
2380 if (tm_change_on_exhaust(t, m) > 0
2381 && tm_exhaust_type(t, m) != NONTTYPE) {
2382 tbprintf(buf,
2383 "If exhausted of %s, %d%% chance to change to %s.%s",
2384 m_type_name(m), tm_change_on_exhaust(t, m),
2385 t_type_name(tm_exhaust_type(t, m)), help_newline());
2386 }
2387 }
2388 }
2389 #ifdef DESIGNERS
2390 if (numdesigners > 0) {
2391 tbprintf(buf, "%sFOR DESIGNERS:%s", help_newline(), help_newline());
2392 if (t_subtype_x(t) ==
2393 c_number(symbol_value(intern_symbol(keyword_name(K_RIVER_X))))) {
2394 tbcatline(buf, "Considered to be a type of river.");
2395 } else if (t_subtype_x(t) ==
2396 c_number(
2397 symbol_value(intern_symbol(keyword_name(K_ROAD_X))))) {
2398 tbcatline(buf, "Considered to be a type of road.");
2399 }
2400 if (tt_table_row_not_default(t, tt_drawable, 1)) {
2401 tbcat(buf, "Draw over terrain: ");
2402 tt_table_row_desc(buf, t, tt_drawable, NULL);
2403 tbcatline(buf, ".");
2404 }
2405 }
2406 #endif /* DESIGNERS */
2407 tbprintf(buf, "%s%sUnits able to enter, with given MP cost.%s",
2408 help_newline(), help_newline(), help_newline());
2409 for_all_unit_types(u) {
2410 e = ut_mp_to_enter(u,t);
2411 speed = type_max_speed(u);
2412 acp = type_max_acp(u);
2413 mp = (acp*speed)/100;
2414 if (speed == 0)
2415 continue;
2416 if (e > mp)
2417 continue;
2418 tbprintf(buf, "%s:%d ", u_type_name(u), e);
2419 }
2420 tbprintf(buf,"%s%s", help_newline(), help_newline());
2421 tbcatline(buf, "Units able to leave, with given MP cost.");
2422 for_all_unit_types(u) {
2423 l = ut_mp_to_leave(u,t);
2424 speed = type_max_speed(u);
2425 acp = type_max_acp(u);
2426 mp = (acp*speed)/100;
2427 if (speed == 0)
2428 continue;
2429 if (l <= mp)
2430 tbprintf(buf, "%s:%d ", u_type_name(u), l);
2431 }
2432 tbprintf(buf,"%s%s", help_newline(), help_newline());
2433 tbcatline(buf, "An attacker in this terrain, attacking the given units,");
2434 tbcatline(buf, "has the following multiplier, ( > 1 = advantage).");
2435 for_all_unit_types(u) {
2436 eff = ut_attack_terrain_effect(u,t);
2437 if (eff == 100)
2438 continue;
2439 tbprintf(buf, "%s:%s%d.%d ", u_type_name(u), "*", eff / 100, eff % 100);
2440 }
2441 tbprintf(buf,"%s%s", help_newline(), help_newline());
2442
2443 tbcatline(buf, "A defender in this terrain,");
2444 tbcatline(buf, "has the following multiplier, ( < 1 = advantage).");
2445 for_all_unit_types(u) {
2446 eff = ut_defend_terrain_effect(u,t);
2447 if (eff == 100)
2448 continue;
2449 tbprintf(buf, "%s:%s%d.%d ", u_type_name(u), "*", eff / 100, eff % 100);
2450 }
2451 tbprintf(buf,"%s%s", help_newline(), help_newline());
2452 tbcatline(buf,"The following units vanish in this terrain.");
2453 for_all_unit_types(u) {
2454 if (ut_vanishes_on(u,t))
2455 tbprintf(buf, "%s ", u_type_name(u));
2456 }
2457 tbprintf(buf,"%s%s", help_newline(), help_newline());
2458 tbcatline(buf, "The following units wreck in this terrain.");
2459 for_all_unit_types(u) {
2460 if (ut_wrecks_on(u,t))
2461 tbprintf(buf, "%s ", u_type_name(u));
2462 }
2463 tbprintf(buf,"%s%s", help_newline(), help_newline());
2464 }
2465
2466 static void
describe_atype(int a,char * key,TextBuffer * buf)2467 describe_atype(int a, char *key, TextBuffer *buf)
2468 {
2469 int u, found = FALSE;
2470
2471 append_help_phrase(buf, a_help(a));
2472 /* Display the designer's notes for this type. */
2473 if (a_notes(a) != lispnil) {
2474 tbprintf(buf, "%sNotes:%s", help_newline(), help_newline());
2475 append_notes(buf, a_notes(a));
2476 tbprintf(buf, "%s%s", help_newline(), help_newline());
2477 }
2478 tbprintf(buf, "Research points needed for discovery: %d.%s", a_rp(a),
2479 help_newline());
2480 tbcatline(buf, "");
2481 if (aa_table_row_not_default(a, aa_needed_to_research, 0)) {
2482 tbcat(buf, "Prerequisites: ");
2483 aa_table_row_desc(buf, a, aa_needed_to_research, tb_bool_desc);
2484 tbprintf(buf, ".%s%s", help_newline(), help_newline());
2485 } else {
2486 tbprintf(buf, "No prerequisites.%s%s", help_newline(), help_newline());
2487 }
2488 if (aa_table_column_not_default(a, aa_needed_to_research, 0)) {
2489 tbcat(buf, "Needed for: ");
2490 aa_table_column_desc(buf, a, aa_needed_to_research, tb_bool_desc);
2491 tbprintf(buf, ".%s%s", help_newline(), help_newline());
2492 } else {
2493 tbprintf(buf, "Not needed for any further advances.%s%s",
2494 help_newline(), help_newline());
2495 }
2496 for_all_unit_types(u) {
2497 if (ua_needed_to_build(u, a) > 0) {
2498 if (found) {
2499 tbprintf(buf, ", ");
2500 } else {
2501 tbcat(buf, "Enables: ");
2502 }
2503 tbprintf(buf, "%s", u_type_name(u));
2504 found = TRUE;
2505 }
2506 }
2507 if (found) {
2508 tbcatline(buf, ".");
2509 } else {
2510 tbcatline(buf, "Does not enable any new unit types.");
2511 }
2512 }
2513
2514 static void
describe_scorekeepers(int arg,char * key,TextBuffer * buf)2515 describe_scorekeepers(int arg, char *key, TextBuffer *buf)
2516 {
2517 int i = 1;
2518 Scorekeeper *sk;
2519
2520 if (scorekeepers == NULL) {
2521 tbcat(buf, "No scores are being kept.");
2522 } else {
2523 for_all_scorekeepers(sk) {
2524 if (numscorekeepers > 1) {
2525 tbprintf(buf, "%d. ", i++);
2526 }
2527 if (symbolp(sk->body)
2528 && match_keyword(sk->body, K_LAST_SIDE_WINS)) {
2529 tbcat(buf, "The last side left in the game wins.");
2530 /* (should mention point values also) */
2531 }
2532 else if (symbolp(sk->body)
2533 && match_keyword(sk->body, K_LAST_ALLIANCE_WINS)) {
2534 tbcat(buf, "The last alliance left in the game wins.");
2535 /* (should mention point values also) */
2536 } else {
2537 tbcat(buf, "(an indescribably complicated scorekeeper)");
2538 }
2539 tbcatline(buf, "");
2540 }
2541 }
2542 }
2543
2544 /* List each synthesis method and its parameters. */
2545
2546 static void
describe_setup(int arg,char * key,TextBuffer * buf)2547 describe_setup(int arg, char *key, TextBuffer *buf)
2548 {
2549 int u, t, t2, methkey;
2550 Obj *synthlist, *methods, *method;
2551 int firstitem = TRUE;
2552
2553 synthlist = g_synth_methods();
2554 if (synthlist == lispnil)
2555 tbcatline(buf, "No synthesis done when setting up this game.");
2556 else
2557 tbcatline(buf, "Synthesis done when setting up this game:");
2558 for (methods = synthlist; methods != lispnil; methods = cdr(methods)) {
2559 tbcatline(buf, "");
2560 method = car(methods);
2561 if (symbolp(method)) {
2562 methkey = keyword_code(c_string(method));
2563 switch (methkey) {
2564 case K_MAKE_COUNTRIES:
2565 tbcat(buf, "Countries:");
2566 describe_synth_run(buf, methkey);
2567 tbcatline(buf, "");
2568 if (g_radius_min() != gvar_i_default(g_radius_min)) {
2569 tbprintf(buf, "%s%d cells across", help_indent(2),
2570 2 * g_radius_min() + 1);
2571 if (g_separation_min() != gvar_i_default(g_separation_min))
2572 tbprintf(buf, ", at least %d cells apart",
2573 g_separation_min());
2574 if (g_separation_max() != gvar_i_default(g_separation_max))
2575 tbprintf(buf, ", and at most %d cells apart",
2576 g_separation_max());
2577 tbcatline(buf, "");
2578 }
2579 if (t_property_not_default(t_country_min,
2580 tprop_i_default(t_country_min))) {
2581 tbprintf(buf, "%sMinimum terrain in each country: ",
2582 help_indent(2));
2583 t_property_desc(buf, t_country_min, NULL);
2584 tbcatline(buf, ".");
2585 }
2586 if (t_property_not_default(t_country_max,
2587 tprop_i_default(t_country_max))) {
2588 tbprintf(buf, "%sMaximum terrain in each country: ",
2589 help_indent(2));
2590 t_property_desc(buf, t_country_max, NULL);
2591 tbcatline(buf, ".");
2592 }
2593 if (u_property_not_default(u_start_with,
2594 uprop_i_default(u_start_with))) {
2595 tbprintf(buf, "%sStart with: ", help_indent(2));
2596 u_property_desc(buf, u_start_with, NULL);
2597 tbcatline(buf, ".");
2598 }
2599 if (u_property_not_default(u_indep_near_start,
2600 uprop_i_default(
2601 u_indep_near_start))) {
2602 tbprintf(buf, "%sIndependents nearby: ", help_indent(2));
2603 u_property_desc(buf, u_indep_near_start, NULL);
2604 tbcatline(buf, ".");
2605 }
2606 tbprintf(buf, "%sFavored terrain:%s", help_indent(2),
2607 help_newline());
2608 for_all_unit_types(u) {
2609 if (u_start_with(u) > 0 || u_indep_near_start(u)) {
2610 tbprintf(buf, "%s%s: ", help_indent(4), u_type_name(u));
2611 ut_table_row_desc(buf, u, ut_favored, tb_percent_desc,
2612 NULL);
2613 tbcatline(buf, ".");
2614 }
2615 }
2616 if (g_radius_max() != 0) {
2617 tbprintf(buf, "%sCountry growth:%s", help_indent(2),
2618 help_newline());
2619 if (g_radius_max() == -1) {
2620 tbprintf(buf, "%sUp to entire world", help_indent(4));
2621 } else {
2622 tbprintf(buf, "%sUp to %d cells across",
2623 help_indent(4), 2 * g_radius_max() + 1);
2624 }
2625 tbprintf(buf, ", %d chance to stop if blocked.%s",
2626 g_growth_stop(), help_newline());
2627 if (t_property_not_default(t_country_growth,
2628 tprop_i_default(
2629 t_country_growth))) {
2630 tbprintf(buf, "%sGrowth chance, by terrain: ",
2631 help_indent(2));
2632 t_property_desc(buf, t_country_max, tb_percent_desc);
2633 tbcatline(buf, ".");
2634 }
2635 if (t_property_not_default(t_country_takeover,
2636 tprop_i_default(
2637 t_country_takeover))) {
2638 tbprintf(buf, "%sTakeover chance, by terrain: ",
2639 help_indent(2));
2640 t_property_desc(buf, t_country_takeover, NULL);
2641 tbcatline(buf, ".");
2642 }
2643 if (u_property_not_default(u_unit_growth,
2644 uprop_i_default(
2645 u_unit_growth))) {
2646 tbprintf(buf, "%sChance for additional unit: ",
2647 help_indent(2));
2648 u_property_desc(buf, u_unit_growth, NULL);
2649 tbcatline(buf, ".");
2650 }
2651 if (u_property_not_default(u_indep_growth,
2652 uprop_i_default(
2653 u_indep_growth))) {
2654 tbprintf(buf,
2655 "%sChance for additional independent unit: ",
2656 help_indent(2));
2657 u_property_desc(buf, u_indep_growth, NULL);
2658 tbcatline(buf, ".");
2659 }
2660 if (u_property_not_default(u_unit_takeover,
2661 uprop_i_default(
2662 u_unit_takeover))) {
2663 tbprintf(buf, "%sChance to take over units: ",
2664 help_indent(2));
2665 u_property_desc(buf, u_unit_takeover, NULL);
2666 tbcatline(buf, ".");
2667 }
2668 if (u_property_not_default(u_indep_takeover,
2669 uprop_i_default(
2670 u_indep_takeover))) {
2671 tbprintf(buf,
2672 "%sChance to take over independent unit: ",
2673 help_indent(2));
2674 u_property_desc(buf, u_indep_takeover, NULL);
2675 tbcatline(buf, ".");
2676 }
2677 if (u_property_not_default(u_country_units_max,
2678 uprop_i_default(
2679 u_country_units_max))) {
2680 tbprintf(buf, "%sMaximum units in country: ",
2681 help_indent(2));
2682 u_property_desc(buf, u_country_units_max, NULL);
2683 tbcatline(buf, ".");
2684 }
2685 if (t_property_not_default(t_country_people,
2686 tprop_i_default(
2687 t_country_people))) {
2688 tbprintf(buf, "%sPeople takeover chance, by terrain: ",
2689 help_indent(2));
2690 t_property_desc(buf, t_country_people, NULL);
2691 tbcatline(buf, ".");
2692 }
2693 if (t_property_not_default(t_indep_people,
2694 tprop_i_default(
2695 t_indep_people))) {
2696 tbprintf(buf, "%sIndependent people chance: ",
2697 help_indent(2));
2698 t_property_desc(buf, t_indep_people, NULL);
2699 tbcatline(buf, ".");
2700 }
2701 }
2702 break;
2703 case K_MAKE_EARTHLIKE_TERRAIN:
2704 tbcat(buf, "Earthlike terrain:");
2705 describe_synth_run(buf, methkey);
2706 tbcatline(buf, "");
2707 tbprintf(buf, "%sTerrain around edge is %s.%s",
2708 help_indent(2), t_type_name(g_edge_terrain()),
2709 help_newline());
2710 /* (should describe tt_adj_terr_effect workings here) */
2711 break;
2712 case K_MAKE_FRACTAL_PTILE_TERRAIN:
2713 tbcat(buf, "Fractal percentile terrain:");
2714 describe_synth_run(buf, methkey);
2715 tbcatline(buf, "");
2716 tbprintf(buf, "%sAlt blobs density %d, size %d, height %d%s",
2717 help_indent(2), g_alt_blob_density(),
2718 g_alt_blob_size(), g_alt_blob_height(),
2719 help_newline());
2720 tbprintf(buf, "%s%d smoothing passes%s", help_indent(4),
2721 g_alt_smoothing(), help_newline());
2722 tbprintf(buf, "%sLower percentiles: ", help_indent(4));
2723 t_property_desc(buf, t_alt_min, tb_percent_desc);
2724 tbcatline(buf, ".");
2725 tbprintf(buf, "%sUpper percentiles: ", help_indent(4));
2726 t_property_desc(buf, t_alt_max, tb_percent_desc);
2727 tbcatline(buf, ".");
2728 tbprintf(buf, "%sWet blobs density %d, size %d, height %d%s",
2729 help_indent(2), g_wet_blob_density(),
2730 g_wet_blob_size(), g_wet_blob_height(),
2731 help_newline());
2732 tbprintf(buf, "%s%d smoothing passes%s", help_indent(4),
2733 g_wet_smoothing(), help_newline());
2734 tbprintf(buf, "%sLower percentiles: ", help_indent(4));
2735 t_property_desc(buf, t_wet_min, tb_percent_desc);
2736 tbcatline(buf, ".");
2737 tbprintf(buf, "%sUpper percentiles: ", help_indent(4));
2738 t_property_desc(buf, t_wet_max, tb_percent_desc);
2739 tbcatline(buf, ".");
2740 tbprintf(buf, "%sTerrain around edge is %s.%s",
2741 help_indent(2), t_type_name(g_edge_terrain()),
2742 help_newline());
2743 /* (should describe tt_adj_terr_effect workings here) */
2744 break;
2745 case K_MAKE_INDEPENDENT_UNITS:
2746 tbcat(buf, "Independent units:");
2747 describe_synth_run(buf, methkey);
2748 tbcatline(buf, "");
2749 for_all_unit_types(u) {
2750 if (ut_table_row_not_default(u, ut_indep_density,
2751 table_default(
2752 ut_indep_density))) {
2753 tbprintf(buf, "%sChance of independent %s: ",
2754 help_indent(2), u_type_name(u));
2755 ut_table_row_desc(buf, u, ut_indep_density,
2756 tb_percent_100th_desc, "in");
2757 tbcatline(buf, ".");
2758 }
2759 }
2760 /* (should show indep people) */
2761 break;
2762 case K_MAKE_INITIAL_MATERIALS:
2763 tbcat(buf, "Materials:");
2764 describe_synth_run(buf, methkey);
2765 tbcatline(buf, "");
2766 /* (should show unit and terrain initial supply) */
2767 break;
2768 case K_MAKE_MAZE_TERRAIN:
2769 tbcat(buf, "Maze terrain:");
2770 describe_synth_run(buf, methkey);
2771 tbcatline(buf, "");
2772 tbprintf(buf, "%s%d.%2.2d%% of maze is room.%s",
2773 help_indent(2), g_maze_room() / 100,
2774 g_maze_room() % 100, help_newline());
2775 tbcat(buf, "Room terrain types will be");
2776 for_all_terrain_types(t) {
2777 if (t_maze_room_occurrence(t) > 0) {
2778 tbprintf(buf, " %s(%d)", t_type_name(t),
2779 t_maze_room_occurrence(t));
2780 }
2781 }
2782 tbcatline(buf, ".");
2783 tbprintf(buf, "%s%d.%2.2d%% of maze is passageway.%s",
2784 help_indent(2), g_maze_passage() / 100,
2785 g_maze_passage() % 100, help_newline());
2786 tbcat(buf, "Passageway terrain types will be");
2787 for_all_terrain_types(t) {
2788 if (t_maze_passage_occurrence(t) > 0) {
2789 tbprintf(buf, " %s(%d)", t_type_name(t),
2790 t_maze_passage_occurrence(t));
2791 }
2792 }
2793 tbcatline(buf, ".");
2794 tbprintf(buf, "%sTerrain around edge is %s.%s",
2795 help_indent(2), t_type_name(g_edge_terrain()),
2796 help_newline());
2797 break;
2798 case K_MAKE_RANDOM_DATE:
2799 tbcat(buf, "Random date:");
2800 describe_synth_run(buf, methkey);
2801 tbcatline(buf, "");
2802 break;
2803 case K_MAKE_RANDOM_TERRAIN:
2804 tbcat(buf, "Random terrain:");
2805 describe_synth_run(buf, methkey);
2806 tbcatline(buf, "");
2807 tbprintf(buf, "%sTerrain types will be", help_indent(2));
2808 for_all_terrain_types(t) {
2809 if (t_occurrence(t) > 0) {
2810 tbprintf(buf, " %s(%d)", t_type_name(t),
2811 t_occurrence(t));
2812 }
2813 }
2814 tbcatline(buf, ".");
2815 tbprintf(buf, "%sTerrain around edge is %s.%s",
2816 help_indent(2), t_type_name(g_edge_terrain()),
2817 help_newline());
2818 break;
2819 case K_MAKE_RIVERS:
2820 tbcat(buf, "Rivers:");
2821 describe_synth_run(buf, methkey);
2822 tbcatline(buf, "");
2823 if (t_property_not_default(t_river_chance,
2824 tprop_i_default(t_river_chance))) {
2825 tbprintf(buf, "%sChance to be river source: ",
2826 help_indent(2));
2827 t_property_desc(buf, t_river_chance,
2828 tb_percent_100th_desc);
2829 tbcatline(buf, ".");
2830 if (g_river_sink_terrain() != NONTTYPE)
2831 tbprintf(buf, "%sSink is %s.%s",
2832 help_indent(2),
2833 t_type_name(g_river_sink_terrain()),
2834 help_newline());
2835 else
2836 tbprintf(buf, "%sNo special sink terrain.%s",
2837 help_indent(2), help_newline());
2838 }
2839 for_all_terrain_types(t) {
2840 if (!t_is_border(t))
2841 continue;
2842 if (K_RIVER_X != t_subtype_x(t))
2843 continue;
2844 if (tt_table_row_not_default(t, tt_adj_terr_effect,
2845 table_default(
2846 tt_adj_terr_effect))) {
2847 tbprintf(buf, "%sRiver of type %s incompatible with ",
2848 help_indent(2), t_type_name(t));
2849 firstitem = TRUE;
2850 for_all_terrain_types(t2) {
2851 if (tt_adj_terr_effect(t, t2) >= 0) {
2852 if (!firstitem)
2853 tbprintf(buf, ", %s", t_type_name(t2));
2854 else {
2855 tbprintf(buf, " %s", t_type_name(t2));
2856 firstitem = FALSE;
2857 }
2858 }
2859 }
2860 tbcatline(buf, ".");
2861 }
2862 }
2863 break;
2864 case K_MAKE_ROADS:
2865 tbcat(buf, "Roads:");
2866 describe_synth_run(buf, methkey);
2867 tbcatline(buf, "");
2868 tbprintf(buf, "%sChance to run:%s", help_indent(2),
2869 help_newline());
2870 for_all_unit_types(u) {
2871 if (uu_table_row_not_default(u, uu_road_chance, 0)) {
2872 tbprintf(buf, "%sFrom %s: ", help_indent(2),
2873 u_type_name(u));
2874 uu_table_row_desc(buf, u, uu_road_chance,
2875 tb_percent_desc, "to");
2876 tbcatline(buf, "");
2877 }
2878 if (u_spur_chance(u) > 0) {
2879 tbprintf(buf,
2880 "%s%d%% chance of spur, if within %d of road.%s",
2881 help_indent(4), u_spur_chance(u),
2882 u_spur_range(u), help_newline());
2883 }
2884 if (u_road_to_edge_chance(u) > 0) {
2885 tbprintf(buf, "%s%d%% chance of road to edge.%s",
2886 help_indent(4), u_road_to_edge_chance(u),
2887 help_newline());
2888 }
2889 }
2890 for_all_terrain_types(t) {
2891 if (t_subtype(t) == cellsubtype
2892 && tt_table_row_not_default(t, tt_road_into_chance,
2893 table_default(
2894 tt_road_into_chance))) {
2895 tbprintf(buf, "%sRouting of road from %s: ",
2896 help_indent(2), t_type_name(t));
2897 /* Note: this is actually a weight, not a
2898 percent chance. */
2899 tt_table_row_desc(buf, t, tt_road_into_chance,
2900 tb_value_desc /*, "to" */);
2901 tbcatline(buf, ".");
2902 }
2903 }
2904 if (g_edge_road_density() > 0) {
2905 tbcat(buf, help_indent(2));
2906 tb_percent_100th_desc(buf, g_edge_road_density());
2907 tbprintf(buf, "%sof edge gets road run to another edge.%s",
2908 help_indent(2), help_newline());
2909 }
2910 break;
2911 case K_MAKE_WEATHER:
2912 tbcat(buf, "Weather:");
2913 describe_synth_run(buf, methkey);
2914 tbcatline(buf, "");
2915 break;
2916 case K_NAME_GEOGRAPHICAL_FEATURES:
2917 tbcat(buf, "Names for geographical features:");
2918 describe_synth_run(buf, methkey);
2919 tbcatline(buf, "");
2920 break;
2921 case K_NAME_UNITS_RANDOMLY:
2922 tbcat(buf, "Names for units:");
2923 describe_synth_run(buf, methkey);
2924 tbcatline(buf, "");
2925 break;
2926 default:
2927 tbprintf(buf, "%s:", c_string(method));
2928 describe_synth_run(buf, methkey);
2929 tbcatline(buf, "");
2930 break;
2931 }
2932 } else if (consp(method)) {
2933 /* (what?) */
2934 }
2935 }
2936 tbcatline(buf, "");
2937 if (g_side_lib() != lispnil)
2938 tbprintf(buf, "%d choices in side library.%s", length(g_side_lib()),
2939 help_newline());
2940 }
2941
2942 static void
describe_synth_run(TextBuffer * buf,int methkey)2943 describe_synth_run(TextBuffer *buf, int methkey)
2944 {
2945 int calls, runs;
2946
2947 if (get_synth_method_uses(methkey, &calls, &runs)) {
2948 if (calls > 0) {
2949 if (calls == 1 && runs == 1) {
2950 tbcat(buf, " (was run)");
2951 } else if (calls == 1 && runs == 0) {
2952 tbcat(buf, " (was not run)");
2953 } else {
2954 tbprintf(buf, " (was called %d times, was run %d times)",
2955 calls, runs);
2956 }
2957 } else {
2958 tbcat(buf, " (not attempted)");
2959 }
2960 } else {
2961 tbcat(buf, " (? ? ?)");
2962 }
2963 }
2964
2965 static void
describe_world(int arg,char * key,TextBuffer * buf)2966 describe_world(int arg, char *key, TextBuffer *buf)
2967 {
2968 tbprintf(buf, "World circumference: %d.%s", world.circumference,
2969 help_newline());
2970 tbcatline(buf, "");
2971 tbprintf(buf, "Area in world: %d wide x %d high", area.width, area.height);
2972 if (area.width == world.circumference)
2973 tbprintf(buf, " (wraps completely around world).%s", help_indent(2));
2974 else
2975 tbprintf(buf, ".%s", help_indent(2));
2976
2977 tbprintf(buf, "Latitude: %d.%sLongitude: %d.\n", area.latitude,
2978 help_indent(2), area.longitude);
2979 tbcatline(buf, "");
2980 if (elevations_defined()) {
2981 tbprintf(buf, "Elevations range from %d to %d, averaging %d.%s",
2982 area.minelev, area.maxelev, area.avgelev, help_newline());
2983 tbprintf(buf, "Cells are %d elevation units across.%s",
2984 area.cellwidth, help_newline());
2985 }
2986 if (world.yearlength > 1) {
2987 tbprintf(buf, "Length of year: %d turns.%s", world.yearlength,
2988 help_newline());
2989 }
2990 if (world.daylength != 1) {
2991 tbprintf(buf, "Length of day: %d turns.%s", world.daylength,
2992 help_newline());
2993 tbprintf(buf, "Percentage daylight: %d%%.%s", world.daylight_fraction,
2994 help_newline());
2995 tbprintf(buf, "Percentage twilight: %d%%.%s",
2996 world.twilight_fraction - world.daylight_fraction,
2997 help_newline());
2998 }
2999 if (area.temp_year != lispnil) {
3000 /* (should describe temperature year cycle here) */
3001 }
3002 #ifdef DESIGNERS
3003 if (numdesigners > 0) {
3004 tbcatline(buf, "FOR DESIGNERS:");
3005 tbprintf(buf, "Area projection is %d.%s", area.projection,
3006 help_newline());
3007 tbprintf(buf, "Default contour line color is \"%s\".%s",
3008 g_contour_color(), help_newline());
3009 tbprintf(buf, "Default country border color is \"%s\".%s",
3010 g_country_border_color(), help_newline());
3011 tbprintf(buf, "Default frontline color is \"%s\".%s",
3012 g_frontline_color(), help_newline());
3013 tbprintf(buf, "Default grid color is \"%s\".%s",
3014 g_grid_color(), help_newline());
3015 tbprintf(buf, "Default meridian color is \"%s\".%s",
3016 g_meridian_color(), help_newline());
3017 tbprintf(buf, "Default shoreline color is \"%s\".%s",
3018 g_shoreline_color(), help_newline());
3019 tbprintf(buf, "Default unit name color is \"%s\".%s",
3020 g_unit_name_color(), help_newline());
3021 tbprintf(buf, "Default unseen color is \"%s\".%s",
3022 g_unseen_color(), help_newline());
3023 }
3024 #endif /* DESIGNERS */
3025 }
3026
3027 /* The following globals don't make sense for online help, but are listed
3028 here so that a cross-check of *.def and online help doesn't list them
3029 as undocumented: g_random_state g_run_serial_number g_turn. */
3030
3031 /* This describes a command (from cmd.def et al) in a way that all
3032 interfaces can use. */
3033
3034 void
describe_command(int ch,char * name,char * help,int onechar,TextBuffer * buf)3035 describe_command (int ch, char *name, char *help, int onechar, TextBuffer *buf)
3036 {
3037 if (onechar && ch != '\0') {
3038 if (ch < ' ' || ch > '~') {
3039 tbprintf(buf, "^%c ", (ch ^ 0x40));
3040 } else if (ch == ' ') {
3041 tbprintf(buf, "'%c' ", ch);
3042 } else {
3043 tbprintf(buf, " %c ", ch);
3044 }
3045 } else if (!onechar && ch == '\0') {
3046 tbcat(buf, "\"");
3047 tbcat(buf, name);
3048 tbcat(buf, "\"");
3049 } else
3050 return;
3051 tbcat(buf, " ");
3052 tbcat(buf, help);
3053 tbcatline(buf, "");
3054 }
3055
3056 static int
u_property_not_default(int (* fn)(int i),int dflt)3057 u_property_not_default(int (*fn)(int i), int dflt)
3058 {
3059 int u, val;
3060
3061 for_all_unit_types(u) {
3062 val = (*fn)(u);
3063 if (val != dflt)
3064 return TRUE;
3065 }
3066 return FALSE;
3067 }
3068
3069 static int
t_property_not_default(int (* fn)(int i),int dflt)3070 t_property_not_default(int (*fn)(int i), int dflt)
3071 {
3072 int t, val;
3073
3074 for_all_terrain_types(t) {
3075 val = (*fn)(t);
3076 if (val != dflt)
3077 return TRUE;
3078 }
3079 return FALSE;
3080 }
3081
3082 static int
uu_table_row_not_default(int u,int (* fn)(int i,int j),int dflt)3083 uu_table_row_not_default(int u, int (*fn)(int i, int j), int dflt)
3084 {
3085 int u2, val2;
3086
3087 for_all_unit_types(u2) {
3088 val2 = (*fn)(u, u2);
3089 if (val2 != dflt)
3090 return TRUE;
3091 }
3092 return FALSE;
3093 }
3094
3095 static int
ut_table_row_not_default(int u,int (* fn)(int i,int j),int dflt)3096 ut_table_row_not_default(int u, int (*fn)(int i, int j), int dflt)
3097 {
3098 int t, val2;
3099
3100 for_all_terrain_types(t) {
3101 val2 = (*fn)(u, t);
3102 if (val2 != dflt)
3103 return TRUE;
3104 }
3105 return FALSE;
3106 }
3107
3108 static int
um_table_row_not_default(int u,int (* fn)(int i,int j),int dflt)3109 um_table_row_not_default(int u, int (*fn)(int i, int j), int dflt)
3110 {
3111 int m, val2;
3112
3113 for_all_material_types(m) {
3114 val2 = (*fn)(u, m);
3115 if (val2 != dflt)
3116 return TRUE;
3117 }
3118 return FALSE;
3119 }
3120
3121 static int
ua_table_row_not_default(int u,int (* fn)(int i,int j),int dflt)3122 ua_table_row_not_default(int u, int (*fn)(int i, int j), int dflt)
3123 {
3124 int a, val2;
3125
3126 for_all_advance_types(a) {
3127 val2 = (*fn)(u, a);
3128 if (val2 != dflt)
3129 return TRUE;
3130 }
3131 return FALSE;
3132 }
3133
3134 static int
tt_table_row_not_default(int t1,int (* fn)(int i,int j),int dflt)3135 tt_table_row_not_default(int t1, int (*fn)(int i, int j), int dflt)
3136 {
3137 int t2, val2;
3138
3139 for_all_terrain_types(t2) {
3140 val2 = (*fn)(t1, t2);
3141 if (val2 != dflt)
3142 return TRUE;
3143 }
3144 return FALSE;
3145 }
3146
3147 #if 0 /* not used currently */
3148 static int
3149 tm_table_row_not_default(int t, int (*fn)(int i, int j), int dflt)
3150 {
3151 int m, val2;
3152
3153 for_all_material_types(m) {
3154 val2 = (*fn)(t, m);
3155 if (val2 != dflt)
3156 return TRUE;
3157 }
3158 return FALSE;
3159 }
3160 #endif
3161
3162 static int
aa_table_row_not_default(int a1,int (* fn)(int i,int j),int dflt)3163 aa_table_row_not_default(int a1, int (*fn)(int i, int j), int dflt)
3164 {
3165 int a2, val2;
3166
3167 for_all_advance_types(a2) {
3168 val2 = (*fn)(a1, a2);
3169 if (val2 != dflt)
3170 return TRUE;
3171 }
3172 return FALSE;
3173 }
3174
3175 static int
uu_table_column_not_default(int u,int (* fn)(int i,int j),int dflt)3176 uu_table_column_not_default(int u, int (*fn)(int i, int j), int dflt)
3177 {
3178 int u2, val2;
3179
3180 for_all_unit_types(u2) {
3181 val2 = (*fn)(u2, u);
3182 if (val2 != dflt)
3183 return TRUE;
3184 }
3185 return FALSE;
3186 }
3187
3188 static int
aa_table_column_not_default(int a1,int (* fn)(int i,int j),int dflt)3189 aa_table_column_not_default(int a1, int (*fn)(int i, int j), int dflt)
3190 {
3191 int a2, val2;
3192
3193 for_all_advance_types(a2) {
3194 val2 = (*fn)(a2, a1);
3195 if (val2 != dflt)
3196 return TRUE;
3197 }
3198 return FALSE;
3199 }
3200
3201 struct histo {
3202 int val, num;
3203 };
3204
3205 /* This compare will sort histogram entries in *reverse* order
3206 (most common values first). */
3207
3208 static int
histogram_compare(CONST void * h1,CONST void * h2)3209 histogram_compare(CONST void *h1, CONST void *h2)
3210 {
3211 if (((struct histo *) h2)->num != ((struct histo *) h1)->num) {
3212 return ((struct histo *) h2)->num - ((struct histo *) h1)->num;
3213 } else {
3214 return ((struct histo *) h2)->val - ((struct histo *) h1)->val;
3215 }
3216 }
3217
3218 static struct histo *u_histogram;
3219
3220 static void
u_property_desc(TextBuffer * buf,int (* fn)(int i),void (* formatter)(TextBuffer * buf,int val))3221 u_property_desc(TextBuffer *buf, int (*fn)(int i),
3222 void (*formatter)(TextBuffer *buf, int val))
3223 {
3224 int val, u, val2, constant = TRUE, found;
3225 int i, numentries, first;
3226
3227 if (formatter == NULL)
3228 formatter = tb_value_desc;
3229 if (u_histogram == NULL)
3230 u_histogram =
3231 (struct histo *) xmalloc(numutypes * sizeof(struct histo));
3232 /* Compute a histogram of all the values for the given property. */
3233 numentries = 0;
3234 val = (*fn)(0);
3235 u_histogram[numentries].val = val;
3236 u_histogram[numentries].num = 1;
3237 ++numentries;
3238 for_all_unit_types(u) {
3239 val2 = (*fn)(u);
3240 if (val2 == val) {
3241 ++(u_histogram[0].num);
3242 } else {
3243 constant = FALSE;
3244 found = FALSE;
3245 for (i = 1; i < numentries; ++i) {
3246 if (val2 == u_histogram[i].val) {
3247 ++(u_histogram[i].num);
3248 found = TRUE;
3249 break;
3250 }
3251 }
3252 if (!found) {
3253 u_histogram[numentries].val = val2;
3254 u_histogram[numentries].num = 1;
3255 ++numentries;
3256 }
3257 }
3258 }
3259 /* The constant table/row case is easily disposed of. */
3260 if (constant) {
3261 (*formatter)(buf, val);
3262 tbcat(buf, " for all unit types");
3263 return;
3264 }
3265 /* Not a constant row; sort the histogram and compose a description. */
3266 qsort(u_histogram, numentries, sizeof(struct histo), histogram_compare);
3267 /* Show a "by default" clause if at least half of the entries are all
3268 the same. */
3269 if (u_histogram[0].num * 2 >= numutypes) {
3270 (*formatter)(buf, u_histogram[0].val);
3271 tbcat(buf, " by default");
3272 i = 1;
3273 } else {
3274 i = 0;
3275 }
3276 for (; i < numentries; ++i) {
3277 if (i > 0)
3278 tbcat(buf, ", ");
3279 (*formatter)(buf, u_histogram[i].val);
3280 tbcat(buf, " for ");
3281 first = TRUE;
3282 for_all_unit_types(u) {
3283 if ((*fn)(u) == u_histogram[i].val) {
3284 if (!first)
3285 /* For this and similar situations, we need a space
3286 in addition to the comma, so interface elements
3287 (such as the Mac's text display) can decide to
3288 add line breaks. */
3289 tbcat(buf, ", ");
3290 else
3291 first = FALSE;
3292 tbcat(buf, u_type_name(u));
3293 }
3294 }
3295 }
3296 }
3297
3298 static struct histo *t_histogram;
3299
3300 static void
t_property_desc(TextBuffer * buf,int (* fn)(int i),void (* formatter)(TextBuffer * buf,int val))3301 t_property_desc(TextBuffer *buf, int (*fn)(int i),
3302 void (*formatter)(TextBuffer *buf, int val))
3303 {
3304 int val, t, val2, constant = TRUE, found;
3305 int i, numentries, first;
3306
3307 if (formatter == NULL)
3308 formatter = tb_value_desc;
3309 if (t_histogram == NULL)
3310 t_histogram =
3311 (struct histo *) xmalloc(numttypes * sizeof(struct histo));
3312 /* Compute a histogram of all the values for the given property. */
3313 numentries = 0;
3314 val = (*fn)(0);
3315 t_histogram[numentries].val = val;
3316 t_histogram[numentries].num = 1;
3317 ++numentries;
3318 for_all_terrain_types(t) {
3319 val2 = (*fn)(t);
3320 if (val2 == val) {
3321 ++(t_histogram[0].num);
3322 } else {
3323 constant = FALSE;
3324 found = FALSE;
3325 for (i = 1; i < numentries; ++i) {
3326 if (val2 == t_histogram[i].val) {
3327 ++(t_histogram[i].num);
3328 found = TRUE;
3329 break;
3330 }
3331 }
3332 if (!found) {
3333 t_histogram[numentries].val = val2;
3334 t_histogram[numentries].num = 1;
3335 ++numentries;
3336 }
3337 }
3338 }
3339 /* The constant table/row case is easily disposed of. */
3340 if (constant) {
3341 (*formatter)(buf, val);
3342 tbcat(buf, " for all terrain types");
3343 return;
3344 }
3345 /* Not a constant row; sort the histogram and compose a description. */
3346 qsort(t_histogram, numentries, sizeof(struct histo), histogram_compare);
3347 /* Show a "by default" clause if at least half of the entries are all
3348 the same. */
3349 if (t_histogram[0].num * 2 >= numttypes) {
3350 (*formatter)(buf, t_histogram[0].val);
3351 tbcat(buf, " by default");
3352 i = 1;
3353 } else {
3354 i = 0;
3355 }
3356 for (; i < numentries; ++i) {
3357 if (i > 0)
3358 tbcat(buf, ", ");
3359 (*formatter)(buf, t_histogram[i].val);
3360 tbcat(buf, " for ");
3361 first = TRUE;
3362 for_all_terrain_types(t) {
3363 if ((*fn)(t) == t_histogram[i].val) {
3364 if (!first)
3365 tbcat(buf, ", ");
3366 else
3367 first = FALSE;
3368 tbcat(buf, t_type_name(t));
3369 }
3370 }
3371 }
3372 }
3373
3374 /* Generate a textual description of a single unit's interaction with all other
3375 unit types wrt a given table. */
3376
3377 static void
uu_table_row_desc(TextBuffer * buf,int u,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val),char * connect)3378 uu_table_row_desc(TextBuffer *buf, int u, int (*fn)(int i, int j),
3379 void (*formatter)(TextBuffer *buf, int val), char *connect)
3380 {
3381 uu_table_rowcol_desc(buf, u, fn, formatter, connect, 0);
3382 }
3383
3384 static void
uu_table_column_desc(TextBuffer * buf,int u,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val),char * connect)3385 uu_table_column_desc(TextBuffer *buf, int u, int (*fn)(int i, int j),
3386 void (*formatter)(TextBuffer *buf, int val),
3387 char *connect)
3388 {
3389 uu_table_rowcol_desc(buf, u, fn, formatter, connect, 1);
3390 }
3391
3392 static void
uu_table_rowcol_desc(TextBuffer * buf,int u,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val),char * connect,int rowcol)3393 uu_table_rowcol_desc(TextBuffer *buf, int u, int (*fn)(int i, int j),
3394 void (*formatter)(TextBuffer *buf, int val),
3395 char *connect, int rowcol)
3396 {
3397 int val, val2, u2, constant = TRUE, found;
3398 int i, numentries, first;
3399
3400 if (formatter == NULL)
3401 formatter = tb_value_desc;
3402 if (empty_string(connect))
3403 connect = "for";
3404 if (u_histogram == NULL)
3405 u_histogram =
3406 (struct histo *) xmalloc(numutypes * sizeof(struct histo));
3407 val = (rowcol ? (*fn)(0, u) : (*fn)(u, 0));
3408 /* Compute a histogram of all the values in the row of the table. */
3409 numentries = 0;
3410 u_histogram[numentries].val = val;
3411 u_histogram[numentries].num = 1;
3412 ++numentries;
3413 for_all_unit_types(u2) {
3414 val2 = (rowcol ? (*fn)(u2, u) : (*fn)(u, u2));
3415 if (val2 == val) {
3416 ++(u_histogram[0].num);
3417 } else {
3418 constant = FALSE;
3419 found = FALSE;
3420 for (i = 1; i < numentries; ++i) {
3421 if (val2 == u_histogram[i].val) {
3422 ++(u_histogram[i].num);
3423 found = TRUE;
3424 break;
3425 }
3426 }
3427 if (!found) {
3428 u_histogram[numentries].val = val2;
3429 u_histogram[numentries].num = 1;
3430 ++numentries;
3431 }
3432 }
3433 }
3434 /* The constant table/row case is easily disposed of. */
3435 if (constant) {
3436 (*formatter)(buf, val);
3437 tbprintf(buf, " %s all unit types", connect);
3438 return;
3439 }
3440 /* Not a constant row; sort the histogram and compose a description. */
3441 qsort(u_histogram, numentries, sizeof(struct histo), histogram_compare);
3442 /* Show a "by default" clause if at least half of the entries are all
3443 the same. */
3444 if (u_histogram[0].num * 2 >= numutypes) {
3445 (*formatter)(buf, u_histogram[0].val);
3446 tbcat(buf, " by default");
3447 i = 1;
3448 } else {
3449 i = 0;
3450 }
3451 for (; i < numentries; ++i) {
3452 if (i > 0)
3453 tbcat(buf, ", ");
3454 (*formatter)(buf, u_histogram[i].val);
3455 tbprintf(buf, " %s ", connect);
3456 first = TRUE;
3457 for_all_unit_types(u2) {
3458 val2 = (rowcol ? (*fn)(u2, u) : (*fn)(u, u2));
3459 if (val2 == u_histogram[i].val) {
3460 if (!first)
3461 tbcat(buf, ", ");
3462 else
3463 first = FALSE;
3464 tbcat(buf, u_type_name(u2));
3465 }
3466 }
3467 }
3468 }
3469
3470 /* Generate a textual description of a single unit's interaction with all
3471 terrain types wrt a given table. */
3472
3473 static void
ut_table_row_desc(TextBuffer * buf,int u,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val),char * connect)3474 ut_table_row_desc(TextBuffer *buf, int u, int (*fn)(int i, int j),
3475 void (*formatter)(TextBuffer *buf, int val), char *connect)
3476 {
3477 int val = (*fn)(u, 0), val2, t, constant = TRUE, found;
3478 int i, numentries, first;
3479
3480 if (formatter == NULL)
3481 formatter = tb_value_desc;
3482 if (empty_string(connect))
3483 connect = "for";
3484 if (u_histogram == NULL)
3485 u_histogram =
3486 (struct histo *) xmalloc(numutypes * sizeof(struct histo));
3487 /* Compute a histogram of all the values in the row of the table. */
3488 numentries = 0;
3489 u_histogram[numentries].val = val;
3490 u_histogram[numentries].num = 1;
3491 ++numentries;
3492 for_all_terrain_types(t) {
3493 val2 = (*fn)(u, t);
3494 if (val2 == val) {
3495 ++(u_histogram[0].num);
3496 } else {
3497 constant = FALSE;
3498 found = FALSE;
3499 for (i = 1; i < numentries; ++i) {
3500 if (val2 == u_histogram[i].val) {
3501 ++(u_histogram[i].num);
3502 found = TRUE;
3503 break;
3504 }
3505 }
3506 if (!found) {
3507 u_histogram[numentries].val = val2;
3508 u_histogram[numentries].num = 1;
3509 ++numentries;
3510 }
3511 }
3512 }
3513 /* The constant table/row case is easily disposed of. */
3514 if (constant) {
3515 (*formatter)(buf, val);
3516 tbprintf(buf, " %s all terrain types", connect);
3517 return;
3518 }
3519 /* Not a constant row; sort the histogram and compose a description. */
3520 qsort(u_histogram, numentries, sizeof(struct histo), histogram_compare);
3521 /* Show a "by default" clause if at least half of the entries are all
3522 the same. */
3523 if (u_histogram[0].num * 2 >= numttypes) {
3524 (*formatter)(buf, u_histogram[0].val);
3525 tbcat(buf, " by default");
3526 i = 1;
3527 } else {
3528 i = 0;
3529 }
3530 for (; i < numentries; ++i) {
3531 if (i > 0)
3532 tbcat(buf, ", ");
3533 (*formatter)(buf, u_histogram[i].val);
3534 tbprintf(buf, " %s ", connect);
3535 first = TRUE;
3536 for_all_terrain_types(t) {
3537 if ((*fn)(u, t) == u_histogram[i].val) {
3538 if (!first)
3539 tbcat(buf, ", ");
3540 else
3541 first = FALSE;
3542 tbcat(buf, t_type_name(t));
3543 }
3544 }
3545 }
3546 }
3547
3548 static void
um_table_row_desc(TextBuffer * buf,int u,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val))3549 um_table_row_desc(TextBuffer *buf, int u, int (*fn)(int i, int j),
3550 void (*formatter)(TextBuffer *buf, int val))
3551 {
3552 int val = (*fn)(u, 0), val2, m, constant = TRUE, found;
3553 int i, numentries, first;
3554 char *connect = "vs";
3555
3556 if (formatter == NULL)
3557 formatter = tb_value_desc;
3558 if (u_histogram == NULL)
3559 u_histogram =
3560 (struct histo *) xmalloc(numutypes * sizeof(struct histo));
3561 /* Compute a histogram of all the values in the row of the table. */
3562 numentries = 0;
3563 u_histogram[numentries].val = val;
3564 u_histogram[numentries].num = 1;
3565 ++numentries;
3566 for_all_material_types(m) {
3567 val2 = (*fn)(u, m);
3568 if (val2 == val) {
3569 ++(u_histogram[0].num);
3570 } else {
3571 constant = FALSE;
3572 found = FALSE;
3573 for (i = 1; i < numentries; ++i) {
3574 if (val2 == u_histogram[i].val) {
3575 ++(u_histogram[i].num);
3576 found = TRUE;
3577 break;
3578 }
3579 }
3580 if (!found) {
3581 u_histogram[numentries].val = val2;
3582 u_histogram[numentries].num = 1;
3583 ++numentries;
3584 }
3585 }
3586 }
3587 /* The constant table/row case is easily disposed of. */
3588 if (constant) {
3589 (*formatter)(buf, val);
3590 tbprintf(buf, " %s all material types", connect);
3591 return;
3592 }
3593 /* Not a constant row; sort the histogram and compose a description. */
3594 qsort(u_histogram, numentries, sizeof(struct histo), histogram_compare);
3595 /* Show a "by default" clause if at least half of the entries are all
3596 the same. */
3597 if (u_histogram[0].num * 2 >= nummtypes) {
3598 (*formatter)(buf, u_histogram[0].val);
3599 tbcat(buf, " by default");
3600 i = 1;
3601 } else {
3602 i = 0;
3603 }
3604 for (; i < numentries; ++i) {
3605 if (i > 0)
3606 tbcat(buf, ", ");
3607 (*formatter)(buf, u_histogram[i].val);
3608 tbprintf(buf, " %s ", connect);
3609 first = TRUE;
3610 for_all_material_types(m) {
3611 if ((*fn)(u, m) == u_histogram[i].val) {
3612 if (!first)
3613 tbcat(buf, ", ");
3614 else
3615 first = FALSE;
3616 tbcat(buf, m_type_name(m));
3617 }
3618 }
3619 }
3620 }
3621
3622 static void
ua_table_row_desc(TextBuffer * buf,int u,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val))3623 ua_table_row_desc(TextBuffer *buf, int u, int (*fn)(int i, int j),
3624 void (*formatter)(TextBuffer *buf, int val))
3625 {
3626 int val = (*fn)(u, 0), val2, a, constant = TRUE, found;
3627 int i, numentries, first;
3628 char *connect = "vs";
3629
3630 if (formatter == NULL)
3631 formatter = tb_value_desc;
3632 if (u_histogram == NULL)
3633 u_histogram =
3634 (struct histo *) xmalloc(numutypes * sizeof(struct histo));
3635 /* Compute a histogram of all the values in the row of the table. */
3636 numentries = 0;
3637 u_histogram[numentries].val = val;
3638 u_histogram[numentries].num = 1;
3639 ++numentries;
3640 for_all_advance_types(a) {
3641 val2 = (*fn)(u, a);
3642 if (val2 == val) {
3643 ++(u_histogram[0].num);
3644 } else {
3645 constant = FALSE;
3646 found = FALSE;
3647 for (i = 1; i < numentries; ++i) {
3648 if (val2 == u_histogram[i].val) {
3649 ++(u_histogram[i].num);
3650 found = TRUE;
3651 break;
3652 }
3653 }
3654 if (!found) {
3655 u_histogram[numentries].val = val2;
3656 u_histogram[numentries].num = 1;
3657 ++numentries;
3658 }
3659 }
3660 }
3661 /* The constant table/row case is easily disposed of. */
3662 if (constant) {
3663 (*formatter)(buf, val);
3664 tbprintf(buf, " %s all advance types", connect);
3665 return;
3666 }
3667 /* Not a constant row; sort the histogram and compose a description. */
3668 qsort(u_histogram, numentries, sizeof(struct histo), histogram_compare);
3669 /* Show a "by default" clause if at least half of the entries are all
3670 the same. */
3671 if (u_histogram[0].num * 2 >= nummtypes) {
3672 (*formatter)(buf, u_histogram[0].val);
3673 tbcat(buf, " by default");
3674 i = 1;
3675 } else {
3676 i = 0;
3677 }
3678 for (; i < numentries; ++i) {
3679 if (i > 0)
3680 tbcat(buf, ", ");
3681 (*formatter)(buf, u_histogram[i].val);
3682 tbprintf(buf, " %s ", connect);
3683 first = TRUE;
3684 for_all_advance_types(a) {
3685 if ((*fn)(u, a) == u_histogram[i].val) {
3686 if (!first)
3687 tbcat(buf, ", ");
3688 else
3689 first = FALSE;
3690 tbcat(buf, a_type_name(a));
3691 }
3692 }
3693 }
3694 }
3695
3696 static void
tt_table_row_desc(TextBuffer * buf,int t0,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val))3697 tt_table_row_desc(TextBuffer *buf, int t0, int (*fn)(int i, int j),
3698 void (*formatter)(TextBuffer *buf, int val))
3699 {
3700 int val = (*fn)(t0, 0), val2, t, constant = TRUE, found;
3701 int i, numentries, first;
3702
3703 if (formatter == NULL)
3704 formatter = tb_value_desc;
3705 if (t_histogram == NULL)
3706 t_histogram =
3707 (struct histo *) xmalloc(numttypes * sizeof(struct histo));
3708 /* Compute a histogram of all the values in the row of the table. */
3709 numentries = 0;
3710 t_histogram[numentries].val = val;
3711 t_histogram[numentries].num = 1;
3712 ++numentries;
3713 for_all_terrain_types(t) {
3714 val2 = (*fn)(t0, t);
3715 if (val2 == val) {
3716 ++(t_histogram[0].num);
3717 } else {
3718 constant = FALSE;
3719 found = FALSE;
3720 for (i = 1; i < numentries; ++i) {
3721 if (val2 == t_histogram[i].val) {
3722 ++(t_histogram[i].num);
3723 found = TRUE;
3724 break;
3725 }
3726 }
3727 if (!found) {
3728 t_histogram[numentries].val = val2;
3729 t_histogram[numentries].num = 1;
3730 ++numentries;
3731 }
3732 }
3733 }
3734 /* The constant table/row case is easily disposed of. */
3735 if (constant) {
3736 (*formatter)(buf, val);
3737 tbcat(buf, " for all terrain types");
3738 return;
3739 }
3740 /* Not a constant row; sort the histogram and compose a description. */
3741 qsort(t_histogram, numentries, sizeof(struct histo), histogram_compare);
3742 /* Show a "by default" clause if at least half of the entries are all
3743 the same. */
3744 if (t_histogram[0].num * 2 >= numttypes) {
3745 (*formatter)(buf, t_histogram[0].val);
3746 tbcat(buf, " by default");
3747 i = 1;
3748 } else {
3749 i = 0;
3750 }
3751 for (; i < numentries; ++i) {
3752 if (i > 0)
3753 tbcat(buf, ", ");
3754 (*formatter)(buf, t_histogram[i].val);
3755 tbcat(buf, " vs ");
3756 first = TRUE;
3757 for_all_terrain_types(t) {
3758 if ((*fn)(t0, t) == t_histogram[i].val) {
3759 if (!first)
3760 tbcat(buf, ", ");
3761 else
3762 first = FALSE;
3763 tbcat(buf, t_type_name(t));
3764 }
3765 }
3766 }
3767 }
3768
3769 static void
aa_table_row_desc(TextBuffer * buf,int a0,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val))3770 aa_table_row_desc(TextBuffer *buf, int a0, int (*fn)(int i, int j),
3771 void (*formatter)(TextBuffer *buf, int val))
3772 {
3773 aa_table_rowcol_desc(buf, a0, fn, formatter, 0);
3774 }
3775
3776 static void
aa_table_column_desc(TextBuffer * buf,int a1,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val))3777 aa_table_column_desc(TextBuffer *buf, int a1, int (*fn)(int i, int j),
3778 void (*formatter)(TextBuffer *buf, int val))
3779 {
3780 aa_table_rowcol_desc(buf, a1, fn, formatter, 1);
3781 }
3782
3783 static struct histo *a_histogram;
3784
3785 static void
aa_table_rowcol_desc(TextBuffer * buf,int a1,int (* fn)(int i,int j),void (* formatter)(TextBuffer * buf,int val),int rowcol)3786 aa_table_rowcol_desc(TextBuffer *buf, int a1, int (*fn)(int i, int j),
3787 void (*formatter)(TextBuffer *buf, int val), int rowcol)
3788 {
3789 int val = (*fn)(0, a1), val2, a, constant = TRUE, found;
3790 int i, numentries, first;
3791
3792 if (formatter == NULL)
3793 formatter = tb_value_desc;
3794 if (a_histogram == NULL)
3795 a_histogram =
3796 (struct histo *) xmalloc(numatypes * sizeof(struct histo));
3797 val = (rowcol ? (*fn)(0, a1) : (*fn)(a1, 0));
3798 /* Compute a histogram of all the values in the row of the table. */
3799 numentries = 0;
3800 a_histogram[numentries].val = val;
3801 a_histogram[numentries].num = 1;
3802 ++numentries;
3803 for_all_advance_types(a) {
3804 val2 = (rowcol ? (*fn)(a, a1) : (*fn)(a1, a));
3805 if (val2 == val) {
3806 ++(a_histogram[0].num);
3807 } else {
3808 constant = FALSE;
3809 found = FALSE;
3810 for (i = 1; i < numentries; ++i) {
3811 if (val2 == a_histogram[i].val) {
3812 ++(a_histogram[i].num);
3813 found = TRUE;
3814 break;
3815 }
3816 }
3817 if (!found) {
3818 a_histogram[numentries].val = val2;
3819 a_histogram[numentries].num = 1;
3820 ++numentries;
3821 }
3822 }
3823 }
3824 /* The constant table/row case is easily disposed of. */
3825 if (constant) {
3826 (*formatter)(buf, val);
3827 tbcat(buf, " for all advance types");
3828 return;
3829 }
3830 /* Not a constant column; sort the histogram and compose a description. */
3831 qsort(a_histogram, numentries, sizeof(struct histo), histogram_compare);
3832 for (i = 0; i < numentries; ++i) {
3833 /* Skip over advances that are not needed. */
3834 if (a_histogram[i].val == 0)
3835 continue;
3836 first = TRUE;
3837 for_all_advance_types(a) {
3838 val2 = (rowcol ? (*fn)(a, a1) : (*fn)(a1, a));
3839 if (val2 == a_histogram[i].val) {
3840 if (!first)
3841 tbcat(buf, ", ");
3842 else
3843 first = FALSE;
3844 tbcat(buf, a_type_name(a));
3845 }
3846 }
3847 }
3848 }
3849
3850 #if 0 /* not currently used */
3851 /* A simple table-printing utility. Blanks out default values so they don't
3852 clutter the table. */
3853
3854 static void
3855 append_number(buf, value, dflt)
3856 TextBuffer *buf;
3857 int value, dflt;
3858 {
3859 if (value != dflt) {
3860 tbprintf(buf, "%5d ", value);
3861 } else {
3862 tbprintf(buf, "%s", help_indent(6));
3863 }
3864 }
3865 #endif
3866
3867 static void
append_help_phrase(TextBuffer * buf,char * phrase)3868 append_help_phrase(TextBuffer *buf, char *phrase)
3869 {
3870 if (empty_string(phrase))
3871 return;
3872
3873 /* Extra new line makes display less cluttered. */
3874 tbcat(buf, "----- ");
3875 tbcat(buf, phrase);
3876 tbprintf(buf, " -----%s%s", help_newline(), help_newline());
3877 }
3878
3879 static void
append_notes(TextBuffer * buf,Obj * notes)3880 append_notes(TextBuffer *buf, Obj *notes)
3881 {
3882 char *notestr;
3883 Obj *rest;
3884
3885 if (stringp(notes)) {
3886 notestr = c_string(notes);
3887 if (strlen(notestr) > 0) {
3888 tbcat(buf, notestr);
3889 tbcat(buf, " ");
3890 } else {
3891 tbcatline(buf, "");
3892 }
3893 } else if (consp(notes)) {
3894 for_all_list(notes, rest) {
3895 append_notes(buf, car(rest));
3896 }
3897 } else {
3898 run_warning("notes not list or strings, ignoring");
3899 }
3900 }
3901
3902 void
append_blurb_strings(char * buf,Obj * notes)3903 append_blurb_strings(char *buf, Obj *notes)
3904 {
3905 char *str;
3906 Obj *rest;
3907
3908 if (stringp(notes)) {
3909 str = c_string(notes);
3910 if (strlen(str) > 0) {
3911 tnprintf(buf, BLURBSIZE, str);
3912 tnprintf(buf, BLURBSIZE, " ");
3913 } else {
3914 tnprintf(buf, BLURBSIZE, help_newline());
3915 }
3916 } else if (consp(notes)) {
3917 for_all_list(notes, rest) {
3918 append_blurb_strings(buf, car(rest));
3919 }
3920 } else {
3921 run_warning("blurb not list or strings, ignoring");
3922 }
3923 }
3924
3925 void
notify_instructions(void)3926 notify_instructions(void)
3927 {
3928 Obj *instructions = mainmodule->instructions, *rest;
3929
3930 if (instructions != lispnil) {
3931 if (stringp(instructions)) {
3932 notify_all("%s", c_string(instructions));
3933 } else if (consp(instructions)) {
3934 for (rest = instructions; rest != lispnil; rest = cdr(rest)) {
3935 if (stringp(car(rest))) {
3936 notify_all("%s", c_string(car(rest)));
3937 } else {
3938 /* (should probably warn about this case too) */
3939 }
3940 }
3941 } else {
3942 run_warning("Instructions are of wrong type");
3943 }
3944 } else {
3945 notify_all("(no instructions supplied)");
3946 }
3947 }
3948
3949 /* Print the news file onto the console if there is anything to print. */
3950
3951 void
print_any_news(void)3952 print_any_news(void)
3953 {
3954 FILE *fp;
3955
3956 fp = open_library_file(news_filename());
3957 if (fp != NULL) {
3958 printf("\n XCONQ NEWS\n\n");
3959 while (fgets(spbuf, BUFSIZE-1, fp) != NULL) {
3960 fputs(spbuf, stdout);
3961 }
3962 /* Add another blank line, to separate from init printouts. */
3963 printf("\n");
3964 fclose(fp);
3965 }
3966 }
3967
3968 /* Generate a readable description of the game (design) being played. */
3969 /* This works by writing out appropriate help nodes, along with some
3970 indexing material. This does *not* do interface-specific help,
3971 such as commands. */
3972
3973 void
print_game_description_to_file(FILE * fp)3974 print_game_description_to_file(FILE *fp)
3975 {
3976 HelpNode *node;
3977
3978 /* (need to work on which nodes to dump out) */
3979 for (node = first_help_node; node != first_help_node; node = node->next) {
3980 get_help_text(node);
3981 if (node->text != NULL) {
3982 fprintf(fp, "\014\n%s\n", node->key);
3983 fprintf(fp, "%s\n", node->text);
3984 }
3985 }
3986 }
3987
3988 static void
tb_value_desc(TextBuffer * buf,int val)3989 tb_value_desc(TextBuffer *buf, int val)
3990 {
3991 char charbuf[30];
3992
3993 sprintf(charbuf, "%d", val);
3994 tbcat(buf, charbuf);
3995 }
3996
3997 static void
tb_fraction_desc(TextBuffer * buf,int val)3998 tb_fraction_desc(TextBuffer *buf, int val)
3999 {
4000 char charbuf[30];
4001
4002 if (val % 100 == 0)
4003 sprintf(charbuf, "%d", val / 100);
4004 else
4005 sprintf(charbuf, "%d.%2.2d", val / 100, val % 100);
4006 tbcat(buf, charbuf);
4007 }
4008
4009 static void
tb_percent_desc(TextBuffer * buf,int val)4010 tb_percent_desc(TextBuffer *buf, int val)
4011 {
4012 tb_value_desc(buf, val);
4013 tbcat(buf, "%");
4014 }
4015
4016 static void
tb_percent_100th_desc(TextBuffer * buf,int val)4017 tb_percent_100th_desc(TextBuffer *buf, int val)
4018 {
4019 tb_fraction_desc(buf, val);
4020 tbcat(buf, "%");
4021 }
4022
4023 /* Print out a prob_fraction-style value */
4024 static void
tb_probfraction_desc(TextBuffer * buf,int val)4025 tb_probfraction_desc(TextBuffer *buf, int val)
4026 {
4027 char charbuf[80];
4028
4029 if (val % 100 == 0) {
4030 sprintf(charbuf, "%d", val / 100);
4031 } else if (val < 100) {
4032 sprintf(charbuf, "%u%% chance of 1", val);
4033 } else {
4034 sprintf(charbuf, "%u%% chance of %u (otherwise %u)", val % 100,
4035 val / 100 + 1, val / 100);
4036 }
4037 tbcat(buf, charbuf);
4038 }
4039
4040 static void
tb_dice_desc(TextBuffer * buf,int val)4041 tb_dice_desc(TextBuffer *buf, int val)
4042 {
4043 char charbuf[30];
4044
4045 dice_desc(charbuf, val);
4046 tbcat(buf, charbuf);
4047 }
4048
4049 static void
tb_mult_desc(TextBuffer * buf,int val)4050 tb_mult_desc(TextBuffer *buf, int val)
4051 {
4052 char charbuf[30];
4053
4054 sprintf(charbuf, "*%d.%2.2d", val / 100, val % 100);
4055 tbcat(buf, charbuf);
4056 }
4057
4058 static void
tb_bool_desc(TextBuffer * buf,int val)4059 tb_bool_desc(TextBuffer *buf, int val)
4060 {
4061 tbcat(buf, (char *)(val ? "true" : "false"));
4062 }
4063
4064 void
tbprintf(TextBuffer * buf,char * str,...)4065 tbprintf(TextBuffer *buf, char *str, ...)
4066 {
4067 va_list ap;
4068 char line[300];
4069
4070 va_start(ap, str);
4071 vsnprintf(line, 300, str, ap);
4072 tbcat(buf, line);
4073 va_end(ap);
4074 }
4075
4076 #undef bcopy
4077 #define bcopy(a,b,c) memcpy(b,a,c)
4078
4079 void
tbcat(TextBuffer * buf,char * str)4080 tbcat(TextBuffer *buf, char *str)
4081 {
4082 obstack_grow(&(buf->ostack), str, strlen(str));
4083 }
4084
4085 void
tbcat_si(TextBuffer * buf,char * str)4086 tbcat_si(TextBuffer *buf, char *str)
4087 {
4088 tbprintf(buf, "%s", help_indent(2));
4089 obstack_grow(&(buf->ostack), str, strlen(str));
4090 }
4091
4092 void
tbcatline(TextBuffer * buf,char * str)4093 tbcatline(TextBuffer *buf, char *str)
4094 {
4095 assert_error(buf, "Tried to concatenate a line to a NULL text buffer");
4096 assert_error(str, "Tried to concatenate a NULL string to a text buffer");
4097 tbcat(buf, str);
4098 tbcat(buf, help_newline());
4099 }
4100
4101 void
tbcatline_si(TextBuffer * buf,char * str)4102 tbcatline_si(TextBuffer *buf, char *str)
4103 {
4104 assert_error(buf, "Tried to concatenate a line to a NULL text buffer");
4105 assert_error(str, "Tried to concatenate a NULL string to a text buffer");
4106 tbprintf(buf, "%s%s%s", help_indent(2), str, help_newline());
4107 }
4108
4109 /* Prepares a newline for the help system in the current output format. */
4110 static char *
help_newline(void)4111 help_newline(void)
4112 {
4113 switch (help_output_mode) {
4114 case HELP_OUTPUT_PLAIN_TEXT:
4115 return "\n";
4116 case HELP_OUTPUT_HTML:
4117 return "<BR>\n";
4118 default:
4119 return NULL;
4120 }
4121 return NULL;
4122 }
4123
4124 /* Prepares an indent for the help system in the current output format. */
4125 static char *
help_indent(unsigned int spaces)4126 help_indent(unsigned int spaces)
4127 {
4128 int i = 0;
4129 int indentchars = 0;
4130
4131 if (!spaces)
4132 return "";
4133 memset(tmpbuf, 0, BUFSIZE);
4134 switch (help_output_mode) {
4135 case HELP_OUTPUT_PLAIN_TEXT:
4136 indentchars = spaces;
4137 break;
4138 case HELP_OUTPUT_HTML:
4139 indentchars = spaces * 12;
4140 break;
4141 default: break;
4142 }
4143 assert_warning_return(indentchars <= BUFSIZE,
4144 "Number of requested indentation chars exceeds buffer size",
4145 "");
4146 switch (help_output_mode) {
4147 case HELP_OUTPUT_PLAIN_TEXT:
4148 memset(tmpbuf, ' ', spaces);
4149 break;
4150 case HELP_OUTPUT_HTML:
4151 for (i = 0; i < spaces; ++i)
4152 strcat(tmpbuf, " ");
4153 break;
4154 default: break;
4155 }
4156 return tmpbuf;
4157 }
4158
4159 /* Prepares help file for a "Table of Contents" section. */
4160
4161 void
prep_help_cc_toc(void)4162 prep_help_cc_toc(void)
4163 {
4164 char *ccfilebname = "index";
4165 char *ccfileext = NULL;
4166 char ccfilename [BUFSIZE];
4167
4168 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4169 return;
4170 ccfileext = get_help_file_extension();
4171 make_pathname(NULL, ccfilebname, ccfileext, ccfilename);
4172 help_output_toc_filep = prep_help_file(ccfilename);
4173 write_help_file_header(help_output_toc_filep, "Table of Contents");
4174 write_help_section_header(help_output_toc_filep, "Table of Contents");
4175 if (mainmodule && mainmodule->blurb && stringp(mainmodule->blurb))
4176 fprintf(help_output_toc_filep,
4177 "<CENTER><H5>"%s"</H5></CENTER><BR>\n",
4178 c_string(mainmodule->blurb));
4179 write_help_file_navbar(help_output_toc_filep, HELP_PAGE_TOC, -1);
4180 }
4181
4182 /* Prepares help file for any standard section using the temp file buffer. */
4183
4184 static void
meta_prep_help_cc(HelpPage hpage,int hpageidx)4185 meta_prep_help_cc(HelpPage hpage, int hpageidx)
4186 {
4187 char namebuf [BUFSIZE];
4188 char numbuf [BUFSIZE];
4189 char filebnamebuf [BUFSIZE];
4190 int indentlvl = 0;
4191
4192 strncpy(filebnamebuf, hpagedefns[hpage].filebname, BUFSIZE);
4193 strncpy(namebuf, hpagedefns[hpage].name, BUFSIZE);
4194 if (-1 < hpageidx) {
4195 switch (hpage) {
4196 case HELP_PAGE_UTYPE:
4197 strncpy(namebuf, u_type_name(hpageidx), BUFSIZE);
4198 break;
4199 case HELP_PAGE_TTYPE:
4200 strncpy(namebuf, t_type_name(hpageidx), BUFSIZE);
4201 break;
4202 case HELP_PAGE_MTYPE:
4203 strncpy(namebuf, m_type_name(hpageidx), BUFSIZE);
4204 break;
4205 case HELP_PAGE_ATYPE:
4206 strncpy(namebuf, a_type_name(hpageidx), BUFSIZE);
4207 break;
4208 default: break;
4209 }
4210 sprintf(numbuf, "%d", hpageidx);
4211 strcat(filebnamebuf, numbuf);
4212 indentlvl = 1;
4213 }
4214 prep_help_cc_any(filebnamebuf, namebuf);
4215 write_help_file_navbar(help_output_tmp_filep, hpage, hpageidx);
4216 write_help_toc_entry(filebnamebuf, namebuf, indentlvl);
4217 }
4218
4219 /* Prepares help file for any section. */
4220
4221 void
prep_help_cc_any(char * ccfilebname,char * sectionname)4222 prep_help_cc_any(char *ccfilebname, char *sectionname)
4223 {
4224 char *ccfileext = NULL;
4225 char ccfilename [BUFSIZE];
4226
4227 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4228 return;
4229 assert_error(ccfilebname,
4230 "Attempted to prepare an help file with a NULL name");
4231 assert_error(ccfilebname[0],
4232 "Attempted to prepare an help file with an empty name");
4233 assert_error(sectionname,
4234 "Attempted to prepare an help file with a NULL section name");
4235 ccfileext = get_help_file_extension();
4236 make_pathname(NULL, ccfilebname, ccfileext, ccfilename);
4237 help_output_tmp_filep = prep_help_file(ccfilename);
4238 write_help_file_header(help_output_tmp_filep, sectionname);
4239 write_help_section_header(help_output_tmp_filep, sectionname);
4240 }
4241
4242 /* Generic core for prepping a new help system CC file. */
4243
4244 FILE *
prep_help_file(char * hfilename)4245 prep_help_file(char *hfilename)
4246 {
4247 FILE *ccfile = NULL;
4248 char *ccfilepath = NULL;
4249
4250 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4251 return NULL;
4252 assert_error(hfilename,
4253 "Attempted to open a help file with a NULL name.");
4254 assert_error(hfilename[0],
4255 "Attempted to open a help file with an empty name.");
4256 ccfilepath = (char *)xmalloc(strlen(help_output_dir) + 10 +
4257 strlen(hfilename));
4258 make_pathname(help_output_dir, hfilename, NULL, ccfilepath);
4259 ccfile = fopen(ccfilepath, "w");
4260 if (NULL == ccfile)
4261 run_error("Failed to open %s for writing.", ccfilepath);
4262 return ccfile;
4263 }
4264
4265 /* Add TOC entry to TOC help file stream. */
4266
4267 #define HELP_PLAIN_TEXT_COLS_MAX 72
4268 #define HELP_PLAIN_TEXT_TRUNC_COL (HELP_PLAIN_TEXT_COLS_MAX - 1)
4269 #define HELP_PLAIN_TEXT_IDX_TRUNC_COL 39
4270
4271 void
write_help_toc_entry(char * hfilebname,char * sectionname,int indentlvl)4272 write_help_toc_entry(char *hfilebname, char *sectionname, int indentlvl)
4273 {
4274 char ccfilename [BUFSIZE];
4275 char *ccfileext = NULL;
4276 char ptextbuf [HELP_PLAIN_TEXT_COLS_MAX];
4277 int lookupwidth = 0, indexpos = 0;
4278
4279 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4280 return;
4281 assert_error(sectionname, "Tried to index a NULL section name");
4282 /* Build up the help file name, if the basename is not NULL. */
4283 if (hfilebname) {
4284 ccfileext = get_help_file_extension();
4285 make_pathname("", hfilebname, ccfileext, ccfilename);
4286 }
4287 /* Write out the TOC entry line. */
4288 switch (help_output_mode) {
4289 case HELP_OUTPUT_PLAIN_TEXT:
4290 /* Fill output buffer with guide dots for easier lookup, ......
4291 and that cluttered, information-packed look. ;-) */
4292 memset(ptextbuf, 0, HELP_PLAIN_TEXT_COLS_MAX);
4293 /* Prepare section name with left justification, starting in the
4294 leftmost column. */
4295 snprintf(ptextbuf, HELP_PLAIN_TEXT_IDX_TRUNC_COL, "%s%s",
4296 help_indent(indentlvl*2), sectionname);
4297 indexpos = min(strlen(ptextbuf), HELP_PLAIN_TEXT_IDX_TRUNC_COL);
4298 memset(ptextbuf + indexpos, '.', HELP_PLAIN_TEXT_TRUNC_COL - indexpos);
4299 if (hfilebname) {
4300 lookupwidth = strlen(ccfilename) + 2;
4301 if (lookupwidth > (HELP_PLAIN_TEXT_COLS_MAX - 10))
4302 run_error(
4303 "File name is too large for table of contents help file");
4304 /* Prepare file name with right justification, starting in the
4305 rightmost column. */
4306 sprintf(ptextbuf + (HELP_PLAIN_TEXT_TRUNC_COL - lookupwidth),
4307 "..%s", ccfilename);
4308 }
4309 /* Output the prepared buffer. */
4310 fprintf(help_output_toc_filep, "%s\n", ptextbuf);
4311 break;
4312 case HELP_OUTPUT_HTML:
4313 if (hfilebname)
4314 fprintf(help_output_toc_filep, "%s<A Href=\"%s\">%s</A><BR>\n",
4315 help_indent(indentlvl*2), ccfilename, sectionname);
4316 else
4317 fprintf(help_output_toc_filep, "%s%s<BR>\n",
4318 help_indent(indentlvl*2), sectionname);
4319 break;
4320 default: break;
4321 }
4322 fflush(help_output_toc_filep);
4323 }
4324
4325 /* Flush help node to help file stream for any section. */
4326
4327 void
flush_help_cc_tmp(HelpNode * helpnode)4328 flush_help_cc_tmp(HelpNode *helpnode)
4329 {
4330 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4331 return;
4332 flush_help_cc(help_output_tmp_filep, helpnode);
4333 }
4334
4335 /* Generic core for flushing a completed help node to help system file. */
4336
4337 void
flush_help_cc(FILE * ccfile,HelpNode * helpnode)4338 flush_help_cc(FILE *ccfile, HelpNode *helpnode)
4339 {
4340 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4341 return;
4342 assert_error(ccfile,
4343 "Attempted to flush a help node to a NULL help file");
4344 assert_error(helpnode,
4345 "Attempted to flush a NULL help node to a help file");
4346 fprintf(ccfile, "%s", get_help_text(helpnode));
4347 if (helpnode->text) {
4348 free(helpnode->text);
4349 helpnode->text = NULL;
4350 }
4351 switch (help_output_mode) {
4352 case HELP_OUTPUT_PLAIN_TEXT:
4353 fprintf(ccfile, "\n");
4354 break;
4355 case HELP_OUTPUT_HTML:
4356 fprintf(ccfile, "\n<BR>\n");
4357 break;
4358 default: break;
4359 }
4360 fflush(ccfile);
4361 }
4362
4363 /* Finishes help file for "Table of Contents" section. */
4364
4365 void
finish_help_cc_toc(void)4366 finish_help_cc_toc(void)
4367 {
4368 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4369 return;
4370 write_help_section_footer(help_output_toc_filep, "Table of Contents");
4371 write_help_file_navbar(help_output_toc_filep, HELP_PAGE_TOC, -1);
4372 write_help_file_footer(help_output_toc_filep, "Table of Contents");
4373 finish_help_file(help_output_toc_filep);
4374 }
4375
4376 /* Finishes help file for any standard section using the temp file buffer. */
4377
4378 static void
meta_finish_help_cc(HelpPage hpage,int hpageidx,HelpNode * helpnode)4379 meta_finish_help_cc(HelpPage hpage, int hpageidx, HelpNode *helpnode)
4380 {
4381 char namebuf [BUFSIZE];
4382 char numbuf [BUFSIZE];
4383 char filebnamebuf [BUFSIZE];
4384
4385 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4386 return;
4387 strncpy(filebnamebuf, hpagedefns[hpage].filebname, BUFSIZE);
4388 strncpy(namebuf, hpagedefns[hpage].name, BUFSIZE);
4389 if (-1 < hpageidx) {
4390 switch (hpage) {
4391 case HELP_PAGE_UTYPE:
4392 strncpy(namebuf, u_type_name(hpageidx), BUFSIZE);
4393 break;
4394 case HELP_PAGE_TTYPE:
4395 strncpy(namebuf, t_type_name(hpageidx), BUFSIZE);
4396 break;
4397 case HELP_PAGE_MTYPE:
4398 strncpy(namebuf, m_type_name(hpageidx), BUFSIZE);
4399 break;
4400 case HELP_PAGE_ATYPE:
4401 strncpy(namebuf, a_type_name(hpageidx), BUFSIZE);
4402 break;
4403 default: break;
4404 }
4405 sprintf(numbuf, "%d", hpageidx);
4406 strcat(filebnamebuf, numbuf);
4407 }
4408 flush_help_cc_tmp(helpnode);
4409 write_help_section_footer(help_output_tmp_filep, namebuf);
4410 write_help_file_navbar(help_output_tmp_filep, hpage, hpageidx);
4411 write_help_file_footer(help_output_tmp_filep, namebuf);
4412 finish_help_file(help_output_tmp_filep);
4413 }
4414
4415 /* Generic core for finishing an help system file. */
4416
4417 void
finish_help_file(FILE * hfilep)4418 finish_help_file(FILE *hfilep)
4419 {
4420 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4421 return;
4422 assert_error(hfilep, "Attempted to flush a NULL help file.");
4423 fflush(hfilep);
4424 fclose(hfilep);
4425 }
4426
4427 /* Writes header for help file. */
4428
4429 void
write_help_file_header(FILE * hfile,char * headerdata)4430 write_help_file_header(FILE *hfile, char *headerdata)
4431 {
4432 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4433 return;
4434 assert_error(hfile,
4435 "Attempted to write an header to a NULL help file");
4436 switch (help_output_mode) {
4437 case HELP_OUTPUT_PLAIN_TEXT:
4438 assert_error(headerdata,
4439 "Attempted to write an header with NULL header data");
4440 if (mainmodule)
4441 fprintf(hfile, "%s: %s\n\n", mainmodule->name, headerdata);
4442 else
4443 fprintf(hfile, "%s\n\n", headerdata);
4444 break;
4445 case HELP_OUTPUT_HTML:
4446 assert_error(headerdata,
4447 "Attempted to write an header with NULL header data");
4448 if (mainmodule)
4449 fprintf(hfile,
4450 "<HTML>\n<HEAD>\n<TITLE>%s: %s</TITLE>\n</HEAD>\n<BODY>\n",
4451 mainmodule->name, headerdata);
4452 else
4453 fprintf(hfile,
4454 "<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n</HEAD>\n<BODY>\n",
4455 headerdata);
4456 break;
4457 default: break;
4458 }
4459 }
4460
4461 /* Writes footer for help file. */
4462
4463 void
write_help_file_footer(FILE * hfile,char * footerdata)4464 write_help_file_footer(FILE *hfile, char *footerdata)
4465 {
4466 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4467 return;
4468 assert_error(hfile,
4469 "Attempted to write a footer to a NULL help file");
4470 switch (help_output_mode) {
4471 case HELP_OUTPUT_PLAIN_TEXT:
4472 fprintf(hfile, "\n\n%s\n", help_file_brand());
4473 break;
4474 case HELP_OUTPUT_HTML:
4475 fprintf(hfile, "<BR>\n<H6>%s</H6>\n</BODY>\n</HTML>\n",
4476 help_file_brand());
4477 break;
4478 default: break;
4479 }
4480 }
4481
4482 /* Returns a branding mark for help file. */
4483
4484 char *
help_file_brand(void)4485 help_file_brand(void)
4486 {
4487 /* TODO: Add date and time. */
4488 snprintf(tmpbuf, BUFSIZE - 1,
4489 "File produced by Xcscribe for Xconq version %s.",
4490 version_string() /*, copyright_string() */);
4491 return tmpbuf;
4492 }
4493
4494 /* Writes header for a help section. */
4495
4496 void
write_help_section_header(FILE * ccfile,char * headerdata)4497 write_help_section_header(FILE *ccfile, char *headerdata)
4498 {
4499 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4500 return;
4501 assert_error(ccfile,
4502 "Attempted to write an header to a NULL help CC file");
4503 switch (help_output_mode) {
4504 case HELP_OUTPUT_PLAIN_TEXT: break;
4505 case HELP_OUTPUT_HTML:
4506 assert_error(headerdata,
4507 "Attempted to write an header with NULL header data");
4508 fprintf(ccfile, "<CENTER><H1>%s</H1></CENTER><BR>\n", headerdata);
4509 break;
4510 default: break;
4511 }
4512 }
4513
4514 /* Writes footer for help section. */
4515
4516 void
write_help_section_footer(FILE * ccfilep,char * footerdata)4517 write_help_section_footer(FILE *ccfilep, char *footerdata)
4518 {
4519 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4520 return;
4521 assert_error(ccfilep,
4522 "Attempted to write a footer to a NULL help file");
4523 switch (help_output_mode) {
4524 case HELP_OUTPUT_PLAIN_TEXT: case HELP_OUTPUT_HTML: break;
4525 default: break;
4526 }
4527 }
4528
4529 /* Writes header for help subsection. */
4530
4531 void
write_help_subsection_header(FILE * ccfile,char * headerdata)4532 write_help_subsection_header(FILE *ccfile, char *headerdata)
4533 {
4534 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4535 return;
4536 assert_error(ccfile,
4537 "Attempted to write an header to a NULL help CC file");
4538 switch (help_output_mode) {
4539 case HELP_OUTPUT_PLAIN_TEXT:
4540 assert_error(headerdata,
4541 "Attempted to write an header with NULL header data");
4542 fprintf(ccfile, "%s:\n\n", headerdata);
4543 break;
4544 case HELP_OUTPUT_HTML:
4545 assert_error(headerdata,
4546 "Attempted to write an header with NULL header data");
4547 fprintf(ccfile, "<CENTER><H4>%s</H4></CENTER><BR>\n", headerdata);
4548 break;
4549 default: break;
4550 }
4551 }
4552
4553 /* Writes footer for help subsection. */
4554
4555 void
write_help_subsection_footer(FILE * ccfile,char * footerdata)4556 write_help_subsection_footer(FILE *ccfile, char *footerdata)
4557 {
4558 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4559 return;
4560 assert_error(ccfile,
4561 "Attempted to write a footer to a NULL help CC file");
4562 switch (help_output_mode) {
4563 case HELP_OUTPUT_PLAIN_TEXT: case HELP_OUTPUT_HTML: break;
4564 default: break;
4565 }
4566 }
4567
4568 /* Writes a break between subsections. */
4569
4570 void
write_help_subsection_break(FILE * ccfile)4571 write_help_subsection_break(FILE *ccfile)
4572 {
4573 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4574 return;
4575 assert_error(ccfile,
4576 "Attempted to write a footer to a NULL help CC file");
4577 switch (help_output_mode) {
4578 case HELP_OUTPUT_PLAIN_TEXT:
4579 fprintf(ccfile, "\n\n");
4580 break;
4581 case HELP_OUTPUT_HTML:
4582 fprintf(ccfile, "<BR><HR><BR>\n");
4583 break;
4584 default: break;
4585 }
4586 }
4587
4588 /* Returns help file's extension. */
4589
4590 char *
get_help_file_extension(void)4591 get_help_file_extension(void)
4592 {
4593 switch (help_output_mode) {
4594 case HELP_OUTPUT_PLAIN_TEXT:
4595 return "txt";
4596 case HELP_OUTPUT_HTML:
4597 return "html";
4598 default: break;
4599 }
4600 return "";
4601 }
4602
4603 /* Initialize the help pages. */
4604
4605 static void
init_help_pages(void)4606 init_help_pages(void)
4607 {
4608 char midxbname [BUFSIZE];
4609
4610 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4611 return;
4612 /* Fixup the master index file basename. */
4613 memset(midxbname, 0, BUFSIZE);
4614 switch (help_output_mode) {
4615 case HELP_OUTPUT_PLAIN_TEXT:
4616 #if (defined(UNIX) || defined(WIN32))
4617 make_pathname("..", "index", "", midxbname);
4618 #else
4619 make_pathname("::", "index", "", midxbname);
4620 #endif
4621 break;
4622 case HELP_OUTPUT_HTML:
4623 strcpy(midxbname, "../index");
4624 break;
4625 default: break;
4626 }
4627 hpagedefns[HELP_PAGE_MASTER_INDEX].filebname = copy_string(midxbname);
4628 /* Fixup next and previous help pages involving variable-sized help page
4629 clusters. */
4630 hpagedefns[HELP_PAGE_TTYPE].nextpage =
4631 ((nummtypes <= 0) ? ((numatypes <= 0) ? HELP_PAGE_LAST
4632 : HELP_PAGE_ATYPE)
4633 : HELP_PAGE_MTYPE);
4634 hpagedefns[HELP_PAGE_MTYPE].nextpage =
4635 ((numatypes <= 0) ? HELP_PAGE_LAST : HELP_PAGE_ATYPE);
4636 hpagedefns[HELP_PAGE_ATYPE].prevpage =
4637 ((nummtypes <= 0) ? HELP_PAGE_TTYPE : HELP_PAGE_MTYPE);
4638 hpagedefns[HELP_PAGE_LAST].prevpage =
4639 ((numatypes <= 0) ? ((nummtypes <= 0) ? HELP_PAGE_TTYPE
4640 : HELP_PAGE_MTYPE)
4641 : HELP_PAGE_ATYPE);
4642 }
4643
4644 /* Write a navbar for a help page. */
4645
4646 void
write_help_file_navbar(FILE * ccfilep,HelpPage hpage,int hpageidx)4647 write_help_file_navbar(FILE *ccfilep, HelpPage hpage, int hpageidx)
4648 {
4649 char *tocfilebname = "index";
4650 char *prevfilebname = NULL;
4651 char *nextfilebname = NULL;
4652 char *tocname = "Table of Contents";
4653 char *prevname = NULL;
4654 char *nextname = NULL;
4655 char *navfileext = NULL;
4656 int prevhpageidx = -1, nexthpageidx = -1;
4657 char prevnumbuf [BUFSIZE], nextnumbuf [BUFSIZE];
4658
4659 if (HELP_OUTPUT_CC_NONE == help_output_cc)
4660 return;
4661 assert_error(HELP_PAGE_MASTER_INDEX != hpage,
4662 "Attempted to write a navigation bar for an invalid help page.");
4663 assert_error(ccfilep, "Attempted to write to a NULL help file.");
4664 /* Set up the nav links. */
4665 switch (hpage) {
4666 case HELP_PAGE_NONE: return;
4667 default:
4668 tocfilebname = hpagedefns[hpagedefns[hpage].tocpage].filebname;
4669 prevfilebname = hpagedefns[hpagedefns[hpage].prevpage].filebname;
4670 nextfilebname = hpagedefns[hpagedefns[hpage].nextpage].filebname;
4671 tocname = hpagedefns[hpagedefns[hpage].tocpage].name;
4672 prevname = hpagedefns[hpagedefns[hpage].prevpage].name;
4673 nextname = hpagedefns[hpagedefns[hpage].nextpage].name;
4674 }
4675 /* In the case of utypes, ttypes, mtypes, etc..., the links may need to
4676 be edited. */
4677 switch (hpage) {
4678 case HELP_PAGE_WORLD:
4679 nexthpageidx = 0;
4680 break;
4681 case HELP_PAGE_UTYPE:
4682 if (hpageidx > 0) {
4683 prevfilebname = hpagedefns[hpage].filebname;
4684 prevname = hpagedefns[hpage].name;
4685 prevhpageidx = hpageidx - 1;
4686 }
4687 if (hpageidx < (numutypes - 1)) {
4688 nextfilebname = hpagedefns[hpage].filebname;
4689 nextname = hpagedefns[hpage].name;
4690 nexthpageidx = hpageidx + 1;
4691 }
4692 else
4693 nexthpageidx = 0;
4694 break;
4695 case HELP_PAGE_TTYPE:
4696 if (hpageidx > 0) {
4697 prevfilebname = hpagedefns[hpage].filebname;
4698 prevname = hpagedefns[hpage].name;
4699 prevhpageidx = hpageidx - 1;
4700 }
4701 if (hpageidx < (numttypes - 1)) {
4702 nextfilebname = hpagedefns[hpage].filebname;
4703 nextname = hpagedefns[hpage].name;
4704 nexthpageidx = hpageidx + 1;
4705 }
4706 else
4707 nexthpageidx = 0;
4708 break;
4709 case HELP_PAGE_MTYPE:
4710 if (hpageidx > 0) {
4711 prevfilebname = hpagedefns[hpage].filebname;
4712 prevname = hpagedefns[hpage].name;
4713 prevhpageidx = hpageidx - 1;
4714 }
4715 if (hpageidx < (nummtypes - 1)) {
4716 nextfilebname = hpagedefns[hpage].filebname;
4717 nextname = hpagedefns[hpage].name;
4718 nexthpageidx = hpageidx + 1;
4719 }
4720 else
4721 nexthpageidx = 0;
4722 break;
4723 case HELP_PAGE_ATYPE:
4724 if (hpageidx > 0) {
4725 prevfilebname = hpagedefns[hpage].filebname;
4726 prevname = hpagedefns[hpage].name;
4727 prevhpageidx = hpageidx - 1;
4728 }
4729 if (hpageidx < (numatypes - 1)) {
4730 nextfilebname = hpagedefns[hpage].filebname;
4731 nextname = hpagedefns[hpage].name;
4732 nexthpageidx = hpageidx + 1;
4733 }
4734 else
4735 nexthpageidx = 0;
4736 break;
4737 default: break;
4738 }
4739 /* In the case of utypes, ttypes, mtypes, etc..., even more editing may
4740 need to be done, since mtypes and atypes may not necessarily be
4741 defined for the game. */
4742 switch (hpage) {
4743 case HELP_PAGE_TTYPE:
4744 if (hpageidx == 0)
4745 prevhpageidx = numutypes - 1;
4746 if (hpageidx >= (numttypes - 1))
4747 nexthpageidx = ((nummtypes > 0) ? 0 : ((numatypes > 0) ? 0 : -1));
4748 break;
4749 case HELP_PAGE_MTYPE:
4750 if (hpageidx == 0)
4751 prevhpageidx = numttypes - 1;
4752 if (hpageidx >= (nummtypes - 1))
4753 nexthpageidx = ((numatypes > 0) ? 0 : -1);
4754 break;
4755 case HELP_PAGE_ATYPE:
4756 if (hpageidx == 0)
4757 prevhpageidx = ((nummtypes > 0) ? nummtypes - 1 : numttypes - 1);
4758 if (hpageidx >= (numatypes - 1))
4759 nexthpageidx = -1;
4760 break;
4761 case HELP_PAGE_LAST:
4762 prevhpageidx = ((numatypes > 0) ? numatypes - 1
4763 : ((nummtypes > 0) ? nummtypes - 1
4764 : numttypes - 1));
4765 default: break;
4766 }
4767 /* Get the numeric indices of the prev and next help files, if relevant. */
4768 /* <<The decimal point can be added to these buffers, if specifying a
4769 "%s" for an empty string causes problems.>> */
4770 memset(prevnumbuf, 0, BUFSIZE);
4771 memset(nextnumbuf, 0, BUFSIZE);
4772 if (-1 < prevhpageidx)
4773 sprintf(prevnumbuf, "%d", prevhpageidx);
4774 if (-1 < nexthpageidx)
4775 sprintf(nextnumbuf, "%d", nexthpageidx);
4776 /* Get the extension for all the help files. */
4777 navfileext = get_help_file_extension();
4778 /* Actually write the navbar. */
4779 fprintf(ccfilep, "%s", help_newline());
4780 switch (help_output_mode) {
4781 case HELP_OUTPUT_PLAIN_TEXT:
4782 if (tocfilebname && tocname)
4783 fprintf(ccfilep, "%s: %s.%s%s", tocname,
4784 tocfilebname, navfileext, help_newline());
4785 if (prevfilebname && prevname)
4786 fprintf(ccfilep, "Previous: %s%s.%s%s", prevfilebname,
4787 prevnumbuf, navfileext, help_newline());
4788 if (nextfilebname && nextname)
4789 fprintf(ccfilep, "Next: %s%s.%s%s", nextfilebname,
4790 nextnumbuf, navfileext, help_newline());
4791 break;
4792 case HELP_OUTPUT_HTML:
4793 /* fprintf(ccfilep, "<CENTER>"); */
4794 if (tocfilebname && tocname)
4795 fprintf(ccfilep, "Up: <A Href=\"%s.%s\">%s</A>%s", tocfilebname,
4796 navfileext, tocname, help_indent(2));
4797 if (prevfilebname && prevname)
4798 fprintf(ccfilep, "Previous: <A Href=\"%s%s.%s\">%s</A>%s",
4799 prevfilebname, prevnumbuf, navfileext, prevname,
4800 help_indent(2));
4801 if (nextfilebname && nextname)
4802 fprintf(ccfilep, "Next: <A Href=\"%s%s.%s\">%s</A>%s", nextfilebname,
4803 nextnumbuf, navfileext, nextname, help_indent(2));
4804 /* fprintf(ccfilep, "</CENTER>%s", help_newline()); */
4805 fprintf(ccfilep, "%s", help_newline());
4806 break;
4807 default: break;
4808 }
4809 fprintf(ccfilep, "%s", help_newline());
4810 fflush(ccfilep);
4811 }
4812