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 */