OSDN Git Service

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