Code cleanup
[crawl.git] / crawl-ref / source / tilesdl.cc
blob815fb538b460c523e5e9f881d086dcaa8d928c05
1 #include "AppHdr.h"
3 #ifdef USE_TILE
5 #include "artefact.h"
6 #include "cio.h"
7 #include "coord.h"
8 #include "directn.h"
9 #include "env.h"
10 #include "files.h"
11 #include "glwrapper.h"
12 #include "libutil.h"
13 #include "macro.h"
14 #include "map_knowledge.h"
15 #include "message.h"
16 #include "mon-util.h"
17 #include "options.h"
18 #include "player.h"
19 #include "state.h"
20 #include "stuff.h"
21 #include "tiledef-dngn.h"
22 #include "tiledef-gui.h"
23 #include "tiledef-main.h"
24 #include "tilefont.h"
25 #include "tilereg.h"
26 #include "tilereg-cmd.h"
27 #include "tilereg-crt.h"
28 #include "tilereg-dgn.h"
29 #include "tilereg-doll.h"
30 #include "tilereg-grid.h"
31 #include "tilereg-inv.h"
32 #include "tilereg-map.h"
33 #include "tilereg-mem.h"
34 #include "tilereg-menu.h"
35 #include "tilereg-mon.h"
36 #include "tilereg-msg.h"
37 #include "tilereg-spl.h"
38 #include "tilereg-skl.h"
39 #include "tilereg-stat.h"
40 #include "tilereg-tab.h"
41 #include "tilereg-text.h"
42 #include "tilereg-title.h"
43 #include "tilesdl.h"
44 #include "tileview.h"
45 #include "travel.h"
46 #include "view.h"
47 #include "viewgeom.h"
48 #include "windowmanager.h"
50 #ifdef TARGET_OS_WINDOWS
52 #include <windows.h>
53 // WINAPI defines these, but we are already using them in
54 // wm_event_type enum,
55 // so we have to undef these to have working input when using
56 // Windows and Tiles
57 #undef WM_KEYDOWN
58 #undef WM_KEYUP
59 #undef WM_QUIT
60 #endif
62 // Default Screen Settings
63 // width, height, map, crt, stat, msg, tip, lbl
64 static int _screen_sizes[4][8] =
66 // Default
67 {1024, 700, 3, 15, 16, 14, 15, 14},
68 // Eee PC 900+
69 {1024, 600, 2, 14, 14, 12, 13, 12},
70 // Small screen
71 {800, 600, 2, 14, 11, 12, 13, 12},
72 // Eee PC
73 {800, 480, 2, 13, 12, 10, 13, 11}
76 TilesFramework tiles;
78 TilesFramework::TilesFramework() :
79 m_windowsz(1024, 768),
80 m_viewsc(0, 0),
81 m_fullscreen(false),
82 m_need_redraw(false),
83 m_active_layer(LAYER_CRT),
84 m_buttons_held(0),
85 m_key_mod(0),
86 m_mouse(-1, -1),
87 m_last_tick_moved(0),
88 m_last_tick_redraw(0)
92 TilesFramework::~TilesFramework()
96 static void _init_consoles()
98 #ifdef TARGET_OS_WINDOWS
99 typedef BOOL (WINAPI *ac_func)(DWORD);
100 ac_func attach_console = (ac_func)GetProcAddress(
101 GetModuleHandle(TEXT("kernel32.dll")), "AttachConsole");
103 if (attach_console)
105 // Redirect output to the console
106 attach_console((DWORD)-1);
107 freopen("CONOUT$", "wb", stdout);
108 freopen("CONOUT$", "wb", stderr);
110 #endif
113 static void _shutdown_console()
115 #ifdef TARGET_OS_WINDOWS
116 typedef BOOL (WINAPI *fc_func)(void);
117 fc_func free_console = (fc_func)GetProcAddress(
118 GetModuleHandle(TEXT("kernel32.dll")), "FreeConsole");
119 if (free_console)
120 free_console();
121 #endif
123 void TilesFramework::shutdown()
125 delete m_region_tile;
126 delete m_region_stat;
127 delete m_region_msg;
128 delete m_region_map;
129 delete m_region_tab;
130 delete m_region_inv;
131 delete m_region_spl;
132 delete m_region_mem;
133 delete m_region_mon;
134 delete m_region_crt;
135 delete m_region_menu;
137 m_region_tile = NULL;
138 m_region_stat = NULL;
139 m_region_msg = NULL;
140 m_region_map = NULL;
141 m_region_tab = NULL;
142 m_region_inv = NULL;
143 m_region_spl = NULL;
144 m_region_mem = NULL;
145 m_region_mon = NULL;
146 m_region_crt = NULL;
147 m_region_menu = NULL;
149 for (tab_iterator it = m_tabs.begin(); it != m_tabs.end(); ++it)
150 delete it->second;
152 m_tabs.clear();
154 for (unsigned int i = 0; i < LAYER_MAX; i++)
155 m_layers[i].m_regions.clear();
157 for (unsigned int i = 0; i < m_fonts.size(); i++)
159 delete m_fonts[i].font;
160 m_fonts[i].font = NULL;
163 delete m_image;
165 GLStateManager::shutdown();
166 WindowManager::shutdown();
168 _shutdown_console();
172 * Creates a new title region and sets it active
173 * Remember to call hide_title() when you're done
174 * showing the title.
176 void TilesFramework::draw_title()
178 TitleRegion* reg = new TitleRegion(m_windowsz.x, m_windowsz.y,
179 m_fonts[m_msg_font].font);
181 m_layers[LAYER_TILE_CONTROL].m_regions.push_back(reg);
182 m_active_layer = LAYER_TILE_CONTROL;
183 redraw();
187 * Updates the loading message text on the title
188 * screen
189 * Assumes that we only have one region on the layer
190 * If at some point it's possible to have multiple regions
191 * open while the title screen shows, the .at(0) will need
192 * to be changed and saved on a variable somewhere instead
194 void TilesFramework::update_title_msg(std::string message)
196 ASSERT(m_layers[LAYER_TILE_CONTROL].m_regions.size() == 1);
197 ASSERT(m_active_layer == LAYER_TILE_CONTROL);
198 TitleRegion* reg = dynamic_cast<TitleRegion*>(
199 m_layers[LAYER_TILE_CONTROL].m_regions.at(0));
200 reg->update_message(message);
201 redraw();
205 * Deletes the dynamically reserved Titlescreen memory
206 * at end. Runs reg->run to get one key input from the user
207 * so that the title screen stays ope until any input is given.
208 * Assumes that we only have one region on the layer
209 * If at some point it's possible to have multiple regions
210 * open while the title screen shows, the .at(0) will need
211 * to be changed and saved on a variable somewhere instead
213 void TilesFramework::hide_title()
215 ASSERT(m_layers[LAYER_TILE_CONTROL].m_regions.size() == 1);
216 ASSERT(m_active_layer == LAYER_TILE_CONTROL);
217 TitleRegion* reg = dynamic_cast<TitleRegion*>(
218 m_layers[LAYER_TILE_CONTROL].m_regions.at(0));
219 redraw();
220 reg->run();
221 delete reg;
222 m_layers[LAYER_TILE_CONTROL].m_regions.clear();
225 void TilesFramework::draw_doll_edit()
227 DollEditRegion* reg = new DollEditRegion(m_image,
228 m_fonts[m_msg_font].font);
229 use_control_region(reg);
230 delete reg;
233 void TilesFramework::use_control_region(ControlRegion *reg)
235 m_layers[LAYER_TILE_CONTROL].m_regions.push_back(reg);
236 m_active_layer = LAYER_TILE_CONTROL;
237 set_need_redraw();
239 reg->run();
241 m_layers[LAYER_TILE_CONTROL].m_regions.clear();
244 void TilesFramework::calculate_default_options()
246 // Find which set of _screen_sizes to use.
247 int auto_size = 0;
248 int num_screen_sizes = sizeof(_screen_sizes) / sizeof(_screen_sizes[0]);
251 if (m_windowsz.x >= _screen_sizes[auto_size][0]
252 && m_windowsz.y >= _screen_sizes[auto_size][1])
254 break;
257 while (++auto_size < num_screen_sizes - 1);
259 m_map_pixels = Options.tile_map_pixels;
260 // Auto pick map and font sizes if option is zero.
261 #define AUTO(x,y) (x = (x) ? (x) : _screen_sizes[auto_size][(y)])
262 AUTO(m_map_pixels, 2);
263 AUTO(Options.tile_font_crt_size, 3);
264 AUTO(Options.tile_font_stat_size, 4);
265 AUTO(Options.tile_font_msg_size, 5);
266 AUTO(Options.tile_font_tip_size, 6);
267 AUTO(Options.tile_font_lbl_size, 7);
268 #undef AUTO
270 m_tab_margin = Options.tile_font_lbl_size + 4;
274 bool TilesFramework::initialise()
276 _init_consoles();
278 const char *icon_name =
279 #ifdef DATA_DIR_PATH
280 DATA_DIR_PATH
281 #endif
282 #ifdef TARGET_OS_MACOSX
283 "dat/tiles/stone_soup_icon-512x512.png";
284 #else
285 "dat/tiles/stone_soup_icon-32x32.png";
286 #endif
288 std::string title = CRAWL " " + Version::Long();
290 // Do our initialization here.
292 // Create an instance of UIWrapper for the library we were compiled for
293 WindowManager::create();
294 if (!wm)
295 return (false);
297 // Initialize the wrapper
298 if (!wm->init(&m_windowsz))
299 return (false);
301 wm->set_window_title(title.c_str());
302 wm->set_window_icon(icon_name);
304 // Copy over constants that need to have been set by the wrapper
305 m_screen_width = wm->screen_width();
306 m_screen_height = wm->screen_height();
308 GLStateManager::init();
310 m_image = new ImageManager();
312 // If the window size is less than the view height, the textures will
313 // have to be shrunk. If this isn't the case, then don't create mipmaps,
314 // as this appears to make things blurry on some users machines.
315 bool need_mips = (m_windowsz.y < 32 * VIEW_MIN_HEIGHT);
316 if (!m_image->load_textures(need_mips))
317 return (false);
319 calculate_default_options();
321 m_crt_font = load_font(Options.tile_font_crt_file.c_str(),
322 Options.tile_font_crt_size, true, true);
323 m_msg_font = load_font(Options.tile_font_msg_file.c_str(),
324 Options.tile_font_msg_size, true, false);
325 int stat_font = load_font(Options.tile_font_stat_file.c_str(),
326 Options.tile_font_stat_size, true, false);
327 m_tip_font = load_font(Options.tile_font_tip_file.c_str(),
328 Options.tile_font_tip_size, true, false);
329 m_lbl_font = load_font(Options.tile_font_lbl_file.c_str(),
330 Options.tile_font_lbl_size, true, true);
332 if (m_crt_font == -1 || m_msg_font == -1 || stat_font == -1
333 || m_tip_font == -1 || m_lbl_font == -1)
335 return (false);
338 m_init = TileRegionInit(m_image, m_fonts[m_lbl_font].font, TILE_X, TILE_Y);
339 m_region_tile = new DungeonRegion(m_init);
340 m_region_tab = new TabbedRegion(m_init);
341 m_region_inv = new InventoryRegion(m_init);
342 m_region_spl = new SpellRegion(m_init);
343 m_region_mem = new MemoriseRegion(m_init);
344 m_region_mon = new MonsterRegion(m_init);
345 m_region_skl = new SkillRegion(m_init);
346 m_region_cmd = new CommandRegion(m_init);
348 m_region_tab->set_tab_region(TAB_ITEM, m_region_inv, TILEG_TAB_ITEM);
349 m_region_tab->set_tab_region(TAB_SPELL, m_region_spl, TILEG_TAB_SPELL);
350 m_region_tab->set_tab_region(TAB_MEMORISE, m_region_mem, TILEG_TAB_MEMORISE);
351 m_region_tab->set_tab_region(TAB_MONSTER, m_region_mon, TILEG_TAB_MONSTER);
352 m_region_tab->set_tab_region(TAB_SKILL, m_region_skl, TILEG_TAB_SKILL);
353 m_region_tab->set_tab_region(TAB_COMMAND, m_region_cmd, TILEG_TAB_COMMAND);
354 m_region_tab->activate_tab(TAB_ITEM);
356 m_region_msg = new MessageRegion(m_fonts[m_msg_font].font);
357 m_region_stat = new StatRegion(m_fonts[stat_font].font);
358 m_region_crt = new CRTRegion(m_fonts[m_crt_font].font);
360 m_region_menu = new MenuRegion(m_image, m_fonts[m_crt_font].font);
362 m_layers[LAYER_NORMAL].m_regions.push_back(m_region_tile);
363 m_layers[LAYER_NORMAL].m_regions.push_back(m_region_tab);
364 m_layers[LAYER_NORMAL].m_regions.push_back(m_region_msg);
365 m_layers[LAYER_NORMAL].m_regions.push_back(m_region_stat);
367 m_layers[LAYER_CRT].m_regions.push_back(m_region_crt);
368 m_layers[LAYER_CRT].m_regions.push_back(m_region_menu);
370 cgotoxy(1, 1, GOTO_CRT);
372 resize();
374 return (true);
377 int TilesFramework::load_font(const char *font_file, int font_size,
378 bool default_on_fail, bool outline)
380 for (unsigned int i = 0; i < m_fonts.size(); i++)
382 font_info &finfo = m_fonts[i];
383 if (finfo.name == font_file && finfo.size == font_size
384 && outline == finfo.outline)
386 return i;
390 FontWrapper *font = FontWrapper::create();
392 if (!font->load_font(font_file, font_size, outline))
394 delete font;
395 if (default_on_fail)
396 return (load_font(MONOSPACED_FONT, 12, false, outline));
397 else
398 return -1;
401 font_info finfo;
402 finfo.font = font;
403 finfo.name = font_file;
404 finfo.size = font_size;
405 finfo.outline = outline;
406 m_fonts.push_back(finfo);
408 return (m_fonts.size() - 1);
410 void TilesFramework::load_dungeon(const crawl_view_buffer &vbuf,
411 const coord_def &gc)
413 m_active_layer = LAYER_NORMAL;
415 m_region_tile->load_dungeon(vbuf, gc);
417 int ox = m_region_tile->mx/2;
418 int oy = m_region_tile->my/2;
419 coord_def win_start(gc.x - ox, gc.y - oy);
420 coord_def win_end(gc.x + ox + 1, gc.y + oy + 1);
422 if (m_region_map)
423 m_region_map->set_window(win_start, win_end);
426 void TilesFramework::load_dungeon(const coord_def &cen)
428 unwind_var<coord_def> viewp(crawl_view.viewp, cen - crawl_view.viewhalfsz);
429 unwind_var<coord_def> vgrdc(crawl_view.vgrdc, cen);
430 unwind_var<coord_def> vlos1(crawl_view.vlos1);
431 unwind_var<coord_def> vlos2(crawl_view.vlos2);
433 crawl_view.calc_vlos();
434 viewwindow(false);
435 tiles.place_cursor(CURSOR_MAP, cen);
438 void TilesFramework::resize()
440 calculate_default_options();
441 do_layout();
443 wm->resize(m_windowsz);
446 int TilesFramework::handle_mouse(MouseEvent &event)
448 // Note: the mouse event goes to all regions in the active layer because
449 // we want to be able to start some GUI event (e.g. far viewing) and
450 // stop if it moves to another region.
451 int return_key = 0;
452 for (unsigned int i = 0; i < m_layers[m_active_layer].m_regions.size(); i++)
454 // TODO enne - what if two regions give a key?
455 int key = 0;
456 key = m_layers[m_active_layer].m_regions[i]->handle_mouse(event);
457 if (key)
458 return_key = key;
461 // Let regions take priority in any mouse mode.
462 if (return_key)
463 return return_key;
465 // Handle "more" mode globally here, rather than duplicate across regions.
466 if (mouse_control::current_mode() == MOUSE_MODE_MORE
467 && event.event == MouseEvent::PRESS)
469 if (event.button == MouseEvent::LEFT)
470 return CK_MOUSE_CLICK;
471 else if (event.button == MouseEvent::RIGHT)
472 return CK_MOUSE_CMD;
475 // TODO enne - in what cases should the buttons be returned?
476 #if 0
477 // If nothing else, return the mouse button that was pressed.
478 switch (event.button)
480 case MouseEvent::LEFT:
481 return CK_MOUSE_B1;
482 case MouseEvent::RIGHT:
483 return CK_MOUSE_B2;
484 case MouseEvent::MIDDLE:
485 return CK_MOUSE_B3;
486 case MouseEvent::SCROLL_UP:
487 return CK_MOUSE_B4;
488 case MouseEvent::SCROLL_DOWN:
489 return CK_MOUSE_B5;
490 default:
491 case MouseEvent::NONE:
492 return 0;
494 #endif
496 return 0;
499 static unsigned int _timer_callback(unsigned int ticks)
501 // force the event loop to break
502 wm->raise_custom_event();
504 unsigned int res = Options.tile_tooltip_ms;
505 return (res);
508 int TilesFramework::getch_ck()
510 wm_event event;
511 cursor_loc cur_loc;
512 cursor_loc tip_loc;
513 cursor_loc last_loc;
515 int key = 0;
517 // Don't update tool tips etc. in targeting mode.
518 const bool mouse_target_mode
519 = (mouse_control::current_mode() == MOUSE_MODE_TARGET_PATH
520 || mouse_control::current_mode() == MOUSE_MODE_TARGET_DIR);
522 const unsigned int ticks_per_screen_redraw = Options.tile_update_rate;
524 unsigned int res = Options.tile_tooltip_ms;
525 wm->set_timer(res, &_timer_callback);
527 m_tooltip.clear();
528 std::string prev_alt = m_region_msg->alt_text();
529 m_region_msg->alt_text().clear();
531 if (need_redraw())
532 redraw();
534 while (!key)
536 unsigned int ticks = 0;
537 last_loc = m_cur_loc;
539 if (wm->wait_event(&event))
541 ticks = wm->get_ticks();
542 if (!mouse_target_mode)
544 if (event.type != WM_CUSTOMEVENT)
546 tiles.clear_text_tags(TAG_CELL_DESC);
547 m_region_msg->alt_text().clear();
550 if (tip_loc != m_cur_loc)
552 set_need_redraw();
554 // FIXME: I've no idea what the ~ is doing there, though
555 // it does mean the compiler complains about comparing
556 // signed and unsigned values, but without it, the alt.
557 // text gets displayed if I last did a mouseclick, which
558 // is exactly what I'm trying to avoid here. (jpeg)
559 if (m_last_tick_moved != UINT_MAX)
561 m_region_msg->alt_text().clear();
562 for (unsigned int i = 0;
563 i < m_layers[m_active_layer].m_regions.size(); ++i)
565 Region *reg = m_layers[m_active_layer].m_regions[i];
566 if (!reg->inside(m_mouse.x, m_mouse.y))
567 continue;
569 if (reg->update_alt_text(m_region_msg->alt_text()))
570 break;
573 if (prev_alt != m_region_msg->alt_text())
574 prev_alt = m_region_msg->alt_text();
579 switch (event.type)
581 case WM_ACTIVEEVENT:
582 // When game gains focus back then set mod state clean
583 // to get rid of stupid Windows/SDL bug with Alt-Tab.
584 if (event.active.gain != 0)
586 wm->set_mod_state(MOD_NONE);
587 set_need_redraw();
589 break;
590 case WM_KEYDOWN:
591 m_key_mod |= event.key.keysym.key_mod;
592 key = event.key.keysym.sym;
593 m_region_tile->place_cursor(CURSOR_MOUSE, Region::NO_CURSOR);
595 // If you hit a key, disable tooltips until the mouse
596 // is moved again.
597 m_last_tick_moved = UINT_MAX;
598 break;
600 case WM_KEYUP:
601 m_key_mod &= ~event.key.keysym.key_mod;
602 m_last_tick_moved = UINT_MAX;
603 break;
605 case WM_MOUSEMOTION:
607 // Record mouse pos for tooltip timer
608 // FIXME: This compares signed and unsigned ints
609 if (m_mouse.x != abs(event.mouse_event.px)
610 || m_mouse.y != abs(event.mouse_event.py))
612 m_last_tick_moved = ticks;
614 m_mouse.x = event.mouse_event.px;
615 m_mouse.y = event.mouse_event.py;
617 event.mouse_event.held = m_buttons_held;
618 event.mouse_event.mod = m_key_mod;
619 int mouse_key = handle_mouse(event.mouse_event);
621 // find mouse location
622 for (unsigned int i = 0;
623 i < m_layers[m_active_layer].m_regions.size(); i++)
625 Region *reg = m_layers[m_active_layer].m_regions[i];
626 if (reg->mouse_pos(m_mouse.x, m_mouse.y,
627 m_cur_loc.cx, m_cur_loc.cy))
629 m_cur_loc.reg = reg;
630 m_cur_loc.mode = mouse_control::current_mode();
631 break;
635 // Don't break back to Crawl and redraw for every mouse
636 // event, because there's a lot of them.
638 // If there are other mouse events in the queue
639 // (possibly because redrawing is slow or the user
640 // is moving the mouse really quickly), process those
641 // first, before bothering to redraw the screen.
642 unsigned int count = wm->get_event_count(WM_MOUSEMOTION);
643 ASSERT(count >= 0);
644 if (count > 0)
645 continue;
647 // Stay within this input loop until the mouse moves
648 // to a semantically different location. Crawl doesn't
649 // care about small mouse movements.
650 if (!need_redraw() && last_loc == m_cur_loc)
651 continue;
653 key = mouse_key;
655 break;
657 case WM_MOUSEBUTTONUP:
659 m_buttons_held &= ~(event.mouse_event.button);
660 event.mouse_event.held = m_buttons_held;
661 event.mouse_event.mod = m_key_mod;
662 key = handle_mouse(event.mouse_event);
663 m_last_tick_moved = UINT_MAX;
665 break;
667 case WM_MOUSEBUTTONDOWN:
669 m_buttons_held |= event.mouse_event.button;
670 event.mouse_event.held = m_buttons_held;
671 event.mouse_event.mod = m_key_mod;
672 key = handle_mouse(event.mouse_event);
673 m_last_tick_moved = UINT_MAX;
675 break;
677 case WM_QUIT:
678 if (crawl_state.need_save)
679 save_game(true);
680 exit(0);
681 break;
683 case WM_CUSTOMEVENT:
684 default:
685 // This is only used to refresh the tooltip.
686 break;
690 if (!mouse_target_mode)
692 const bool timeout = (ticks > m_last_tick_moved
693 && (ticks - m_last_tick_moved
694 > (unsigned int)Options.tile_tooltip_ms));
696 if (timeout)
698 tip_loc = m_cur_loc;
699 tiles.clear_text_tags(TAG_CELL_DESC);
700 if (Options.tile_tooltip_ms > 0 && m_tooltip.empty())
702 for (unsigned int i = 0;
703 i < m_layers[m_active_layer].m_regions.size(); ++i)
705 Region *reg = m_layers[m_active_layer].m_regions[i];
706 if (!reg->inside(m_mouse.x, m_mouse.y))
707 continue;
708 if (reg->update_tip_text(m_tooltip))
710 set_need_redraw();
711 break;
716 else
718 if (last_loc != m_cur_loc)
719 set_need_redraw();
721 m_tooltip.clear();
722 tip_loc.reset();
725 if (need_redraw()
726 || ticks > m_last_tick_redraw
727 && ticks - m_last_tick_redraw > ticks_per_screen_redraw)
729 redraw();
734 // We got some input, so we'll probably have to redraw something.
735 set_need_redraw();
737 wm->set_timer(0, NULL);
739 return key;
742 static const int map_margin = 2;
743 static const int map_stat_margin = 4;
744 static const int crt_width = 80;
745 static const int crt_height = 30;
746 static const int min_stat_height = 12;
747 static const int min_inv_height = 4;
748 static const int max_inv_height = 6;
749 static const int max_mon_height = 3;
751 // Width of status area in characters.
752 static const int stat_width = 42;
755 * Calculates and sets the layout of the main game screen, based on the
756 * available screen estate (window or screensize) and options.
758 void TilesFramework::do_layout()
760 // View size in pixels is (m_viewsc * crawl_view.viewsz)
761 m_viewsc.x = 32;
762 m_viewsc.y = 32;
764 crawl_view.viewsz.x = Options.view_max_width;
765 crawl_view.viewsz.y = Options.view_max_height;
767 // Initial sizes.
768 m_region_tile->dx = m_viewsc.x;
769 m_region_tile->dy = m_viewsc.y;
771 // Locations in pixels. stat_x_divider is the dividing vertical line
772 // between dungeon view on the left and status area on the right.
773 // message_y_divider is the horizontal line between dungeon view on
774 // the top and message window at the bottom.
775 m_stat_x_divider = 0;
776 int message_y_divider = 0;
778 bool message_overlay = Options.tile_force_overlay ? true : false;
780 // We ignore Options.view_max_width and use the maximum space available.
781 int available_width_in_tiles = 0;
783 available_width_in_tiles = (m_windowsz.x - stat_width
784 * m_region_stat->dx) / m_region_tile->dx;
786 // Scale the dungeon region tiles so we have enough space to
787 // display full LOS.
788 if (available_width_in_tiles < ENV_SHOW_DIAMETER)
790 m_region_tile->dx = (m_windowsz.x - stat_width * m_region_stat->dx)
791 / ENV_SHOW_DIAMETER;
792 m_region_tile->dy = m_region_tile->dx;
793 available_width_in_tiles = ENV_SHOW_DIAMETER;
796 m_stat_x_divider = available_width_in_tiles * m_region_tile->dx;
798 // Calculate message_y_divider. First off, if we have already decided to
799 // use the overlay, we can place the divider to the bottom of the screen.
800 if (message_overlay)
802 message_y_divider = m_windowsz.y;
805 // Then, the optimal situation without the overlay - we can fit both
806 // Options.view_max_height and at least Options.msg_min_height in the space.
807 if (std::max(Options.view_max_height, ENV_SHOW_DIAMETER)
808 * m_region_tile->dy + Options.msg_min_height
809 * m_region_msg->dy
810 <= m_windowsz.y && !message_overlay)
812 message_y_divider = std::max(Options.view_max_height, ENV_SHOW_DIAMETER)
813 * m_region_tile->dy;
814 message_y_divider = std::max(message_y_divider, m_windowsz.y -
815 Options.msg_max_height * m_region_msg->dy);
817 else
819 int available_height_in_tiles = 0;
820 available_height_in_tiles = (m_windowsz.y - (message_overlay
821 ? 0 : (Options.msg_min_height
822 * m_region_msg->dy)))
823 / m_region_tile->dy;
825 // If we can't fit the full LOS to the available space, try using the
826 // message overlay.
827 if (available_height_in_tiles < ENV_SHOW_DIAMETER)
829 message_y_divider = m_windowsz.y;
830 message_overlay = true;
832 // If using message_overlay isn't enough, scale the dungeon region
833 // tiles to fit full LOS into the available space.
834 if (m_windowsz.y / m_region_tile->dy < ENV_SHOW_DIAMETER)
836 m_region_tile->dy = m_windowsz.y / ENV_SHOW_DIAMETER;
837 m_region_tile->dx = m_region_tile->dy;
840 else
842 message_y_divider = available_height_in_tiles * m_region_tile->dy;
846 // Resize and place the dungeon region.
847 m_region_tile->resize_to_fit(m_stat_x_divider, message_y_divider);
848 m_region_tile->place(0, 0, 0);
850 crawl_view.viewsz.x = m_region_tile->mx;
851 crawl_view.viewsz.y = m_region_tile->my;
853 // Resize and place the message window.
854 if (message_overlay)
856 m_region_msg->place(0, 0, 0); // TODO: Maybe add an option to place
857 // overlay at the bottom.
858 m_region_msg->resize_to_fit(m_stat_x_divider, Options.msg_min_height
859 * m_region_msg->dy);
860 m_region_msg->set_overlay(message_overlay);
862 else
864 m_region_msg->resize_to_fit(m_stat_x_divider, m_windowsz.y
865 - message_y_divider);
866 m_region_msg->place(0, m_region_tile->ey, 0);
869 crawl_view.msgsz.x = m_region_msg->mx;
870 crawl_view.msgsz.y = m_region_msg->my;
872 m_stat_col = m_stat_x_divider + map_stat_margin;
874 m_region_stat->resize_to_fit(m_windowsz.x - m_stat_x_divider, m_windowsz.y);
875 m_region_stat->place(m_stat_col, 0, 0);
876 m_region_stat->resize(m_region_stat->mx, min_stat_height);
878 layout_statcol();
880 m_region_crt->place(0, 0, 0);
881 m_region_crt->resize_to_fit(m_windowsz.x, m_windowsz.y);
883 m_region_menu->place(0, 0, 0);
884 m_region_menu->resize_to_fit(m_windowsz.x, m_windowsz.y);
886 crawl_view.init_view();
889 void TilesFramework::autosize_minimap()
891 const int horiz = (m_windowsz.x - m_stat_col - map_margin * 2) / GXM;
892 const int vert = (m_statcol_bottom - (m_region_map->sy ? m_region_map->sy
893 : m_statcol_top)
894 - map_margin * 2) / GYM;
895 m_region_map->dx = m_region_map->dy = std::min(horiz, vert);
898 void TilesFramework::place_minimap()
900 m_region_map = new MapRegion(m_map_pixels);
902 if (GXM * m_region_map->dx > m_windowsz.x - m_stat_col)
903 autosize_minimap();
905 m_region_map->resize(GXM, GYM);
906 if (m_region_map->dy * GYM > m_statcol_bottom - m_statcol_top)
908 delete m_region_map;
909 m_region_map = NULL;
910 return;
913 m_layers[LAYER_NORMAL].m_regions.push_back(m_region_map);
914 m_region_map->place(m_stat_col, m_region_stat->ey, map_margin);
916 m_statcol_top = m_region_map->ey;
919 int TilesFramework::calc_tab_lines(const int num_elements)
921 // Integer divison rounded up
922 return (num_elements - 1) / m_region_tab->mx + 1;
925 void TilesFramework::place_tab(int idx)
927 if (idx == -1)
928 return;
930 int min_ln = 1, max_ln = 1;
931 switch (idx)
933 case TAB_SPELL:
934 if (you.spell_no == 0)
936 m_region_tab->enable_tab(TAB_SPELL);
937 return;
939 max_ln = calc_tab_lines(you.spell_no);
940 break;
941 case TAB_MONSTER:
942 max_ln = max_mon_height;
943 break;
944 case TAB_COMMAND:
945 min_ln = max_ln = calc_tab_lines(n_common_commands);
946 break;
947 case TAB_SKILL:
948 min_ln = max_ln = calc_tab_lines(NUM_SKILLS);
949 break;
952 int lines = std::min(max_ln, (m_statcol_bottom - m_statcol_top - m_tab_margin)
953 / m_region_tab->dy);
954 if (lines >= min_ln)
956 TabbedRegion* region_tab = new TabbedRegion(m_init);
957 region_tab->set_tab_region(0, m_region_tab->get_tab_region(idx),
958 m_region_tab->get_tab_tile(idx));
959 m_tabs[idx] = region_tab;
960 region_tab->activate_tab(0);
961 m_region_tab->disable_tab(idx);
962 m_layers[LAYER_NORMAL].m_regions.push_back(region_tab);
963 region_tab->place(m_stat_col, m_statcol_bottom
964 - lines * m_region_tab->dy);
965 region_tab->resize(m_region_tab->mx, lines);
966 m_statcol_bottom = region_tab->sy - m_tab_margin;
968 else
969 m_region_tab->enable_tab(idx);
972 void TilesFramework::resize_inventory()
974 int lines = std::min(max_inv_height - min_inv_height,
975 (m_statcol_bottom - m_statcol_top) / m_region_tab->dy);
976 if (lines == 0)
977 return;
979 int prev_size = m_region_tab->wy;
981 m_region_tab->resize(m_region_tab->mx, min_inv_height + lines);
982 m_region_tab->place(m_stat_col, m_windowsz.y - m_region_tab->wy);
984 int delta_y = m_region_tab->wy - prev_size;
985 for (tab_iterator it = m_tabs.begin(); it != m_tabs.end(); ++it)
986 it->second->place(it->second->sx, it->second->sy - delta_y);
988 m_statcol_bottom -= delta_y;
991 void TilesFramework::place_gold_turns()
993 if (m_statcol_bottom - m_statcol_top < m_region_stat->dy)
994 return;
996 Options.show_gold_turns = true;
997 ++crawl_view.hudsz.y;
998 m_region_stat->resize(m_region_stat->mx, crawl_view.hudsz.y);
999 if (m_region_map)
1000 m_region_map->place(m_region_stat->sx, m_region_stat->ey);
1002 m_statcol_top += m_region_stat->dy;
1005 void TilesFramework::layout_statcol()
1007 for (tab_iterator it = m_tabs.begin(); it != m_tabs.end(); ++it)
1009 delete it->second;
1010 m_layers[LAYER_NORMAL].m_regions.pop_back();
1012 m_tabs.clear();
1014 if (m_region_map)
1016 delete m_region_map;
1017 m_region_map = NULL;
1018 m_layers[LAYER_NORMAL].m_regions.pop_back();
1020 Options.show_gold_turns = false;
1021 crawl_view.hudsz.y = min_stat_height;
1022 m_region_stat->resize(m_region_stat->mx, crawl_view.hudsz.y);
1024 m_statcol_top = m_region_stat->ey;
1026 // Set the inventory region to minimal size.
1027 m_region_tab->place(m_stat_col, m_statcol_top);
1028 m_region_tab->resize_to_fit(m_windowsz.x - m_region_tab->sx,
1029 m_windowsz.y - m_region_tab->sy);
1030 m_region_tab->resize(m_region_tab->mx, min_inv_height);
1031 m_region_tab->place(m_stat_col, m_windowsz.y - m_region_tab->wy);
1033 m_statcol_bottom = m_region_tab->sy - m_tab_margin;
1035 for (int i = 0, size = Options.tile_layout_priority.size(); i < size; ++i)
1037 std::string str = Options.tile_layout_priority[i];
1038 if (str == "inventory")
1039 resize_inventory();
1040 else if (str == "minimap" || str == "map")
1041 place_minimap();
1042 else if (str == "gold_turn" || str == "gold_turns")
1043 place_gold_turns();
1044 else
1045 place_tab(m_region_tab->find_tab(str));
1047 // We stretch the minimap so it is centered in the space left.
1048 if (m_region_map)
1050 if (!Options.tile_map_pixels)
1051 autosize_minimap();
1053 m_region_map->place(m_region_stat->sx, m_region_stat->ey,
1054 m_region_stat->ex, m_statcol_bottom,
1055 map_margin);
1056 tile_new_level(false, false);
1060 void TilesFramework::clrscr()
1062 TextRegion::cursor_region = NULL;
1064 if (m_region_stat)
1065 m_region_stat->clear();
1066 if (m_region_msg)
1067 m_region_msg->clear();
1068 if (m_region_crt)
1069 m_region_crt->clear();
1070 if (m_region_menu)
1071 m_region_menu->clear();
1073 cgotoxy(1,1);
1075 set_need_redraw();
1078 int TilesFramework::get_number_of_lines()
1080 return m_region_crt->my;
1083 int TilesFramework::get_number_of_cols()
1085 switch (m_active_layer)
1087 default:
1088 return 0;
1089 case LAYER_NORMAL:
1090 return m_region_msg->mx;
1091 case LAYER_CRT:
1092 return m_region_crt->mx;
1096 void TilesFramework::cgotoxy(int x, int y, GotoRegion region)
1098 switch (region)
1100 case GOTO_CRT:
1101 m_active_layer = LAYER_CRT;
1102 TextRegion::text_mode = m_region_crt;
1103 break;
1104 case GOTO_MSG:
1105 m_active_layer = LAYER_NORMAL;
1106 TextRegion::text_mode = m_region_msg;
1107 break;
1108 case GOTO_STAT:
1109 m_active_layer = LAYER_NORMAL;
1110 TextRegion::text_mode = m_region_stat;
1111 break;
1112 default:
1113 die("invalid cgotoxy region in tiles: %d", region);
1114 break;
1117 TextRegion::cgotoxy(x, y);
1120 GotoRegion TilesFramework::get_cursor_region() const
1122 if (TextRegion::text_mode == m_region_crt)
1123 return (GOTO_CRT);
1124 if (TextRegion::text_mode == m_region_msg)
1125 return (GOTO_MSG);
1126 if (TextRegion::text_mode == m_region_stat)
1127 return (GOTO_STAT);
1129 die("Bogus region");
1130 return (GOTO_CRT);
1133 // #define DEBUG_TILES_REDRAW
1134 void TilesFramework::redraw()
1136 #ifdef DEBUG_TILES_REDRAW
1137 cprintf("\nredrawing tiles");
1138 #endif
1139 m_need_redraw = false;
1141 glmanager->reset_view_for_redraw(m_viewsc.x, m_viewsc.y);
1143 for (unsigned int i = 0; i < m_layers[m_active_layer].m_regions.size(); ++i)
1144 m_layers[m_active_layer].m_regions[i]->render();
1146 // Draw tooltip
1147 if (Options.tile_tooltip_ms > 0 && !m_tooltip.empty())
1149 const coord_def min_pos(0, 0);
1150 FontWrapper *font = m_fonts[m_tip_font].font;
1152 font->render_string(m_mouse.x, m_mouse.y - 2, m_tooltip.c_str(),
1153 min_pos, m_windowsz, WHITE, false, 220, BLUE, 5,
1154 true);
1156 wm->swap_buffers();
1158 m_last_tick_redraw = wm->get_ticks();
1161 static map_feature get_cell_map_feature(const map_cell& cell)
1163 map_feature mf = MF_SKIP;
1164 if (cell.invisible_monster())
1165 mf = MF_MONS_HOSTILE;
1166 else if (cell.monster() != MONS_NO_MONSTER)
1168 switch (cell.monsterinfo() ? cell.monsterinfo()->attitude : ATT_HOSTILE)
1170 case ATT_FRIENDLY:
1171 mf = MF_MONS_FRIENDLY;
1172 break;
1173 case ATT_GOOD_NEUTRAL:
1174 mf = MF_MONS_PEACEFUL;
1175 break;
1176 case ATT_NEUTRAL:
1177 case ATT_STRICT_NEUTRAL:
1178 mf = MF_MONS_NEUTRAL;
1179 break;
1180 case ATT_HOSTILE:
1181 default:
1182 if (mons_class_flag(cell.monster(), M_NO_EXP_GAIN))
1183 mf = MF_MONS_NO_EXP;
1184 else
1185 mf = MF_MONS_HOSTILE;
1186 break;
1189 else if (cell.cloud())
1191 show_type show;
1192 show.cls = SH_CLOUD;
1193 mf = get_feature_def(show).minimap;
1196 if (mf == MF_SKIP && cell.item())
1197 mf = get_feature_def(*cell.item()).minimap;
1198 if (mf == MF_SKIP)
1199 mf = get_feature_def(cell.feat()).minimap;
1200 if (mf == MF_SKIP)
1201 mf = MF_UNSEEN;
1203 if (mf == MF_WALL || mf == MF_FLOOR)
1205 if (cell.known() && !cell.seen()
1206 || cell.detected_item()
1207 || cell.detected_monster())
1209 mf = (mf == MF_WALL) ? MF_MAP_WALL : MF_MAP_FLOOR;
1213 return mf;
1216 void TilesFramework::update_minimap(const coord_def& gc)
1218 if (!player_in_mappable_area() || !m_region_map)
1219 return;
1221 map_feature mf;
1223 if (!crawl_state.game_is_arena() && gc == you.pos() && you.on_current_level)
1224 mf = MF_PLAYER;
1225 else
1226 mf = get_cell_map_feature(env.map_knowledge(gc));
1228 // XXX: map_cell show have exclusion info
1229 if (mf == MF_FLOOR || mf == MF_MAP_FLOOR || mf == MF_WATER)
1231 if (is_exclude_root(gc))
1232 mf = MF_EXCL_ROOT;
1233 else if (is_excluded(gc))
1234 mf = MF_EXCL;
1237 m_region_map->set(gc, mf);
1240 void TilesFramework::clear_minimap()
1242 if (m_region_map)
1243 m_region_map->clear();
1246 void TilesFramework::update_minimap_bounds()
1248 if (m_region_map)
1249 m_region_map->update_bounds();
1252 void TilesFramework::update_tabs()
1254 if (!Options.tile_show_items || crawl_state.game_is_arena())
1255 return;
1257 m_region_tab->update();
1258 for (tab_iterator it = m_tabs.begin(); it != m_tabs.end(); ++it)
1259 it->second->update();
1262 void TilesFramework::toggle_inventory_display()
1264 int idx = m_region_tab->active_tab();
1265 m_region_tab->activate_tab((idx + 1) % m_region_tab->num_tabs());
1269 void TilesFramework::place_cursor(cursor_type type, const coord_def &gc)
1271 m_region_tile->place_cursor(type, gc);
1274 void TilesFramework::clear_text_tags(text_tag_type type)
1276 m_region_tile->clear_text_tags(type);
1279 void TilesFramework::add_text_tag(text_tag_type type, const std::string &tag,
1280 const coord_def &gc)
1282 m_region_tile->add_text_tag(type, tag, gc);
1285 void TilesFramework::add_text_tag(text_tag_type type, const monster* mon)
1287 // HACK. Large-tile monsters don't interact well with name tags.
1288 if (mon->type == MONS_PANDEMONIUM_DEMON
1289 || mon->type == MONS_LERNAEAN_HYDRA)
1291 return;
1294 const coord_def &gc = mon->pos();
1296 if (mons_is_pghost(mon->type))
1298 // Beautification hack. "Foo's ghost" is a little bit
1299 // verbose as a tag. "Foo" on its own should be sufficient.
1300 tiles.add_text_tag(TAG_NAMED_MONSTER, mon->mname, gc);
1302 else
1303 tiles.add_text_tag(TAG_NAMED_MONSTER, mon->name(DESC_PLAIN), gc);
1306 const coord_def &TilesFramework::get_cursor() const
1308 return (m_region_tile->get_cursor());
1311 void TilesFramework::add_overlay(const coord_def &gc, tileidx_t idx)
1313 m_region_tile->add_overlay(gc, idx);
1316 void TilesFramework::clear_overlays()
1318 m_region_tile->clear_overlays();
1321 void TilesFramework::set_need_redraw(unsigned int min_tick_delay)
1323 unsigned int ticks = (wm->get_ticks() - m_last_tick_redraw);
1324 if (min_tick_delay && ticks <= min_tick_delay)
1325 return;
1327 m_need_redraw = true;
1330 bool TilesFramework::need_redraw() const
1332 return m_need_redraw;
1335 int TilesFramework::to_lines(int num_tiles)
1337 return num_tiles * TILE_Y / get_crt_font()->char_height();
1340 #endif