From 0a155f1fe75992c72a5cd43dad58ebe7c3965fd4 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 27 Nov 2020 21:21:57 +0300 Subject: [PATCH 01/26] Encapsulate map::drawsq() arguments and clarify their meaning --- src/action.cpp | 5 ++- src/animation.cpp | 13 +++++-- src/construction.cpp | 6 ++- src/editmap.cpp | 17 +++++--- src/game.cpp | 9 ++++- src/map.cpp | 92 ++++++++++++++++++++++---------------------- src/map.h | 67 ++++++++++++++++++++------------ src/ranged.cpp | 13 +++++-- 8 files changed, 134 insertions(+), 88 deletions(-) diff --git a/src/action.cpp b/src/action.cpp index df176ca40eda..82959f823196 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -1093,10 +1093,11 @@ cata::optional choose_adjacent_highlight( const std::string &message, shared_ptr_fast hilite_cb; if( !valid.empty() ) { + drawsq_params params; + params.highlight = true; hilite_cb = make_shared_fast( [&]() { for( const tripoint &pos : valid ) { - g->m.drawsq( g->w_terrain, g->u, pos, - true, true, g->u.pos() + g->u.view_offset ); + g->m.drawsq( g->w_terrain, pos, params ); } } ); g->add_draw_callback( hilite_cb ); diff --git a/src/animation.cpp b/src/animation.cpp index d29b8b57ba54..adb5275970f4 100644 --- a/src/animation.cpp +++ b/src/animation.cpp @@ -453,7 +453,9 @@ void draw_bullet_curses( map &m, const tripoint &t, const char bullet, const tri shared_ptr_fast bullet_cb = make_shared_fast( [&]() { if( p != nullptr && p->z == vp.z ) { - m.drawsq( g->w_terrain, g->u, *p, false, true, vp ); + drawsq_params params; + params.set_view_center( vp ); + m.drawsq( g->w_terrain, *p, params ); } mvwputch( g->w_terrain, t.xy() - vp.xy() + point( POSX, POSY ), c_red, bullet ); } ); @@ -618,6 +620,9 @@ namespace void draw_line_curses( game &g, const tripoint ¢er, const std::vector &ret, bool noreveal ) { + drawsq_params params; + params.highlight = true; + params.set_view_center( center ); for( const tripoint &p : ret ) { const auto critter = g.critter_at( p, true ); @@ -634,7 +639,7 @@ void draw_line_curses( game &g, const tripoint ¢er, const std::vector &points ) { + drawsq_params params; + params.highlight = true; for( const tripoint &p : points ) { - g.m.drawsq( g.w_terrain, g.u, p, true, true ); + g.m.drawsq( g.w_terrain, p, params ); } const tripoint p = points.empty() ? tripoint {POSX, POSY, 0} : diff --git a/src/construction.cpp b/src/construction.cpp index 32edbc3b1961..ed97bbebff09 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -889,10 +889,12 @@ void place_construction( const std::string &desc ) } } + drawsq_params params; + params.highlight = true; + params.show_items = false; shared_ptr_fast draw_valid = make_shared_fast( [&]() { for( auto &elem : valid ) { - g->m.drawsq( g->w_terrain, g->u, elem.first, true, false, - g->u.pos() + g->u.view_offset ); + g->m.drawsq( g->w_terrain, elem.first, params ); } } ); g->add_draw_callback( draw_valid ); diff --git a/src/editmap.cpp b/src/editmap.cpp index 4671f61c36da..2f527e23a31f 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -456,12 +456,13 @@ void editmap::uber_draw_ter( const catacurses::window &w, map *m ) bool draw_fld=true; bool draw_veh=true; */ - bool draw_itm = true; bool game_map = m == &g->m || w == g->w_terrain; const int msize = MAPSIZE_X; if( refresh_mplans ) { hilights["mplan"].points.clear(); } + drawsq_params params; + params.set_view_center( center ); for( const tripoint &p : tripoint_range( start, end ) ) { int sym = game_map ? '%' : ' '; if( p.x >= 0 && p.x < msize && p.y >= 0 && p.y < msize ) { @@ -470,7 +471,7 @@ void editmap::uber_draw_ter( const catacurses::window &w, map *m ) if( critter != nullptr ) { critter->draw( w, center.xy(), false ); } else { - m->drawsq( w, g->u, p, false, draw_itm, center, false, true ); + m->drawsq( w, p, params ); } if( refresh_mplans ) { monster *mon = dynamic_cast( critter ); @@ -481,7 +482,7 @@ void editmap::uber_draw_ter( const catacurses::window &w, map *m ) } } } else { - m->drawsq( w, g->u, p, false, draw_itm, center, false, true ); + m->drawsq( w, p, params ); } } else { mvwputch( w, p.xy() - start.xy(), c_dark_gray, sym ); @@ -513,7 +514,10 @@ void editmap::draw_main_ui_overlay() if( critter != nullptr ) { critter->draw( g->w_terrain, target, true ); } else { - g->m.drawsq( g->w_terrain, g->u, target, true, true, target ); + drawsq_params params; + params.highlight = true; + params.set_view_center( target ); + g->m.drawsq( g->w_terrain, target, params ); } #ifdef TILES // give some visual indication of different cursor moving modes @@ -676,9 +680,10 @@ void editmap::draw_main_ui_overlay() #endif hilights["mapgentgt"].draw( *this, true ); tmpmap.reset_vehicle_cache( target.z ); - const tripoint center( SEEX - 1, SEEY - 1, target.z ); + drawsq_params params; + params.set_view_center( tripoint( SEEX - 1, SEEY - 1, target.z ) ); for( const tripoint &p : tmpmap.points_on_zlevel() ) { - tmpmap.drawsq( g->w_terrain, g->u, p, false, true, center, false, true ); + tmpmap.drawsq( g->w_terrain, p, params ); } #ifdef TILES } diff --git a/src/game.cpp b/src/game.cpp index 495dd82b3551..a398487b51cc 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5779,8 +5779,10 @@ void game::pickup() void game::pickup( const tripoint &p ) { // Highlight target + drawsq_params params; + params.highlight = true; shared_ptr_fast hilite_cb = make_shared_fast( [&]() { - m.drawsq( w_terrain, u, p, true, true, u.pos() + u.view_offset ); + m.drawsq( w_terrain, p, params ); } ); add_draw_callback( hilite_cb ); @@ -5863,7 +5865,10 @@ void game::draw_look_around_cursor( const tripoint &lp, const visibility_variabl if( creature != nullptr && u.sees( *creature ) ) { creature->draw( w_terrain, view_center, true ); } else { - m.drawsq( w_terrain, u, lp, true, true, view_center ); + drawsq_params params; + params.highlight = true; + params.set_view_center( view_center ); + m.drawsq( w_terrain, lp, params ); } } else { std::string visibility_indicator; diff --git a/src/map.cpp b/src/map.cpp index e3742d63f540..0a871c4d4eaa 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -5628,6 +5628,8 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) int &x = p.x; int &y = p.y; const bool do_map_memory = g->u.should_show_map_memory(); + drawsq_params params; + params.set_view_center( center ); for( y = center.y - getmaxy( w ) / 2; y <= center.y + getmaxy( w ) / 2; y++ ) { if( y - center.y + getmaxy( w ) / 2 >= getmaxy( w ) ) { continue; @@ -5664,15 +5666,15 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) const visibility_type vis = get_visibility( lighting, cache ); if( !apply_vision_effects( w, vis ) ) { const maptile curr_maptile = maptile( cur_submap, l ); - const bool just_this_zlevel = - draw_maptile( w, g->u, p, curr_maptile, - false, true, center, - lighting == LL_LOW, lighting == LL_BRIGHT, true ); - if( !just_this_zlevel ) { + params.low_light = lighting == LL_LOW; + params.bright_light = lighting == LL_BRIGHT; + params.batch = true; + const bool just_this_z = draw_maptile( w, p, curr_maptile, params ); + if( !just_this_z ) { p.z--; const maptile tile_below = maptile( sm_below, l ); - draw_from_above( w, g->u, p, tile_below, false, center, - lighting == LL_LOW, lighting == LL_BRIGHT, false ); + params.batch = false; + draw_from_above( w, p, tile_below, params ); p.z++; } } else if( do_map_memory && ( vis == VIS_HIDDEN || vis == VIS_DARK ) ) { @@ -5693,19 +5695,12 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) } } -void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, - bool invert, bool show_items ) const -{ - drawsq( w, u, p, invert, show_items, u.pos() + u.view_offset, false, false, false ); -} - -void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, const bool invert_arg, - const bool show_items_arg, const tripoint &view_center, - const bool low_light, const bool bright_light, const bool inorder ) const +void map::drawsq( const catacurses::window &w, const tripoint &p, + const drawsq_params ¶ms ) const { // If we are in tiles mode, the only thing we want to potentially draw is a highlight if( is_draw_tiles_mode() ) { - if( invert_arg ) { + if( params.highlight ) { g->draw_highlight( p ); } return; @@ -5716,14 +5711,13 @@ void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, con } const maptile tile = maptile_at( p ); - const bool done = draw_maptile( w, u, p, tile, invert_arg, show_items_arg, - view_center, low_light, bright_light, inorder ); + const bool done = draw_maptile( w, p, tile, params ); if( !done ) { tripoint below( p.xy(), p.z - 1 ); const maptile tile_below = maptile_at( below ); - draw_from_above( w, u, below, tile_below, - invert_arg, view_center, - low_light, bright_light, false ); + drawsq_params params2 = params; + params2.batch = false; + draw_from_above( w, below, tile_below, params2 ); } } @@ -5733,12 +5727,10 @@ bool map::need_draw_lower_floor( const tripoint &p ) return !( !zlevels || p.z <= -OVERMAP_DEPTH || !ter( p ).obj().has_flag( TFLAG_NO_FLOOR ) ); } -bool map::draw_maptile( const catacurses::window &w, const player &u, const tripoint &p, - const maptile &curr_maptile, - bool invert, bool show_items, - const tripoint &view_center, - const bool low_light, const bool bright_light, const bool inorder ) const +bool map::draw_maptile( const catacurses::window &w, const tripoint &p, + const maptile &curr_maptile, const drawsq_params ¶ms ) const { + drawsq_params param = params; nc_color tercol; const ter_t &curr_ter = curr_maptile.get_ter_t(); const furn_t &curr_furn = curr_maptile.get_furn_t(); @@ -5764,8 +5756,8 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip tercol = curr_ter.color(); } if( curr_ter.has_flag( TFLAG_SWIMMABLE ) && curr_ter.has_flag( TFLAG_DEEP_WATER ) && - !u.is_underwater() ) { - show_items = false; // Can only see underwater items if WE are underwater + !g->u.is_underwater() ) { + param.show_items = false; // Can only see underwater items if WE are underwater } // If there's a trap here, and we have sufficient perception, draw that instead if( curr_trap.can_see( p, g->u ) ) { @@ -5847,7 +5839,7 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip std::string item_sym; // If there are items here, draw those instead - if( show_items && curr_maptile.get_item_count() > 0 && sees_some_items( p, g->u ) ) { + if( param.show_items && curr_maptile.get_item_count() > 0 && sees_some_items( p, g->u ) ) { // if there's furniture/terrain/trap/fields (sym!='.') // and we should not override it, then only highlight the square if( sym != '.' && sym != '%' && !draw_item_sym ) { @@ -5859,7 +5851,7 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip tercol = curr_maptile.get_uppermost_item().color(); } if( curr_maptile.get_item_count() > 1 ) { - invert = !invert; + param.highlight = !param.highlight; } } } @@ -5886,18 +5878,18 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip graf = true; } - const auto u_vision = u.get_vision_modes(); + const auto u_vision = g->u.get_vision_modes(); if( u_vision[BOOMERED] ) { tercol = c_magenta; } else if( u_vision[NV_GOGGLES] ) { - tercol = ( bright_light ) ? c_white : c_light_green; - } else if( low_light ) { + tercol = ( param.bright_light ) ? c_white : c_light_green; + } else if( param.low_light ) { tercol = c_dark_gray; } else if( u_vision[DARKNESS] ) { tercol = c_dark_gray; } - if( invert ) { + if( param.highlight ) { tercol = invert_color( tercol ); } else if( hi ) { tercol = hilite( tercol ); @@ -5905,7 +5897,7 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip tercol = red_background( tercol ); } - if( inorder ) { + if( param.batch ) { // Rastering the whole map, take advantage of automatically moving the cursor. if( item_sym.empty() ) { wputch( w, tercol, sym ); @@ -5914,6 +5906,7 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip } } else { // Otherwise move the cursor before drawing. + const tripoint view_center = param.get_view_center(); const int k = p.x + getmaxx( w ) / 2 - view_center.x; const int j = p.y + getmaxy( w ) / 2 - view_center.y; if( item_sym.empty() ) { @@ -5927,11 +5920,8 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip !curr_ter.has_flag( TFLAG_NO_FLOOR ); } -void map::draw_from_above( const catacurses::window &w, const player &u, const tripoint &p, - const maptile &curr_tile, - const bool invert, - const tripoint &view_center, - bool low_light, bool bright_light, bool inorder ) const +void map::draw_from_above( const catacurses::window &w, const tripoint &p, + const maptile &curr_tile, const drawsq_params ¶ms ) const { static const int AUTO_WALL_PLACEHOLDER = 2; // this should never appear as a real symbol! @@ -5984,24 +5974,25 @@ void map::draw_from_above( const catacurses::window &w, const player &u, const t sym = determine_wall_corner( p ); } - const auto u_vision = u.get_vision_modes(); + const auto u_vision = g->u.get_vision_modes(); if( u_vision[BOOMERED] ) { tercol = c_magenta; } else if( u_vision[NV_GOGGLES] ) { - tercol = ( bright_light ) ? c_white : c_light_green; - } else if( low_light ) { + tercol = ( params.bright_light ) ? c_white : c_light_green; + } else if( params.low_light ) { tercol = c_dark_gray; } else if( u_vision[DARKNESS] ) { tercol = c_dark_gray; } - if( invert ) { + if( params.highlight ) { tercol = invert_color( tercol ); } - if( inorder ) { + if( params.batch ) { wputch( w, tercol, sym ); } else { + const tripoint view_center = params.get_view_center(); const int k = p.x + getmaxx( w ) / 2 - view_center.x; const int j = p.y + getmaxy( w ) / 2 - view_center.y; mvwputch( w, point( k, j ), tercol, sym ); @@ -8564,3 +8555,12 @@ void map::on_saved() { parent_electric_grids.clear(); } + +tripoint drawsq_params::get_view_center() const +{ + if( view_center == tripoint_min ) { + return g->u.pos() + g->u.view_offset; + } else { + return view_center; + } +} diff --git a/src/map.h b/src/map.h index a3e7a3d43d7e..9ea98a6814a9 100644 --- a/src/map.h +++ b/src/map.h @@ -148,6 +148,37 @@ struct bash_params { bool bashing_from_above; }; +/** Draw parameters used by map::drawsq() and similar methods. */ +struct drawsq_params { + private: + tripoint view_center = tripoint_min; + + public: + /** Highlight the tile. On TILES, draws an overlay; on CURSES, reverts color. */ + bool highlight = false; + /** Whether to draw items on the tile. */ + bool show_items = true; + /** Whether tile is low light, and should be drawn with muted color. */ + bool low_light = false; + /** Whether tile is in bright light. Affects NV overlay, and nothing else. */ + bool bright_light = false; + /** + * Set to 'true' when doing batch drawing (e.g. map::draw()). + * Allows to speed up drawing by taking advantage of automatic cursor movement. + */ + bool batch = false; + + /** Set view center. By default, uses avatar's current view center. */ + void set_view_center( const tripoint &p ) { + view_center = p; + } + /** Reset view center to avatar's view center */ + void reset_view_center() { + view_center = tripoint_min; + } + tripoint get_view_center() const; +}; + struct level_cache { // Zeros all relevant values level_cache(); @@ -317,21 +348,14 @@ class map */ void draw( const catacurses::window &w, const tripoint ¢er ); - /** Draw the map tile at the given coordinate. Called by `map::draw()`. - * - * @param w The window we are drawing in - * @param u The player - * @param p The tile on this map to draw. - * @param invert Invert colors if this flag is true - * @param show_items Draw items in tile if this flag is true see `center` in `map::draw()` - */ - void drawsq( const catacurses::window &w, player &u, const tripoint &p, - bool invert, bool show_items, - const tripoint &view_center, - bool low_light = false, bool bright_light = false, - bool inorder = false ) const; - void drawsq( const catacurses::window &w, player &u, const tripoint &p, - bool invert = false, bool show_items = true ) const; + /** + * Draw the map tile at the given coordinate. Called by `map::draw()`. + * + * @param w The window we are drawing in + * @param p The tile on this map to draw. + * @param params Draw parameters. + */ + void drawsq( const catacurses::window &w, const tripoint &p, const drawsq_params ¶ms ) const; /** * Add currently loaded submaps (in @ref grid) to the @ref mapbuffer. @@ -1644,21 +1668,16 @@ class map * Internal version of the drawsq. Keeps a cached maptile for less re-getting. * Returns true if it has drawn all it should, false if `draw_from_above` should be called after. */ - bool draw_maptile( const catacurses::window &w, const player &u, const tripoint &p, - const maptile &tile, - bool invert, bool show_items, - const tripoint &view_center, - bool low_light, bool bright_light, bool inorder ) const; + bool draw_maptile( const catacurses::window &w, const tripoint &p, + const maptile &tile, const drawsq_params ¶ms ) const; bool draw_maptile_from_memory( const catacurses::window &w, const tripoint &p, const tripoint &view_center, bool move_cursor = true ) const; /** * Draws the tile as seen from above. */ - void draw_from_above( const catacurses::window &w, const player &u, const tripoint &p, - const maptile &tile, bool invert, - const tripoint &view_center, - bool low_light, bool bright_light, bool inorder ) const; + void draw_from_above( const catacurses::window &w, const tripoint &p, + const maptile &tile, const drawsq_params ¶ms ) const; int determine_wall_corner( const tripoint &p ) const; // apply a circular light pattern immediately, however it's best to use... diff --git a/src/ranged.cpp b/src/ranged.cpp index c90d1aa7abe8..9b67f45ce3f8 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -1797,7 +1797,9 @@ std::vector target_handler::target_ui( player &pc, target_mode mode, if( critter != nullptr ) { g->draw_critter( *critter, center ); } else if( g->m.pl_sees( dst, -1 ) ) { - g->m.drawsq( g->w_terrain, pc, dst, false, true, center ); + drawsq_params params; + params.set_view_center( center ); + g->m.drawsq( g->w_terrain, dst, params ); } else { mvwputch( g->w_terrain, point( POSX, POSY ), c_black, 'X' ); } @@ -2218,8 +2220,11 @@ std::vector target_handler::target_ui( spell &casting, const bool no_f } g->draw_cursor( dst ); + drawsq_params params; + params.highlight = true; + params.set_view_center( center ); for( const tripoint &area : spell_aoe ) { - g->m.drawsq( g->w_terrain, pc, area, true, true, center ); + g->m.drawsq( g->w_terrain, area, params ); } if( casting.aoe() > 0 ) { @@ -2321,7 +2326,9 @@ std::vector target_handler::target_ui( spell &casting, const bool no_f if( critter != nullptr ) { g->draw_critter( *critter, center ); } else if( g->m.pl_sees( dst, -1 ) ) { - g->m.drawsq( g->w_terrain, pc, dst, false, true, center ); + drawsq_params params; + params.set_view_center( center ); + g->m.drawsq( g->w_terrain, dst, params ); } // constrain by range From 56ced0cbcb7bc8360a8ebc36414386bdfcf5f191 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 27 Nov 2020 18:56:52 +0300 Subject: [PATCH 02/26] Split map memory into submap-sized chunks Note: this naive implementation significantly lowers FPS --- src/avatar.cpp | 4 ++-- src/lru_cache.cpp | 1 - src/map_memory.cpp | 55 ++++++++++++++++++++++++++++++++++++------- src/map_memory.h | 17 +++++++++---- src/savegame_json.cpp | 12 +++++++--- 5 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index a987a98618ea..602f8aab19af 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -154,12 +154,12 @@ memorized_terrain_tile avatar::get_memorized_tile( const tripoint &pos ) const void avatar::memorize_tile( const tripoint &pos, const std::string &ter, const int subtile, const int rotation ) { - player_map_memory.memorize_tile( memorized_tile_count, pos, ter, subtile, rotation ); + player_map_memory.memorize_tile( pos, ter, subtile, rotation ); } void avatar::memorize_symbol( const tripoint &pos, const int symbol ) { - player_map_memory.memorize_symbol( memorized_tile_count, pos, symbol ); + player_map_memory.memorize_symbol( pos, symbol ); } int avatar::get_memorized_symbol( const tripoint &p ) const diff --git a/src/lru_cache.cpp b/src/lru_cache.cpp index 765fdc85ce4d..0559c8e8d977 100644 --- a/src/lru_cache.cpp +++ b/src/lru_cache.cpp @@ -69,6 +69,5 @@ const std::list::Pair> &lru_cache::li } // explicit template initialization for lru_cache of all types -template class lru_cache; template class lru_cache; template class lru_cache; diff --git a/src/map_memory.cpp b/src/map_memory.cpp index ce5f1a1c65ad..316ee8f42731 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -1,30 +1,69 @@ +#include "coordinate_conversions.h" #include "map_memory.h" static const memorized_terrain_tile default_tile{ "", 0, 0 }; +static const int default_symbol = 0; + +memorized_submap::memorized_submap() : tiles{{ default_tile }}, symbols{{ default_symbol }} {} + +struct coord_pair { + tripoint sm; + point loc; + + coord_pair( const tripoint &p ) : loc( p.xy() ) { + sm = tripoint( ms_to_sm_remain( loc.x, loc.y ), p.z ); + } +}; memorized_terrain_tile map_memory::get_tile( const tripoint &pos ) const { - return tile_cache.get( pos, default_tile ); + coord_pair p( pos ); + const memorized_submap &sm = get_submap( p.sm ); + return sm.tiles[p.loc.x][p.loc.y]; } -void map_memory::memorize_tile( int limit, const tripoint &pos, const std::string &ter, +void map_memory::memorize_tile( const tripoint &pos, const std::string &ter, const int subtile, const int rotation ) { - tile_cache.insert( limit, pos, memorized_terrain_tile{ ter, subtile, rotation } ); + coord_pair p( pos ); + memorized_submap &sm = get_submap( p.sm ); + sm.tiles[p.loc.x][p.loc.y] = memorized_terrain_tile{ ter, subtile, rotation }; } int map_memory::get_symbol( const tripoint &pos ) const { - return symbol_cache.get( pos, 0 ); + coord_pair p( pos ); + const memorized_submap &sm = get_submap( p.sm ); + return sm.symbols[p.loc.x][p.loc.y]; } -void map_memory::memorize_symbol( int limit, const tripoint &pos, const int symbol ) +void map_memory::memorize_symbol( const tripoint &pos, const int symbol ) { - symbol_cache.insert( limit, pos, symbol ); + coord_pair p( pos ); + memorized_submap &sm = get_submap( p.sm ); + sm.symbols[p.loc.x][p.loc.y] = symbol; } void map_memory::clear_memorized_tile( const tripoint &pos ) { - tile_cache.remove( pos ); - symbol_cache.remove( pos ); + coord_pair p( pos ); + memorized_submap &sm = get_submap( p.sm ); + sm.symbols[p.loc.x][p.loc.y] = default_symbol; + sm.tiles[p.loc.x][p.loc.y] = default_tile; +} + +const memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) const +{ + std::map::const_iterator sm = submaps.find( sm_pos ); + if( sm == submaps.end() ) { + const static memorized_submap null_sm; + return null_sm; + } else { + return sm->second; + } +} + +memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) +{ + return submaps[sm_pos]; } diff --git a/src/map_memory.h b/src/map_memory.h index 81f80e16b38d..35e1132482f1 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -4,6 +4,7 @@ #include +#include "game_constants.h" #include "lru_cache.h" #include "point.h" // IWYU pragma: keep @@ -17,6 +18,13 @@ struct memorized_terrain_tile { int rotation; }; +struct memorized_submap { + memorized_terrain_tile tiles[SEEX][SEEY]; + int symbols[SEEX][SEEY]; + + memorized_submap(); +}; + class map_memory { public: @@ -25,18 +33,19 @@ class map_memory void load( const JsonObject &jsin ); /** Memorizes a given tile; finalize_tile_memory needs to be called after it */ - void memorize_tile( int limit, const tripoint &pos, const std::string &ter, + void memorize_tile( const tripoint &pos, const std::string &ter, int subtile, int rotation ); /** Returns last stored map tile in given location */ memorized_terrain_tile get_tile( const tripoint &pos ) const; - void memorize_symbol( int limit, const tripoint &pos, int symbol ); + void memorize_symbol( const tripoint &pos, int symbol ); int get_symbol( const tripoint &pos ) const; void clear_memorized_tile( const tripoint &pos ); private: - lru_cache tile_cache; - lru_cache symbol_cache; + const memorized_submap &get_submap( const tripoint &sm_pos ) const; + memorized_submap &get_submap( const tripoint &sm_pos ); + std::map submaps; }; #endif // CATA_SRC_MAP_MEMORY_H diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index f76724a10579..93e0305035ad 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3082,8 +3082,9 @@ void player_morale::load( const JsonObject &jsin ) jsin.read( "morale", points ); } -void map_memory::store( JsonOut &jsout ) const +void map_memory::store( JsonOut &/*jsout*/ ) const { + /* jsout.start_array(); jsout.start_array(); for( const auto &elem : tile_cache.list() ) { @@ -3109,10 +3110,12 @@ void map_memory::store( JsonOut &jsout ) const } jsout.end_array(); jsout.end_array(); + */ } -void map_memory::load( JsonIn &jsin ) +void map_memory::load( JsonIn &/*jsin*/ ) { + /* // Legacy loading of object version. if( jsin.test_object() ) { JsonObject jsobj = jsin.get_object(); @@ -3151,11 +3154,13 @@ void map_memory::load( JsonIn &jsin ) } jsin.end_array(); } + */ } // Deserializer for legacy object-based memory map. -void map_memory::load( const JsonObject &jsin ) +void map_memory::load( const JsonObject &/*jsin*/ ) { + /* tile_cache.clear(); for( JsonObject pmap : jsin.get_array( "map_memory_tiles" ) ) { const tripoint p( pmap.get_int( "x" ), pmap.get_int( "y" ), pmap.get_int( "z" ) ); @@ -3168,6 +3173,7 @@ void map_memory::load( const JsonObject &jsin ) const tripoint p( pmap.get_int( "x" ), pmap.get_int( "y" ), pmap.get_int( "z" ) ); memorize_symbol( std::numeric_limits::max(), p, pmap.get_int( "symbol" ) ); } + */ } void deserialize( point &p, JsonIn &jsin ) From 88b895128014661571059fe9432d6985c550016d Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 27 Nov 2020 18:14:57 +0300 Subject: [PATCH 03/26] Optimize map memory access Move memorized submaps that would be affected during drawing into a 2d array. --- src/avatar.cpp | 5 +++++ src/avatar.h | 1 + src/cata_tiles.cpp | 16 +++++++++++++++ src/map_memory.cpp | 51 +++++++++++++++++++++++++++++++++++++++++----- src/map_memory.h | 49 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index 602f8aab19af..4560b799840f 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -146,6 +146,11 @@ void avatar::deserialize_map_memory( JsonIn &jsin ) player_map_memory.load( jsin ); } +void avatar::prepare_map_memory_region( const tripoint &p1, const tripoint &p2 ) +{ + player_map_memory.prepare_region( p1, p2 ); +} + memorized_terrain_tile avatar::get_memorized_tile( const tripoint &pos ) const { return player_map_memory.get_tile( pos ); diff --git a/src/avatar.h b/src/avatar.h index 2e537914fcbf..e50a69547127 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -80,6 +80,7 @@ class avatar : public player void toggle_map_memory(); bool should_show_map_memory(); + void prepare_map_memory_region( const tripoint &p1, const tripoint &p2 ); /** Memorizes a given tile in tiles mode; finalize_tile_memory needs to be called after it */ void memorize_tile( const tripoint &pos, const std::string &ter, int subtile, int rotation ); diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index 2eeafc0d1131..35154ace3d3d 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -1048,6 +1048,22 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int const auto &ch = g->m.access_cache( center.z ); + // Map memory should be at least the size of the view range + // so that new tiles can be memorized, and at least the size of the display + // since at farthest zoom displayed area may be bigger than view range. + const point min_mm_reg = point( + std::min( o.x, min_visible_x ), + std::min( o.y, min_visible_y ) + ); + const point max_mm_reg = point( + std::max( sx + o.x, max_visible_x ), + std::max( sy + o.y, max_visible_y ) + ); + g->u.prepare_map_memory_region( + g->m.getabs( tripoint( min_mm_reg, center.z ) ), + g->m.getabs( tripoint( max_mm_reg, center.z ) ) + ); + //set up a default tile for the edges outside the render area visibility_type offscreen_type = VIS_DARK; if( cache.u_is_boomered ) { diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 316ee8f42731..952b36809b51 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -52,18 +52,59 @@ void map_memory::clear_memorized_tile( const tripoint &pos ) sm.tiles[p.loc.x][p.loc.y] = default_tile; } -const memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) const +void map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) { - std::map::const_iterator sm = submaps.find( sm_pos ); + assert( p1.z == p2.z ); + assert( p1.x <= p2.x && p1.y <= p2.y ); + + tripoint sm_pos = coord_pair( p1 ).sm - point( 1, 1 ); + point sm_size = ( coord_pair( p2 ).sm - sm_pos ).xy() + point( 1, 1 ); + if( ( sm_pos == cache_pos ) && ( sm_size == cache_size ) ) { + return; + } + + cache_pos = sm_pos; + cache_size = sm_size; + cached.clear(); + cached.reserve( cache_size.x * cache_size.y ); + for( int dy = 0; dy < cache_size.y; dy++ ) { + for( int dx = 0; dx < cache_size.x; dx++ ) { + cached.push_back( prepare_submap( cache_pos + point( dx, dy ) ) ); + } + } +} + +shared_ptr_fast map_memory::prepare_submap( const tripoint &sm_pos ) +{ + auto sm = submaps.find( sm_pos ); if( sm == submaps.end() ) { - const static memorized_submap null_sm; - return null_sm; + // TODO: load memorized submaps from disk + shared_ptr_fast new_sm = make_shared_fast(); + submaps.insert( std::make_pair( sm_pos, new_sm ) ); + return new_sm; } else { return sm->second; } } +static memorized_submap null_mz_submap; + +const memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) const +{ + point idx = ( sm_pos - cache_pos ).xy(); + if( idx.x > 0 && idx.y > 0 && idx.x < cache_size.x && idx.y < cache_size.y ) { + return *cached[idx.y * cache_size.x + idx.x]; + } else { + return null_mz_submap; + } +} + memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) { - return submaps[sm_pos]; + point idx = ( sm_pos - cache_pos ).xy(); + if( idx.x > 0 && idx.y > 0 && idx.x < cache_size.x && idx.y < cache_size.y ) { + return *cached[idx.y * cache_size.x + idx.x]; + } else { + return null_mz_submap; + } } diff --git a/src/map_memory.h b/src/map_memory.h index 35e1132482f1..d638fe515b79 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -6,6 +6,7 @@ #include "game_constants.h" #include "lru_cache.h" +#include "memory_fast.h" #include "point.h" // IWYU pragma: keep class JsonOut; @@ -25,6 +26,13 @@ struct memorized_submap { memorized_submap(); }; +/** + * Manages map tiles memorized by the avatar. + * Note that there are 2 separate memories in here: + * 1. memorized graphic tiles (for TILES with a tileset) + * 2. memorized symbols (for CURSES or TILES in ascii mode) + * TODO: combine tiles and curses. Also, split map memory into layers (terrain/furn/vpart/...)? + */ class map_memory { public: @@ -32,20 +40,55 @@ class map_memory void load( JsonIn &jsin ); void load( const JsonObject &jsin ); - /** Memorizes a given tile; finalize_tile_memory needs to be called after it */ + /** + * Prepares map memory for rendering and/or memorization of given region. + * @param p1 top-left corner of the region, in global ms coords + * @param p2 bottom-right corner of the region, in global ms coords + * Both coords are inclusive and should be on the same Z level. + */ + void prepare_region( const tripoint &p1, const tripoint &p2 ); + + /** + * Memorizes given tile, overwriting old value. + * @param pos tile position, in global ms coords. + */ void memorize_tile( const tripoint &pos, const std::string &ter, int subtile, int rotation ); - /** Returns last stored map tile in given location */ + /** + * Returns memorized tile. + * @param pos tile position, in global ms coords. + */ memorized_terrain_tile get_tile( const tripoint &pos ) const; + /** + * Memorizes given value, overwriting old value. + * @param pos tile position, in global ms coords. + */ void memorize_symbol( const tripoint &pos, int symbol ); + + /** + * Returns memorized symbol. + * @param pos tile position, in global ms coords. + */ int get_symbol( const tripoint &pos ) const; + /** + * Clears memorized tile and symbol. + * @param pos tile position, in global ms coords. + */ void clear_memorized_tile( const tripoint &pos ); + private: + std::map> submaps; + + std::vector> cached; + tripoint cache_pos; + point cache_size; + + shared_ptr_fast prepare_submap( const tripoint &sm_pos ); + const memorized_submap &get_submap( const tripoint &sm_pos ) const; memorized_submap &get_submap( const tripoint &sm_pos ); - std::map submaps; }; #endif // CATA_SRC_MAP_MEMORY_H From bb46b79e79d0d4e328023195d6f0813d2406b88d Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 27 Nov 2020 22:34:12 +0300 Subject: [PATCH 04/26] Memorize tiles on curses only during map::draw() --- src/map.cpp | 32 ++++++++++++++++++++++++++------ src/map.h | 2 ++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 0a871c4d4eaa..d8d800ef7307 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -5621,6 +5621,25 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) const auto &visibility_cache = get_cache_ref( center.z ).visibility_cache; + int wnd_h = getmaxy( w ); + int wnd_w = getmaxx( w ); + + // Map memory should be at least the size of the view range + // so that new tiles can be memorized, and at least the size of the terminal + // since displayed area may be bigger than view range. + const point min_mm_reg = point( + std::min( 0, center.x - wnd_w / 2 ), + std::min( 0, center.y - wnd_h / 2 ) + ); + const point max_mm_reg = point( + std::max( MAPSIZE_X, center.x - wnd_w / 2 + wnd_w ), + std::max( MAPSIZE_Y, center.y - wnd_h / 2 + wnd_h ) + ); + g->u.prepare_map_memory_region( + g->m.getabs( tripoint( min_mm_reg, center.z ) ), + g->m.getabs( tripoint( max_mm_reg, center.z ) ) + ); + // X and y are in map coordinates, but might be out of range of the map. // When they are out of range, we just draw '#'s. tripoint p; @@ -5630,15 +5649,16 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) const bool do_map_memory = g->u.should_show_map_memory(); drawsq_params params; params.set_view_center( center ); - for( y = center.y - getmaxy( w ) / 2; y <= center.y + getmaxy( w ) / 2; y++ ) { - if( y - center.y + getmaxy( w ) / 2 >= getmaxy( w ) ) { + params.memorize = true; + for( y = center.y - wnd_h / 2; y <= center.y + wnd_h / 2; y++ ) { + if( y - center.y + wnd_h / 2 >= wnd_h ) { continue; } - wmove( w, point( 0, y - center.y + getmaxy( w ) / 2 ) ); + wmove( w, point( 0, y - center.y + wnd_h / 2 ) ); - const int maxxrender = center.x - getmaxx( w ) / 2 + getmaxx( w ); - x = center.x - getmaxx( w ) / 2; + const int maxxrender = center.x - wnd_w / 2 + wnd_w; + x = center.x - wnd_w / 2; if( y < 0 || y >= MAPSIZE_Y ) { for( ; x < maxxrender; x++ ) { if( !do_map_memory || !draw_maptile_from_memory( w, p, center, false ) ) { @@ -5869,7 +5889,7 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, } } - if( check_and_set_seen_cache( p ) ) { + if( param.memorize && check_and_set_seen_cache( p ) ) { g->u.memorize_symbol( getabs( p ), memory_sym ); } diff --git a/src/map.h b/src/map.h index 9ea98a6814a9..c90ff2a8d7f6 100644 --- a/src/map.h +++ b/src/map.h @@ -167,6 +167,8 @@ struct drawsq_params { * Allows to speed up drawing by taking advantage of automatic cursor movement. */ bool batch = false; + /** Whether the tile should be memorized. Used only in map::draw(). */ + bool memorize = false; /** Set view center. By default, uses avatar's current view center. */ void set_view_center( const tripoint &p ) { From f0bb35f7d76dc6fee099d30d8b9b43b6c2df513c Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 28 Nov 2020 14:50:17 +0300 Subject: [PATCH 05/26] Save/load memorized submaps; migrate old mm file --- src/avatar.cpp | 8 +-- src/avatar.h | 4 +- src/game.cpp | 10 +-- src/map_memory.cpp | 134 ++++++++++++++++++++++++++++++++++---- src/map_memory.h | 29 +++++++-- src/savegame_json.cpp | 145 +++++++++++++++++++----------------------- 6 files changed, 218 insertions(+), 112 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index 4560b799840f..497bf41c9b0f 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -136,14 +136,14 @@ bool avatar::should_show_map_memory() return show_map_memory; } -void avatar::serialize_map_memory( JsonOut &jsout ) const +bool avatar::save_map_memory() { - player_map_memory.store( jsout ); + return player_map_memory.save( pos() ); } -void avatar::deserialize_map_memory( JsonIn &jsin ) +void avatar::load_map_memory() { - player_map_memory.load( jsin ); + player_map_memory.load( pos() ); } void avatar::prepare_map_memory_region( const tripoint &p1, const tripoint &p2 ) diff --git a/src/avatar.h b/src/avatar.h index e50a69547127..c6a82f57ad9e 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -59,8 +59,8 @@ class avatar : public player void load( const JsonObject &data ); void serialize( JsonOut &json ) const override; void deserialize( JsonIn &jsin ) override; - void serialize_map_memory( JsonOut &jsout ) const; - void deserialize_map_memory( JsonIn &jsin ); + bool save_map_memory(); + void load_map_memory(); // newcharacter.cpp bool create( character_type type, const std::string &tempname = "" ); diff --git a/src/game.cpp b/src/game.cpp index a398487b51cc..745882940ddc 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2728,9 +2728,7 @@ bool game::load( const save_t &name ) return false; } - read_from_file_optional_json( playerpath + SAVE_EXTENSION_MAP_MEMORY, [&]( JsonIn & jsin ) { - u.deserialize_map_memory( jsin ); - } ); + u.load_map_memory(); weather.nextweather = calendar::turn; @@ -2936,11 +2934,7 @@ bool game::save_player_data() const bool saved_data = write_to_file( playerfile + SAVE_EXTENSION, [&]( std::ostream & fout ) { serialize( fout ); }, _( "player data" ) ); - const bool saved_map_memory = write_to_file( playerfile + SAVE_EXTENSION_MAP_MEMORY, [&]( - std::ostream & fout ) { - JsonOut jsout( fout ); - u.serialize_map_memory( jsout ); - }, _( "player map memory" ) ); + const bool saved_map_memory = u.save_map_memory(); const bool saved_log = write_to_file( playerfile + SAVE_EXTENSION_LOG, [&]( std::ostream & fout ) { fout << memorial().dump(); diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 952b36809b51..495d4286acea 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -1,19 +1,35 @@ #include "coordinate_conversions.h" +#include "filesystem.h" +#include "game.h" #include "map_memory.h" +#include "cata_utility.h" static const memorized_terrain_tile default_tile{ "", 0, 0 }; static const int default_symbol = 0; -memorized_submap::memorized_submap() : tiles{{ default_tile }}, symbols{{ default_symbol }} {} +#define MM_SIZE (MAPSIZE * 2) -struct coord_pair { - tripoint sm; - point loc; +static std::string find_legacy_mm_file() +{ + return g->get_player_base_save_path() + SAVE_EXTENSION_MAP_MEMORY; +} - coord_pair( const tripoint &p ) : loc( p.xy() ) { - sm = tripoint( ms_to_sm_remain( loc.x, loc.y ), p.z ); - } -}; +static std::string find_mm_dir() +{ + return string_format( "%s.mm1", g->get_player_base_save_path() ); +} + +static std::string find_submap_path( const std::string &dirname, const tripoint &p ) +{ + return string_format( "%s/%d.%d.%d.mm", dirname, p.x, p.y, p.z ); +} + +memorized_submap::memorized_submap() : tiles{{ default_tile }}, symbols{{ default_symbol }} {} + +map_memory::coord_pair::coord_pair( const tripoint &p ) : loc( p.xy() ) +{ + sm = tripoint( ms_to_sm_remain( loc.x, loc.y ), p.z ); +} memorized_terrain_tile map_memory::get_tile( const tripoint &pos ) const { @@ -69,24 +85,60 @@ void map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) cached.reserve( cache_size.x * cache_size.y ); for( int dy = 0; dy < cache_size.y; dy++ ) { for( int dx = 0; dx < cache_size.x; dx++ ) { - cached.push_back( prepare_submap( cache_pos + point( dx, dy ) ) ); + cached.push_back( fetch_submap( cache_pos + point( dx, dy ) ) ); } } } -shared_ptr_fast map_memory::prepare_submap( const tripoint &sm_pos ) +shared_ptr_fast map_memory::fetch_submap( const tripoint &sm_pos ) { auto sm = submaps.find( sm_pos ); if( sm == submaps.end() ) { - // TODO: load memorized submaps from disk - shared_ptr_fast new_sm = make_shared_fast(); - submaps.insert( std::make_pair( sm_pos, new_sm ) ); - return new_sm; + shared_ptr_fast sm1 = load_submap( sm_pos ); + if( !sm1 ) { + sm1 = allocate_submap(); + } + submaps.insert( std::make_pair( sm_pos, sm1 ) ); + return sm1; } else { return sm->second; } } +shared_ptr_fast map_memory::allocate_submap() +{ + return make_shared_fast(); +} + +shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) +{ + const std::string dirname = find_mm_dir(); + const std::string path = find_submap_path( dirname, sm_pos ); + + if( !dir_exist( dirname ) ) { + // Old saves don't have [plname].mm1 folder + return nullptr; + } + + shared_ptr_fast sm = nullptr; + const auto loader = [&]( JsonIn & jsin ) { + // Don't allocate submap unless we know its file exists + sm = allocate_submap(); + sm->deserialize( jsin ); + }; + + try { + if( read_from_file_optional_json( path, loader ) ) { + return sm; + } + } catch( const std::exception &err ) { + debugmsg( "Failed to load memory submap (%d,%d,%d): %s", + sm_pos.x, sm_pos.y, sm_pos.z, err.what() ); + } + + return nullptr; +} + static memorized_submap null_mz_submap; const memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) const @@ -108,3 +160,57 @@ memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) return null_mz_submap; } } + +void map_memory::load( const tripoint &pos ) +{ + const std::string dirname = find_mm_dir(); + + if( !dir_exist( dirname ) ) { + // Old saves have [plname].mm file and no [plname].mm1 folder + const std::string legacy_file = find_legacy_mm_file(); + if( file_exist( legacy_file ) ) { + try { + read_from_file_optional_json( legacy_file, [&]( JsonIn & jsin ) { + this->load_legacy( jsin ); + } ); + } catch( const std::exception &err ) { + debugmsg( "Failed to load legacy memory map file: %s", err.what() ); + } + } + return; + } + + coord_pair p( pos ); + tripoint start = p.sm - tripoint( MM_SIZE / 2, MM_SIZE / 2, 0 ); + for( int dy = 0; dy < MM_SIZE; dy++ ) { + for( int dx = 0; dx < MM_SIZE; dx++ ) { + fetch_submap( start + tripoint( dx, dy, 0 ) ); + } + } +} + +bool map_memory::save( const tripoint &/*pos*/ ) +{ + const std::string dirname = find_mm_dir(); + assure_dir_exist( dirname ); + + for( const auto &it : submaps ) { + const tripoint &sm_pos = it.first; + const std::string path = find_submap_path( dirname, sm_pos ); + const std::string descr = string_format( + _( "player map memory for (%d,%d,%d)" ), + sm_pos.x, sm_pos.y, sm_pos.z + ); + + write_to_file( path, [&]( std::ostream & fout ) -> void { + fout << serialize_wrapper( [&]( JsonOut & jsout ) + { + it.second->serialize( jsout ); + } ); + }, descr.c_str() ); + } + + // TODO: drop unused submaps + + return true; +} diff --git a/src/map_memory.h b/src/map_memory.h index d638fe515b79..ec561dd020c1 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -10,7 +10,6 @@ #include "point.h" // IWYU pragma: keep class JsonOut; -class JsonObject; class JsonIn; struct memorized_terrain_tile { @@ -24,6 +23,9 @@ struct memorized_submap { int symbols[SEEX][SEEY]; memorized_submap(); + + void serialize( JsonOut &jsout ) const; + void deserialize( JsonIn &jsin ); }; /** @@ -36,9 +38,17 @@ struct memorized_submap { class map_memory { public: - void store( JsonOut &jsout ) const; - void load( JsonIn &jsin ); - void load( const JsonObject &jsin ); + /** Load memorized submaps around given (global) pos. */ + void load( const tripoint &pos ); + + /** Load legacy memory file. TODO: remove after 0.F (or whatever BN will have instead). */ + ///@{ + void load_legacy( const std::string &path ); + void load_legacy( JsonIn &jsin ); + ///@} + + /** Save memorized submaps to disk, drop far-away ones */ + bool save( const tripoint &pos ); /** * Prepares map memory for rendering and/or memorization of given region. @@ -79,13 +89,22 @@ class map_memory void clear_memorized_tile( const tripoint &pos ); private: + struct coord_pair { + tripoint sm; + point loc; + + coord_pair( const tripoint &p ); + }; + std::map> submaps; std::vector> cached; tripoint cache_pos; point cache_size; - shared_ptr_fast prepare_submap( const tripoint &sm_pos ); + shared_ptr_fast fetch_submap( const tripoint &sm_pos ); + shared_ptr_fast load_submap( const tripoint &sm_pos ); + shared_ptr_fast allocate_submap(); const memorized_submap &get_submap( const tripoint &sm_pos ) const; memorized_submap &get_submap( const tripoint &sm_pos ); diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 93e0305035ad..18de7afc2c22 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -1138,10 +1138,6 @@ void avatar::load( const JsonObject &data ) } } - //Load from legacy map_memory save location (now in its own file .mm) - if( data.has_member( "map_memory_tiles" ) || data.has_member( "map_memory_curses" ) ) { - player_map_memory.load( data ); - } data.read( "show_map_memory", show_map_memory ); for( JsonArray pair : data.get_array( "assigned_invlet" ) ) { @@ -3082,98 +3078,89 @@ void player_morale::load( const JsonObject &jsin ) jsin.read( "morale", points ); } -void map_memory::store( JsonOut &/*jsout*/ ) const +void memorized_submap::serialize( JsonOut &jsout ) const { - /* - jsout.start_array(); jsout.start_array(); - for( const auto &elem : tile_cache.list() ) { - jsout.start_array(); - jsout.write( elem.first.x ); - jsout.write( elem.first.y ); - jsout.write( elem.first.z ); - jsout.write( elem.second.tile ); - jsout.write( elem.second.subtile ); - jsout.write( elem.second.rotation ); - jsout.end_array(); - } - jsout.end_array(); - - jsout.start_array(); - for( const auto &elem : symbol_cache.list() ) { - jsout.start_array(); - jsout.write( elem.first.x ); - jsout.write( elem.first.y ); - jsout.write( elem.first.z ); - jsout.write( elem.second ); - jsout.end_array(); + for( size_t y = 0; y < SEEY; y++ ) { + for( size_t x = 0; x < SEEX; x++ ) { + const memorized_terrain_tile &elem = tiles[x][y]; + jsout.start_array(); + jsout.write( elem.tile ); + jsout.write( elem.subtile ); + jsout.write( elem.rotation ); + jsout.write( symbols[x][y] ); + jsout.end_array(); + } } jsout.end_array(); - jsout.end_array(); - */ } -void map_memory::load( JsonIn &/*jsin*/ ) +void memorized_submap::deserialize( JsonIn &jsin ) { - /* - // Legacy loading of object version. - if( jsin.test_object() ) { - JsonObject jsobj = jsin.get_object(); - load( jsobj ); - } else { - // This file is large enough that it's more than called for to minimize the - // amount of data written and read and make it a bit less "friendly", - // and use the streaming interface. - jsin.start_array(); - tile_cache.clear(); - jsin.start_array(); - while( !jsin.end_array() ) { - jsin.start_array(); - tripoint p; - p.x = jsin.get_int(); - p.y = jsin.get_int(); - p.z = jsin.get_int(); - const std::string tile = jsin.get_string(); - const int subtile = jsin.get_int(); - const int rotation = jsin.get_int(); - memorize_tile( std::numeric_limits::max(), p, - tile, subtile, rotation ); - jsin.end_array(); - } - symbol_cache.clear(); - jsin.start_array(); - while( !jsin.end_array() ) { + jsin.start_array(); + for( size_t y = 0; y < SEEY; y++ ) { + for( size_t x = 0; x < SEEX; x++ ) { + memorized_terrain_tile &elem = tiles[x][y]; jsin.start_array(); - tripoint p; - p.x = jsin.get_int(); - p.y = jsin.get_int(); - p.z = jsin.get_int(); - const int symbol = jsin.get_int(); - memorize_symbol( std::numeric_limits::max(), p, symbol ); + elem.tile = jsin.get_string(); + elem.subtile = jsin.get_int(); + elem.rotation = jsin.get_int(); + symbols[x][y] = jsin.get_int(); jsin.end_array(); } - jsin.end_array(); } - */ + jsin.end_array(); } -// Deserializer for legacy object-based memory map. -void map_memory::load( const JsonObject &/*jsin*/ ) +void map_memory::load_legacy( JsonIn &jsin ) { - /* - tile_cache.clear(); - for( JsonObject pmap : jsin.get_array( "map_memory_tiles" ) ) { - const tripoint p( pmap.get_int( "x" ), pmap.get_int( "y" ), pmap.get_int( "z" ) ); - memorize_tile( std::numeric_limits::max(), p, pmap.get_string( "tile" ), - pmap.get_int( "subtile" ), pmap.get_int( "rotation" ) ); + struct mig_elem { + int symbol; + memorized_terrain_tile tile; + }; + std::map elems; + + jsin.start_array(); + jsin.start_array(); + while( !jsin.end_array() ) { + jsin.start_array(); + tripoint p; + p.x = jsin.get_int(); + p.y = jsin.get_int(); + p.z = jsin.get_int(); + mig_elem &elem = elems[p]; + elem.tile.tile = jsin.get_string(); + elem.tile.subtile = jsin.get_int(); + elem.tile.rotation = jsin.get_int(); + jsin.end_array(); + } + jsin.start_array(); + while( !jsin.end_array() ) { + jsin.start_array(); + tripoint p; + p.x = jsin.get_int(); + p.y = jsin.get_int(); + p.z = jsin.get_int(); + elems[p].symbol = jsin.get_int(); + jsin.end_array(); } + jsin.end_array(); - symbol_cache.clear(); - for( JsonObject pmap : jsin.get_array( "map_memory_curses" ) ) { - const tripoint p( pmap.get_int( "x" ), pmap.get_int( "y" ), pmap.get_int( "z" ) ); - memorize_symbol( std::numeric_limits::max(), p, pmap.get_int( "symbol" ) ); + for( const std::pair &elem : elems ) { + coord_pair cp( elem.first ); + + auto sm_iter = submaps.find( cp.sm ); + shared_ptr_fast sm = nullptr; + if( sm_iter == submaps.end() ) { + sm = allocate_submap(); + submaps.insert( std::make_pair( cp.sm, sm ) ); + } else { + sm = sm_iter->second; + } + + sm->tiles[cp.loc.x][cp.loc.y] = elem.second.tile; + sm->symbols[cp.loc.x][cp.loc.y] = elem.second.symbol; } - */ } void deserialize( point &p, JsonIn &jsin ) From 1c99a745f6a09a53e7404fb87ec997781fc8ad86 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 27 Nov 2020 18:51:47 +0300 Subject: [PATCH 06/26] Remove leftovers from map memory limit --- src/avatar.cpp | 2 -- src/avatar.h | 3 --- src/options.cpp | 8 -------- 3 files changed, 13 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index 497bf41c9b0f..96c5f9b87019 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -71,8 +71,6 @@ #include "vehicle.h" #include "vpart_position.h" -extern int memorized_tile_count; - static const activity_id ACT_READ( "ACT_READ" ); static const bionic_id bio_eye_optic( "bio_eye_optic" ); diff --git a/src/avatar.h b/src/avatar.h index c6a82f57ad9e..9b3b8b465448 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -215,9 +215,6 @@ class avatar : public player private: map_memory player_map_memory; bool show_map_memory; - /** Used in max_memorized_tiles to cache memory capacity. **/ - mutable time_point current_map_memory_turn = calendar::before_time_starts; - mutable size_t current_map_memory_capacity = 0; friend class debug_menu::mission_debug; /** diff --git a/src/options.cpp b/src/options.cpp index 3aca404f306b..417f93a82e50 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -56,7 +56,6 @@ int message_cooldown; bool fov_3d; int fov_3d_z_range; bool tile_iso; -int memorized_tile_count; std::map TILESETS; // All found tilesets: std::map SOUNDPACKS; // All found soundpacks: @@ -2007,11 +2006,6 @@ void options_manager::add_options_debug() translate_marker( "If true, enables completely unfinished electric grid system that only slows downs the game." ), false ); - - add( "MEMORIZED_TILE_COUNT", "debug", translate_marker( "Tile memory capacity" ), - translate_marker( "Controls number of memorized seen map tiles. High values increase memory usage and time to recall a tile." ), - 0, INT_MAX, 2 * SEEX * 2 * SEEY * 1000 - ); } void options_manager::add_options_world_default() @@ -3008,7 +3002,6 @@ bool options_manager::save() message_cooldown = ::get_option( "MESSAGE_COOLDOWN" ); fov_3d = ::get_option( "FOV_3D" ); fov_3d_z_range = ::get_option( "FOV_3D_Z_RANGE" ); - memorized_tile_count = ::get_option( "MEMORIZED_TILE_COUNT" ); update_music_volume(); @@ -3042,7 +3035,6 @@ void options_manager::load() message_cooldown = ::get_option( "MESSAGE_COOLDOWN" ); fov_3d = ::get_option( "FOV_3D" ); fov_3d_z_range = ::get_option( "FOV_3D_Z_RANGE" ); - memorized_tile_count = ::get_option( "MEMORIZED_TILE_COUNT" ); #if defined(SDL_SOUND) sounds::sound_enabled = ::get_option( "SOUND_ENABLED" ); #endif From 10f7388c93fb0e0f2567d5a49f33802e32b6684f Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 28 Nov 2020 14:59:33 +0300 Subject: [PATCH 07/26] Some map memory cleanup --- src/game.h | 1 - src/map_memory.cpp | 2 +- src/map_memory.h | 31 +++++++++++++++++++------------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/game.h b/src/game.h index 6b4d798469ae..28c661f06a47 100644 --- a/src/game.h +++ b/src/game.h @@ -42,7 +42,6 @@ class spell_events; static const std::string SAVE_MASTER( "master.gsav" ); static const std::string SAVE_ARTIFACTS( "artifacts.gsav" ); static const std::string SAVE_EXTENSION( ".sav" ); -static const std::string SAVE_EXTENSION_MAP_MEMORY( ".mm" ); static const std::string SAVE_EXTENSION_LOG( ".log" ); static const std::string SAVE_EXTENSION_WEATHER( ".weather" ); static const std::string SAVE_EXTENSION_SHORTCUTS( ".shortcuts" ); diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 495d4286acea..a31a1a49df54 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -11,7 +11,7 @@ static const int default_symbol = 0; static std::string find_legacy_mm_file() { - return g->get_player_base_save_path() + SAVE_EXTENSION_MAP_MEMORY; + return g->get_player_base_save_path() + ".mm"; } static std::string find_mm_dir() diff --git a/src/map_memory.h b/src/map_memory.h index ec561dd020c1..9fc1588f1e5c 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -5,7 +5,6 @@ #include #include "game_constants.h" -#include "lru_cache.h" #include "memory_fast.h" #include "point.h" // IWYU pragma: keep @@ -37,15 +36,24 @@ struct memorized_submap { */ class map_memory { + private: + /** + * Helper class for converting global ms coord into + * global sm coord + ms coord within the submap. + */ + struct coord_pair { + tripoint sm; + point loc; + + coord_pair( const tripoint &p ); + }; + public: /** Load memorized submaps around given (global) pos. */ void load( const tripoint &pos ); /** Load legacy memory file. TODO: remove after 0.F (or whatever BN will have instead). */ - ///@{ - void load_legacy( const std::string &path ); void load_legacy( JsonIn &jsin ); - ///@} /** Save memorized submaps to disk, drop far-away ones */ bool save( const tripoint &pos ); @@ -71,7 +79,7 @@ class map_memory memorized_terrain_tile get_tile( const tripoint &pos ) const; /** - * Memorizes given value, overwriting old value. + * Memorizes given symbol, overwriting old value. * @param pos tile position, in global ms coords. */ void memorize_symbol( const tripoint &pos, int symbol ); @@ -89,25 +97,24 @@ class map_memory void clear_memorized_tile( const tripoint &pos ); private: - struct coord_pair { - tripoint sm; - point loc; - - coord_pair( const tripoint &p ); - }; - std::map> submaps; std::vector> cached; tripoint cache_pos; point cache_size; + /** Make sure submap exists within 'submaps' and return its pointer */ shared_ptr_fast fetch_submap( const tripoint &sm_pos ); + /** Load submap from disk. @returns nullptr if failed. */ shared_ptr_fast load_submap( const tripoint &sm_pos ); + /** Allocate empty submap */ shared_ptr_fast allocate_submap(); + /** Get submap from within the cache */ + //@{ const memorized_submap &get_submap( const tripoint &sm_pos ) const; memorized_submap &get_submap( const tripoint &sm_pos ); + //@} }; #endif // CATA_SRC_MAP_MEMORY_H From 5df0454d80df8a7495e543f1396c85d04ba9870b Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 28 Nov 2020 16:37:44 +0300 Subject: [PATCH 08/26] Don't save empty mm submaps --- src/map_memory.cpp | 9 ++++++++- src/map_memory.h | 1 + src/savegame_json.cpp | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index a31a1a49df54..a35e57e9622a 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -24,7 +24,8 @@ static std::string find_submap_path( const std::string &dirname, const tripoint return string_format( "%s/%d.%d.%d.mm", dirname, p.x, p.y, p.z ); } -memorized_submap::memorized_submap() : tiles{{ default_tile }}, symbols{{ default_symbol }} {} +memorized_submap::memorized_submap() : clean( true ), + tiles{{ default_tile }}, symbols{{ default_symbol }} {} map_memory::coord_pair::coord_pair( const tripoint &p ) : loc( p.xy() ) { @@ -43,6 +44,7 @@ void map_memory::memorize_tile( const tripoint &pos, const std::string &ter, { coord_pair p( pos ); memorized_submap &sm = get_submap( p.sm ); + sm.clean = false; sm.tiles[p.loc.x][p.loc.y] = memorized_terrain_tile{ ter, subtile, rotation }; } @@ -57,6 +59,7 @@ void map_memory::memorize_symbol( const tripoint &pos, const int symbol ) { coord_pair p( pos ); memorized_submap &sm = get_submap( p.sm ); + sm.clean = false; sm.symbols[p.loc.x][p.loc.y] = symbol; } @@ -124,6 +127,7 @@ shared_ptr_fast map_memory::load_submap( const tripoint &sm_po const auto loader = [&]( JsonIn & jsin ) { // Don't allocate submap unless we know its file exists sm = allocate_submap(); + sm->clean = false; sm->deserialize( jsin ); }; @@ -195,6 +199,9 @@ bool map_memory::save( const tripoint &/*pos*/ ) assure_dir_exist( dirname ); for( const auto &it : submaps ) { + if( it.second->clean ) { + continue; + } const tripoint &sm_pos = it.first; const std::string path = find_submap_path( dirname, sm_pos ); const std::string descr = string_format( diff --git a/src/map_memory.h b/src/map_memory.h index 9fc1588f1e5c..65b63bf592e4 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -18,6 +18,7 @@ struct memorized_terrain_tile { }; struct memorized_submap { + bool clean; memorized_terrain_tile tiles[SEEX][SEEY]; int symbols[SEEX][SEEY]; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 18de7afc2c22..fa7df63a8588 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3153,6 +3153,7 @@ void map_memory::load_legacy( JsonIn &jsin ) shared_ptr_fast sm = nullptr; if( sm_iter == submaps.end() ) { sm = allocate_submap(); + sm->clean = false; submaps.insert( std::make_pair( cp.sm, sm ) ); } else { sm = sm_iter->second; From 542d5bd8c78f7b18379962e1520c3ba4f14ec5d4 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 28 Nov 2020 16:37:33 +0300 Subject: [PATCH 09/26] Deallocate far-away mm submaps on save; report save failure --- src/map_memory.cpp | 63 ++++++++++++++++++++++++++++++++-------------- src/map_memory.h | 4 +++ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index a35e57e9622a..1feaf651cef7 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -2,6 +2,7 @@ #include "filesystem.h" #include "game.h" #include "map_memory.h" +#include "line.h" #include "cata_utility.h" static const memorized_terrain_tile default_tile{ "", 0, 0 }; @@ -32,6 +33,11 @@ map_memory::coord_pair::coord_pair( const tripoint &p ) : loc( p.xy() ) sm = tripoint( ms_to_sm_remain( loc.x, loc.y ), p.z ); } +map_memory::map_memory() +{ + clear_cache(); +} + memorized_terrain_tile map_memory::get_tile( const tripoint &pos ) const { coord_pair p( pos ); @@ -169,6 +175,8 @@ void map_memory::load( const tripoint &pos ) { const std::string dirname = find_mm_dir(); + clear_cache(); + if( !dir_exist( dirname ) ) { // Old saves have [plname].mm file and no [plname].mm1 folder const std::string legacy_file = find_legacy_mm_file(); @@ -193,31 +201,48 @@ void map_memory::load( const tripoint &pos ) } } -bool map_memory::save( const tripoint &/*pos*/ ) +bool map_memory::save( const tripoint &pos ) { + tripoint sm_center = coord_pair( pos ).sm; const std::string dirname = find_mm_dir(); assure_dir_exist( dirname ); - for( const auto &it : submaps ) { - if( it.second->clean ) { - continue; + clear_cache(); + + bool result = true; + + for( auto it = submaps.cbegin(); it != submaps.cend(); ) { + const tripoint &sm_pos = it->first; + if( !it->second->clean ) { + const std::string path = find_submap_path( dirname, sm_pos ); + const std::string descr = string_format( + _( "player map memory for (%d,%d,%d)" ), + sm_pos.x, sm_pos.y, sm_pos.z + ); + + const auto writer = [&]( std::ostream & fout ) -> void { + fout << serialize_wrapper( [&]( JsonOut & jsout ) + { + it->second->serialize( jsout ); + } ); + }; + + const bool res = write_to_file( path, writer, descr.c_str() ); + result = result & res; + } + if( square_dist( sm_center, sm_pos ) > MM_SIZE ) { + it = submaps.erase( it ); + } else { + ++it; } - const tripoint &sm_pos = it.first; - const std::string path = find_submap_path( dirname, sm_pos ); - const std::string descr = string_format( - _( "player map memory for (%d,%d,%d)" ), - sm_pos.x, sm_pos.y, sm_pos.z - ); - - write_to_file( path, [&]( std::ostream & fout ) -> void { - fout << serialize_wrapper( [&]( JsonOut & jsout ) - { - it.second->serialize( jsout ); - } ); - }, descr.c_str() ); } - // TODO: drop unused submaps + return result; +} - return true; +void map_memory::clear_cache() +{ + cached.clear(); + cache_pos = tripoint_min; + cache_size = point_zero; } diff --git a/src/map_memory.h b/src/map_memory.h index 65b63bf592e4..39664ccc19ed 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -50,6 +50,8 @@ class map_memory }; public: + map_memory(); + /** Load memorized submaps around given (global) pos. */ void load( const tripoint &pos ); @@ -116,6 +118,8 @@ class map_memory const memorized_submap &get_submap( const tripoint &sm_pos ) const; memorized_submap &get_submap( const tripoint &sm_pos ); //@} + + void clear_cache(); }; #endif // CATA_SRC_MAP_MEMORY_H From 197969054fbfc3f530319b8b9d15b677dd92114e Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 28 Nov 2020 19:16:50 +0300 Subject: [PATCH 10/26] Rework tests for map memory --- src/map_memory.cpp | 9 ++++- src/map_memory.h | 3 +- tests/map_memory_test.cpp | 83 +++++++++++++++------------------------ 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 1feaf651cef7..ca2a75bdf6ea 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -77,7 +77,7 @@ void map_memory::clear_memorized_tile( const tripoint &pos ) sm.tiles[p.loc.x][p.loc.y] = default_tile; } -void map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) +bool map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) { assert( p1.z == p2.z ); assert( p1.x <= p2.x && p1.y <= p2.y ); @@ -85,7 +85,7 @@ void map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) tripoint sm_pos = coord_pair( p1 ).sm - point( 1, 1 ); point sm_size = ( coord_pair( p2 ).sm - sm_pos ).xy() + point( 1, 1 ); if( ( sm_pos == cache_pos ) && ( sm_size == cache_size ) ) { - return; + return false; } cache_pos = sm_pos; @@ -97,6 +97,7 @@ void map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) cached.push_back( fetch_submap( cache_pos + point( dx, dy ) ) ); } } + return true; } shared_ptr_fast map_memory::fetch_submap( const tripoint &sm_pos ) @@ -121,6 +122,10 @@ shared_ptr_fast map_memory::allocate_submap() shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) { + if( test_mode ) { + return nullptr; + } + const std::string dirname = find_mm_dir(); const std::string path = find_submap_path( dirname, sm_pos ); diff --git a/src/map_memory.h b/src/map_memory.h index 39664ccc19ed..6188c3edab57 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -66,8 +66,9 @@ class map_memory * @param p1 top-left corner of the region, in global ms coords * @param p2 bottom-right corner of the region, in global ms coords * Both coords are inclusive and should be on the same Z level. + * @return whether the region was re-cached */ - void prepare_region( const tripoint &p1, const tripoint &p2 ); + bool prepare_region( const tripoint &p1, const tripoint &p2 ); /** * Memorizes given tile, overwriting old value. diff --git a/tests/map_memory_test.cpp b/tests/map_memory_test.cpp index 023e5b03938d..f6c3a67df914 100644 --- a/tests/map_memory_test.cpp +++ b/tests/map_memory_test.cpp @@ -11,13 +11,33 @@ #include "map_memory.h" #include "point.h" -static constexpr tripoint p1{ tripoint_above }; -static constexpr tripoint p2{ 0, 0, 2 }; -static constexpr tripoint p3{ 0, 0, 3 }; +static constexpr tripoint p1{ -SEEX - 2, -SEEY - 3, -1 }; +static constexpr tripoint p2{ 5, 7, -1 }; +static constexpr tripoint p3{ SEEX * 2 + 5, SEEY + 7, -1 }; +static constexpr tripoint p4{ SEEX * 3 + 2, SEEY * 7 + 1, -1 }; + +TEST_CASE( "map_memory_keeps_region", "[map_memory]" ) +{ + map_memory memory; + CHECK( memory.prepare_region( p1, p2 ) ); + CHECK( !memory.prepare_region( p1, p2 ) ); + CHECK( !memory.prepare_region( p1 + tripoint_east, p2 + tripoint_east ) ); + CHECK( memory.prepare_region( p2, p3 ) ); + CHECK( memory.prepare_region( p1, p3 ) ); + CHECK( !memory.prepare_region( p1, p3 ) ); + CHECK( !memory.prepare_region( p2, p3 ) ); + CHECK( memory.prepare_region( p1, p4 ) ); + CHECK( !memory.prepare_region( p2, p3 ) ); + CHECK( memory.prepare_region( + tripoint( p2.x, p2.y, -p2.z ), + tripoint( p3.x, p3.y, -p3.z ) + ) ); +} TEST_CASE( "map_memory_defaults", "[map_memory]" ) { map_memory memory; + memory.prepare_region( p1, p2 ); CHECK( memory.get_symbol( p1 ) == 0 ); memorized_terrain_tile default_tile = memory.get_tile( p1 ); CHECK( default_tile.tile.empty() ); @@ -28,66 +48,25 @@ TEST_CASE( "map_memory_defaults", "[map_memory]" ) TEST_CASE( "map_memory_remembers", "[map_memory]" ) { map_memory memory; - memory.memorize_symbol( 2, p1, 1 ); - memory.memorize_symbol( 2, p2, 2 ); + memory.prepare_region( p1, p2 ); + memory.memorize_symbol( p1, 1 ); + memory.memorize_symbol( p2, 2 ); CHECK( memory.get_symbol( p1 ) == 1 ); CHECK( memory.get_symbol( p2 ) == 2 ); } -TEST_CASE( "map_memory_limited", "[map_memory]" ) -{ - lru_cache symbol_cache; - symbol_cache.insert( 2, p1, 1 ); - symbol_cache.insert( 2, p2, 1 ); - symbol_cache.insert( 2, p3, 1 ); - CHECK( symbol_cache.get( p1, 0 ) == 0 ); -} - TEST_CASE( "map_memory_overwrites", "[map_memory]" ) { map_memory memory; - memory.memorize_symbol( 2, p1, 1 ); - memory.memorize_symbol( 2, p2, 2 ); - memory.memorize_symbol( 2, p2, 3 ); + memory.prepare_region( p1, p2 ); + memory.memorize_symbol( p1, 1 ); + memory.memorize_symbol( p2, 2 ); + memory.memorize_symbol( p2, 3 ); CHECK( memory.get_symbol( p1 ) == 1 ); CHECK( memory.get_symbol( p2 ) == 3 ); } -TEST_CASE( "map_memory_erases_lru", "[map_memory]" ) -{ - lru_cache symbol_cache; - symbol_cache.insert( 2, p1, 1 ); - symbol_cache.insert( 2, p2, 2 ); - symbol_cache.insert( 2, p1, 1 ); - symbol_cache.insert( 2, p3, 3 ); - CHECK( symbol_cache.get( p1, 0 ) == 1 ); - CHECK( symbol_cache.get( p2, 0 ) == 0 ); - CHECK( symbol_cache.get( p3, 0 ) == 3 ); -} - -TEST_CASE( "map_memory_survives_save_lod", "[map_memory]" ) -{ - map_memory memory; - memory.memorize_symbol( 2, p1, 1 ); - memory.memorize_symbol( 2, p2, 2 ); - - // Save and reload - std::ostringstream jsout_s; - JsonOut jsout( jsout_s ); - memory.store( jsout ); - - INFO( "Json was: " << jsout_s.str() ); - std::istringstream jsin_s( jsout_s.str() ); - JsonIn jsin( jsin_s ); - map_memory memory2; - memory2.load( jsin ); - - memory.memorize_symbol( 2, p3, 3 ); - memory2.memorize_symbol( 2, p3, 3 ); - CHECK( memory.get_symbol( p1 ) == memory2.get_symbol( p1 ) ); - CHECK( memory.get_symbol( p2 ) == memory2.get_symbol( p2 ) ); - CHECK( memory.get_symbol( p3 ) == memory2.get_symbol( p3 ) ); -} +// TODO: map memory save / load #include From 46ade5ca5e9d2156dc74bf576d8590b1cbe87df6 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 28 Nov 2020 19:47:22 +0300 Subject: [PATCH 11/26] Don't re-allocate region if old region contains required submaps --- src/map_memory.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index ca2a75bdf6ea..8c9d069784d1 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -84,8 +84,11 @@ bool map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) tripoint sm_pos = coord_pair( p1 ).sm - point( 1, 1 ); point sm_size = ( coord_pair( p2 ).sm - sm_pos ).xy() + point( 1, 1 ); - if( ( sm_pos == cache_pos ) && ( sm_size == cache_size ) ) { - return false; + if( sm_pos.z == cache_pos.z ) { + rectangle rect( cache_pos.xy(), cache_pos.xy() + cache_size ); + if( rect.contains_half_open( sm_pos.xy() ) && rect.contains_inclusive( sm_pos.xy() + sm_size ) ) { + return false; + } } cache_pos = sm_pos; From 4f17cc80fbf35e69c0c73b106aff83a630c8ab59 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 28 Nov 2020 20:59:57 +0300 Subject: [PATCH 12/26] Remove map_memory.h from avatar.h --- src/avatar.cpp | 24 +++++++++++++++--------- src/avatar.h | 12 +++++++++--- src/map_memory.cpp | 2 +- src/map_memory.h | 2 +- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index 96c5f9b87019..717d04bdc1a5 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -40,6 +40,7 @@ #include "iuse.h" #include "kill_tracker.h" #include "map.h" +#include "map_memory.h" #include "martialarts.h" #include "messages.h" #include "mission.h" @@ -119,11 +120,16 @@ static void skim_book_msg( const item &book, avatar &u ); avatar::avatar() { + player_map_memory = std::make_unique(); show_map_memory = true; active_mission = nullptr; grab_type = OBJECT_NONE; } +avatar::~avatar() = default; +avatar::avatar( avatar && ) = default; +avatar &avatar::operator=( avatar && ) = default; + void avatar::toggle_map_memory() { show_map_memory = !show_map_memory; @@ -136,43 +142,43 @@ bool avatar::should_show_map_memory() bool avatar::save_map_memory() { - return player_map_memory.save( pos() ); + return player_map_memory->save( pos() ); } void avatar::load_map_memory() { - player_map_memory.load( pos() ); + player_map_memory->load( pos() ); } void avatar::prepare_map_memory_region( const tripoint &p1, const tripoint &p2 ) { - player_map_memory.prepare_region( p1, p2 ); + player_map_memory->prepare_region( p1, p2 ); } -memorized_terrain_tile avatar::get_memorized_tile( const tripoint &pos ) const +const memorized_terrain_tile &avatar::get_memorized_tile( const tripoint &pos ) const { - return player_map_memory.get_tile( pos ); + return player_map_memory->get_tile( pos ); } void avatar::memorize_tile( const tripoint &pos, const std::string &ter, const int subtile, const int rotation ) { - player_map_memory.memorize_tile( pos, ter, subtile, rotation ); + player_map_memory->memorize_tile( pos, ter, subtile, rotation ); } void avatar::memorize_symbol( const tripoint &pos, const int symbol ) { - player_map_memory.memorize_symbol( pos, symbol ); + player_map_memory->memorize_symbol( pos, symbol ); } int avatar::get_memorized_symbol( const tripoint &p ) const { - return player_map_memory.get_symbol( p ); + return player_map_memory->get_symbol( p ); } void avatar::clear_memorized_tile( const tripoint &pos ) { - player_map_memory.clear_memorized_tile( pos ); + player_map_memory->clear_memorized_tile( pos ); } std::vector avatar::get_active_missions() const diff --git a/src/avatar.h b/src/avatar.h index 9b3b8b465448..32f178659bca 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -13,7 +13,6 @@ #include "enums.h" #include "item.h" #include "magic_teleporter_list.h" -#include "map_memory.h" #include "player.h" #include "pldata.h" #include "point.h" @@ -25,6 +24,8 @@ class faction; class mission; class monster; class npc; +class map_memory; +struct memorized_terrain_tile; namespace debug_menu { @@ -54,6 +55,11 @@ class avatar : public player { public: avatar(); + avatar( const avatar & ) = delete; + avatar( avatar && ); + ~avatar(); + avatar &operator=( const avatar & ) = delete; + avatar &operator=( avatar && ); void store( JsonOut &json ) const; void load( const JsonObject &data ); @@ -85,7 +91,7 @@ class avatar : public player void memorize_tile( const tripoint &pos, const std::string &ter, int subtile, int rotation ); /** Returns last stored map tile in given location in tiles mode */ - memorized_terrain_tile get_memorized_tile( const tripoint &p ) const; + const memorized_terrain_tile &get_memorized_tile( const tripoint &p ) const; /** Memorizes a given tile in curses mode; finalize_terrain_memory_curses needs to be called after it */ void memorize_symbol( const tripoint &pos, int symbol ); /** Returns last stored map tile in given location in curses mode */ @@ -213,7 +219,7 @@ class avatar : public player } private: - map_memory player_map_memory; + std::unique_ptr player_map_memory; bool show_map_memory; friend class debug_menu::mission_debug; diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 8c9d069784d1..1e4263b45264 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -38,7 +38,7 @@ map_memory::map_memory() clear_cache(); } -memorized_terrain_tile map_memory::get_tile( const tripoint &pos ) const +const memorized_terrain_tile &map_memory::get_tile( const tripoint &pos ) const { coord_pair p( pos ); const memorized_submap &sm = get_submap( p.sm ); diff --git a/src/map_memory.h b/src/map_memory.h index 6188c3edab57..5607636fe374 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -80,7 +80,7 @@ class map_memory * Returns memorized tile. * @param pos tile position, in global ms coords. */ - memorized_terrain_tile get_tile( const tripoint &pos ) const; + const memorized_terrain_tile &get_tile( const tripoint &pos ) const; /** * Memorizes given symbol, overwriting old value. From 2acbf2093df8acc0ca565b0e066de93ba500ac7a Mon Sep 17 00:00:00 2001 From: olanti-p Date: Thu, 3 Dec 2020 20:37:23 +0300 Subject: [PATCH 13/26] Rename memorized_submap -> mm_submap --- src/map_memory.cpp | 30 +++++++++++++++--------------- src/map_memory.h | 18 +++++++++--------- src/savegame_json.cpp | 6 +++--- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 1e4263b45264..70b7d12003d4 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -25,7 +25,7 @@ static std::string find_submap_path( const std::string &dirname, const tripoint return string_format( "%s/%d.%d.%d.mm", dirname, p.x, p.y, p.z ); } -memorized_submap::memorized_submap() : clean( true ), +mm_submap::mm_submap() : clean( true ), tiles{{ default_tile }}, symbols{{ default_symbol }} {} map_memory::coord_pair::coord_pair( const tripoint &p ) : loc( p.xy() ) @@ -41,7 +41,7 @@ map_memory::map_memory() const memorized_terrain_tile &map_memory::get_tile( const tripoint &pos ) const { coord_pair p( pos ); - const memorized_submap &sm = get_submap( p.sm ); + const mm_submap &sm = get_submap( p.sm ); return sm.tiles[p.loc.x][p.loc.y]; } @@ -49,7 +49,7 @@ void map_memory::memorize_tile( const tripoint &pos, const std::string &ter, const int subtile, const int rotation ) { coord_pair p( pos ); - memorized_submap &sm = get_submap( p.sm ); + mm_submap &sm = get_submap( p.sm ); sm.clean = false; sm.tiles[p.loc.x][p.loc.y] = memorized_terrain_tile{ ter, subtile, rotation }; } @@ -57,14 +57,14 @@ void map_memory::memorize_tile( const tripoint &pos, const std::string &ter, int map_memory::get_symbol( const tripoint &pos ) const { coord_pair p( pos ); - const memorized_submap &sm = get_submap( p.sm ); + const mm_submap &sm = get_submap( p.sm ); return sm.symbols[p.loc.x][p.loc.y]; } void map_memory::memorize_symbol( const tripoint &pos, const int symbol ) { coord_pair p( pos ); - memorized_submap &sm = get_submap( p.sm ); + mm_submap &sm = get_submap( p.sm ); sm.clean = false; sm.symbols[p.loc.x][p.loc.y] = symbol; } @@ -72,7 +72,7 @@ void map_memory::memorize_symbol( const tripoint &pos, const int symbol ) void map_memory::clear_memorized_tile( const tripoint &pos ) { coord_pair p( pos ); - memorized_submap &sm = get_submap( p.sm ); + mm_submap &sm = get_submap( p.sm ); sm.symbols[p.loc.x][p.loc.y] = default_symbol; sm.tiles[p.loc.x][p.loc.y] = default_tile; } @@ -103,11 +103,11 @@ bool map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) return true; } -shared_ptr_fast map_memory::fetch_submap( const tripoint &sm_pos ) +shared_ptr_fast map_memory::fetch_submap( const tripoint &sm_pos ) { auto sm = submaps.find( sm_pos ); if( sm == submaps.end() ) { - shared_ptr_fast sm1 = load_submap( sm_pos ); + shared_ptr_fast sm1 = load_submap( sm_pos ); if( !sm1 ) { sm1 = allocate_submap(); } @@ -118,12 +118,12 @@ shared_ptr_fast map_memory::fetch_submap( const tripoint &sm_p } } -shared_ptr_fast map_memory::allocate_submap() +shared_ptr_fast map_memory::allocate_submap() { - return make_shared_fast(); + return make_shared_fast(); } -shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) +shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) { if( test_mode ) { return nullptr; @@ -137,7 +137,7 @@ shared_ptr_fast map_memory::load_submap( const tripoint &sm_po return nullptr; } - shared_ptr_fast sm = nullptr; + shared_ptr_fast sm = nullptr; const auto loader = [&]( JsonIn & jsin ) { // Don't allocate submap unless we know its file exists sm = allocate_submap(); @@ -157,9 +157,9 @@ shared_ptr_fast map_memory::load_submap( const tripoint &sm_po return nullptr; } -static memorized_submap null_mz_submap; +static mm_submap null_mz_submap; -const memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) const +const mm_submap &map_memory::get_submap( const tripoint &sm_pos ) const { point idx = ( sm_pos - cache_pos ).xy(); if( idx.x > 0 && idx.y > 0 && idx.x < cache_size.x && idx.y < cache_size.y ) { @@ -169,7 +169,7 @@ const memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) const } } -memorized_submap &map_memory::get_submap( const tripoint &sm_pos ) +mm_submap &map_memory::get_submap( const tripoint &sm_pos ) { point idx = ( sm_pos - cache_pos ).xy(); if( idx.x > 0 && idx.y > 0 && idx.x < cache_size.x && idx.y < cache_size.y ) { diff --git a/src/map_memory.h b/src/map_memory.h index 5607636fe374..f1c105f766a9 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -17,12 +17,12 @@ struct memorized_terrain_tile { int rotation; }; -struct memorized_submap { +struct mm_submap { bool clean; memorized_terrain_tile tiles[SEEX][SEEY]; int symbols[SEEX][SEEY]; - memorized_submap(); + mm_submap(); void serialize( JsonOut &jsout ) const; void deserialize( JsonIn &jsin ); @@ -101,23 +101,23 @@ class map_memory void clear_memorized_tile( const tripoint &pos ); private: - std::map> submaps; + std::map> submaps; - std::vector> cached; + std::vector> cached; tripoint cache_pos; point cache_size; /** Make sure submap exists within 'submaps' and return its pointer */ - shared_ptr_fast fetch_submap( const tripoint &sm_pos ); + shared_ptr_fast fetch_submap( const tripoint &sm_pos ); /** Load submap from disk. @returns nullptr if failed. */ - shared_ptr_fast load_submap( const tripoint &sm_pos ); + shared_ptr_fast load_submap( const tripoint &sm_pos ); /** Allocate empty submap */ - shared_ptr_fast allocate_submap(); + shared_ptr_fast allocate_submap(); /** Get submap from within the cache */ //@{ - const memorized_submap &get_submap( const tripoint &sm_pos ) const; - memorized_submap &get_submap( const tripoint &sm_pos ); + const mm_submap &get_submap( const tripoint &sm_pos ) const; + mm_submap &get_submap( const tripoint &sm_pos ); //@} void clear_cache(); diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index fa7df63a8588..afd66ed62627 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3078,7 +3078,7 @@ void player_morale::load( const JsonObject &jsin ) jsin.read( "morale", points ); } -void memorized_submap::serialize( JsonOut &jsout ) const +void mm_submap::serialize( JsonOut &jsout ) const { jsout.start_array(); for( size_t y = 0; y < SEEY; y++ ) { @@ -3095,7 +3095,7 @@ void memorized_submap::serialize( JsonOut &jsout ) const jsout.end_array(); } -void memorized_submap::deserialize( JsonIn &jsin ) +void mm_submap::deserialize( JsonIn &jsin ) { jsin.start_array(); for( size_t y = 0; y < SEEY; y++ ) { @@ -3150,7 +3150,7 @@ void map_memory::load_legacy( JsonIn &jsin ) coord_pair cp( elem.first ); auto sm_iter = submaps.find( cp.sm ); - shared_ptr_fast sm = nullptr; + shared_ptr_fast sm = nullptr; if( sm_iter == submaps.end() ) { sm = allocate_submap(); sm->clean = false; From 9c06fdeaad2d47d7cd972a3bf19f5cbe3c5f7ad8 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Thu, 3 Dec 2020 21:02:17 +0300 Subject: [PATCH 14/26] Rename mm_submap::clean -> mm_submap::empty, add comments. --- src/map_memory.cpp | 10 +++++----- src/map_memory.h | 4 +++- src/savegame_json.cpp | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 70b7d12003d4..9ebbc04d436e 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -25,7 +25,7 @@ static std::string find_submap_path( const std::string &dirname, const tripoint return string_format( "%s/%d.%d.%d.mm", dirname, p.x, p.y, p.z ); } -mm_submap::mm_submap() : clean( true ), +mm_submap::mm_submap() : empty( true ), tiles{{ default_tile }}, symbols{{ default_symbol }} {} map_memory::coord_pair::coord_pair( const tripoint &p ) : loc( p.xy() ) @@ -50,7 +50,7 @@ void map_memory::memorize_tile( const tripoint &pos, const std::string &ter, { coord_pair p( pos ); mm_submap &sm = get_submap( p.sm ); - sm.clean = false; + sm.empty = false; sm.tiles[p.loc.x][p.loc.y] = memorized_terrain_tile{ ter, subtile, rotation }; } @@ -65,7 +65,7 @@ void map_memory::memorize_symbol( const tripoint &pos, const int symbol ) { coord_pair p( pos ); mm_submap &sm = get_submap( p.sm ); - sm.clean = false; + sm.empty = false; sm.symbols[p.loc.x][p.loc.y] = symbol; } @@ -141,7 +141,7 @@ shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) const auto loader = [&]( JsonIn & jsin ) { // Don't allocate submap unless we know its file exists sm = allocate_submap(); - sm->clean = false; + sm->empty = false; sm->deserialize( jsin ); }; @@ -221,7 +221,7 @@ bool map_memory::save( const tripoint &pos ) for( auto it = submaps.cbegin(); it != submaps.cend(); ) { const tripoint &sm_pos = it->first; - if( !it->second->clean ) { + if( !it->second->empty ) { const std::string path = find_submap_path( dirname, sm_pos ); const std::string descr = string_format( _( "player map memory for (%d,%d,%d)" ), diff --git a/src/map_memory.h b/src/map_memory.h index f1c105f766a9..fffb566338c3 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -18,7 +18,9 @@ struct memorized_terrain_tile { }; struct mm_submap { - bool clean; + /** Whether this mm_submap is empty. Empty submaps are skipped during saving. */ + bool empty; + memorized_terrain_tile tiles[SEEX][SEEY]; int symbols[SEEX][SEEY]; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index afd66ed62627..934a534fe3aa 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3153,7 +3153,7 @@ void map_memory::load_legacy( JsonIn &jsin ) shared_ptr_fast sm = nullptr; if( sm_iter == submaps.end() ) { sm = allocate_submap(); - sm->clean = false; + sm->empty = false; submaps.insert( std::make_pair( cp.sm, sm ) ); } else { sm = sm_iter->second; From 08899f4f920ba80c650a2b0661875fe412135c53 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Thu, 3 Dec 2020 21:49:48 +0300 Subject: [PATCH 15/26] Improve drawsq_params interface --- src/action.cpp | 4 +- src/animation.cpp | 12 ++--- src/construction.cpp | 5 +- src/editmap.cpp | 11 ++-- src/game.cpp | 9 +--- src/map.cpp | 47 ++++++++-------- src/map.h | 124 +++++++++++++++++++++++++++++++++++-------- src/ranged.cpp | 12 ++--- 8 files changed, 139 insertions(+), 85 deletions(-) diff --git a/src/action.cpp b/src/action.cpp index 82959f823196..e1c9785814cf 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -1093,11 +1093,9 @@ cata::optional choose_adjacent_highlight( const std::string &message, shared_ptr_fast hilite_cb; if( !valid.empty() ) { - drawsq_params params; - params.highlight = true; hilite_cb = make_shared_fast( [&]() { for( const tripoint &pos : valid ) { - g->m.drawsq( g->w_terrain, pos, params ); + g->m.drawsq( g->w_terrain, pos, drawsq_params().highlight( true ) ); } } ); g->add_draw_callback( hilite_cb ); diff --git a/src/animation.cpp b/src/animation.cpp index adb5275970f4..cad4121a3e06 100644 --- a/src/animation.cpp +++ b/src/animation.cpp @@ -453,9 +453,7 @@ void draw_bullet_curses( map &m, const tripoint &t, const char bullet, const tri shared_ptr_fast bullet_cb = make_shared_fast( [&]() { if( p != nullptr && p->z == vp.z ) { - drawsq_params params; - params.set_view_center( vp ); - m.drawsq( g->w_terrain, *p, params ); + m.drawsq( g->w_terrain, *p, drawsq_params().center( vp ) ); } mvwputch( g->w_terrain, t.xy() - vp.xy() + point( POSX, POSY ), c_red, bullet ); } ); @@ -620,9 +618,7 @@ namespace void draw_line_curses( game &g, const tripoint ¢er, const std::vector &ret, bool noreveal ) { - drawsq_params params; - params.highlight = true; - params.set_view_center( center ); + drawsq_params params = drawsq_params().highlight( true ).center( center ); for( const tripoint &p : ret ) { const auto critter = g.critter_at( p, true ); @@ -676,10 +672,8 @@ namespace { void draw_line_curses( game &g, const std::vector &points ) { - drawsq_params params; - params.highlight = true; for( const tripoint &p : points ) { - g.m.drawsq( g.w_terrain, p, params ); + g.m.drawsq( g.w_terrain, p, drawsq_params().highlight( true ) ); } const tripoint p = points.empty() ? tripoint {POSX, POSY, 0} : diff --git a/src/construction.cpp b/src/construction.cpp index ed97bbebff09..2217e93187ee 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -889,12 +889,9 @@ void place_construction( const std::string &desc ) } } - drawsq_params params; - params.highlight = true; - params.show_items = false; shared_ptr_fast draw_valid = make_shared_fast( [&]() { for( auto &elem : valid ) { - g->m.drawsq( g->w_terrain, elem.first, params ); + g->m.drawsq( g->w_terrain, elem.first, drawsq_params().highlight( true ).show_items( true ) ); } } ); g->add_draw_callback( draw_valid ); diff --git a/src/editmap.cpp b/src/editmap.cpp index 2f527e23a31f..cd4025c10832 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -461,8 +461,7 @@ void editmap::uber_draw_ter( const catacurses::window &w, map *m ) if( refresh_mplans ) { hilights["mplan"].points.clear(); } - drawsq_params params; - params.set_view_center( center ); + drawsq_params params = drawsq_params().center( center ); for( const tripoint &p : tripoint_range( start, end ) ) { int sym = game_map ? '%' : ' '; if( p.x >= 0 && p.x < msize && p.y >= 0 && p.y < msize ) { @@ -514,10 +513,7 @@ void editmap::draw_main_ui_overlay() if( critter != nullptr ) { critter->draw( g->w_terrain, target, true ); } else { - drawsq_params params; - params.highlight = true; - params.set_view_center( target ); - g->m.drawsq( g->w_terrain, target, params ); + g->m.drawsq( g->w_terrain, target, drawsq_params().highlight( true ).center( target ) ); } #ifdef TILES // give some visual indication of different cursor moving modes @@ -680,8 +676,7 @@ void editmap::draw_main_ui_overlay() #endif hilights["mapgentgt"].draw( *this, true ); tmpmap.reset_vehicle_cache( target.z ); - drawsq_params params; - params.set_view_center( tripoint( SEEX - 1, SEEY - 1, target.z ) ); + drawsq_params params = drawsq_params().center( tripoint( SEEX - 1, SEEY - 1, target.z ) ); for( const tripoint &p : tmpmap.points_on_zlevel() ) { tmpmap.drawsq( g->w_terrain, p, params ); } diff --git a/src/game.cpp b/src/game.cpp index 745882940ddc..0d8069478218 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5773,10 +5773,8 @@ void game::pickup() void game::pickup( const tripoint &p ) { // Highlight target - drawsq_params params; - params.highlight = true; shared_ptr_fast hilite_cb = make_shared_fast( [&]() { - m.drawsq( w_terrain, p, params ); + m.drawsq( w_terrain, p, drawsq_params().highlight( true ) ); } ); add_draw_callback( hilite_cb ); @@ -5859,10 +5857,7 @@ void game::draw_look_around_cursor( const tripoint &lp, const visibility_variabl if( creature != nullptr && u.sees( *creature ) ) { creature->draw( w_terrain, view_center, true ); } else { - drawsq_params params; - params.highlight = true; - params.set_view_center( view_center ); - m.drawsq( w_terrain, lp, params ); + m.drawsq( w_terrain, lp, drawsq_params().highlight( true ).center( view_center ) ); } } else { std::string visibility_indicator; diff --git a/src/map.cpp b/src/map.cpp index d8d800ef7307..1884ab60413f 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -5647,9 +5647,7 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) int &x = p.x; int &y = p.y; const bool do_map_memory = g->u.should_show_map_memory(); - drawsq_params params; - params.set_view_center( center ); - params.memorize = true; + drawsq_params params = drawsq_params().center( center ).memorize( true ); for( y = center.y - wnd_h / 2; y <= center.y + wnd_h / 2; y++ ) { if( y - center.y + wnd_h / 2 >= wnd_h ) { continue; @@ -5686,14 +5684,15 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) const visibility_type vis = get_visibility( lighting, cache ); if( !apply_vision_effects( w, vis ) ) { const maptile curr_maptile = maptile( cur_submap, l ); - params.low_light = lighting == LL_LOW; - params.bright_light = lighting == LL_BRIGHT; - params.batch = true; + params + .low_light( lighting == LL_LOW ) + .bright_light( lighting == LL_BRIGHT ) + .batch( true ); const bool just_this_z = draw_maptile( w, p, curr_maptile, params ); if( !just_this_z ) { p.z--; const maptile tile_below = maptile( sm_below, l ); - params.batch = false; + params.batch( false ); draw_from_above( w, p, tile_below, params ); p.z++; } @@ -5720,7 +5719,7 @@ void map::drawsq( const catacurses::window &w, const tripoint &p, { // If we are in tiles mode, the only thing we want to potentially draw is a highlight if( is_draw_tiles_mode() ) { - if( params.highlight ) { + if( params.highlight() ) { g->draw_highlight( p ); } return; @@ -5736,7 +5735,7 @@ void map::drawsq( const catacurses::window &w, const tripoint &p, tripoint below( p.xy(), p.z - 1 ); const maptile tile_below = maptile_at( below ); drawsq_params params2 = params; - params2.batch = false; + params2.batch( false ); draw_from_above( w, below, tile_below, params2 ); } } @@ -5777,7 +5776,7 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, } if( curr_ter.has_flag( TFLAG_SWIMMABLE ) && curr_ter.has_flag( TFLAG_DEEP_WATER ) && !g->u.is_underwater() ) { - param.show_items = false; // Can only see underwater items if WE are underwater + param.show_items( false ); // Can only see underwater items if WE are underwater } // If there's a trap here, and we have sufficient perception, draw that instead if( curr_trap.can_see( p, g->u ) ) { @@ -5859,7 +5858,7 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, std::string item_sym; // If there are items here, draw those instead - if( param.show_items && curr_maptile.get_item_count() > 0 && sees_some_items( p, g->u ) ) { + if( param.show_items() && curr_maptile.get_item_count() > 0 && sees_some_items( p, g->u ) ) { // if there's furniture/terrain/trap/fields (sym!='.') // and we should not override it, then only highlight the square if( sym != '.' && sym != '%' && !draw_item_sym ) { @@ -5871,7 +5870,7 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, tercol = curr_maptile.get_uppermost_item().color(); } if( curr_maptile.get_item_count() > 1 ) { - param.highlight = !param.highlight; + param.highlight( !param.highlight() ); } } } @@ -5889,7 +5888,7 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, } } - if( param.memorize && check_and_set_seen_cache( p ) ) { + if( param.memorize() && check_and_set_seen_cache( p ) ) { g->u.memorize_symbol( getabs( p ), memory_sym ); } @@ -5902,14 +5901,14 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, if( u_vision[BOOMERED] ) { tercol = c_magenta; } else if( u_vision[NV_GOGGLES] ) { - tercol = ( param.bright_light ) ? c_white : c_light_green; - } else if( param.low_light ) { + tercol = param.bright_light() ? c_white : c_light_green; + } else if( param.low_light() ) { tercol = c_dark_gray; } else if( u_vision[DARKNESS] ) { tercol = c_dark_gray; } - if( param.highlight ) { + if( param.highlight() ) { tercol = invert_color( tercol ); } else if( hi ) { tercol = hilite( tercol ); @@ -5917,7 +5916,7 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, tercol = red_background( tercol ); } - if( param.batch ) { + if( param.batch() ) { // Rastering the whole map, take advantage of automatically moving the cursor. if( item_sym.empty() ) { wputch( w, tercol, sym ); @@ -5926,7 +5925,7 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, } } else { // Otherwise move the cursor before drawing. - const tripoint view_center = param.get_view_center(); + const tripoint view_center = param.center(); const int k = p.x + getmaxx( w ) / 2 - view_center.x; const int j = p.y + getmaxy( w ) / 2 - view_center.y; if( item_sym.empty() ) { @@ -5998,21 +5997,21 @@ void map::draw_from_above( const catacurses::window &w, const tripoint &p, if( u_vision[BOOMERED] ) { tercol = c_magenta; } else if( u_vision[NV_GOGGLES] ) { - tercol = ( params.bright_light ) ? c_white : c_light_green; - } else if( params.low_light ) { + tercol = params.bright_light() ? c_white : c_light_green; + } else if( params.low_light() ) { tercol = c_dark_gray; } else if( u_vision[DARKNESS] ) { tercol = c_dark_gray; } - if( params.highlight ) { + if( params.highlight() ) { tercol = invert_color( tercol ); } - if( params.batch ) { + if( params.batch() ) { wputch( w, tercol, sym ); } else { - const tripoint view_center = params.get_view_center(); + const tripoint view_center = params.center(); const int k = p.x + getmaxx( w ) / 2 - view_center.x; const int j = p.y + getmaxy( w ) / 2 - view_center.y; mvwputch( w, point( k, j ), tercol, sym ); @@ -8576,7 +8575,7 @@ void map::on_saved() parent_electric_grids.clear(); } -tripoint drawsq_params::get_view_center() const +tripoint drawsq_params::center() const { if( view_center == tripoint_min ) { return g->u.pos() + g->u.view_offset; diff --git a/src/map.h b/src/map.h index c90ff2a8d7f6..c2beff8c189b 100644 --- a/src/map.h +++ b/src/map.h @@ -152,33 +152,115 @@ struct bash_params { struct drawsq_params { private: tripoint view_center = tripoint_min; + bool do_highlight = false; + bool do_show_items = true; + bool do_low_light = false; + bool do_bright_light = false; + bool do_batch = false; + bool do_memorize = false; public: - /** Highlight the tile. On TILES, draws an overlay; on CURSES, reverts color. */ - bool highlight = false; - /** Whether to draw items on the tile. */ - bool show_items = true; - /** Whether tile is low light, and should be drawn with muted color. */ - bool low_light = false; - /** Whether tile is in bright light. Affects NV overlay, and nothing else. */ - bool bright_light = false; - /** - * Set to 'true' when doing batch drawing (e.g. map::draw()). - * Allows to speed up drawing by taking advantage of automatic cursor movement. - */ - bool batch = false; - /** Whether the tile should be memorized. Used only in map::draw(). */ - bool memorize = false; - - /** Set view center. By default, uses avatar's current view center. */ - void set_view_center( const tripoint &p ) { + constexpr drawsq_params() = default; + + /** + * Highlight the tile. On TILES, draws an overlay; on CURSES, inverts color. + * Default: false. + */ + //@{ + constexpr drawsq_params &highlight( bool v ) { + do_highlight = v; + return *this; + } + constexpr bool highlight() const { + return do_highlight; + } + //@} + + /** + * Whether to draw items on the tile. + * Default: true. + */ + //@{ + constexpr drawsq_params &show_items( bool v ) { + do_show_items = v; + return *this; + } + constexpr bool show_items() const { + return do_show_items; + } + //@} + + /** + * Whether tile is low light, and should be drawn with muted color. + * Default: false. + */ + //@{ + constexpr drawsq_params &low_light( bool v ) { + do_low_light = v; + return *this; + } + constexpr bool low_light() const { + return do_low_light; + } + //@} + + /** + * Whether tile is in bright light. Affects NV overlay, and nothing else. + * Default: false; + */ + //@{ + constexpr drawsq_params &bright_light( bool v ) { + do_bright_light = v; + return *this; + } + constexpr bool bright_light() const { + return do_bright_light; + } + //@} + + /** + * Speed up drawing by taking advantage of automatic cursor movement. + * Default: false. + */ + //@{ + constexpr drawsq_params &batch( bool v ) { + do_batch = v; + return *this; + } + constexpr bool batch() const { + return do_batch; + } + //@} + + /** + * Whether the tile should be memorized. Used only in map::draw(). + * Default: false. + */ + //@{ + constexpr drawsq_params &memorize( bool v ) { + do_memorize = v; + return *this; + } + constexpr bool memorize() const { + return do_memorize; + } + //@} + + /** + * Set view center. + * Default: uses avatar's current view center. + */ + //@{ + constexpr drawsq_params ¢er( const tripoint &p ) { view_center = p; + return *this; } - /** Reset view center to avatar's view center */ - void reset_view_center() { + constexpr drawsq_params ¢er_at_avatar() { view_center = tripoint_min; + return *this; } - tripoint get_view_center() const; + tripoint center() const; + //@} }; struct level_cache { diff --git a/src/ranged.cpp b/src/ranged.cpp index 9b67f45ce3f8..e8da96c79859 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -1797,9 +1797,7 @@ std::vector target_handler::target_ui( player &pc, target_mode mode, if( critter != nullptr ) { g->draw_critter( *critter, center ); } else if( g->m.pl_sees( dst, -1 ) ) { - drawsq_params params; - params.set_view_center( center ); - g->m.drawsq( g->w_terrain, dst, params ); + g->m.drawsq( g->w_terrain, dst, drawsq_params().center( center ) ); } else { mvwputch( g->w_terrain, point( POSX, POSY ), c_black, 'X' ); } @@ -2220,9 +2218,7 @@ std::vector target_handler::target_ui( spell &casting, const bool no_f } g->draw_cursor( dst ); - drawsq_params params; - params.highlight = true; - params.set_view_center( center ); + drawsq_params params = drawsq_params().highlight( true ).center( center ); for( const tripoint &area : spell_aoe ) { g->m.drawsq( g->w_terrain, area, params ); } @@ -2326,9 +2322,7 @@ std::vector target_handler::target_ui( spell &casting, const bool no_f if( critter != nullptr ) { g->draw_critter( *critter, center ); } else if( g->m.pl_sees( dst, -1 ) ) { - drawsq_params params; - params.set_view_center( center ); - g->m.drawsq( g->w_terrain, dst, params ); + g->m.drawsq( g->w_terrain, dst, drawsq_params().center( center ) ); } // constrain by range From ef044ced79b2df4413068eb24ebdfd3b91fba2f2 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Thu, 3 Dec 2020 23:33:04 +0300 Subject: [PATCH 16/26] Remove map_memory.h from lru_cache.cpp --- src/lru_cache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lru_cache.cpp b/src/lru_cache.cpp index 0559c8e8d977..982843a7b307 100644 --- a/src/lru_cache.cpp +++ b/src/lru_cache.cpp @@ -3,7 +3,6 @@ #include #include -#include "map_memory.h" #include "point.h" template From 1dacb75f6e10dd391bafe4292943a798735c08ce Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 4 Dec 2020 01:50:20 +0300 Subject: [PATCH 17/26] Add function for rectangle overlapping --- src/point.h | 10 ++++++++ tests/point_test.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/point.h b/src/point.h index 3878ccc423cf..475416fb7fa6 100644 --- a/src/point.h +++ b/src/point.h @@ -223,10 +223,20 @@ struct rectangle { p.y >= p_min.y && p.y < p_max.y; } + constexpr bool overlaps_half_open( const rectangle &r ) const { + return !( r.p_min.x >= p_max.x || r.p_min.y >= p_max.y || + p_min.x >= r.p_max.x || p_min.y >= r.p_max.y ); + } + constexpr bool contains_inclusive( const point &p ) const { return p.x >= p_min.x && p.x <= p_max.x && p.y >= p_min.y && p.y <= p_max.y; } + + constexpr bool overlaps_inclusive( const rectangle &r ) const { + return !( r.p_min.x > p_max.x || r.p_min.y > p_max.y || + p_min.x > r.p_max.x || p_min.y > r.p_max.y ); + } }; // Clamp p to the half-open rectangle r. diff --git a/tests/point_test.cpp b/tests/point_test.cpp index a2dc6e728bd8..c464cfc4eedc 100644 --- a/tests/point_test.cpp +++ b/tests/point_test.cpp @@ -20,6 +20,65 @@ TEST_CASE( "rectangle_containment", "[point]" ) CHECK( !r.contains_inclusive( point( 0, 3 ) ) ); } +TEST_CASE( "rectangle_overlapping", "[point]" ) +{ + rectangle r1( point( 0, 0 ), point( 2, 2 ) ); // NOLINT(cata-use-named-point-constants) + rectangle r2( point( 2, 2 ), point( 3, 3 ) ); + rectangle r3( point( 0, 0 ), point( 2, 1 ) ); // NOLINT(cata-use-named-point-constants) + rectangle r4( point( -2, -4 ), point( 4, -1 ) ); + rectangle r5( point( -1, -3 ), point( 0, -2 ) ); + + CHECK( r1.overlaps_inclusive( r1 ) ); + CHECK( r1.overlaps_inclusive( r2 ) ); + CHECK( r1.overlaps_inclusive( r3 ) ); + CHECK( !r1.overlaps_inclusive( r4 ) ); + CHECK( !r1.overlaps_inclusive( r5 ) ); + + CHECK( r2.overlaps_inclusive( r1 ) ); + CHECK( r2.overlaps_inclusive( r2 ) ); + CHECK( !r2.overlaps_inclusive( r3 ) ); + CHECK( !r2.overlaps_inclusive( r4 ) ); + CHECK( !r2.overlaps_inclusive( r5 ) ); + + CHECK( r3.overlaps_inclusive( r1 ) ); + CHECK( !r3.overlaps_inclusive( r2 ) ); + CHECK( r3.overlaps_inclusive( r3 ) ); + CHECK( !r3.overlaps_inclusive( r4 ) ); + CHECK( !r3.overlaps_inclusive( r5 ) ); + + CHECK( !r4.overlaps_inclusive( r1 ) ); + CHECK( !r4.overlaps_inclusive( r2 ) ); + CHECK( !r4.overlaps_inclusive( r3 ) ); + CHECK( r4.overlaps_inclusive( r4 ) ); + CHECK( r4.overlaps_inclusive( r5 ) ); + CHECK( r5.overlaps_inclusive( r4 ) ); + + CHECK( r1.overlaps_half_open( r1 ) ); + CHECK( !r1.overlaps_half_open( r2 ) ); + CHECK( r1.overlaps_half_open( r3 ) ); + CHECK( !r1.overlaps_half_open( r4 ) ); + CHECK( !r1.overlaps_half_open( r5 ) ); + + CHECK( !r2.overlaps_half_open( r1 ) ); + CHECK( r2.overlaps_half_open( r2 ) ); + CHECK( !r2.overlaps_half_open( r3 ) ); + CHECK( !r2.overlaps_half_open( r4 ) ); + CHECK( !r2.overlaps_half_open( r5 ) ); + + CHECK( r3.overlaps_half_open( r1 ) ); + CHECK( !r3.overlaps_half_open( r2 ) ); + CHECK( r3.overlaps_half_open( r3 ) ); + CHECK( !r3.overlaps_half_open( r4 ) ); + CHECK( !r3.overlaps_half_open( r5 ) ); + + CHECK( !r4.overlaps_half_open( r1 ) ); + CHECK( !r4.overlaps_half_open( r2 ) ); + CHECK( !r4.overlaps_half_open( r3 ) ); + CHECK( r4.overlaps_half_open( r4 ) ); + CHECK( r4.overlaps_half_open( r5 ) ); + CHECK( r5.overlaps_half_open( r4 ) ); +} + TEST_CASE( "box_shrinks", "[point]" ) { box b( tripoint_zero, tripoint( 3, 3, 3 ) ); From 4cbd2f848c446aff3002d31e33b0441a2bb2bd64 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 4 Dec 2020 00:04:38 +0300 Subject: [PATCH 18/26] Save mm_submaps in regions --- src/coordinate_conversions.cpp | 10 +++ src/coordinate_conversions.h | 13 +++ src/game_constants.h | 3 + src/map_memory.cpp | 153 +++++++++++++++++++++++++-------- src/map_memory.h | 25 +++++- src/savegame_json.cpp | 48 ++++++++--- 6 files changed, 204 insertions(+), 48 deletions(-) diff --git a/src/coordinate_conversions.cpp b/src/coordinate_conversions.cpp index 04227c08a946..1a46a16ccb9e 100644 --- a/src/coordinate_conversions.cpp +++ b/src/coordinate_conversions.cpp @@ -184,3 +184,13 @@ tripoint omt_to_seg_copy( const tripoint &p ) { return tripoint( divide( p.x, SEG_SIZE ), divide( p.y, SEG_SIZE ), p.z ); } + +point sm_to_mmr_remain( int &x, int &y ) +{ + return point( divide( x, MM_REG_SIZE, x ), divide( y, MM_REG_SIZE, y ) ); +} + +tripoint mmr_to_sm_copy( const tripoint &p ) +{ + return tripoint( p.x * MM_REG_SIZE, p.y * MM_REG_SIZE, p.z ); +} diff --git a/src/coordinate_conversions.h b/src/coordinate_conversions.h index 2b5913df98f5..e40182575196 100644 --- a/src/coordinate_conversions.h +++ b/src/coordinate_conversions.h @@ -18,6 +18,14 @@ * om.y /= SEG_SIZE * (with special handling for negative values). * + * memory map region (mmr): Memory map region is a unit of tile memory saved to a directory. + * Each region contains MM_REG_SIZExMM_REG_SIZE memorized submaps, and is used only for + * saving/loading memorized submaps, see map_memory.cpp. + * Translation from sm to mmr: + * sm.x /= MM_REG_SIZE + * sm.y /= MM_REG_SIZE + * (with special handling for negative values). + * * overmap terrain (omt): the position of a overmap terrain (oter_id). * Each overmap contains (OMAPX * OMAPY) overmap terrains. * Translation from omt to om: @@ -190,5 +198,10 @@ inline point ms_to_omt_remain( point &p ) } // overmap terrain to map segment. tripoint omt_to_seg_copy( const tripoint &p ); +// Submap to memory map region. +point sm_to_mmr_remain( int &x, int &y ); +// Memory map region to submap. +// Note: this produces sm coords of top-left corner of the region. +tripoint mmr_to_sm_copy( const tripoint &p ); #endif // CATA_SRC_COORDINATE_CONVERSIONS_H diff --git a/src/game_constants.h b/src/game_constants.h index 13212cf87ef6..ea184acbf821 100644 --- a/src/game_constants.h +++ b/src/game_constants.h @@ -54,6 +54,9 @@ // Size of a square unit of terrain saved to a directory. #define SEG_SIZE 32 +// Size of a square unit of tile memory saved in a single file, in mm_submaps. +#define MM_REG_SIZE 8 + /** * Items on the map with at most this distance to the player are considered available for crafting, * see inventory::form_from_map diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 9ebbc04d436e..8343520efc2b 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -20,14 +20,41 @@ static std::string find_mm_dir() return string_format( "%s.mm1", g->get_player_base_save_path() ); } -static std::string find_submap_path( const std::string &dirname, const tripoint &p ) +static std::string find_region_path( const std::string &dirname, const tripoint &p ) { - return string_format( "%s/%d.%d.%d.mm", dirname, p.x, p.y, p.z ); + return string_format( "%s/%d.%d.%d.mmr", dirname, p.x, p.y, p.z ); } +/** + * Helper class for converting global sm coord into + * global mm_region coord + sm coord within the region. + */ +struct reg_coord_pair { + tripoint reg; + point sm_loc; + + reg_coord_pair( const tripoint &p ) : sm_loc( p.xy() ) { + reg = tripoint( sm_to_mmr_remain( sm_loc.x, sm_loc.y ), p.z ); + } +}; + mm_submap::mm_submap() : empty( true ), tiles{{ default_tile }}, symbols{{ default_symbol }} {} +mm_region::mm_region() : submaps {{ nullptr }} {} + +bool mm_region::is_empty() const +{ + for( size_t y = 0; y < MM_REG_SIZE; y++ ) { + for( size_t x = 0; x < MM_REG_SIZE; x++ ) { + if( !submaps[x][y]->empty ) { + return false; + } + } + } + return true; +} + map_memory::coord_pair::coord_pair( const tripoint &p ) : loc( p.xy() ) { sm = tripoint( ms_to_sm_remain( loc.x, loc.y ), p.z ); @@ -105,22 +132,46 @@ bool map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) shared_ptr_fast map_memory::fetch_submap( const tripoint &sm_pos ) { - auto sm = submaps.find( sm_pos ); - if( sm == submaps.end() ) { - shared_ptr_fast sm1 = load_submap( sm_pos ); - if( !sm1 ) { - sm1 = allocate_submap(); + shared_ptr_fast sm = find_submap( sm_pos ); + if( sm ) { + return sm; + } + sm = load_submap( sm_pos ); + if( sm ) { + return sm; + } + return allocate_submap( sm_pos ); +} + +shared_ptr_fast map_memory::allocate_submap( const tripoint &sm_pos ) +{ + // Since all save/load operations are done on regions of submaps, + // we need to allocate the whole region at once. + shared_ptr_fast ret; + tripoint reg = reg_coord_pair( sm_pos ).reg; + + for( size_t y = 0; y < MM_REG_SIZE; y++ ) { + for( size_t x = 0; x < MM_REG_SIZE; x++ ) { + tripoint pos = mmr_to_sm_copy( reg ) + tripoint( x, y, 0 ); + shared_ptr_fast sm = make_shared_fast(); + if( pos == sm_pos ) { + ret = sm; + } + submaps.insert( std::make_pair( pos, sm ) ); } - submaps.insert( std::make_pair( sm_pos, sm1 ) ); - return sm1; - } else { - return sm->second; } + + return ret; } -shared_ptr_fast map_memory::allocate_submap() +shared_ptr_fast map_memory::find_submap( const tripoint &sm_pos ) { - return make_shared_fast(); + auto sm = submaps.find( sm_pos ); + if( sm == submaps.end() ) { + return nullptr; + } else { + return sm->second; + } } shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) @@ -130,31 +181,44 @@ shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) } const std::string dirname = find_mm_dir(); - const std::string path = find_submap_path( dirname, sm_pos ); + reg_coord_pair p( sm_pos ); + const std::string path = find_region_path( dirname, p.reg ); if( !dir_exist( dirname ) ) { // Old saves don't have [plname].mm1 folder return nullptr; } - shared_ptr_fast sm = nullptr; + mm_region mmr; const auto loader = [&]( JsonIn & jsin ) { - // Don't allocate submap unless we know its file exists - sm = allocate_submap(); - sm->empty = false; - sm->deserialize( jsin ); + mmr.deserialize( jsin ); }; try { - if( read_from_file_optional_json( path, loader ) ) { - return sm; + if( !read_from_file_optional_json( path, loader ) ) { + // Region not found + return nullptr; } } catch( const std::exception &err ) { - debugmsg( "Failed to load memory submap (%d,%d,%d): %s", - sm_pos.x, sm_pos.y, sm_pos.z, err.what() ); + debugmsg( "Failed to load memory map region (%d,%d,%d): %s", + p.reg.x, p.reg.y, p.reg.z, err.what() ); + return nullptr; } - return nullptr; + shared_ptr_fast ret; + + for( size_t y = 0; y < MM_REG_SIZE; y++ ) { + for( size_t x = 0; x < MM_REG_SIZE; x++ ) { + tripoint pos = mmr_to_sm_copy( p.reg ) + tripoint( x, y, 0 ); + shared_ptr_fast &sm = mmr.submaps[x][y]; + if( pos == sm_pos ) { + ret = sm; + } + submaps.insert( std::make_pair( pos, sm ) ); + } + } + + return ret; } static mm_submap null_mz_submap; @@ -217,31 +281,50 @@ bool map_memory::save( const tripoint &pos ) clear_cache(); + // Since mm_submaps are always allocated in regions, + // we are certain that each region will be filled. + std::map regions; + for( auto &it : submaps ) { + const reg_coord_pair p( it.first ); + regions[p.reg].submaps[p.sm_loc.x][p.sm_loc.y] = it.second; + } + submaps.clear(); + + constexpr point MM_SIZE_P = point( MM_SIZE, MM_SIZE ); + rectangle rect_keep( sm_center.xy() - MM_SIZE_P, sm_center.xy() + MM_SIZE_P ); + bool result = true; - for( auto it = submaps.cbegin(); it != submaps.cend(); ) { - const tripoint &sm_pos = it->first; - if( !it->second->empty ) { - const std::string path = find_submap_path( dirname, sm_pos ); + for( auto &it : regions ) { + const tripoint ®p = it.first; + mm_region ® = it.second; + if( !reg.is_empty() ) { + const std::string path = find_region_path( dirname, regp ); const std::string descr = string_format( - _( "player map memory for (%d,%d,%d)" ), - sm_pos.x, sm_pos.y, sm_pos.z + _( "memory map region for (%d,%d,%d)" ), + regp.x, regp.y, regp.z ); const auto writer = [&]( std::ostream & fout ) -> void { fout << serialize_wrapper( [&]( JsonOut & jsout ) { - it->second->serialize( jsout ); + reg.serialize( jsout ); } ); }; const bool res = write_to_file( path, writer, descr.c_str() ); result = result & res; } - if( square_dist( sm_center, sm_pos ) > MM_SIZE ) { - it = submaps.erase( it ); - } else { - ++it; + rectangle rect_reg( mmr_to_sm_copy( regp ).xy(), point( MM_REG_SIZE, MM_REG_SIZE ) ); + if( rect_reg.overlaps_half_open( rect_keep ) ) { + // Put submaps back + for( size_t y = 0; y < MM_REG_SIZE; y++ ) { + for( size_t x = 0; x < MM_REG_SIZE; x++ ) { + tripoint p = regp + tripoint( x, y, 0 ); + shared_ptr_fast &sm = reg.submaps[x][y]; + submaps.insert( std::make_pair( p, sm ) ); + } + } } } diff --git a/src/map_memory.h b/src/map_memory.h index fffb566338c3..674e42beb371 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -17,6 +17,7 @@ struct memorized_terrain_tile { int rotation; }; +/** Represent a submap-sized chunk of tile memory. */ struct mm_submap { /** Whether this mm_submap is empty. Empty submaps are skipped during saving. */ bool empty; @@ -30,6 +31,22 @@ struct mm_submap { void deserialize( JsonIn &jsin ); }; +/** + * Represents a square of mm_submaps. + * For faster save/load, submaps are collected into regions + * and each region is saved in its own file. + */ +struct mm_region { + shared_ptr_fast submaps[MM_REG_SIZE][MM_REG_SIZE]; + + mm_region(); + + bool is_empty() const; + + void serialize( JsonOut &jsout ) const; + void deserialize( JsonIn &jsin ); +}; + /** * Manages map tiles memorized by the avatar. * Note that there are 2 separate memories in here: @@ -109,12 +126,14 @@ class map_memory tripoint cache_pos; point cache_size; - /** Make sure submap exists within 'submaps' and return its pointer */ + /** Find, load or allocate a submap. @returns the submap. */ shared_ptr_fast fetch_submap( const tripoint &sm_pos ); + /** Find submap amongst the loaded submaps. @returns nullptr if failed. */ + shared_ptr_fast find_submap( const tripoint &sm_pos ); /** Load submap from disk. @returns nullptr if failed. */ shared_ptr_fast load_submap( const tripoint &sm_pos ); - /** Allocate empty submap */ - shared_ptr_fast allocate_submap(); + /** Allocate empty submap. @returns the submap. */ + shared_ptr_fast allocate_submap( const tripoint &sm_pos ); /** Get submap from within the cache */ //@{ diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 934a534fe3aa..c77addde0f5d 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3112,6 +3112,40 @@ void mm_submap::deserialize( JsonIn &jsin ) jsin.end_array(); } +void mm_region::serialize( JsonOut &jsout ) const +{ + jsout.start_array(); + for( size_t y = 0; y < MM_REG_SIZE; y++ ) { + for( size_t x = 0; x < MM_REG_SIZE; x++ ) { + const shared_ptr_fast &sm = submaps[x][y]; + if( sm->empty ) { + jsout.write_null(); + } else { + sm->serialize( jsout ); + } + } + } + jsout.end_array(); +} + +void mm_region::deserialize( JsonIn &jsin ) +{ + jsin.start_array(); + for( size_t y = 0; y < MM_REG_SIZE; y++ ) { + for( size_t x = 0; x < MM_REG_SIZE; x++ ) { + shared_ptr_fast &sm = submaps[x][y]; + sm = make_shared_fast(); + if( jsin.test_null() ) { + jsin.skip_null(); + } else { + sm->empty = false; + sm->deserialize( jsin ); + } + } + } + jsin.end_array(); +} + void map_memory::load_legacy( JsonIn &jsin ) { struct mig_elem { @@ -3148,17 +3182,11 @@ void map_memory::load_legacy( JsonIn &jsin ) for( const std::pair &elem : elems ) { coord_pair cp( elem.first ); - - auto sm_iter = submaps.find( cp.sm ); - shared_ptr_fast sm = nullptr; - if( sm_iter == submaps.end() ) { - sm = allocate_submap(); - sm->empty = false; - submaps.insert( std::make_pair( cp.sm, sm ) ); - } else { - sm = sm_iter->second; + shared_ptr_fast sm = find_submap( cp.sm ); + if( !sm ) { + sm = allocate_submap( cp.sm ); } - + sm->empty = false; sm->tiles[cp.loc.x][cp.loc.y] = elem.second.tile; sm->symbols[cp.loc.x][cp.loc.y] = elem.second.symbol; } From f1e3b4d05f47d3fe47b02aca2e262111a4a0a86b Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 4 Dec 2020 01:36:09 +0300 Subject: [PATCH 19/26] Fix save/load not using avatar's global pos, improve docs. --- src/avatar.cpp | 4 ++-- src/map_memory.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index 717d04bdc1a5..8122defaa59a 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -142,12 +142,12 @@ bool avatar::should_show_map_memory() bool avatar::save_map_memory() { - return player_map_memory->save( pos() ); + return player_map_memory->save( g->m.getabs( pos() ) ); } void avatar::load_map_memory() { - player_map_memory->load( pos() ); + player_map_memory->load( g->m.getabs( pos() ) ); } void avatar::prepare_map_memory_region( const tripoint &p1, const tripoint &p2 ) diff --git a/src/map_memory.h b/src/map_memory.h index 674e42beb371..2a45a381b2f5 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -71,13 +71,13 @@ class map_memory public: map_memory(); - /** Load memorized submaps around given (global) pos. */ + /** Load memorized submaps around given global map square pos. */ void load( const tripoint &pos ); /** Load legacy memory file. TODO: remove after 0.F (or whatever BN will have instead). */ void load_legacy( JsonIn &jsin ); - /** Save memorized submaps to disk, drop far-away ones */ + /** Save memorized submaps to disk, drop ones far from given global map square pos. */ bool save( const tripoint &pos ); /** From 4d7ec4c7af41ba8b3a81cfdd458ccd73c356fd59 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Fri, 4 Dec 2020 02:10:00 +0300 Subject: [PATCH 20/26] When saving, compress mm_submaps using RLE --- src/savegame_json.cpp | 77 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index c77addde0f5d..05dd91831fe1 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3078,35 +3078,84 @@ void player_morale::load( const JsonObject &jsin ) jsin.read( "morale", points ); } +struct mm_elem { + memorized_terrain_tile tile; + int symbol; + + bool operator==( const mm_elem &rhs ) { + return symbol == rhs.symbol && tile.subtile == rhs.tile.subtile && + tile.rotation == rhs.tile.rotation && tile.tile == rhs.tile.tile; + } +}; + void mm_submap::serialize( JsonOut &jsout ) const { jsout.start_array(); + + // Uses RLE for compression. + + mm_elem last; + int num_same = 1; + + const auto write_seq = [&]() { + jsout.start_array(); + jsout.write( last.tile.tile ); + jsout.write( last.tile.subtile ); + jsout.write( last.tile.rotation ); + jsout.write( last.symbol ); + if( num_same != 1 ) { + jsout.write( num_same ); + } + jsout.end_array(); + }; + for( size_t y = 0; y < SEEY; y++ ) { for( size_t x = 0; x < SEEX; x++ ) { - const memorized_terrain_tile &elem = tiles[x][y]; - jsout.start_array(); - jsout.write( elem.tile ); - jsout.write( elem.subtile ); - jsout.write( elem.rotation ); - jsout.write( symbols[x][y] ); - jsout.end_array(); + const mm_elem elem = { tiles[x][y], symbols[x][y] }; + if( x == 0 && y == 0 ) { + last = elem; + continue; + } + if( last == elem ) { + num_same += 1; + continue; + } + write_seq(); + num_same = 1; + last = elem; } } + write_seq(); + jsout.end_array(); } void mm_submap::deserialize( JsonIn &jsin ) { jsin.start_array(); + + // Uses RLE for compression. + + mm_elem elem; + size_t remaining = 0; + for( size_t y = 0; y < SEEY; y++ ) { for( size_t x = 0; x < SEEX; x++ ) { - memorized_terrain_tile &elem = tiles[x][y]; - jsin.start_array(); - elem.tile = jsin.get_string(); - elem.subtile = jsin.get_int(); - elem.rotation = jsin.get_int(); - symbols[x][y] = jsin.get_int(); - jsin.end_array(); + if( remaining > 0 ) { + remaining -= 1; + } else { + jsin.start_array(); + elem.tile.tile = jsin.get_string(); + elem.tile.subtile = jsin.get_int(); + elem.tile.rotation = jsin.get_int(); + elem.symbol = jsin.get_int(); + if( jsin.test_int() ) { + remaining = jsin.get_int() - 1; + } + jsin.end_array(); + } + tiles[x][y] = elem.tile; + symbols[x][y] = elem.symbol; } } jsin.end_array(); From edb33d619f83a0aade9b841ca0b0bb0c8e3579a2 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 5 Dec 2020 00:08:21 +0300 Subject: [PATCH 21/26] Simplify map rendering in ascii mode Collect map::draw code in one place, simplify indexing/bound checks. Remove the "batch drawing" optimization: it doesn't help with drawing, but harms code clarity. Instead, convert all relevant draw methods to use 'wputch' and rely on caller to position the cursor properly. --- src/map.cpp | 232 +++++++++++++++++++++------------------------------- src/map.h | 20 ----- 2 files changed, 92 insertions(+), 160 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 1884ab60413f..65323857ceee 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -5556,55 +5556,21 @@ visibility_type map::get_visibility( const lit_level ll, const visibility_variab return VIS_HIDDEN; } -bool map::apply_vision_effects( const catacurses::window &w, const visibility_type vis ) const +static bool has_memory_at( const tripoint &p ) { - int symbol = ' '; - nc_color color = c_black; - - switch( vis ) { - case VIS_CLEAR: - // Drew the tile, so bail out now. - return false; - case VIS_LIT: - // can only tell that this square is bright - symbol = '#'; - color = c_light_gray; - break; - case VIS_BOOMER: - symbol = '#'; - color = c_pink; - break; - case VIS_BOOMER_DARK: - symbol = '#'; - color = c_magenta; - break; - case VIS_DARK: - // can't see this square at all - case VIS_HIDDEN: - symbol = ' '; - color = c_black; - break; + if( g->u.should_show_map_memory() ) { + int t = g->u.get_memorized_symbol( g->m.getabs( p ) ); + return t != 0; } - wputch( w, color, symbol ); - return true; + return false; } -bool map::draw_maptile_from_memory( const catacurses::window &w, const tripoint &p, - const tripoint &view_center, bool move_cursor ) const +static int get_memory_at( const tripoint &p ) { - int sym = g->u.get_memorized_symbol( getabs( p ) ); - if( sym == 0 ) { - return false; + if( g->u.should_show_map_memory() ) { + return g->u.get_memorized_symbol( g->m.getabs( p ) ); } - if( move_cursor ) { - const int k = p.x + getmaxx( w ) / 2 - view_center.x; - const int j = p.y + getmaxy( w ) / 2 - view_center.y; - - mvwputch( w, point( k, j ), c_brown, sym ); - } else { - wputch( w, c_brown, sym ); - } - return true; + return ' '; } void map::draw( const catacurses::window &w, const tripoint ¢er ) @@ -5623,93 +5589,87 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) int wnd_h = getmaxy( w ); int wnd_w = getmaxx( w ); + const tripoint offs = center - tripoint( wnd_w / 2, wnd_h / 2, 0 ); // Map memory should be at least the size of the view range // so that new tiles can be memorized, and at least the size of the terminal // since displayed area may be bigger than view range. const point min_mm_reg = point( - std::min( 0, center.x - wnd_w / 2 ), - std::min( 0, center.y - wnd_h / 2 ) + std::min( 0, offs.x ), + std::min( 0, offs.y ) ); const point max_mm_reg = point( - std::max( MAPSIZE_X, center.x - wnd_w / 2 + wnd_w ), - std::max( MAPSIZE_Y, center.y - wnd_h / 2 + wnd_h ) + std::max( MAPSIZE_X, offs.x + wnd_w ), + std::max( MAPSIZE_Y, offs.y + wnd_h ) ); g->u.prepare_map_memory_region( g->m.getabs( tripoint( min_mm_reg, center.z ) ), g->m.getabs( tripoint( max_mm_reg, center.z ) ) ); - // X and y are in map coordinates, but might be out of range of the map. - // When they are out of range, we just draw '#'s. - tripoint p; - p.z = center.z; - int &x = p.x; - int &y = p.y; - const bool do_map_memory = g->u.should_show_map_memory(); - drawsq_params params = drawsq_params().center( center ).memorize( true ); - for( y = center.y - wnd_h / 2; y <= center.y + wnd_h / 2; y++ ) { - if( y - center.y + wnd_h / 2 >= wnd_h ) { - continue; + const auto draw_background = [&]( const tripoint & p ) { + int sym = ' '; + nc_color col = c_black; + if( has_memory_at( p ) ) { + sym = get_memory_at( p ); + col = c_brown; } + wputch( w, col, sym ); + }; - wmove( w, point( 0, y - center.y + wnd_h / 2 ) ); + const auto draw_vision_effect = [&]( const visibility_type vis ) -> bool { + int sym = '#'; + nc_color col; + switch( vis ) + { + case VIS_LIT: + // can only tell that this square is bright + col = c_light_gray; + break; + case VIS_BOOMER: + col = c_pink; + break; + case VIS_BOOMER_DARK: + col = c_magenta; + break; + default: + return false; + } + wputch( w, col, sym ); + return true; + }; - const int maxxrender = center.x - wnd_w / 2 + wnd_w; - x = center.x - wnd_w / 2; - if( y < 0 || y >= MAPSIZE_Y ) { - for( ; x < maxxrender; x++ ) { - if( !do_map_memory || !draw_maptile_from_memory( w, p, center, false ) ) { - wputch( w, c_black, ' ' ); - } + drawsq_params params = drawsq_params().memorize( true ); + for( int wy = 0; wy < wnd_h; wy++ ) { + for( int wx = 0; wx < wnd_w; wx++ ) { + wmove( w, point( wx, wy ) ); + const tripoint p = offs + tripoint( wx, wy, 0 ); + if( !inbounds( p ) ) { + draw_background( p ); + continue; } - continue; - } - while( x < 0 ) { - if( !do_map_memory || !draw_maptile_from_memory( w, p, center, false ) ) { - wputch( w, c_black, ' ' ); - } - x++; - } - - point l; - const int maxx = std::min( MAPSIZE_X, maxxrender ); - while( x < maxx ) { - submap *cur_submap = get_submap_at( p, l ); - submap *sm_below = p.z > -OVERMAP_DEPTH ? - get_submap_at( {p.xy(), p.z - 1}, l ) : cur_submap; - while( l.x < SEEX && x < maxx ) { - const lit_level lighting = visibility_cache[x][y]; - const visibility_type vis = get_visibility( lighting, cache ); - if( !apply_vision_effects( w, vis ) ) { - const maptile curr_maptile = maptile( cur_submap, l ); - params - .low_light( lighting == LL_LOW ) - .bright_light( lighting == LL_BRIGHT ) - .batch( true ); - const bool just_this_z = draw_maptile( w, p, curr_maptile, params ); - if( !just_this_z ) { - p.z--; - const maptile tile_below = maptile( sm_below, l ); - params.batch( false ); - draw_from_above( w, p, tile_below, params ); - p.z++; - } - } else if( do_map_memory && ( vis == VIS_HIDDEN || vis == VIS_DARK ) ) { - draw_maptile_from_memory( w, p, center ); - } + const lit_level lighting = visibility_cache[p.x][p.y]; + const visibility_type vis = get_visibility( lighting, cache ); - l.x++; - x++; + if( draw_vision_effect( vis ) ) { + continue; } - } - while( x < maxxrender ) { - if( !do_map_memory || !draw_maptile_from_memory( w, p, center, false ) ) { - wputch( w, c_black, ' ' ); + if( vis == VIS_HIDDEN || vis == VIS_DARK ) { + draw_background( p ); + continue; } - x++; + + const maptile curr_maptile = maptile_at_internal( p ); + params + .low_light( lighting == LL_LOW ) + .bright_light( lighting == LL_BRIGHT ); + if( draw_maptile( w, p, curr_maptile, params ) ) { + continue; + } + const maptile tile_below = maptile_at_internal( p - tripoint( 0, 0, 1 ) ); + draw_from_above( w, tripoint( p.xy(), p.z - 1 ), tile_below, params ); } } } @@ -5729,15 +5689,19 @@ void map::drawsq( const catacurses::window &w, const tripoint &p, return; } + const tripoint view_center = params.center(); + const int k = p.x + getmaxx( w ) / 2 - view_center.x; + const int j = p.y + getmaxy( w ) / 2 - view_center.y; + wmove( w, point( k, j ) ); + const maptile tile = maptile_at( p ); - const bool done = draw_maptile( w, p, tile, params ); - if( !done ) { - tripoint below( p.xy(), p.z - 1 ); - const maptile tile_below = maptile_at( below ); - drawsq_params params2 = params; - params2.batch( false ); - draw_from_above( w, below, tile_below, params2 ); + if( draw_maptile( w, p, tile, params ) ) { + return; } + + tripoint below( p.xy(), p.z - 1 ); + const maptile tile_below = maptile_at( below ); + draw_from_above( w, below, tile_below, params ); } // a check to see if the lower floor needs to be rendered in tiles @@ -5916,27 +5880,22 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, tercol = red_background( tercol ); } - if( param.batch() ) { - // Rastering the whole map, take advantage of automatically moving the cursor. - if( item_sym.empty() ) { - wputch( w, tercol, sym ); + if( item_sym.empty() && sym == ' ' ) { + if( !zlevels || p.z <= -OVERMAP_DEPTH || !curr_ter.has_flag( TFLAG_NO_FLOOR ) ) { + // Print filler symbol + sym = ' '; + tercol = c_black; } else { - wprintz( w, tercol, item_sym ); + // Draw tile underneath this one instead + return false; } + } + if( item_sym.empty() ) { + wputch( w, tercol, sym ); } else { - // Otherwise move the cursor before drawing. - const tripoint view_center = param.center(); - const int k = p.x + getmaxx( w ) / 2 - view_center.x; - const int j = p.y + getmaxy( w ) / 2 - view_center.y; - if( item_sym.empty() ) { - mvwputch( w, point( k, j ), tercol, sym ); - } else { - mvwprintz( w, point( k, j ), tercol, item_sym ); - } + wprintz( w, tercol, item_sym ); } - - return !zlevels || sym != ' ' || !item_sym.empty() || p.z <= -OVERMAP_DEPTH || - !curr_ter.has_flag( TFLAG_NO_FLOOR ); + return true; } void map::draw_from_above( const catacurses::window &w, const tripoint &p, @@ -6008,14 +5967,7 @@ void map::draw_from_above( const catacurses::window &w, const tripoint &p, tercol = invert_color( tercol ); } - if( params.batch() ) { - wputch( w, tercol, sym ); - } else { - const tripoint view_center = params.center(); - const int k = p.x + getmaxx( w ) / 2 - view_center.x; - const int j = p.y + getmaxy( w ) / 2 - view_center.y; - mvwputch( w, point( k, j ), tercol, sym ); - } + wputch( w, tercol, sym ); } bool map::sees( const tripoint &F, const tripoint &T, const int range ) const diff --git a/src/map.h b/src/map.h index c2beff8c189b..bc28bf7703d4 100644 --- a/src/map.h +++ b/src/map.h @@ -156,7 +156,6 @@ struct drawsq_params { bool do_show_items = true; bool do_low_light = false; bool do_bright_light = false; - bool do_batch = false; bool do_memorize = false; public: @@ -218,20 +217,6 @@ struct drawsq_params { } //@} - /** - * Speed up drawing by taking advantage of automatic cursor movement. - * Default: false. - */ - //@{ - constexpr drawsq_params &batch( bool v ) { - do_batch = v; - return *this; - } - constexpr bool batch() const { - return do_batch; - } - //@} - /** * Whether the tile should be memorized. Used only in map::draw(). * Default: false. @@ -413,8 +398,6 @@ class map visibility_type get_visibility( lit_level ll, const visibility_variables &cache ) const; - bool apply_vision_effects( const catacurses::window &w, visibility_type vis ) const; - // See field.cpp std::tuple get_wind_blockers( const int &winddirection, const tripoint &pos ); @@ -1754,9 +1737,6 @@ class map */ bool draw_maptile( const catacurses::window &w, const tripoint &p, const maptile &tile, const drawsq_params ¶ms ) const; - bool draw_maptile_from_memory( const catacurses::window &w, const tripoint &p, - const tripoint &view_center, - bool move_cursor = true ) const; /** * Draws the tile as seen from above. */ From a6ef4ffdcfa1ab1ad6ed816ce4b240f340d0679e Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 5 Dec 2020 21:44:10 +0300 Subject: [PATCH 22/26] Memorize off-screen tiles in ascii mode --- src/map.cpp | 42 +++++++++++++++++++++++++++++++++++++----- src/map.h | 16 ++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 65323857ceee..fe547bbdd331 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -5672,6 +5672,33 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) draw_from_above( w, tripoint( p.xy(), p.z - 1 ), tile_below, params ); } } + + // Memorize off-screen tiles + rectangle display( offs.xy(), offs.xy() + point( wnd_w, wnd_h ) ); + drawsq_params mm_params = drawsq_params().memorize( true ).output( false ); + for( int y = 0; y < MAPSIZE_Y; y++ ) { + for( int x = 0; x < MAPSIZE_X; x++ ) { + const tripoint p( x, y, center.z ); + if( display.contains_half_open( p.xy() ) ) { + // Have been memorized during display loop + continue; + } + + const lit_level lighting = visibility_cache[p.x][p.y]; + const visibility_type vis = get_visibility( lighting, cache ); + + if( vis != VIS_CLEAR ) { + continue; + } + + const maptile curr_maptile = maptile_at_internal( p ); + params + .low_light( lighting == LL_LOW ) + .bright_light( lighting == LL_BRIGHT ); + + draw_maptile( w, p, curr_maptile, mm_params ); + } + } } void map::drawsq( const catacurses::window &w, const tripoint &p, @@ -5890,10 +5917,13 @@ bool map::draw_maptile( const catacurses::window &w, const tripoint &p, return false; } } - if( item_sym.empty() ) { - wputch( w, tercol, sym ); - } else { - wprintz( w, tercol, item_sym ); + + if( params.output() ) { + if( item_sym.empty() ) { + wputch( w, tercol, sym ); + } else { + wprintz( w, tercol, item_sym ); + } } return true; } @@ -5967,7 +5997,9 @@ void map::draw_from_above( const catacurses::window &w, const tripoint &p, tercol = invert_color( tercol ); } - wputch( w, tercol, sym ); + if( params.output() ) { + wputch( w, tercol, sym ); + } } bool map::sees( const tripoint &F, const tripoint &T, const int range ) const diff --git a/src/map.h b/src/map.h index bc28bf7703d4..ab04b7ddf393 100644 --- a/src/map.h +++ b/src/map.h @@ -157,6 +157,7 @@ struct drawsq_params { bool do_low_light = false; bool do_bright_light = false; bool do_memorize = false; + bool do_output = true; public: constexpr drawsq_params() = default; @@ -231,6 +232,21 @@ struct drawsq_params { } //@} + /** + * HACK: Whether the tile should be printed. Used only in map::draw() + * as a hack for memorizing off-screen tiles. + * Default: true. + */ + //@{ + constexpr drawsq_params &output( bool v ) { + do_output = v; + return *this; + } + constexpr bool output() const { + return do_output; + } + //@} + /** * Set view center. * Default: uses avatar's current view center. From 6caaf9ece4cddef0c15323f6a2dde9602d412068 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sat, 5 Dec 2020 22:14:44 +0300 Subject: [PATCH 23/26] Fix copypasted code --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index fe547bbdd331..0cce1da3c602 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -5692,7 +5692,7 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) } const maptile curr_maptile = maptile_at_internal( p ); - params + mm_params .low_light( lighting == LL_LOW ) .bright_light( lighting == LL_BRIGHT ); From 43bbc56e076a001674666a572875fa00041cc4ac Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sun, 6 Dec 2020 21:31:20 +0300 Subject: [PATCH 24/26] Fix tile memory saving code, minor cleanup. --- src/map_memory.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 8343520efc2b..9fe027a7f316 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -109,11 +109,15 @@ bool map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) assert( p1.z == p2.z ); assert( p1.x <= p2.x && p1.y <= p2.y ); - tripoint sm_pos = coord_pair( p1 ).sm - point( 1, 1 ); - point sm_size = ( coord_pair( p2 ).sm - sm_pos ).xy() + point( 1, 1 ); + tripoint sm_p1 = coord_pair( p1 ).sm - point( 1, 1 ); + tripoint sm_p2 = coord_pair( p2 ).sm + point( 1, 1 ); + + tripoint sm_pos = sm_p1; + point sm_size = sm_p2.xy() - sm_p1.xy(); + if( sm_pos.z == cache_pos.z ) { rectangle rect( cache_pos.xy(), cache_pos.xy() + cache_size ); - if( rect.contains_half_open( sm_pos.xy() ) && rect.contains_inclusive( sm_pos.xy() + sm_size ) ) { + if( rect.contains_inclusive( sm_p1.xy() ) && rect.contains_inclusive( sm_p2.xy() ) ) { return false; } } @@ -290,8 +294,8 @@ bool map_memory::save( const tripoint &pos ) } submaps.clear(); - constexpr point MM_SIZE_P = point( MM_SIZE, MM_SIZE ); - rectangle rect_keep( sm_center.xy() - MM_SIZE_P, sm_center.xy() + MM_SIZE_P ); + constexpr point MM_HSIZE_P = point( MM_SIZE / 2, MM_SIZE / 2 ); + rectangle rect_keep( sm_center.xy() - MM_HSIZE_P, sm_center.xy() + MM_HSIZE_P ); bool result = true; @@ -315,12 +319,13 @@ bool map_memory::save( const tripoint &pos ) const bool res = write_to_file( path, writer, descr.c_str() ); result = result & res; } - rectangle rect_reg( mmr_to_sm_copy( regp ).xy(), point( MM_REG_SIZE, MM_REG_SIZE ) ); + point regp_sm = mmr_to_sm_copy( regp ).xy(); + rectangle rect_reg( regp_sm, regp_sm + point( MM_REG_SIZE, MM_REG_SIZE ) ); if( rect_reg.overlaps_half_open( rect_keep ) ) { // Put submaps back for( size_t y = 0; y < MM_REG_SIZE; y++ ) { for( size_t x = 0; x < MM_REG_SIZE; x++ ) { - tripoint p = regp + tripoint( x, y, 0 ); + tripoint p = regp_sm + tripoint( x, y, 0 ); shared_ptr_fast &sm = reg.submaps[x][y]; submaps.insert( std::make_pair( p, sm ) ); } From 740c10a6a7d9319196e4aeffc5372857455f3040 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sun, 6 Dec 2020 19:33:17 +0300 Subject: [PATCH 25/26] Add debug logging for tile memory operations --- src/debug.cpp | 3 +++ src/debug.h | 2 ++ src/map_memory.cpp | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/src/debug.cpp b/src/debug.cpp index d0e9f05f7b08..6019a1254cf0 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -466,6 +466,9 @@ static std::ostream &operator<<( std::ostream &out, DebugClass cl ) if( cl & D_SDL ) { out << "SDL "; } + if( cl & D_MMAP ) { + out << "MMAP "; + } } return out; } diff --git a/src/debug.h b/src/debug.h index 5f29db900570..be79f21412b1 100644 --- a/src/debug.h +++ b/src/debug.h @@ -147,6 +147,8 @@ enum DebugClass { D_NPC = 1 << 5, /** SDL & tiles & anything graphical */ D_SDL = 1 << 6, + /** Related to tile memory (map_memory.cpp) */ + D_MMAP = 1 << 7, DC_ALL = ( 1 << 30 ) - 1 }; diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 9fe027a7f316..e10785289c3f 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -10,6 +10,8 @@ static const int default_symbol = 0; #define MM_SIZE (MAPSIZE * 2) +#define dbg(x) DebugLog((x),D_MMAP) << __FILE__ << ":" << __LINE__ << ": " + static std::string find_legacy_mm_file() { return g->get_player_base_save_path() + ".mm"; @@ -122,6 +124,8 @@ bool map_memory::prepare_region( const tripoint &p1, const tripoint &p2 ) } } + dbg( D_INFO ) << "Preparing memory map for area: pos: " << sm_pos << " size: " << sm_size; + cache_pos = sm_pos; cache_size = sm_size; cached.clear(); @@ -154,6 +158,8 @@ shared_ptr_fast map_memory::allocate_submap( const tripoint &sm_pos ) shared_ptr_fast ret; tripoint reg = reg_coord_pair( sm_pos ).reg; + dbg( D_INFO ) << "Allocated mm_region " << reg << " [" << mmr_to_sm_copy( reg ) << "]"; + for( size_t y = 0; y < MM_REG_SIZE; y++ ) { for( size_t x = 0; x < MM_REG_SIZE; x++ ) { tripoint pos = mmr_to_sm_copy( reg ) + tripoint( x, y, 0 ); @@ -209,6 +215,8 @@ shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) return nullptr; } + dbg( D_INFO ) << "Loaded mm_region " << p.reg << " [" << mmr_to_sm_copy( p.reg ) << "]"; + shared_ptr_fast ret; for( size_t y = 0; y < MM_REG_SIZE; y++ ) { @@ -270,11 +278,14 @@ void map_memory::load( const tripoint &pos ) coord_pair p( pos ); tripoint start = p.sm - tripoint( MM_SIZE / 2, MM_SIZE / 2, 0 ); + dbg( D_INFO ) << "[LOAD] Loading memory map around " << p.sm << ". Loading submaps within " << start + << "->" << start + tripoint( MM_SIZE, MM_SIZE, 0 ); for( int dy = 0; dy < MM_SIZE; dy++ ) { for( int dx = 0; dx < MM_SIZE; dx++ ) { fetch_submap( start + tripoint( dx, dy, 0 ) ); } } + dbg( D_INFO ) << "[LOAD] Done."; } bool map_memory::save( const tripoint &pos ) @@ -285,6 +296,8 @@ bool map_memory::save( const tripoint &pos ) clear_cache(); + dbg( D_INFO ) << "N submaps before save: " << submaps.size(); + // Since mm_submaps are always allocated in regions, // we are certain that each region will be filled. std::map regions; @@ -297,6 +310,9 @@ bool map_memory::save( const tripoint &pos ) constexpr point MM_HSIZE_P = point( MM_SIZE / 2, MM_SIZE / 2 ); rectangle rect_keep( sm_center.xy() - MM_HSIZE_P, sm_center.xy() + MM_HSIZE_P ); + dbg( D_INFO ) << "[SAVE] Saving memory map around " << sm_center << ". Keeping submaps within " << + rect_keep.p_min << "->" << rect_keep.p_max; + bool result = true; for( auto &it : regions ) { @@ -322,6 +338,7 @@ bool map_memory::save( const tripoint &pos ) point regp_sm = mmr_to_sm_copy( regp ).xy(); rectangle rect_reg( regp_sm, regp_sm + point( MM_REG_SIZE, MM_REG_SIZE ) ); if( rect_reg.overlaps_half_open( rect_keep ) ) { + dbg( D_INFO ) << "Keeping mm_region " << regp << " [" << mmr_to_sm_copy( regp ) << "]\n"; // Put submaps back for( size_t y = 0; y < MM_REG_SIZE; y++ ) { for( size_t x = 0; x < MM_REG_SIZE; x++ ) { @@ -330,9 +347,14 @@ bool map_memory::save( const tripoint &pos ) submaps.insert( std::make_pair( p, sm ) ); } } + } else { + dbg( D_INFO ) << "Dropping mm_region " << regp << " [" << mmr_to_sm_copy( regp ) << "]\n"; } } + dbg( D_INFO ) << "[SAVE] Done."; + dbg( D_INFO ) << "N submaps after save: " << submaps.size(); + return result; } From c7ce984f204a5b0c81e7b39645930ec973ac4cc1 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Sun, 6 Dec 2020 23:47:22 +0300 Subject: [PATCH 26/26] Purge stray newlines --- src/map_memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map_memory.cpp b/src/map_memory.cpp index e10785289c3f..967595d60864 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -338,7 +338,7 @@ bool map_memory::save( const tripoint &pos ) point regp_sm = mmr_to_sm_copy( regp ).xy(); rectangle rect_reg( regp_sm, regp_sm + point( MM_REG_SIZE, MM_REG_SIZE ) ); if( rect_reg.overlaps_half_open( rect_keep ) ) { - dbg( D_INFO ) << "Keeping mm_region " << regp << " [" << mmr_to_sm_copy( regp ) << "]\n"; + dbg( D_INFO ) << "Keeping mm_region " << regp << " [" << mmr_to_sm_copy( regp ) << "]"; // Put submaps back for( size_t y = 0; y < MM_REG_SIZE; y++ ) { for( size_t x = 0; x < MM_REG_SIZE; x++ ) { @@ -348,7 +348,7 @@ bool map_memory::save( const tripoint &pos ) } } } else { - dbg( D_INFO ) << "Dropping mm_region " << regp << " [" << mmr_to_sm_copy( regp ) << "]\n"; + dbg( D_INFO ) << "Dropping mm_region " << regp << " [" << mmr_to_sm_copy( regp ) << "]"; } }