1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
#include <set>
#include <vector>
#include "avatar.h"
#include "cata_catch.h"
#include "item.h"
#include "itype.h"
#include "map.h"
#include "map_helpers.h"
#include "map_iterator.h"
#include "mapdata.h"
#include "point.h"
#include "type_id.h"
// Destroying pavement with a pickaxe should not leave t_flat_roof.
// See issue #24707:
// https://github.com/CleverRaven/Cataclysm-DDA/issues/24707
TEST_CASE( "pavement_destroy", "[.]" )
{
const ter_id flat_roof_id = ter_id( "t_flat_roof" );
REQUIRE( flat_roof_id != t_null );
clear_map_and_put_player_underground();
map &here = get_map();
// Populate the map with pavement.
here.ter_set( tripoint_zero, ter_id( "t_pavement" ) );
// Destroy it
here.destroy( tripoint_zero, true );
ter_id after_destroy = here.ter( tripoint_zero );
if( after_destroy == flat_roof_id ) {
FAIL( flat_roof_id.obj().name() << " found after destroying pavement" );
} else {
INFO( "After destroy, ground is " << after_destroy.obj().name() );
}
}
// Ground-destroying explosions on dirt or grass shouldn't leave t_flat_roof.
// See issue #23250:
// https://github.com/CleverRaven/Cataclysm-DDA/issues/23250
TEST_CASE( "explosion_on_ground", "[.]" )
{
ter_id flat_roof_id = ter_id( "t_flat_roof" );
REQUIRE( flat_roof_id != t_null );
clear_map_and_put_player_underground();
std::vector<ter_id> test_terrain_id = {
ter_id( "t_dirt" ), ter_id( "t_grass" )
};
map &here = get_map();
int idx = 0;
const int area_dim = 16;
// Populate map with various test terrain.
for( int x = 0; x < area_dim; x++ ) {
for( int y = 0; y < area_dim; y++ ) {
here.ter_set( tripoint( x, y, 0 ), test_terrain_id[idx] );
idx = ( idx + 1 ) % test_terrain_id.size();
}
}
// Detonate an RDX keg item in the middle of the populated map space
itype_id rdx_keg_typeid( "tool_rdx_charge_act" );
REQUIRE( item::type_is_defined( rdx_keg_typeid ) );
const tripoint area_center( area_dim / 2, area_dim / 2, 0 );
item rdx_keg( rdx_keg_typeid );
rdx_keg.charges = 0;
rdx_keg.type->invoke( &get_avatar(), rdx_keg, area_center );
// Check area to see if any t_flat_roof is present.
for( int x = 0; x < area_dim; x++ ) {
for( int y = 0; y < area_dim; y++ ) {
tripoint pt( x, y, 0 );
ter_id t_id = here.ter( pt );
if( t_id == flat_roof_id ) {
FAIL( "After explosion, " << t_id.obj().name() << " found at " << x << "," << y );
}
}
}
}
// Ground-destroying explosions on t_floor with a t_rock_floor basement
// below should create some t_open_air, not just t_flat_roof (which is
// the defined roof of a t_rock-floor).
TEST_CASE( "explosion_on_floor_with_rock_floor_basement", "[.]" )
{
ter_id flat_roof_id = ter_id( "t_flat_roof" );
ter_id floor_id = ter_id( "t_floor" );
ter_id rock_floor_id = ter_id( "t_rock_floor" );
ter_id open_air_id = ter_id( "t_open_air" );
REQUIRE( flat_roof_id != t_null );
REQUIRE( floor_id != t_null );
REQUIRE( rock_floor_id != t_null );
REQUIRE( open_air_id != t_null );
clear_map_and_put_player_underground();
map &here = get_map();
const int area_dim = 24;
for( int x = 0; x < area_dim; x++ ) {
for( int y = 0; y < area_dim; y++ ) {
here.ter_set( tripoint( x, y, 0 ), floor_id );
here.ter_set( tripoint( x, y, -1 ), rock_floor_id );
}
}
// Detonate an RDX keg item in the middle of the populated map space
itype_id rdx_keg_typeid( "tool_rdx_charge_act" );
REQUIRE( item::type_is_defined( rdx_keg_typeid ) );
const tripoint area_center( area_dim / 2, area_dim / 2, 0 );
item rdx_keg( rdx_keg_typeid );
rdx_keg.charges = 0;
rdx_keg.type->invoke( &get_avatar(), rdx_keg, area_center );
// Check z0 for open air
bool found_open_air = false;
for( int x = 0; x < area_dim; x++ ) {
for( int y = 0; y < area_dim; y++ ) {
tripoint pt( x, y, 0 );
ter_id t_id = here.ter( pt );
INFO( "t " << t_id.obj().name() << " at " << x << "," << y );
if( t_id == open_air_id ) {
found_open_air = true;
break;
}
}
if( found_open_air ) {
break;
}
}
if( !found_open_air ) {
FAIL( "After explosion, no open air was found" );
}
}
// Destroying interior floors shouldn't cause the roofs above to collapse.
// Destroying supporting walls should cause the roofs above to collapse.
TEST_CASE( "collapse_checks", "[.]" )
{
constexpr int wall_size = 5;
const ter_id floor_id = ter_id( "t_floor" );
const ter_id dirt_id = ter_id( "t_dirt" );
const ter_id wall_id = ter_id( "t_wall" );
const ter_id open_air_id = ter_id( "t_open_air" );
REQUIRE( floor_id != t_null );
REQUIRE( dirt_id != t_null );
REQUIRE( wall_id != t_null );
REQUIRE( open_air_id != t_null );
clear_map_and_put_player_underground();
map &here = get_map();
// build a structure
const tripoint &midair = tripoint( tripoint_zero.xy(), tripoint_zero.z + 1 );
for( const tripoint &pt : here.points_in_radius( midair, wall_size, 1 ) ) {
here.ter_set( pt, floor_id );
}
std::set<tripoint> corners;
for( int delta_z = 0; delta_z < 3; delta_z++ ) {
for( int delta_x = 0; delta_x <= 1; delta_x++ ) {
for( int delta_y = 0; delta_y <= 1; delta_y++ ) {
const tripoint pt( delta_x * wall_size, delta_y * wall_size, delta_z );
corners.insert( pt );
here.ter_set( pt, wall_id );
}
}
}
// make sure it's a valid structure
for( const tripoint &pt : here.points_in_radius( midair, wall_size, 1 ) ) {
if( corners.find( pt ) != corners.end() ) {
REQUIRE( here.ter( pt ) == wall_id );
} else {
REQUIRE( here.ter( pt ) == floor_id );
}
}
// destroy the floor on the first floor; floor above should not collapse
for( const tripoint &pt : here.points_in_radius( tripoint_zero, wall_size ) ) {
if( corners.find( pt ) == corners.end() ) {
here.destroy( pt, true );
}
}
for( const tripoint &pt : here.points_in_radius( midair, wall_size ) ) {
if( corners.find( pt ) != corners.end() ) {
CHECK( here.ter( pt ) == wall_id );
} else {
CHECK( here.ter( pt ) == floor_id );
}
}
// destroy the walls on the first floor; upper floors should mostly collapse
for( int delta_x = 0; delta_x <= 1; delta_x++ ) {
for( int delta_y = 0; delta_y <= 1; delta_y++ ) {
const tripoint pt( delta_x * wall_size, delta_y * wall_size, 0 );
here.destroy( pt, true );
}
}
int open_air_count = 0;
int tile_count = 0;
int no_wall_count = 0;
for( const tripoint &pt : here.points_in_radius( midair, wall_size, 1 ) ) {
if( pt.z == 0 ) {
continue;
}
const ter_id t_id = here.ter( pt );
tile_count += 1;
if( t_id == t_open_air ) {
open_air_count += 1;
if( corners.find( pt ) != corners.end() ) {
no_wall_count += 1;
}
}
}
int partial_tiles = tile_count / 5;
CHECK( open_air_count > partial_tiles );
if( open_air_count < partial_tiles ) {
FAIL( open_air_count << " open air tiles were found on the upper floors after the walls "
"collapsed." );
} else {
INFO( open_air_count << " open air tiles were found on the upper floors after the walls "
"collapsed." );
}
CHECK( no_wall_count == 8 );
if( no_wall_count != 8 ) {
FAIL( "Only " << no_wall_count << " walls on the upper floors collapsed." );
} else {
INFO( "All " << no_wall_count << " walls on the upper floors collapsed." );
}
}
|