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