1 /* NetHack 3.6 mhmap.c $NHDT-Date: 1435002695 2015/06/22 19:51:35 $ $NHDT-Branch: master $:$NHDT-Revision: 1.56 $ */
2 /* Copyright (C) 2001 by Alex Kompel */
3 /* NetHack may be freely redistributed. See license for details. */
16 #include "patchlevel.h"
18 #define NHMAP_FONT_NAME TEXT("Terminal")
19 #define NHMAP_TTFONT_NAME TEXT("Consolas")
20 #define MAXWINDOWTEXT 255
22 #define CURSOR_BLINK_INTERVAL 1000 // milliseconds
23 #define CURSOR_HEIGHT 2 // pixels
25 extern short glyph2tile[];
27 #define TILEBMP_X(ntile) \
28 ((ntile % GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_X)
29 #define TILEBMP_Y(ntile) \
30 ((ntile / GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_Y)
33 typedef struct mswin_nethack_map_window {
34 HWND hWnd; /* window */
36 int map[COLNO][ROWNO]; /* glyph map */
37 int bkmap[COLNO][ROWNO]; /* backround glyph map */
38 boolean mapDirty[COLNO][ROWNO]; /* dirty flag for map */
40 int mapMode; /* current map mode */
41 boolean bAsciiMode; /* switch ASCII/tiled mode */
42 boolean bFitToScreenMode; /* switch Fit map to screen mode on/off */
43 int xPos, yPos; /* scroll position */
44 int xPageSize, yPageSize; /* scroll page size */
45 int xMin, xMax, yMin, yMax; /* scroll range */
46 int xCur, yCur; /* position of the cursor */
47 int xFrontTile, yFrontTile; /* size of tile in front buffer in pixels */
48 int xBackTile, yBackTile; /* size of tile in back buffer in pixels */
49 POINT map_orig; /* map origin point */
51 HFONT hMapFont; /* font for ASCII mode */
52 boolean bUnicodeFont; /* font supports unicode page 437 */
54 int tileWidth; /* width of tile in pixels at 96 dpi */
55 int tileHeight; /* height of tile in pixels at 96 dpi */
56 double backScale; /* scaling from source to back buffer */
57 double frontScale; /* scaling from back to front */
58 double monitorScale; /* from 96dpi to monitor dpi*/
61 int yNoBlinkCursor; /* non-blinking cursor height inback buffer
63 int yBlinkCursor; /* blinking cursor height inback buffer
66 int backWidth; /* back buffer width */
67 int backHeight; /* back buffer height */
68 HBITMAP hBackBuffer; /* back buffe bitmap */
69 HDC backBufferDC; /* back buffer drawing context */
71 HDC tileDC; /* tile drawing context */
73 } NHMapWindow, *PNHMapWindow;
75 static TCHAR szNHMapWindowClass[] = TEXT("MSNethackMapWndClass");
76 LRESULT CALLBACK MapWndProc(HWND, UINT, WPARAM, LPARAM);
77 static void register_map_window_class(void);
78 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
79 static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
80 static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
81 static void onPaint(HWND hWnd);
82 static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
83 static void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut);
84 static void paint(PNHMapWindow data, int i, int j);
85 static void dirtyAll(PNHMapWindow data);
86 static void dirty(PNHMapWindow data, int i, int j);
87 static void setGlyph(PNHMapWindow data, int i, int j, int fg, int bg);
88 static void clearAll(PNHMapWindow data);
90 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
91 static void nhglyph2charcolor(short glyph, uchar *ch, int *color);
93 extern boolean win32_cursorblink; /* from sys\winnt\winnt.c */
96 mswin_init_map_window()
98 static int run_once = 0;
103 register_map_window_class();
107 /* get window position */
108 if (GetNHApp()->bAutoLayout) {
109 SetRect(&rt, 0, 0, 0, 0);
111 mswin_get_window_placement(NHW_MAP, &rt);
114 /* create map window object */
116 szNHMapWindowClass, /* registered class name */
117 NULL, /* window name */
118 WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_CLIPSIBLINGS
119 | WS_SIZEBOX, /* window style */
120 rt.left, /* horizontal position of window */
121 rt.top, /* vertical position of window */
122 rt.right - rt.left, /* window width */
123 rt.bottom - rt.top, /* window height */
124 GetNHApp()->hMainWnd, /* handle to parent or owner window */
125 NULL, /* menu handle or child identifier */
126 GetNHApp()->hApp, /* handle to application instance */
127 NULL); /* window-creation data */
129 panic("Cannot create map window");
132 /* Set window caption */
133 SetWindowText(hWnd, "Map");
135 mswin_apply_window_style(hWnd);
137 /* set cursor blink timer */
138 SetTimer(hWnd, 0, CURSOR_BLINK_INTERVAL, NULL);
144 mswin_map_stretch(HWND hWnd, LPSIZE map_size, BOOL redraw)
146 /* check arguments */
147 if (!IsWindow(hWnd) || !map_size || map_size->cx <= 0
148 || map_size->cy <= 0)
151 PNHMapWindow data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
153 /* calculate window size */
155 GetClientRect(hWnd, &client_rt);
158 wnd_size.cx = client_rt.right - client_rt.left;
159 wnd_size.cy = client_rt.bottom - client_rt.top;
161 // calculate back buffer scale
162 data->monitorScale = win10_monitor_scale(hWnd);
164 boolean bText = data->bAsciiMode ||
165 (u.uz.dlevel != 0 && Is_rogue_level(&u.uz));
167 if (bText && !data->bFitToScreenMode)
168 data->backScale = data->monitorScale;
170 data->backScale = 1.0;
172 /* set back buffer tile size */
173 if (bText && data->bFitToScreenMode) {
174 data->xBackTile = wnd_size.cx / COLNO;
175 data->yBackTile = wnd_size.cy / ROWNO;
176 data->yBackTile = max(data->yBackTile, 12);
178 data->xBackTile = (int)(data->tileWidth * data->backScale);
179 data->yBackTile = (int)(data->tileHeight * data->backScale);
185 ZeroMemory(&lgfnt, sizeof(lgfnt));
186 if (data->bFitToScreenMode) {
187 lgfnt.lfHeight = -data->yBackTile; // height of font
188 lgfnt.lfWidth = 0; // average character width
190 lgfnt.lfHeight = -data->yBackTile; // height of font
191 lgfnt.lfWidth = -data->xBackTile; // average character width
193 lgfnt.lfEscapement = 0; // angle of escapement
194 lgfnt.lfOrientation = 0; // base-line orientation angle
195 lgfnt.lfWeight = FW_NORMAL; // font weight
196 lgfnt.lfItalic = FALSE; // italic attribute option
197 lgfnt.lfUnderline = FALSE; // underline attribute option
198 lgfnt.lfStrikeOut = FALSE; // strikeout attribute option
199 lgfnt.lfCharSet = mswin_charset(); // character set identifier
200 lgfnt.lfOutPrecision = OUT_DEFAULT_PRECIS; // output precision
201 lgfnt.lfClipPrecision = CLIP_DEFAULT_PRECIS; // clipping precision
202 if (data->bFitToScreenMode) {
203 lgfnt.lfQuality = ANTIALIASED_QUALITY; // output quality
205 lgfnt.lfQuality = NONANTIALIASED_QUALITY; // output quality
207 if (iflags.wc_font_map && *iflags.wc_font_map) {
208 lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family
209 NH_A2W(iflags.wc_font_map, lgfnt.lfFaceName, LF_FACESIZE);
211 if (!data->bFitToScreenMode) {
212 lgfnt.lfPitchAndFamily = FIXED_PITCH; // pitch and family
213 NH_A2W(NHMAP_FONT_NAME, lgfnt.lfFaceName, LF_FACESIZE);
215 lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family
216 NH_A2W(NHMAP_TTFONT_NAME, lgfnt.lfFaceName, LF_FACESIZE);
220 TEXTMETRIC textMetrics;
228 font = CreateFontIndirect(&lgfnt);
230 SelectObject(data->backBufferDC, font);
232 GetTextMetrics(data->backBufferDC, &textMetrics);
234 if (!data->bFitToScreenMode)
237 if ((textMetrics.tmHeight > data->yBackTile ||
238 textMetrics.tmAveCharWidth > data->xBackTile) &&
239 lgfnt.lfHeight < -MIN_FONT_HEIGHT) {
248 DeleteObject(data->hMapFont);
250 data->hMapFont = font;
252 data->bUnicodeFont = winos_font_support_cp437(data->hMapFont);
254 // set tile size to match font metrics
256 data->xBackTile = textMetrics.tmAveCharWidth;
257 data->yBackTile = textMetrics.tmHeight;
261 int backWidth = COLNO * data->xBackTile;
262 int backHeight = ROWNO * data->yBackTile;
264 /* create back buffer */
266 if (data->backWidth != backWidth || data->backHeight != backHeight) {
268 HDC frontBufferDC = GetDC(hWnd);
269 HBITMAP hBackBuffer = CreateCompatibleBitmap(frontBufferDC, backWidth, backHeight);
270 ReleaseDC(hWnd, frontBufferDC);
272 if (data->hBackBuffer != NULL) {
273 SelectBitmap(data->backBufferDC, hBackBuffer);
274 DeleteObject(data->hBackBuffer);
277 data->backWidth = backWidth;
278 data->backHeight = backHeight;
280 SelectBitmap(data->backBufferDC, hBackBuffer);
281 data->hBackBuffer = hBackBuffer;
284 /* calculate front buffer tile size */
286 if (wnd_size.cx > 0 && wnd_size.cy > 0 && !bText && data->bFitToScreenMode) {
287 double windowAspectRatio =
288 (double) wnd_size.cx / (double) wnd_size.cy;
290 double backAspectRatio =
291 (double) data->backWidth / (double) data->backHeight;
293 if (windowAspectRatio > backAspectRatio)
294 data->frontScale = (double) wnd_size.cy / (double) data->backHeight;
296 data->frontScale = (double) wnd_size.cx / (double) data->backWidth;
301 data->frontScale = 1.0;
303 data->frontScale = data->monitorScale;
308 data->xFrontTile = (int) ((double) data->xBackTile * data->frontScale);
309 data->yFrontTile = (int) ((double) data->yBackTile * data->frontScale);
311 /* calcuate ASCII cursor height */
312 data->yBlinkCursor = (int) ((double) CURSOR_HEIGHT * data->backScale);
313 data->yNoBlinkCursor = data->yBackTile;
315 /* set map origin point */
317 max(0, client_rt.left + (wnd_size.cx - data->xFrontTile * COLNO) / 2);
319 max(0, client_rt.top + (wnd_size.cy - data->yFrontTile * ROWNO) / 2);
321 data->map_orig.x -= data->map_orig.x % data->xFrontTile;
322 data->map_orig.y -= data->map_orig.y % data->yFrontTile;
324 // Set horizontal scroll
326 data->xPageSize = min(COLNO, wnd_size.cx / data->xFrontTile);
328 GetNHApp()->bNoHScroll = (data->xPageSize == COLNO);
331 data->xMax = COLNO - data->xPageSize;
332 data->xPos = max(0, min(data->xMax, u.ux - (data->xPageSize / 2)));
336 si.cbSize = sizeof(si);
337 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
338 si.nMin = data->xMin;
339 si.nMax = data->xMax;
341 si.nPos = data->xPos;
342 SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
344 data->yPageSize = min(ROWNO, wnd_size.cy / data->yFrontTile);
346 GetNHApp()->bNoVScroll = (data->yPageSize == ROWNO);
349 data->yMax = ROWNO - data->yPageSize;
350 data->yPos = max(0, min(data->yMax, u.uy - (data->yPageSize / 2)));
352 si.cbSize = sizeof(si);
353 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
354 si.nMin = data->yMin;
355 si.nMax = data->yMax;
357 si.nPos = data->yPos;
358 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
360 mswin_cliparound(data->xCur, data->yCur);
364 InvalidateRect(hWnd, NULL, TRUE);
370 mswin_map_mode(HWND hWnd, int mode)
376 data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
377 if (mode == data->mapMode)
380 oldMode = data->mapMode;
381 data->mapMode = mode;
383 switch (data->mapMode) {
384 case MAP_MODE_ASCII4x6:
385 data->bAsciiMode = TRUE;
386 data->bFitToScreenMode = FALSE;
388 data->tileHeight = 6;
391 case MAP_MODE_ASCII6x8:
392 data->bAsciiMode = TRUE;
393 data->bFitToScreenMode = FALSE;
395 data->tileHeight = 8;
398 case MAP_MODE_ASCII8x8:
399 data->bAsciiMode = TRUE;
400 data->bFitToScreenMode = FALSE;
402 data->tileHeight = 8;
405 case MAP_MODE_ASCII16x8:
406 data->bAsciiMode = TRUE;
407 data->bFitToScreenMode = FALSE;
408 data->tileWidth = 16;
409 data->tileHeight = 8;
412 case MAP_MODE_ASCII7x12:
413 data->bAsciiMode = TRUE;
414 data->bFitToScreenMode = FALSE;
416 data->tileHeight = 12;
419 case MAP_MODE_ASCII8x12:
420 data->bAsciiMode = TRUE;
421 data->bFitToScreenMode = FALSE;
423 data->tileHeight = 12;
426 case MAP_MODE_ASCII16x12:
427 data->bAsciiMode = TRUE;
428 data->bFitToScreenMode = FALSE;
429 data->tileWidth = 16;
430 data->tileHeight = 12;
433 case MAP_MODE_ASCII12x16:
434 data->bAsciiMode = TRUE;
435 data->bFitToScreenMode = FALSE;
436 data->tileWidth = 12;
437 data->tileHeight = 16;
440 case MAP_MODE_ASCII10x18:
441 data->bAsciiMode = TRUE;
442 data->bFitToScreenMode = FALSE;
443 data->tileWidth = 10;
444 data->tileHeight = 18;
447 case MAP_MODE_ASCII_FIT_TO_SCREEN:
448 data->bAsciiMode = TRUE;
449 data->bFitToScreenMode = TRUE;
450 data->tileWidth = 12;
451 data->tileHeight = 16;
454 case MAP_MODE_TILES_FIT_TO_SCREEN:
455 data->bAsciiMode = FALSE;
456 data->bFitToScreenMode = TRUE;
457 data->tileWidth = GetNHApp()->mapTile_X;
458 data->tileHeight = GetNHApp()->mapTile_Y;
463 data->bAsciiMode = FALSE;
464 data->bFitToScreenMode = FALSE;
465 data->tileWidth = GetNHApp()->mapTile_X;
466 data->tileHeight = GetNHApp()->mapTile_Y;
470 mapSize.cx = data->tileWidth * COLNO;
471 mapSize.cy = data->tileHeight * ROWNO;
473 mswin_map_stretch(hWnd, &mapSize, TRUE);
475 mswin_update_inventory(); /* for perm_invent to hide/show tiles */
480 /* register window class for map window */
482 register_map_window_class()
485 ZeroMemory(&wcex, sizeof(wcex));
488 wcex.style = CS_NOCLOSE | CS_DBLCLKS;
489 wcex.lpfnWndProc = (WNDPROC) MapWndProc;
492 wcex.hInstance = GetNHApp()->hApp;
494 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
496 CreateSolidBrush(RGB(0, 0, 0)); /* set backgroup here */
497 wcex.lpszMenuName = NULL;
498 wcex.lpszClassName = szNHMapWindowClass;
500 if (!RegisterClass(&wcex)) {
501 panic("cannot register Map window class");
505 /* map window procedure */
507 MapWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
511 data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
514 onCreate(hWnd, wParam, lParam);
517 case WM_MSNH_COMMAND:
518 onMSNHCommand(hWnd, wParam, lParam);
526 /* transfer focus back to the main window */
527 SetFocus(GetNHApp()->hMainWnd);
531 onMSNH_HScroll(hWnd, wParam, lParam);
535 onMSNH_VScroll(hWnd, wParam, lParam);
542 if (data->bFitToScreenMode) {
543 size.cx = LOWORD(lParam);
544 size.cy = HIWORD(lParam);
546 /* mapping factor is unchaged we just need to adjust scroll bars
548 size.cx = data->xFrontTile * COLNO;
549 size.cy = data->yFrontTile * ROWNO;
551 mswin_map_stretch(hWnd, &size, TRUE);
553 /* update window placement */
554 GetWindowRect(hWnd, &rt);
555 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
556 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
557 mswin_update_window_placement(NHW_MAP, &rt);
562 GetWindowRect(hWnd, &rt);
563 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
564 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
565 mswin_update_window_placement(NHW_MAP, &rt);
571 max(0, min(COLNO, data->xPos
572 + (LOWORD(lParam) - data->map_orig.x)
573 / data->xFrontTile)),
574 max(0, min(ROWNO, data->yPos
575 + (HIWORD(lParam) - data->map_orig.y)
576 / data->yFrontTile)));
579 case WM_LBUTTONDBLCLK:
582 max(0, min(COLNO, data->xPos
583 + (LOWORD(lParam) - data->map_orig.x)
584 / data->xFrontTile)),
585 max(0, min(ROWNO, data->yPos
586 + (HIWORD(lParam) - data->map_orig.y)
587 / data->yFrontTile)));
592 DeleteObject(data->hMapFont);
593 if (data->hBackBuffer)
594 DeleteBitmap(data->hBackBuffer);
595 if (data->backBufferDC)
596 DeleteDC(data->backBufferDC);
598 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
602 data->cursorOn = !data->cursorOn;
603 dirty(data, data->xCur, data->yCur);
606 case WM_DPICHANGED: {
608 GetWindowRect(hWnd, &rt);
609 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT)&rt);
610 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT)&rt) + 1);
611 mswin_update_window_placement(NHW_MAP, &rt);
615 return DefWindowProc(hWnd, message, wParam, lParam);
622 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
626 data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
628 case MSNH_MSG_PRINT_GLYPH: {
629 PMSNHMsgPrintGlyph msg_data = (PMSNHMsgPrintGlyph) lParam;
630 setGlyph(data, msg_data->x, msg_data->y,
631 msg_data->glyph, msg_data->bkglyph);
634 case MSNH_MSG_CLIPAROUND: {
635 PMSNHMsgClipAround msg_data = (PMSNHMsgClipAround) lParam;
637 BOOL scroll_x, scroll_y;
638 int mcam = iflags.wc_scroll_margin;
640 /* calculate if you should clip around */
642 !GetNHApp()->bNoHScroll
643 && (msg_data->x < (data->xPos + mcam)
644 || msg_data->x > (data->xPos + data->xPageSize - mcam));
646 !GetNHApp()->bNoVScroll
647 && (msg_data->y < (data->yPos + mcam)
648 || msg_data->y > (data->yPos + data->yPageSize - mcam));
650 mcam += iflags.wc_scroll_amount - 1;
651 /* get page size and center horizontally on x-position */
653 if (data->xPageSize <= 2 * mcam) {
654 x = max(0, min(COLNO, msg_data->x - data->xPageSize / 2));
655 } else if (msg_data->x < data->xPos + data->xPageSize / 2) {
656 x = max(0, min(COLNO, msg_data->x - mcam));
658 x = max(0, min(COLNO, msg_data->x - data->xPageSize + mcam));
660 SendMessage(hWnd, WM_HSCROLL, (WPARAM) MAKELONG(SB_THUMBTRACK, x),
664 /* get page size and center vertically on y-position */
666 if (data->yPageSize <= 2 * mcam) {
667 y = max(0, min(ROWNO, msg_data->y - data->yPageSize / 2));
668 } else if (msg_data->y < data->yPos + data->yPageSize / 2) {
669 y = max(0, min(ROWNO, msg_data->y - mcam));
671 y = max(0, min(ROWNO, msg_data->y - data->yPageSize + mcam));
673 SendMessage(hWnd, WM_VSCROLL, (WPARAM) MAKELONG(SB_THUMBTRACK, y),
678 case MSNH_MSG_CLEAR_WINDOW:
682 case MSNH_MSG_CURSOR: {
683 PMSNHMsgCursor msg_data = (PMSNHMsgCursor) lParam;
685 if (data->xCur != msg_data->x || data->yCur != msg_data->y) {
687 dirty(data, data->xCur, data->yCur);
688 dirty(data, msg_data->x, msg_data->y);
690 data->xCur = msg_data->x;
691 data->yCur = msg_data->y;
696 case MSNH_MSG_GETTEXT: {
697 PMSNHMsgGetText msg_data = (PMSNHMsgGetText) lParam;
705 for (row = 0; row < ROWNO; row++) {
706 for (col = 0; col < COLNO; col++) {
707 if (index >= msg_data->max_size)
709 if (data->map[col][row] == NO_GLYPH) {
712 (void) mapglyph(data->map[col][row], &mgch, &color,
715 msg_data->buffer[index] = mgch;
718 if (index >= msg_data->max_size - 1)
720 msg_data->buffer[index++] = '\r';
721 msg_data->buffer[index++] = '\n';
725 case MSNH_MSG_RANDOM_INPUT:
726 nhassert(0); // unexpected
729 } /* end switch(wParam) */
734 onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
738 UNREFERENCED_PARAMETER(wParam);
739 UNREFERENCED_PARAMETER(lParam);
741 /* set window data */
742 data = (PNHMapWindow) malloc(sizeof(NHMapWindow));
744 panic("out of memory");
746 ZeroMemory(data, sizeof(NHMapWindow));
750 data->bAsciiMode = FALSE;
751 data->cursorOn = TRUE;
753 data->xFrontTile = GetNHApp()->mapTile_X;
754 data->yFrontTile = GetNHApp()->mapTile_Y;
755 data->tileWidth = GetNHApp()->mapTile_X;
756 data->tileHeight = GetNHApp()->mapTile_Y;
758 HDC hDC = GetDC(hWnd);
759 data->backBufferDC = CreateCompatibleDC(hDC);
760 data->tileDC = CreateCompatibleDC(hDC);
761 ReleaseDC(hWnd, hDC);
763 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
770 paintTile(PNHMapWindow data, int i, int j, RECT * rect)
782 glyph = data->map[i][j];
783 bkglyph = data->bkmap[i][j];
785 if (glyph == NO_GLYPH && bkglyph == NO_GLYPH) {
786 HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
787 FillRect(data->backBufferDC, rect, blackBrush);
788 DeleteObject(blackBrush);
791 if (bkglyph != NO_GLYPH) {
792 ntile = glyph2tile[bkglyph];
793 t_x = TILEBMP_X(ntile);
794 t_y = TILEBMP_Y(ntile);
796 StretchBlt(data->backBufferDC, rect->left, rect->top,
797 data->xBackTile, data->yBackTile, data->tileDC,
798 t_x, t_y, GetNHApp()->mapTile_X,
799 GetNHApp()->mapTile_Y, SRCCOPY);
803 if ((glyph != NO_GLYPH) && (glyph != bkglyph)) {
804 ntile = glyph2tile[glyph];
805 t_x = TILEBMP_X(ntile);
806 t_y = TILEBMP_Y(ntile);
809 (*GetNHApp()->lpfnTransparentBlt)(
810 data->backBufferDC, rect->left, rect->top,
811 data->xBackTile, data->yBackTile, data->tileDC, t_x,
812 t_y, GetNHApp()->mapTile_X,
813 GetNHApp()->mapTile_Y, TILE_BK_COLOR);
815 StretchBlt(data->backBufferDC, rect->left, rect->top,
816 data->xBackTile, data->yBackTile, data->tileDC,
817 t_x, t_y, GetNHApp()->mapTile_X,
818 GetNHApp()->mapTile_Y, SRCCOPY);
825 /* rely on NetHack core helper routine */
826 (void) mapglyph(data->map[i][j], &mgch, &color, &special,
828 if ((glyph != NO_GLYPH) && (special & MG_PET)
830 if ((glyph != NO_GLYPH) && glyph_is_pet(glyph)
832 && iflags.wc_hilite_pet) {
833 /* apply pet mark transparently over
836 HBITMAP bmPetMarkOld;
838 /* this is DC for petmark bitmap */
839 hdcPetMark = CreateCompatibleDC(data->backBufferDC);
841 SelectObject(hdcPetMark, GetNHApp()->bmpPetMark);
843 (*GetNHApp()->lpfnTransparentBlt)(
844 data->backBufferDC, rect->left, rect->top,
845 data->xBackTile, data->yBackTile, hdcPetMark, 0, 0,
846 TILE_X, TILE_Y, TILE_BK_COLOR);
847 SelectObject(hdcPetMark, bmPetMarkOld);
848 DeleteDC(hdcPetMark);
851 if ((glyph != NO_GLYPH) && (special & MG_OBJPILE)
852 && iflags.hilite_pile) {
853 /* apply pilemark transparently over other image */
855 HBITMAP bmPileMarkOld;
857 /* this is DC for pilemark bitmap */
858 hdcPileMark = CreateCompatibleDC(data->backBufferDC);
859 bmPileMarkOld = SelectObject(hdcPileMark,
860 GetNHApp()->bmpPileMark);
862 (*GetNHApp()->lpfnTransparentBlt)(
863 data->backBufferDC, rect->left, rect->top,
864 data->xBackTile, data->yBackTile, hdcPileMark, 0, 0,
865 TILE_X, TILE_Y, TILE_BK_COLOR);
866 SelectObject(hdcPileMark, bmPileMarkOld);
867 DeleteDC(hdcPileMark);
871 if (i == data->xCur && j == data->yCur &&
872 (data->cursorOn || !win32_cursorblink))
873 DrawFocusRect(data->backBufferDC, rect);
878 paintGlyph(PNHMapWindow data, int i, int j, RECT * rect)
880 if (data->map[i][j] >= 0) {
890 SetBkMode(data->backBufferDC, TRANSPARENT);
892 HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
893 FillRect(data->backBufferDC, rect, blackBrush);
894 DeleteObject(blackBrush);
896 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
897 nhglyph2charcolor(data->map[i][j], &ch, &color);
898 OldFg = SetTextColor(hDC, nhcolor_to_RGB(color));
900 /* rely on NetHack core helper routine */
901 (void) mapglyph(data->map[i][j], &mgch, &color,
904 if (((special & MG_PET) && iflags.hilite_pet)
905 || ((special & (MG_DETECT | MG_BW_LAVA))
906 && iflags.use_inverse)) {
908 CreateSolidBrush(nhcolor_to_RGB(CLR_GRAY));
909 FillRect(data->backBufferDC, rect, back_brush);
910 DeleteObject(back_brush);
914 OldFg = SetTextColor(
915 data->backBufferDC, nhcolor_to_RGB(CLR_BLACK));
919 SetTextColor(data->backBufferDC, nhcolor_to_RGB(color));
922 OldFg = SetTextColor(data->backBufferDC, nhcolor_to_RGB(color));
925 if (data->bUnicodeFont) {
926 wch = winos_ascii_to_wide(ch);
927 if (wch == 0x2591 || wch == 0x2592) {
929 HBRUSH brush = CreateSolidBrush(RGB(level, level, level));
930 FillRect(data->backBufferDC, rect, brush);
932 level = (wch == 0x2591 ? 100 : 200);
933 brush = CreateSolidBrush(RGB(level, level, level));
934 RECT smallRect = { rect->left + 1, rect->top + 1,
935 rect->right - 1, rect->bottom - 1 };
936 FillRect(data->backBufferDC, &smallRect, brush);
939 DrawTextW(data->backBufferDC, &wch, 1, rect,
940 DT_CENTER | DT_VCENTER | DT_NOPREFIX
944 DrawTextA(data->backBufferDC, &ch, 1, rect,
945 DT_CENTER | DT_VCENTER | DT_NOPREFIX
949 SetTextColor(data->backBufferDC, OldFg);
952 if (i == data->xCur && j == data->yCur &&
953 (data->cursorOn || !win32_cursorblink)) {
954 int yCursor = (win32_cursorblink ? data->yBlinkCursor :
955 data->yNoBlinkCursor);
956 PatBlt(data->backBufferDC,
957 rect->left, rect->bottom - yCursor,
958 rect->right - rect->left,
964 static void setGlyph(PNHMapWindow data, int i, int j, int fg, int bg)
966 if ((data->map[i][j] != fg) || (data->bkmap[i][j] != bg)) {
967 data->map[i][j] = fg;
968 data->bkmap[i][j] = bg;
969 data->mapDirty[i][j] = TRUE;
972 nhcoord2display(data, i, j, &rect);
973 InvalidateRect(data->hWnd, &rect, FALSE);
977 static void clearAll(PNHMapWindow data)
979 for (int x = 0; x < COLNO; x++)
980 for (int y = 0; y < ROWNO; y++) {
981 data->map[x][y] = NO_GLYPH;
982 data->bkmap[x][y] = NO_GLYPH;
983 data->mapDirty[x][y] = TRUE;
985 InvalidateRect(data->hWnd, NULL, FALSE);
988 static void dirtyAll(PNHMapWindow data)
990 for (int i = 0; i < COLNO; i++)
991 for (int j = 0; j < ROWNO; j++)
992 data->mapDirty[i][j] = TRUE;
994 InvalidateRect(data->hWnd, NULL, FALSE);
997 static void dirty(PNHMapWindow data, int x, int y)
999 data->mapDirty[x][y] = TRUE;
1002 nhcoord2display(data, data->xCur, data->yCur, &rt);
1004 InvalidateRect(data->hWnd, &rt, FALSE);
1008 paint(PNHMapWindow data, int i, int j)
1012 rect.left = i * data->xBackTile;
1013 rect.top = j * data->yBackTile;
1014 rect.right = rect.left + data->xBackTile;
1015 rect.bottom = rect.top + data->yBackTile;
1017 if (data->bAsciiMode || Is_rogue_level(&u.uz)) {
1018 paintGlyph(data, i, j, &rect);
1020 paintTile(data, i, j, &rect);
1023 data->mapDirty[i][j] = FALSE;
1031 PNHMapWindow data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1033 /* update back buffer */
1034 HBITMAP savedBitmap = SelectObject(data->tileDC, GetNHApp()->bmpMapTiles);
1036 for (int i = 0; i < COLNO; i++)
1037 for (int j = 0; j < ROWNO; j++)
1038 if (data->mapDirty[i][j])
1041 SelectObject(data->tileDC, savedBitmap);
1044 HDC hFrontBufferDC = BeginPaint(hWnd, &ps);
1046 /* stretch back buffer onto front buffer window */
1047 int frontWidth = COLNO * data->xFrontTile;
1048 int frontHeight = ROWNO * data->yFrontTile;
1050 StretchBlt(hFrontBufferDC,
1051 data->map_orig.x - (data->xPos * data->xFrontTile),
1052 data->map_orig.y - (data->yPos * data->yFrontTile), frontWidth, frontHeight,
1053 data->backBufferDC, 0, 0, data->backWidth, data->backHeight, SRCCOPY);
1055 EndPaint(hWnd, &ps);
1060 onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
1067 UNREFERENCED_PARAMETER(lParam);
1069 /* get window data */
1070 data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1072 switch (LOWORD(wParam)) {
1073 /* User clicked shaft left of the scroll box. */
1075 yNewPos = data->yPos - data->yPageSize;
1078 /* User clicked shaft right of the scroll box. */
1080 yNewPos = data->yPos + data->yPageSize;
1083 /* User clicked the left arrow. */
1085 yNewPos = data->yPos - 1;
1088 /* User clicked the right arrow. */
1090 yNewPos = data->yPos + 1;
1093 /* User dragged the scroll box. */
1095 yNewPos = HIWORD(wParam);
1099 yNewPos = data->yPos;
1102 yNewPos = max(0, min(data->yMax, yNewPos));
1103 if (yNewPos == data->yPos)
1106 yDelta = yNewPos - data->yPos;
1107 data->yPos = yNewPos;
1109 ScrollWindowEx(hWnd, 0, -data->yFrontTile * yDelta, (CONST RECT *) NULL,
1110 (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
1111 SW_INVALIDATE | SW_ERASE);
1113 si.cbSize = sizeof(si);
1115 si.nPos = data->yPos;
1116 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
1121 onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
1128 UNREFERENCED_PARAMETER(lParam);
1130 /* get window data */
1131 data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1133 switch (LOWORD(wParam)) {
1134 /* User clicked shaft left of the scroll box. */
1136 xNewPos = data->xPos - data->xPageSize;
1139 /* User clicked shaft right of the scroll box. */
1141 xNewPos = data->xPos + data->xPageSize;
1144 /* User clicked the left arrow. */
1146 xNewPos = data->xPos - 1;
1149 /* User clicked the right arrow. */
1151 xNewPos = data->xPos + 1;
1154 /* User dragged the scroll box. */
1156 xNewPos = HIWORD(wParam);
1160 xNewPos = data->xPos;
1163 xNewPos = max(0, min(data->xMax, xNewPos));
1164 if (xNewPos == data->xPos)
1167 xDelta = xNewPos - data->xPos;
1168 data->xPos = xNewPos;
1170 ScrollWindowEx(hWnd, -data->xFrontTile * xDelta, 0, (CONST RECT *) NULL,
1171 (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
1172 SW_INVALIDATE | SW_ERASE);
1174 si.cbSize = sizeof(si);
1176 si.nPos = data->xPos;
1177 SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
1180 /* map nethack map coordinates to the screen location */
1182 nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut)
1184 lpOut->left = (x - data->xPos) * data->xFrontTile + data->map_orig.x;
1185 lpOut->top = (y - data->yPos) * data->yFrontTile + data->map_orig.y;
1186 lpOut->right = lpOut->left + data->xFrontTile;
1187 lpOut->bottom = lpOut->top + data->yFrontTile;
1190 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
1191 /* map glyph to character/color combination */
1193 nhglyph2charcolor(short g, uchar *ch, int *color)
1198 #define zap_color(n) *color = iflags.use_color ? zapcolors[n] : NO_COLOR
1199 #define cmap_color(n) *color = iflags.use_color ? defsyms[n].color : NO_COLOR
1200 #define obj_color(n) \
1201 *color = iflags.use_color ? objects[n].oc_color : NO_COLOR
1202 #define mon_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR
1203 #define pet_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR
1204 #define warn_color(n) \
1205 *color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
1207 #else /* no text color */
1209 #define zap_color(n)
1210 #define cmap_color(n)
1211 #define obj_color(n)
1212 #define mon_color(n)
1213 #define pet_color(c)
1214 #define warn_color(c)
1218 if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */
1219 *ch = showsyms[offset + SYM_OFF_W];
1221 } else if ((offset = (g - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */
1222 /* see swallow_to_glyph() in display.c */
1223 *ch = (uchar) showsyms[(S_sw_tl + (offset & 0x7)) + SYM_OFF_P];
1224 mon_color(offset >> 3);
1225 } else if ((offset = (g - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */
1226 /* see zapdir_to_glyph() in display.c */
1227 *ch = showsyms[(S_vbeam + (offset & 0x3)) + SYM_OFF_P];
1228 zap_color((offset >> 2));
1229 } else if ((offset = (g - GLYPH_CMAP_OFF)) >= 0) { /* cmap */
1230 *ch = showsyms[offset + SYM_OFF_P];
1232 } else if ((offset = (g - GLYPH_OBJ_OFF)) >= 0) { /* object */
1233 *ch = showsyms[(int) objects[offset].oc_class + SYM_OFF_O];
1235 } else if ((offset = (g - GLYPH_BODY_OFF)) >= 0) { /* a corpse */
1236 *ch = showsyms[(int) objects[CORPSE].oc_class + SYM_OFF_O];
1238 } else if ((offset = (g - GLYPH_PET_OFF)) >= 0) { /* a pet */
1239 *ch = showsyms[(int) mons[offset].mlet + SYM_OFF_M];
1241 } else { /* a monster */
1242 *ch = showsyms[(int) mons[g].mlet + SYM_OFF_M];
1245 // end of wintty code
1249 /* map nethack color to RGB */
1251 nhcolor_to_RGB(int c)
1253 if (c >= 0 && c < CLR_MAX)
1254 return GetNHApp()->regMapColors[c];
1255 return RGB(0x00, 0x00, 0x00);