From 4fb08daae5945c60de47d4690b400dbffba7ddb3 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Wed, 6 Aug 2025 11:41:02 -0500 Subject: [PATCH] Refactor minimap rendering and fix tiny town crash --- src/game/boe.items.cpp | 4 +- src/game/boe.town.cpp | 142 ++++++++++++++++++++++++----------------- src/game/boe.town.hpp | 1 + 3 files changed, 87 insertions(+), 60 deletions(-) diff --git a/src/game/boe.items.cpp b/src/game/boe.items.cpp index 049ee77e..6ffaba6a 100644 --- a/src/game/boe.items.cpp +++ b/src/game/boe.items.cpp @@ -683,6 +683,8 @@ short get_num_of_items(short max_num) { return minmax(0,max_num,numPanel.getResult()); } +// extern declaration doesn't work here?? +const short view_max_dim = 40; void init_mini_map() { double map_scale = get_ui_scale_map(); if (map_scale < 0.1) map_scale = 1.0; @@ -702,7 +704,7 @@ void init_mini_map() { makeFrontWindow(mainPtr(), mini_map()); // Create and initialize map gworld - if(!map_gworld().create(384, 384)) { + if(!map_gworld().create(view_max_dim * 6, view_max_dim * 6)) { play_sound(2); throw std::string("Failed to initialized automap!"); } else { diff --git a/src/game/boe.town.cpp b/src/game/boe.town.cpp index 46a0cfbb..acc15dab 100644 --- a/src/game/boe.town.cpp +++ b/src/game/boe.town.cpp @@ -1283,75 +1283,57 @@ void clear_map() { draw_map(true); } +const short view_max_dim = 40; +rectangle minimap_view_rect() { + rectangle view_rect; + short total_size, party_loc_x, party_loc_y; + + if((is_out()) || ((is_combat()) && (which_combat_type == 0)) || + ((overall_mode == MODE_TALKING) && (store_pre_talk_mode == MODE_OUTDOORS)) || + ((overall_mode == MODE_SHOPPING) && (store_pre_shop_mode == MODE_OUTDOORS))) { + total_size = 48; + party_loc_x = univ.party.loc_in_sec.x; + party_loc_y = univ.party.loc_in_sec.y; + } + else { + total_size = univ.town->max_dim; + party_loc_x = univ.party.town_loc.x; + party_loc_y = univ.party.town_loc.y; + } + if(total_size <= view_max_dim){ + view_rect = {0, 0, total_size, total_size}; + }else{ + short scroll_max = total_size - view_max_dim; + view_rect.left = minmax(0, scroll_max, party_loc_x - view_max_dim / 2); + view_rect.top = minmax(0, scroll_max, party_loc_y - view_max_dim / 2); + view_rect.right = view_rect.left + view_max_dim; + view_rect.bottom = view_rect.top + view_max_dim; + } + return view_rect; +} + void draw_map(bool need_refresh) { if(!map_visible) return; pic_num_t pic; - rectangle the_rect,map_world_rect = {0,0,384,384}; + rectangle the_rect; + + rectangle map_world_rect(map_gworld()); location where; location kludge; rectangle draw_rect,orig_draw_rect = {0,0,6,6},ter_temp_from,base_source_rect = {0,0,12,12}; rectangle dlogpicrect = {6,6,42,42}; bool draw_pcs = true,out_mode; - rectangle view_rect= {0,0,48,48},tiny_rect = {0,0,32,32}, - redraw_rect = {0,0,48,48},big_rect = {0,0,64,64}; // Rectangle visible in view screen - rectangle area_to_draw_from,area_to_draw_on = {29,47,269,287}; + rectangle area_to_draw_on = {29,47,269,287}; ter_num_t what_ter; bool expl; - short total_size = 48; // if full redraw, use this to figure out everything rectangle custom_from; town_map_adj.x = 0; town_map_adj.y = 0; - // view rect is rect that is visible, redraw rect is area to redraw now - // area_to_draw_from is final draw from rect - // area_to_draw_on is final draw to rect - // extern short store_pre_shop_mode,store_pre_talk_mode; - if((is_out()) || ((is_combat()) && (which_combat_type == 0)) || - ((overall_mode == MODE_TALKING) && (store_pre_talk_mode == MODE_OUTDOORS)) || - ((overall_mode == MODE_SHOPPING) && (store_pre_shop_mode == MODE_OUTDOORS))) { - view_rect.left = minmax(0,8,univ.party.loc_in_sec.x - 20); - view_rect.right = view_rect.left + 40; - view_rect.top = minmax(0,8,univ.party.loc_in_sec.y - 20); - view_rect.bottom = view_rect.top + 40; - redraw_rect = view_rect; - } - else { - total_size = univ.town->max_dim; - switch(total_size) { - case 64: - view_rect.left = minmax(0,24,univ.party.town_loc.x - 20); - view_rect.right = view_rect.left + 40; - view_rect.top = minmax(0,24,univ.party.town_loc.y - 20); - view_rect.bottom = view_rect.top + 40; - redraw_rect = big_rect; - break; - case 48: - view_rect.left = minmax(0,8,univ.party.town_loc.x - 20); - view_rect.right = view_rect.left + 40; - view_rect.top = minmax(0,8,univ.party.town_loc.y - 20); - view_rect.bottom = view_rect.top + 40; - redraw_rect = view_rect; - break; - case 32: - view_rect = tiny_rect; - redraw_rect = view_rect; - break; - } - } - if((is_out()) || ((is_combat()) && (which_combat_type == 0)) || - ((overall_mode == MODE_TALKING) && (store_pre_talk_mode == MODE_OUTDOORS)) || - ((overall_mode == MODE_SHOPPING) && (store_pre_shop_mode == MODE_OUTDOORS)) || - is_town() || is_combat()) { - area_to_draw_from = view_rect; - area_to_draw_from.width() = 40; - area_to_draw_from.height() = 40; - area_to_draw_from.left *= 6; - area_to_draw_from.right *= 6; - area_to_draw_from.top *= 6; - area_to_draw_from.bottom *= 6; - } + // We use view_rect as a camera to show only as much of the map as can fit in the window + rectangle view_rect = minimap_view_rect(); if(is_combat()) draw_pcs = false; @@ -1376,7 +1358,7 @@ void draw_map(bool need_refresh) { // Now, if shopping or talking, just don't touch anything. if((overall_mode == MODE_SHOPPING) || (overall_mode == MODE_TALKING)) - redraw_rect.right = -1; + view_rect.right = -1; // Otherwise, clear to black first: else fill_rect(map_gworld(), map_world_rect, sf::Color::Black); @@ -1388,12 +1370,11 @@ void draw_map(bool need_refresh) { out_mode = true; else out_mode = false; - // TODO: It could be possible to draw the entire map here and then only refresh if a spot actually changes terrain type sf::Texture& small_ter_gworld = *ResMgr::graphics.get("termap"); - for(where.x = redraw_rect.left; where.x < redraw_rect.right; where.x++) - for(where.y = redraw_rect.top; where.y < redraw_rect.bottom; where.y++) { + for(where.x = view_rect.left; where.x < view_rect.right; where.x++) + for(where.y = view_rect.top; where.y < view_rect.bottom; where.y++) { draw_rect = orig_draw_rect; - draw_rect.offset(6 * where.x, 6 * where.y); + draw_rect.offset(6 * (where.x - view_rect.left), 6 * (where.y - view_rect.top)); if(out_mode) what_ter = univ.out[where.x + 48 * univ.party.i_w_c.x][where.y + 48 * univ.party.i_w_c.y]; @@ -1405,6 +1386,7 @@ void draw_map(bool need_refresh) { expl = univ.out.out_e[where.x + 48 * univ.party.i_w_c.x][where.y + 48 * univ.party.i_w_c.y]; else expl = is_explored(where.x,where.y); + // Draw tile if(expl != 0) { pic = univ.scenario.ter_types[what_ter].map_pic; bool drawLargeIcon = false; @@ -1453,6 +1435,48 @@ void draw_map(bool need_refresh) { draw_rect.inset(1,1); rect_draw_some_item(*ResMgr::graphics.get("trim"),{8,112,12,116},map_gworld(),draw_rect); } + + // Draw discovered edges of rooms/areas + const std::vector& area_desc = is_out() ? univ.out->area_desc : univ.town->area_desc; + location a; + location b; + for(info_rect_t area : area_desc){ + location adjusted_where = where; + adjusted_where.x -= view_rect.left; + adjusted_where.y -= view_rect.top; + if(where.y == area.top && where.x >= area.left && where.x <= area.right){ + a = orig_draw_rect.topLeft(); + a.x += 6 * adjusted_where.x; + a.y += 6 * adjusted_where.y; + b = a; + b.x += 6; + draw_line(map_gworld(), a, b, 1, Colours::RED); + } + if(where.x == area.left && where.y >= area.top && where.y <= area.bottom){ + a = orig_draw_rect.topLeft(); + a.x += 6 * adjusted_where.x; + a.y += 6 * adjusted_where.y; + b = a; + b.y += 6; + draw_line(map_gworld(), a, b, 1, Colours::RED); + } + if(where.y == area.bottom && where.x >= area.left && where.x <= area.right){ + a = orig_draw_rect.topLeft(); + a.x += 6 * adjusted_where.x; + a.y += 6 * (adjusted_where.y + 1) - 1; + b = a; + b.x += 6; + draw_line(map_gworld(), a, b, 1, Colours::RED); + } + if(where.x == area.right && where.y >= area.top && where.y <= area.bottom){ + a = orig_draw_rect.topLeft(); + a.x += 6 * (adjusted_where.x + 1) - 1; + a.y += 6 * adjusted_where.y; + b = a; + b.y += 6; + draw_line(map_gworld(), a, b, 1, Colours::RED); + } + } } } @@ -1483,7 +1507,7 @@ void draw_map(bool need_refresh) { win_draw_string(mini_map(), map_bar_rect,"(Hit Escape to close.)",eTextMode::WRAP,style); if(canMap) { - rect_draw_some_item(map_gworld().getTexture(),area_to_draw_from,mini_map(),area_to_draw_on); + rect_draw_some_item(map_gworld().getTexture(),the_rect,mini_map(),area_to_draw_on); // Now place PCs and monsters if(draw_pcs) { diff --git a/src/game/boe.town.hpp b/src/game/boe.town.hpp index 9467ad2c..8bb6429c 100644 --- a/src/game/boe.town.hpp +++ b/src/game/boe.town.hpp @@ -28,6 +28,7 @@ void erase_completed_specials(cArea& sector, std::function clear void erase_out_specials(); bool does_location_have_special(cOutdoors& sector, location loc, eTerSpec type); void clear_map(); +rectangle minimap_view_rect(); void draw_map(bool need_refresh); bool is_door(location destination); void display_map();