diff --git a/src/action.cpp b/src/action.cpp index 94549b732af3..8e79d7fe4081 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -693,7 +693,11 @@ action_id handle_action_menu() // display that action at the top of the list. for( const tripoint &pos : g->m.points_in_radius( g->u.pos(), 1 ) ) { if( pos != g->u.pos() ) { - // Check for actions that work on nearby tiles + // Check for actions that work on nearby tiles, skipping tiles blocked by vehicles + if( g->m.obstructed_by_vehicle_rotation( g->u.pos(), pos ) ) { + continue; + } + if( can_interact_at( ACTION_OPEN, pos ) ) { action_weightings[ACTION_OPEN] = 200; } @@ -1006,7 +1010,17 @@ cata::optional choose_direction( const std::string &message, const boo cata::optional choose_adjacent( const std::string &message, const bool allow_vertical ) { const cata::optional dir = choose_direction( message, allow_vertical ); - return dir ? *dir + g->u.pos() : dir; + + if( !dir ) { + return cata::nullopt; + } + + if( g->m.obstructed_by_vehicle_rotation( g->u.pos(), *dir + g->u.pos() ) ) { + add_msg( _( "You can't reach through there." ) ); + return cata::nullopt; + } + + return *dir + g->u.pos(); } cata::optional choose_adjacent_highlight( const std::string &message, @@ -1025,7 +1039,7 @@ cata::optional choose_adjacent_highlight( const std::string &message, std::vector valid; if( allowed ) { for( const tripoint &pos : g->m.points_in_radius( g->u.pos(), 1 ) ) { - if( allowed( pos ) ) { + if( !g->m.obstructed_by_vehicle_rotation( g->u.pos(), pos ) && allowed( pos ) ) { valid.emplace_back( pos ); } } diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index 8d30e34ef687..58b9b5a3191b 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -250,6 +250,11 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) return false; } + if( m.obstructed_by_vehicle_rotation( you.pos(), dest_loc ) ) { + add_msg( _( "You can't squeeze through there" ) ); + return false; + } + if( monster *const mon_ptr = g->critter_at( dest_loc, true ) ) { monster &critter = *mon_ptr; if( critter.friendly == 0 && diff --git a/src/ballistics.cpp b/src/ballistics.cpp index 5799f0cc855b..1607fae5a0fd 100644 --- a/src/ballistics.cpp +++ b/src/ballistics.cpp @@ -427,6 +427,24 @@ dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tri } else if( in_veh != nullptr && veh_pointer_or_null( g->m.veh_at( tp ) ) == in_veh ) { // Don't do anything, especially don't call map::shoot as this would damage the vehicle } else { + + if( g->m.obstructed_by_vehicle_rotation( prev_point, tp ) ) { + //We're firing through an impassible gap in a rotated vehicle, randomly hit one of the two walls + tripoint rand = tp; + if( one_in( 2 ) ) { + rand.x = prev_point.x; + } else { + rand.y = prev_point.y; + } + g->m.shoot( rand, proj, false ); + if( proj.impact.total_damage() <= 0 ) { + //If the projectile stops here move it back a square so it doesn't end up inside the vehicle + traj_len = i - 1; + tp = prev_point; + break; + } + } + g->m.shoot( tp, proj, !no_item_damage && tp == target ); has_momentum = proj.impact.total_damage() > 0; } diff --git a/src/creature.cpp b/src/creature.cpp index 0960fa320608..0d8f6c3baeb5 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -224,9 +224,12 @@ bool Creature::sees( const Creature &critter ) const const Character *ch = critter.as_character(); const int wanted_range = rl_dist( pos(), critter.pos() ); - // Can always see adjacent monsters on the same level. + // Can always see adjacent monsters on the same level, unless they're through a vehicle wall. // We also bypass lighting for vertically adjacent monsters, but still check for floors. if( wanted_range <= 1 && ( posz() == critter.posz() || g->m.sees( pos(), critter.pos(), 1 ) ) ) { + if( g->m.obscured_by_vehicle_rotation( pos(), critter.pos() ) ) { + return false; + } return visible( ch ); } else if( ( wanted_range > 1 && critter.digging() ) || ( critter.has_flag( MF_NIGHT_INVISIBILITY ) && g->m.light_at( critter.pos() ) <= lit_level::LOW ) || diff --git a/src/explosion.cpp b/src/explosion.cpp index 0310e173dc8c..7b2aaafc1bef 100644 --- a/src/explosion.cpp +++ b/src/explosion.cpp @@ -629,7 +629,8 @@ static std::map shrapnel( const tripoint &src, const proj const int offset_distance = 60 - 1 - fragment.range; castLightAll - ( visited_cache, obstacle_cache, src.xy(), offset_distance, fragment.range + 1.0f ); + ( visited_cache, obstacle_cache, g->m.access_cache( src.z ).blocked_cache, src.xy(), + offset_distance, fragment.range + 1.0f ); // Now visited_caches are populated with density and velocity of fragments. for( const tripoint &target : area ) { diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 7d55fc5fafe0..95b56294ca51 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -83,6 +83,7 @@ bool map::build_transparency_cache( const int zlev ) auto &map_cache = get_cache( zlev ); auto &transparency_cache = map_cache.transparency_cache; auto &outside_cache = map_cache.outside_cache; + auto &blocked_cache = map_cache.blocked_cache; if( map_cache.transparency_cache_dirty.none() ) { return false; @@ -95,6 +96,8 @@ bool map::build_transparency_cache( const int zlev ) // Default to just barely not transparent. std::uninitialized_fill_n( &transparency_cache[0][0], MAPSIZE_X * MAPSIZE_Y, static_cast( LIGHT_TRANSPARENCY_OPEN_AIR ) ); + + std::uninitialized_fill_n( &blocked_cache[0][0][0], MAPSIZE_X * MAPSIZE_Y * 2, false ); } const float sight_penalty = get_weather().weather_id->sight_penalty; @@ -120,6 +123,7 @@ bool map::build_transparency_cache( const int zlev ) cur_submap->get_furn( sp ).obj().transparent ) ) { return LIGHT_TRANSPARENCY_SOLID; } + if( outside_cache[p.x][p.y] ) { // FIXME: Places inside vehicles haven't been marked as // inside yet so this is incorrectly penalising for @@ -156,6 +160,67 @@ bool map::build_transparency_cache( const int zlev ) } } } + + if( !rebuild_all ) { + for( int sx = 0; sx < SEEX; ++sx ) { + std::uninitialized_fill_n( &blocked_cache[sm_offset.x + sx][sm_offset.y][0], SEEY * 2, false ); + } + } + + for( auto it = cur_submap->vehicles.begin(); it != cur_submap->vehicles.end(); ++it ) { + std::set checked; + + auto parts = ( *it )->get_all_parts(); + + for( auto part = parts.begin(); part != parts.end(); ++part ) { + + auto trip = ( *it )->mount_to_tripoint( part->mount() ); + + for( int dy = -1; dy <= 1; dy++ ) { + for( int dx = -1; dx <= 1; dx++ ) { + + + tripoint from = { trip.x + dx, trip.y + dy, trip.z }; + + if( checked.find( {from.x, from.y} ) != checked.end() || from.x < 0 || from.y < 0 || + from.x >= MAPSIZE_X || from.y >= MAPSIZE_Y ) { + continue; + } + checked.insert( {from.x, from.y} ); + + + auto from_mount = ( *it )->tripoint_to_mount( from ); + + for( int my = -1; my <= 1; my += 2 ) { + for( int mx = -1; mx <= 1; mx += 2 ) { + + auto t = ( *it )->tripoint_to_mount( { from.x + mx, from.y + my, from.z } ); + + if( !( *it )->allowed_light( t, from_mount ) ) { + if( my == -1 && mx == -1 ) { + blocked_cache[from.x][from.y][0] = true; + } else if( my == -1 && mx == 1 ) { + blocked_cache[from.x][from.y][1] = true; + } else if( my == 1 && mx == -1 ) { + if( from.x == 0 || from.y == MAPSIZE_Y - 1 ) { + continue; + } + blocked_cache[from.x - 1][from.y + 1][1] = true; + } else { + if( from.x == MAPSIZE_X - 1 || from.y == MAPSIZE_Y - 1 ) { + continue; + } + blocked_cache[from.x + 1][from.y + 1][0] = true; + } + } + + } + } + } + } + } + } + } } map_cache.transparency_cache_dirty.reset(); @@ -797,6 +862,7 @@ void cast_zlight_segment( const array_of_grids_of &output_caches, const array_of_grids_of &input_arrays, const array_of_grids_of &floor_caches, + const array_of_grids_of &blocked_caches, const tripoint &offset, int offset_distance, T numerator = 1.0f, int row = 1, float start_major = 0.0f, float end_major = 1.0f, @@ -811,6 +877,7 @@ void cast_zlight_segment( const array_of_grids_of &output_caches, const array_of_grids_of &input_arrays, const array_of_grids_of &floor_caches, + const array_of_grids_of &blocked_caches, const tripoint &offset, const int offset_distance, const T numerator, const int row, float start_major, const float end_major, @@ -821,6 +888,29 @@ void cast_zlight_segment( return; } + constexpr quadrant quad = quadrant_from_x_y( -xx - xy, -yx - yy ); + + const auto checkBlocked = [blocked_caches]( const tripoint & p ) -> bool{ + switch( quad ) + { + case quadrant::NW: + return ( *blocked_caches[p.z + OVERMAP_DEPTH] )[p.x][p.y][0]; + break; + case quadrant::NE: + return ( *blocked_caches[p.z + OVERMAP_DEPTH] )[p.x][p.y][1]; + break; + case quadrant::SE: + return ( p.x < MAPSIZE_X - 1 && p.y < MAPSIZE_Y - 1 && + ( *blocked_caches[p.z + OVERMAP_DEPTH] )[p.x + 1][p.y + 1][0] ); + break; + case quadrant::SW: + return ( p.x > 1 && p.y < MAPSIZE_Y - 1 && + ( *blocked_caches[p.z + OVERMAP_DEPTH] )[p.x - 1][p.y + 1][1] ); + break; + } + }; + + float radius = 60.0f - offset_distance; constexpr int min_z = -OVERMAP_DEPTH; @@ -890,7 +980,7 @@ void cast_zlight_segment( const int dist = rl_dist( tripoint_zero, delta ) + offset_distance; last_intensity = calc( numerator, cumulative_transparency, dist ); - if( !floor_block ) { + if( !floor_block && !checkBlocked( current ) ) { ( *output_caches[z_index] )[current.x][current.y] = std::max( ( *output_caches[z_index] )[current.x][current.y], last_intensity ); } @@ -936,14 +1026,14 @@ void cast_zlight_segment( const float trailing_clipped = std::max( trailing_edge_major, start_major ); const float major_mid = merge_blocks ? leading_edge_major : trailing_clipped; cast_zlight_segment( - output_caches, input_arrays, floor_caches, + output_caches, input_arrays, floor_caches, blocked_caches, offset, offset_distance, numerator, distance + 1, start_major, major_mid, start_minor, end_minor, next_cumulative_transparency ); if( !merge_blocks ) { // One line that is too short to be part of the rectangle above cast_zlight_segment( - output_caches, input_arrays, floor_caches, + output_caches, input_arrays, floor_caches, blocked_caches, offset, offset_distance, numerator, distance + 1, major_mid, leading_edge_major, start_minor, trailing_edge_minor, next_cumulative_transparency ); @@ -965,7 +1055,7 @@ void cast_zlight_segment( // leading_edge_major plus some epsilon float after_leading_edge_major = ( delta.z + 0.50001f ) / ( delta.y - 0.5f ); cast_zlight_segment( - output_caches, input_arrays, floor_caches, + output_caches, input_arrays, floor_caches, blocked_caches, offset, offset_distance, numerator, distance, after_leading_edge_major, end_major, old_start_minor, start_minor, cumulative_transparency ); @@ -1004,49 +1094,50 @@ void cast_zlight( const array_of_grids_of &output_caches, const array_of_grids_of &input_arrays, const array_of_grids_of &floor_caches, + const array_of_grids_of &blocked_caches, const tripoint &origin, const int offset_distance, const T numerator ) { // Down cast_zlight_segment < 0, 1, 0, 1, 0, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 1, 0, 0, 0, 1, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 0, -1, 0, 1, 0, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < -1, 0, 0, 0, 1, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 0, 1, 0, -1, 0, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 1, 0, 0, 0, -1, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 0, -1, 0, -1, 0, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < -1, 0, 0, 0, -1, 0, -1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); // Up cast_zlight_segment<0, 1, 0, 1, 0, 0, 1, T, calc, check, accumulate>( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment<1, 0, 0, 0, 1, 0, 1, T, calc, check, accumulate>( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 0, -1, 0, 1, 0, 0, 1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < -1, 0, 0, 0, 1, 0, 1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 0, 1, 0, -1, 0, 0, 1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 1, 0, 0, 0, -1, 0, 1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < 0, -1, 0, -1, 0, 0, 1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); cast_zlight_segment < -1, 0, 0, 0, -1, 0, 1, T, calc, check, accumulate > ( - output_caches, input_arrays, floor_caches, origin, offset_distance, numerator ); + output_caches, input_arrays, floor_caches, blocked_caches, origin, offset_distance, numerator ); } // I can't figure out how to make implicit instantiation work when the parameters of @@ -1055,12 +1146,14 @@ template void cast_zlight &output_caches, const array_of_grids_of &input_arrays, const array_of_grids_of &floor_caches, + const array_of_grids_of &blocked_caches, const tripoint &origin, int offset_distance, float numerator ); template void cast_zlight( const array_of_grids_of &output_caches, const array_of_grids_of &input_arrays, const array_of_grids_of &floor_caches, + const array_of_grids_of &blocked_caches, const tripoint &origin, int offset_distance, float numerator ); template void castLight( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], const T( &input_array )[MAPSIZE_X][MAPSIZE_Y], + const bool( &blocked_array )[MAPSIZE_X][MAPSIZE_Y][2], const point &offset, int offsetDistance, T numerator = VISIBILITY_FULL, int row = 1, float start = 1.0f, float end = 0.0f, @@ -1082,10 +1176,29 @@ template void castLight( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], const T( &input_array )[MAPSIZE_X][MAPSIZE_Y], + const bool( &blocked_array )[MAPSIZE_X][MAPSIZE_Y][2], const point &offset, const int offsetDistance, const T numerator, const int row, float start, const float end, T cumulative_transparency ) { constexpr quadrant quad = quadrant_from_x_y( -xx - xy, -yx - yy ); + + const auto checkBlocked = [blocked_array]( const point & p ) { + switch( quad ) { + case quadrant::NW: + return blocked_array[p.x][p.y][0]; + break; + case quadrant::NE: + return blocked_array[p.x][p.y][1]; + break; + case quadrant::SE: + return ( p.x < MAPSIZE_X - 1 && p.y < MAPSIZE_Y - 1 && blocked_array[p.x + 1][p.y + 1][0] ); + break; + case quadrant::SW: + return ( p.x > 1 && p.y < MAPSIZE_Y - 1 && blocked_array[p.x - 1][p.y + 1][1] ); + break; + } + }; + float newStart = 0.0f; float radius = 60.0f - offsetDistance; if( start < end ) { @@ -1114,6 +1227,9 @@ void castLight( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], } else if( end > trailingEdge ) { break; } + if( checkBlocked( current ) ) { + continue; + } if( !started_row ) { started_row = true; current_transparency = input_array[ current.x ][ current.y ]; @@ -1123,7 +1239,6 @@ void castLight( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], last_intensity = calc( numerator, cumulative_transparency, dist ); T new_transparency = input_array[ current.x ][ current.y ]; - if( check( new_transparency, last_intensity ) ) { update_output( output_cache[current.x][current.y], last_intensity, quadrant::default_ ); @@ -1135,10 +1250,12 @@ void castLight( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], newStart = leadingEdge; continue; } + + // Only cast recursively if previous span was not opaque. if( check( current_transparency, last_intensity ) ) { castLight( - output_cache, input_array, offset, offsetDistance, + output_cache, input_array, blocked_array, offset, offsetDistance, numerator, distance + 1, start, trailingEdge, accumulate( cumulative_transparency, current_transparency, distance ) ); } @@ -1172,33 +1289,35 @@ template void castLightAll( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], const T( &input_array )[MAPSIZE_X][MAPSIZE_Y], + const bool( &blocked_array )[MAPSIZE_X][MAPSIZE_Y][2], const point &offset, int offsetDistance, T numerator ) { castLight<0, 1, 1, 0, T, Out, calc, check, update_output, accumulate>( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); castLight<1, 0, 0, 1, T, Out, calc, check, update_output, accumulate>( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); castLight < 0, -1, 1, 0, T, Out, calc, check, update_output, accumulate > ( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); castLight < -1, 0, 0, 1, T, Out, calc, check, update_output, accumulate > ( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); castLight < 0, 1, -1, 0, T, Out, calc, check, update_output, accumulate > ( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); castLight < 1, 0, 0, -1, T, Out, calc, check, update_output, accumulate > ( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); castLight < 0, -1, -1, 0, T, Out, calc, check, update_output, accumulate > ( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); castLight < -1, 0, 0, -1, T, Out, calc, check, update_output, accumulate > ( - output_cache, input_array, offset, offsetDistance, numerator ); + output_cache, input_array, blocked_array, offset, offsetDistance, numerator ); } template void castLightAll( four_quadrants( &output_cache )[MAPSIZE_X][MAPSIZE_Y], const float ( &input_array )[MAPSIZE_X][MAPSIZE_Y], + const bool( &blocked_array )[MAPSIZE_X][MAPSIZE_Y][2], const point &offset, int offsetDistance, float numerator ); template void @@ -1207,6 +1326,7 @@ castLightAll( - seen_cache, transparency_cache, origin.xy(), 0 ); + seen_cache, transparency_cache, blocked_cache, origin.xy(), 0 ); } } } else { @@ -1253,11 +1374,13 @@ void map::build_seen_cache( const tripoint &origin, const int target_z ) array_of_grids_of transparency_caches; array_of_grids_of seen_caches; array_of_grids_of floor_caches; + array_of_grids_of blocked_caches; for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) { auto &cur_cache = get_cache( z ); transparency_caches[z + OVERMAP_DEPTH] = &cur_cache.vision_transparency_cache; seen_caches[z + OVERMAP_DEPTH] = &cur_cache.seen_cache; floor_caches[z + OVERMAP_DEPTH] = &cur_cache.floor_cache; + blocked_caches[z + OVERMAP_DEPTH] = &cur_cache.blocked_cache; std::uninitialized_fill_n( &cur_cache.seen_cache[0][0], map_dimensions, light_transparency_solid ); cur_cache.seen_cache_dirty = false; @@ -1266,7 +1389,7 @@ void map::build_seen_cache( const tripoint &origin, const int target_z ) get_cache( origin.z ).seen_cache[origin.x][origin.y] = VISIBILITY_FULL; } cast_zlight( - seen_caches, transparency_caches, floor_caches, origin, 0, 1.0 ); + seen_caches, transparency_caches, floor_caches, blocked_caches, origin, 0, 1.0 ); } const optional_vpart_position vp = veh_at( origin ); @@ -1322,7 +1445,7 @@ void map::build_seen_cache( const tripoint &origin, const int target_z ) // The naive solution of making the mirrors act like a second player // at an offset appears to give reasonable results though. castLightAll( - camera_cache, transparency_cache, mirror_pos.xy(), offsetDistance ); + camera_cache, transparency_cache, blocked_cache, mirror_pos.xy(), offsetDistance ); } } @@ -1363,6 +1486,7 @@ void map::apply_light_source( const tripoint &p, float luminance ) float ( &sm )[MAPSIZE_X][MAPSIZE_Y] = cache.sm; float ( &transparency_cache )[MAPSIZE_X][MAPSIZE_Y] = cache.transparency_cache; float ( &light_source_buffer )[MAPSIZE_X][MAPSIZE_Y] = cache.light_source_buffer; + bool ( &blocked_cache )[MAPSIZE_X][MAPSIZE_Y][2] = cache.blocked_cache; const point p2( p.xy() ); @@ -1402,37 +1526,37 @@ void map::apply_light_source( const tripoint &p, float luminance ) if( north ) { castLight < 1, 0, 0, -1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < -1, 0, 0, -1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } if( east ) { castLight < 0, -1, 1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < 0, -1, -1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } if( south ) { castLight<1, 0, 0, 1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency>( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < -1, 0, 0, 1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } if( west ) { castLight<0, 1, 1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency>( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < 0, 1, -1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } } @@ -1443,35 +1567,36 @@ void map::apply_directional_light( const tripoint &p, int direction, float lumin auto &cache = get_cache( p.z ); four_quadrants( &lm )[MAPSIZE_X][MAPSIZE_Y] = cache.lm; float ( &transparency_cache )[MAPSIZE_X][MAPSIZE_Y] = cache.transparency_cache; + bool ( &blocked_cache )[MAPSIZE_X][MAPSIZE_Y][2] = cache.blocked_cache; if( direction == 90 ) { castLight < 1, 0, 0, -1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < -1, 0, 0, -1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } else if( direction == 0 ) { castLight < 0, -1, 1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < 0, -1, -1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } else if( direction == 270 ) { castLight<1, 0, 0, 1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency>( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < -1, 0, 0, 1, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } else if( direction == 180 ) { castLight<0, 1, 1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency>( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); castLight < 0, 1, -1, 0, float, four_quadrants, light_calc, light_check, update_light_quadrants, accumulate_transparency > ( - lm, transparency_cache, p2, 0, luminance ); + lm, transparency_cache, blocked_cache, p2, 0, luminance ); } } diff --git a/src/map.cpp b/src/map.cpp index c7d7bfa013b2..09f0279152d9 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -6413,6 +6413,51 @@ bool map::clear_path( const tripoint &f, const tripoint &t, const int range, return is_clear; } +bool map::obstructed_by_vehicle_rotation( const tripoint &from, const tripoint &to ) +{ + const optional_vpart_position vp0 = veh_at( from ); + vehicle *const veh0 = veh_pointer_or_null( vp0 ); + const optional_vpart_position vp1 = veh_at( to ); + vehicle *const veh1 = veh_pointer_or_null( vp1 ); + + if( veh1 != nullptr ) { + point veh1p = veh1->tripoint_to_mount( from ); + if( !veh1->allowed_move( veh1p, vp1->mount() ) ) { + return true; + } + } + if( veh0 != nullptr && veh1 != veh0 ) { + point veh0p = veh0->tripoint_to_mount( to ); + if( !veh0->allowed_move( vp0->mount(), veh0p ) ) { + return true; + } + } + return false; +} + + +bool map::obscured_by_vehicle_rotation( const tripoint &from, const tripoint &to ) +{ + const optional_vpart_position vp0 = veh_at( from ); + vehicle *const veh0 = veh_pointer_or_null( vp0 ); + const optional_vpart_position vp1 = veh_at( to ); + vehicle *const veh1 = veh_pointer_or_null( vp1 ); + + if( veh1 != nullptr ) { + point veh1p = veh1->tripoint_to_mount( from ); + if( !veh1->allowed_light( veh1p, vp1->mount() ) ) { + return true; + } + } + if( veh0 != nullptr && veh1 != veh0 ) { + point veh0p = veh0->tripoint_to_mount( to ); + if( !veh0->allowed_light( vp0->mount(), veh0p ) ) { + return true; + } + } + return false; +} + bool map::accessible_items( const tripoint &t ) const { return !has_flag( "SEALED", t ) || has_flag( "LIQUIDCONT", t ); @@ -8482,6 +8527,7 @@ level_cache::level_cache() std::fill_n( &outside_cache[0][0], map_dimensions, false ); std::fill_n( &floor_cache[0][0], map_dimensions, false ); std::fill_n( &transparency_cache[0][0], map_dimensions, 0.0f ); + std::fill_n( &blocked_cache[0][0][0], map_dimensions * 2, false ); std::fill_n( &vision_transparency_cache[0][0], map_dimensions, 0.0f ); std::fill_n( &seen_cache[0][0], map_dimensions, 0.0f ); std::fill_n( &camera_cache[0][0], map_dimensions, 0.0f ); diff --git a/src/map.h b/src/map.h index 00c4910987dd..79f3bad2b428 100644 --- a/src/map.h +++ b/src/map.h @@ -316,6 +316,10 @@ struct level_cache { // units: "transparency" (see LIGHT_TRANSPARENCY_OPEN_AIR) float transparency_cache[MAPSIZE_X][MAPSIZE_Y]; + // true when light entering a tile diagonally is blocked by the walls of a turned vehicle. First bit represents nw direction, the second ne + // check the values of adjacent tiles to find sw and se directions + bool blocked_cache[MAPSIZE_X][MAPSIZE_Y][2]; + // stores "adjusted transparency" of the tiles // initial values derived from transparency_cache, uses same units // examples of adjustment: changed transparency on player's tile and special case for crouching @@ -686,6 +690,16 @@ class map bool clear_path( const tripoint &f, const tripoint &t, int range, int cost_min, int cost_max ) const; + /** + * Checks if a rotated vehicle is blocking diagonal movement, tripoints must be adjacent + */ + bool obstructed_by_vehicle_rotation( const tripoint &from, const tripoint &to ); + + /** + * Checks if a rotated vehicle is blocking diagonal vision, tripoints must be adjacent + */ + bool obscured_by_vehicle_rotation( const tripoint &from, const tripoint &to ); + /** * Populates a vector of points that are reachable within a number of steps from a * point. It could be generalized to take advantage of z levels, but would need some diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 3f0c2647ed95..4a5180db958f 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -47,6 +47,10 @@ static bool is_adjacent( const monster &z, const Creature &target ) return false; } + if( !z.can_squeeze_to( target.pos() ) ) { + return false; + } + return z.posz() == target.posz(); } diff --git a/src/monattack.cpp b/src/monattack.cpp index 68fffcd3fd2b..ece7733b95a0 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -282,6 +282,10 @@ static bool is_adjacent( const monster *z, const Creature *target, const bool al return false; } + if( !z->can_squeeze_to( target->pos() ) ) { + return false; + } + if( z->posz() == target->posz() ) { return true; } diff --git a/src/monmove.cpp b/src/monmove.cpp index 757e02fa9b9f..2bcdd40196ec 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -246,6 +246,13 @@ bool monster::can_reach_to( const tripoint &p ) const return true; } +bool monster::can_squeeze_to( const tripoint &p ) const +{ + map &m = get_map(); + + return !m.obstructed_by_vehicle_rotation( pos(), p ); +} + bool monster::can_move_to( const tripoint &p ) const { return can_reach_to( p ) && will_move_to( p ); @@ -801,7 +808,7 @@ void monster::move() bool try_to_move = false; for( const tripoint &dest : g->m.points_in_radius( pos(), 1 ) ) { if( dest != pos() ) { - if( can_move_to( dest ) && + if( can_move_to( dest ) && can_squeeze_to( dest ) && g->critter_at( dest, true ) == nullptr ) { try_to_move = true; break; @@ -964,7 +971,7 @@ void monster::move() // Try to shove vehicle out of the way shove_vehicle( destination, candidate ); // Bail out if we can't move there and we can't bash. - if( !pathed && !can_move_to( candidate ) ) { + if( !pathed && ( !can_move_to( candidate ) || !can_squeeze_to( candidate ) ) ) { if( !can_bash ) { continue; } @@ -1540,6 +1547,10 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, return false; } + if( !can_squeeze_to( destination ) ) { + return false; + } + // Make sure that we can move there, unless force is true. if( !force && !can_move_to( destination ) ) { return false; diff --git a/src/monster.cpp b/src/monster.cpp index 0bf02f93c2f6..9aabc3f10ce0 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1386,6 +1386,10 @@ void monster::melee_attack( Creature &target, float accuracy ) return; } + if( !can_squeeze_to( target.pos() ) ) { + return; + } + if( target.is_player() || ( target.is_npc() && g->u.attitude_to( target ) == A_FRIENDLY ) ) { // Make us a valid target for a few turns diff --git a/src/monster.h b/src/monster.h index d6596516cf45..63164a7b2a64 100644 --- a/src/monster.h +++ b/src/monster.h @@ -186,10 +186,12 @@ class monster : public Creature, public visitable * will_move_to() checks for impassable terrain etc * can_reach_to() checks for z-level difference. * can_move_to() is a wrapper for both of them. + * can_squeeze_to() checks for vehicle holes. */ bool can_move_to( const tripoint &p ) const; bool can_reach_to( const tripoint &p ) const; bool will_move_to( const tripoint &p ) const; + bool can_squeeze_to( const tripoint &p ) const; bool will_reach( const point &p ); // Do we have plans to get to (x, y)? int turns_to_reach( const point &p ); // How long will it take? diff --git a/src/npcmove.cpp b/src/npcmove.cpp index c1158360e07c..9cb8a88d17d0 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -2270,6 +2270,12 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo path.clear(); move_pause(); } + + if( g->m.obstructed_by_vehicle_rotation( pos(), p ) ) { + move_pause(); + return; + } + bool attacking = false; if( g->critter_at( p ) ) { attacking = true; diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index 9638a0b7f9a9..5ced8f30b4d4 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -293,6 +293,9 @@ std::vector map::route( const tripoint &f, const tripoint &t, const auto &pf_cache = get_pathfinding_cache_ref( cur.z ); const auto cur_special = pf_cache.special[cur.x][cur.y]; + int curPart; + const vehicle *curVeh = veh_at_internal( cur, curPart ); + // 7 3 5 // 1 . 2 // 6 4 8 @@ -342,6 +345,18 @@ std::vector map::route( const tripoint &f, const tripoint &t, continue; } + if( curVeh && + !curVeh->allowed_move( curVeh->tripoint_to_mount( cur ), curVeh->tripoint_to_mount( p ) ) ) { + //Trying to squeeze through a vehicle hole, skip this movement but don't close the tile as other paths may lead to it + continue; + } + + if( veh && veh != curVeh && + !veh->allowed_move( veh->tripoint_to_mount( cur ), veh->tripoint_to_mount( p ) ) ) { + //Same as above but moving into rather than out of a vehicle + continue; + } + newg += cost; if( cost == 0 ) { if( climb_cost > 0 && p_special & PF_CLIMBABLE ) { diff --git a/src/shadowcasting.h b/src/shadowcasting.h index 7e8409ccfbc9..bf99f1356d21 100644 --- a/src/shadowcasting.h +++ b/src/shadowcasting.h @@ -109,6 +109,7 @@ template void castLightAll( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], const T( &input_array )[MAPSIZE_X][MAPSIZE_Y], + const bool( &blocked_array )[MAPSIZE_X][MAPSIZE_Y][2], const point &offset, int offsetDistance = 0, T numerator = 1.0 ); @@ -123,6 +124,7 @@ void cast_zlight( const array_of_grids_of &output_caches, const array_of_grids_of &input_arrays, const array_of_grids_of &floor_caches, + const array_of_grids_of &blocked_caches, const tripoint &origin, int offset_distance, T numerator ); #endif // CATA_SRC_SHADOWCASTING_H diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 21a1b0774f11..27efba8c7f9e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2576,6 +2576,40 @@ bool vehicle::has_part( const tripoint &pos, const std::string &flag, bool enabl return false; } +int vehicle::obstacle_at_position( const point &pos ) const +{ + int i = part_with_feature( pos, "OBSTACLE", true ); + + if( i == -1 ) { + return -1; + } + + auto ref = parts[i]; + + if( ref.info().has_flag( VPFLAG_OPENABLE ) && ref.open ) { + return -1; + } + + return i; +} + +int vehicle::opaque_at_position( const point &pos ) const +{ + int i = part_with_feature( pos, "OPAQUE", true ); + + if( i == -1 ) { + return -1; + } + + auto ref = parts[i]; + + if( ref.info().has_flag( VPFLAG_OPENABLE ) && ref.open ) { + return -1; + } + + return i; +} + std::vector vehicle::get_parts_at( const tripoint &pos, const std::string &flag, const part_status_flag condition ) { @@ -3026,13 +3060,9 @@ const struct { void vehicle::coord_translate( units::angle dir, const point &pivot, const point &p, tripoint &q ) const { - point relative = p - pivot; - - int increment = ( std::lround( to_degrees( dir ) ) % 360 ) / 15; - if( increment < 0 ) { - increment += 360 / 15; - } + int increment = angle_to_increment( dir ); + point relative = p - pivot; float skew = std::trunc( relative.x * rotation_info[increment].gradient ); q.x = relative.x; @@ -3051,6 +3081,37 @@ void vehicle::coord_translate( units::angle dir, const point &pivot, const point } } +void vehicle::coord_translate_reverse( units::angle dir, const point &pivot, const tripoint &p, + point &q ) const +{ + int increment = angle_to_increment( dir ); + + q.x = p.x; + q.y = p.y; + + + if( rotation_info[increment].flipY ) { + q.y = -q.y; + } + + if( rotation_info[increment].flipX ) { + q.x = -q.x; + } + + if( rotation_info[increment].swapXY ) { + auto swap = q.x; + q.x = q.y; + q.y = swap; + } + + float skew = std::trunc( q.x * rotation_info[increment].gradient ); + + q.y -= skew; + + q += pivot; + +} + tripoint vehicle::mount_to_tripoint( const point &mount ) const { return mount_to_tripoint( mount, point_zero ); @@ -3063,17 +3124,38 @@ tripoint vehicle::mount_to_tripoint( const point &mount, const point &offset ) c return global_pos3() + mnt_translated; } +point vehicle::tripoint_to_mount( const tripoint &p ) const +{ + tripoint translated = p - global_pos3(); + + point result; + coord_translate_reverse( pivot_rotation[0], pivot_anchor[0], translated, result ); + + return result; +} + +int vehicle::angle_to_increment( units::angle dir ) +{ + int increment = ( std::lround( to_degrees( dir ) ) % 360 ) / 15; + if( increment < 0 ) { + increment += 360 / 15; + } + return increment; +} + + void vehicle::precalc_mounts( int idir, units::angle dir, const point &pivot ) { if( idir < 0 || idir > 1 ) { idir = 0; } - std::unordered_map mount_to_precalc; + for( auto &p : parts ) { if( p.removed ) { continue; } + auto q = mount_to_precalc.find( p.mount ); if( q == mount_to_precalc.end() ) { coord_translate( dir, pivot, p.mount, p.precalc[idir] ); @@ -3086,6 +3168,60 @@ void vehicle::precalc_mounts( int idir, units::angle dir, const point &pivot ) pivot_rotation[idir] = dir; } +bool vehicle::check_rotated_intervening( const point &from, const point &to, + bool( *check )( const vehicle *, const point & ) ) const +{ + point delta = to - from; + if( abs( delta.x ) <= 1 && abs( delta.y ) <= 1 ) { //Just a normal move + return true; + } + + if( !( ( abs( delta.x ) == 2 && abs( delta.y ) == 1 ) || ( abs( delta.x ) == 1 && + abs( delta.y ) == 2 ) ) ) { //Check that we're moving like a knight + debugmsg( "Unexpected movement in rotated vehicle vector:%d,%d", delta.x, delta.y ); + return false; + } + + if( abs( delta.x ) == 2 ) { //Mostly horizontal move + point t1 = {from.x + ( delta.x / 2 ), from.y + delta.y}; + if( check( this, t1 ) ) { + return true; + } + + point t2 = {from.x + ( delta.x / 2 ), from.y}; + if( check( this, t2 ) ) { + return true; + } + + } else { //Mostly vertical move + point t1 = {from.x + delta.x, from.y + ( delta.y / 2 )}; + if( check( this, t1 ) ) { + return true; + } + + point t2 = {from.x, from.y + ( delta.y / 2 )}; + if( check( this, t2 ) ) { + return true; + } + } + + return false; +} + +bool vehicle::allowed_light( const point &from, const point &to ) const +{ + return check_rotated_intervening( from, to, []( const vehicle * veh, const point & p ) { + return ( veh->opaque_at_position( p ) == -1 ); + } ); +} + +bool vehicle::allowed_move( const point &from, const point &to ) const +{ + return check_rotated_intervening( from, to, []( const vehicle * veh, const point & p ) { + return ( veh->obstacle_at_position( p ) == -1 ); + } ); +} + std::vector vehicle::boarded_parts() const { std::vector res; diff --git a/src/vehicle.h b/src/vehicle.h index e1f4d50e5e18..667f9f5e66a6 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -994,6 +994,9 @@ class vehicle int avail_part_with_feature( const point &pt, const std::string &f, bool unbroken ) const; int avail_part_with_feature( int p, vpart_bitflags f, bool unbroken ) const; + int obstacle_at_position( const point &pos ) const; + int opaque_at_position( const point &pos ) const; + /** * Check if vehicle has at least one unbroken part with specified flag * @param flag Specified flag to search parts for @@ -1067,9 +1070,16 @@ class vehicle void coord_translate( units::angle dir, const point &pivot, const point &p, tripoint &q ) const; + // Translate rotated tile coordinates "p" into mount coordinates "q" using given pivot direction and anchor + void coord_translate_reverse( units::angle dir, const point &pivot, const tripoint &p, + point &q ) const; + tripoint mount_to_tripoint( const point &mount ) const; tripoint mount_to_tripoint( const point &mount, const point &offset ) const; + //Translate tile coordinates into mount coordinates + point tripoint_to_mount( const tripoint &p ) const; + // Seek a vehicle part which obstructs tile with given coordinates relative to vehicle position int part_at( const point &dp ) const; int part_displayed_at( const point &dp ) const; @@ -1204,6 +1214,9 @@ class vehicle */ void invalidate_mass(); + //Converts angles into turning increments + static int angle_to_increment( units::angle dir ); + // get the total mass of vehicle, including cargo and passengers units::mass total_mass() const; @@ -1710,6 +1723,16 @@ class vehicle void interact_with( const tripoint &pos, int interact_part ); + //Check if a movement is blocked, must be adjacent points + bool allowed_move( const point &from, const point &to ) const; + + //Check if light is blocked, must be adjacent points + bool allowed_light( const point &from, const point &to ) const; + + //Checks if the conditional holds for tiles that can be skipped due to rotation + bool check_rotated_intervening( const point &from, const point &to, bool( *check )( const vehicle *, + const point & ) ) const; + std::string disp_name() const; /** Required strength to be able to successfully lift the vehicle unaided by equipment */ diff --git a/tests/shadowcasting_test.cpp b/tests/shadowcasting_test.cpp index f1b07eee5810..2eee4db1bd9f 100644 --- a/tests/shadowcasting_test.cpp +++ b/tests/shadowcasting_test.cpp @@ -221,6 +221,9 @@ static void shadowcasting_runoff( const int iterations, const bool test_bresenha float seen_squares_control[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{0}}; float seen_squares_experiment[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{0}}; float transparency_cache[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{0}}; + bool blocked_cache[MAPSIZE * SEEX][MAPSIZE * SEEY][2] = {{{0}}}; + + std::uninitialized_fill_n( &blocked_cache[0][0][0], MAPSIZE * SEEX * MAPSIZE * SEEY * 2, false ); randomly_fill_transparency( transparency_cache ); @@ -249,7 +252,7 @@ static void shadowcasting_runoff( const int iterations, const bool test_bresenha for( int i = 0; i < iterations; i++ ) { // Then the current algorithm. castLightAll( - seen_squares_experiment, transparency_cache, offset ); + seen_squares_experiment, transparency_cache, blocked_cache, offset ); } const auto end2 = std::chrono::high_resolution_clock::now(); @@ -290,6 +293,9 @@ static void shadowcasting_float_quad( float lit_squares_float[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{0}}; four_quadrants lit_squares_quad[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{}}; float transparency_cache[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{0}}; + bool blocked_cache[MAPSIZE * SEEX][MAPSIZE * SEEY][2] = {{{0}}}; + + std::uninitialized_fill_n( &blocked_cache[0][0][0], MAPSIZE * SEEX * MAPSIZE * SEEY * 2, false ); randomly_fill_transparency( transparency_cache, denominator ); @@ -301,7 +307,7 @@ static void shadowcasting_float_quad( for( int i = 0; i < iterations; i++ ) { castLightAll( - lit_squares_quad, transparency_cache, offset ); + lit_squares_quad, transparency_cache, blocked_cache, offset ); } const auto end1 = std::chrono::high_resolution_clock::now(); @@ -310,7 +316,7 @@ static void shadowcasting_float_quad( // Then the current algorithm. castLightAll( - lit_squares_float, transparency_cache, offset ); + lit_squares_float, transparency_cache, blocked_cache, offset ); } const auto end2 = std::chrono::high_resolution_clock::now(); @@ -343,6 +349,9 @@ static void shadowcasting_3d_2d( const int iterations ) float seen_squares_experiment[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{0}}; float transparency_cache[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{0}}; bool floor_cache[MAPSIZE * SEEX][MAPSIZE * SEEY] = {{false}}; + bool blocked_cache[MAPSIZE_X][MAPSIZE_Y][2] = {{{0}}}; + + std::fill_n( &blocked_cache[0][0][0], MAPSIZE * SEEX * MAPSIZE * SEEY * 2, false ); randomly_fill_transparency( transparency_cache ); @@ -354,7 +363,7 @@ static void shadowcasting_3d_2d( const int iterations ) for( int i = 0; i < iterations; i++ ) { // First the control algorithm. castLightAll( - seen_squares_control, transparency_cache, offset.xy() ); + seen_squares_control, transparency_cache, blocked_cache, offset.xy() ); } const auto end1 = std::chrono::high_resolution_clock::now(); @@ -362,18 +371,20 @@ static void shadowcasting_3d_2d( const int iterations ) std::array transparency_caches; std::array seen_caches; std::array floor_caches; + std::array blocked_caches; for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) { // TODO: Give some more proper values here transparency_caches[z + OVERMAP_DEPTH] = &transparency_cache; seen_caches[z + OVERMAP_DEPTH] = &seen_squares_experiment; floor_caches[z + OVERMAP_DEPTH] = &floor_cache; + blocked_caches[z + OVERMAP_DEPTH] = &blocked_cache; } const auto start2 = std::chrono::high_resolution_clock::now(); for( int i = 0; i < iterations; i++ ) { // Then the newer algorithm. cast_zlight( - seen_caches, transparency_caches, floor_caches, origin, 0, 1.0 ); + seen_caches, transparency_caches, floor_caches, blocked_caches, origin, 0, 1.0 ); } const auto end2 = std::chrono::high_resolution_clock::now(); @@ -446,6 +457,9 @@ static void run_spot_check( const grid_overlay &test_case, const grid_overlay &e { float seen_squares[ MAPSIZE * SEEY ][ MAPSIZE * SEEX ] = {{ 0 }}; float transparency_cache[ MAPSIZE * SEEY ][ MAPSIZE * SEEX ] = {{ 0 }}; + bool blocked_cache[MAPSIZE_X][MAPSIZE_Y][2] = {{{0}}}; + + std::fill_n( &blocked_cache[0][0][0], MAPSIZE_X * MAPSIZE_Y * 2, false ); for( int y = 0; y < static_cast( sizeof( transparency_cache ) / sizeof( transparency_cache[0] ) ); ++y ) { @@ -456,7 +470,7 @@ static void run_spot_check( const grid_overlay &test_case, const grid_overlay &e } castLightAll( - seen_squares, transparency_cache, ORIGIN ); + seen_squares, transparency_cache, blocked_cache, ORIGIN ); // Compares the whole grid, but out-of-bounds compares will de-facto pass. for( int y = 0; y < expected_result.height(); ++y ) { diff --git a/tests/vehicle_test.cpp b/tests/vehicle_test.cpp index 2dd5e708e267..ade94f34d6cb 100644 --- a/tests/vehicle_test.cpp +++ b/tests/vehicle_test.cpp @@ -113,3 +113,30 @@ TEST_CASE( "check_vehicle_rotation_against_old" ) } } +TEST_CASE( "vehicle_rotation_reverse" ) +{ + + + clear_map(); + const tripoint test_origin( 60, 60, 0 ); + const tripoint vehicle_origin = test_origin; + vehicle *veh_ptr = g->m.add_vehicle( vproto_id( "bicycle" ), vehicle_origin, 0_degrees, 0, 0 ); + const point pivot( 0, 0 ); + + for( int dir = 0; dir < 24; dir++ ) { + for( int x = -100; x <= 100; x++ ) { + for( int y = -100; y <= 100; y++ ) { + point p = {x, y}; + tripoint result; + veh_ptr->coord_translate( 15_degrees * dir, pivot, p, result ); + + point reversed; + veh_ptr->coord_translate_reverse( 15_degrees * dir, pivot, result, reversed ); + + CHECK( reversed.x == p.x ); + CHECK( reversed.y == p.y ); + + } + } + } +} diff --git a/tests/vision_test.cpp b/tests/vision_test.cpp index 4378dce4bfdf..f2fcd0047163 100644 --- a/tests/vision_test.cpp +++ b/tests/vision_test.cpp @@ -23,6 +23,9 @@ #include "shadowcasting.h" #include "type_id.h" #include "weather.h" +#include "vehicle.h" +#include "vpart_position.h" +#include "vpart_range.h" enum class vision_test_flags { none = 0, @@ -44,7 +47,9 @@ static bool operator!( vision_test_flags f ) static void full_map_test( const std::vector &setup, const std::vector &expected_results, const time_point &time, - const vision_test_flags flags ) + const vision_test_flags flags, + const std::string vehicle_id, + const units::angle vehicle_rotation ) { const ter_id t_brick_wall( "t_brick_wall" ); const ter_id t_window_frame( "t_window_frame" ); @@ -119,6 +124,7 @@ static void full_map_test( const std::vector &setup, } map &here = get_map(); + vehicle *veh = nullptr; for( int y = 0; y < height; ++y ) { for( int x = 0; x < width; ++x ) { const tripoint p = origin + point( x, y ); @@ -148,6 +154,12 @@ static void full_map_test( const std::vector &setup, case 'V': // Already handled above break; + case 'C': + veh = here.add_vehicle( vproto_id( vehicle_id ), p, vehicle_rotation, 0, 0 ); + for( const vpart_reference &vp : veh->get_avail_parts( "OPENABLE" ) ) { + veh->close( vp.part_index() ); + } + break; default: FAIL( "unexpected setup char '" << setup[y][x] << "'" ); } @@ -256,6 +268,8 @@ struct vision_test_case { std::vector expected_results; time_point time; vision_test_flags flags; + std::string vehicle_id = ""; + units::angle vehicle_rotation = 0_degrees; static void transpose( std::vector &v ) { if( v.empty() ) { @@ -292,7 +306,7 @@ struct vision_test_case { } void test() const { - full_map_test( setup, expected_results, time, flags ); + full_map_test( setup, expected_results, time, flags, vehicle_id, vehicle_rotation ); } void test_all_transformations() const { @@ -344,6 +358,7 @@ static const time_point midday = calendar::turn_zero + 12_hours; // 'L' - light, indoors // '#' - wall // '=' - window frame +// 'C' - The origin of the vehicle TEST_CASE( "vision_daylight", "[shadowcasting][vision]" ) { @@ -621,3 +636,69 @@ TEST_CASE( "vision_player_opaque_neighbors_still_visible_night", "[shadowcasting t.test_all(); } + +TEST_CASE( "vision_see_out_of_vehicle", "[shadowcasting][vision]" ) +{ + + vision_test_case t { + { + " ", + " C ", + " ", + " ", + " U ", + " ", + " ", + " ", + }, + { + "66666666666666666", + "66666666666666666", + "66666666664111666", + "66666666641114666", + "66666666411146666", + "66666664111466666", + "66666661114666666", + "66666666666666666", + }, + midday, + vision_test_flags::none, + "cube_van", + -45_degrees + }; + + t.test(); +} + +TEST_CASE( "vision_see_into_vehicle", "[shadowcasting][vision]" ) +{ + + vision_test_case t { + { + " ", + " C ", + " ", + " ", + " ", + " U ", + " ", + " ", + }, + { + "66666666666666664", + "66666666666666644", + "66666666666666444", + "66666666666664444", + "66666666666644444", + "66666666666444444", + "66666666664444444", + "66666666644444444", + }, + midday, + vision_test_flags::none, + "cube_van", + -45_degrees + }; + + t.test(); +}