void game::draw_custom_explosion( const tripoint &, const std::map<tripoint, nc_color> &all_area ) { constexpr explosion_neighbors all_neighbors = N_NORTH | N_SOUTH | N_WEST | N_EAST; // We will "shell" the explosion area // Each phase will strip a single layer of points // A layer contains all points that have less than 4 neighbors in cardinal directions // Layers will first be generated, then drawn in inverse order // Start by getting rid of everything except current z-level std::map<point, explosion_tile> neighbors; #if defined(TILES) if( !use_tiles ) { for( const auto &pr : all_area ) { const tripoint relative_point = relative_view_pos( u, pr.first ); if( relative_point.z == 0 ) { point flat_point{ relative_point.x, relative_point.y }; neighbors[flat_point] = explosion_tile{ N_NO_NEIGHBORS, pr.second }; } } } else { // In tiles mode, the coordinates have to be absolute const tripoint view_center = relative_view_pos( u, u.pos() ); for( const auto &pr : all_area ) { const tripoint &pt = pr.first; // Relative point is only used for z level check const tripoint relative_point = relative_view_pos( u, pr.first ); if( relative_point.z == view_center.z ) { point flat_point{ pt.x, pt.y }; neighbors[flat_point] = explosion_tile{ N_NO_NEIGHBORS, pr.second }; } } } #else for( const auto &pr : all_area ) { const tripoint relative_point = relative_view_pos( u, pr.first ); if( relative_point.z == 0 ) { point flat_point{ relative_point.x, relative_point.y }; neighbors[flat_point] = explosion_tile{ N_NO_NEIGHBORS, pr.second }; } } #endif // Searches for a neighbor, sets the neighborhood flag on current point and on the neighbor const auto set_neighbors = [&]( const point &pos, explosion_neighbors &ngh, explosion_neighbors here, explosion_neighbors there ) { if( ( ngh & here ) == N_NO_NEIGHBORS ) { auto other = neighbors.find( pos ); if( other != neighbors.end() ) { ngh = ngh | here; other->second.neighborhood = other->second.neighborhood | there; } } }; // If the point we are about to remove has a neighbor in a given direction // unset that neighbor's flag that our current point is its neighbor const auto unset_neighbor = [&]( const point &pos, const explosion_neighbors ngh, explosion_neighbors here, explosion_neighbors there ) { if( ( ngh & here ) != N_NO_NEIGHBORS ) { auto other = neighbors.find( pos ); if( other != neighbors.end() ) { other->second.neighborhood = ( other->second.neighborhood | there ) ^ there; } } }; // Find all neighborhoods for( auto &pr : neighbors ) { const point &pt = pr.first; explosion_neighbors &ngh = pr.second.neighborhood; set_neighbors( point( pt.x - 1, pt.y ), ngh, N_WEST, N_EAST ); set_neighbors( point( pt.x + 1, pt.y ), ngh, N_EAST, N_WEST ); set_neighbors( point( pt.x, pt.y - 1 ), ngh, N_NORTH, N_SOUTH ); set_neighbors( point( pt.x, pt.y + 1 ), ngh, N_SOUTH, N_NORTH ); } // We need to save the layers because we will draw them in reverse order std::list< std::map<point, explosion_tile> > layers; bool changed; while( !neighbors.empty() ) { std::map<point, explosion_tile> layer; changed = false; // Find a layer that can be drawn for( const auto &pr : neighbors ) { if( pr.second.neighborhood != all_neighbors ) { changed = true; layer.insert( pr ); } } if( !changed ) { // An error, but a minor one - let it slide return; } // Remove the layer from the area to process for( const auto &pr : layer ) { const point &pt = pr.first; const explosion_neighbors ngh = pr.second.neighborhood; unset_neighbor( point( pt.x - 1, pt.y ), ngh, N_WEST, N_EAST ); unset_neighbor( point( pt.x + 1, pt.y ), ngh, N_EAST, N_WEST ); unset_neighbor( point( pt.x, pt.y - 1 ), ngh, N_NORTH, N_SOUTH ); unset_neighbor( point( pt.x, pt.y + 1 ), ngh, N_SOUTH, N_NORTH ); neighbors.erase( pr.first ); } layers.push_front( std::move( layer ) ); } #if defined(TILES) if( !use_tiles ) { draw_custom_explosion_curses( *this, layers ); return; } // We need to draw all explosions up to now std::map<point, explosion_tile> combined_layer; for( const auto &layer : layers ) { combined_layer.insert( layer.begin(), layer.end() ); tilecontext->init_custom_explosion_layer( combined_layer ); wrefresh(w_terrain); draw_animation_delay(EXPLOSION_MULTIPLIER); } tilecontext->void_custom_explosion(); #else draw_custom_explosion_curses( *this, layers ); #endif }
void game::draw_cursor( const tripoint &p ) { const tripoint rp = relative_view_pos( *this, p ); mvwputch_inv( w_terrain, rp.y, rp.x, c_light_green, 'X' ); }