1 /* NetHack 3.6 tilemap.c $NHDT-Date: 1542501042 2018/11/18 00:30:42 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.35 $ */
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 #define Fprintf (void) fprintf
15 const char *FDECL(tilename, (int, int));
16 void NDECL(init_tilemap);
17 void FDECL(process_substitutions, (FILE *));
18 boolean FDECL(acceptable_tilename, (int, const char *, const char *));
20 #if defined(MICRO) || defined(WIN32)
22 #if !defined(MSDOS) && !defined(WIN32)
23 extern void FDECL(exit, (int));
27 #if defined(MSDOS) || defined(WIN32) || defined(X11_GRAPHICS)
28 #define STATUES_LOOK_LIKE_MONSTERS
33 #define OTH_GLYPH 3 /* fortunately unnecessary */
35 #define EXTRA_SCROLL_DESCR_COUNT ((SCR_BLANK_PAPER - SCR_STINKING_CLOUD) - 1)
37 /* note that the ifdefs here should be the opposite sense from monst.c/
42 int sequence, predecessor;
45 #ifndef CHARON /* not supported */
46 { MON_GLYPH, PM_HELL_HOUND, "Cerberus" },
48 /* commented out in monst.c at present */
49 { MON_GLYPH, PM_SHOCKING_SPHERE, "beholder" },
50 { MON_GLYPH, PM_BABY_SILVER_DRAGON, "baby shimmering dragon" },
51 { MON_GLYPH, PM_SILVER_DRAGON, "shimmering dragon" },
52 { MON_GLYPH, PM_JABBERWOCK, "vorpal jabberwock" },
53 { MON_GLYPH, PM_VAMPIRE_LORD, "vampire mage" },
54 #ifndef CHARON /* not supported yet */
55 { MON_GLYPH, PM_CROESUS, "Charon" },
58 { MON_GLYPH, PM_FAMINE, "mail daemon" },
60 /* commented out in monst.c at present */
61 { MON_GLYPH, PM_SHAMAN_KARNOV, "Earendil" },
62 { MON_GLYPH, PM_SHAMAN_KARNOV, "Elwing" },
63 /* commented out in monst.c at present */
64 { MON_GLYPH, PM_CHROMATIC_DRAGON, "Goblin King" },
65 { MON_GLYPH, PM_NEANDERTHAL, "High-elf" },
66 /* objects commented out in objects.c at present */
67 { OBJ_GLYPH, SILVER_DRAGON_SCALE_MAIL, "shimmering dragon scale mail" },
68 { OBJ_GLYPH, SILVER_DRAGON_SCALES, "shimmering dragon scales" },
69 /* allow slime mold to look like slice of pizza, since we
70 * don't know what a slime mold should look like when renamed anyway
73 { OBJ_GLYPH, SCR_STINKING_CLOUD + EXTRA_SCROLL_DESCR_COUNT,
80 * Some entries in glyph2tile[] should be substituted for on various levels.
81 * The tiles used for the substitute entries will follow the usual ones in
82 * other.til in the order given here, which should have every substitution
83 * for the same set of tiles grouped together. You will have to change
84 * more code in process_substitutions()/substitute_tiles() if the sets
85 * overlap in the future.
88 int first_glyph, last_glyph;
89 const char *sub_name; /* for explanations */
90 const char *level_test;
91 } substitutes[] = { { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
92 "mine walls", "In_mines(plev)" },
93 { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
94 "gehennom walls", "In_hell(plev)" },
95 { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
96 "knox walls", "Is_knox(plev)" },
97 { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
98 "sokoban walls", "In_sokoban(plev)" } };
103 * entry is the position of the tile within the monsters/objects/other set
109 int i, j, condnum, tilenum;
110 static char buf[BUFSZ];
112 /* Note: these initializers don't do anything except guarantee that
113 we're linked properly.
117 (void) def_char_to_objclass(']');
119 condnum = tilenum = 0;
121 for (i = 0; i < NUMMONS; i++) {
122 if (set == MON_GLYPH && tilenum == entry)
123 return mons[i].mname;
125 while (conditionals[condnum].sequence == MON_GLYPH
126 && conditionals[condnum].predecessor == i) {
127 if (set == MON_GLYPH && tilenum == entry)
128 return conditionals[condnum].name;
133 if (set == MON_GLYPH && tilenum == entry)
134 return "invisible monster";
136 tilenum = 0; /* set-relative number */
137 for (i = 0; i < NUM_OBJECTS; i++) {
138 /* prefer to give the description - that's all the tile's
139 * appearance should reveal */
140 if (set == OBJ_GLYPH && tilenum == entry) {
141 if (!obj_descr[i].oc_descr)
142 return obj_descr[i].oc_name;
143 if (!obj_descr[i].oc_name)
144 return obj_descr[i].oc_descr;
146 Sprintf(buf, "%s / %s", obj_descr[i].oc_descr,
147 obj_descr[i].oc_name);
152 while (conditionals[condnum].sequence == OBJ_GLYPH
153 && conditionals[condnum].predecessor == i) {
154 if (set == OBJ_GLYPH && tilenum == entry)
155 return conditionals[condnum].name;
161 tilenum = 0; /* set-relative number */
162 for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) {
163 if (set == OTH_GLYPH && tilenum == entry) {
164 if (*defsyms[i].explanation) {
165 return defsyms[i].explanation;
167 Sprintf(buf, "cmap %d", tilenum);
172 while (conditionals[condnum].sequence == OTH_GLYPH
173 && conditionals[condnum].predecessor == i) {
174 if (set == OTH_GLYPH && tilenum == entry)
175 return conditionals[condnum].name;
181 tilenum = MAXPCHARS - MAXEXPCHARS;
183 if (i < (MAXEXPCHARS * EXPL_MAX)) {
184 if (set == OTH_GLYPH) {
185 static const char *explosion_types[] = {
187 "dark", "noxious", "muddy", "wet", "magical", "fiery",
190 Sprintf(buf, "explosion %s %d", explosion_types[i / MAXEXPCHARS],
195 tilenum += (MAXEXPCHARS * EXPL_MAX);
198 if (i < (NUM_ZAP << 2)) {
199 if (set == OTH_GLYPH) {
200 Sprintf(buf, "zap %d %d", i / 4, i % 4);
204 tilenum += (NUM_ZAP << 2);
208 if (set == OTH_GLYPH) {
209 Sprintf(buf, "warning %d", i);
213 tilenum += WARNCOUNT;
215 for (i = 0; i < SIZE(substitutes); i++) {
217 if (j <= substitutes[i].last_glyph - substitutes[i].first_glyph) {
218 if (set == OTH_GLYPH) {
219 Sprintf(buf, "sub %s %d", substitutes[i].sub_name, j);
223 tilenum += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
226 Sprintf(buf, "unknown %d %d", set, entry);
232 #define TILE_FILE "tile.c"
235 #define SOURCE_TEMPLATE "NH:src/%s"
238 #define SOURCE_TEMPLATE ":src:%s"
240 #define SOURCE_TEMPLATE "../src/%s"
244 short tilemap[MAX_GLYPH];
246 #ifdef STATUES_LOOK_LIKE_MONSTERS
247 int lastmontile, lastobjtile, lastothtile, laststatuetile;
249 int lastmontile, lastobjtile, lastothtile;
252 /* Number of tiles for invisible monsters */
253 #define NUM_INVIS_TILES 1
256 * set up array to map glyph numbers to tile numbers
258 * assumes tiles are numbered sequentially through monsters/objects/other,
259 * with entries for all supported compilation options
261 * "other" contains cmap and zaps (the swallow sets are a repeated portion
262 * of cmap), as well as the "flash" glyphs for the new warning system
263 * introduced in 3.3.1.
268 int i, j, condnum, tilenum;
269 int corpsetile, swallowbase;
271 for (i = 0; i < MAX_GLYPH; i++) {
275 corpsetile = NUMMONS + NUM_INVIS_TILES + CORPSE;
276 swallowbase = NUMMONS + NUM_INVIS_TILES + NUM_OBJECTS + S_sw_tl;
278 /* add number compiled out */
279 for (i = 0; conditionals[i].sequence; i++) {
280 switch (conditionals[i].sequence) {
286 if (conditionals[i].predecessor < CORPSE)
291 if (conditionals[i].predecessor < S_sw_tl)
297 condnum = tilenum = 0;
298 for (i = 0; i < NUMMONS; i++) {
299 tilemap[GLYPH_MON_OFF + i] = tilenum;
300 tilemap[GLYPH_PET_OFF + i] = tilenum;
301 tilemap[GLYPH_DETECT_OFF + i] = tilenum;
302 tilemap[GLYPH_RIDDEN_OFF + i] = tilenum;
303 tilemap[GLYPH_BODY_OFF + i] = corpsetile;
304 j = GLYPH_SWALLOW_OFF + 8 * i;
305 tilemap[j] = swallowbase;
306 tilemap[j + 1] = swallowbase + 1;
307 tilemap[j + 2] = swallowbase + 2;
308 tilemap[j + 3] = swallowbase + 3;
309 tilemap[j + 4] = swallowbase + 4;
310 tilemap[j + 5] = swallowbase + 5;
311 tilemap[j + 6] = swallowbase + 6;
312 tilemap[j + 7] = swallowbase + 7;
314 while (conditionals[condnum].sequence == MON_GLYPH
315 && conditionals[condnum].predecessor == i) {
320 tilemap[GLYPH_INVISIBLE] = tilenum++;
321 lastmontile = tilenum - 1;
323 for (i = 0; i < NUM_OBJECTS; i++) {
324 tilemap[GLYPH_OBJ_OFF + i] = tilenum;
326 while (conditionals[condnum].sequence == OBJ_GLYPH
327 && conditionals[condnum].predecessor == i) {
332 lastobjtile = tilenum - 1;
334 for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) {
335 tilemap[GLYPH_CMAP_OFF + i] = tilenum;
337 while (conditionals[condnum].sequence == OTH_GLYPH
338 && conditionals[condnum].predecessor == i) {
344 for (i = 0; i < (MAXEXPCHARS * EXPL_MAX); i++) {
345 tilemap[GLYPH_EXPLODE_OFF + i] = tilenum;
347 while (conditionals[condnum].sequence == OTH_GLYPH
348 && conditionals[condnum].predecessor == (i + MAXPCHARS)) {
354 for (i = 0; i < NUM_ZAP << 2; i++) {
355 tilemap[GLYPH_ZAP_OFF + i] = tilenum;
357 while (conditionals[condnum].sequence == OTH_GLYPH
358 && conditionals[condnum].predecessor == (i + MAXEXPCHARS)) {
364 for (i = 0; i < WARNCOUNT; i++) {
365 tilemap[GLYPH_WARNING_OFF + i] = tilenum;
369 #ifndef STATUES_LOOK_LIKE_MONSTERS
370 /* statue patch: statues still use the same glyph as in vanilla */
372 for (i = 0; i < NUMMONS; i++) {
373 tilemap[GLYPH_STATUE_OFF + i] = tilemap[GLYPH_OBJ_OFF + STATUE];
377 lastothtile = tilenum - 1;
379 #ifdef STATUES_LOOK_LIKE_MONSTERS
380 /* skip over the substitutes to get to the grayscale statues */
381 for (i = 0; i < SIZE(substitutes); i++) {
382 tilenum += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
385 /* statue patch: statues look more like the monster */
386 condnum = 0; /* doing monsters again, so reset */
387 for (i = 0; i < NUMMONS; i++) {
388 tilemap[GLYPH_STATUE_OFF + i] = tilenum;
390 while (conditionals[condnum].sequence == MON_GLYPH
391 && conditionals[condnum].predecessor == i) {
396 laststatuetile = tilenum - 1;
400 const char *prolog[] = { "", "void", "substitute_tiles(plev)",
401 "d_level *plev;", "{", " int i;", "" };
403 const char *epilog[] = { " return;", "}" };
405 /* write out the substitutions in an easily-used form. */
407 process_substitutions(ofp)
410 static const char Dent[] = " "; /* 4 space indentation */
411 int i, j, k, span, start;
415 j = 0; /* unnecessary */
417 for (i = 0; i < SIZE(substitutes); i++) {
418 if (i == 0 || substitutes[i].first_glyph != substitutes[j].first_glyph
419 || substitutes[i].last_glyph != substitutes[j].last_glyph) {
422 Fprintf(ofp, "short std_tiles%d[] = { ", span);
423 for (k = substitutes[i].first_glyph;
424 k < substitutes[i].last_glyph; k++)
425 Fprintf(ofp, "%d, ", tilemap[k]);
426 Fprintf(ofp, "%d };\n", tilemap[substitutes[i].last_glyph]);
430 for (i = 0; i < SIZE(prolog); i++) {
431 Fprintf(ofp, "%s\n", prolog[i]);
435 start = lastothtile + 1;
436 for (i = 0; i < SIZE(substitutes); i++) {
437 if (i == 0 || substitutes[i].first_glyph != substitutes[j].first_glyph
438 || substitutes[i].last_glyph != substitutes[j].last_glyph) {
439 if (i != 0) { /* finish previous span */
440 Fprintf(ofp, "%s} else {\n", Dent);
441 Fprintf(ofp, "%s%sfor (i = %d; i <= %d; i++)\n", Dent, Dent,
442 substitutes[j].first_glyph, substitutes[j].last_glyph);
443 Fprintf(ofp, "%s%s%sglyph2tile[i] = std_tiles%d[i - %d];\n",
444 Dent, Dent, Dent, span, substitutes[j].first_glyph);
445 Fprintf(ofp, "%s}\n\n", Dent);
450 Fprintf(ofp, "%s%sif (%s) {\n", Dent, (i == j) ? "" : "} else ",
451 substitutes[i].level_test);
452 Fprintf(ofp, "%s%sfor (i = %d; i <= %d; i++)\n", Dent, Dent,
453 substitutes[i].first_glyph, substitutes[i].last_glyph);
454 Fprintf(ofp, "%s%s%sglyph2tile[i] = %d + i - %d;\n",
455 Dent, Dent, Dent, start, substitutes[i].first_glyph);
456 start += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
458 /* finish last span */
459 Fprintf(ofp, "%s} else {\n", Dent);
460 Fprintf(ofp, "%s%sfor (i = %d; i <= %d; i++)\n", Dent, Dent,
461 substitutes[j].first_glyph, substitutes[j].last_glyph);
462 Fprintf(ofp, "%s%s%sglyph2tile[i] = std_tiles%d[i - %d];\n",
463 Dent, Dent, Dent, span, substitutes[j].first_glyph);
464 Fprintf(ofp, "%s}\n", Dent);
466 for (i = 0; i < SIZE(epilog); i++) {
467 Fprintf(ofp, "%s\n", epilog[i]);
470 lastothtile = start - 1;
471 #ifdef STATUES_LOOK_LIKE_MONSTERS
472 start = laststatuetile + 1;
474 Fprintf(ofp, "\nint total_tiles_used = %d;\n", start);
487 * create the source file, "tile.c"
489 Sprintf(filename, SOURCE_TEMPLATE, TILE_FILE);
490 if (!(ofp = fopen(filename, "w"))) {
495 "/* This file is automatically generated. Do not edit. */\n");
496 Fprintf(ofp, "\n#include \"hack.h\"\n");
497 Fprintf(ofp, "\nshort glyph2tile[MAX_GLYPH] = {\n");
499 for (i = 0; i < MAX_GLYPH; i++) {
500 Fprintf(ofp, " %4d,", tilemap[i]);
501 if ((i % 12) == 11 || i == MAX_GLYPH - 1)
504 Fprintf(ofp, "};\n");
506 process_substitutions(ofp);
508 Fprintf(ofp, "\n#define MAXMONTILE %d\n", lastmontile);
509 Fprintf(ofp, "#define MAXOBJTILE %d\n", lastobjtile);
510 Fprintf(ofp, "#define MAXOTHTILE %d\n", lastothtile);
511 #ifdef STATUES_LOOK_LIKE_MONSTERS
512 Fprintf(ofp, "/* #define MAXSTATUETILE %d */\n", laststatuetile);
514 Fprintf(ofp, "\n/*tile.c*/\n");
522 #endif /* TILETEXT */
526 const char *betterlabel;
527 const char *expectedlabel;
529 {S_stone, "dark part of a room", "dark part of a room"},
530 {S_vwall, "vertical wall", "wall"},
531 {S_hwall, "horizontal wall", "wall"},
532 {S_tlcorn, "top left corner wall", "wall"},
533 {S_trcorn, "top right corner wall", "wall"},
534 {S_blcorn, "bottom left corner wall", "wall"},
535 {S_brcorn, "bottom right corner wall", "wall"},
536 {S_crwall, "cross wall", "wall"},
537 {S_tuwall, "tuwall", "wall"},
538 {S_tdwall, "tdwall", "wall"},
539 {S_tlwall, "tlwall", "wall"},
540 {S_trwall, "trwall", "wall"},
541 {S_ndoor, "no door", "doorway"},
542 {S_vodoor, "vertical open door", "open door"},
543 {S_hodoor, "horizontal open door", "open door"},
544 {S_vcdoor, "vertical closed door", "closed door"},
545 {S_hcdoor, "horizontal closed door", "closed door"},
546 {S_bars, "iron bars", "iron bars"},
547 {S_tree, "tree", "tree"},
548 {S_room, "room", "floor of a room"},
549 {S_darkroom, "darkroom", "dark part of a room"},
550 {S_corr, "corridor", "corridor"},
551 {S_litcorr, "lit corridor", "lit corridor"},
552 {S_upstair, "up stairs", "staircase up"},
553 {S_dnstair, "down stairs", "staircase down"},
554 {S_upladder, "up ladder", "ladder up"},
555 {S_dnladder, "down ladder", "ladder down"},
556 {S_altar, "altar", "altar"},
557 {S_grave, "grave", "grave"},
558 {S_throne, "throne", "opulent throne"},
559 {S_sink, "sink", "sink"},
560 {S_fountain, "fountain", "fountain"},
561 {S_pool, "pool", "water"},
562 {S_ice, "ice", "ice"},
563 {S_lava, "lava", "molten lava"},
564 {S_vodbridge, "vertical open drawbridge", "lowered drawbridge"},
565 {S_hodbridge, "horizontal open drawbridge", "lowered drawbridge"},
566 {S_vcdbridge, "vertical closed drawbridge", "raised drawbridge"},
567 {S_hcdbridge, "horizontal closed drawbridge", "raised drawbridge"},
568 {S_air, "air", "air"},
569 {S_cloud, "cloud", "cloud"},
570 {S_water, "water", "water"},
571 {S_arrow_trap, "arrow trap", "arrow trap"},
572 {S_dart_trap, "dart trap", "dart trap"},
573 {S_falling_rock_trap, "falling rock trap", "falling rock trap"},
574 {S_squeaky_board, "squeaky board", "squeaky board"},
575 {S_bear_trap, "bear trap", "bear trap"},
576 {S_land_mine, "land mine", "land mine"},
577 {S_rolling_boulder_trap, "rolling boulder trap", "rolling boulder trap"},
578 {S_sleeping_gas_trap, "sleeping gas trap", "sleeping gas trap"},
579 {S_rust_trap, "rust trap", "rust trap"},
580 {S_fire_trap, "fire trap", "fire trap"},
581 {S_pit, "pit", "pit"},
582 {S_spiked_pit, "spiked pit", "spiked pit"},
583 {S_hole, "hole", "hole"},
584 {S_trap_door, "trap door", "trap door"},
585 {S_teleportation_trap, "teleportation trap", "teleportation trap"},
586 {S_level_teleporter, "level teleporter", "level teleporter"},
587 {S_magic_portal, "magic portal", "magic portal"},
588 {S_web, "web", "web"},
589 {S_statue_trap, "statue trap", "statue trap"},
590 {S_magic_trap, "magic trap", "magic trap"},
591 {S_anti_magic_trap, "anti magic trap", "anti-magic field"},
592 {S_polymorph_trap, "polymorph trap", "polymorph trap"},
593 {S_vibrating_square, "vibrating square", "vibrating square"},
594 {S_vbeam, "vertical beam", "cmap 65"},
595 {S_hbeam, "horizontal beam", "cmap 66"},
596 {S_lslant, "left slant beam", "cmap 67"},
597 {S_rslant, "right slant beam", "cmap 68"},
598 {S_digbeam, "dig beam", "cmap 69"},
599 {S_flashbeam, "flash beam", "cmap 70"},
600 {S_boomleft, "boom left", "cmap 71"},
601 {S_boomright, "boom right", "cmap 72"},
602 {S_ss1, "shield1", "cmap 73"},
603 {S_ss2, "shield2", "cmap 74"},
604 {S_ss3, "shield3", "cmap 75"},
605 {S_ss4, "shield4", "cmap 76"},
606 {S_poisoncloud, "poison cloud", "poison cloud"},
607 {S_goodpos, "valid position", "valid position"},
608 {S_sw_tl, "swallow top left", "cmap 79"},
609 {S_sw_tc, "swallow top center", "cmap 80"},
610 {S_sw_tr, "swallow top right", "cmap 81"},
611 {S_sw_ml, "swallow middle left", "cmap 82"},
612 {S_sw_mr, "swallow middle right", "cmap 83"},
613 {S_sw_bl, "swallow bottom left ", "cmap 84"},
614 {S_sw_bc, "swallow bottom center", "cmap 85"},
615 {S_sw_br, "swallow bottom right", "cmap 86"},
616 {S_explode1, "explosion top left", "explosion dark 0"},
617 {S_explode2, "explosion top centre", "explosion dark 1"},
618 {S_explode3, "explosion top right", "explosion dark 2"},
619 {S_explode4, "explosion middle left", "explosion dark 3"},
620 {S_explode5, "explosion middle center", "explosion dark 4"},
621 {S_explode6, "explosion middle right", "explosion dark 5"},
622 {S_explode7, "explosion bottom left", "explosion dark 6"},
623 {S_explode8, "explosion bottom center", "explosion dark 7"},
624 {S_explode9, "explosion bottom right", "explosion dark 8"},
628 acceptable_tilename(idx, encountered, expected)
630 const char *encountered, *expected;
632 if (idx >= 0 && idx < SIZE(altlabels)) {
633 if (!strcmp(altlabels[idx].expectedlabel, expected)) {
634 if (!strcmp(altlabels[idx].betterlabel, encountered))