1 /* NetHack 3.6 tilemap.c $NHDT-Date: 1524689272 2018/04/25 20:47:52 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.33 $ */
2 /* Copyright (c) 2016 by Michael Allison */
3 /* NetHack may be freely redistributed. See license for details. */
6 * This source file is compiled twice:
7 * once without TILETEXT defined to make tilemap.{o,obj},
8 * then again with it defined to produce tiletxt.{o,obj}.
13 const char *FDECL(tilename, (int, int));
14 void NDECL(init_tilemap);
15 void FDECL(process_substitutions, (FILE *));
16 boolean FDECL(acceptable_tilename, (int, const char *, const char *));
18 #if defined(MICRO) || defined(WIN32)
20 #if !defined(MSDOS) && !defined(WIN32)
21 extern void FDECL(exit, (int));
25 #if defined(MSDOS) || defined(WIN32) || defined(X11_GRAPHICS)
26 #define STATUES_LOOK_LIKE_MONSTERS
31 #define OTH_GLYPH 3 /* fortunately unnecessary */
33 #define EXTRA_SCROLL_DESCR_COUNT ((SCR_BLANK_PAPER - SCR_STINKING_CLOUD) - 1)
35 /* note that the ifdefs here should be the opposite sense from monst.c/
40 int sequence, predecessor;
43 #ifndef CHARON /* not supported yet */
44 { MON_GLYPH, PM_HELL_HOUND, "Cerberus" },
46 /* commented out in monst.c at present */
47 { MON_GLYPH, PM_SHOCKING_SPHERE, "beholder" },
48 { MON_GLYPH, PM_BABY_SILVER_DRAGON, "baby shimmering dragon" },
49 { MON_GLYPH, PM_SILVER_DRAGON, "shimmering dragon" },
50 { MON_GLYPH, PM_JABBERWOCK, "vorpal jabberwock" },
51 { MON_GLYPH, PM_VAMPIRE_LORD, "vampire mage" },
52 #ifndef CHARON /* not supported yet */
53 { MON_GLYPH, PM_CROESUS, "Charon" },
56 { MON_GLYPH, PM_FAMINE, "mail daemon" },
58 /* commented out in monst.c at present */
59 { MON_GLYPH, PM_SHAMAN_KARNOV, "Earendil" },
60 { MON_GLYPH, PM_SHAMAN_KARNOV, "Elwing" },
61 /* commented out in monst.c at present */
62 { MON_GLYPH, PM_CHROMATIC_DRAGON, "Goblin King" },
63 { MON_GLYPH, PM_NEANDERTHAL, "High-elf" },
64 /* objects commented out in objects.c at present */
65 { OBJ_GLYPH, SILVER_DRAGON_SCALE_MAIL, "shimmering dragon scale mail" },
66 { OBJ_GLYPH, SILVER_DRAGON_SCALES, "shimmering dragon scales" },
67 /* allow slime mold to look like slice of pizza, since we
68 * don't know what a slime mold should look like when renamed anyway
71 { OBJ_GLYPH, SCR_STINKING_CLOUD + EXTRA_SCROLL_DESCR_COUNT, "stamped / mail" },
77 * Some entries in glyph2tile[] should be substituted for on various levels.
78 * The tiles used for the substitute entries will follow the usual ones in
79 * other.til in the order given here, which should have every substitution
80 * for the same set of tiles grouped together. You will have to change
81 * more code in process_substitutions()/substitute_tiles() if the sets
82 * overlap in the future.
85 int first_glyph, last_glyph;
86 const char *sub_name; /* for explanations */
87 const char *level_test;
88 } substitutes[] = { { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
89 "mine walls", "In_mines(plev)" },
90 { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
91 "gehennom walls", "In_hell(plev)" },
92 { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
93 "knox walls", "Is_knox(plev)" },
94 { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
95 "sokoban walls", "In_sokoban(plev)" } };
100 * entry is the position of the tile within the monsters/objects/other set
106 int i, j, condnum, tilenum;
107 static char buf[BUFSZ];
109 /* Note: these initializers don't do anything except guarantee that
110 we're linked properly.
114 (void) def_char_to_objclass(']');
116 condnum = tilenum = 0;
118 for (i = 0; i < NUMMONS; i++) {
119 if (set == MON_GLYPH && tilenum == entry)
120 return mons[i].mname;
122 while (conditionals[condnum].sequence == MON_GLYPH
123 && conditionals[condnum].predecessor == i) {
124 if (set == MON_GLYPH && tilenum == entry)
125 return conditionals[condnum].name;
130 if (set == MON_GLYPH && tilenum == entry)
131 return "invisible monster";
133 tilenum = 0; /* set-relative number */
134 for (i = 0; i < NUM_OBJECTS; i++) {
135 /* prefer to give the description - that's all the tile's
136 * appearance should reveal */
137 if (set == OBJ_GLYPH && tilenum == entry) {
138 if (!obj_descr[i].oc_descr)
139 return obj_descr[i].oc_name;
140 if (!obj_descr[i].oc_name)
141 return obj_descr[i].oc_descr;
143 Sprintf(buf, "%s / %s", obj_descr[i].oc_descr,
144 obj_descr[i].oc_name);
149 while (conditionals[condnum].sequence == OBJ_GLYPH
150 && conditionals[condnum].predecessor == i) {
151 if (set == OBJ_GLYPH && tilenum == entry)
152 return conditionals[condnum].name;
158 tilenum = 0; /* set-relative number */
159 for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) {
160 if (set == OTH_GLYPH && tilenum == entry) {
161 if (*defsyms[i].explanation) {
162 return defsyms[i].explanation;
164 Sprintf(buf, "cmap %d", tilenum);
169 while (conditionals[condnum].sequence == OTH_GLYPH
170 && conditionals[condnum].predecessor == i) {
171 if (set == OTH_GLYPH && tilenum == entry)
172 return conditionals[condnum].name;
178 tilenum = MAXPCHARS - MAXEXPCHARS;
180 if (i < (MAXEXPCHARS * EXPL_MAX)) {
181 if (set == OTH_GLYPH) {
182 static const char *explosion_types[] = {
184 "dark", "noxious", "muddy", "wet", "magical", "fiery",
187 Sprintf(buf, "explosion %s %d", explosion_types[i / MAXEXPCHARS],
192 tilenum += (MAXEXPCHARS * EXPL_MAX);
195 if (i < (NUM_ZAP << 2)) {
196 if (set == OTH_GLYPH) {
197 Sprintf(buf, "zap %d %d", i / 4, i % 4);
201 tilenum += (NUM_ZAP << 2);
205 if (set == OTH_GLYPH) {
206 Sprintf(buf, "warning %d", i);
210 tilenum += WARNCOUNT;
212 for (i = 0; i < SIZE(substitutes); i++) {
214 if (j <= substitutes[i].last_glyph - substitutes[i].first_glyph) {
215 if (set == OTH_GLYPH) {
216 Sprintf(buf, "sub %s %d", substitutes[i].sub_name, j);
220 tilenum += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
223 Sprintf(buf, "unknown %d %d", set, entry);
229 #define TILE_FILE "tile.c"
232 #define SOURCE_TEMPLATE "NH:src/%s"
235 #define SOURCE_TEMPLATE ":src:%s"
237 #define SOURCE_TEMPLATE "../src/%s"
241 short tilemap[MAX_GLYPH];
243 #ifdef STATUES_LOOK_LIKE_MONSTERS
244 int lastmontile, lastobjtile, lastothtile, laststatuetile;
246 int lastmontile, lastobjtile, lastothtile;
249 /* Number of tiles for invisible monsters */
250 #define NUM_INVIS_TILES 1
253 * set up array to map glyph numbers to tile numbers
255 * assumes tiles are numbered sequentially through monsters/objects/other,
256 * with entries for all supported compilation options
258 * "other" contains cmap and zaps (the swallow sets are a repeated portion
259 * of cmap), as well as the "flash" glyphs for the new warning system
260 * introduced in 3.3.1.
265 int i, j, condnum, tilenum;
266 int corpsetile, swallowbase;
268 for (i = 0; i < MAX_GLYPH; i++) {
272 corpsetile = NUMMONS + NUM_INVIS_TILES + CORPSE;
273 swallowbase = NUMMONS + NUM_INVIS_TILES + NUM_OBJECTS + S_sw_tl;
275 /* add number compiled out */
276 for (i = 0; conditionals[i].sequence; i++) {
277 switch (conditionals[i].sequence) {
283 if (conditionals[i].predecessor < CORPSE)
288 if (conditionals[i].predecessor < S_sw_tl)
294 condnum = tilenum = 0;
295 for (i = 0; i < NUMMONS; i++) {
296 tilemap[GLYPH_MON_OFF + i] = tilenum;
297 tilemap[GLYPH_PET_OFF + i] = tilenum;
298 tilemap[GLYPH_DETECT_OFF + i] = tilenum;
299 tilemap[GLYPH_RIDDEN_OFF + i] = tilenum;
300 tilemap[GLYPH_BODY_OFF + i] = corpsetile;
301 j = GLYPH_SWALLOW_OFF + 8 * i;
302 tilemap[j] = swallowbase;
303 tilemap[j + 1] = swallowbase + 1;
304 tilemap[j + 2] = swallowbase + 2;
305 tilemap[j + 3] = swallowbase + 3;
306 tilemap[j + 4] = swallowbase + 4;
307 tilemap[j + 5] = swallowbase + 5;
308 tilemap[j + 6] = swallowbase + 6;
309 tilemap[j + 7] = swallowbase + 7;
311 while (conditionals[condnum].sequence == MON_GLYPH
312 && conditionals[condnum].predecessor == i) {
317 tilemap[GLYPH_INVISIBLE] = tilenum++;
318 lastmontile = tilenum - 1;
320 for (i = 0; i < NUM_OBJECTS; i++) {
321 tilemap[GLYPH_OBJ_OFF + i] = tilenum;
323 while (conditionals[condnum].sequence == OBJ_GLYPH
324 && conditionals[condnum].predecessor == i) {
329 lastobjtile = tilenum - 1;
331 for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) {
332 tilemap[GLYPH_CMAP_OFF + i] = tilenum;
334 while (conditionals[condnum].sequence == OTH_GLYPH
335 && conditionals[condnum].predecessor == i) {
341 for (i = 0; i < (MAXEXPCHARS * EXPL_MAX); i++) {
342 tilemap[GLYPH_EXPLODE_OFF + i] = tilenum;
344 while (conditionals[condnum].sequence == OTH_GLYPH
345 && conditionals[condnum].predecessor == (i + MAXPCHARS)) {
351 for (i = 0; i < NUM_ZAP << 2; i++) {
352 tilemap[GLYPH_ZAP_OFF + i] = tilenum;
354 while (conditionals[condnum].sequence == OTH_GLYPH
355 && conditionals[condnum].predecessor == (i + MAXEXPCHARS)) {
361 for (i = 0; i < WARNCOUNT; i++) {
362 tilemap[GLYPH_WARNING_OFF + i] = tilenum;
366 #ifndef STATUES_LOOK_LIKE_MONSTERS
367 /* statue patch: statues still use the same glyph as in vanilla */
369 for (i = 0; i < NUMMONS; i++) {
370 tilemap[GLYPH_STATUE_OFF + i] = tilemap[GLYPH_OBJ_OFF + STATUE];
374 lastothtile = tilenum - 1;
376 #ifdef STATUES_LOOK_LIKE_MONSTERS
377 /* skip over the substitutes to get to the grayscale statues */
378 for (i = 0; i < SIZE(substitutes); i++) {
379 tilenum += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
382 /* statue patch: statues look more like the monster */
383 condnum = 0; /* doing monsters again, so reset */
384 for (i = 0; i < NUMMONS; i++) {
385 tilemap[GLYPH_STATUE_OFF + i] = tilenum;
387 while (conditionals[condnum].sequence == MON_GLYPH
388 && conditionals[condnum].predecessor == i) {
393 laststatuetile = tilenum - 1;
397 const char *prolog[] = { "", "", "void", "substitute_tiles(plev)",
398 "d_level *plev;", "{", "\tint i;", "" };
400 const char *epilog[] = { "}" };
402 /* write out the substitutions in an easily-used form. */
404 process_substitutions(ofp)
407 int i, j, k, span, start;
409 fprintf(ofp, "\n\n");
411 j = 0; /* unnecessary */
413 for (i = 0; i < SIZE(substitutes); i++) {
414 if (i == 0 || substitutes[i].first_glyph != substitutes[j].first_glyph
415 || substitutes[i].last_glyph != substitutes[j].last_glyph) {
418 fprintf(ofp, "short std_tiles%d[] = { ", span);
419 for (k = substitutes[i].first_glyph;
420 k < substitutes[i].last_glyph; k++)
421 fprintf(ofp, "%d, ", tilemap[k]);
422 fprintf(ofp, "%d };\n", tilemap[substitutes[i].last_glyph]);
426 for (i = 0; i < SIZE(prolog); i++) {
427 fprintf(ofp, "%s\n", prolog[i]);
431 start = lastothtile + 1;
432 for (i = 0; i < SIZE(substitutes); i++) {
433 if (i == 0 || substitutes[i].first_glyph != substitutes[j].first_glyph
434 || substitutes[i].last_glyph != substitutes[j].last_glyph) {
435 if (i != 0) { /* finish previous span */
436 fprintf(ofp, "\t} else {\n");
437 fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
438 substitutes[j].first_glyph,
439 substitutes[j].last_glyph);
440 fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n",
441 span, substitutes[j].first_glyph);
442 fprintf(ofp, "\t}\n\n");
448 fprintf(ofp, "\t} else ");
449 fprintf(ofp, "\tif (%s) {\n", substitutes[i].level_test);
450 fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
451 substitutes[i].first_glyph, substitutes[i].last_glyph);
452 fprintf(ofp, "\t\t\tglyph2tile[i] = %d + i - %d;\n", start,
453 substitutes[i].first_glyph);
454 start += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
456 /* finish last span */
457 fprintf(ofp, "\t} else {\n");
458 fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
459 substitutes[j].first_glyph, substitutes[j].last_glyph);
460 fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n", span,
461 substitutes[j].first_glyph);
462 fprintf(ofp, "\t}\n\n");
464 for (i = 0; i < SIZE(epilog); i++) {
465 fprintf(ofp, "%s\n", epilog[i]);
468 lastothtile = start - 1;
469 #ifdef STATUES_LOOK_LIKE_MONSTERS
470 start = laststatuetile + 1;
472 fprintf(ofp, "\nint total_tiles_used = %d;\n", start);
485 * create the source file, "tile.c"
487 Sprintf(filename, SOURCE_TEMPLATE, TILE_FILE);
488 if (!(ofp = fopen(filename, "w"))) {
493 "/* This file is automatically generated. Do not edit. */\n");
494 fprintf(ofp, "\n#include \"hack.h\"\n\n");
495 fprintf(ofp, "short glyph2tile[MAX_GLYPH] = {\n");
497 for (i = 0; i < MAX_GLYPH; i++) {
498 fprintf(ofp, "%2d,%c", tilemap[i], (i % 12) ? ' ' : '\n');
500 fprintf(ofp, "%s};\n", (i % 12) ? "\n" : "");
502 process_substitutions(ofp);
504 fprintf(ofp, "\n#define MAXMONTILE %d\n", lastmontile);
505 fprintf(ofp, "#define MAXOBJTILE %d\n", lastobjtile);
506 fprintf(ofp, "#define MAXOTHTILE %d\n", lastothtile);
507 #ifdef STATUES_LOOK_LIKE_MONSTERS
508 fprintf(ofp, "/* #define MAXSTATUETILE %d */\n", laststatuetile);
510 fprintf(ofp, "\n/*tile.c*/\n");
518 #endif /* TILETEXT */
522 const char *betterlabel;
523 const char *expectedlabel;
525 {S_stone, "dark part of a room", "dark part of a room"},
526 {S_vwall, "vertical wall", "wall"},
527 {S_hwall, "horizontal wall", "wall"},
528 {S_tlcorn, "top left corner wall", "wall"},
529 {S_trcorn, "top right corner wall", "wall"},
530 {S_blcorn, "bottom left corner wall", "wall"},
531 {S_brcorn, "bottom right corner wall", "wall"},
532 {S_crwall, "cross wall", "wall"},
533 {S_tuwall, "tuwall", "wall"},
534 {S_tdwall, "tdwall", "wall"},
535 {S_tlwall, "tlwall", "wall"},
536 {S_trwall, "trwall", "wall"},
537 {S_ndoor, "no door", "doorway"},
538 {S_vodoor, "vertical open door", "open door"},
539 {S_hodoor, "horizontal open door", "open door"},
540 {S_vcdoor, "vertical closed door", "closed door"},
541 {S_hcdoor, "horizontal closed door", "closed door"},
542 {S_bars, "iron bars", "iron bars"},
543 {S_tree, "tree", "tree"},
544 {S_room, "room", "floor of a room"},
545 {S_darkroom, "darkroom", "dark part of a room"},
546 {S_corr, "corridor", "corridor"},
547 {S_litcorr, "lit corridor", "lit corridor"},
548 {S_upstair, "up stairs", "staircase up"},
549 {S_dnstair, "down stairs", "staircase down"},
550 {S_upladder, "up ladder", "ladder up"},
551 {S_dnladder, "down ladder", "ladder down"},
552 {S_altar, "altar", "altar"},
553 {S_grave, "grave", "grave"},
554 {S_throne, "throne", "opulent throne"},
555 {S_sink, "sink", "sink"},
556 {S_fountain, "fountain", "fountain"},
557 {S_pool, "pool", "water"},
558 {S_ice, "ice", "ice"},
559 {S_lava, "lava", "molten lava"},
560 {S_vodbridge, "vertical open drawbridge", "lowered drawbridge"},
561 {S_hodbridge, "horizontal open drawbridge", "lowered drawbridge"},
562 {S_vcdbridge, "vertical closed drawbridge", "raised drawbridge"},
563 {S_hcdbridge, "horizontal closed drawbridge", "raised drawbridge"},
564 {S_air, "air", "air"},
565 {S_cloud, "cloud", "cloud"},
566 {S_water, "water", "water"},
567 {S_arrow_trap, "arrow trap", "arrow trap"},
568 {S_dart_trap, "dart trap", "dart trap"},
569 {S_falling_rock_trap, "falling rock trap", "falling rock trap"},
570 {S_squeaky_board, "squeaky board", "squeaky board"},
571 {S_bear_trap, "bear trap", "bear trap"},
572 {S_land_mine, "land mine", "land mine"},
573 {S_rolling_boulder_trap, "rolling boulder trap", "rolling boulder trap"},
574 {S_sleeping_gas_trap, "sleeping gas trap", "sleeping gas trap"},
575 {S_rust_trap, "rust trap", "rust trap"},
576 {S_fire_trap, "fire trap", "fire trap"},
577 {S_pit, "pit", "pit"},
578 {S_spiked_pit, "spiked pit", "spiked pit"},
579 {S_hole, "hole", "hole"},
580 {S_trap_door, "trap door", "trap door"},
581 {S_teleportation_trap, "teleportation trap", "teleportation trap"},
582 {S_level_teleporter, "level teleporter", "level teleporter"},
583 {S_magic_portal, "magic portal", "magic portal"},
584 {S_web, "web", "web"},
585 {S_statue_trap, "statue trap", "statue trap"},
586 {S_magic_trap, "magic trap", "magic trap"},
587 {S_anti_magic_trap, "anti magic trap", "anti-magic field"},
588 {S_polymorph_trap, "polymorph trap", "polymorph trap"},
589 {S_vibrating_square, "vibrating square", "vibrating square"},
590 {S_vbeam, "vertical beam", "cmap 65"},
591 {S_hbeam, "horizontal beam", "cmap 66"},
592 {S_lslant, "left slant beam", "cmap 67"},
593 {S_rslant, "right slant beam", "cmap 68"},
594 {S_digbeam, "dig beam", "cmap 69"},
595 {S_flashbeam, "flash beam", "cmap 70"},
596 {S_boomleft, "boom left", "cmap 71"},
597 {S_boomright, "boom right", "cmap 72"},
598 {S_ss1, "shield1", "cmap 73"},
599 {S_ss2, "shield2", "cmap 74"},
600 {S_ss3, "shield3", "cmap 75"},
601 {S_ss4, "shield4", "cmap 76"},
602 {S_poisoncloud, "poison cloud", "poison cloud"},
603 {S_goodpos, "valid position", "valid position"},
604 {S_sw_tl, "swallow top left", "cmap 79"},
605 {S_sw_tc, "swallow top center", "cmap 80"},
606 {S_sw_tr, "swallow top right", "cmap 81"},
607 {S_sw_ml, "swallow middle left", "cmap 82"},
608 {S_sw_mr, "swallow middle right", "cmap 83"},
609 {S_sw_bl, "swallow bottom left ", "cmap 84"},
610 {S_sw_bc, "swallow bottom center", "cmap 85"},
611 {S_sw_br, "swallow bottom right", "cmap 86"},
612 {S_explode1, "explosion top left", "explosion dark 0"},
613 {S_explode2, "explosion top centre", "explosion dark 1"},
614 {S_explode3, "explosion top right", "explosion dark 2"},
615 {S_explode4, "explosion middle left", "explosion dark 3"},
616 {S_explode5, "explosion middle center", "explosion dark 4"},
617 {S_explode6, "explosion middle right", "explosion dark 5"},
618 {S_explode7, "explosion bottom left", "explosion dark 6"},
619 {S_explode8, "explosion bottom center", "explosion dark 7"},
620 {S_explode9, "explosion bottom right", "explosion dark 8"},
624 acceptable_tilename(idx, encountered, expected)
626 const char *encountered, *expected;
628 if (idx >= 0 && idx < SIZE(altlabels)) {
629 if (!strcmp(altlabels[idx].expectedlabel, expected)) {
630 if (!strcmp(altlabels[idx].betterlabel, encountered))