diff --git a/src/animation.cpp b/src/animation.cpp index 6a9850c5b64b1..c547918ebf1f1 100644 --- a/src/animation.cpp +++ b/src/animation.cpp @@ -775,3 +775,139 @@ void game::draw_zones( const tripoint &start, const tripoint &end, const tripoin draw_zones_curses( w_terrain, start, end, offset ); } #endif + +#if defined(TILES) +void game::draw_radiation_override( const tripoint &p, const int rad ) +{ + if( use_tiles ) { + tilecontext->init_draw_radiation_override( p, rad ); + } +} +#else +void game::draw_radiation_override( const tripoint &, const int ) +{ +} +#endif + +#if defined(TILES) +void game::draw_terrain_override( const tripoint &p, const ter_id &id ) +{ + if( use_tiles ) { + tilecontext->init_draw_terrain_override( p, id ); + } +} +#else +void game::draw_terrain_override( const tripoint &, const ter_id & ) +{ +} +#endif + +#if defined(TILES) +void game::draw_furniture_override( const tripoint &p, const furn_id &id ) +{ + if( use_tiles ) { + tilecontext->init_draw_furniture_override( p, id ); + } +} +#else +void game::draw_furniture_override( const tripoint &, const furn_id & ) +{ +} +#endif + +#if defined(TILES) +void game::draw_graffiti_override( const tripoint &p, const bool has ) +{ + if( use_tiles ) { + tilecontext->init_draw_graffiti_override( p, has ); + } +} +#else +void game::draw_graffiti_override( const tripoint &, const bool ) +{ +} +#endif + +#if defined(TILES) +void game::draw_trap_override( const tripoint &p, const trap_id &id ) +{ + if( use_tiles ) { + tilecontext->init_draw_trap_override( p, id ); + } +} +#else +void game::draw_trap_override( const tripoint &, const trap_id & ) +{ +} +#endif + +#if defined(TILES) +void game::draw_field_override( const tripoint &p, const field_type_id &id ) +{ + if( use_tiles ) { + tilecontext->init_draw_field_override( p, id ); + } +} +#else +void game::draw_field_override( const tripoint &, const field_type_id & ) +{ +} +#endif + +#if defined(TILES) +void game::draw_item_override( const tripoint &p, const itype_id &id, const mtype_id &mid, + const bool hilite ) +{ + if( use_tiles ) { + tilecontext->init_draw_item_override( p, id, mid, hilite ); + } +} +#else +void game::draw_item_override( const tripoint &, const itype_id &, const mtype_id &, + const bool ) +{ +} +#endif + +#if defined(TILES) +void game::draw_vpart_override( const tripoint &p, const vpart_id &id, const int part_mod, + const int veh_dir, const bool hilite, const point &mount ) +{ + if( use_tiles ) { + tilecontext->init_draw_vpart_override( p, id, part_mod, veh_dir, hilite, mount ); + } +} +#else +void game::draw_vpart_override( const tripoint &, const vpart_id &, const int, + const int, const bool, const point & ) +{ +} +#endif + +#if defined(TILES) +void game::draw_below_override( const tripoint &p, const bool draw ) +{ + if( use_tiles ) { + tilecontext->init_draw_below_override( p, draw ); + } +} +#else +void game::draw_below_override( const tripoint &, const bool ) +{ +} +#endif + +#if defined(TILES) +void game::draw_monster_override( const tripoint &p, const mtype_id &id, const int count, + const bool more, const Creature::Attitude att ) +{ + if( use_tiles ) { + tilecontext->init_draw_monster_override( p, id, count, more, att ); + } +} +#else +void game::draw_monster_override( const tripoint &, const mtype_id &, const int, + const bool, const Creature::Attitude ) +{ +} +#endif diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index f60bc6745b826..0e4f236556d68 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -53,7 +53,6 @@ #include "calendar.h" #include "character.h" #include "color.h" -#include "creature.h" #include "cursesdef.h" #include "int_id.h" #include "map_memory.h" @@ -978,10 +977,26 @@ void tileset_loader::load_tile_spritelists( JsonObject &entry, struct tile_render_info { const tripoint pos; int height_3d = 0; // accumulator for 3d tallness of sprites rendered here so far - tile_render_info( const tripoint &pos, int height_3d ) : pos( pos ), height_3d( height_3d ) { + bool invisible[5]; + tile_render_info( const tripoint &pos, const int height_3d, const bool ( &invisible )[5] ) + : pos( pos ), height_3d( height_3d ) { + std::copy( invisible, invisible + 5, this->invisible ); } }; +static int divide_round_down( int a, int b ) +{ + if( b < 0 ) { + a = -a; + b = -b; + } + if( a >= 0 ) { + return a / b; + } else { + return -( ( -a + b - 1 ) / b ); + } +} + void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int height, std::multimap &overlay_strings, color_block_overlay_container &color_blocks ) @@ -1007,8 +1022,6 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int render_fill_rect( renderer, clipRect, 0, 0, 0 ); } - const point pos = center.xy(); - int sx = 0; int sy = 0; get_window_tile_counts( width, height, sx, sy ); @@ -1019,7 +1032,7 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int const bool iso_mode = tile_iso; - o = iso_mode ? pos : pos - point( POSX, POSY ); + o = iso_mode ? center.xy() : center.xy() - point( POSX, POSY ); op = dest; // Rounding up to include incomplete tiles at the bottom/right edges @@ -1037,11 +1050,7 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int const int max_visible_x = ( g->u.posx() % SEEX ) + ( MAPSIZE - 1 ) * SEEX; const int max_visible_y = ( g->u.posy() % SEEY ) + ( MAPSIZE - 1 ) * SEEY; - tripoint temp; - temp.z = center.z; - int &x = temp.x; - int &y = temp.y; - auto &ch = g->m.access_cache( temp.z ); + const auto &ch = g->m.access_cache( center.z ); //set up a default tile for the edges outside the render area visibility_type offscreen_type = VIS_DARK; @@ -1064,33 +1073,45 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int } } + static const point neighborhood[4] = { + point_south, point_east, point_west, point_north + }; for( int row = min_row; row < max_row; row ++ ) { std::vector draw_points; draw_points.reserve( max_col ); for( int col = min_col; col < max_col; col ++ ) { + int temp_x, temp_y; if( iso_mode ) { //in isometric, rows and columns represent a checkerboard screen space, and we place //the appropriate tile in valid squares by getting position relative to the screen center. - if( modulo( row + o.y, 2 ) != modulo( col + o.x, 2 ) ) { + if( modulo( row - sy / 2, 2 ) != modulo( col - sx / 2, 2 ) ) { continue; } - x = ( col - row - sx / 2 + sy / 2 ) / 2 + o.x; - y = ( row + col - sy / 2 - sx / 2 ) / 2 + o.y; + temp_x = divide_round_down( col - row - sx / 2 + sy / 2, 2 ) + o.x; + temp_y = divide_round_down( row + col - sy / 2 - sx / 2, 2 ) + o.y; } else { - x = col + o.x; - y = row + o.y; + temp_x = col + o.x; + temp_y = row + o.y; } + const tripoint pos( temp_x, temp_y, center.z ); + const int &x = pos.x; + const int &y = pos.y; + + bool invisible[5]; // invisible to normal eyes + invisible[0] = false; + if( y < min_visible_y || y > max_visible_y || x < min_visible_x || x > max_visible_x ) { - int height_3d = 0; - if( !draw_terrain_from_memory( tripoint( x, y, center.z ), height_3d ) ) { - apply_vision_effects( temp, offscreen_type ); + if( has_draw_override( pos ) || has_memory_at( pos ) ) { + invisible[0] = true; + } else { + apply_vision_effects( pos, offscreen_type ); + continue; } - continue; } // Add scent value to the overlay_strings list for every visible tile when displaying scent - if( g->displaying_scent ) { - const int scent_value = g->scent.get( {x, y, center.z} ); + if( g->displaying_scent && !invisible[0] ) { + const int scent_value = g->scent.get( pos ); if( scent_value > 0 ) { overlay_strings.emplace( player_to_screen( point( x, y ) ) + point( tile_width / 2, 0 ), formatted_text( std::to_string( scent_value ), 8 + catacurses::yellow, @@ -1099,20 +1120,24 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int } if( g->displaying_radiation ) { - const int rad_value = g->m.get_radiation( {x, y, center.z} ); - catacurses::base_color col; - if( rad_value > 0 ) { - col = catacurses::green; - } else { - col = catacurses::cyan; + const auto rad_override = radiation_override.find( pos ); + const bool rad_overridden = rad_override != radiation_override.end(); + if( rad_overridden || !invisible[0] ) { + const int rad_value = rad_overridden ? rad_override->second : g->m.get_radiation( pos ); + catacurses::base_color col; + if( rad_value > 0 ) { + col = catacurses::green; + } else { + col = catacurses::cyan; + } + overlay_strings.emplace( player_to_screen( point( x, y ) ) + point( tile_width / 2, 0 ), + formatted_text( std::to_string( rad_value ), 8 + col, NORTH ) ); } - overlay_strings.emplace( player_to_screen( point( x, y ) ) + point( tile_width / 2, 0 ), - formatted_text( std::to_string( rad_value ), 8 + col, NORTH ) ); } // Add temperature value to the overlay_strings list for every visible tile when displaying temperature - if( g->displaying_temperature ) { - int temp_value = g->weather.get_temperature( {x, y, center.z} ); + if( g->displaying_temperature && !invisible[0] ) { + int temp_value = g->weather.get_temperature( pos ); int ctemp = temp_to_celsius( temp_value ); short color; const short bold = 8; @@ -1140,8 +1165,9 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int NORTH ) ); } - if( g->displaying_visibility && ( g->displaying_visibility_creature != nullptr ) ) { - const bool visibility = g->displaying_visibility_creature->sees( { x, y, center.z } ); + if( g->displaying_visibility && g->displaying_visibility_creature && + !invisible[0] ) { + const bool visibility = g->displaying_visibility_creature->sees( pos ); // color overlay. auto block_color = visibility ? windowsPalette[catacurses::green] : SDL_Color{ 192, 192, 192, 255 }; @@ -1156,27 +1182,31 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int formatted_text( visibility_str, catacurses::black, NORTH ) ); } - if( apply_vision_effects( temp, g->m.get_visibility( ch.visibility_cache[x][y], cache ) ) ) { - int height_3d = 0; - draw_terrain_from_memory( tripoint( x, y, center.z ), height_3d ); - const auto critter = g->critter_at( tripoint( x, y, center.z ), true ); - if( critter != nullptr && g->u.sees_with_infrared( *critter ) ) { - // TODO: defer drawing this until later when we know how tall - // the terrain/furniture under the creature is. - draw_from_id_string( "infrared_creature", C_NONE, empty_string, temp, 0, 0, LL_LIT, false ); + if( !invisible[0] && + apply_vision_effects( pos, g->m.get_visibility( ch.visibility_cache[x][y], cache ) ) ) { + + const Creature *critter = g->critter_at( pos, true ); + if( has_draw_override( pos ) || has_memory_at( pos ) || + ( critter && g->u.sees_with_infrared( *critter ) ) ) { + + invisible[0] = true; + } else { + continue; } - continue; + } + for( int i = 0; i < 4; i++ ) { + const tripoint np = pos + neighborhood[i]; + invisible[1 + i] = np.y < min_visible_y || np.y > max_visible_y || + np.x < min_visible_x || np.x > max_visible_x || + would_apply_vision_effects( g->m.get_visibility( ch.visibility_cache[np.x][np.y], cache ) ); } int height_3d = 0; // light level is now used for choosing between grayscale filter and normal lit tiles. - // Draw Terrain if possible. If not possible then we need to continue on to the next part of loop - if( !draw_terrain( tripoint( x, y, center.z ), ch.visibility_cache[x][y], height_3d ) ) { - continue; - } + draw_terrain( pos, ch.visibility_cache[x][y], height_3d, invisible ); - draw_points.push_back( tile_render_info( tripoint( x, y, center.z ), height_3d ) ); + draw_points.emplace_back( pos, height_3d, invisible ); } const std::array drawing_layers = {{ &cata_tiles::draw_furniture, &cata_tiles::draw_graffiti, &cata_tiles::draw_trap, @@ -1190,46 +1220,87 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int for( auto f : drawing_layers ) { // ... draw all the points we drew terrain for, in the same order for( auto &p : draw_points ) { - ( this->*f )( p.pos, ch.visibility_cache[p.pos.x][p.pos.y], p.height_3d ); + ( this->*f )( p.pos, ch.visibility_cache[p.pos.x][p.pos.y], p.height_3d, p.invisible ); + } + } + // display number of monsters to spawn in mapgen preview + for( const auto &p : draw_points ) { + const auto mon_override = monster_override.find( p.pos ); + if( mon_override != monster_override.end() ) { + const int count = std::get<1>( mon_override->second ); + const bool more = std::get<2>( mon_override->second ); + if( count > 1 || more ) { + std::string text = "x" + std::to_string( count ); + if( more ) { + text += "+"; + } + overlay_strings.emplace( player_to_screen( p.pos.xy() ) + point( tile_width / 2, 0 ), + formatted_text( text, catacurses::red, NORTH ) ); + } } } } + // tile overrides are already drawn in the previous code + void_radiation_override(); + void_terrain_override(); + void_furniture_override(); + void_graffiti_override(); + void_trap_override(); + void_field_override(); + void_item_override(); + void_vpart_override(); + void_draw_below_override(); + void_monster_override(); //Memorize everything the character just saw even if it wasn't displayed. - for( int mem_y = 0; mem_y < MAPSIZE_Y; mem_y++ ) { - for( int mem_x = 0; mem_x < MAPSIZE_X; mem_x++ ) { - tripoint p( mem_x, mem_y, center.z ); - //just finished o through s+o so skip them - rectangle skip_region( o, o + point( sx, sy ) ); - if( skip_region.contains_half_open( p.xy() ) ) { - continue; - } - int height_3d = 0; + for( int mem_y = min_visible_y; mem_y <= max_visible_y; mem_y++ ) { + for( int mem_x = min_visible_x; mem_x <= max_visible_x; mem_x++ ) { + rectangle already_drawn( point( min_col, min_row ), point( max_col, max_row ) ); if( iso_mode ) { - //Iso_mode skips in a checkerboard - if( modulo( mem_y, 2 ) != modulo( mem_x, 2 ) ) { + // calculate the screen position according to the drawing code above (division rounded down): + + // mem_x = ( col - row - sx / 2 + sy / 2 ) / 2 + o.x; + // mem_y = ( row + col - sy / 2 - sx / 2 ) / 2 + o.y; + // ( col - sx / 2 ) % 2 = ( row - sy / 2 ) % 2 + // || + // \/ + const int col = mem_y + mem_x + sx / 2 - o.y - o.x; + const int row = mem_y - mem_x + sy / 2 - o.y + o.x; + if( already_drawn.contains_half_open( point( col, row ) ) ) { continue; } - //in isometric, rows and columns represent a checkerboard screen space, and we place - //the appropriate tile in valid squares by getting position relative to the screen center. - //The MAPSIZE_X/2 offset is to keep the rectangle in the upper right quadrant. - p.x = ( mem_x - mem_y - MAPSIZE_X / 2 + MAPSIZE_Y / 2 ) / 2 + MAPSIZE_X / 2; - p.y = ( mem_y + mem_x - MAPSIZE_Y / 2 - MAPSIZE_X / 2 ) / 2 + MAPSIZE_Y / 2; - //Check if we're in previously done iso_mode space - if( p.x >= ( 0 - sy - sx / 2 + sy / 2 ) / 2 + o.x && p.x < ( sx - 0 - sx / 2 + sy / 2 ) / 2 + o.x && - p.y >= ( 0 + 0 - sy / 2 - sx / 2 ) / 2 + o.y && p.y < ( sy + sx - sy / 2 - sx / 2 ) / 2 + o.y ) { + } else { + // calculate the screen position according to the drawing code above: + + // mem_x = col + o.x + // mem_y = row + o.y + // || + // \/ + // col = mem_x - o.x + // row = mem_y - o.y + if( already_drawn.contains_half_open( point( mem_x, mem_y ) - o ) ) { continue; } } + const tripoint p( mem_x, mem_y, center.z ); lit_level lighting = ch.visibility_cache[p.x][p.y]; if( apply_vision_effects( p, g->m.get_visibility( lighting, cache ) ) ) { continue; } + int height_3d = 0; + bool invisible[5]; + invisible[0] = false; + for( int i = 0; i < 4; i++ ) { + const tripoint np = p + neighborhood[i]; + invisible[1 + i] = np.y < min_visible_y || np.y > max_visible_y || + np.x < min_visible_x || np.x > max_visible_x || + would_apply_vision_effects( g->m.get_visibility( ch.visibility_cache[np.x][np.y], cache ) ); + } //calling draw to memorize everything. - draw_terrain( p, lighting, height_3d ); - draw_furniture( p, lighting, height_3d ); - draw_trap( p, lighting, height_3d ); - draw_vpart( p, lighting, height_3d ); + draw_terrain( p, lighting, height_3d, invisible ); + draw_furniture( p, lighting, height_3d, invisible ); + draw_trap( p, lighting, height_3d, invisible ); + draw_vpart( p, lighting, height_3d, invisible ); } } @@ -1661,9 +1732,19 @@ bool cata_tiles::draw_from_id_string( std::string id, TILE_CATEGORY category, // TODO: also use some vehicle id, for less predictability { // new scope for variable declarations - const optional_vpart_position vp = g->m.veh_at( pos ); - if( vp ) { - seed = vp->mount().x + vp->mount().y * 65536; + const auto vp_override = vpart_override.find( pos ); + const bool vp_overridden = vp_override != vpart_override.end(); + if( vp_overridden ) { + const vpart_id &vp_id = std::get<0>( vp_override->second ); + if( vp_id ) { + const point &mount = std::get<4>( vp_override->second ); + seed = mount.x + mount.y * 65536; + } + } else { + const optional_vpart_position vp = g->m.veh_at( pos ); + if( vp ) { + seed = vp->mount().x + vp->mount().y * 65536; + } } //convert vehicle 360-degree direction (0=E,45=SE, etc) to 4-way tile rotation (0=N,1=W,etc) @@ -1683,7 +1764,9 @@ bool cata_tiles::draw_from_id_string( std::string id, TILE_CATEGORY category, break; case C_MONSTER: // FIXME: add persistent id to Creature type, instead of using monster pointer address - seed = reinterpret_cast( g->critter_at( pos ) ); + if( monster_override.find( pos ) == monster_override.end() ) { + seed = reinterpret_cast( g->critter_at( pos ) ); + } break; default: // player @@ -1878,9 +1961,17 @@ bool cata_tiles::draw_tile_at( return true; } +bool cata_tiles::would_apply_vision_effects( const visibility_type visibility ) const +{ + return visibility != VIS_CLEAR; +} + bool cata_tiles::apply_vision_effects( const tripoint &pos, const visibility_type visibility ) { + if( !would_apply_vision_effects( visibility ) ) { + return false; + } std::string light_name; switch( visibility ) { case VIS_HIDDEN: @@ -1898,8 +1989,9 @@ bool cata_tiles::apply_vision_effects( const tripoint &pos, case VIS_DARK: light_name = "lighting_lowlight_dark"; break; - case VIS_CLEAR: // Handled by the caller. - return false; + case VIS_CLEAR: + // should never happen + break; } // lighting is never rotated, though, could possibly add in random rotation? @@ -1908,9 +2000,13 @@ bool cata_tiles::apply_vision_effects( const tripoint &pos, return true; } -bool cata_tiles::draw_terrain_below( const tripoint &p, lit_level /*ll*/, int &/*height_3d*/ ) +bool cata_tiles::draw_terrain_below( const tripoint &p, const lit_level, int &, + const bool ( &invisible )[5] ) { - if( !g->m.need_draw_lower_floor( p ) ) { + const auto low_override = draw_below_override.find( p ); + const bool low_overridden = low_override != draw_below_override.end(); + if( low_overridden ? !low_override->second : ( invisible[0] || + !g->m.need_draw_lower_floor( p ) ) ) { return false; } @@ -1974,238 +2070,493 @@ bool cata_tiles::draw_terrain_below( const tripoint &p, lit_level /*ll*/, int &/ return true; } -bool cata_tiles::draw_terrain( const tripoint &p, lit_level ll, int &height_3d ) +bool cata_tiles::draw_terrain( const tripoint &p, const lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) { - const ter_id t = g->m.ter( p ); // get the ter_id value at this point - // check for null, if null return false - if( t == t_null ) { - return false; + const auto override = terrain_override.find( p ); + const bool overridden = override != terrain_override.end(); + bool neighborhood_overridden = overridden; + if( !neighborhood_overridden ) { + for( const point &dir : { + point_south, point_east, point_west, point_north + } ) { + if( terrain_override.find( p + dir ) != terrain_override.end() ) { + neighborhood_overridden = true; + break; + } + } + } + // first memorize the actual terrain + const ter_id &t = g->m.ter( p ); + if( t && !invisible[0] ) { + int subtile = 0; + int rotation = 0; + int connect_group = 0; + if( t.obj().connects( connect_group ) ) { + get_connect_values( p, subtile, rotation, connect_group, {} ); + } else { + get_terrain_orientation( p, rotation, subtile, {}, invisible ); + // do something to get other terrain orientation values + } + const std::string &tname = t.id().str(); + if( !g->m.check_and_set_seen_cache( p ) ) { + g->u.memorize_tile( g->m.getabs( p ), tname, subtile, rotation ); + } + // draw the actual terrain if there's no override + if( !neighborhood_overridden ) { + return draw_from_id_string( tname, C_TERRAIN, empty_string, p, subtile, rotation, ll, + nv_goggles_activated, height_3d ); + } + } + if( invisible[0] ? overridden : neighborhood_overridden ) { + // and then draw the override terrain + const ter_id &t2 = overridden ? override->second : t; + if( t2 ) { + // both the current and neighbouring overrides may change the appearance + // of the tile, so always re-calculate it. + int subtile = 0; + int rotation = 0; + int connect_group = 0; + if( t2.obj().connects( connect_group ) ) { + get_connect_values( p, subtile, rotation, connect_group, terrain_override ); + } else { + get_terrain_orientation( p, rotation, subtile, terrain_override, invisible ); + } + const std::string &tname = t2.id().str(); + // tile overrides are never memorized + // tile overrides are always shown with full visibility + const lit_level lit = overridden ? LL_LIT : ll; + const bool nv = overridden ? false : nv_goggles_activated; + return draw_from_id_string( tname, C_TERRAIN, empty_string, p, subtile, rotation, lit, nv, + height_3d ); + } + } else if( invisible[0] && has_terrain_memory_at( p ) ) { + // try drawing memory if invisible and not overridden + const auto &t = get_terrain_memory_at( p ); + return draw_from_id_string( t.tile, C_TERRAIN, empty_string, p, t.subtile, t.rotation, + LL_MEMORIZED, nv_goggles_activated, height_3d ); } + return false; +} - //char alteration = 0; - int subtile = 0; - int rotation = 0; - int connect_group; - if( g->m.ter( p ).obj().connects( connect_group ) ) { - get_connect_values( p, subtile, rotation, connect_group ); - } else { - get_terrain_orientation( p, rotation, subtile ); - // do something to get other terrain orientation values +bool cata_tiles::has_memory_at( const tripoint &p ) const +{ + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + return !t.tile.empty(); } + return false; +} - const std::string &tname = t.obj().id.str(); - if( !g->m.check_and_set_seen_cache( p ) ) { - g->u.memorize_tile( g->m.getabs( p ), tname, subtile, rotation ); +bool cata_tiles::has_terrain_memory_at( const tripoint &p ) const +{ + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 2 ) == "t_" ) { + return true; + } } - - return draw_from_id_string( tname, C_TERRAIN, empty_string, p, subtile, rotation, ll, - nv_goggles_activated, height_3d ); + return false; } -bool cata_tiles::draw_terrain_from_memory( const tripoint &p, int &height_3d ) +bool cata_tiles::has_furniture_memory_at( const tripoint &p ) const { - if( !g->u.should_show_map_memory() ) { - return false; - } - const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); - if( t.tile.empty() ) { - return false; + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 2 ) == "f_" ) { + return true; + } } + return false; +} - TILE_CATEGORY category = C_NONE; - - switch( t.tile[0] ) { - case 't': - category = C_TERRAIN; - break; - case 'f': - category = C_FURNITURE; - break; - case 'v': - category = C_VEHICLE_PART; - break; - default: - debugmsg( "Could not infer category for memorized tile %s", t.tile ); - break; +bool cata_tiles::has_trap_memory_at( const tripoint &p ) const +{ + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 3 ) == "tr_" ) { + return true; + } } - - return draw_from_id_string( t.tile, category, empty_string, p, t.subtile, t.rotation, - LL_MEMORIZED, nv_goggles_activated, height_3d ); + return false; } -bool cata_tiles::draw_furniture( const tripoint &p, lit_level ll, int &height_3d ) +bool cata_tiles::has_vpart_memory_at( const tripoint &p ) const { - // get furniture ID at x,y - bool has_furn = g->m.has_furn( p ); - if( !has_furn ) { - return false; + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 3 ) == "vp_" ) { + return true; + } } + return false; +} - const furn_id f_id = g->m.furn( p ); - - // for rotation information - const int neighborhood[4] = { - static_cast( g->m.furn( p + point_south ) ), - static_cast( g->m.furn( p + point_east ) ), - static_cast( g->m.furn( p + point_west ) ), - static_cast( g->m.furn( p + point_north ) ) - }; - - int subtile = 0; - int rotation = 0; - get_tile_values( f_id, neighborhood, subtile, rotation ); - - // get the name of this furniture piece - const std::string &f_name = f_id.obj().id.str(); - if( !g->m.check_and_set_seen_cache( p ) ) { - g->u.memorize_tile( g->m.getabs( p ), f_name, subtile, rotation ); +memorized_terrain_tile cata_tiles::get_terrain_memory_at( const tripoint &p ) const +{ + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 2 ) == "t_" ) { + return t; + } } + return {}; +} - bool ret = draw_from_id_string( f_name, C_FURNITURE, empty_string, p, subtile, rotation, ll, - nv_goggles_activated, height_3d ); - return ret; +memorized_terrain_tile cata_tiles::get_furniture_memory_at( const tripoint &p ) const +{ + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 2 ) == "f_" ) { + return t; + } + } + return {}; } -bool cata_tiles::draw_trap( const tripoint &p, lit_level ll, int &height_3d ) +memorized_terrain_tile cata_tiles::get_trap_memory_at( const tripoint &p ) const { - const trap &tr = g->m.tr_at( p ); - if( !tr.can_see( p, g->u ) ) { - return false; + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 3 ) == "tr_" ) { + return t; + } } + return {}; +} - const int neighborhood[4] = { - static_cast( g->m.tr_at( p + point_south ).loadid ), - static_cast( g->m.tr_at( p + point_east ).loadid ), - static_cast( g->m.tr_at( p + point_west ).loadid ), - static_cast( g->m.tr_at( p + point_north ).loadid ) - }; +memorized_terrain_tile cata_tiles::get_vpart_memory_at( const tripoint &p ) const +{ + if( g->u.should_show_map_memory() ) { + const memorized_terrain_tile t = g->u.get_memorized_tile( g->m.getabs( p ) ); + if( t.tile.substr( 0, 3 ) == "vp_" ) { + return t; + } + } + return {}; +} - int subtile = 0; - int rotation = 0; - get_tile_values( tr.loadid, neighborhood, subtile, rotation ); +bool cata_tiles::draw_furniture( const tripoint &p, const lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) +{ + const auto override = furniture_override.find( p ); + const bool overridden = override != furniture_override.end(); + bool neighborhood_overridden = overridden; + if( !neighborhood_overridden ) { + for( const point &dir : { + point_south, point_east, point_west, point_north + } ) { + if( furniture_override.find( p + dir ) != furniture_override.end() ) { + neighborhood_overridden = true; + break; + } + } + } + // first memorize the actual furniture + const furn_id &f = g->m.furn( p ); + if( f && !invisible[0] ) { + const int neighborhood[4] = { + static_cast( g->m.furn( p + point_south ) ), + static_cast( g->m.furn( p + point_east ) ), + static_cast( g->m.furn( p + point_west ) ), + static_cast( g->m.furn( p + point_north ) ) + }; + int subtile = 0; + int rotation = 0; + get_tile_values( f, neighborhood, subtile, rotation ); + const std::string &fname = f.id().str(); + if( !g->m.check_and_set_seen_cache( p ) ) { + g->u.memorize_tile( g->m.getabs( p ), fname, subtile, rotation ); + } + // draw the actual furniture if there's no override + if( !neighborhood_overridden ) { + return draw_from_id_string( fname, C_FURNITURE, empty_string, p, subtile, rotation, ll, + nv_goggles_activated, height_3d ); + } + } + if( invisible[0] ? overridden : neighborhood_overridden ) { + // and then draw the override furniture + const furn_id &f2 = overridden ? override->second : f; + if( f2 ) { + // both the current and neighbouring overrides may change the appearance + // of the tile, so always re-calculate it. + const auto furn = [&]( const tripoint & q, const bool invis ) -> furn_id { + const auto it = furniture_override.find( q ); + return it != furniture_override.end() ? it->second : + ( !overridden || !invis ) ? g->m.furn( q ) : f_null; + }; + const int neighborhood[4] = { + static_cast( furn( p + point_south, invisible[1] ) ), + static_cast( furn( p + point_east, invisible[2] ) ), + static_cast( furn( p + point_west, invisible[3] ) ), + static_cast( furn( p + point_north, invisible[4] ) ) + }; + int subtile = 0; + int rotation = 0; + get_tile_values( f2, neighborhood, subtile, rotation ); + const std::string &fname = f2.id().str(); + // tile overrides are never memorized + // tile overrides are always shown with full visibility + const lit_level lit = overridden ? LL_LIT : ll; + const bool nv = overridden ? false : nv_goggles_activated; + return draw_from_id_string( fname, C_FURNITURE, empty_string, p, subtile, rotation, lit, nv, + height_3d ); + } + } else if( invisible[0] && has_furniture_memory_at( p ) ) { + // try drawing memory if invisible and not overridden + const auto &t = get_furniture_memory_at( p ); + return draw_from_id_string( t.tile, C_FURNITURE, empty_string, p, t.subtile, t.rotation, + LL_MEMORIZED, nv_goggles_activated, height_3d ); + } + return false; +} - if( !g->m.check_and_set_seen_cache( p ) ) { - g->u.memorize_tile( g->m.getabs( p ), tr.id.str(), subtile, rotation ); +bool cata_tiles::draw_trap( const tripoint &p, const lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) +{ + const auto override = trap_override.find( p ); + const bool overridden = override != trap_override.end(); + bool neighborhood_overridden = overridden; + if( !neighborhood_overridden ) { + for( const point &dir : { + point_south, point_east, point_west, point_north + } ) { + if( trap_override.find( p + dir ) != trap_override.end() ) { + neighborhood_overridden = true; + break; + } + } } - return draw_from_id_string( tr.id.str(), C_TRAP, empty_string, p, subtile, rotation, ll, - nv_goggles_activated, height_3d ); + // first memorize the actual trap + const trap_id &tr = g->m.tr_at( p ).loadid; + if( tr && !invisible[0] && tr.obj().can_see( p, g->u ) ) { + const int neighborhood[4] = { + static_cast( g->m.tr_at( p + point_south ).loadid ), + static_cast( g->m.tr_at( p + point_east ).loadid ), + static_cast( g->m.tr_at( p + point_west ).loadid ), + static_cast( g->m.tr_at( p + point_north ).loadid ) + }; + int subtile = 0; + int rotation = 0; + get_tile_values( tr, neighborhood, subtile, rotation ); + const std::string trname = tr.id().str(); + if( !g->m.check_and_set_seen_cache( p ) ) { + g->u.memorize_tile( g->m.getabs( p ), trname, subtile, rotation ); + } + // draw the actual trap if there's no override + if( !neighborhood_overridden ) { + return draw_from_id_string( trname, C_TRAP, empty_string, p, subtile, rotation, ll, + nv_goggles_activated, height_3d ); + } + } + if( overridden || ( !invisible[0] && neighborhood_overridden && tr.obj().can_see( p, g->u ) ) ) { + // and then draw the override trap + const trap_id &tr2 = overridden ? override->second : tr; + if( tr2 ) { + // both the current and neighbouring overrides may change the appearance + // of the tile, so always re-calculate it. + const auto tr_at = [&]( const tripoint & q, const bool invis ) -> trap_id { + const auto it = trap_override.find( q ); + return it != trap_override.end() ? it->second : + ( !overridden || !invis ) ? g->m.tr_at( q ).loadid : tr_null; + }; + const int neighborhood[4] = { + static_cast( tr_at( p + point_south, invisible[1] ) ), + static_cast( tr_at( p + point_east, invisible[2] ) ), + static_cast( tr_at( p + point_west, invisible[3] ) ), + static_cast( tr_at( p + point_north, invisible[4] ) ) + }; + int subtile = 0; + int rotation = 0; + get_tile_values( tr2, neighborhood, subtile, rotation ); + const std::string &trname = tr2.id().str(); + // tile overrides are never memorized + // tile overrides are always shown with full visibility + const lit_level lit = overridden ? LL_LIT : ll; + const bool nv = overridden ? false : nv_goggles_activated; + return draw_from_id_string( trname, C_TRAP, empty_string, p, subtile, rotation, lit, nv, + height_3d ); + } + } else if( invisible[0] && has_trap_memory_at( p ) ) { + // try drawing memory if invisible and not overridden + const auto &t = get_trap_memory_at( p ); + return draw_from_id_string( t.tile, C_TRAP, empty_string, p, t.subtile, t.rotation, + LL_MEMORIZED, nv_goggles_activated, height_3d ); + } + return false; } -bool cata_tiles::draw_graffiti( const tripoint &p, lit_level ll, int &height_3d ) +bool cata_tiles::draw_graffiti( const tripoint &p, const lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) { - if( !g->m.has_graffiti_at( p ) ) { + const auto override = graffiti_override.find( p ); + const bool overridden = override != graffiti_override.end(); + if( overridden ? !override->second : ( invisible[0] || !g->m.has_graffiti_at( p ) ) ) { return false; } - - return draw_from_id_string( "graffiti", C_NONE, empty_string, p, 0, 0, ll, false, height_3d ); + const lit_level lit = overridden ? LL_LIT : ll; + return draw_from_id_string( "graffiti", C_NONE, empty_string, p, 0, 0, lit, false, height_3d ); } -bool cata_tiles::draw_field_or_item( const tripoint &p, lit_level ll, int &height_3d ) +bool cata_tiles::draw_field_or_item( const tripoint &p, const lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) { - const field &f = g->m.field_at( p ); - const field_type &fld_type = g->m.field_at( p ).displayed_field_type().obj(); - bool ret_draw_field = true; - bool ret_draw_items = true; - if( fld_type.display_field ) { + const auto fld_override = field_override.find( p ); + const bool fld_overridden = fld_override != field_override.end(); + const field_type_id &fld = fld_overridden ? + fld_override->second : g->m.field_at( p ).displayed_field_type(); + bool ret_draw_field = false; + bool ret_draw_items = false; + if( ( fld_overridden || !invisible[0] ) && fld.obj().display_field ) { + const lit_level lit = fld_overridden ? LL_LIT : ll; + const bool nv = fld_overridden ? false : nv_goggles_activated; + + auto field_at = [&]( const tripoint & q, const bool invis ) -> field_type_id { + const auto it = field_override.find( q ); + return it != field_override.end() ? it->second : + ( !fld_overridden || !invis ) ? g->m.field_at( q ).displayed_field_type() : fd_null; + }; // for rotation information const int neighborhood[4] = { - static_cast( g->m.field_at( p + point_south ).displayed_field_type() ), - static_cast( g->m.field_at( p + point_east ).displayed_field_type() ), - static_cast( g->m.field_at( p + point_west ).displayed_field_type() ), - static_cast( g->m.field_at( p + point_north ).displayed_field_type() ) + static_cast( field_at( p + point_south, invisible[1] ) ), + static_cast( field_at( p + point_east, invisible[2] ) ), + static_cast( field_at( p + point_west, invisible[3] ) ), + static_cast( field_at( p + point_north, invisible[4] ) ) }; int subtile = 0; int rotation = 0; - get_tile_values( f.displayed_field_type(), neighborhood, subtile, rotation ); - - ret_draw_field = draw_from_id_string( fld_type.id.c_str(), C_FIELD, empty_string, p, subtile, - rotation, ll, nv_goggles_activated ); - } - if( fld_type.display_items ) { - if( !g->m.sees_some_items( p, g->u ) ) { - return false; - } - const maptile &cur_maptile = g->m.maptile_at( p ); - // get the last item in the stack, it will be used for display - const item &displayed_item = cur_maptile.get_uppermost_item(); - // get the item's name, as that is the key used to find it in the map - const std::string it_name = displayed_item.is_corpse() ? "corpse_" + - displayed_item.get_mtype()->id.str() : displayed_item.typeId(); - - const std::string it_category = displayed_item.type->get_item_type_string(); - ret_draw_items = draw_from_id_string( it_name, C_ITEM, it_category, p, 0, 0, ll, - nv_goggles_activated, height_3d ); - if( ret_draw_items && cur_maptile.get_item_count() > 1 ) { - draw_item_highlight( p ); + get_tile_values( fld, neighborhood, subtile, rotation ); + + ret_draw_field = draw_from_id_string( fld.id().str(), C_FIELD, empty_string, p, subtile, + rotation, lit, nv ); + } + if( fld.obj().display_items ) { + const auto it_override = item_override.find( p ); + const bool it_overridden = it_override != item_override.end(); + + itype_id it_id; + mtype_id mon_id; + bool hilite; + const itype *it_type; + if( it_overridden ) { + it_id = std::get<0>( it_override->second ); + mon_id = std::get<1>( it_override->second ); + hilite = std::get<2>( it_override->second ); + it_type = item::find_type( it_id ); + } else if( !invisible[0] && g->m.sees_some_items( p, g->u ) ) { + const maptile &tile = g->m.maptile_at( p ); + const item &itm = tile.get_uppermost_item(); + const mtype *const mon = itm.get_mtype(); + it_id = itm.typeId(); + mon_id = mon ? mon->id : mtype_id::NULL_ID(); + hilite = tile.get_item_count() > 1; + it_type = itm.type; + } else { + it_type = nullptr; + } + if( it_type && it_id != "null" ) { + const std::string disp_id = it_id == "corpse" && mon_id ? + "corpse_" + mon_id.str() : it_id; + const std::string it_category = it_type->get_item_type_string(); + const lit_level lit = it_overridden ? LL_LIT : ll; + const bool nv = it_overridden ? false : nv_goggles_activated; + + ret_draw_items = draw_from_id_string( disp_id, C_ITEM, it_category, p, 0, 0, lit, + nv, height_3d ); + if( ret_draw_items && hilite ) { + draw_item_highlight( p ); + } } } return ret_draw_field && ret_draw_items; } -bool cata_tiles::draw_vpart_below( const tripoint &p, lit_level /*ll*/, int &/*height_3d*/ ) +bool cata_tiles::draw_vpart_below( const tripoint &p, const lit_level /*ll*/, int &/*height_3d*/, + const bool ( &invisible )[5] ) { - if( !g->m.need_draw_lower_floor( p ) ) { + const auto low_override = draw_below_override.find( p ); + const bool low_overridden = low_override != draw_below_override.end(); + if( low_overridden ? !low_override->second : ( invisible[0] || + !g->m.need_draw_lower_floor( p ) ) ) { return false; } tripoint pbelow( p.xy(), p.z - 1 ); int height_3d_below = 0; - return draw_vpart( pbelow, LL_LOW, height_3d_below ); + bool below_invisible[5]; + std::fill( below_invisible, below_invisible + 5, false ); + return draw_vpart( pbelow, LL_LOW, height_3d_below, below_invisible ); } -bool cata_tiles::draw_vpart( const tripoint &p, lit_level ll, int &height_3d ) +bool cata_tiles::draw_vpart( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) { + const auto override = vpart_override.find( p ); + const bool overridden = override != vpart_override.end(); + // first memorize the actual vpart const optional_vpart_position vp = g->m.veh_at( p ); - - if( !vp ) { - return false; - } - vehicle *const veh = &vp->vehicle(); - // veh_part is the index of the part - const int veh_part = vp->part_index(); - - // Gets the visible part, should work fine once tileset vp_ids are updated to work with the vehicle part json ids - // get the vpart_id - char part_mod = 0; - const vpart_id &vp_id = veh->part_id_string( veh_part, part_mod ); - - // prefix with vp_ ident - const std::string vpid = "vp_" + vp_id.str(); - int subtile = 0; - if( part_mod > 0 ) { - switch( part_mod ) { - case 1: - subtile = open_; - break; - case 2: - subtile = broken; - break; - } - } - const cata::optional cargopart = vp.part_with_feature( "CARGO", true ); - bool draw_highlight = cargopart && !veh->get_items( cargopart->part_index() ).empty(); - - int veh_dir = veh->face.dir(); - if( !veh->forward_velocity() && !veh->player_in_control( g->u ) ) { - if( !g->m.check_and_set_seen_cache( p ) ) { - g->u.memorize_tile( g->m.getabs( p ), vpid, subtile, veh_dir ); + if( vp && !invisible[0] ) { + const vehicle &veh = vp->vehicle(); + const int veh_part = vp->part_index(); + // Gets the visible part, should work fine once tileset vp_ids are updated to work with the vehicle part json ids + // get the vpart_id + char part_mod = 0; + const vpart_id &vp_id = veh.part_id_string( veh_part, part_mod ); + const int subtile = part_mod == 1 ? open_ : part_mod == 2 ? broken : 0; + const int rotation = veh.face.dir(); + const std::string vpname = "vp_" + vp_id.str(); + if( !veh.forward_velocity() && !veh.player_in_control( g->u ) && + !g->m.check_and_set_seen_cache( p ) ) { + g->u.memorize_tile( g->m.getabs( p ), vpname, subtile, rotation ); + } + if( !overridden ) { + const cata::optional cargopart = vp.part_with_feature( "CARGO", true ); + const bool draw_highlight = cargopart && !veh.get_items( cargopart->part_index() ).empty(); + const bool ret = draw_from_id_string( vpname, C_VEHICLE_PART, empty_string, p, subtile, rotation, + ll, nv_goggles_activated, height_3d ); + if( ret && draw_highlight ) { + draw_item_highlight( p ); + } + return ret; + } + } + if( overridden ) { + // and then draw the override vpart + const vpart_id &vp2 = std::get<0>( override->second ); + if( vp2 ) { + const char part_mod = std::get<1>( override->second ); + const int subtile = part_mod == 1 ? open_ : part_mod == 2 ? broken : 0; + const int rotation = std::get<2>( override->second ); + const int draw_highlight = std::get<3>( override->second ); + const std::string vpname = "vp_" + vp2.str(); + // tile overrides are never memorized + // tile overrides are always shown with full visibility + const bool ret = draw_from_id_string( vpname, C_VEHICLE_PART, empty_string, p, subtile, rotation, + LL_LIT, false, height_3d ); + if( ret && draw_highlight ) { + draw_item_highlight( p ); + } + return ret; } + } else if( invisible[0] && has_vpart_memory_at( p ) ) { + // try drawing memory if invisible and not overridden + const auto &t = get_vpart_memory_at( p ); + return draw_from_id_string( t.tile, C_VEHICLE_PART, empty_string, p, t.subtile, t.rotation, + LL_MEMORIZED, nv_goggles_activated, height_3d ); } - - bool ret = draw_from_id_string( vpid, C_VEHICLE_PART, empty_string, p, subtile, veh_dir, - ll, nv_goggles_activated, height_3d ); - if( ret && draw_highlight ) { - draw_item_highlight( p ); - } - return ret; + return false; } -bool cata_tiles::draw_critter_at_below( const tripoint &p, lit_level, int & ) +bool cata_tiles::draw_critter_at_below( const tripoint &p, const lit_level, int &, + const bool ( &invisible )[5] ) { // Check if we even need to draw below. If not, bail. - if( !g->m.need_draw_lower_floor( p ) ) { + const auto low_override = draw_below_override.find( p ); + const bool low_overridden = low_override != draw_below_override.end(); + if( low_overridden ? !low_override->second : ( invisible[0] || + !g->m.need_draw_lower_floor( p ) ) ) { return false; } @@ -2251,17 +2602,126 @@ bool cata_tiles::draw_critter_at_below( const tripoint &p, lit_level, int & ) return true; } -bool cata_tiles::draw_critter_at( const tripoint &p, lit_level ll, int &height_3d ) +bool cata_tiles::draw_critter_at( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) { - const auto critter = g->critter_at( p, true ); - if( critter != nullptr ) { - return draw_entity( *critter, p, ll, height_3d ); + bool result; + bool is_player; + bool sees_player; + Creature::Attitude attitude; + const auto override = monster_override.find( p ); + if( override != monster_override.end() ) { + const mtype_id id = std::get<0>( override->second ); + if( !id ) { + return false; + } + is_player = false; + sees_player = false; + attitude = std::get<3>( override->second ); + const std::string &chosen_id = id.str(); + const std::string &ent_subcategory = id.obj().species.empty() ? + empty_string : id.obj().species.begin()->str(); + result = draw_from_id_string( chosen_id, C_MONSTER, ent_subcategory, p, corner, 0, LL_LIT, false, + height_3d ); + } else if( !invisible[0] ) { + const Creature *pcritter = g->critter_at( p, true ); + if( pcritter == nullptr ) { + return false; + } + const Creature &critter = *pcritter; + + if( !g->u.sees( critter ) ) { + if( g->u.sees_with_infrared( critter ) ) { + return draw_from_id_string( "infrared_creature", C_NONE, empty_string, p, 0, 0, + LL_LIT, false, height_3d ); + } + return false; + } + result = false; + sees_player = false; + is_player = false; + attitude = Creature::A_ANY; + const monster *m = dynamic_cast( &critter ); + if( m != nullptr ) { + const auto ent_category = C_MONSTER; + std::string ent_subcategory = empty_string; + if( !m->type->species.empty() ) { + ent_subcategory = m->type->species.begin()->str(); + } + const int subtile = corner; + // depending on the toggle flip sprite left or right + int rot_facing = -1; + if( m->facing == FD_RIGHT ) { + rot_facing = 0; + } else if( m->facing == FD_LEFT ) { + rot_facing = 4; + } + if( rot_facing >= 0 ) { + const auto ent_name = m->type->id; + std::string chosen_id = ent_name.str(); + if( m->has_effect( effect_ridden ) ) { + int pl_under_height = 6; + if( m->mounted_player ) { + draw_entity_with_overlays( *m->mounted_player, p, ll, pl_under_height ); + } + const std::string prefix = "rid_"; + std::string copy_id = chosen_id; + const std::string ridden_id = copy_id.insert( 0, prefix ); + const tile_type *tt = tileset_ptr->find_tile_type( ridden_id ); + if( tt ) { + chosen_id = ridden_id; + } + } + result = draw_from_id_string( chosen_id, ent_category, ent_subcategory, p, subtile, rot_facing, + ll, false, height_3d ); + sees_player = m->sees( g->u ); + attitude = m->attitude_to( g-> u ); + } + } + const player *pl = dynamic_cast( &critter ); + if( pl != nullptr ) { + draw_entity_with_overlays( *pl, p, ll, height_3d ); + result = true; + if( pl->is_player() ) { + is_player = true; + } else { + sees_player = pl->sees( g-> u ); + attitude = pl->attitude_to( g-> u ); + } + } + } else { // invisible + const Creature *critter = g->critter_at( p, true ); + if( critter && g->u.sees_with_infrared( *critter ) ) { + // try drawing infrared creature if invisible and not overridden + // return directly without drawing overlay + return draw_from_id_string( "infrared_creature", C_NONE, empty_string, p, 0, 0, LL_LIT, false, + height_3d ); + } else { + return false; + } } - return false; + + if( result && !is_player ) { + std::ostringstream tmp_id; + tmp_id << "overlay_" << Creature::attitude_raw_string( attitude ); + if( sees_player ) { + tmp_id << "_sees_player"; + } + const std::string draw_id = tmp_id.str(); + if( tileset_ptr->find_tile_type( draw_id ) ) { + draw_from_id_string( draw_id, C_NONE, empty_string, p, 0, 0, LL_LIT, false, height_3d ); + } + } + return result; } -bool cata_tiles::draw_zone_mark( const tripoint &p, lit_level ll, int &height_3d ) +bool cata_tiles::draw_zone_mark( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ) { + if( invisible[0] ) { + return false; + } + if( !g->is_zones_manager_open() ) { return false; } @@ -2282,83 +2742,6 @@ bool cata_tiles::draw_zone_mark( const tripoint &p, lit_level ll, int &height_3d return false; } -bool cata_tiles::draw_entity( const Creature &critter, const tripoint &p, lit_level ll, - int &height_3d ) -{ - if( !g->u.sees( critter ) ) { - if( g->u.sees_with_infrared( critter ) ) { - return draw_from_id_string( "infrared_creature", C_NONE, empty_string, p, 0, 0, - LL_LIT, false, height_3d ); - } - return false; - } - bool result = false; - bool sees_player = false; - bool is_player = false; - Creature::Attitude attitude = Creature::A_ANY; - const monster *m = dynamic_cast( &critter ); - if( m != nullptr ) { - const auto ent_category = C_MONSTER; - std::string ent_subcategory = empty_string; - if( !m->type->species.empty() ) { - ent_subcategory = m->type->species.begin()->str(); - } - const int subtile = corner; - // depending on the toggle flip sprite left or right - int rot_facing = -1; - if( m->facing == FD_RIGHT ) { - rot_facing = 0; - } else if( m->facing == FD_LEFT ) { - rot_facing = 4; - } - if( rot_facing >= 0 ) { - const auto ent_name = m->type->id; - std::string chosen_id = ent_name.str(); - if( m->has_effect( effect_ridden ) ) { - int pl_under_height = 6; - if( m->mounted_player ) { - draw_entity_with_overlays( *m->mounted_player, p, ll, pl_under_height ); - } - const std::string prefix = "rid_"; - std::string copy_id = chosen_id; - const std::string ridden_id = copy_id.insert( 0, prefix ); - const tile_type *tt = tileset_ptr->find_tile_type( ridden_id ); - if( tt ) { - chosen_id = ridden_id; - } - } - result = draw_from_id_string( chosen_id, ent_category, ent_subcategory, p, subtile, rot_facing, - ll, false, height_3d ); - sees_player = m->sees( g->u ); - attitude = m->attitude_to( g-> u ); - } - } - const player *pl = dynamic_cast( &critter ); - if( pl != nullptr ) { - draw_entity_with_overlays( *pl, p, ll, height_3d ); - result = true; - if( pl->is_player() ) { - is_player = true; - } else { - sees_player = pl->sees( g-> u ); - attitude = pl->attitude_to( g-> u ); - } - } - - if( result && !is_player ) { - std::ostringstream tmp_id; - tmp_id << "overlay_" << Creature::attitude_raw_string( attitude ); - if( sees_player ) { - tmp_id << "_sees_player"; - } - const std::string draw_id = tmp_id.str(); - if( tileset_ptr->find_tile_type( draw_id ) ) { - draw_from_id_string( draw_id, C_NONE, empty_string, p, 0, 0, LL_LIT, false, height_3d ); - } - } - return result; -} - void cata_tiles::draw_entity_with_overlays( const player &pl, const tripoint &p, lit_level ll, int &height_3d ) { @@ -2481,6 +2864,49 @@ void cata_tiles::init_draw_zones( const tripoint &_start, const tripoint &_end, zone_end = _end; zone_offset = _offset; } +void cata_tiles::init_draw_radiation_override( const tripoint &p, const int rad ) +{ + radiation_override.emplace( p, rad ); +} +void cata_tiles::init_draw_terrain_override( const tripoint &p, const ter_id &id ) +{ + terrain_override.emplace( p, id ); +} +void cata_tiles::init_draw_furniture_override( const tripoint &p, const furn_id &id ) +{ + furniture_override.emplace( p, id ); +} +void cata_tiles::init_draw_graffiti_override( const tripoint &p, const bool has ) +{ + graffiti_override.emplace( p, has ); +} +void cata_tiles::init_draw_trap_override( const tripoint &p, const trap_id &id ) +{ + trap_override.emplace( p, id ); +} +void cata_tiles::init_draw_field_override( const tripoint &p, const field_type_id &id ) +{ + field_override.emplace( p, id ); +} +void cata_tiles::init_draw_item_override( const tripoint &p, const itype_id &id, + const mtype_id &mid, const bool hilite ) +{ + item_override.emplace( p, std::make_tuple( id, mid, hilite ) ); +} +void cata_tiles::init_draw_vpart_override( const tripoint &p, const vpart_id &id, + const int part_mod, const int veh_dir, const bool hilite, const point &mount ) +{ + vpart_override.emplace( p, std::make_tuple( id, part_mod, veh_dir, hilite, mount ) ); +} +void cata_tiles::init_draw_below_override( const tripoint &p, const bool draw ) +{ + draw_below_override.emplace( p, draw ); +} +void cata_tiles::init_draw_monster_override( const tripoint &p, const mtype_id &id, const int count, + const bool more, const Creature::Attitude att ) +{ + monster_override.emplace( p, std::make_tuple( id, count, more, att ) ); +} /* -- Void Animators */ void cata_tiles::void_explosion() { @@ -2537,6 +2963,59 @@ void cata_tiles::void_zones() { do_draw_zones = false; } +void cata_tiles::void_radiation_override() +{ + radiation_override.clear(); +} +void cata_tiles::void_terrain_override() +{ + terrain_override.clear(); +} +void cata_tiles::void_furniture_override() +{ + furniture_override.clear(); +} +void cata_tiles::void_graffiti_override() +{ + graffiti_override.clear(); +} +void cata_tiles::void_trap_override() +{ + trap_override.clear(); +} +void cata_tiles::void_field_override() +{ + field_override.clear(); +} +void cata_tiles::void_item_override() +{ + item_override.clear(); +} +void cata_tiles::void_vpart_override() +{ + vpart_override.clear(); +} +void cata_tiles::void_draw_below_override() +{ + draw_below_override.clear(); +} +void cata_tiles::void_monster_override() +{ + monster_override.clear(); +} +bool cata_tiles::has_draw_override( const tripoint &p ) const +{ + return radiation_override.find( p ) != radiation_override.end() || + terrain_override.find( p ) != terrain_override.end() || + furniture_override.find( p ) != furniture_override.end() || + graffiti_override.find( p ) != graffiti_override.end() || + trap_override.find( p ) != trap_override.end() || + field_override.find( p ) != field_override.end() || + item_override.find( p ) != item_override.end() || + vpart_override.find( p ) != vpart_override.end() || + draw_below_override.find( p ) != draw_below_override.end() || + monster_override.find( p ) != monster_override.end(); +} /* -- Animation Renders */ void cata_tiles::draw_explosion_frame() { @@ -2768,10 +3247,18 @@ void cata_tiles::init_light() g->reset_light_level(); } -void cata_tiles::get_terrain_orientation( const tripoint &p, int &rota, int &subtile ) +void cata_tiles::get_terrain_orientation( const tripoint &p, int &rota, int &subtile, + const std::map &ter_override, const bool ( &invisible )[5] ) { + const bool overridden = ter_override.find( p ) != ter_override.end(); + const auto ter = [&]( const tripoint & q, const bool invis ) -> ter_id { + const auto override = ter_override.find( q ); + return override != ter_override.end() ? override->second : + ( !overridden || !invis ) ? g->m.ter( q ) : t_null; + }; + // get terrain at x,y - ter_id tid = g->m.ter( p ); + const ter_id tid = ter( p, invisible[0] ); if( tid == t_null ) { subtile = 0; rota = 0; @@ -2780,20 +3267,17 @@ void cata_tiles::get_terrain_orientation( const tripoint &p, int &rota, int &sub // get terrain neighborhood const ter_id neighborhood[4] = { - g->m.ter( p + point_south ), - g->m.ter( p + point_east ), - g->m.ter( p + point_west ), - g->m.ter( p + point_north ) + ter( p + point_south, invisible[1] ), + ter( p + point_east, invisible[2] ), + ter( p + point_west, invisible[3] ), + ter( p + point_north, invisible[4] ) }; - bool connects[4]; char val = 0; // populate connection information for( int i = 0; i < 4; ++i ) { - connects[i] = ( neighborhood[i] == tid ); - - if( connects[i] ) { + if( neighborhood[i] == tid ) { val += 1 << i; } } @@ -2878,9 +3362,9 @@ void cata_tiles::get_rotation_and_subtile( const char val, int &rotation, int &s } void cata_tiles::get_connect_values( const tripoint &p, int &subtile, int &rotation, - int connect_group ) + const int connect_group, const std::map &ter_override ) { - uint8_t connections = g->m.get_known_connections( p, connect_group ); + uint8_t connections = g->m.get_known_connections( p, connect_group, ter_override ); get_rotation_and_subtile( connections, rotation, subtile ); } diff --git a/src/cata_tiles.h b/src/cata_tiles.h index 9e3ee035e503a..de08172077338 100644 --- a/src/cata_tiles.h +++ b/src/cata_tiles.h @@ -12,8 +12,10 @@ #include "sdl_wrappers.h" #include "animation.h" +#include "creature.h" #include "lightmap.h" #include "line.h" +#include "map_memory.h" #include "options.h" #include "weather.h" #include "enums.h" @@ -25,6 +27,8 @@ class player; class pixel_minimap; class JsonObject; +using itype_id = std::string; + extern void set_displaybuffer_rendertarget(); /** Structures */ @@ -310,25 +314,49 @@ class cata_tiles /* Tile Picking */ void get_tile_values( int t, const int *tn, int &subtile, int &rotation ); - void get_connect_values( const tripoint &p, int &subtile, int &rotation, int connect_group ); - void get_terrain_orientation( const tripoint &p, int &rota, int &subtile ); + void get_connect_values( const tripoint &p, int &subtile, int &rotation, int connect_group, + const std::map &ter_override ); + void get_terrain_orientation( const tripoint &p, int &rota, int &subtile, + const std::map &ter_override, + const bool ( &invisible )[5] ); void get_rotation_and_subtile( char val, int &rota, int &subtile ); + /** Map memory */ + bool has_memory_at( const tripoint &p ) const; + bool has_terrain_memory_at( const tripoint &p ) const; + bool has_furniture_memory_at( const tripoint &p ) const; + bool has_trap_memory_at( const tripoint &p ) const; + bool has_vpart_memory_at( const tripoint &p ) const; + memorized_terrain_tile get_terrain_memory_at( const tripoint &p ) const; + memorized_terrain_tile get_furniture_memory_at( const tripoint &p ) const; + memorized_terrain_tile get_trap_memory_at( const tripoint &p ) const; + memorized_terrain_tile get_vpart_memory_at( const tripoint &p ) const; + /** Drawing Layers */ + bool would_apply_vision_effects( visibility_type visibility ) const; bool apply_vision_effects( const tripoint &pos, visibility_type visibility ); - bool draw_terrain( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_terrain_from_memory( const tripoint &p, int &height_3d ); - bool draw_terrain_below( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_furniture( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_graffiti( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_trap( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_field_or_item( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_vpart( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_vpart_below( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_critter_at( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_critter_at_below( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_zone_mark( const tripoint &p, lit_level ll, int &height_3d ); - bool draw_entity( const Creature &critter, const tripoint &p, lit_level ll, int &height_3d ); + bool draw_terrain( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_terrain_below( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_furniture( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_graffiti( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_trap( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_field_or_item( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_vpart( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_vpart_below( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_critter_at( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_critter_at_below( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); + bool draw_zone_mark( const tripoint &p, lit_level ll, int &height_3d, + const bool ( &invisible )[5] ); void draw_entity_with_overlays( const player &pl, const tripoint &p, lit_level ll, int &height_3d ); bool draw_item_highlight( const tripoint &pos ); @@ -379,6 +407,40 @@ class cata_tiles void draw_zones_frame(); void void_zones(); + void init_draw_radiation_override( const tripoint &p, int rad ); + void void_radiation_override(); + + void init_draw_terrain_override( const tripoint &p, const ter_id &id ); + void void_terrain_override(); + + void init_draw_furniture_override( const tripoint &p, const furn_id &id ); + void void_furniture_override(); + + void init_draw_graffiti_override( const tripoint &p, bool has ); + void void_graffiti_override(); + + void init_draw_trap_override( const tripoint &p, const trap_id &id ); + void void_trap_override(); + + void init_draw_field_override( const tripoint &p, const field_type_id &id ); + void void_field_override(); + + void init_draw_item_override( const tripoint &p, const itype_id &id, const mtype_id &mid, + bool hilite ); + void void_item_override(); + + void init_draw_vpart_override( const tripoint &p, const vpart_id &id, int part_mod, + int veh_dir, bool hilite, const point &mount ); + void void_vpart_override(); + + void init_draw_below_override( const tripoint &p, bool draw ); + void void_draw_below_override(); + + void init_draw_monster_override( const tripoint &p, const mtype_id &id, int count, + bool more, Creature::Attitude att ); + void void_monster_override(); + + bool has_draw_override( const tripoint &p ) const; public: /** * Initialize the current tileset (load tile images, load mapping), using the current @@ -486,6 +548,21 @@ class cata_tiles // offset for drawing, in pixels. point op; + std::map radiation_override; + std::map terrain_override; + std::map furniture_override; + std::map graffiti_override; + std::map trap_override; + std::map field_override; + // bool represents item highlight + std::map> item_override; + // int, int, bool represents part_mod, veh_dir, and highlight respectively + // point represents the mount direction + std::map> vpart_override; + std::map draw_below_override; + // int represents spawn count + std::map> monster_override; + private: /** * Tracks active night vision goggle status for each draw call. diff --git a/src/coordinate_conversions.cpp b/src/coordinate_conversions.cpp index 2d772d5d41c74..1d7b624b85162 100644 --- a/src/coordinate_conversions.cpp +++ b/src/coordinate_conversions.cpp @@ -87,7 +87,7 @@ point sm_to_om_remain( int &x, int &y ) point omt_to_ms_copy( const point &p ) { - return point( p.x * 2 * SEEX, p.x * 2 * SEEY ); + return point( p.x * 2 * SEEX, p.y * 2 * SEEY ); } point omt_to_sm_copy( int x, int y ) diff --git a/src/editmap.cpp b/src/editmap.cpp index 1701b40b0ac04..f596ae33a2c71 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -311,6 +312,7 @@ cata::optional editmap::edit() uberdraw = uistate.editmap_nsa_viewmode; infoHeight = 20; + blink = true; w_info = catacurses::newwin( infoHeight, width, point( offsetX, TERMY - infoHeight ) ); do { @@ -361,13 +363,10 @@ cata::optional editmap::edit() target_list.push_back( target ); } else if( move_target( action, 1 ) ) { recalc_target( editshape ); // target_list must follow movement - if( target_list.size() > 1 ) { - blink = true; // display entire list if it's more than just target point - } - } else { - blink = !blink; } + blink = action == "TIMEOUT" ? !blink : true; } while( action != "QUIT" ); + blink = false; uistate.editmap_nsa_viewmode = uberdraw; @@ -453,36 +452,74 @@ void editmap::update_view_with_help( const std::string &txt, const std::string & } else { g->m.drawsq( g->w_terrain, g->u, target, true, true, target ); } - g->draw_cursor( target ); - - // hilight target_list points if blink=true (and if it's more than a point ) - if( blink && target_list.size() > 1 ) { - for( auto &elem : target_list ) { - const tripoint &p = elem; - // but only if there's no vehicles/mobs/npcs on a point - if( !g->m.veh_at( p ) && !g->critter_at( p ) ) { - const ter_t &terrain = g->m.ter( p ).obj(); - char t_sym = terrain.symbol(); - nc_color t_col = terrain.color(); - - if( g->m.has_furn( p ) ) { - const furn_t &furniture_type = g->m.furn( p ).obj(); - t_sym = furniture_type.symbol(); - t_col = furniture_type.color(); +#ifdef TILES + // give some visual indication of different cursor moving modes + if( use_tiles && altblink ) { + point p[2] = { origin.xy(), target.xy() }; + if( editshape == editmap_rect || editshape == editmap_rect_filled || p[0] == p[1] ) { + if( p[0] == p[1] ) { + // ensure more than one cursor is drawn to differ from resizing mode + p[0] += point_north_west; + p[1] += point_south_east; + } + for( const auto &pi : p ) { + for( const auto &pj : p ) { + g->draw_cursor( tripoint( pi.x, pj.y, target.z ) ); } - const field &t_field = g->m.field_at( p ); - if( t_field.field_count() > 0 ) { - field_type_id t_ftype = t_field.displayed_field_type(); - const field_entry *t_fld = t_field.find_field( t_ftype ); - if( t_fld != nullptr ) { - t_col = t_fld->color(); - t_sym = t_fld->symbol()[0]; + } + } else if( editshape == editmap_circle ) { + g->draw_cursor( target ); + g->draw_cursor( origin * 2 - target ); + } else if( editshape == editmap_line ) { + g->draw_cursor( origin ); + g->draw_cursor( target ); + } + } else { +#endif + g->draw_cursor( target ); +#ifdef TILES + } +#endif + + // hilight target_list points if blink=true + if( blink ) { + for( const auto &p : target_list ) { +#ifdef TILES + if( use_tiles ) { + if( draw_target_override ) { + draw_target_override.value()( p ); + } else { + g->draw_highlight( p ); + } + } else { +#endif + // but only if there's no vehicles/mobs/npcs on a point + if( !g->m.veh_at( p ) && !g->critter_at( p ) ) { + const ter_t &terrain = g->m.ter( p ).obj(); + char t_sym = terrain.symbol(); + nc_color t_col = terrain.color(); + + if( g->m.has_furn( p ) ) { + const furn_t &furniture_type = g->m.furn( p ).obj(); + t_sym = furniture_type.symbol(); + t_col = furniture_type.color(); } + const field &t_field = g->m.field_at( p ); + if( t_field.field_count() > 0 ) { + field_type_id t_ftype = t_field.displayed_field_type(); + const field_entry *t_fld = t_field.find_field( t_ftype ); + if( t_fld != nullptr ) { + t_col = t_fld->color(); + t_sym = t_fld->symbol()[0]; + } + } + t_col = altblink ? green_background( t_col ) : cyan_background( t_col ); + tripoint scrpos = pos2screen( p ); + mvwputch( g->w_terrain, scrpos.xy(), t_col, t_sym ); } - t_col = altblink ? green_background( t_col ) : cyan_background( t_col ); - tripoint scrpos = pos2screen( p ); - mvwputch( g->w_terrain, scrpos.xy(), t_col, t_sym ); +#ifdef TILES } +#endif } } @@ -778,6 +815,27 @@ std::string describe( const trap &type ) type.is_benign() ? _( "Yes" ) : _( "No" ) ); } +template +static void draw_override( const tripoint &p, const T_id &id ); + +template<> +void draw_override( const tripoint &p, const ter_id &id ) +{ + g->draw_terrain_override( p, id ); +} + +template<> +void draw_override( const tripoint &p, const furn_id &id ) +{ + g->draw_furniture_override( p, id ); +} + +template<> +void draw_override( const tripoint &p, const trap_id &id ) +{ + g->draw_trap_override( p, id ); +} + template static void apply( const T_t &t, shapetype editshape, const tripoint &target, const tripoint &origin, const std::vector &target_list ); @@ -894,8 +952,17 @@ void editmap::edit_feature() int current_feature = emenu.selected = feature( target ).to_i(); emenu.entries[current_feature].text_color = c_green; + blink = true; bool quit = false; do { + const T_id override( emenu.selected ); + if( override ) { + draw_target_override = [override]( const tripoint & p ) { + draw_override( p, override ); + }; + } else { + draw_target_override = cata::nullopt; + } input_context ctxt( emenu.input_category ); update_view_with_help( string_format( pgettext( "keybinding descriptions", "%s, %s, %s, %s, %s" ), ctxt.describe_key_and_name( "CONFIRM" ), @@ -904,7 +971,7 @@ void editmap::edit_feature() ctxt.describe_key_and_name( "EDITMAP_TAB" ), ctxt.describe_key_and_name( "EDITMAP_MOVE" ) ), info_title() ); - emenu.query(); + emenu.query( false, BLINK_SPEED ); if( emenu.ret == UILIST_CANCEL ) { quit = true; } else if( ( emenu.ret >= 0 && static_cast( emenu.ret ) < T_t::count() ) || @@ -929,7 +996,10 @@ void editmap::edit_feature() uberdraw = !uberdraw; } } + blink = emenu.ret == UILIST_TIMEOUT ? !blink : true; } while( !quit ); + blink = false; + draw_target_override = cata::nullopt; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -985,7 +1055,16 @@ void editmap::edit_fld() }; fmenu.allow_additional = true; + blink = true; do { + const field_type_id override( fmenu.selected ); + if( override ) { + draw_target_override = [override]( const tripoint & p ) { + g->draw_field_override( p, override ); + }; + } else { + draw_target_override = cata::nullopt; + } input_context ctxt( fmenu.input_category ); // \u00A0 is the non-breaking space update_view_with_help( string_format( pgettext( "keybinding descriptions", @@ -997,7 +1076,7 @@ void editmap::edit_fld() ctxt.describe_key_and_name( "QUIT" ), ctxt.describe_key_and_name( "EDITMAP_SHOW_ALL" ) ), pgettext( "Map editor: Editing field effects", "Field effects" ) ); - fmenu.query(); + fmenu.query( false, BLINK_SPEED ); if( ( fmenu.ret > 0 && static_cast( fmenu.ret ) < field_type::count() ) || ( fmenu.ret == UILIST_ADDITIONAL && ( fmenu.ret_act == "LEFT" || fmenu.ret_act == "RIGHT" ) ) ) { @@ -1095,7 +1174,10 @@ void editmap::edit_fld() uberdraw = !uberdraw; } } + blink = fmenu.ret == UILIST_TIMEOUT ? !blink : true; } while( fmenu.ret != UILIST_CANCEL ); + blink = false; + draw_target_override = cata::nullopt; } /* @@ -1336,6 +1418,8 @@ int editmap::select_shape( shapetype shape, int mode ) { tripoint orig = target; tripoint origor = origin; + shapetype origshape = editshape; + editshape = shape; input_context ctxt( "EDITMAP_SHAPE" ); ctxt.set_iso( true ); ctxt.register_directions(); @@ -1396,7 +1480,7 @@ int editmap::select_shape( shapetype shape, int mode ) smenu.addentry( editmap_line, true, 'l', pgettext( "shape", "Line" ) ); smenu.addentry( editmap_circle, true, 'c', pgettext( "shape", "Filled Circle" ) ); smenu.addentry( -2, true, 'p', pgettext( "shape", "Point" ) ); - smenu.selected = static_cast( shape ); + smenu.selected = static_cast( editshape ); smenu.additional_actions = { { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings }; @@ -1407,7 +1491,7 @@ int editmap::select_shape( shapetype shape, int mode ) if( smenu.ret == UILIST_CANCEL ) { // canceled } else if( smenu.ret != -2 ) { - shape = static_cast( smenu.ret ); + editshape = static_cast( smenu.ret ); update = true; } else if( smenu.ret != UILIST_ADDITIONAL ) { target_list.clear(); @@ -1441,29 +1525,23 @@ int editmap::select_shape( shapetype shape, int mode ) } } else if( move_target( action ) ) { update = true; - } else { - blink = !blink; } if( update ) { - blink = true; update = false; - recalc_target( shape ); + recalc_target( editshape ); } + blink = action == "TIMEOUT" ? !blink : true; altblink = moveall; } while( action != "CONFIRM" && action != "QUIT" ); - blink = true; + blink = false; altblink = false; - if( action == "CONFIRM" ) { - editshape = shape; - return target_list.size(); - } else { - target_list.clear(); + if( action != "CONFIRM" ) { target = orig; origin = origor; - target_list.push_back( target ); - blink = false; - return -1; + editshape = origshape; + recalc_target( editshape ); } + return target_list.size(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1523,9 +1601,77 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) omt_ref = oter_id( gmenu.selected ); cleartmpmap( tmpmap ); tmpmap.generate( tripoint( omt_pos.x * 2, omt_pos.y * 2, target.z ), calendar::turn ); - showpreview = true; } input_context ctxt( gpmenu.input_category ); +#ifdef TILES + if( use_tiles && showpreview ) { + const point origin_p = target.xy() + point( 1 - SEEX, 1 - SEEY ); + for( int x = 0; x < SEEX * 2; x++ ) { + for( int y = 0; y < SEEY * 2; y++ ) { + const tripoint tmp_p( x, y, target.z ); + const tripoint map_p = origin_p + tmp_p; + g->draw_radiation_override( map_p, tmpmap.get_radiation( tmp_p ) ); + // scent is managed in `game` instead of `map`, so there's no override for it + // temperature is managed in `game` instead of `map`, so there's no override for it + // TODO: visibility could be affected by both the actual map and the preview map, + // which complicates calculation, so there's no override for it (yet) + g->draw_terrain_override( map_p, tmpmap.ter( tmp_p ) ); + g->draw_furniture_override( map_p, tmpmap.furn( tmp_p ) ); + g->draw_graffiti_override( map_p, tmpmap.has_graffiti_at( tmp_p ) ); + g->draw_trap_override( map_p, tmpmap.tr_at( tmp_p ).loadid ); + g->draw_field_override( map_p, tmpmap.field_at( tmp_p ).displayed_field_type() ); + const maptile &tile = tmpmap.maptile_at( tmp_p ); + if( tmpmap.sees_some_items( tmp_p, g->u.pos() - origin_p ) ) { + const item &itm = tile.get_uppermost_item(); + const mtype *const mon = itm.get_mtype(); + g->draw_item_override( map_p, itm.typeId(), mon ? mon->id : mtype_id::NULL_ID(), + tile.get_item_count() > 1 ); + } else { + g->draw_item_override( map_p, "null", mtype_id::NULL_ID(), false ); + } + const optional_vpart_position vp = tmpmap.veh_at( tmp_p ); + if( vp ) { + const vehicle &veh = vp->vehicle(); + const int veh_part = vp->part_index(); + char part_mod = 0; + const vpart_id &vp_id = veh.part_id_string( veh_part, part_mod ); + const cata::optional cargopart = vp.part_with_feature( "CARGO", true ); + bool draw_highlight = cargopart && !veh.get_items( cargopart->part_index() ).empty(); + int veh_dir = veh.face.dir(); + g->draw_vpart_override( map_p, vp_id, part_mod, veh_dir, draw_highlight, vp->mount() ); + } else { + g->draw_vpart_override( map_p, vpart_id::NULL_ID(), 0, 0, false, point_zero ); + } + g->draw_below_override( map_p, g->m.has_zlevels() && + tmpmap.ter( tmp_p ).obj().has_flag( TFLAG_NO_FLOOR ) ); + } + } + // int: count, bool: more than 1 spawn data + std::map> spawns; + for( int x = 0; x < 2; x++ ) { + for( int y = 0; y < 2; y++ ) { + submap *sm = tmpmap.get_submap_at_grid( { x, y, target.z } ); + if( sm ) { + const tripoint sm_origin = origin_p + tripoint( x * SEEX, y * SEEY, target.z ); + for( const auto &sp : sm->spawns ) { + const tripoint spawn_p = sm_origin + sp.pos; + const auto spawn_it = spawns.find( spawn_p ); + if( spawn_it == spawns.end() ) { + const Creature::Attitude att = sp.friendly ? Creature::A_FRIENDLY : Creature::A_ANY; + spawns.emplace( spawn_p, std::make_tuple( sp.type, sp.count, false, att ) ); + } else { + std::get<2>( spawn_it->second ) = true; + } + } + } + } + } + for( const auto &it : spawns ) { + g->draw_monster_override( it.first, std::get<0>( it.second ), std::get<1>( it.second ), + std::get<2>( it.second ), std::get<3>( it.second ) ); + } + } +#endif // \u00A0 is the non-breaking space update_view_with_help( string_format( pgettext( "keybinding descriptions", "[%s,%s]\u00A0prev/next oter type, [%s,%s]\u00A0select, %s, %s" ), @@ -1535,7 +1681,12 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) ctxt.describe_key_and_name( "QUIT" ) ), string_format( pgettext( "map editor state", "Mapgen: %s" ), oter_id( gmenu.selected ).id().str() ) ); - if( showpreview ) { +#ifdef TILES + const bool drawsq_preview = !use_tiles && showpreview; +#else + const bool drawsq_preview = showpreview; +#endif + if( drawsq_preview ) { hilights["mapgentgt"].draw( *this, true ); g->draw_panels(); tmpmap.reset_vehicle_cache( target.z ); @@ -1548,60 +1699,60 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) wrefresh( w_preview ); } gmenu.show(); - gpmenu.query( true, BLINK_SPEED * 3 ); - - if( gpmenu.ret == UILIST_TIMEOUT ) { - showpreview = !showpreview; - } else if( gpmenu.ret != UILIST_ADDITIONAL ) { - if( gpmenu.ret == 0 ) { - cleartmpmap( tmpmap ); - tmpmap.generate( tripoint( omt_pos.x * 2, omt_pos.y * 2, target.z ), calendar::turn ); - showpreview = true; - } else if( gpmenu.ret == 1 ) { - tmpmap.rotate( 1 ); - showpreview = true; - } else if( gpmenu.ret == 2 ) { - - point target_sub( target.x / SEEX, target.y / SEEY ); - - g->m.set_transparency_cache_dirty( target.z ); - g->m.set_outside_cache_dirty( target.z ); - g->m.set_floor_cache_dirty( target.z ); - g->m.set_pathfinding_cache_dirty( target.z ); - - g->m.clear_vehicle_cache( target.z ); - g->m.clear_vehicle_list( target.z ); - - for( int x = 0; x < 2; x++ ) { - for( int y = 0; y < 2; y++ ) { - // Apply previewed mapgen to map. Since this is a function for testing, we try avoid triggering - // functions that would alter the results - const auto dest_pos = target_sub + tripoint( x, y, target.z ); - const auto src_pos = tripoint{ x, y, target.z }; - - submap *destsm = g->m.get_submap_at_grid( dest_pos ); - submap *srcsm = tmpmap.get_submap_at_grid( src_pos ); - - std::swap( *destsm, *srcsm ); - - for( auto &veh : destsm->vehicles ) { - veh->sm_pos = dest_pos; - } + gpmenu.query( false, BLINK_SPEED * 3 ); - g->m.update_vehicle_list( destsm, target.z ); // update real map's vcaches + if( gpmenu.ret == 0 ) { + cleartmpmap( tmpmap ); + tmpmap.generate( tripoint( omt_pos.x * 2, omt_pos.y * 2, target.z ), calendar::turn ); + } else if( gpmenu.ret == 1 ) { + tmpmap.rotate( 1 ); + } else if( gpmenu.ret == 2 ) { + const point target_sub( target.x / SEEX, target.y / SEEY ); - if( !destsm->spawns.empty() ) { // trigger spawnpoints - g->m.spawn_monsters( true ); - } + g->m.set_transparency_cache_dirty( target.z ); + g->m.set_outside_cache_dirty( target.z ); + g->m.set_floor_cache_dirty( target.z ); + g->m.set_pathfinding_cache_dirty( target.z ); + + g->m.clear_vehicle_cache( target.z ); + g->m.clear_vehicle_list( target.z ); + + for( int x = 0; x < 2; x++ ) { + for( int y = 0; y < 2; y++ ) { + // Apply previewed mapgen to map. Since this is a function for testing, we try avoid triggering + // functions that would alter the results + const auto dest_pos = target_sub + tripoint( x, y, target.z ); + const auto src_pos = tripoint{ x, y, target.z }; + + submap *destsm = g->m.get_submap_at_grid( dest_pos ); + submap *srcsm = tmpmap.get_submap_at_grid( src_pos ); + + std::swap( *destsm, *srcsm ); + + for( auto &veh : destsm->vehicles ) { + veh->sm_pos = dest_pos; + } + + if( !destsm->spawns.empty() ) { // trigger spawnpoints + g->m.spawn_monsters( true ); } } - g->m.reset_vehicle_cache( target.z ); + } - } else if( gpmenu.ret == 3 ) { - popup( _( "Changed oter_id from '%s' (%s) to '%s' (%s)" ), - orig_oters->get_name(), orig_oters.id().str(), - omt_ref->get_name(), omt_ref.id().str() ); + // Since we cleared the vehicle cache of the whole z-level (not just the generate map), we add it back here + for( int x = 0; x < g->m.getmapsize(); x++ ) { + for( int y = 0; y < g->m.getmapsize(); y++ ) { + const tripoint dest_pos = tripoint( x, y, target.z ); + const submap *destsm = g->m.get_submap_at_grid( dest_pos ); + g->m.update_vehicle_list( destsm, target.z ); // update real map's vcaches + } } + + g->m.reset_vehicle_cache( target.z ); + } else if( gpmenu.ret == 3 ) { + popup( _( "Changed oter_id from '%s' (%s) to '%s' (%s)" ), + orig_oters->get_name(), orig_oters.id().str(), + omt_ref->get_name(), omt_ref.id().str() ); } else if( gpmenu.ret == UILIST_ADDITIONAL ) { if( gpmenu.ret_act == "LEFT" ) { gmenu.scrollby( -1 ); @@ -1613,6 +1764,7 @@ void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) gmenu.refresh(); } } + showpreview = gpmenu.ret == UILIST_TIMEOUT ? !showpreview : true; } while( gpmenu.ret != 2 && gpmenu.ret != 3 && gpmenu.ret != UILIST_CANCEL ); if( gpmenu.ret != 2 && // we didn't apply, so restore the original om_ter @@ -1698,13 +1850,13 @@ void editmap::mapgen_retarget() std::string action; tripoint origm = target; + blink = true; do { update_view_with_help( string_format( pgettext( "keybinding descriptions", "%s, %s" ), ctxt.describe_key_and_name( "CONFIRM" ), ctxt.describe_key_and_name( "QUIT" ) ), pgettext( "map generator", "Mapgen: Moving target" ) ); action = ctxt.handle_input( BLINK_SPEED ); - blink = !blink; if( const cata::optional vec = ctxt.get_direction( action ) ) { point vec_ms = omt_to_ms_copy( vec->xy() ); tripoint ptarget = target + vec_ms; @@ -1713,21 +1865,22 @@ void editmap::mapgen_retarget() target = ptarget; target_list.clear(); - for( int x = target.x - SEEX - 1; x < target.x + SEEX + 1; x++ ) { - for( int y = target.y - SEEY - 1; y < target.y + SEEY + 1; y++ ) { - target_list.push_back( tripoint( x, y, target.z ) ); + for( int x = target.x - SEEX + 1; x < target.x + SEEX + 1; x++ ) { + for( int y = target.y - SEEY + 1; y < target.y + SEEY + 1; y++ ) { + if( x == target.x - SEEX + 1 || x == target.x + SEEX || + y == target.y - SEEY + 1 || y == target.y + SEEY ) { + target_list.push_back( tripoint( x, y, target.z ) ); + } } } - blink = true; } - } else { - blink = !blink; } + blink = action == "TIMEOUT" ? !blink : true; } while( action != "QUIT" && action != "CONFIRM" ); if( action != "CONFIRM" ) { target = origm; } - blink = true; + blink = false; } /* @@ -1785,14 +1938,17 @@ void editmap::edit_mapgen() pgettext( "map generator", "Mapgen stamp" ) ); gmenu.query(); - if( gmenu.ret > 0 ) { + if( gmenu.ret >= 0 ) { + blink = false; mapgen_preview( tc, gmenu ); + blink = true; } else if( gmenu.ret == UILIST_ADDITIONAL ) { if( gmenu.ret_act == "EDITMAP_MOVE" ) { mapgen_retarget(); } } } while( gmenu.ret != UILIST_CANCEL ); + blink = false; } /* diff --git a/src/editmap.h b/src/editmap.h index 34441801a97c3..e681085055c5c 100644 --- a/src/editmap.h +++ b/src/editmap.h @@ -2,6 +2,7 @@ #ifndef EDITMAP_H #define EDITMAP_H +#include #include #include #include @@ -86,6 +87,7 @@ class editmap shapetype editshape; std::vector target_list; + cata::optional> draw_target_override; std::map hilights; bool blink; bool altblink; diff --git a/src/game.h b/src/game.h index 32fdb64157219..864041cdbc4d4 100644 --- a/src/game.h +++ b/src/game.h @@ -627,6 +627,18 @@ class game // Draw a highlight graphic at p, for example when examining something. // TILES only, in curses this does nothing void draw_highlight( const tripoint &p ); + void draw_radiation_override( const tripoint &p, int rad ); + void draw_terrain_override( const tripoint &p, const ter_id &id ); + void draw_furniture_override( const tripoint &p, const furn_id &id ); + void draw_graffiti_override( const tripoint &p, bool has ); + void draw_trap_override( const tripoint &p, const trap_id &id ); + void draw_field_override( const tripoint &p, const field_type_id &id ); + void draw_item_override( const tripoint &p, const itype_id &id, const mtype_id &mid, bool hilite ); + void draw_vpart_override( const tripoint &p, const vpart_id &id, int part_mod, int veh_dir, + bool hilite, const point &mount ); + void draw_below_override( const tripoint &p, bool draw ); + void draw_monster_override( const tripoint &p, const mtype_id &id, int count, + bool more, Creature::Attitude att ); bool is_in_viewport( const tripoint &p, int margin = 0 ) const; /** diff --git a/src/map.cpp b/src/map.cpp index d8244d86ab656..792886756d031 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -284,7 +284,7 @@ void map::clear_vehicle_list( const int zlev ) ch.zone_vehicles.clear(); } -void map::update_vehicle_list( submap *const to, const int zlev ) +void map::update_vehicle_list( const submap *const to, const int zlev ) { // Update vehicle data auto &ch = get_cache( zlev ); @@ -1407,15 +1407,14 @@ ter_id map::ter( const tripoint &p ) const return current_submap->get_ter( l ); } -uint8_t map::get_known_connections( const tripoint &p, int connect_group ) const +uint8_t map::get_known_connections( const tripoint &p, int connect_group, + const std::map &override ) const { constexpr std::array offsets = {{ point_south, point_east, point_west, point_north } }; auto &ch = access_cache( p.z ); - bool is_transparent = - ch.transparency_cache[p.x][p.y] > LIGHT_TRANSPARENCY_SOLID; uint8_t val = 0; std::function is_memorized; #ifdef TILES @@ -1434,16 +1433,26 @@ uint8_t map::get_known_connections( const tripoint &p, int connect_group ) const } #endif + const bool overridden = override.find( p ) != override.end(); + const bool is_transparent = ch.transparency_cache[p.x][p.y] > LIGHT_TRANSPARENCY_SOLID; + // populate connection information for( int i = 0; i < 4; ++i ) { tripoint neighbour = p + offsets[i]; if( !inbounds( neighbour ) ) { continue; } - if( is_transparent || - ch.visibility_cache[neighbour.x][neighbour.y] <= LL_BRIGHT || - is_memorized( neighbour ) ) { - const ter_t &neighbour_terrain = ter( neighbour ).obj(); + const auto neighbour_override = override.find( neighbour ); + const bool neighbour_overridden = neighbour_override != override.end(); + // if there's some non-memory terrain to show at the neighboring tile + const bool may_connect = neighbour_overridden || + get_visibility( ch.visibility_cache[neighbour.x][neighbour.y], + get_visibility_variables_cache() ) == VIS_CLEAR || + // or if an actual center tile is transparent or next to a memorized tile + ( !overridden && ( is_transparent || is_memorized( neighbour ) ) ); + if( may_connect ) { + const ter_t &neighbour_terrain = neighbour_overridden ? + neighbour_override->second.obj() : ter( neighbour ).obj(); if( neighbour_terrain.connects_to( connect_group ) ) { val += 1 << i; } @@ -4733,10 +4742,20 @@ void map::process_items_in_vehicle( vehicle &cur_veh, submap ¤t_submap, co bool map::sees_some_items( const tripoint &p, const Creature &who ) const { // Can only see items if there are any items. - return has_items( p ) && could_see_items( p, who ); + return has_items( p ) && could_see_items( p, who.pos() ); +} + +bool map::sees_some_items( const tripoint &p, const tripoint &from ) const +{ + return has_items( p ) && could_see_items( p, from ); } bool map::could_see_items( const tripoint &p, const Creature &who ) const +{ + return could_see_items( p, who.pos() ); +} + +bool map::could_see_items( const tripoint &p, const tripoint &from ) const { static const std::string container_string( "CONTAINER" ); const bool container = has_flag_ter_or_furn( container_string, p ); @@ -4748,9 +4767,9 @@ bool map::could_see_items( const tripoint &p, const Creature &who ) const if( container ) { // can see inside of containers if adjacent or // on top of the container - return ( abs( p.x - who.posx() ) <= 1 && - abs( p.y - who.posy() ) <= 1 && - abs( p.z - who.posz() ) <= 1 ); + return ( abs( p.x - from.x ) <= 1 && + abs( p.y - from.y ) <= 1 && + abs( p.z - from.z ) <= 1 ); } return true; } diff --git a/src/map.h b/src/map.h index ed0263f33f211..336b4f42936aa 100644 --- a/src/map.h +++ b/src/map.h @@ -528,7 +528,7 @@ class map void reset_vehicle_cache( int zlev ); void clear_vehicle_cache( int zlev ); void clear_vehicle_list( int zlev ); - void update_vehicle_list( submap *to, int zlev ); + void update_vehicle_list( const submap *to, int zlev ); //Returns true if vehicle zones are dirty and need to be recached bool check_vehicle_zones( int zlev ); std::vector get_vehicle_zones( int zlev ); @@ -628,8 +628,11 @@ class map // connect_group. From least-significant bit the order is south, east, // west, north (because that's what cata_tiles expects). // Based on a combination of visibility and memory, not simply the true - // terrain. - uint8_t get_known_connections( const tripoint &p, int connect_group ) const; + // terrain. Additional overrides can be passed in to override terrain + // at specific positions. This is used to display terrain overview in + // the map editor. + uint8_t get_known_connections( const tripoint &p, int connect_group, + const std::map &override = {} ) const; /** * Returns the full harvest list, for spawning. */ @@ -657,12 +660,14 @@ class map * the creature is at p or at an adjacent square). */ bool sees_some_items( const tripoint &p, const Creature &who ) const; + bool sees_some_items( const tripoint &p, const tripoint &from ) const; /** * Check if the creature could see items at p if there were * any items. This is similar to @ref sees_some_items, but it * does not check that there are actually any items. */ bool could_see_items( const tripoint &p, const Creature &who ) const; + bool could_see_items( const tripoint &p, const tripoint &from ) const; /** * Checks for existence of items. Faster than i_at(p).empty */ diff --git a/src/mapgen.cpp b/src/mapgen.cpp index e4ea422ed2723..977b98ea12cad 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -7067,6 +7067,9 @@ void map::rotate( int turns ) overmap_buffer.insert_npc( npc_ptr ); } + clear_vehicle_cache( abs_sub.z ); + clear_vehicle_list( abs_sub.z ); + // Move the submaps around. if( turns == 2 ) { std::swap( *get_submap_at_grid( point_zero ), *get_submap_at_grid( point_south_east ) ); @@ -7092,10 +7095,13 @@ void map::rotate( int turns ) sm->rotate( turns ); for( auto &veh : sm->vehicles ) { - veh->sm_pos = abs_sub + p; + veh->sm_pos = tripoint( p, abs_sub.z ); } + + update_vehicle_list( sm, abs_sub.z ); } } + reset_vehicle_cache( abs_sub.z ); // rotate zones zone_manager &mgr = zone_manager::get_manager();