/* NetHack 3.6	tilemap.c	$NHDT-Date: 1447306925 2015/11/12 05:42:05 $  $NHDT-Branch: master $:$NHDT-Revision: 1.25 $ */
/* NetHack may be freely redistributed.  See license for details. */

/*
 *	This source file is compiled twice:
 *	once without TILETEXT defined to make tilemap.{o,obj},
 *	then again with it defined to produce tiletxt.{o,obj}.
 */

#include "hack.h"

const char *FDECL(tilename, (int, int));
void NDECL(init_tilemap);
void FDECL(process_substitutions, (FILE *));

#if defined(MICRO) || defined(WIN32)
#undef exit
#if !defined(MSDOS) && !defined(WIN32)
extern void FDECL(exit, (int));
#endif
#endif

#if defined(WIN32)
#define STATUES_LOOK_LIKE_MONSTERS
#endif

#define MON_GLYPH 1
#define OBJ_GLYPH 2
#define OTH_GLYPH 3 /* fortunately unnecessary */

#define EXTRA_SCROLL_DESCR_COUNT ((SCR_BLANK_PAPER - SCR_STINKING_CLOUD) - 1)

/* note that the ifdefs here should be the opposite sense from monst.c/
 * objects.c/rm.h
 */

struct conditionals {
    int sequence, predecessor;
    const char *name;
} conditionals[] = {
#ifndef CHARON /* not supported yet */
    { MON_GLYPH, PM_HELL_HOUND, "Cerberus" },
#endif
    /* commented out in monst.c at present */
    { MON_GLYPH, PM_SHOCKING_SPHERE, "beholder" },
    { MON_GLYPH, PM_BABY_SILVER_DRAGON, "baby shimmering dragon" },
    { MON_GLYPH, PM_SILVER_DRAGON, "shimmering dragon" },
    { MON_GLYPH, PM_JABBERWOCK, "vorpal jabberwock" },
    { MON_GLYPH, PM_VAMPIRE_LORD, "vampire mage" },
#ifndef CHARON /* not supported yet */
    { MON_GLYPH, PM_CROESUS, "Charon" },
#endif
#ifndef MAIL
    { MON_GLYPH, PM_FAMINE, "mail daemon" },
#endif
    /* commented out in monst.c at present */
    { MON_GLYPH, PM_SHAMAN_KARNOV, "Earendil" },
    { MON_GLYPH, PM_SHAMAN_KARNOV, "Elwing" },
    /* commented out in monst.c at present */
    { MON_GLYPH, PM_CHROMATIC_DRAGON, "Goblin King" },
    { MON_GLYPH, PM_NEANDERTHAL, "High-elf" },
    /* objects commented out in objects.c at present */
    { OBJ_GLYPH, SILVER_DRAGON_SCALE_MAIL, "shimmering dragon scale mail" },
    { OBJ_GLYPH, SILVER_DRAGON_SCALES, "shimmering dragon scales" },
/* allow slime mold to look like slice of pizza, since we
 * don't know what a slime mold should look like when renamed anyway
 */
#ifndef MAIL
    { OBJ_GLYPH, SCR_STINKING_CLOUD + EXTRA_SCROLL_DESCR_COUNT, "stamped / mail" },
#endif
    { 0, 0, 0 }
};

/*
 * Some entries in glyph2tile[] should be substituted for on various levels.
 * The tiles used for the substitute entries will follow the usual ones in
 * other.til in the order given here, which should have every substitution
 * for the same set of tiles grouped together.  You will have to change
 * more code in process_substitutions()/substitute_tiles() if the sets
 * overlap in the future.
 */
struct substitute {
    int first_glyph, last_glyph;
    const char *sub_name; /* for explanations */
    const char *level_test;
} substitutes[] = { { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
                      "mine walls", "In_mines(plev)" },
                    { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
                      "gehennom walls", "In_hell(plev)" },
                    { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
                      "knox walls", "Is_knox(plev)" },
                    { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall,
                      "sokoban walls", "In_sokoban(plev)" } };

#ifdef TILETEXT

/*
 * entry is the position of the tile within the monsters/objects/other set
 */
const char *
tilename(set, entry)
int set, entry;
{
    int i, j, condnum, tilenum;
    static char buf[BUFSZ];

    /* Note:  these initializers don't do anything except guarantee that
            we're linked properly.
    */
    monst_init();
    objects_init();
    (void) def_char_to_objclass(']');

    condnum = tilenum = 0;

    for (i = 0; i < NUMMONS; i++) {
        if (set == MON_GLYPH && tilenum == entry)
            return mons[i].mname;
        tilenum++;
        while (conditionals[condnum].sequence == MON_GLYPH
               && conditionals[condnum].predecessor == i) {
            if (set == MON_GLYPH && tilenum == entry)
                return conditionals[condnum].name;
            condnum++;
            tilenum++;
        }
    }
    if (set == MON_GLYPH && tilenum == entry)
        return "invisible monster";

    tilenum = 0; /* set-relative number */
    for (i = 0; i < NUM_OBJECTS; i++) {
        /* prefer to give the description - that's all the tile's
         * appearance should reveal */
        if (set == OBJ_GLYPH && tilenum == entry) {
            if (!obj_descr[i].oc_descr)
                return obj_descr[i].oc_name;
            if (!obj_descr[i].oc_name)
                return obj_descr[i].oc_descr;

            Sprintf(buf, "%s / %s", obj_descr[i].oc_descr,
                    obj_descr[i].oc_name);
            return buf;
        }

        tilenum++;
        while (conditionals[condnum].sequence == OBJ_GLYPH
               && conditionals[condnum].predecessor == i) {
            if (set == OBJ_GLYPH && tilenum == entry)
                return conditionals[condnum].name;
            condnum++;
            tilenum++;
        }
    }

    tilenum = 0; /* set-relative number */
    for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) {
        if (set == OTH_GLYPH && tilenum == entry) {
            if (*defsyms[i].explanation) {
                return defsyms[i].explanation;
            } else {
                Sprintf(buf, "cmap %d", tilenum);
                return buf;
            }
        }
        tilenum++;
        while (conditionals[condnum].sequence == OTH_GLYPH
               && conditionals[condnum].predecessor == i) {
            if (set == OTH_GLYPH && tilenum == entry)
                return conditionals[condnum].name;
            condnum++;
            tilenum++;
        }
    }
    /* explosions */
    tilenum = MAXPCHARS - MAXEXPCHARS;
    i = entry - tilenum;
    if (i < (MAXEXPCHARS * EXPL_MAX)) {
        if (set == OTH_GLYPH) {
            static const char *explosion_types[] = {
                /* hack.h */
                "dark", "noxious", "muddy", "wet", "magical", "fiery",
                "frosty"
            };
            Sprintf(buf, "explosion %s %d", explosion_types[i / MAXEXPCHARS],
                    i % MAXEXPCHARS);
            return buf;
        }
    }
    tilenum += (MAXEXPCHARS * EXPL_MAX);

    i = entry - tilenum;
    if (i < (NUM_ZAP << 2)) {
        if (set == OTH_GLYPH) {
            Sprintf(buf, "zap %d %d", i / 4, i % 4);
            return buf;
        }
    }
    tilenum += (NUM_ZAP << 2);

    i = entry - tilenum;
    if (i < WARNCOUNT) {
        if (set == OTH_GLYPH) {
            Sprintf(buf, "warning %d", i);
            return buf;
        }
    }
    tilenum += WARNCOUNT;

    for (i = 0; i < SIZE(substitutes); i++) {
        j = entry - tilenum;
        if (j <= substitutes[i].last_glyph - substitutes[i].first_glyph) {
            if (set == OTH_GLYPH) {
                Sprintf(buf, "sub %s %d", substitutes[i].sub_name, j);
                return buf;
            }
        }
        tilenum += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
    }

    Sprintf(buf, "unknown %d %d", set, entry);
    return buf;
}

#else /* TILETEXT */

#define TILE_FILE "tile.c"

#ifdef AMIGA
#define SOURCE_TEMPLATE "NH:src/%s"
#else
#ifdef MAC
#define SOURCE_TEMPLATE ":src:%s"
#else
#define SOURCE_TEMPLATE "../src/%s"
#endif
#endif

short tilemap[MAX_GLYPH];

#ifdef STATUES_LOOK_LIKE_MONSTERS
int lastmontile, lastobjtile, lastothtile, laststatuetile;
#else
int lastmontile, lastobjtile, lastothtile;
#endif

/* Number of tiles for invisible monsters */
#define NUM_INVIS_TILES 1

/*
 * set up array to map glyph numbers to tile numbers
 *
 * assumes tiles are numbered sequentially through monsters/objects/other,
 * with entries for all supported compilation options
 *
 * "other" contains cmap and zaps (the swallow sets are a repeated portion
 * of cmap), as well as the "flash" glyphs for the new warning system
 * introduced in 3.3.1.
 */
void
init_tilemap()
{
    int i, j, condnum, tilenum;
    int corpsetile, swallowbase;

    for (i = 0; i < MAX_GLYPH; i++) {
        tilemap[i] = -1;
    }

    corpsetile = NUMMONS + NUM_INVIS_TILES + CORPSE;
    swallowbase = NUMMONS + NUM_INVIS_TILES + NUM_OBJECTS + S_sw_tl;

    /* add number compiled out */
    for (i = 0; conditionals[i].sequence; i++) {
        switch (conditionals[i].sequence) {
        case MON_GLYPH:
            corpsetile++;
            swallowbase++;
            break;
        case OBJ_GLYPH:
            if (conditionals[i].predecessor < CORPSE)
                corpsetile++;
            swallowbase++;
            break;
        case OTH_GLYPH:
            if (conditionals[i].predecessor < S_sw_tl)
                swallowbase++;
            break;
        }
    }

    condnum = tilenum = 0;
    for (i = 0; i < NUMMONS; i++) {
        tilemap[GLYPH_MON_OFF + i] = tilenum;
        tilemap[GLYPH_PET_OFF + i] = tilenum;
        tilemap[GLYPH_DETECT_OFF + i] = tilenum;
        tilemap[GLYPH_RIDDEN_OFF + i] = tilenum;
        tilemap[GLYPH_BODY_OFF + i] = corpsetile;
        j = GLYPH_SWALLOW_OFF + 8 * i;
        tilemap[j] = swallowbase;
        tilemap[j + 1] = swallowbase + 1;
        tilemap[j + 2] = swallowbase + 2;
        tilemap[j + 3] = swallowbase + 3;
        tilemap[j + 4] = swallowbase + 4;
        tilemap[j + 5] = swallowbase + 5;
        tilemap[j + 6] = swallowbase + 6;
        tilemap[j + 7] = swallowbase + 7;
        tilenum++;
        while (conditionals[condnum].sequence == MON_GLYPH
               && conditionals[condnum].predecessor == i) {
            condnum++;
            tilenum++;
        }
    }
    tilemap[GLYPH_INVISIBLE] = tilenum++;
    lastmontile = tilenum - 1;

    for (i = 0; i < NUM_OBJECTS; i++) {
        tilemap[GLYPH_OBJ_OFF + i] = tilenum;
        tilenum++;
        while (conditionals[condnum].sequence == OBJ_GLYPH
               && conditionals[condnum].predecessor == i) {
            condnum++;
            tilenum++;
        }
    }
    lastobjtile = tilenum - 1;

    for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) {
        tilemap[GLYPH_CMAP_OFF + i] = tilenum;
        tilenum++;
        while (conditionals[condnum].sequence == OTH_GLYPH
               && conditionals[condnum].predecessor == i) {
            condnum++;
            tilenum++;
        }
    }

    for (i = 0; i < (MAXEXPCHARS * EXPL_MAX); i++) {
        tilemap[GLYPH_EXPLODE_OFF + i] = tilenum;
        tilenum++;
        while (conditionals[condnum].sequence == OTH_GLYPH
               && conditionals[condnum].predecessor == (i + MAXPCHARS)) {
            condnum++;
            tilenum++;
        }
    }

    for (i = 0; i < NUM_ZAP << 2; i++) {
        tilemap[GLYPH_ZAP_OFF + i] = tilenum;
        tilenum++;
        while (conditionals[condnum].sequence == OTH_GLYPH
               && conditionals[condnum].predecessor == (i + MAXEXPCHARS)) {
            condnum++;
            tilenum++;
        }
    }

    for (i = 0; i < WARNCOUNT; i++) {
        tilemap[GLYPH_WARNING_OFF + i] = tilenum;
        tilenum++;
    }

#ifndef STATUES_LOOK_LIKE_MONSTERS
    /* statue patch: statues still use the same glyph as in vanilla */

    for (i = 0; i < NUMMONS; i++) {
        tilemap[GLYPH_STATUE_OFF + i] = tilemap[GLYPH_OBJ_OFF + STATUE];
    }
#endif

    lastothtile = tilenum - 1;

#ifdef STATUES_LOOK_LIKE_MONSTERS
    /* skip over the substitutes to get to the grayscale statues */
    for (i = 0; i < SIZE(substitutes); i++) {
        tilenum += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
    }

    /* statue patch: statues look more like the monster */
    condnum = 0; /* doing monsters again, so reset */
    for (i = 0; i < NUMMONS; i++) {
        tilemap[GLYPH_STATUE_OFF + i] = tilenum;
        tilenum++;
        while (conditionals[condnum].sequence == MON_GLYPH
               && conditionals[condnum].predecessor == i) {
            condnum++;
            tilenum++;
        }
    }
    laststatuetile = tilenum - 1;
#endif
}

const char *prolog[] = { "", "", "void", "substitute_tiles(plev)",
                         "d_level *plev;", "{", "\tint i;", "" };

const char *epilog[] = { "}" };

/* write out the substitutions in an easily-used form. */
void
process_substitutions(ofp)
FILE *ofp;
{
    int i, j, k, span, start;

    fprintf(ofp, "\n\n");

    j = 0; /* unnecessary */
    span = -1;
    for (i = 0; i < SIZE(substitutes); i++) {
        if (i == 0 || substitutes[i].first_glyph != substitutes[j].first_glyph
            || substitutes[i].last_glyph != substitutes[j].last_glyph) {
            j = i;
            span++;
            fprintf(ofp, "short std_tiles%d[] = { ", span);
            for (k = substitutes[i].first_glyph;
                 k < substitutes[i].last_glyph; k++)
                fprintf(ofp, "%d, ", tilemap[k]);
            fprintf(ofp, "%d };\n", tilemap[substitutes[i].last_glyph]);
        }
    }

    for (i = 0; i < SIZE(prolog); i++) {
        fprintf(ofp, "%s\n", prolog[i]);
    }
    j = -1;
    span = -1;
    start = lastothtile + 1;
    for (i = 0; i < SIZE(substitutes); i++) {
        if (i == 0 || substitutes[i].first_glyph != substitutes[j].first_glyph
            || substitutes[i].last_glyph != substitutes[j].last_glyph) {
            if (i != 0) { /* finish previous span */
                fprintf(ofp, "\t} else {\n");
                fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
                        substitutes[j].first_glyph,
                        substitutes[j].last_glyph);
                fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n",
                        span, substitutes[j].first_glyph);
                fprintf(ofp, "\t}\n\n");
            }
            j = i;
            span++;
        }
        if (i != j)
            fprintf(ofp, "\t} else ");
        fprintf(ofp, "\tif (%s) {\n", substitutes[i].level_test);
        fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
                substitutes[i].first_glyph, substitutes[i].last_glyph);
        fprintf(ofp, "\t\t\tglyph2tile[i] = %d + i - %d;\n", start,
                substitutes[i].first_glyph);
        start += substitutes[i].last_glyph - substitutes[i].first_glyph + 1;
    }
    /* finish last span */
    fprintf(ofp, "\t} else {\n");
    fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n",
            substitutes[j].first_glyph, substitutes[j].last_glyph);
    fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n", span,
            substitutes[j].first_glyph);
    fprintf(ofp, "\t}\n\n");

    for (i = 0; i < SIZE(epilog); i++) {
        fprintf(ofp, "%s\n", epilog[i]);
    }

    fprintf(ofp, "\nint total_tiles_used = %d;\n", start);
    lastothtile = start - 1;
}

int
main()
{
    register int i;
    char filename[30];
    FILE *ofp;

    init_tilemap();

    /*
     * create the source file, "tile.c"
     */
    Sprintf(filename, SOURCE_TEMPLATE, TILE_FILE);
    if (!(ofp = fopen(filename, "w"))) {
        perror(filename);
        exit(EXIT_FAILURE);
    }
    fprintf(ofp,
            "/* This file is automatically generated.  Do not edit. */\n");
    fprintf(ofp, "\n#include \"hack.h\"\n\n");
    fprintf(ofp, "short glyph2tile[MAX_GLYPH] = {\n");

    for (i = 0; i < MAX_GLYPH; i++) {
        fprintf(ofp, "%2d,%c", tilemap[i], (i % 12) ? ' ' : '\n');
    }
    fprintf(ofp, "%s};\n", (i % 12) ? "\n" : "");

    process_substitutions(ofp);

    fprintf(ofp, "\n#define MAXMONTILE %d\n", lastmontile);
    fprintf(ofp, "#define MAXOBJTILE %d\n", lastobjtile);
    fprintf(ofp, "#define MAXOTHTILE %d\n", lastothtile);

    fprintf(ofp, "\n/*tile.c*/\n");

    fclose(ofp);
    exit(EXIT_SUCCESS);
    /*NOTREACHED*/
    return 0;
}

#endif /* TILETEXT */
