Files
nethack/win/share/tiletext.c
T
nhmall de842ec483 add a set of decals to stock tileset
win/share/decals.txt contains (initially) a decal_delimiter tile,
a decal_pet tile, and a decal_pile tile.
The latter two can be used for hilite_pet and hilite_pile implementations
that don't have something in place already. The implementation would
just need to apply (merge) the non-background decal pixels over a
regular tile.

The special decal_delimiter tile can be used to confirm the presence
of the delimiter tiles in the tileset and mark the end of the regular tiles,
and the start of the decal tiles.
   row 1 contains the background color whose pixels should be
   ignored when applying a decal to a tile.
   row 2 contains a row of pixels colored (0, 0, 0).
   row 3 contains a row of pixels colored pure green (0, 255, 0).
   row 3 contains a row of pixels colored pure blue  (0, 0, 255).
2026-07-29 20:47:50 -04:00

461 lines
14 KiB
C

/* NetHack 5.0 tiletext.c $NHDT-Date: 1781973099 2026/06/20 16:31:39 $ $NHDT-Branch: NetHack-5.0 $:$NHDT-Revision: 1.31 $ */
/* Copyright (c) 2016 by Pasi Kallinen */
/* NetHack may be freely redistributed. See license for details. */
#include "config.h"
#include "tile.h"
pixval ColorMap[3][MAXCOLORMAPSIZE];
int colorsinmap;
pixval MainColorMap[3][MAXCOLORMAPSIZE];
int colorsinmainmap;
static short color_index[MAXCOLORMAPSIZE];
static int num_colors;
static char charcolors[MAXCOLORMAPSIZE];
static int placeholder_init = 0;
static pixel placeholder[TILE_Y][TILE_X];
static FILE *tile_file;
static int tile_set, tile_set_indx;
static const char *const text_sets[] = {
#if (TILE_X == 8)
"monthin.txt", "objthin.txt", "oththin.txt", "monthin.txt", "decthin.txt"
#else
"monsters.txt", "objects.txt", "other.txt", "monsters.txt", "decals.txt"
#endif
};
static char inbuf[BUFSZ];
extern const char *tilename(int, int, int);
extern boolean acceptable_tilename(int, int, const char *, const char *);
static int get_next_line(FILE *, boolean);
static void read_text_colormap(FILE *);
static boolean write_text_colormap(FILE *);
static boolean read_txttile(FILE *, pixel (*)[TILE_X]);
static void write_txttile(FILE *, pixel (*)[TILE_X]);
enum { MONSTER_SET, OBJECT_SET, OTHER_SET, STATUE_SET, DECAL_SET };
/* Ugh. DICE doesn't like %[A-Z], so we have to spell it out... */
#define FORMAT_STRING \
"%1[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.] = " \
"(%d, %d, %d) "
/*
-----------------------------------------------------------------------------
Tile palette Feb 9, 2022
[0] . = (71, 108, 108)
[1] A = (0, 0, 0) grayshade maps to [1] itself
[2] B = (0, 182, 255) maps to [17] or Q for shade of gray
[3] C = (255, 108, 0) maps to [18] or R for shade of gray
[4] D = (255, 0, 0) maps to [19] or S for shade of gray
[5] E = (0, 0, 255) maps to [20] or T for shade of gray
[6] F = (0, 145, 0) maps to [27] or 0 for shade of gray
[7] G = (108, 255, 0) maps to [22] or V for shade of gray
[8] H = (255, 255, 0) maps to [23] or W for shade of gray
[9] I = (255, 0, 255) maps to [24] or X for shade of gray
[10] J = (145, 71, 0) maps to [25] or Y for shade of gray
[11] K = (204, 79, 0) maps to [26] or Z for shade of gray
[12] L = (255, 182, 145) maps to [21] or U for shade of gray
[13] M = (237, 237, 237) grayshade maps to [15] or O for shade of gray
[14] N = (255, 255, 255) grayshade maps to [13] or M for shade of gray
[15] O = (215, 215, 215) grayshade maps to [14] or N for shade of gray
[16] P = (108, 145, 182) maps to [14] or N for shade of gray
[17] Q = (18, 18, 18) grayshade maps to [1] or A for shade of gray
[18] R = (54, 54, 54) grayshade maps to [17] or Q for shade of gray
[19] S = (73, 73, 73) grayshade maps to [18] or R for shade of gray
[20] T = (82, 82, 82) grayshade maps to [19] or S for shade of gray
[21] U = (205,205,205) grayshade maps to [20] or T for shade of gray
[22] V = (104, 104, 104) grayshade maps to [27] or 0 for shade of gray
[23] W = (131, 131, 131) grayshade maps to [22] or V for shade of gray
[24] X = (140, 140, 140) grayshade maps to [23] or W for shade of gray
[25] Y = (149, 149, 149) grayshade maps to [24] or X for shade of gray
[26] Z = (195, 195, 195) grayshade maps to [25] or Y for shade of gray
[27] 0 = (100, 100, 100) grayshade maps to [20] or T for shade of gray
[28] 1 = (72, 108, 108) not mapped in graymappings
-----------------------------------------------------------------------------
*/
static int grayscale = 0;
/* grayscale color mapping */
static const int graymappings[] = {
/* . A B C D E F G H I J K L M N O P */
0, 1, 17, 18, 19, 20, 27, 22, 23, 24, 25, 26, 21, 15, 13, 14, 14,
/* Q R S T U V W X Y Z 0 */
1, 17, 18, 19, 20, 27, 22, 23, 24, 25, 20
};
void
set_grayscale(int gs)
{
grayscale = gs;
}
/* read next line; repeat until it's a non-comment; populates 'inbuf[]';
the buffer might already have data (first line after colormap) */
static int
get_next_line(FILE *txtfile, boolean force)
{
int ch;
for (;;) {
if (force || !inbuf[0]) {
/* skip leading whitespace */
do {
ch = fgetc(txtfile);
} while (ch == ' ');
ungetc(ch, txtfile);
/* get rest of line */
if (!fgets(inbuf, BUFSZ, txtfile))
break;
force = TRUE;
/* ignore blank lines;
the old fscanf() processing did that, possibly by accident */
if (!inbuf[0] || (inbuf[0] == '\n' && !inbuf[1]))
continue;
}
if (inbuf[0] != '#' || !strncmp(inbuf, "# tile ", 7))
return 1;
}
inbuf[0] = '\0';
return 0;
}
static void
read_text_colormap(FILE *txtfile)
{
int i, r, g, b;
char c[2];
for (i = 0; i < MAXCOLORMAPSIZE; i++)
color_index[i] = -1;
num_colors = 0;
while (get_next_line(txtfile, TRUE)) {
if (sscanf(inbuf, FORMAT_STRING, c, &r, &g, &b) != 4)
break;
color_index[(int) c[0]] = num_colors;
ColorMap[CM_RED][num_colors] = r;
ColorMap[CM_GREEN][num_colors] = g;
ColorMap[CM_BLUE][num_colors] = b;
num_colors++;
}
colorsinmap = num_colors;
}
#undef FORMAT_STRING
static boolean
write_text_colormap(FILE *txtfile)
{
int i;
char c;
num_colors = colorsinmainmap;
if (num_colors > 62) {
Fprintf(stderr, "too many colors (%d)\n", num_colors);
return FALSE;
}
for (i = 0; i < num_colors; i++) {
if (i < 26)
c = 'A' + i;
else if (i < 52)
c = 'a' + i - 26;
else
c = '0' + i - 52;
charcolors[i] = c;
Fprintf(
txtfile, "%c = (%d, %d, %d)\n", c, (int) MainColorMap[CM_RED][i],
(int) MainColorMap[CM_GREEN][i], (int) MainColorMap[CM_BLUE][i]);
}
return TRUE;
}
/* read one tile from win/share/{monsters,objects,other}.txt */
static boolean
read_txttile(FILE *txtfile, pixel (*pixels)[TILE_X])
{
static int gidx = 0;
int ph, i = 0, j, k, reslt;
char buf[BUFSZ], ttype[BUFSZ], gend[BUFSZ];
const char *p;
char *q;
boolean res = FALSE;
gend[0] = '\0';
reslt = 0;
if (get_next_line(txtfile, FALSE)) {
if (tile_set == MONSTER_SET || tile_set == STATUE_SET)
reslt = sscanf(inbuf, "# %255s %d (%255[^,],%255[^)])",
ttype, &i, buf, gend);
else
reslt = sscanf(inbuf, "# %255s %d (%255[^)])",
ttype, &i, buf);
}
if (reslt <= 0)
goto done;
ttype[sizeof ttype - 1] = '\0';
buf[sizeof buf - 1] = '\0';
if ((tile_set == MONSTER_SET || tile_set == STATUE_SET) && gend[0] == 'f')
gidx = 1;
ph = strcmp(ttype, "placeholder") == 0;
if (!ph && strcmp(ttype, "tile") != 0)
Fprintf(stderr, "Keyword \"%s\" unexpected for entry %d\n", ttype, i);
/* check tile name, but not relative number, which will
* change when tiles are added
*/
p = tilename(tile_set, tile_set_indx, gidx);
if (p && (q = strstr(p, " {")) != 0) {
*q = '\0';
}
if (p && strcmp(p, buf)) {
boolean other_mismatch =
(tile_set == OTHER_SET
&& !acceptable_tilename(tile_set, tile_set_indx, buf, p));
if (tile_set != OTHER_SET || other_mismatch) {
Fprintf(stderr, "warning: for tile %d (numbered %d) of %s,\n",
tile_set_indx, i, text_sets[tile_set]);
Fprintf(stderr, "\tfound '%s' while expecting '%s'\n", buf, p);
}
}
tile_set_indx++;
/* look for non-whitespace at each stage */
if (!get_next_line(txtfile, TRUE)) {
Fprintf(stderr, "unexpected EOF\n");
goto done;
}
if (inbuf[0] != '{') {
Fprintf(stderr, "didn't find expected '{'\n");
goto done;
}
for (j = 0; j < TILE_Y; j++) {
/* read next line of color indices */
if (!get_next_line(txtfile, TRUE)) {
Fprintf(stderr, "unexpected EOF\n");
goto done;
}
if ((q = strchr(inbuf, '\n')) == 0)
q = &inbuf[strlen(inbuf)]; /* eos(inbuf) */
while (q < &inbuf[TILE_X])
*q++ = '.';
*q = '\0';
for (i = 0; i < TILE_X; i++) {
k = color_index[(unsigned char) inbuf[i]];
if (grayscale && k >= 0) {
if (k > (SIZE(graymappings) - 1))
Fprintf(stderr, "Gray mapping issue %d > %d.\n", k,
SIZE(graymappings) - 1);
else
k = graymappings[k];
}
if (k == -1) {
Fprintf(stderr, "color %c not in colormap!\n", inbuf[i]);
} else if (k >= 0 && k < MAXCOLORMAPSIZE) {
pixels[j][i].r = ColorMap[CM_RED][k];
pixels[j][i].g = ColorMap[CM_GREEN][k];
pixels[j][i].b = ColorMap[CM_BLUE][k];
} else {
Fprintf(stderr,
"%d exceeds array boundary for ColorMap[][%d]!\n",
k, MAXCOLORMAPSIZE);
}
}
}
if (ph) {
/* remember it for later */
memcpy(placeholder, pixels, sizeof placeholder);
}
if (!get_next_line(txtfile, TRUE)) {
Fprintf(stderr, "unexpected EOF\n");
goto done;
}
if (inbuf[0] != '}') {
Fprintf(stderr, "didn't find expected '}'\n");
goto done;
}
res = TRUE;
done:
inbuf[0] = '\0';
return res;
}
static void
write_txttile(FILE *txtfile, pixel (*pixels)[TILE_X])
{
const char *p;
const char *type;
int i = 0, j, k;
if (memcmp(placeholder, pixels, sizeof(placeholder)) == 0)
type = "placeholder";
else
type = "tile";
if (tile_set == MONSTER_SET) {
for (i = 0; i < 2; ++i) {
Fprintf(txtfile, "# %s %d (unknown,%s)\n", type, tile_set_indx,
i ? "female" : "male");
if (i == 0)
tile_set_indx++;
}
} else {
p = tilename(tile_set, tile_set_indx, i);
if (p)
Fprintf(txtfile, "# %s %d (%s)\n", type, tile_set_indx, p);
else
Fprintf(txtfile, "# %s %d (null)\n", type, tile_set_indx);
}
tile_set_indx++;
Fprintf(txtfile, "{\n");
for (j = 0; j < TILE_Y; j++) {
Fprintf(txtfile, " ");
for (i = 0; i < TILE_X; i++) {
for (k = 0; k < num_colors; k++) {
if (ColorMap[CM_RED][k] == pixels[j][i].r
&& ColorMap[CM_GREEN][k] == pixels[j][i].g
&& ColorMap[CM_BLUE][k] == pixels[j][i].b)
break;
}
if (k >= num_colors)
Fprintf(stderr, "color not in colormap!\n");
(void) fputc(charcolors[k], txtfile);
}
Fprintf(txtfile, "\n");
}
Fprintf(txtfile, "}\n");
}
/* initialize main colormap from globally accessed ColorMap */
void
init_colormap(void)
{
int i;
colorsinmainmap = colorsinmap;
for (i = 0; i < colorsinmap; i++) {
MainColorMap[CM_RED][i] = ColorMap[CM_RED][i];
MainColorMap[CM_GREEN][i] = ColorMap[CM_GREEN][i];
MainColorMap[CM_BLUE][i] = ColorMap[CM_BLUE][i];
}
}
/* merge new colors from ColorMap into MainColorMap */
void
merge_colormap(void)
{
int i, j;
for (i = 0; i < colorsinmap; i++) {
for (j = 0; j < colorsinmainmap; j++) {
if (MainColorMap[CM_RED][j] == ColorMap[CM_RED][i]
&& MainColorMap[CM_GREEN][j] == ColorMap[CM_GREEN][i]
&& MainColorMap[CM_BLUE][j] == ColorMap[CM_BLUE][i])
break;
}
if (j >= colorsinmainmap) { /* new color */
if (colorsinmainmap >= MAXCOLORMAPSIZE) {
Fprintf(stderr,
"Too many colors to merge -- excess ignored.\n");
}
j = colorsinmainmap;
MainColorMap[CM_RED][j] = ColorMap[CM_RED][i];
MainColorMap[CM_GREEN][j] = ColorMap[CM_GREEN][i];
MainColorMap[CM_BLUE][j] = ColorMap[CM_BLUE][i];
colorsinmainmap++;
}
}
}
boolean
fopen_text_file(const char *filename, const char *type)
{
const char *p;
int i;
if (tile_file != (FILE *) 0) {
Fprintf(stderr, "can only open one text file at a time\n");
return FALSE;
}
tile_file = fopen(filename, type);
if (tile_file == (FILE *) 0) {
Fprintf(stderr, "cannot open text file %s\n", filename);
return FALSE;
}
p = strrchr(filename, '/');
if (p)
p++;
else
p = filename;
tile_set = 0;
for (i = 0; i < SIZE(text_sets); i++) {
if (!strcmp(p, text_sets[i]))
tile_set = i;
}
tile_set_indx = 0;
if (!strcmp(type, RDTMODE)) {
/* Fill placeholder with noise */
if (!placeholder_init) {
placeholder_init++;
for (i = 0; i < (int) sizeof placeholder; i++)
((char *) placeholder)[i] = i % 256;
}
read_text_colormap(tile_file);
if (!colorsinmainmap)
init_colormap();
else
merge_colormap();
return TRUE;
} else if (!strcmp(type, WRTMODE)) {
if (!colorsinmainmap) {
Fprintf(stderr, "no colormap set yet\n");
return FALSE;
}
return (write_text_colormap(tile_file));
} else {
Fprintf(stderr, "bad mode (%s) for fopen_text_file\n", type);
return FALSE;
}
}
boolean
read_text_tile(pixel (*pixels)[TILE_X])
{
return read_txttile(tile_file, pixels);
}
boolean
write_text_tile(pixel (*pixels)[TILE_X])
{
write_txttile(tile_file, pixels);
return TRUE;
}
int
fclose_text_file(void)
{
int ret;
ret = fclose(tile_file);
tile_file = (FILE *) 0;
return ret;
}