1 /* NetHack 3.7	mhmap.c	$NHDT-Date: 1596498353 2020/08/03 23:45:53 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.85 $ */
2 /* Copyright (C) 2001 by Alex Kompel      */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 #include "win10.h"
6 #include "winMS.h"
7 #include "winos.h"
8 
9 #include "mhfont.h"
10 #include "mhinput.h"
11 #include "mhmap.h"
12 #include "mhmsg.h"
13 #include "resource.h"
14 
15 #include "color.h"
16 #if !defined(PATCHLEVEL_H)
17 #include "patchlevel.h"
18 #endif
19 
20 #define NHMAP_FONT_NAME TEXT("Terminal")
21 #define NHMAP_TTFONT_NAME TEXT("Consolas")
22 #define MAXWINDOWTEXT 255
23 
24 #define CURSOR_BLINK_INTERVAL 1000 // milliseconds
25 #define CURSOR_HEIGHT 2 // pixels
26 
27 extern short glyph2tile[];
28 
29 #define TILEBMP_X(ntile) \
30     ((ntile % GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_X)
31 #define TILEBMP_Y(ntile) \
32     ((ntile / GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_Y)
33 
34 /* map window data */
35 typedef struct mswin_nethack_map_window {
36     HWND hWnd;                  /* window */
37 
38     glyph_info map[COLNO][ROWNO];
39     glyph_info bkmap[COLNO][ROWNO];
40     boolean locDirty[COLNO][ROWNO]; /* dirty flag for map location */
41     boolean mapDirty;           /* one or more map locations are dirty */
42     int mapMode;                /* current map mode */
43     boolean bAsciiMode;         /* switch ASCII/tiled mode */
44     boolean bFitToScreenMode;   /* switch Fit map to screen mode on/off */
45     int xPos, yPos;             /* scroll position */
46     int xPageSize, yPageSize;   /* scroll page size */
47     int xMin, xMax, yMin, yMax; /* scroll range */
48     int xCur, yCur;             /* position of the cursor */
49     int xFrontTile, yFrontTile; /* size of tile in front buffer in pixels */
50     int xBackTile, yBackTile;   /* size of tile in back buffer in pixels */
51     POINT map_orig;             /* map origin point */
52 
53     HFONT hMapFont;             /* font for ASCII mode */
54     boolean bUnicodeFont;       /* font supports unicode page 437 */
55 
56     int tileWidth;              /* width of tile in pixels at 96 dpi */
57     int tileHeight;             /* height of tile in pixels at 96 dpi */
58     double backScale;           /* scaling from source to back buffer */
59     double frontScale;          /* scaling from back to front */
60     double monitorScale;        /* from 96dpi to monitor dpi*/
61 
62     boolean cursorOn;
63     int yNoBlinkCursor;         /* non-blinking cursor height inback buffer
64                                    in pixels */
65     int yBlinkCursor;           /* blinking cursor height inback buffer
66                                    in pixels */
67 
68     int backWidth;              /* back buffer width */
69     int backHeight;             /* back buffer height */
70     HBITMAP hBackBuffer;        /* back buffe bitmap */
71     HDC backBufferDC;          /* back buffer drawing context */
72 
73     HDC tileDC;                /* tile drawing context */
74 
75 } NHMapWindow, *PNHMapWindow;
76 
77 static TCHAR szNHMapWindowClass[] = TEXT("MSNethackMapWndClass");
78 LRESULT CALLBACK MapWndProc(HWND, UINT, WPARAM, LPARAM);
79 static void register_map_window_class(void);
80 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
81 static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
82 static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
83 static void onPaint(HWND hWnd);
84 static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
85 static void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut);
86 static void paint(PNHMapWindow data, int i, int j);
87 static void dirtyAll(PNHMapWindow data);
88 static void dirty(PNHMapWindow data, int i, int j);
89 static void setGlyph(PNHMapWindow data, int i, int j,
90                      const glyph_info *fg, const glyph_info *bg);
91 static void clearAll(PNHMapWindow data);
92 
93 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
94 static void nhglyph2charcolor(short glyph, uchar *ch, int *color);
95 #endif
96 extern boolean win32_cursorblink;       /* from sys\winnt\winnt.c */
97 
98 HWND
mswin_init_map_window(void)99 mswin_init_map_window(void)
100 {
101     static int run_once = 0;
102     HWND hWnd;
103     RECT rt;
104 
105     if (!run_once) {
106         register_map_window_class();
107         run_once = 1;
108     }
109 
110     /* get window position */
111     if (GetNHApp()->bAutoLayout) {
112         SetRect(&rt, 0, 0, 0, 0);
113     } else {
114         mswin_get_window_placement(NHW_MAP, &rt);
115     }
116 
117     /* create map window object */
118     hWnd = CreateWindow(
119         szNHMapWindowClass, /* registered class name */
120         NULL,               /* window name */
121         WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_CLIPSIBLINGS
122             | WS_SIZEBOX,     /* window style */
123         rt.left,              /* horizontal position of window */
124         rt.top,               /* vertical position of window */
125         rt.right - rt.left,   /* window width */
126         rt.bottom - rt.top,   /* window height */
127         GetNHApp()->hMainWnd, /* handle to parent or owner window */
128         NULL,                 /* menu handle or child identifier */
129         GetNHApp()->hApp,     /* handle to application instance */
130         NULL);                /* window-creation data */
131     if (!hWnd) {
132         panic("Cannot create map window");
133     }
134 
135     /* Set window caption */
136     SetWindowText(hWnd, "Map");
137 
138     mswin_apply_window_style(hWnd);
139 
140     /* set cursor blink timer */
141     SetTimer(hWnd, 0, CURSOR_BLINK_INTERVAL, NULL);
142 
143     return hWnd;
144 }
145 
146 void
mswin_map_layout(HWND hWnd,LPSIZE map_size)147 mswin_map_layout(HWND hWnd, LPSIZE map_size)
148 {
149     /* check arguments */
150     if (!IsWindow(hWnd) || !map_size || map_size->cx <= 0
151         || map_size->cy <= 0)
152         return;
153 
154     PNHMapWindow data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
155 
156     /* calculate window size */
157     RECT client_rt;
158     GetClientRect(hWnd, &client_rt);
159 
160     SIZE wnd_size;
161     wnd_size.cx = client_rt.right - client_rt.left;
162     wnd_size.cy = client_rt.bottom - client_rt.top;
163 
164     // calculate back buffer scale
165     data->monitorScale = win10_monitor_scale(hWnd);
166 
167     boolean bText = data->bAsciiMode;
168 
169     if (bText && !data->bFitToScreenMode)
170         data->backScale = data->monitorScale;
171     else
172         data->backScale = 1.0;
173 
174     /* set back buffer tile size */
175     if (bText && data->bFitToScreenMode) {
176         data->xBackTile = wnd_size.cx / COLNO;
177         data->yBackTile = wnd_size.cy / ROWNO;
178         data->yBackTile = max(data->yBackTile, 12);
179     } else {
180         data->xBackTile = (int)(data->tileWidth * data->backScale);
181         data->yBackTile = (int)(data->tileHeight * data->backScale);
182     }
183 
184     if (bText) {
185         LOGFONT lgfnt;
186 
187         ZeroMemory(&lgfnt, sizeof(lgfnt));
188         if (data->bFitToScreenMode) {
189             lgfnt.lfHeight = -data->yBackTile;     // height of font
190             lgfnt.lfWidth = 0;                     // average character width
191         } else {
192             lgfnt.lfHeight = -data->yBackTile;     // height of font
193             lgfnt.lfWidth = -data->xBackTile;      // average character width
194         }
195         lgfnt.lfEscapement = 0;                    // angle of escapement
196         lgfnt.lfOrientation = 0;                   // base-line orientation angle
197         lgfnt.lfWeight = FW_NORMAL;                // font weight
198         lgfnt.lfItalic = FALSE;                    // italic attribute option
199         lgfnt.lfUnderline = FALSE;                 // underline attribute option
200         lgfnt.lfStrikeOut = FALSE;                 // strikeout attribute option
201         lgfnt.lfCharSet = mswin_charset();         // character set identifier
202         lgfnt.lfOutPrecision = OUT_DEFAULT_PRECIS; // output precision
203         lgfnt.lfClipPrecision = CLIP_DEFAULT_PRECIS; // clipping precision
204         if (data->bFitToScreenMode) {
205             lgfnt.lfQuality = ANTIALIASED_QUALITY; // output quality
206         } else {
207             lgfnt.lfQuality = NONANTIALIASED_QUALITY; // output quality
208         }
209         if (iflags.wc_font_map && *iflags.wc_font_map) {
210             lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family
211             NH_A2W(iflags.wc_font_map, lgfnt.lfFaceName, LF_FACESIZE);
212         } else {
213             if (!data->bFitToScreenMode) {
214                 lgfnt.lfPitchAndFamily = FIXED_PITCH; // pitch and family
215                 NH_A2W(NHMAP_FONT_NAME, lgfnt.lfFaceName, LF_FACESIZE);
216             } else {
217                 lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family
218                 NH_A2W(NHMAP_TTFONT_NAME, lgfnt.lfFaceName, LF_FACESIZE);
219             }
220         }
221 
222         TEXTMETRIC textMetrics;
223         HFONT font = NULL;
224 
225         while (1) {
226 
227             if (font != NULL)
228                 DeleteObject(font);
229 
230             font = CreateFontIndirect(&lgfnt);
231 
232             SelectObject(data->backBufferDC, font);
233 
234             GetTextMetrics(data->backBufferDC, &textMetrics);
235 
236             if (!data->bFitToScreenMode)
237                 break;
238 
239             if ((textMetrics.tmHeight > data->yBackTile ||
240                  textMetrics.tmAveCharWidth > data->xBackTile) &&
241                 lgfnt.lfHeight < -MIN_FONT_HEIGHT) {
242                 lgfnt.lfHeight++;
243                 continue;
244             }
245 
246             break;
247         }
248 
249         if (data->hMapFont)
250             DeleteObject(data->hMapFont);
251 
252         data->hMapFont = font;
253 
254         data->bUnicodeFont = winos_font_support_cp437(data->hMapFont);
255 
256         // set tile size to match font metrics
257 
258         data->xBackTile = textMetrics.tmAveCharWidth;
259         data->yBackTile = textMetrics.tmHeight;
260 
261     }
262 
263     int backWidth = COLNO * data->xBackTile;
264     int backHeight = ROWNO * data->yBackTile;
265 
266     /* create back buffer */
267 
268     if (data->backWidth != backWidth || data->backHeight != backHeight) {
269 
270         HDC frontBufferDC = GetDC(hWnd);
271         HBITMAP hBackBuffer = CreateCompatibleBitmap(frontBufferDC, backWidth, backHeight);
272         ReleaseDC(hWnd, frontBufferDC);
273 
274         if (data->hBackBuffer != NULL) {
275             SelectBitmap(data->backBufferDC, hBackBuffer);
276             DeleteObject(data->hBackBuffer);
277         }
278 
279         data->backWidth = backWidth;
280         data->backHeight = backHeight;
281 
282         SelectBitmap(data->backBufferDC, hBackBuffer);
283         data->hBackBuffer = hBackBuffer;
284     }
285 
286     /* calculate front buffer tile size */
287 
288     if (wnd_size.cx > 0 && wnd_size.cy > 0 && !bText && data->bFitToScreenMode) {
289         double windowAspectRatio =
290             (double) wnd_size.cx / (double) wnd_size.cy;
291 
292         double backAspectRatio =
293             (double) data->backWidth / (double) data->backHeight;
294 
295         if (windowAspectRatio > backAspectRatio)
296             data->frontScale = (double) wnd_size.cy / (double) data->backHeight;
297         else
298             data->frontScale = (double) wnd_size.cx / (double) data->backWidth;
299 
300     } else {
301 
302         if (bText) {
303             data->frontScale = 1.0;
304         } else {
305             data->frontScale = data->monitorScale;
306         }
307 
308     }
309 
310     /* TODO: Should we round instead of clamping? */
311     data->xFrontTile = (int) ((double) data->xBackTile * data->frontScale);
312     data->yFrontTile = (int) ((double) data->yBackTile * data->frontScale);
313 
314     /* ensure tile is at least one pixel in size */
315     if (data->xFrontTile < 1) data->xFrontTile = 1;
316     if (data->yFrontTile < 1) data->yFrontTile = 1;
317 
318     /* ensure front tile is non-zero in size */
319     data->xFrontTile = max(data->xFrontTile, 1);
320     data->yFrontTile = max(data->yFrontTile, 1);
321 
322     /* calcuate ASCII cursor height */
323     data->yBlinkCursor = (int) ((double) CURSOR_HEIGHT * data->backScale);
324     data->yNoBlinkCursor = data->yBackTile;
325 
326     /* set map origin point */
327     data->map_orig.x =
328         max(0, client_rt.left + (wnd_size.cx - data->xFrontTile * COLNO) / 2);
329     data->map_orig.y =
330         max(0, client_rt.top + (wnd_size.cy - data->yFrontTile * ROWNO) / 2);
331 
332     data->map_orig.x -= data->map_orig.x % data->xFrontTile;
333     data->map_orig.y -= data->map_orig.y % data->yFrontTile;
334 
335     // Set horizontal scroll
336 
337     data->xPageSize = min(COLNO, wnd_size.cx / data->xFrontTile);
338 
339     GetNHApp()->bNoHScroll = (data->xPageSize == COLNO);
340 
341     data->xMin = 0;
342     data->xMax = COLNO - data->xPageSize;
343     data->xPos = max(0, min(data->xMax, u.ux - (data->xPageSize / 2)));
344 
345     SCROLLINFO si;
346 
347     si.cbSize = sizeof(si);
348     si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
349     si.nMin = data->xMin;
350     si.nMax = data->xMax;
351     si.nPage = 1;
352     si.nPos = data->xPos;
353     SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
354 
355     data->yPageSize = min(ROWNO, wnd_size.cy / data->yFrontTile);
356 
357     GetNHApp()->bNoVScroll = (data->yPageSize == ROWNO);
358 
359     data->yMin = 0;
360     data->yMax = ROWNO - data->yPageSize;
361     data->yPos = max(0, min(data->yMax, u.uy - (data->yPageSize / 2)));
362 
363     si.cbSize = sizeof(si);
364     si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
365     si.nMin = data->yMin;
366     si.nMax = data->yMax;
367     si.nPage = 1;
368     si.nPos = data->yPos;
369     SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
370 
371     mswin_cliparound(data->xCur, data->yCur);
372 
373     // redraw all map locations
374         dirtyAll(data);
375 
376     // invalidate entire map window
377         InvalidateRect(hWnd, NULL, TRUE);
378 }
379 
380 /* set map mode */
381 int
mswin_map_mode(HWND hWnd,int mode)382 mswin_map_mode(HWND hWnd, int mode)
383 {
384     PNHMapWindow data;
385     int oldMode;
386     SIZE mapSize;
387 
388     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
389     if (mode == data->mapMode)
390         return mode;
391 
392     oldMode = data->mapMode;
393     data->mapMode = mode;
394 
395     switch (data->mapMode) {
396     case MAP_MODE_ASCII4x6:
397         data->bAsciiMode = TRUE;
398         data->bFitToScreenMode = FALSE;
399         data->tileWidth = 4;
400         data->tileHeight = 6;
401         break;
402 
403     case MAP_MODE_ASCII6x8:
404         data->bAsciiMode = TRUE;
405         data->bFitToScreenMode = FALSE;
406         data->tileWidth = 6;
407         data->tileHeight = 8;
408         break;
409 
410     case MAP_MODE_ASCII8x8:
411         data->bAsciiMode = TRUE;
412         data->bFitToScreenMode = FALSE;
413         data->tileWidth = 8;
414         data->tileHeight = 8;
415         break;
416 
417     case MAP_MODE_ASCII16x8:
418         data->bAsciiMode = TRUE;
419         data->bFitToScreenMode = FALSE;
420         data->tileWidth = 16;
421         data->tileHeight = 8;
422         break;
423 
424     case MAP_MODE_ASCII7x12:
425         data->bAsciiMode = TRUE;
426         data->bFitToScreenMode = FALSE;
427         data->tileWidth = 7;
428         data->tileHeight = 12;
429         break;
430 
431     case MAP_MODE_ASCII8x12:
432         data->bAsciiMode = TRUE;
433         data->bFitToScreenMode = FALSE;
434         data->tileWidth = 8;
435         data->tileHeight = 12;
436         break;
437 
438     case MAP_MODE_ASCII16x12:
439         data->bAsciiMode = TRUE;
440         data->bFitToScreenMode = FALSE;
441         data->tileWidth = 16;
442         data->tileHeight = 12;
443         break;
444 
445     case MAP_MODE_ASCII12x16:
446         data->bAsciiMode = TRUE;
447         data->bFitToScreenMode = FALSE;
448         data->tileWidth = 12;
449         data->tileHeight = 16;
450         break;
451 
452     case MAP_MODE_ASCII10x18:
453         data->bAsciiMode = TRUE;
454         data->bFitToScreenMode = FALSE;
455         data->tileWidth = 10;
456         data->tileHeight = 18;
457         break;
458 
459     case MAP_MODE_ASCII_FIT_TO_SCREEN:
460         data->bAsciiMode = TRUE;
461         data->bFitToScreenMode = TRUE;
462         data->tileWidth = 12;
463         data->tileHeight = 16;
464         break;
465 
466     case MAP_MODE_TILES_FIT_TO_SCREEN:
467         data->bAsciiMode = FALSE;
468         data->bFitToScreenMode = TRUE;
469         data->tileWidth = GetNHApp()->mapTile_X;
470         data->tileHeight = GetNHApp()->mapTile_Y;
471         break;
472 
473     case MAP_MODE_TILES:
474     default:
475         data->bAsciiMode = FALSE;
476         data->bFitToScreenMode = FALSE;
477         data->tileWidth = GetNHApp()->mapTile_X;
478         data->tileHeight = GetNHApp()->mapTile_Y;
479         break;
480     }
481 
482     mapSize.cx = data->tileWidth * COLNO;
483     mapSize.cy = data->tileHeight * ROWNO;
484 
485     mswin_map_layout(hWnd, &mapSize);
486 
487     mswin_update_inventory(0); /* for perm_invent to hide/show tiles */
488 
489     return oldMode;
490 }
491 
mswin_map_update(HWND hWnd)492 void mswin_map_update(HWND hWnd)
493 {
494     PNHMapWindow data = (PNHMapWindow)GetWindowLongPtr(hWnd, GWLP_USERDATA);
495 
496     if (data->mapDirty)
497     {
498         /* update back buffer */
499         HBITMAP savedBitmap = SelectObject(data->tileDC, GetNHApp()->bmpMapTiles);
500 
501         for (int i = 0; i < COLNO; i++)
502             for (int j = 0; j < ROWNO; j++)
503                 if (data->locDirty[i][j])
504                 {
505                     paint(data, i, j);
506                     RECT rect;
507                     nhcoord2display(data, i, j, &rect);
508                     InvalidateRect(data->hWnd, &rect, FALSE);
509                 }
510 
511         SelectObject(data->tileDC, savedBitmap);
512         data->mapDirty = FALSE;
513     }
514 
515 }
516 
517 /* register window class for map window */
518 void
register_map_window_class(void)519 register_map_window_class(void)
520 {
521     WNDCLASS wcex;
522     ZeroMemory(&wcex, sizeof(wcex));
523 
524     /* window class */
525     wcex.style = CS_NOCLOSE | CS_DBLCLKS;
526     wcex.lpfnWndProc = (WNDPROC) MapWndProc;
527     wcex.cbClsExtra = 0;
528     wcex.cbWndExtra = 0;
529     wcex.hInstance = GetNHApp()->hApp;
530     wcex.hIcon = NULL;
531     wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
532     wcex.hbrBackground =
533         CreateSolidBrush(RGB(0, 0, 0)); /* set backgroup here */
534     wcex.lpszMenuName = NULL;
535     wcex.lpszClassName = szNHMapWindowClass;
536 
537     if (!RegisterClass(&wcex)) {
538         panic("cannot register Map window class");
539     }
540 }
541 
542 /* map window procedure */
543 LRESULT CALLBACK
MapWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)544 MapWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
545 {
546     PNHMapWindow data;
547 
548     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
549     switch (message) {
550     case WM_CREATE:
551         onCreate(hWnd, wParam, lParam);
552         break;
553 
554     case WM_MSNH_COMMAND:
555         onMSNHCommand(hWnd, wParam, lParam);
556         break;
557 
558     case WM_PAINT:
559         onPaint(hWnd);
560         break;
561 
562     case WM_SETFOCUS:
563         /* transfer focus back to the main window */
564         SetFocus(GetNHApp()->hMainWnd);
565         break;
566 
567     case WM_HSCROLL:
568         onMSNH_HScroll(hWnd, wParam, lParam);
569         break;
570 
571     case WM_VSCROLL:
572         onMSNH_VScroll(hWnd, wParam, lParam);
573         break;
574 
575     case WM_SIZE: {
576         RECT rt;
577         SIZE size;
578 
579         if (data->bFitToScreenMode) {
580             size.cx = LOWORD(lParam);
581             size.cy = HIWORD(lParam);
582         } else {
583             /* mapping factor is unchaged we just need to adjust scroll bars
584              */
585             size.cx = data->xFrontTile * COLNO;
586             size.cy = data->yFrontTile * ROWNO;
587         }
588         mswin_map_layout(hWnd, &size);
589 
590         /* update window placement */
591         GetWindowRect(hWnd, &rt);
592         ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
593         ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
594         mswin_update_window_placement(NHW_MAP, &rt);
595     } break;
596 
597     case WM_MOVE: {
598         RECT rt;
599         GetWindowRect(hWnd, &rt);
600         ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
601         ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
602         mswin_update_window_placement(NHW_MAP, &rt);
603     }
604         return FALSE;
605 
606     case WM_LBUTTONDOWN:
607         NHEVENT_MS(CLICK_1,
608                    max(0, min(COLNO, data->xPos
609                                          + (LOWORD(lParam) - data->map_orig.x)
610                                                / data->xFrontTile)),
611                    max(0, min(ROWNO, data->yPos
612                                          + (HIWORD(lParam) - data->map_orig.y)
613                                                / data->yFrontTile)));
614         return 0;
615 
616     case WM_LBUTTONDBLCLK:
617     case WM_RBUTTONDOWN:
618         NHEVENT_MS(CLICK_2,
619                    max(0, min(COLNO, data->xPos
620                                          + (LOWORD(lParam) - data->map_orig.x)
621                                                / data->xFrontTile)),
622                    max(0, min(ROWNO, data->yPos
623                                          + (HIWORD(lParam) - data->map_orig.y)
624                                                / data->yFrontTile)));
625         return 0;
626 
627     case WM_DESTROY:
628         if (data->hMapFont)
629             DeleteObject(data->hMapFont);
630         if (data->hBackBuffer)
631             DeleteBitmap(data->hBackBuffer);
632         if (data->backBufferDC)
633             DeleteDC(data->backBufferDC);
634         free(data);
635         SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
636         break;
637 
638     case WM_TIMER:
639         data->cursorOn = !data->cursorOn;
640         dirty(data, data->xCur, data->yCur);
641         break;
642 
643     case WM_DPICHANGED: {
644         RECT rt;
645         GetWindowRect(hWnd, &rt);
646         ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT)&rt);
647         ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT)&rt) + 1);
648         mswin_update_window_placement(NHW_MAP, &rt);
649     } break;
650 
651     default:
652         return DefWindowProc(hWnd, message, wParam, lParam);
653     }
654     return 0;
655 }
656 
657 /* on WM_COMMAND */
658 void
onMSNHCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)659 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
660 {
661     PNHMapWindow data;
662 
663     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
664     switch (wParam) {
665     case MSNH_MSG_PRINT_GLYPH: {
666         PMSNHMsgPrintGlyph msg_data = (PMSNHMsgPrintGlyph) lParam;
667         setGlyph(data, msg_data->x, msg_data->y,
668             &msg_data->glyphinfo, &msg_data->bkglyphinfo);
669     } break;
670 
671     case MSNH_MSG_CLIPAROUND: {
672         PMSNHMsgClipAround msg_data = (PMSNHMsgClipAround) lParam;
673         int x, y;
674         BOOL scroll_x, scroll_y;
675         int mcam = iflags.wc_scroll_margin;
676 
677         /* calculate if you should clip around */
678         scroll_x =
679             !GetNHApp()->bNoHScroll
680             && (msg_data->x < (data->xPos + mcam)
681                 || msg_data->x > (data->xPos + data->xPageSize - mcam));
682         scroll_y =
683             !GetNHApp()->bNoVScroll
684             && (msg_data->y < (data->yPos + mcam)
685                 || msg_data->y > (data->yPos + data->yPageSize - mcam));
686 
687         mcam += iflags.wc_scroll_amount - 1;
688         /* get page size and center horizontally on x-position */
689         if (scroll_x) {
690             if (data->xPageSize <= 2 * mcam) {
691                 x = max(0, min(COLNO, msg_data->x - data->xPageSize / 2));
692             } else if (msg_data->x < data->xPos + data->xPageSize / 2) {
693                 x = max(0, min(COLNO, msg_data->x - mcam));
694             } else {
695                 x = max(0, min(COLNO, msg_data->x - data->xPageSize + mcam));
696             }
697             SendMessage(hWnd, WM_HSCROLL, (WPARAM) MAKELONG(SB_THUMBTRACK, x),
698                         (LPARAM) NULL);
699         }
700 
701         /* get page size and center vertically on y-position */
702         if (scroll_y) {
703             if (data->yPageSize <= 2 * mcam) {
704                 y = max(0, min(ROWNO, msg_data->y - data->yPageSize / 2));
705             } else if (msg_data->y < data->yPos + data->yPageSize / 2) {
706                 y = max(0, min(ROWNO, msg_data->y - mcam));
707             } else {
708                 y = max(0, min(ROWNO, msg_data->y - data->yPageSize + mcam));
709             }
710             SendMessage(hWnd, WM_VSCROLL, (WPARAM) MAKELONG(SB_THUMBTRACK, y),
711                         (LPARAM) NULL);
712         }
713     } break;
714 
715     case MSNH_MSG_CLEAR_WINDOW:
716         clearAll(data);
717         break;
718 
719     case MSNH_MSG_CURSOR: {
720         PMSNHMsgCursor msg_data = (PMSNHMsgCursor) lParam;
721 
722         if (data->xCur != msg_data->x || data->yCur != msg_data->y) {
723 
724             dirty(data, data->xCur, data->yCur);
725             dirty(data, msg_data->x, msg_data->y);
726 
727             data->xCur = msg_data->x;
728             data->yCur = msg_data->y;
729         }
730 
731     } break;
732 
733     case MSNH_MSG_GETTEXT: {
734         PMSNHMsgGetText msg_data = (PMSNHMsgGetText) lParam;
735         size_t index;
736         int col, row;
737 #if 0
738         int color;
739         unsigned special = 0U;
740 #endif
741         int mgch;
742 
743         index = 0;
744         for (row = 0; row < ROWNO; row++) {
745             for (col = 0; col < COLNO; col++) {
746                 if (index >= msg_data->max_size)
747                     break;
748                 if (data->map[col][row].glyph == NO_GLYPH)
749                     mgch = ' ';
750                 msg_data->buffer[index] = data->map[col][row].ttychar;
751                 index++;
752             }
753             if (index >= msg_data->max_size - 1)
754                 break;
755             msg_data->buffer[index++] = '\r';
756             msg_data->buffer[index++] = '\n';
757         }
758     } break;
759 
760 	case MSNH_MSG_RANDOM_INPUT:
761 		nhassert(0); // unexpected
762 		break;
763 
764 	} /* end switch(wParam) */
765 }
766 
767 /* on WM_CREATE */
768 void
onCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)769 onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
770 {
771     PNHMapWindow data;
772 
773     UNREFERENCED_PARAMETER(wParam);
774     UNREFERENCED_PARAMETER(lParam);
775 
776     /* set window data */
777     data = (PNHMapWindow) malloc(sizeof(NHMapWindow));
778     if (!data)
779         panic("out of memory");
780 
781     ZeroMemory(data, sizeof(NHMapWindow));
782 
783     data->hWnd = hWnd;
784 
785     data->bAsciiMode = FALSE;
786     data->cursorOn = TRUE;
787 
788     data->xFrontTile = GetNHApp()->mapTile_X;
789     data->yFrontTile = GetNHApp()->mapTile_Y;
790     data->tileWidth = GetNHApp()->mapTile_X;
791     data->tileHeight = GetNHApp()->mapTile_Y;
792 
793     HDC hDC = GetDC(hWnd);
794     data->backBufferDC = CreateCompatibleDC(hDC);
795     data->tileDC = CreateCompatibleDC(hDC);
796     ReleaseDC(hWnd, hDC);
797 
798     SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
799 
800     clearAll(data);
801 
802 }
803 
804 static void
paintTile(PNHMapWindow data,int i,int j,RECT * rect)805 paintTile(PNHMapWindow data, int i, int j, RECT * rect)
806 {
807     short ntile;
808     int t_x, t_y;
809     int glyph, bkglyph;
810     int layer;
811 #ifdef USE_PILEMARK
812 //    int color;
813 //    unsigned special = 0U;
814 //    int mgch;
815 #endif
816     layer = 0;
817     glyph = data->map[i][j].glyph;
818     bkglyph = data->bkmap[i][j].glyph;
819 
820     if (glyph == NO_GLYPH && bkglyph == NO_GLYPH) {
821         HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
822         FillRect(data->backBufferDC, rect, blackBrush);
823         DeleteObject(blackBrush);
824     }
825 
826     if (bkglyph != NO_GLYPH) {
827         ntile = glyph2tile[bkglyph];
828         t_x = TILEBMP_X(ntile);
829         t_y = TILEBMP_Y(ntile);
830 
831         StretchBlt(data->backBufferDC, rect->left, rect->top,
832                     data->xBackTile, data->yBackTile, data->tileDC,
833                     t_x, t_y, GetNHApp()->mapTile_X,
834                     GetNHApp()->mapTile_Y, SRCCOPY);
835         layer++;
836     }
837 
838     if ((glyph != NO_GLYPH) && (glyph != bkglyph)) {
839         /* rely on NetHack core helper routine */
840         ntile = glyph2tile[glyph];
841         if (data->map[i][j].glyphflags & MG_FEMALE)
842             ntile++;
843         t_x = TILEBMP_X(ntile);
844         t_y = TILEBMP_Y(ntile);
845 
846         if (layer > 0) {
847             (*GetNHApp()->lpfnTransparentBlt)(
848                 data->backBufferDC, rect->left, rect->top,
849                 data->xBackTile, data->yBackTile, data->tileDC, t_x,
850                 t_y, GetNHApp()->mapTile_X,
851                 GetNHApp()->mapTile_Y, TILE_BK_COLOR);
852         } else {
853             StretchBlt(data->backBufferDC, rect->left, rect->top,
854                         data->xBackTile, data->yBackTile, data->tileDC,
855                         t_x, t_y, GetNHApp()->mapTile_X,
856                         GetNHApp()->mapTile_Y, SRCCOPY);
857         }
858 
859         layer++;
860     }
861 
862 #ifdef USE_PILEMARK
863     if ((glyph != NO_GLYPH) && (data->map[i][j].glyphflags & MG_PET)
864 #else
865     if ((glyph != NO_GLYPH) && glyph_is_pet(glyph)
866 #endif
867         && iflags.wc_hilite_pet) {
868         /* apply pet mark transparently over
869             pet image */
870         HDC hdcPetMark;
871         HBITMAP bmPetMarkOld;
872 
873         /* this is DC for petmark bitmap */
874         hdcPetMark = CreateCompatibleDC(data->backBufferDC);
875         bmPetMarkOld =
876             SelectObject(hdcPetMark, GetNHApp()->bmpPetMark);
877 
878         (*GetNHApp()->lpfnTransparentBlt)(
879             data->backBufferDC, rect->left, rect->top,
880             data->xBackTile, data->yBackTile, hdcPetMark, 0, 0,
881             TILE_X, TILE_Y, TILE_BK_COLOR);
882         SelectObject(hdcPetMark, bmPetMarkOld);
883         DeleteDC(hdcPetMark);
884     }
885 #ifdef USE_PILEMARK
886     if ((glyph != NO_GLYPH) && (data->map[i][j].glyphflags & MG_OBJPILE)
887         && iflags.hilite_pile) {
888         /* apply pilemark transparently over other image */
889         HDC hdcPileMark;
890         HBITMAP bmPileMarkOld;
891 
892         /* this is DC for pilemark bitmap */
893         hdcPileMark = CreateCompatibleDC(data->backBufferDC);
894         bmPileMarkOld = SelectObject(hdcPileMark,
895                                         GetNHApp()->bmpPileMark);
896 
897         (*GetNHApp()->lpfnTransparentBlt)(
898             data->backBufferDC, rect->left, rect->top,
899             data->xBackTile, data->yBackTile, hdcPileMark, 0, 0,
900             TILE_X, TILE_Y, TILE_BK_COLOR);
901         SelectObject(hdcPileMark, bmPileMarkOld);
902         DeleteDC(hdcPileMark);
903     }
904 #endif
905 
906     if (i == data->xCur && j == data->yCur &&
907         (data->cursorOn || !win32_cursorblink))
908         DrawFocusRect(data->backBufferDC, rect);
909 }
910 
911 
912 static void
paintGlyph(PNHMapWindow data,int i,int j,RECT * rect)913 paintGlyph(PNHMapWindow data, int i, int j, RECT * rect)
914 {
915     if (data->map[i][j].glyph >= 0) {
916 
917         char ch;
918         WCHAR wch;
919         int color;
920 //        unsigned special;
921 //        int mgch;
922         HBRUSH back_brush;
923         COLORREF OldFg;
924 
925         SetBkMode(data->backBufferDC, TRANSPARENT);
926 
927         HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
928         FillRect(data->backBufferDC, rect, blackBrush);
929         DeleteObject(blackBrush);
930 
931     #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
932         nhglyph2charcolor(data->map[i][j], &ch, &color);
933         OldFg = SetTextColor(hDC, nhcolor_to_RGB(color));
934     #else
935         ch = (char) data->map[i][j].ttychar;
936         color = (int) data->map[i][j].color;
937         if (((data->map[i][j].glyphflags & MG_PET) && iflags.hilite_pet)
938             || ((data->map[i][j].glyphflags & (MG_DETECT | MG_BW_LAVA))
939                 && iflags.use_inverse)) {
940             back_brush =
941                 CreateSolidBrush(nhcolor_to_RGB(CLR_GRAY));
942             FillRect(data->backBufferDC, rect, back_brush);
943             DeleteObject(back_brush);
944             switch (color) {
945             case CLR_GRAY:
946             case CLR_WHITE:
947                 OldFg = SetTextColor(
948                     data->backBufferDC, nhcolor_to_RGB(CLR_BLACK));
949                 break;
950             default:
951                 OldFg =
952                     SetTextColor(data->backBufferDC, nhcolor_to_RGB(color));
953             }
954         } else {
955             OldFg = SetTextColor(data->backBufferDC, nhcolor_to_RGB(color));
956         }
957     #endif
958         if (data->bUnicodeFont) {
959             wch = winos_ascii_to_wide(ch);
960             if (wch == 0x2591 || wch == 0x2592) {
961                 int intensity = 80;
962                 HBRUSH brush = CreateSolidBrush(RGB(intensity, intensity, intensity));
963                 FillRect(data->backBufferDC, rect, brush);
964                 DeleteObject(brush);
965                 intensity = (wch == 0x2591 ? 100 : 200);
966                 brush = CreateSolidBrush(RGB(intensity, intensity, intensity));
967                 RECT smallRect = { rect->left + 1, rect->top + 1,
968                                     rect->right - 1, rect->bottom - 1 };
969                 FillRect(data->backBufferDC, &smallRect, brush);
970                 DeleteObject(brush);
971             } else {
972                 DrawTextW(data->backBufferDC, &wch, 1, rect,
973                     DT_CENTER | DT_VCENTER | DT_NOPREFIX
974                     | DT_SINGLELINE);
975             }
976         } else {
977             DrawTextA(data->backBufferDC, &ch, 1, rect,
978                         DT_CENTER | DT_VCENTER | DT_NOPREFIX
979                             | DT_SINGLELINE);
980         }
981 
982         SetTextColor(data->backBufferDC, OldFg);
983     }
984 
985     if (i == data->xCur && j == data->yCur &&
986         (data->cursorOn || !win32_cursorblink)) {
987         int yCursor = (win32_cursorblink ? data->yBlinkCursor :
988                                            data->yNoBlinkCursor);
989         PatBlt(data->backBufferDC,
990                 rect->left, rect->bottom - yCursor,
991                 rect->right - rect->left,
992                 yCursor,
993                 DSTINVERT);
994     }
995 }
996 
setGlyph(PNHMapWindow data,int i,int j,const glyph_info * fg,const glyph_info * bg)997 static void setGlyph(PNHMapWindow data, int i, int j,
998                      const glyph_info *fg, const glyph_info *bg)
999 {
1000     if ((data->map[i][j].glyph != fg->glyph)
1001             || (data->bkmap[i][j].glyph != bg->glyph)
1002         || data->map[i][j].ttychar != fg->ttychar
1003         || data->map[i][j].color != fg->color
1004         || data->map[i][j].glyphflags != fg->glyphflags) {
1005         data->map[i][j] = *fg;
1006         data->bkmap[i][j] = *bg;
1007         data->locDirty[i][j] = TRUE;
1008         data->mapDirty = TRUE;
1009     }
1010 }
1011 
clearAll(PNHMapWindow data)1012 static void clearAll(PNHMapWindow data)
1013 {
1014     for (int x = 0; x < COLNO; x++)
1015         for (int y = 0; y < ROWNO; y++) {
1016             data->map[x][y] = nul_glyphinfo;
1017             data->bkmap[x][y] = nul_glyphinfo;
1018             data->locDirty[x][y] = TRUE;
1019         }
1020     data->mapDirty = TRUE;
1021 }
1022 
dirtyAll(PNHMapWindow data)1023 static void dirtyAll(PNHMapWindow data)
1024 {
1025     for (int i = 0; i < COLNO; i++)
1026         for (int j = 0; j < ROWNO; j++)
1027             data->locDirty[i][j] = TRUE;
1028     data->mapDirty = TRUE;
1029 }
1030 
dirty(PNHMapWindow data,int x,int y)1031 static void dirty(PNHMapWindow data, int x, int y)
1032 {
1033     data->locDirty[x][y] = TRUE;
1034     data->mapDirty = TRUE;
1035 }
1036 
1037 static void
paint(PNHMapWindow data,int i,int j)1038 paint(PNHMapWindow data, int i, int j)
1039 {
1040     RECT rect;
1041 
1042     rect.left = i * data->xBackTile;
1043     rect.top = j * data->yBackTile;
1044     rect.right = rect.left + data->xBackTile;
1045     rect.bottom = rect.top + data->yBackTile;
1046 
1047     if (data->bAsciiMode) {
1048         paintGlyph(data, i, j, &rect);
1049     } else {
1050         paintTile(data, i, j, &rect);
1051     }
1052 
1053     data->locDirty[i][j] = FALSE;
1054 }
1055 
1056 
1057 /* on WM_PAINT */
1058 void
onPaint(HWND hWnd)1059 onPaint(HWND hWnd)
1060 {
1061     PNHMapWindow data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1062 
1063     PAINTSTRUCT ps;
1064     HDC hFrontBufferDC = BeginPaint(hWnd, &ps);
1065 
1066     /* stretch back buffer onto front buffer window */
1067     int frontWidth = COLNO * data->xFrontTile;
1068     int frontHeight = ROWNO * data->yFrontTile;
1069 
1070     StretchBlt(hFrontBufferDC,
1071         data->map_orig.x - (data->xPos * data->xFrontTile),
1072         data->map_orig.y - (data->yPos * data->yFrontTile), frontWidth, frontHeight,
1073                 data->backBufferDC, 0, 0, data->backWidth, data->backHeight, SRCCOPY);
1074 
1075     EndPaint(hWnd, &ps);
1076 }
1077 
1078 /* on WM_VSCROLL */
1079 void
onMSNH_VScroll(HWND hWnd,WPARAM wParam,LPARAM lParam)1080 onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
1081 {
1082     PNHMapWindow data;
1083     SCROLLINFO si;
1084     int yNewPos;
1085     int yDelta;
1086 
1087     UNREFERENCED_PARAMETER(lParam);
1088 
1089     /* get window data */
1090     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1091 
1092     switch (LOWORD(wParam)) {
1093     /* User clicked shaft left of the scroll box. */
1094     case SB_PAGEUP:
1095         yNewPos = data->yPos - data->yPageSize;
1096         break;
1097 
1098     /* User clicked shaft right of the scroll box. */
1099     case SB_PAGEDOWN:
1100         yNewPos = data->yPos + data->yPageSize;
1101         break;
1102 
1103     /* User clicked the left arrow. */
1104     case SB_LINEUP:
1105         yNewPos = data->yPos - 1;
1106         break;
1107 
1108     /* User clicked the right arrow. */
1109     case SB_LINEDOWN:
1110         yNewPos = data->yPos + 1;
1111         break;
1112 
1113     /* User dragged the scroll box. */
1114     case SB_THUMBTRACK:
1115         yNewPos = HIWORD(wParam);
1116         break;
1117 
1118     default:
1119         yNewPos = data->yPos;
1120     }
1121 
1122     yNewPos = max(0, min(data->yMax, yNewPos));
1123     if (yNewPos == data->yPos)
1124         return;
1125 
1126     yDelta = yNewPos - data->yPos;
1127     data->yPos = yNewPos;
1128 
1129     ScrollWindowEx(hWnd, 0, -data->yFrontTile * yDelta, (CONST RECT *) NULL,
1130                    (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
1131                    SW_INVALIDATE | SW_ERASE);
1132 
1133     si.cbSize = sizeof(si);
1134     si.fMask = SIF_POS;
1135     si.nPos = data->yPos;
1136     SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
1137 }
1138 
1139 /* on WM_HSCROLL */
1140 void
onMSNH_HScroll(HWND hWnd,WPARAM wParam,LPARAM lParam)1141 onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
1142 {
1143     PNHMapWindow data;
1144     SCROLLINFO si;
1145     int xNewPos;
1146     int xDelta;
1147 
1148     UNREFERENCED_PARAMETER(lParam);
1149 
1150     /* get window data */
1151     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1152 
1153     switch (LOWORD(wParam)) {
1154     /* User clicked shaft left of the scroll box. */
1155     case SB_PAGEUP:
1156         xNewPos = data->xPos - data->xPageSize;
1157         break;
1158 
1159     /* User clicked shaft right of the scroll box. */
1160     case SB_PAGEDOWN:
1161         xNewPos = data->xPos + data->xPageSize;
1162         break;
1163 
1164     /* User clicked the left arrow. */
1165     case SB_LINEUP:
1166         xNewPos = data->xPos - 1;
1167         break;
1168 
1169     /* User clicked the right arrow. */
1170     case SB_LINEDOWN:
1171         xNewPos = data->xPos + 1;
1172         break;
1173 
1174     /* User dragged the scroll box. */
1175     case SB_THUMBTRACK:
1176         xNewPos = HIWORD(wParam);
1177         break;
1178 
1179     default:
1180         xNewPos = data->xPos;
1181     }
1182 
1183     xNewPos = max(0, min(data->xMax, xNewPos));
1184     if (xNewPos == data->xPos)
1185         return;
1186 
1187     xDelta = xNewPos - data->xPos;
1188     data->xPos = xNewPos;
1189 
1190     ScrollWindowEx(hWnd, -data->xFrontTile * xDelta, 0, (CONST RECT *) NULL,
1191                    (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
1192                    SW_INVALIDATE | SW_ERASE);
1193 
1194     si.cbSize = sizeof(si);
1195     si.fMask = SIF_POS;
1196     si.nPos = data->xPos;
1197     SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
1198 }
1199 
1200 /* map nethack map coordinates to the screen location */
1201 void
nhcoord2display(PNHMapWindow data,int x,int y,LPRECT lpOut)1202 nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut)
1203 {
1204     lpOut->left = (x - data->xPos) * data->xFrontTile + data->map_orig.x;
1205     lpOut->top = (y - data->yPos) * data->yFrontTile + data->map_orig.y;
1206     lpOut->right = lpOut->left + data->xFrontTile;
1207     lpOut->bottom = lpOut->top + data->yFrontTile;
1208 }
1209 
1210 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
1211 /* map glyph to character/color combination */
1212 void
nhglyph2charcolor(short g,uchar * ch,int * color)1213 nhglyph2charcolor(short g, uchar *ch, int *color)
1214 {
1215     int offset;
1216 #ifdef TEXTCOLOR
1217 
1218 #define zap_color(n) *color = iflags.use_color ? zapcolors[n] : NO_COLOR
1219 #define cmap_color(n) *color = iflags.use_color ? defsyms[n].color : NO_COLOR
1220 #define obj_color(n) \
1221     *color = iflags.use_color ? objects[n].oc_color : NO_COLOR
1222 #define mon_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR
1223 #define pet_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR
1224 #define warn_color(n) \
1225     *color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
1226 
1227 #else /* no text color */
1228 
1229 #define zap_color(n)
1230 #define cmap_color(n)
1231 #define obj_color(n)
1232 #define mon_color(n)
1233 #define pet_color(c)
1234 #define warn_color(c)
1235     *color = CLR_WHITE;
1236 #endif
1237 
1238     if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */
1239         *ch = showsyms[offset + SYM_OFF_W];
1240         warn_color(offset);
1241     } else if ((offset = (g - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */
1242         /* see swallow_to_glyph() in display.c */
1243         *ch = (uchar) showsyms[(S_sw_tl + (offset & 0x7)) + SYM_OFF_P];
1244         mon_color(offset >> 3);
1245     } else if ((offset = (g - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */
1246         /* see zapdir_to_glyph() in display.c */
1247         *ch = showsyms[(S_vbeam + (offset & 0x3)) + SYM_OFF_P];
1248         zap_color((offset >> 2));
1249     } else if ((offset = (g - GLYPH_CMAP_OFF)) >= 0) { /* cmap */
1250         *ch = showsyms[offset + SYM_OFF_P];
1251         cmap_color(offset);
1252     } else if ((offset = (g - GLYPH_OBJ_OFF)) >= 0) { /* object */
1253         *ch = showsyms[(int) objects[offset].oc_class + SYM_OFF_O];
1254         obj_color(offset);
1255     } else if ((offset = (g - GLYPH_BODY_OFF)) >= 0) { /* a corpse */
1256         *ch = showsyms[(int) objects[CORPSE].oc_class + SYM_OFF_O];
1257         mon_color(offset);
1258     } else if ((offset = (g - GLYPH_PET_OFF)) >= 0) { /* a pet */
1259         *ch = showsyms[(int) mons[offset].mlet + SYM_OFF_M];
1260         pet_color(offset);
1261     } else { /* a monster */
1262         *ch = showsyms[(int) mons[g].mlet + SYM_OFF_M];
1263         mon_color(g);
1264     }
1265     // end of wintty code
1266 }
1267 #endif
1268 
1269 /* map nethack color to RGB */
1270 COLORREF
nhcolor_to_RGB(int c)1271 nhcolor_to_RGB(int c)
1272 {
1273     if (c >= 0 && c < CLR_MAX)
1274         return GetNHApp()->regMapColors[c];
1275     return RGB(0x00, 0x00, 0x00);
1276 }
1277