/** Helper for place_player * Flood-fills the area from a given point, then returns the area it found. * Considers unpassable tiles passable if they can be bashed down or opened. * Will return INT_MAX if it reaches upstairs or map edge. * We can't really use map::route here, because it expects a destination * Maybe TODO: Allow "picking up" items or parts of bashable furniture * and using them to help with bash attempts. */ int rate_location( map &m, const tripoint &p, const bool must_be_inside, const int bash_str, const int attempt, int ( &checked )[MAPSIZE * SEEX][MAPSIZE * SEEY] ) { if( ( must_be_inside && m.is_outside( p ) ) || m.impassable( p ) || checked[p.x][p.y] > 0 ) { return 0; } // Vector that will be used as a stack std::vector<tripoint> st; st.reserve( MAPSIZE * SEEX * MAPSIZE * SEEY ); st.push_back( p ); // If not checked yet and either can be moved into, can be bashed down or opened, // add it on the top of the stack. const auto maybe_add = [&]( const int x, const int y, const tripoint & from ) { if( checked[x][y] >= attempt ) { return; } const tripoint pt( x, y, p.z ); if( m.passable( pt ) || m.bash_resistance( pt ) <= bash_str || m.open_door( pt, !m.is_outside( from ), true ) ) { st.push_back( pt ); } }; int area = 0; while( !st.empty() ) { area++; const tripoint cur = st.back(); st.pop_back(); checked[cur.x][cur.y] = attempt; if( cur.x == 0 || cur.x == SEEX * MAPSIZE - 1 || cur.y == 0 || cur.y == SEEY * MAPSIZE - 1 || m.has_flag( "GOES_UP", cur ) ) { return INT_MAX; } maybe_add( cur.x - 1, cur.y, cur ); maybe_add( cur.x, cur.y - 1, cur ); maybe_add( cur.x + 1, cur.y, cur ); maybe_add( cur.x, cur.y + 1, cur ); maybe_add( cur.x - 1, cur.y - 1, cur ); maybe_add( cur.x + 1, cur.y - 1, cur ); maybe_add( cur.x - 1, cur.y + 1, cur ); maybe_add( cur.x + 1, cur.y + 1, cur ); } return area; }
// check if tile at p should be boarded with some kind of furniture. void add_boardable( map &m, const tripoint &p, std::vector<tripoint> &vec ) { if( m.has_furn( p ) ) { // Don't need to board this up, is already occupied return; } if( m.ter( p ) != t_floor ) { // Other terrain (door, wall, ...), not boarded either return; } if( m.is_outside( p ) ) { // Don't board up the outside return; } if( std::find( vec.begin(), vec.end(), p ) != vec.end() ) { // Already registered to be boarded return; } vec.push_back( p ); }
void board_up( map &m, const tripoint &start, const tripoint &end ) { std::vector<tripoint> furnitures1; std::vector<tripoint> furnitures2; std::vector<tripoint> boardables; tripoint p; p.z = m.get_abs_sub().z; int &x = p.x; int &y = p.y; int &z = p.z; for( x = start.x; x < end.x; x++ ) { for( y = start.y; y < end.y; y++ ) { bool must_board_around = false; const ter_id t = m.ter( x, y ); if( t == t_window_domestic || t == t_window || t == t_window_no_curtains ) { // Windows are always to the outside and must be boarded must_board_around = true; m.ter_set( p, t_window_boarded ); } else if( t == t_door_c || t == t_door_locked || t == t_door_c_peep ) { // Only board up doors that lead to the outside if( m.is_outside( tripoint( x + 1, y, z ) ) || m.is_outside( tripoint( x - 1, y, z ) ) || m.is_outside( tripoint( x, y + 1, z ) ) || m.is_outside( tripoint( x, y - 1, z ) ) ) { m.ter_set( p, t_door_boarded ); must_board_around = true; } else { // internal doors are opened instead m.ter_set( p, t_door_o ); } } if( must_board_around ) { // Board up the surroundings of the door/window add_boardable( m, tripoint( x + 1, y, z ), boardables ); add_boardable( m, tripoint( x - 1, y, z ), boardables ); add_boardable( m, tripoint( x, y + 1, z ), boardables ); add_boardable( m, tripoint( x, y - 1, z ), boardables ); add_boardable( m, tripoint( x + 1, y + 1, z ), boardables ); add_boardable( m, tripoint( x - 1, y + 1, z ), boardables ); add_boardable( m, tripoint( x + 1, y - 1, z ), boardables ); add_boardable( m, tripoint( x - 1, y - 1, z ), boardables ); } } } // Find all furniture that can be used to board up some place for( x = start.x; x < end.x; x++ ) { for( y = start.y; y < end.y; y++ ) { if( std::find( boardables.begin(), boardables.end(), p ) != boardables.end() ) { continue; } if( !m.has_furn( p ) ) { continue; } // If the furniture is movable and the character can move it, use it to barricade // g->u is workable here as NPCs by definition are not starting the game. (Let's hope.) ///\EFFECT_STR determines what furniture might be used as a starting area barricade if( m.furn( p ).obj().move_str_req > 0 && m.furn( p ).obj().move_str_req < g->u.get_str() ) { if( m.furn( p ).obj().movecost == 0 ) { // Obstacles are better, prefer them furnitures1.push_back( p ); } else { furnitures2.push_back( p ); } } } } while( ( !furnitures1.empty() || !furnitures2.empty() ) && !boardables.empty() ) { const tripoint fp = random_entry_removed( furnitures1.empty() ? furnitures2 : furnitures1 ); const tripoint bp = random_entry_removed( boardables ); m.furn_set( bp, m.furn( fp ) ); m.furn_set( fp, f_null ); auto destination_items = m.i_at( bp ); for( auto moved_item : m.i_at( fp ) ) { destination_items.push_back( moved_item ); } m.i_clear( fp ); } }
void board_up( map &m, int sx, int sy, int dx, int dy ) { std::vector<point> furnitures1; std::vector<point> furnitures2; std::vector<point> boardables; for( int x = sx; x < sx + dx; x++ ) { for( int y = sy; y < sy + dy; y++ ) { bool must_board_around = false; const ter_id t = m.ter( x, y ); if( t == t_window_domestic || t == t_window ) { // Windows are always to the outside and must be boarded must_board_around = true; m.ter_set( x, y, t_window_boarded ); } else if( t == t_door_c || t == t_door_locked || t == t_door_c_peep ) { // Only board up doors that lead to the outside if( m.is_outside( x + 1, y ) || m.is_outside( x - 1, y ) || m.is_outside( x, y + 1 ) || m.is_outside( x, y - 1 ) ) { m.ter_set( x, y, t_door_boarded ); must_board_around = true; } else { // internal doors are opened instead m.ter_set( x, y, t_door_o ); } } if( must_board_around ) { // Board up the surroundings of the door/window add_boardable( m, point( x + 1, y ), boardables ); add_boardable( m, point( x - 1, y ), boardables ); add_boardable( m, point( x, y + 1 ), boardables ); add_boardable( m, point( x, y - 1 ), boardables ); add_boardable( m, point( x + 1, y + 1 ), boardables ); add_boardable( m, point( x - 1, y + 1 ), boardables ); add_boardable( m, point( x + 1, y - 1 ), boardables ); add_boardable( m, point( x - 1, y - 1 ), boardables ); } } } // Find all furniture that can be used to board up some place for( int x = sx; x < sx + dx; x++ ) { for( int y = sy; y < sy + dy; y++ ) { if( std::find( boardables.begin(), boardables.end(), point( x, y ) ) != boardables.end() ) { continue; } if( !m.has_furn( x, y ) ) { continue; } // If the furniture is movable and the character can move it, use it to barricade // g->u is workable here as NPCs by definition are not starting the game. (Let's hope.) if( (m.furn_at( x, y ).move_str_req > 0) && (m.furn_at( x, y ).move_str_req < g->u.get_str() )) { if( m.furn_at( x, y ).movecost == 0 ) { // Obstacles are better, prefer them furnitures1.push_back( point( x, y ) ); } else { furnitures2.push_back( point( x, y ) ); } } } } while( ( !furnitures1.empty() || !furnitures2.empty() ) && !boardables.empty() ) { const point fp = furnitures1.empty() ? get_random_from_vec( furnitures2 ) : get_random_from_vec( furnitures1 ); const point bp = get_random_from_vec( boardables ); m.furn_set( bp.x, bp.y, m.furn( fp.x, fp.y ) ); m.furn_set( fp.x, fp.y, f_null ); auto destination_items = m.i_at(bp.x, bp.y); for( auto moved_item : m.i_at(fp.x, fp.y) ) { destination_items.push_back( moved_item ); } m.i_clear( fp.x, fp.y ); } }