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 *
tilename(set,entry)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
init_tilemap()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
process_substitutions(ofp)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 
main()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 */
505