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
|
#include "cata_catch.h"
#include "creature_tracker.h"
#include "map.h"
#include "map_helpers.h"
#include "map_iterator.h"
#include "monster.h"
#include "rng.h"
#include <vector>
// The area of the whole map from two below to one above ground level.
static const tripoint p1( 0, 0, -2 );
static const tripoint p2( ( MAPSIZE *SEEX ) - 1, ( MAPSIZE *SEEY ) - 1, 1 );
struct structure {
structure( const tripoint &origin, const tripoint &dimensions, const std::string &name ) :
area( origin, origin + dimensions ), name_( name ) {};
bool contains( const tripoint &p ) const {
return area.contains( p );
}
inclusive_cuboid<tripoint> area;
std::string name_;
};
static void place_structures( const std::vector<structure> &spawn_areas,
std::vector<std::vector<tripoint>> &interior_areas,
std::vector<tripoint> &outside_area,
std::vector<tripoint> &rooftop_area )
{
map &here = get_map();
for( const tripoint &p : here.points_in_rectangle( p1, p2 ) ) {
bool found = false;
for( unsigned int i = 0; i < spawn_areas.size(); ++i ) {
// Indexing so these can pair up with interior_areas.
const structure &spawn_area = spawn_areas[i];
if( spawn_area.contains( p ) ) {
found = true;
// Uppermost level (if any) gets a roof covering the whole footprint.
if( spawn_area.area.p_max.z != spawn_area.area.p_min.z && p.z == spawn_area.area.p_max.z ) {
rooftop_area.emplace_back( p );
here.ter_set( p, ter_id( "t_flat_roof" ) );
break;
}
inclusive_cuboid<tripoint> interior{
rectangle{
spawn_area.area.p_min.xy() + point_south_east,
spawn_area.area.p_max.xy() - point_south_east
},
spawn_area.area.p_min.z, spawn_area.area.p_min.z
};
if( interior.contains( p ) ) {
here.ter_set( p, ter_id( "t_floor" ) );
interior_areas[i].emplace_back( p );
} else {
here.ter_set( p, ter_id( "t_concrete_wall" ) );
}
break;
}
}
if( !found && p.z == 0 ) {
outside_area.push_back( p );
}
}
}
// Just make all the buildings the same size.
static constexpr int building_width = 7;
static constexpr int building_height = 1;
static constexpr tripoint building_offset{ building_width, building_width, building_height };
static constexpr tripoint cave_offset{ building_width, building_width, 0 };
TEST_CASE( "visitable_zone_surface_test" )
{
map &here = get_map();
clear_map();
std::string mon_type = "mon_zombie";
std::vector<monster *> monsters;
// All of these origins must be building_width + 3 away from each other.
// Surface building locations.
const tripoint enclosed_building{ 30, 30, 0 };
const tripoint closed_door = enclosed_building + tripoint_east * 2;
// Surface building with a door.
const tripoint door_building{ 40, 30, 0 };
const tripoint open_door = door_building + tripoint_east * 2;
// Surface building with a window.
const tripoint window_building{ 50, 30, 0 };
const tripoint window = window_building + tripoint_south * 2;
// Underground room.
const tripoint underground_room{ 60, 30, -1 };
// Underground room.
const tripoint underground_stairs_room{ 70, 30, -1 };
const tripoint underground_stairs_up = underground_stairs_room + tripoint_south_east * 3;
const tripoint underground_stairs_down = underground_stairs_up + tripoint_above;
// Elevated building with stairs to the surface.
const tripoint stilts_building{ 80, 30, 1 };
const tripoint stilts_stairs_down = stilts_building + tripoint_south_east * 3;
const tripoint stilts_stairs_up = stilts_stairs_down + tripoint_below;
std::vector<structure> spawn_locations = {{
{ enclosed_building, building_offset, "closed door building" },
{ door_building, building_offset, "open door building" },
{ window_building, building_offset, "window building" },
{ underground_room, cave_offset, "cave" },
{ underground_stairs_room, cave_offset, "cave with stairs" },
{ stilts_building, building_offset, "building on stilts" }
}
};
std::vector<std::vector<tripoint>> interior_areas( spawn_locations.size() );
std::vector<tripoint> outside_area;
std::vector<tripoint> rooftop_area;
place_structures( spawn_locations, interior_areas, outside_area, rooftop_area );
here.ter_set( closed_door, ter_id( "t_door_c" ) );
here.ter_set( open_door, ter_id( "t_door_o" ) );
here.ter_set( window, ter_id( "t_window" ) );
here.ter_set( underground_stairs_up, ter_id( "t_wood_stairs_up" ) );
here.ter_set( underground_stairs_down, ter_id( "t_wood_stairs_down" ) );
here.ter_set( stilts_stairs_up, ter_id( "t_wood_stairs_up" ) );
here.ter_set( stilts_stairs_down, ter_id( "t_wood_stairs_down" ) );
// Reset all the map caches after editing the map.
for( int i = OVERMAP_HEIGHT; i >= -OVERMAP_DEPTH; --i ) {
here.invalidate_map_cache( i );
here.build_map_cache( i, true );
}
// Some spot checks of our map edits.
REQUIRE( !here.passable( enclosed_building + point_east ) );
REQUIRE( !here.passable( door_building + point_south ) );
REQUIRE( !here.is_transparent_wo_fields( enclosed_building + point_east ) );
REQUIRE( !here.is_transparent_wo_fields( door_building + point_south ) );
REQUIRE( here.passable( open_door ) );
REQUIRE( here.is_transparent_wo_fields( open_door ) );
REQUIRE( !here.passable( closed_door ) );
REQUIRE( !here.is_transparent_wo_fields( closed_door ) );
REQUIRE( !here.passable( window ) );
REQUIRE( here.is_transparent_wo_fields( window ) );
for( std::vector<tripoint> &area : interior_areas ) {
for( const int &choice : rng_sequence( 2, 0, area.size() - 1 ) ) {
monster &mon = spawn_test_monster( mon_type, area[choice] );
monsters.push_back( &mon );
}
}
for( const int &choice : rng_sequence( 5, 0, outside_area.size() - 1 ) ) {
monster &mon = spawn_test_monster( mon_type, outside_area[choice] );
monsters.push_back( &mon );
}
for( const int &choice : rng_sequence( 5, 0, rooftop_area.size() - 1 ) ) {
monster &mon = spawn_test_monster( mon_type, rooftop_area[choice] );
monsters.push_back( &mon );
}
std::unordered_map<int, int> count_by_zone;
for( monster *mon : monsters ) {
mon->plan();
const int zone = mon->get_reachable_zone();
++count_by_zone[zone];
}
CHECK( count_by_zone.size() == 3 );
for( monster *mon : monsters ) {
tripoint mpos = mon->pos();
std::vector<structure>::iterator it =
find_if( spawn_locations.begin(), spawn_locations.end(),
[mon]( structure & a_structure ) {
return a_structure.contains( mon->pos() );
} );
std::string spawn_site = "outside";
if( mpos.z >= 1 ) {
spawn_site = "rooftop";
} else if( it != spawn_locations.end() ) {
spawn_site = it->name_;
}
CAPTURE( spawn_site );
CAPTURE( mon->pos() );
CAPTURE( mon->get_reachable_zone() );
CHECK( mon->get_reachable_zone() != 0 );
}
creature_tracker &tracker = get_creature_tracker();
for( monster *mon : monsters ) {
const int zone = mon->get_reachable_zone();
int visited_count = 0;
tracker.for_each_reachable( *mon, [zone, &visited_count]( Creature * creature ) {
CHECK( zone == creature->get_reachable_zone() );
// Don't count the player.
if( creature->is_monster() ) {
++visited_count;
}
} );
CHECK( visited_count == count_by_zone[zone] );
}
}
|