1 | /* SCCS Id: @(#)tilemap.c 3.3 2000/06/04 */
2 | /* NetHack may be freely redistributed. See license for details. */
3 |
4 | /*
5 | * This source file is compiled twice:
6 | * once without TILETEXT defined to make tilemap.{o,obj},
7 | * then again with it defined to produce tiletxt.{o,obj}.
8 | */
9 |
10 | #include "hack.h"
11 |
12 | const char * FDECL(tilename, (int, int));
13 | void NDECL(init_tilemap);
14 | void FDECL(process_substitutions, (FILE *));
15 |
16 | #ifdef MICRO
17 | #undef exit
18 | #if !defined(MSDOS) && !defined(WIN32)
19 | extern void FDECL(exit, (int));
20 | #endif
21 | #endif
22 |
23 | #define MON_GLYPH 1
24 | #define OBJ_GLYPH 2
25 | #define OTH_GLYPH 3 /* fortunately unnecessary */
26 |
27 | /* note that the ifdefs here should be the opposite sense from monst.c/
28 | * objects.c/rm.h
29 | */
30 |
31 | struct conditionals {
32 | int sequence, predecessor;
33 | const char *name;
34 | } conditionals[] = {
35 | #ifndef CHARON /* not supported yet */
36 | { MON_GLYPH, PM_HELL_HOUND, "Cerberus" },
37 | #endif
38 | /* commented out in monst.c at present */
39 | { MON_GLYPH, PM_SHOCKING_SPHERE, "beholder" },
40 | { MON_GLYPH, PM_BABY_SILVER_DRAGON, "baby shimmering dragon" },
41 | { MON_GLYPH, PM_SILVER_DRAGON, "shimmering dragon" },
42 | { MON_GLYPH, PM_JABBERWOCK, "vorpal jabberwock" },
43 | #ifndef KOPS
44 | { MON_GLYPH, PM_JABBERWOCK, "Keystone Kop" },
45 | { MON_GLYPH, PM_JABBERWOCK, "Kop Sergeant" },
46 | { MON_GLYPH, PM_JABBERWOCK, "Kop Lieutenant" },
47 | { MON_GLYPH, PM_JABBERWOCK, "Kop Kaptain" },
48 | #endif
49 | { MON_GLYPH, PM_VAMPIRE_LORD, "vampire mage" },
50 | #ifndef CHARON /* not supported yet */
51 | { MON_GLYPH, PM_CROESUS, "Charon" },
52 | #endif
53 | #ifndef MAIL
54 | { MON_GLYPH, PM_FAMINE, "mail daemon" },
55 | #endif
56 | #ifndef TOURIST
57 | { MON_GLYPH, PM_SAMURAI, "tourist" },
58 | #endif
59 | /* commented out in monst.c at present */
60 | { MON_GLYPH, PM_SHAMAN_KARNOV, "Earendil" },
61 | { MON_GLYPH, PM_SHAMAN_KARNOV, "Elwing" },
62 | #ifndef TOURIST
63 | { MON_GLYPH, PM_LORD_SATO, "Twoflower" },
64 | #endif
65 | /* commented out in monst.c at present */
66 | { MON_GLYPH, PM_CHROMATIC_DRAGON, "Goblin King" },
67 | { MON_GLYPH, PM_NEANDERTHAL, "High-elf" },
68 | #ifndef TOURIST
69 | { MON_GLYPH, PM_ROSHI, "guide" },
70 | #endif
71 | #ifndef KOPS
72 | { OBJ_GLYPH, CLUB, "rubber hose" },
73 | #endif
74 | /* objects commented out in objects.c at present */
75 | { OBJ_GLYPH, SILVER_DRAGON_SCALE_MAIL, "shimmering dragon scale mail" },
76 | { OBJ_GLYPH, SILVER_DRAGON_SCALES, "shimmering dragon scales" },
77 | #ifndef TOURIST
78 | { OBJ_GLYPH, LEATHER_JACKET, "Hawaiian shirt" },
79 | { OBJ_GLYPH, LEATHER_JACKET, "T-shirt" },
80 | { OBJ_GLYPH, LOCK_PICK, "credit card" },
81 | { OBJ_GLYPH, MAGIC_LAMP, "expensive camera" },
82 | #endif
83 | #ifndef STEED
84 | { OBJ_GLYPH, TOWEL, "saddle" },
85 | #endif
86 | /* allow slime mold to look like slice of pizza, since we
87 | * don't know what a slime mold should look like when renamed anyway
88 | */
89 | #ifndef MAIL
90 | { OBJ_GLYPH, SCR_STINKING_CLOUD+4, "stamped / mail" },
91 | #endif
92 | { 0, 0, 0}
93 | };
94 |
95 |
96 | /*
97 | * Some entries in glyph2tile[] should be substituted for on various levels.
98 | * The tiles used for the substitute entries will follow the usual ones in
99 | * other.til in the order given here, which should have every substitution
100 | * for the same set of tiles grouped together. You will have to change
101 | * more code in process_substitutions()/substitute_tiles() if the sets
102 | * overlap in the future.
103 | */
104 | struct substitute {
105 | int first_glyph, last_glyph;
106 | const char *sub_name; /* for explanations */
107 | const char *level_test;
108 | } substitutes[] = {
109 | { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
110 | "mine walls", "In_mines(plev)" },
111 | { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
112 | "gehennom walls", "In_hell(plev)" },
113 | { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
114 | "knox walls", "Is_knox(plev)" },
115 | { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
116 | "sokoban walls", "In_sokoban(plev)" }
117 | };
118 |
119 |
120 | #ifdef TILETEXT
121 |
122 | /*
123 | * entry is the position of the tile within the monsters/objects/other set
124 | */
125 | const char *
126 | tilename(set, entry)
127 | int set, entry;
128 | {
129 | int i, j, condnum, tilenum;
130 | static char buf[BUFSZ];
131 |
132 | /* Note: these initializers don't do anything except guarantee that
133 | we're linked properly.
134 | */
135 | monst_init();
136 | objects_init();
137 | (void) def_char_to_objclass(']');
138 |
139 | condnum = tilenum = 0;
140 |
141 | for (i = 0; i < NUMMONS; i++) {
142 | if (set == MON_GLYPH && tilenum == entry)
143 | return mons[i].mname;
144 | tilenum++;
145 | while (conditionals[condnum].sequence == MON_GLYPH &&
146 | conditionals[condnum].predecessor == i) {
147 | if (set == MON_GLYPH && tilenum == entry)
148 | return conditionals[condnum].name;
149 | condnum++;
150 | tilenum++;
151 | }
152 | }
153 | if (set == MON_GLYPH && tilenum == entry)
154 | return "invisible monster";
155 |
156 | tilenum = 0; /* set-relative number */
157 | for (i = 0; i < NUM_OBJECTS; i++) {
158 | /* prefer to give the description - that's all the tile's
159 | * appearance should reveal */
160 | if (set == OBJ_GLYPH && tilenum == entry) {
161 | if ( !obj_descr[i].oc_descr )
162 | return obj_descr[i].oc_name;
163 | if ( !obj_descr[i].oc_name )
164 | return obj_descr[i].oc_descr;
165 |
166 | Sprintf(buf, "%s / %s",
167 | obj_descr[i].oc_descr,
168 | obj_descr[i].oc_name);
169 | return buf;
170 | }
171 |
172 | tilenum++;
173 | while (conditionals[condnum].sequence == OBJ_GLYPH &&
174 | conditionals[condnum].predecessor == i) {
175 | if (set == OBJ_GLYPH && tilenum == entry)
176 | return conditionals[condnum].name;
177 | condnum++;
178 | tilenum++;
179 | }
180 | }
181 |
182 | tilenum = 0; /* set-relative number */
183 | for (i = 0; i < MAXPCHARS; i++) {
184 | if (set == OTH_GLYPH && tilenum == entry) {
185 | if (*defsyms[i].explanation)
186 | return defsyms[i].explanation;
187 | else {
188 | /* if SINKS are turned off, this
189 | * string won't be there (and can't be there
190 | * to prevent symbol-identification and
191 | * special-level mimic appearances from
192 | * thinking the items exist)
193 | */
194 | switch (i) {
195 | case S_sink:
196 | Sprintf(buf, "sink");
197 | break;
198 | default:
199 | Sprintf(buf, "cmap %d", tilenum);
200 | break;
201 | }
202 | return buf;
203 | }
204 | }
205 | tilenum++;
206 | while (conditionals[condnum].sequence == OTH_GLYPH &&
207 | conditionals[condnum].predecessor == i) {
208 | if (set == OTH_GLYPH && tilenum == entry)
209 | return conditionals[condnum].name;
210 | condnum++;
211 | tilenum++;
212 | }
213 | }
214 |
215 | i = entry - tilenum;
216 | if (i < (NUM_ZAP << 2)) {
217 | if (set == OTH_GLYPH) {
218 | Sprintf(buf, "zap %d %d", i/4, i%4);
219 | return buf;
220 | }
221 | }
222 | tilenum += (NUM_ZAP << 2);
223 |
224 | i = entry - tilenum;
225 | if (i < WARNCOUNT) {
226 | if (set == OTH_GLYPH) {
227 | Sprintf(buf, "warning %d", i);
228 | return buf;
229 | }
230 | }
231 | tilenum += WARNCOUNT;
232 |
233 | for (i = 0; i < SIZE(substitutes); i++) {
234 | j = entry - tilenum;
235 | if (j <= substitutes[i].last_glyph - substitutes[i].first_glyph) {
236 | if (set == OTH_GLYPH) {
237 | Sprintf(buf, "sub %s %d", substitutes[i].sub_name, j);
238 | return buf;
239 | }
240 | }
241 | tilenum += substitutes[i].last_glyph
242 | - substitutes[i].first_glyph + 1;
243 | }
244 |
245 | Sprintf(buf, "unknown %d %d", set, entry);
246 | return buf;
247 | }
248 |
249 | #else /* TILETEXT */
250 |
251 | #define TILE_FILE "tile.c"
252 |
253 | #ifdef AMIGA
254 | # define SOURCE_TEMPLATE "NH:src/%s"
255 | #else
256 | # ifdef MAC
257 | # define SOURCE_TEMPLATE ":src:%s"
258 | # else
259 | # define SOURCE_TEMPLATE "../src/%s"
260 | # endif
261 | #endif
262 |
263 | short tilemap[MAX_GLYPH];
264 | int lastmontile, lastobjtile, lastothtile;
265 |
266 | /* Number of tiles for invisible monsters */
267 | #define NUM_INVIS_TILES 1
268 |
269 | /*
270 | * set up array to map glyph numbers to tile numbers
271 | *
272 | * assumes tiles are numbered sequentially through monsters/objects/other,
273 | * with entries for all supported compilation options
274 | *
275 | * "other" contains cmap and zaps (the swallow sets are a repeated portion
276 | * of cmap), as well as the "flash" glyphs for the new warning system
277 | * introduced in 3.3.1.
278 | */
279 | void
280 | init_tilemap()
281 | {
282 | int i, j, condnum, tilenum;
283 | int corpsetile, swallowbase;
284 |
285 | for (i = 0; i < MAX_GLYPH; i++) {
286 | tilemap[i] = -1;
287 | }
288 |
289 | corpsetile = NUMMONS + NUM_INVIS_TILES + CORPSE;
290 | swallowbase= NUMMONS + NUM_INVIS_TILES + NUM_OBJECTS + S_sw_tl;
291 |
292 | /* add number compiled out */
293 | for (i = 0; conditionals[i].sequence; i++) {
294 | switch (conditionals[i].sequence) {
295 | case MON_GLYPH:
296 | corpsetile++;
297 | swallowbase++;
298 | break;
299 | case OBJ_GLYPH:
300 | if (conditionals[i].predecessor < CORPSE)
301 | corpsetile++;
302 | swallowbase++;
303 | break;
304 | case OTH_GLYPH:
305 | if (conditionals[i].predecessor < S_sw_tl)
306 | swallowbase++;
307 | break;
308 | }
309 | }
310 |
311 | condnum = tilenum = 0;
312 | for (i = 0; i < NUMMONS; i++) {
313 | tilemap[GLYPH_MON_OFF+i] = tilenum;
314 | tilemap[GLYPH_PET_OFF+i] = tilenum;
315 | tilemap[GLYPH_DETECT_OFF+i] = tilenum;
316 | tilemap[GLYPH_RIDDEN_OFF+i] = tilenum;
317 | tilemap[GLYPH_BODY_OFF+i] = corpsetile;
318 | j = GLYPH_SWALLOW_OFF + 8*i;
319 | tilemap[j] = swallowbase;
320 | tilemap[j+1] = swallowbase+1;
321 | tilemap[j+2] = swallowbase+2;
322 | tilemap[j+3] = swallowbase+3;
323 | tilemap[j+4] = swallowbase+4;
324 | tilemap[j+5] = swallowbase+5;
325 | tilemap[j+6] = swallowbase+6;
326 | tilemap[j+7] = swallowbase+7;
327 | tilenum++;
328 | while (conditionals[condnum].sequence == MON_GLYPH &&
329 | conditionals[condnum].predecessor == i) {
330 | condnum++;
331 | tilenum++;
332 | }
333 | }
334 | tilemap[GLYPH_INVISIBLE] = tilenum++;
335 | lastmontile = tilenum - 1;
336 |
337 | for (i = 0; i < NUM_OBJECTS; i++) {
338 | tilemap[GLYPH_OBJ_OFF+i] = tilenum;
339 | tilenum++;
340 | while (conditionals[condnum].sequence == OBJ_GLYPH &&
341 | conditionals[condnum].predecessor == i) {
342 | condnum++;
343 | tilenum++;
344 | }
345 | }
346 | lastobjtile = tilenum - 1;
347 |
348 | for (i = 0; i < MAXPCHARS; i++) {
349 | tilemap[GLYPH_CMAP_OFF+i] = tilenum;
350 | tilenum++;
351 | while (conditionals[condnum].sequence == OTH_GLYPH &&
352 | conditionals[condnum].predecessor == i) {
353 | condnum++;
354 | tilenum++;
355 | }
356 | }
357 |
358 | for (i = 0; i < NUM_ZAP << 2; i++) {
359 | tilemap[GLYPH_ZAP_OFF+i] = tilenum;
360 | tilenum++;
361 | while (conditionals[condnum].sequence == OTH_GLYPH &&
362 | conditionals[condnum].predecessor == (i + MAXPCHARS)) {
363 | condnum++;
364 | tilenum++;
365 | }
366 | }
367 |
368 | for (i = 0; i < WARNCOUNT; i++) {
369 | tilemap[GLYPH_WARNING_OFF+i] = tilenum;
370 | tilenum++;
371 | }
372 |
373 | lastothtile = tilenum - 1;
374 | }
375 |
376 | const char *prolog[] = {
377 | "",
378 | "",
379 | "void",
380 | "substitute_tiles(plev)",
381 | "d_level *plev;",
382 | "{",
383 | "\tint i;",
384 | ""
385 | };
386 |
387 | const char *epilog[] = {
388 | "}"
389 | };
390 |
391 | /* write out the substitutions in an easily-used form. */
392 | void
393 | process_substitutions(ofp)
394 | FILE *ofp;
395 | {
396 | int i, j, k, span, start;
397 |
398 | fprintf(ofp, "\n\n");
399 |
400 | j = 0; /* unnecessary */
401 | span = -1;
402 | for (i = 0; i < SIZE(substitutes); i++) {
403 | if (i == 0
404 | || substitutes[i].first_glyph != substitutes[j].first_glyph
405 | || substitutes[i].last_glyph != substitutes[j].last_glyph) {
406 | j = i;
407 | span++;
408 | fprintf(ofp, "short std_tiles%d[] = { ", span);
409 | for (k = substitutes[i].first_glyph;
410 | k < substitutes[i].last_glyph; k++)
411 | fprintf(ofp, "%d, ", tilemap[k]);
412 | fprintf(ofp, "%d };\n",
413 | tilemap[substitutes[i].last_glyph]);
414 | }
415 | }
416 |
417 | for (i = 0; i < SIZE(prolog); i++) {
418 | fprintf(ofp, "%s\n", prolog[i]);
419 | }
420 | j = -1;
421 | span = -1;
422 | start = lastothtile + 1;
423 | for (i = 0; i < SIZE(substitutes); i++) {
424 | if (i == 0
425 | || substitutes[i].first_glyph != substitutes[j].first_glyph
426 | || substitutes[i].last_glyph != substitutes[j].last_glyph) {
427 | if (i != 0) { /* finish previous span */
428 | fprintf(ofp, "\t} else {\n");
429 | fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
430 | substitutes[j].first_glyph,
431 | substitutes[j].last_glyph);
432 | fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n",
433 | span, substitutes[j].first_glyph);
434 | fprintf(ofp, "\t}\n\n");
435 | }
436 | j = i;
437 | span++;
438 | }
439 | if (i != j) fprintf(ofp, "\t} else ");
440 | fprintf(ofp, "\tif (%s) {\n", substitutes[i].level_test);
441 | fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
442 | substitutes[i].first_glyph,
443 | substitutes[i].last_glyph);
444 | fprintf(ofp, "\t\t\tglyph2tile[i] = %d + i - %d;\n",
445 | start, substitutes[i].first_glyph);
446 | start += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
447 | }
448 | /* finish last span */
449 | fprintf(ofp, "\t} else {\n");
450 | fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
451 | substitutes[j].first_glyph,
452 | substitutes[j].last_glyph);
453 | fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n",
454 | span, substitutes[j].first_glyph);
455 | fprintf(ofp, "\t}\n\n");
456 |
457 | for (i = 0; i < SIZE(epilog); i++) {
458 | fprintf(ofp, "%s\n", epilog[i]);
459 | }
460 |
461 | fprintf(ofp, "\nint total_tiles_used = %d;\n", start);
462 | lastothtile = start - 1;
463 | }
464 |
465 | int main()
466 | {
467 | register int i;
468 | char filename[30];
469 | FILE *ofp;
470 |
471 | init_tilemap();
472 |
473 | /*
474 | * create the source file, "tile.c"
475 | */
476 | Sprintf(filename, SOURCE_TEMPLATE, TILE_FILE);
477 | if (!(ofp = fopen(filename, "w"))) {
478 | perror(filename);
479 | exit(EXIT_FAILURE);
480 | }
481 | fprintf(ofp,"/* This file is automatically generated. Do not edit. */\n");
482 | fprintf(ofp,"\n#include \"hack.h\"\n\n");
483 | fprintf(ofp,"short glyph2tile[MAX_GLYPH] = {\n");
484 |
485 | for (i = 0; i < MAX_GLYPH; i++) {
486 | fprintf(ofp,"%2d,%c", tilemap[i], (i % 12) ? ' ' : '\n');
487 | }
488 | fprintf(ofp,"%s};\n", (i % 12) ? "\n" : "");
489 |
490 | process_substitutions(ofp);
491 |
492 | fprintf(ofp,"\n#define MAXMONTILE %d\n", lastmontile);
493 | fprintf(ofp,"#define MAXOBJTILE %d\n", lastobjtile);
494 | fprintf(ofp,"#define MAXOTHTILE %d\n", lastothtile);
495 |
496 | fprintf(ofp,"\n/*tile.c*/\n");
497 |
498 | fclose(ofp);
499 | exit(EXIT_SUCCESS);
500 | /*NOTREACHED*/
501 | return 0;
502 | }
503 |
504 | #endif /* TILETEXT */