OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / win / win32 / mhmap.c
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. */
4
5 #include "winMS.h"
6 #include "resource.h"
7 #include "mhmap.h"
8 #include "mhmsg.h"
9 #include "mhinput.h"
10 #include "mhfont.h"
11
12 #include "color.h"
13 #include "patchlevel.h"
14
15 #define NHMAP_FONT_NAME TEXT("Terminal")
16 #define MAXWINDOWTEXT 255
17
18 extern short glyph2tile[];
19
20 #define TILEBMP_X(ntile) ((ntile % GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_X)
21 #define TILEBMP_Y(ntile) ((ntile / GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_Y)
22
23 /* map window data */
24 typedef struct mswin_nethack_map_window {
25     int map[COLNO][ROWNO]; /* glyph map */
26     int bkmap[COLNO][ROWNO]; /* backround glyph map */
27
28     int mapMode;              /* current map mode */
29     boolean bAsciiMode;       /* switch ASCII/tiled mode */
30     boolean bFitToScreenMode; /* switch Fit map to screen mode on/off */
31     int xPos, yPos;           /* scroll position */
32     int xPageSize, yPageSize; /* scroll page size */
33     int xCur, yCur;           /* position of the cursor */
34     int xScrTile, yScrTile;   /* size of display tile */
35     POINT map_orig;           /* map origin point */
36
37     HFONT hMapFont;           /* font for ASCII mode */
38 } NHMapWindow, *PNHMapWindow;
39
40 static TCHAR szNHMapWindowClass[] = TEXT("MSNethackMapWndClass");
41 LRESULT CALLBACK MapWndProc(HWND, UINT, WPARAM, LPARAM);
42 static void register_map_window_class(void);
43 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
44 static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
45 static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
46 static void onPaint(HWND hWnd);
47 static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
48 static void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut);
49 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
50 static void nhglyph2charcolor(short glyph, uchar *ch, int *color);
51 #endif
52
53 HWND
54 mswin_init_map_window()
55 {
56     static int run_once = 0;
57     HWND ret;
58     RECT rt;
59
60     if (!run_once) {
61         register_map_window_class();
62         run_once = 1;
63     }
64
65     /* get window position */
66     if (GetNHApp()->bAutoLayout) {
67         SetRect(&rt, 0, 0, 0, 0);
68     } else {
69         mswin_get_window_placement(NHW_MAP, &rt);
70     }
71
72     /* create map window object */
73     ret = CreateWindow(
74         szNHMapWindowClass, /* registered class name */
75         NULL,               /* window name */
76         WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_CLIPSIBLINGS
77             | WS_SIZEBOX,     /* window style */
78         rt.left,              /* horizontal position of window */
79         rt.top,               /* vertical position of window */
80         rt.right - rt.left,   /* window width */
81         rt.bottom - rt.top,   /* window height */
82         GetNHApp()->hMainWnd, /* handle to parent or owner window */
83         NULL,                 /* menu handle or child identifier */
84         GetNHApp()->hApp,     /* handle to application instance */
85         NULL);                /* window-creation data */
86     if (!ret) {
87         panic("Cannot create map window");
88     }
89
90     /* Set window caption */
91     SetWindowText(ret, "Map");
92
93     mswin_apply_window_style(ret);
94
95     return ret;
96 }
97
98 void
99 mswin_map_stretch(HWND hWnd, LPSIZE lpsz, BOOL redraw)
100 {
101     PNHMapWindow data;
102     RECT client_rt;
103     SCROLLINFO si;
104     SIZE wnd_size;
105     LOGFONT lgfnt;
106
107     /* check arguments */
108     if (!IsWindow(hWnd) || !lpsz || lpsz->cx <= 0 || lpsz->cy <= 0)
109         return;
110
111     /* calculate window size */
112     GetClientRect(hWnd, &client_rt);
113     wnd_size.cx = client_rt.right - client_rt.left;
114     wnd_size.cy = client_rt.bottom - client_rt.top;
115
116     /* set new screen tile size */
117     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
118     data->xScrTile =
119         max(1, (data->bFitToScreenMode ? wnd_size.cx : lpsz->cx) / COLNO);
120     data->yScrTile =
121         max(1, (data->bFitToScreenMode ? wnd_size.cy : lpsz->cy) / ROWNO);
122
123     /* set map origin point */
124     data->map_orig.x =
125         max(0, client_rt.left + (wnd_size.cx - data->xScrTile * COLNO) / 2);
126     data->map_orig.y =
127         max(0, client_rt.top + (wnd_size.cy - data->yScrTile * ROWNO) / 2);
128
129     data->map_orig.x -= data->map_orig.x % data->xScrTile;
130     data->map_orig.y -= data->map_orig.y % data->yScrTile;
131
132     /* adjust horizontal scroll bar */
133     if (data->bFitToScreenMode)
134         data->xPageSize = COLNO + 1; /* disable scroll bar */
135     else
136         data->xPageSize = wnd_size.cx / data->xScrTile;
137
138     if (data->xPageSize >= COLNO) {
139         data->xPos = 0;
140         GetNHApp()->bNoHScroll = TRUE;
141     } else {
142         GetNHApp()->bNoHScroll = FALSE;
143         data->xPos = max(
144             0, min(COLNO - data->xPageSize + 1, u.ux - data->xPageSize / 2));
145     }
146
147     si.cbSize = sizeof(si);
148     si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
149     si.nMin = 0;
150     si.nMax = COLNO;
151     si.nPage = data->xPageSize;
152     si.nPos = data->xPos;
153     SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
154
155     /* adjust vertical scroll bar */
156     if (data->bFitToScreenMode)
157         data->yPageSize = ROWNO + 1; /* disable scroll bar */
158     else
159         data->yPageSize = wnd_size.cy / data->yScrTile;
160
161     if (data->yPageSize >= ROWNO) {
162         data->yPos = 0;
163         GetNHApp()->bNoVScroll = TRUE;
164     } else {
165         GetNHApp()->bNoVScroll = FALSE;
166         data->yPos = max(
167             0, min(ROWNO - data->yPageSize + 1, u.uy - data->yPageSize / 2));
168     }
169
170     si.cbSize = sizeof(si);
171     si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
172     si.nMin = 0;
173     si.nMax = ROWNO;
174     si.nPage = data->yPageSize;
175     si.nPos = data->yPos;
176     SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
177
178     /* create font */
179     if (data->hMapFont)
180         DeleteObject(data->hMapFont);
181     ZeroMemory(&lgfnt, sizeof(lgfnt));
182     lgfnt.lfHeight = -data->yScrTile;          // height of font
183     lgfnt.lfWidth = -data->xScrTile;           // average character width
184     lgfnt.lfEscapement = 0;                    // angle of escapement
185     lgfnt.lfOrientation = 0;                   // base-line orientation angle
186     lgfnt.lfWeight = FW_NORMAL;                // font weight
187     lgfnt.lfItalic = FALSE;                    // italic attribute option
188     lgfnt.lfUnderline = FALSE;                 // underline attribute option
189     lgfnt.lfStrikeOut = FALSE;                 // strikeout attribute option
190     lgfnt.lfCharSet = mswin_charset();         // character set identifier
191     lgfnt.lfOutPrecision = OUT_DEFAULT_PRECIS; // output precision
192     lgfnt.lfClipPrecision = CLIP_DEFAULT_PRECIS; // clipping precision
193     lgfnt.lfQuality = DEFAULT_QUALITY;           // output quality
194     if (iflags.wc_font_map && *iflags.wc_font_map) {
195         lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family
196         NH_A2W(iflags.wc_font_map, lgfnt.lfFaceName, LF_FACESIZE);
197     } else {
198         lgfnt.lfPitchAndFamily = FIXED_PITCH; // pitch and family
199         NH_A2W(NHMAP_FONT_NAME, lgfnt.lfFaceName, LF_FACESIZE);
200     }
201     data->hMapFont = CreateFontIndirect(&lgfnt);
202
203     mswin_cliparound(data->xCur, data->yCur);
204
205     if (redraw)
206         InvalidateRect(hWnd, NULL, TRUE);
207 }
208
209 /* set map mode */
210 int
211 mswin_map_mode(HWND hWnd, int mode)
212 {
213     PNHMapWindow data;
214     int oldMode;
215     SIZE mapSize;
216
217     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
218     if (mode == data->mapMode)
219         return mode;
220
221     oldMode = data->mapMode;
222     data->mapMode = mode;
223
224     switch (data->mapMode) {
225     case MAP_MODE_ASCII4x6:
226         data->bAsciiMode = TRUE;
227         data->bFitToScreenMode = FALSE;
228         mapSize.cx = 4 * COLNO;
229         mapSize.cy = 6 * ROWNO;
230         break;
231
232     case MAP_MODE_ASCII6x8:
233         data->bAsciiMode = TRUE;
234         data->bFitToScreenMode = FALSE;
235         mapSize.cx = 6 * COLNO;
236         mapSize.cy = 8 * ROWNO;
237         break;
238
239     case MAP_MODE_ASCII8x8:
240         data->bAsciiMode = TRUE;
241         data->bFitToScreenMode = FALSE;
242         mapSize.cx = 8 * COLNO;
243         mapSize.cy = 8 * ROWNO;
244         break;
245
246     case MAP_MODE_ASCII16x8:
247         data->bAsciiMode = TRUE;
248         data->bFitToScreenMode = FALSE;
249         mapSize.cx = 16 * COLNO;
250         mapSize.cy = 8 * ROWNO;
251         break;
252
253     case MAP_MODE_ASCII7x12:
254         data->bAsciiMode = TRUE;
255         data->bFitToScreenMode = FALSE;
256         mapSize.cx = 7 * COLNO;
257         mapSize.cy = 12 * ROWNO;
258         break;
259
260     case MAP_MODE_ASCII8x12:
261         data->bAsciiMode = TRUE;
262         data->bFitToScreenMode = FALSE;
263         mapSize.cx = 8 * COLNO;
264         mapSize.cy = 12 * ROWNO;
265         break;
266
267     case MAP_MODE_ASCII16x12:
268         data->bAsciiMode = TRUE;
269         data->bFitToScreenMode = FALSE;
270         mapSize.cx = 16 * COLNO;
271         mapSize.cy = 12 * ROWNO;
272         break;
273
274     case MAP_MODE_ASCII12x16:
275         data->bAsciiMode = TRUE;
276         data->bFitToScreenMode = FALSE;
277         mapSize.cx = 12 * COLNO;
278         mapSize.cy = 16 * ROWNO;
279         break;
280
281     case MAP_MODE_ASCII10x18:
282         data->bAsciiMode = TRUE;
283         data->bFitToScreenMode = FALSE;
284         mapSize.cx = 10 * COLNO;
285         mapSize.cy = 18 * ROWNO;
286         break;
287
288     case MAP_MODE_ASCII_FIT_TO_SCREEN: {
289         RECT client_rt;
290         GetClientRect(hWnd, &client_rt);
291         mapSize.cx = client_rt.right - client_rt.left;
292         mapSize.cy = client_rt.bottom - client_rt.top;
293
294         data->bAsciiMode = TRUE;
295         data->bFitToScreenMode = TRUE;
296     } break;
297
298     case MAP_MODE_TILES_FIT_TO_SCREEN: {
299         RECT client_rt;
300         GetClientRect(hWnd, &client_rt);
301         mapSize.cx = client_rt.right - client_rt.left;
302         mapSize.cy = client_rt.bottom - client_rt.top;
303
304         data->bAsciiMode = FALSE;
305         data->bFitToScreenMode = TRUE;
306     } break;
307
308     case MAP_MODE_TILES:
309     default:
310         data->bAsciiMode = FALSE;
311         data->bFitToScreenMode = FALSE;
312         mapSize.cx = GetNHApp()->mapTile_X * COLNO;
313         mapSize.cy = GetNHApp()->mapTile_Y * ROWNO;
314         break;
315     }
316
317     mswin_map_stretch(hWnd, &mapSize, TRUE);
318
319     mswin_update_inventory(); /* for perm_invent to hide/show tiles */
320
321     return oldMode;
322 }
323
324 /* register window class for map window */
325 void
326 register_map_window_class()
327 {
328     WNDCLASS wcex;
329     ZeroMemory(&wcex, sizeof(wcex));
330
331     /* window class */
332     wcex.style = CS_NOCLOSE | CS_DBLCLKS;
333     wcex.lpfnWndProc = (WNDPROC) MapWndProc;
334     wcex.cbClsExtra = 0;
335     wcex.cbWndExtra = 0;
336     wcex.hInstance = GetNHApp()->hApp;
337     wcex.hIcon = NULL;
338     wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
339     wcex.hbrBackground =
340         CreateSolidBrush(RGB(0, 0, 0)); /* set backgroup here */
341     wcex.lpszMenuName = NULL;
342     wcex.lpszClassName = szNHMapWindowClass;
343
344     if (!RegisterClass(&wcex)) {
345         panic("cannot register Map window class");
346     }
347 }
348
349 /* map window procedure */
350 LRESULT CALLBACK
351 MapWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
352 {
353     PNHMapWindow data;
354
355     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
356     switch (message) {
357     case WM_CREATE:
358         onCreate(hWnd, wParam, lParam);
359         break;
360
361     case WM_MSNH_COMMAND:
362         onMSNHCommand(hWnd, wParam, lParam);
363         break;
364
365     case WM_PAINT:
366         onPaint(hWnd);
367         break;
368
369     case WM_SETFOCUS:
370         /* transfer focus back to the main window */
371         SetFocus(GetNHApp()->hMainWnd);
372         break;
373
374     case WM_HSCROLL:
375         onMSNH_HScroll(hWnd, wParam, lParam);
376         break;
377
378     case WM_VSCROLL:
379         onMSNH_VScroll(hWnd, wParam, lParam);
380         break;
381
382     case WM_SIZE: {
383         RECT rt;
384         SIZE size;
385
386         if (data->bFitToScreenMode) {
387             size.cx = LOWORD(lParam);
388             size.cy = HIWORD(lParam);
389         } else {
390             /* mapping factor is unchaged we just need to adjust scroll bars
391              */
392             size.cx = data->xScrTile * COLNO;
393             size.cy = data->yScrTile * ROWNO;
394         }
395         mswin_map_stretch(hWnd, &size, TRUE);
396
397         /* update window placement */
398         GetWindowRect(hWnd, &rt);
399         ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
400         ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
401         mswin_update_window_placement(NHW_MAP, &rt);
402     } break;
403
404     case WM_MOVE: {
405         RECT rt;
406         GetWindowRect(hWnd, &rt);
407         ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
408         ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
409         mswin_update_window_placement(NHW_MAP, &rt);
410     }
411         return FALSE;
412
413     case WM_LBUTTONDOWN:
414         NHEVENT_MS(CLICK_1,
415                    max(0, min(COLNO, data->xPos
416                                          + (LOWORD(lParam) - data->map_orig.x)
417                                                / data->xScrTile)),
418                    max(0, min(ROWNO, data->yPos
419                                          + (HIWORD(lParam) - data->map_orig.y)
420                                                / data->yScrTile)));
421         return 0;
422
423     case WM_LBUTTONDBLCLK:
424     case WM_RBUTTONDOWN:
425         NHEVENT_MS(CLICK_2,
426                    max(0, min(COLNO, data->xPos
427                                          + (LOWORD(lParam) - data->map_orig.x)
428                                                / data->xScrTile)),
429                    max(0, min(ROWNO, data->yPos
430                                          + (HIWORD(lParam) - data->map_orig.y)
431                                                / data->yScrTile)));
432         return 0;
433
434     case WM_DESTROY:
435         if (data->hMapFont)
436             DeleteObject(data->hMapFont);
437         free(data);
438         SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
439         break;
440
441     default:
442         return DefWindowProc(hWnd, message, wParam, lParam);
443     }
444     return 0;
445 }
446
447 /* on WM_COMMAND */
448 void
449 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
450 {
451     PNHMapWindow data;
452     RECT rt;
453
454     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
455     switch (wParam) {
456     case MSNH_MSG_PRINT_GLYPH: {
457         PMSNHMsgPrintGlyph msg_data = (PMSNHMsgPrintGlyph) lParam;
458         if ((data->map[msg_data->x][msg_data->y] != msg_data->glyph)
459             || (data->bkmap[msg_data->x][msg_data->y] != msg_data->bkglyph)) {
460             data->map[msg_data->x][msg_data->y] = msg_data->glyph;
461             data->bkmap[msg_data->x][msg_data->y] = msg_data->bkglyph;
462
463             /* invalidate the update area. Erase backround if there 
464                is nothing to paint or we are in text mode */
465             nhcoord2display(data, msg_data->x, msg_data->y, &rt);
466             InvalidateRect(hWnd, &rt, 
467                            (((msg_data->glyph == NO_GLYPH) && (msg_data->bkglyph == NO_GLYPH))
468                             || data->bAsciiMode 
469                             || Is_rogue_level(&u.uz)));
470         }
471     } break;
472
473     case MSNH_MSG_CLIPAROUND: {
474         PMSNHMsgClipAround msg_data = (PMSNHMsgClipAround) lParam;
475         int x, y;
476         BOOL scroll_x, scroll_y;
477         int mcam = iflags.wc_scroll_margin;
478
479         /* calculate if you should clip around */
480         scroll_x =
481             !GetNHApp()->bNoHScroll
482             && (msg_data->x < (data->xPos + mcam)
483                 || msg_data->x > (data->xPos + data->xPageSize - mcam));
484         scroll_y =
485             !GetNHApp()->bNoVScroll
486             && (msg_data->y < (data->yPos + mcam)
487                 || msg_data->y > (data->yPos + data->yPageSize - mcam));
488
489         mcam += iflags.wc_scroll_amount - 1;
490         /* get page size and center horizontally on x-position */
491         if (scroll_x) {
492             if (data->xPageSize <= 2 * mcam) {
493                 x = max(0, min(COLNO, msg_data->x - data->xPageSize / 2));
494             } else if (msg_data->x < data->xPos + data->xPageSize / 2) {
495                 x = max(0, min(COLNO, msg_data->x - mcam));
496             } else {
497                 x = max(0, min(COLNO, msg_data->x - data->xPageSize + mcam));
498             }
499             SendMessage(hWnd, WM_HSCROLL, (WPARAM) MAKELONG(SB_THUMBTRACK, x),
500                         (LPARAM) NULL);
501         }
502
503         /* get page size and center vertically on y-position */
504         if (scroll_y) {
505             if (data->yPageSize <= 2 * mcam) {
506                 y = max(0, min(ROWNO, msg_data->y - data->yPageSize / 2));
507             } else if (msg_data->y < data->yPos + data->yPageSize / 2) {
508                 y = max(0, min(ROWNO, msg_data->y - mcam));
509             } else {
510                 y = max(0, min(ROWNO, msg_data->y - data->yPageSize + mcam));
511             }
512             SendMessage(hWnd, WM_VSCROLL, (WPARAM) MAKELONG(SB_THUMBTRACK, y),
513                         (LPARAM) NULL);
514         }
515     } break;
516
517     case MSNH_MSG_CLEAR_WINDOW: {
518         int i, j;
519         for (i = 0; i < COLNO; i++)
520             for (j = 0; j < ROWNO; j++) {
521                 data->map[i][j] = NO_GLYPH;
522                 data->bkmap[i][j] = NO_GLYPH;
523             }
524         InvalidateRect(hWnd, NULL, TRUE);
525     } break;
526
527     case MSNH_MSG_CURSOR: {
528         PMSNHMsgCursor msg_data = (PMSNHMsgCursor) lParam;
529         HDC hdc;
530         RECT rt;
531
532         /* move focus rectangle at the cursor postion */
533         hdc = GetDC(hWnd);
534
535         nhcoord2display(data, data->xCur, data->yCur, &rt);
536         if (data->bAsciiMode) {
537             PatBlt(hdc, rt.left, rt.top, rt.right - rt.left,
538                    rt.bottom - rt.top, DSTINVERT);
539         } else {
540             DrawFocusRect(hdc, &rt);
541         }
542
543         data->xCur = msg_data->x;
544         data->yCur = msg_data->y;
545
546         nhcoord2display(data, data->xCur, data->yCur, &rt);
547         if (data->bAsciiMode) {
548             PatBlt(hdc, rt.left, rt.top, rt.right - rt.left,
549                    rt.bottom - rt.top, DSTINVERT);
550         } else {
551             DrawFocusRect(hdc, &rt);
552         }
553
554         ReleaseDC(hWnd, hdc);
555     } break;
556
557     case MSNH_MSG_GETTEXT: {
558         PMSNHMsgGetText msg_data = (PMSNHMsgGetText) lParam;
559         size_t index;
560         int col, row;
561         int color;
562         unsigned special;
563         int mgch;
564
565         index = 0;
566         for (row = 0; row < ROWNO; row++) {
567             for (col = 0; col < COLNO; col++) {
568                 if (index >= msg_data->max_size)
569                     break;
570                 if (data->map[col][row] == NO_GLYPH) {
571                     mgch = ' ';
572                 } else {
573                     (void) mapglyph(data->map[col][row], &mgch, &color,
574                                     &special, col, row);
575                 }
576                 msg_data->buffer[index] = mgch;
577                 index++;
578             }
579             if (index >= msg_data->max_size - 1)
580                 break;
581             msg_data->buffer[index++] = '\r';
582             msg_data->buffer[index++] = '\n';
583         }
584     } break;
585
586     } /* end switch(wParam) */
587 }
588
589 /* on WM_CREATE */
590 void
591 onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
592 {
593     PNHMapWindow data;
594     int i, j;
595
596     UNREFERENCED_PARAMETER(wParam);
597     UNREFERENCED_PARAMETER(lParam);
598
599     /* set window data */
600     data = (PNHMapWindow) malloc(sizeof(NHMapWindow));
601     if (!data)
602         panic("out of memory");
603
604     ZeroMemory(data, sizeof(NHMapWindow));
605     for (i = 0; i < COLNO; i++)
606         for (j = 0; j < ROWNO; j++) {
607             data->map[i][j] = NO_GLYPH;
608             data->bkmap[i][j] = NO_GLYPH;
609         }
610
611     data->bAsciiMode = FALSE;
612
613     data->xScrTile = GetNHApp()->mapTile_X;
614     data->yScrTile = GetNHApp()->mapTile_Y;
615
616     SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
617 }
618
619 /* on WM_PAINT */
620 void
621 onPaint(HWND hWnd)
622 {
623     PNHMapWindow data;
624     PAINTSTRUCT ps;
625     HDC hDC;
626     HDC tileDC;
627     HGDIOBJ saveBmp;
628     RECT paint_rt;
629     int i, j;
630
631     /* get window data */
632     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
633
634     hDC = BeginPaint(hWnd, &ps);
635
636     /* calculate paint rectangle */
637     if (!IsRectEmpty(&ps.rcPaint)) {
638         /* calculate paint rectangle */
639         paint_rt.left =
640             max(data->xPos
641                     + (ps.rcPaint.left - data->map_orig.x) / data->xScrTile,
642                 0);
643         paint_rt.top = max(
644             data->yPos + (ps.rcPaint.top - data->map_orig.y) / data->yScrTile,
645             0);
646         paint_rt.right = min(
647             data->xPos
648                 + (ps.rcPaint.right - data->map_orig.x) / data->xScrTile + 1,
649             COLNO);
650         paint_rt.bottom = min(
651             data->yPos
652                 + (ps.rcPaint.bottom - data->map_orig.y) / data->yScrTile + 1,
653             ROWNO);
654
655         if (data->bAsciiMode || Is_rogue_level(&u.uz)) {
656             /* You enter a VERY primitive world! */
657             HGDIOBJ oldFont;
658
659             oldFont = SelectObject(hDC, data->hMapFont);
660             SetBkMode(hDC, TRANSPARENT);
661
662             /* draw the map */
663             for (i = paint_rt.left; i < paint_rt.right; i++)
664                 for (j = paint_rt.top; j < paint_rt.bottom; j++)
665                     if (data->map[i][j] >= 0) {
666                         char ch;
667                         TCHAR wch;
668                         RECT glyph_rect;
669                         int color;
670                         unsigned special;
671                         int mgch;
672                         HBRUSH back_brush;
673                         COLORREF OldFg;
674
675                         nhcoord2display(data, i, j, &glyph_rect);
676
677 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
678                         nhglyph2charcolor(data->map[i][j], &ch, &color);
679                         OldFg = SetTextColor(hDC, nhcolor_to_RGB(color));
680 #else
681                         /* rely on NetHack core helper routine */
682                         (void) mapglyph(data->map[i][j], &mgch, &color,
683                                         &special, i, j);
684                         ch = (char) mgch;
685                         if (((special & MG_PET) && iflags.hilite_pet)
686                             || ((special & (MG_DETECT | MG_BW_LAVA))
687                                 && iflags.use_inverse)) {
688                             back_brush =
689                                 CreateSolidBrush(nhcolor_to_RGB(CLR_GRAY));
690                             FillRect(hDC, &glyph_rect, back_brush);
691                             DeleteObject(back_brush);
692                             switch (color) {
693                             case CLR_GRAY:
694                             case CLR_WHITE:
695                                 OldFg = SetTextColor(
696                                     hDC, nhcolor_to_RGB(CLR_BLACK));
697                                 break;
698                             default:
699                                 OldFg =
700                                     SetTextColor(hDC, nhcolor_to_RGB(color));
701                             }
702                         } else {
703                             OldFg = SetTextColor(hDC, nhcolor_to_RGB(color));
704                         }
705 #endif
706
707                         DrawText(hDC, NH_A2W(&ch, &wch, 1), 1, &glyph_rect,
708                                  DT_CENTER | DT_VCENTER | DT_NOPREFIX);
709                         SetTextColor(hDC, OldFg);
710                     }
711             SelectObject(hDC, oldFont);
712         } else {
713             short ntile;
714             int t_x, t_y;
715             int glyph, bkglyph;
716             RECT glyph_rect;
717             int layer;
718 #ifdef USE_PILEMARK
719             int color;
720             unsigned special;
721             int mgch;
722 #endif
723             /* prepare tiles DC for mapping */
724             tileDC = CreateCompatibleDC(hDC);
725             saveBmp = SelectObject(tileDC, GetNHApp()->bmpMapTiles);
726
727             /* draw the map */
728             for (i = paint_rt.left; i < paint_rt.right; i++)
729                 for (j = paint_rt.top; j < paint_rt.bottom; j++) {
730                     layer = 0;
731                     glyph = data->map[i][j];
732                     bkglyph = data->bkmap[i][j];
733
734                     if (bkglyph != NO_GLYPH) {
735                         ntile = glyph2tile[bkglyph];
736                         t_x = TILEBMP_X(ntile);
737                         t_y = TILEBMP_Y(ntile);
738                         nhcoord2display(data, i, j, &glyph_rect);
739
740                         StretchBlt(hDC, glyph_rect.left, glyph_rect.top,
741                                    data->xScrTile, data->yScrTile, tileDC,
742                                    t_x, t_y, GetNHApp()->mapTile_X,
743                                    GetNHApp()->mapTile_Y, SRCCOPY);
744                         layer ++;
745                     }
746
747                     if ((glyph != NO_GLYPH) && (glyph != bkglyph)) {
748                         ntile = glyph2tile[glyph];
749                         t_x = TILEBMP_X(ntile);
750                         t_y = TILEBMP_Y(ntile);
751                         nhcoord2display(data, i, j, &glyph_rect);
752
753                         if (layer > 0) {
754                             (*GetNHApp()->lpfnTransparentBlt)(
755                                 hDC, glyph_rect.left, glyph_rect.top,
756                                 data->xScrTile, data->yScrTile, tileDC,
757                                 t_x, t_y, GetNHApp()->mapTile_X,
758                                 GetNHApp()->mapTile_Y, TILE_BK_COLOR);
759                         } else {
760                             StretchBlt(hDC, glyph_rect.left, glyph_rect.top,
761                                        data->xScrTile, data->yScrTile, tileDC,
762                                        t_x, t_y, GetNHApp()->mapTile_X,
763                                        GetNHApp()->mapTile_Y, SRCCOPY);
764                         }
765
766                         layer ++;
767                      }
768
769 #ifdef USE_PILEMARK
770                      /* rely on NetHack core helper routine */
771                      (void) mapglyph(data->map[i][j], &mgch, &color,
772                                         &special, i, j);
773                      if ((glyph != NO_GLYPH) && (special & MG_PET) 
774 #else
775                      if ((glyph != NO_GLYPH) && glyph_is_pet(glyph)
776 #endif
777                            && iflags.wc_hilite_pet) {
778                         /* apply pet mark transparently over
779                            pet image */
780                         HDC hdcPetMark;
781                         HBITMAP bmPetMarkOld;
782
783                         /* this is DC for petmark bitmap */
784                         hdcPetMark = CreateCompatibleDC(hDC);
785                         bmPetMarkOld = SelectObject(
786                             hdcPetMark, GetNHApp()->bmpPetMark);
787
788                         (*GetNHApp()->lpfnTransparentBlt)(
789                             hDC, glyph_rect.left, glyph_rect.top,
790                             data->xScrTile, data->yScrTile, hdcPetMark, 0,
791                             0, TILE_X, TILE_Y, TILE_BK_COLOR);
792                         SelectObject(hdcPetMark, bmPetMarkOld);
793                         DeleteDC(hdcPetMark);
794                     }
795 #ifdef USE_PILEMARK
796                     if ((glyph != NO_GLYPH)
797                         && (special & MG_OBJPILE) && iflags.hilite_pile) {
798                         /* apply pilemark transparently over other image */
799                         HDC hdcPileMark;
800                         HBITMAP bmPileMarkOld;
801
802                         /* this is DC for pilemark bitmap */
803                         hdcPileMark = CreateCompatibleDC(hDC);
804                         bmPileMarkOld = SelectObject(
805                             hdcPileMark, GetNHApp()->bmpPileMark);
806
807                         (*GetNHApp()->lpfnTransparentBlt)(
808                             hDC, glyph_rect.left, glyph_rect.top,
809                             data->xScrTile, data->yScrTile, hdcPileMark, 0,
810                             0, TILE_X, TILE_Y, TILE_BK_COLOR);
811                         SelectObject(hdcPileMark, bmPileMarkOld);
812                         DeleteDC(hdcPileMark);                        
813                     }
814 #endif
815                 }
816
817             SelectObject(tileDC, saveBmp);
818             DeleteDC(tileDC);
819         }
820
821         /* draw focus rect */
822         nhcoord2display(data, data->xCur, data->yCur, &paint_rt);
823         if (data->bAsciiMode) {
824             PatBlt(hDC, paint_rt.left, paint_rt.top,
825                    paint_rt.right - paint_rt.left,
826                    paint_rt.bottom - paint_rt.top, DSTINVERT);
827         } else {
828             DrawFocusRect(hDC, &paint_rt);
829         }
830     }
831     EndPaint(hWnd, &ps);
832 }
833
834 /* on WM_VSCROLL */
835 void
836 onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
837 {
838     PNHMapWindow data;
839     SCROLLINFO si;
840     int yNewPos;
841     int yDelta;
842
843     UNREFERENCED_PARAMETER(lParam);
844
845     /* get window data */
846     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
847
848     switch (LOWORD(wParam)) {
849     /* User clicked shaft left of the scroll box. */
850     case SB_PAGEUP:
851         yNewPos = data->yPos - data->yPageSize;
852         break;
853
854     /* User clicked shaft right of the scroll box. */
855     case SB_PAGEDOWN:
856         yNewPos = data->yPos + data->yPageSize;
857         break;
858
859     /* User clicked the left arrow. */
860     case SB_LINEUP:
861         yNewPos = data->yPos - 1;
862         break;
863
864     /* User clicked the right arrow. */
865     case SB_LINEDOWN:
866         yNewPos = data->yPos + 1;
867         break;
868
869     /* User dragged the scroll box. */
870     case SB_THUMBTRACK:
871         yNewPos = HIWORD(wParam);
872         break;
873
874     default:
875         yNewPos = data->yPos;
876     }
877
878     yNewPos = max(0, min(ROWNO - data->yPageSize + 1, yNewPos));
879     if (yNewPos == data->yPos)
880         return;
881
882     yDelta = yNewPos - data->yPos;
883     data->yPos = yNewPos;
884
885     ScrollWindowEx(hWnd, 0, -data->yScrTile * yDelta, (CONST RECT *) NULL,
886                    (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
887                    SW_INVALIDATE | SW_ERASE);
888
889     si.cbSize = sizeof(si);
890     si.fMask = SIF_POS;
891     si.nPos = data->yPos;
892     SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
893 }
894
895 /* on WM_HSCROLL */
896 void
897 onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
898 {
899     PNHMapWindow data;
900     SCROLLINFO si;
901     int xNewPos;
902     int xDelta;
903
904     UNREFERENCED_PARAMETER(lParam);
905
906     /* get window data */
907     data = (PNHMapWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
908
909     switch (LOWORD(wParam)) {
910     /* User clicked shaft left of the scroll box. */
911     case SB_PAGEUP:
912         xNewPos = data->xPos - data->xPageSize;
913         break;
914
915     /* User clicked shaft right of the scroll box. */
916     case SB_PAGEDOWN:
917         xNewPos = data->xPos + data->xPageSize;
918         break;
919
920     /* User clicked the left arrow. */
921     case SB_LINEUP:
922         xNewPos = data->xPos - 1;
923         break;
924
925     /* User clicked the right arrow. */
926     case SB_LINEDOWN:
927         xNewPos = data->xPos + 1;
928         break;
929
930     /* User dragged the scroll box. */
931     case SB_THUMBTRACK:
932         xNewPos = HIWORD(wParam);
933         break;
934
935     default:
936         xNewPos = data->xPos;
937     }
938
939     xNewPos = max(0, min(COLNO - data->xPageSize + 1, xNewPos));
940     if (xNewPos == data->xPos)
941         return;
942
943     xDelta = xNewPos - data->xPos;
944     data->xPos = xNewPos;
945
946     ScrollWindowEx(hWnd, -data->xScrTile * xDelta, 0, (CONST RECT *) NULL,
947                    (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
948                    SW_INVALIDATE | SW_ERASE);
949
950     si.cbSize = sizeof(si);
951     si.fMask = SIF_POS;
952     si.nPos = data->xPos;
953     SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
954 }
955
956 /* map nethack map coordinates to the screen location */
957 void
958 nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut)
959 {
960     lpOut->left = (x - data->xPos) * data->xScrTile + data->map_orig.x;
961     lpOut->top = (y - data->yPos) * data->yScrTile + data->map_orig.y;
962     lpOut->right = lpOut->left + data->xScrTile;
963     lpOut->bottom = lpOut->top + data->yScrTile;
964 }
965
966 #if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2)
967 /* map glyph to character/color combination */
968 void
969 nhglyph2charcolor(short g, uchar *ch, int *color)
970 {
971     int offset;
972 #ifdef TEXTCOLOR
973
974 #define zap_color(n) *color = iflags.use_color ? zapcolors[n] : NO_COLOR
975 #define cmap_color(n) *color = iflags.use_color ? defsyms[n].color : NO_COLOR
976 #define obj_color(n) \
977     *color = iflags.use_color ? objects[n].oc_color : NO_COLOR
978 #define mon_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR
979 #define pet_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR
980 #define warn_color(n) \
981     *color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
982
983 #else /* no text color */
984
985 #define zap_color(n)
986 #define cmap_color(n)
987 #define obj_color(n)
988 #define mon_color(n)
989 #define pet_color(c)
990 #define warn_color(c)
991     *color = CLR_WHITE;
992 #endif
993
994     if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */
995         *ch = showsyms[offset + SYM_OFF_W];
996         warn_color(offset);
997     } else if ((offset = (g - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */
998         /* see swallow_to_glyph() in display.c */
999         *ch = (uchar) showsyms[(S_sw_tl + (offset & 0x7)) + SYM_OFF_P];
1000         mon_color(offset >> 3);
1001     } else if ((offset = (g - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */
1002         /* see zapdir_to_glyph() in display.c */
1003         *ch = showsyms[(S_vbeam + (offset & 0x3)) + SYM_OFF_P];
1004         zap_color((offset >> 2));
1005     } else if ((offset = (g - GLYPH_CMAP_OFF)) >= 0) { /* cmap */
1006         *ch = showsyms[offset + SYM_OFF_P];
1007         cmap_color(offset);
1008     } else if ((offset = (g - GLYPH_OBJ_OFF)) >= 0) { /* object */
1009         *ch = showsyms[(int) objects[offset].oc_class + SYM_OFF_O];
1010         obj_color(offset);
1011     } else if ((offset = (g - GLYPH_BODY_OFF)) >= 0) { /* a corpse */
1012         *ch = showsyms[(int) objects[CORPSE].oc_class + SYM_OFF_O];
1013         mon_color(offset);
1014     } else if ((offset = (g - GLYPH_PET_OFF)) >= 0) { /* a pet */
1015         *ch = showsyms[(int) mons[offset].mlet + SYM_OFF_M];
1016         pet_color(offset);
1017     } else { /* a monster */
1018         *ch = showsyms[(int) mons[g].mlet + SYM_OFF_M];
1019         mon_color(g);
1020     }
1021     // end of wintty code
1022 }
1023 #endif
1024
1025 /* map nethack color to RGB */
1026 COLORREF
1027 nhcolor_to_RGB(int c)
1028 {
1029     if (c >= 0 && c < CLR_MAX)
1030         return GetNHApp()->regMapColors[c];
1031     return RGB(0x00, 0x00, 0x00);
1032 }
1033