11 #include "glwrapper.h"
14 #include "map_knowledge.h"
21 #include "tiledef-dngn.h"
22 #include "tiledef-gui.h"
23 #include "tiledef-main.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"
48 #include "windowmanager.h"
50 #ifdef TARGET_OS_WINDOWS
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
62 // Default Screen Settings
63 // width, height, map, crt, stat, msg, tip, lbl
64 static int _screen_sizes
[4][8] =
67 {1024, 700, 3, 15, 16, 14, 15, 14},
69 {1024, 600, 2, 14, 14, 12, 13, 12},
71 {800, 600, 2, 14, 11, 12, 13, 12},
73 {800, 480, 2, 13, 12, 10, 13, 11}
78 TilesFramework::TilesFramework() :
79 m_windowsz(1024, 768),
83 m_active_layer(LAYER_CRT
),
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");
105 // Redirect output to the console
106 attach_console((DWORD
)-1);
107 freopen("CONOUT$", "wb", stdout
);
108 freopen("CONOUT$", "wb", stderr
);
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");
123 void TilesFramework::shutdown()
125 delete m_region_tile
;
126 delete m_region_stat
;
135 delete m_region_menu
;
137 m_region_tile
= NULL
;
138 m_region_stat
= NULL
;
147 m_region_menu
= NULL
;
149 for (tab_iterator it
= m_tabs
.begin(); it
!= m_tabs
.end(); ++it
)
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
;
165 GLStateManager::shutdown();
166 WindowManager::shutdown();
172 * Creates a new title region and sets it active
173 * Remember to call hide_title() when you're done
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
;
187 * Updates the loading message text on the title
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
);
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));
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
);
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
;
241 m_layers
[LAYER_TILE_CONTROL
].m_regions
.clear();
244 void TilesFramework::calculate_default_options()
246 // Find which set of _screen_sizes to use.
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])
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);
270 m_tab_margin
= Options
.tile_font_lbl_size
+ 4;
274 bool TilesFramework::initialise()
278 const char *icon_name
=
282 #ifdef TARGET_OS_MACOSX
283 "dat/tiles/stone_soup_icon-512x512.png";
285 "dat/tiles/stone_soup_icon-32x32.png";
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();
297 // Initialize the wrapper
298 if (!wm
->init(&m_windowsz
))
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
))
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)
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
);
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
)
390 FontWrapper
*font
= FontWrapper::create();
392 if (!font
->load_font(font_file
, font_size
, outline
))
396 return (load_font(MONOSPACED_FONT
, 12, false, outline
));
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
,
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);
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();
435 tiles
.place_cursor(CURSOR_MAP
, cen
);
438 void TilesFramework::resize()
440 calculate_default_options();
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.
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?
456 key
= m_layers
[m_active_layer
].m_regions
[i
]->handle_mouse(event
);
461 // Let regions take priority in any mouse mode.
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
)
475 // TODO enne - in what cases should the buttons be returned?
477 // If nothing else, return the mouse button that was pressed.
478 switch (event
.button
)
480 case MouseEvent::LEFT
:
482 case MouseEvent::RIGHT
:
484 case MouseEvent::MIDDLE
:
486 case MouseEvent::SCROLL_UP
:
488 case MouseEvent::SCROLL_DOWN
:
491 case MouseEvent::NONE
:
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
;
508 int TilesFramework::getch_ck()
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
);
528 std::string prev_alt
= m_region_msg
->alt_text();
529 m_region_msg
->alt_text().clear();
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
)
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
))
569 if (reg
->update_alt_text(m_region_msg
->alt_text()))
573 if (prev_alt
!= m_region_msg
->alt_text())
574 prev_alt
= m_region_msg
->alt_text();
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
);
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
597 m_last_tick_moved
= UINT_MAX
;
601 m_key_mod
&= ~event
.key
.keysym
.key_mod
;
602 m_last_tick_moved
= UINT_MAX
;
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
))
630 m_cur_loc
.mode
= mouse_control::current_mode();
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
);
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
)
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
;
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
;
678 if (crawl_state
.need_save
)
685 // This is only used to refresh the tooltip.
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
));
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
))
708 if (reg
->update_tip_text(m_tooltip
))
718 if (last_loc
!= m_cur_loc
)
726 || ticks
> m_last_tick_redraw
727 && ticks
- m_last_tick_redraw
> ticks_per_screen_redraw
)
734 // We got some input, so we'll probably have to redraw something.
737 wm
->set_timer(0, NULL
);
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)
764 crawl_view
.viewsz
.x
= Options
.view_max_width
;
765 crawl_view
.viewsz
.y
= Options
.view_max_height
;
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
788 if (available_width_in_tiles
< ENV_SHOW_DIAMETER
)
790 m_region_tile
->dx
= (m_windowsz
.x
- stat_width
* m_region_stat
->dx
)
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.
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
810 <= m_windowsz
.y
&& !message_overlay
)
812 message_y_divider
= std::max(Options
.view_max_height
, ENV_SHOW_DIAMETER
)
814 message_y_divider
= std::max(message_y_divider
, m_windowsz
.y
-
815 Options
.msg_max_height
* m_region_msg
->dy
);
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
)))
825 // If we can't fit the full LOS to the available space, try using the
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
;
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.
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
860 m_region_msg
->set_overlay(message_overlay
);
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
);
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
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
)
905 m_region_map
->resize(GXM
, GYM
);
906 if (m_region_map
->dy
* GYM
> m_statcol_bottom
- m_statcol_top
)
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
)
930 int min_ln
= 1, max_ln
= 1;
934 if (you
.spell_no
== 0)
936 m_region_tab
->enable_tab(TAB_SPELL
);
939 max_ln
= calc_tab_lines(you
.spell_no
);
942 max_ln
= max_mon_height
;
945 min_ln
= max_ln
= calc_tab_lines(n_common_commands
);
948 min_ln
= max_ln
= calc_tab_lines(NUM_SKILLS
);
952 int lines
= std::min(max_ln
, (m_statcol_bottom
- m_statcol_top
- m_tab_margin
)
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
;
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
);
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
)
996 Options
.show_gold_turns
= true;
997 ++crawl_view
.hudsz
.y
;
998 m_region_stat
->resize(m_region_stat
->mx
, crawl_view
.hudsz
.y
);
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
)
1010 m_layers
[LAYER_NORMAL
].m_regions
.pop_back();
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")
1040 else if (str
== "minimap" || str
== "map")
1042 else if (str
== "gold_turn" || str
== "gold_turns")
1045 place_tab(m_region_tab
->find_tab(str
));
1047 // We stretch the minimap so it is centered in the space left.
1050 if (!Options
.tile_map_pixels
)
1053 m_region_map
->place(m_region_stat
->sx
, m_region_stat
->ey
,
1054 m_region_stat
->ex
, m_statcol_bottom
,
1056 tile_new_level(false, false);
1060 void TilesFramework::clrscr()
1062 TextRegion::cursor_region
= NULL
;
1065 m_region_stat
->clear();
1067 m_region_msg
->clear();
1069 m_region_crt
->clear();
1071 m_region_menu
->clear();
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
)
1090 return m_region_msg
->mx
;
1092 return m_region_crt
->mx
;
1096 void TilesFramework::cgotoxy(int x
, int y
, GotoRegion region
)
1101 m_active_layer
= LAYER_CRT
;
1102 TextRegion::text_mode
= m_region_crt
;
1105 m_active_layer
= LAYER_NORMAL
;
1106 TextRegion::text_mode
= m_region_msg
;
1109 m_active_layer
= LAYER_NORMAL
;
1110 TextRegion::text_mode
= m_region_stat
;
1113 die("invalid cgotoxy region in tiles: %d", region
);
1117 TextRegion::cgotoxy(x
, y
);
1120 GotoRegion
TilesFramework::get_cursor_region() const
1122 if (TextRegion::text_mode
== m_region_crt
)
1124 if (TextRegion::text_mode
== m_region_msg
)
1126 if (TextRegion::text_mode
== m_region_stat
)
1129 die("Bogus region");
1133 // #define DEBUG_TILES_REDRAW
1134 void TilesFramework::redraw()
1136 #ifdef DEBUG_TILES_REDRAW
1137 cprintf("\nredrawing tiles");
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();
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,
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
)
1171 mf
= MF_MONS_FRIENDLY
;
1173 case ATT_GOOD_NEUTRAL
:
1174 mf
= MF_MONS_PEACEFUL
;
1177 case ATT_STRICT_NEUTRAL
:
1178 mf
= MF_MONS_NEUTRAL
;
1182 if (mons_class_flag(cell
.monster(), M_NO_EXP_GAIN
))
1183 mf
= MF_MONS_NO_EXP
;
1185 mf
= MF_MONS_HOSTILE
;
1189 else if (cell
.cloud())
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
;
1199 mf
= get_feature_def(cell
.feat()).minimap
;
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
;
1216 void TilesFramework::update_minimap(const coord_def
& gc
)
1218 if (!player_in_mappable_area() || !m_region_map
)
1223 if (!crawl_state
.game_is_arena() && gc
== you
.pos() && you
.on_current_level
)
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
))
1233 else if (is_excluded(gc
))
1237 m_region_map
->set(gc
, mf
);
1240 void TilesFramework::clear_minimap()
1243 m_region_map
->clear();
1246 void TilesFramework::update_minimap_bounds()
1249 m_region_map
->update_bounds();
1252 void TilesFramework::update_tabs()
1254 if (!Options
.tile_show_items
|| crawl_state
.game_is_arena())
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
)
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
);
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
)
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();