Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| #include "mapgen_functions.h" | |
| #include <cstdlib> | |
| #include <algorithm> | |
| #include <array> | |
| #include <iterator> | |
| #include <initializer_list> | |
| #include <map> | |
| #include <ostream> | |
| #include <queue> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include "debug.h" | |
| #include "field.h" | |
| #include "item.h" | |
| #include "line.h" | |
| #include "map.h" | |
| #include "map_iterator.h" | |
| #include "mapdata.h" | |
| #include "mapgen.h" | |
| #include "mapgenformat.h" | |
| #include "omdata.h" | |
| #include "options.h" | |
| #include "overmap.h" | |
| #include "trap.h" | |
| #include "vehicle_group.h" | |
| #include "calendar.h" | |
| #include "game_constants.h" | |
| #include "regional_settings.h" | |
| #include "rng.h" | |
| #include "string_id.h" | |
| #include "int_id.h" | |
| #include "enums.h" | |
| class npc_template; | |
| #define dbg(x) DebugLog((x),D_MAP_GEN) << __FILE__ << ":" << __LINE__ << ": " | |
| const mtype_id mon_ant_larva( "mon_ant_larva" ); | |
| const mtype_id mon_ant_queen( "mon_ant_queen" ); | |
| const mtype_id mon_bat( "mon_bat" ); | |
| const mtype_id mon_bee( "mon_bee" ); | |
| const mtype_id mon_beekeeper( "mon_beekeeper" ); | |
| const mtype_id mon_fungaloid_queen( "mon_fungaloid_queen" ); | |
| const mtype_id mon_fungaloid_seeder( "mon_fungaloid_seeder" ); | |
| const mtype_id mon_fungaloid_tower( "mon_fungaloid_tower" ); | |
| const mtype_id mon_rat_king( "mon_rat_king" ); | |
| const mtype_id mon_sewer_rat( "mon_sewer_rat" ); | |
| const mtype_id mon_spider_widow_giant( "mon_spider_widow_giant" ); | |
| const mtype_id mon_spider_cellar_giant( "mon_spider_cellar_giant" ); | |
| const mtype_id mon_zombie_jackson( "mon_zombie_jackson" ); | |
| mapgendata::mapgendata( oter_id north, oter_id east, oter_id south, oter_id west, | |
| oter_id northeast, oter_id southeast, oter_id southwest, oter_id northwest, | |
| oter_id up, oter_id down, int z, const regional_settings &rsettings, map &mp ) | |
| : t_nesw{ north, east, south, west, northeast, southeast, southwest, northwest } | |
| , t_above( up ) | |
| , t_below( down ) | |
| , zlevel( z ) | |
| , region( rsettings ) | |
| , m( mp ) | |
| , default_groundcover( region.default_groundcover ) | |
| { | |
| } | |
| tripoint rotate_point( const tripoint &p, int rotations ) | |
| { | |
| if( p.x < 0 || p.x >= SEEX * 2 || | |
| p.y < 0 || p.y >= SEEY * 2 ) { | |
| debugmsg( "Point out of range: %d,%d,%d", p.x, p.y, p.z ); | |
| // Mapgen is vulnerable, don't supply invalid points, debugmsg is enough | |
| return tripoint( 0, 0, p.z ); | |
| } | |
| rotations = rotations % 4; | |
| tripoint ret = p; | |
| switch( rotations ) { | |
| case 0: | |
| break; | |
| case 1: | |
| ret.x = p.y; | |
| ret.y = SEEX * 2 - 1 - p.x; | |
| break; | |
| case 2: | |
| ret.x = SEEX * 2 - 1 - p.x; | |
| ret.y = SEEY * 2 - 1 - p.y; | |
| break; | |
| case 3: | |
| ret.x = SEEY * 2 - 1 - p.y; | |
| ret.y = p.x; | |
| break; | |
| } | |
| return ret; | |
| } | |
| building_gen_pointer get_mapgen_cfunction( const std::string &ident ) | |
| { | |
| static const std::map<std::string, building_gen_pointer> pointers = { { | |
| { "null", &mapgen_null }, | |
| { "crater", &mapgen_crater }, | |
| { "field", &mapgen_field }, | |
| { "forest", &mapgen_forest }, | |
| { "forest_trail_straight", &mapgen_forest_trail_straight }, | |
| { "forest_trail_curved", &mapgen_forest_trail_curved }, | |
| // TODO: Add a dedicated dead-end function. For now it copies the straight section above. | |
| { "forest_trail_end", &mapgen_forest_trail_straight }, | |
| { "forest_trail_tee", &mapgen_forest_trail_tee }, | |
| { "forest_trail_four_way", &mapgen_forest_trail_four_way }, | |
| { "hive", &mapgen_hive }, | |
| { "spider_pit", &mapgen_spider_pit }, | |
| { "fungal_bloom", &mapgen_fungal_bloom }, | |
| { "fungal_tower", &mapgen_fungal_tower }, | |
| { "fungal_flowers", &mapgen_fungal_flowers }, | |
| { "road_straight", &mapgen_road }, | |
| { "road_curved", &mapgen_road }, | |
| { "road_end", &mapgen_road }, | |
| { "road_tee", &mapgen_road }, | |
| { "road_four_way", &mapgen_road }, | |
| { "field", &mapgen_field }, | |
| { "bridge", &mapgen_bridge }, | |
| { "highway", &mapgen_highway }, | |
| { "railroad_straight", &mapgen_railroad }, | |
| { "railroad_curved", &mapgen_railroad }, | |
| { "railroad_end", &mapgen_railroad }, | |
| { "railroad_tee", &mapgen_railroad }, | |
| { "railroad_four_way", &mapgen_railroad }, | |
| { "railroad_bridge", &mapgen_railroad_bridge }, | |
| { "river_center", &mapgen_river_center }, | |
| { "river_curved_not", &mapgen_river_curved_not }, | |
| { "river_straight", &mapgen_river_straight }, | |
| { "river_curved", &mapgen_river_curved }, | |
| { "parking_lot", &mapgen_parking_lot }, | |
| { "house_generic_boxy", &mapgen_generic_house_boxy }, | |
| { "house_generic_big_livingroom", &mapgen_generic_house_big_livingroom }, | |
| { "house_generic_center_hallway", &mapgen_generic_house_center_hallway }, | |
| { "spider_pit", mapgen_spider_pit }, | |
| { "basement_generic_layout", &mapgen_basement_generic_layout }, // empty, not bound | |
| { "basement_junk", &mapgen_basement_junk }, | |
| { "basement_spiders", &mapgen_basement_spiders }, | |
| { "cave", &mapgen_cave }, | |
| { "cave_rat", &mapgen_cave_rat }, | |
| { "cavern", &mapgen_cavern }, | |
| { "open_air", &mapgen_open_air }, | |
| { "rift", &mapgen_rift }, | |
| { "hellmouth", &mapgen_hellmouth }, | |
| // New rock function - should be default, but isn't yet for compatibility reasons (old overmaps) | |
| { "empty_rock", &mapgen_rock }, | |
| // Old rock behavior, for compatibility and near caverns and slime pits | |
| { "rock", &mapgen_rock_partial }, | |
| { "subway_straight", &mapgen_subway }, | |
| { "subway_curved", &mapgen_subway }, | |
| // TODO: Add a dedicated dead-end function. For now it copies the straight section above. | |
| { "subway_end", &mapgen_subway }, | |
| { "subway_tee", &mapgen_subway }, | |
| { "subway_four_way", &mapgen_subway }, | |
| { "sewer_straight", &mapgen_sewer_straight }, | |
| { "sewer_curved", &mapgen_sewer_curved }, | |
| // TODO: Add a dedicated dead-end function. For now it copies the straight section above. | |
| { "sewer_end", &mapgen_sewer_straight }, | |
| { "sewer_tee", &mapgen_sewer_tee }, | |
| { "sewer_four_way", &mapgen_sewer_four_way }, | |
| { "ants_straight", &mapgen_ants_straight }, | |
| { "ants_curved", &mapgen_ants_curved }, | |
| // TODO: Add a dedicated dead-end function. For now it copies the straight section above. | |
| { "ants_end", &mapgen_ants_straight }, | |
| { "ants_tee", &mapgen_ants_tee }, | |
| { "ants_four_way", &mapgen_ants_four_way }, | |
| { "ants_food", &mapgen_ants_food }, | |
| { "ants_larvae", &mapgen_ants_larvae }, | |
| { "ants_queen", &mapgen_ants_queen }, | |
| { "tutorial", &mapgen_tutorial }, | |
| { "lake_shore", &mapgen_lake_shore }, | |
| } | |
| }; | |
| const auto iter = pointers.find( ident ); | |
| return iter == pointers.end() ? nullptr : iter->second; | |
| } | |
| void mapgendata::set_dir( int dir_in, int val ) | |
| { | |
| switch( dir_in ) { | |
| case 0: | |
| n_fac = val; | |
| break; | |
| case 1: | |
| e_fac = val; | |
| break; | |
| case 2: | |
| s_fac = val; | |
| break; | |
| case 3: | |
| w_fac = val; | |
| break; | |
| case 4: | |
| ne_fac = val; | |
| break; | |
| case 5: | |
| se_fac = val; | |
| break; | |
| case 6: | |
| sw_fac = val; | |
| break; | |
| case 7: | |
| nw_fac = val; | |
| break; | |
| default: | |
| debugmsg( "Invalid direction for mapgendata::set_dir. dir_in = %d", dir_in ); | |
| break; | |
| } | |
| } | |
| void mapgendata::fill( int val ) | |
| { | |
| n_fac = val; | |
| e_fac = val; | |
| s_fac = val; | |
| w_fac = val; | |
| ne_fac = val; | |
| se_fac = val; | |
| sw_fac = val; | |
| nw_fac = val; | |
| } | |
| int &mapgendata::dir( int dir_in ) | |
| { | |
| switch( dir_in ) { | |
| case 0: | |
| return n_fac; | |
| case 1: | |
| return e_fac; | |
| case 2: | |
| return s_fac; | |
| case 3: | |
| return w_fac; | |
| case 4: | |
| return ne_fac; | |
| case 5: | |
| return se_fac; | |
| case 6: | |
| return sw_fac; | |
| case 7: | |
| return nw_fac; | |
| default: | |
| debugmsg( "Invalid direction for mapgendata::set_dir. dir_in = %d", dir_in ); | |
| //return something just so the compiler doesn't freak out. Not really correct, though. | |
| return n_fac; | |
| } | |
| } | |
| ter_id grass_or_dirt() | |
| { | |
| if( one_in( 4 ) ) { | |
| return t_grass; | |
| } | |
| return t_dirt; | |
| } | |
| ter_id clay_or_sand() | |
| { | |
| if( one_in( 16 ) ) { | |
| return t_sand; | |
| } | |
| return t_clay; | |
| } | |
| void mapgendata::square_groundcover( const int x1, const int y1, const int x2, const int y2 ) | |
| { | |
| m.draw_square_ter( this->default_groundcover, x1, y1, x2, y2 ); | |
| } | |
| void mapgendata::fill_groundcover() | |
| { | |
| m.draw_fill_background( this->default_groundcover ); | |
| } | |
| bool mapgendata::is_groundcover( const ter_id &iid ) const | |
| { | |
| for( const auto &pr : default_groundcover ) { | |
| if( pr.obj == iid ) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool mapgendata::has_basement() const | |
| { | |
| const std::vector<std::string> &all_basements = region.city_spec.basements.all; | |
| return std::any_of( all_basements.begin(), all_basements.end(), [this]( const std::string & b ) { | |
| return t_below == oter_id( b ); | |
| } ); | |
| } | |
| ter_id mapgendata::groundcover() | |
| { | |
| const ter_id *tid = default_groundcover.pick(); | |
| return tid != nullptr ? *tid : t_null; | |
| } | |
| const oter_id &mapgendata::neighbor_at( om_direction::type dir ) const | |
| { | |
| // TODO: De-uglify, implement proper conversion somewhere | |
| switch( dir ) { | |
| case om_direction::type::north: | |
| return north(); | |
| case om_direction::type::east: | |
| return east(); | |
| case om_direction::type::south: | |
| return south(); | |
| case om_direction::type::west: | |
| return west(); | |
| default: | |
| break; | |
| } | |
| debugmsg( "Tried to get neighbor from invalid direction %d", dir ); | |
| return north(); | |
| } | |
| void mapgen_rotate( map *m, oter_id terrain_type, bool north_is_down ) | |
| { | |
| const auto dir = terrain_type->get_dir(); | |
| m->rotate( static_cast<int>( north_is_down ? om_direction::opposite( dir ) : dir ) ); | |
| } | |
| #define autorotate(x) mapgen_rotate(m, terrain_type, x) | |
| #define autorotate_down() mapgen_rotate(m, terrain_type, true) | |
| ///////////////////////////////////////////////////////////////////////////////////////////////// | |
| ///// builtin terrain-specific mapgen functions. big multi-overmap-tile terrains are located in | |
| ///// mapgen_functions_big.cpp | |
| void mapgen_null( map *m, oter_id, mapgendata, const time_point &, float ) | |
| { | |
| debugmsg( "Generating null terrain, please report this as a bug" ); | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| m->ter_set( i, j, t_null ); | |
| m->set_radiation( i, j, 0 ); | |
| } | |
| } | |
| } | |
| void mapgen_crater( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| for( int i = 0; i < 4; i++ ) { | |
| if( dat.t_nesw[i] != "crater" ) { | |
| dat.set_dir( i, 6 ); | |
| } | |
| } | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( rng( 0, dat.w_fac ) <= i && rng( 0, dat.e_fac ) <= SEEX * 2 - 1 - i && | |
| rng( 0, dat.n_fac ) <= j && rng( 0, dat.s_fac ) <= SEEX * 2 - 1 - j ) { | |
| m->ter_set( i, j, t_dirt ); | |
| m->make_rubble( tripoint( i, j, m->get_abs_sub().z ), f_rubble_rock, true ); | |
| m->set_radiation( i, j, rng( 0, 4 ) * rng( 0, 2 ) ); | |
| } else { | |
| m->ter_set( i, j, dat.groundcover() ); | |
| m->set_radiation( i, j, rng( 0, 2 ) * rng( 0, 2 ) * rng( 0, 2 ) ); | |
| } | |
| } | |
| } | |
| m->place_items( "wreckage", 83, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| } | |
| // TODO: make void map::ter_or_furn_set(const int x, const int y, const ter_furn_id & tfid); | |
| static void ter_or_furn_set( map *m, const int x, const int y, const ter_furn_id &tfid ) | |
| { | |
| if( tfid.ter != t_null ) { | |
| m->ter_set( x, y, tfid.ter ); | |
| } else if( tfid.furn != f_null ) { | |
| m->furn_set( x, y, tfid.furn ); | |
| } | |
| } | |
| /* | |
| * Default above ground non forested 'blank' area; typically a grassy field with a scattering of shrubs, | |
| * but changes according to dat->region | |
| */ | |
| void mapgen_field( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| // random area of increased vegetation. Or lava / toxic sludge / etc | |
| const bool boosted_vegetation = ( dat.region.field_coverage.boost_chance > rng( 0, 1000000 ) ); | |
| const int &mpercent_bush = ( boosted_vegetation ? | |
| dat.region.field_coverage.boosted_mpercent_coverage : | |
| dat.region.field_coverage.mpercent_coverage | |
| ); | |
| ter_furn_id altbush = dat.region.field_coverage.pick( | |
| true ); // one dominant plant type ( for boosted_vegetation == true ) | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| m->ter_set( i, j, dat.groundcover() ); // default is | |
| if( mpercent_bush > rng( 0, 1000000 ) ) { // yay, a shrub ( or tombstone ) | |
| if( boosted_vegetation && dat.region.field_coverage.boosted_other_mpercent > rng( 0, 1000000 ) ) { | |
| // already chose the lucky terrain/furniture/plant/rock/etc | |
| ter_or_furn_set( m, i, j, altbush ); | |
| } else { | |
| // pick from weighted list | |
| ter_or_furn_set( m, i, j, dat.region.field_coverage.pick( false ) ); | |
| } | |
| } | |
| } | |
| } | |
| m->place_items( "field", 60, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, | |
| turn ); // FIXME: take 'rock' out and add as regional biome setting | |
| } | |
| void mapgen_hive( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| // Start with a basic forest pattern | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| int rn = rng( 0, 14 ); | |
| if( rn > 13 ) { | |
| m->ter_set( i, j, t_tree ); | |
| } else if( rn > 11 ) { | |
| m->ter_set( i, j, t_tree_young ); | |
| } else if( rn > 10 ) { | |
| m->ter_set( i, j, t_underbrush ); | |
| } else { | |
| m->ter_set( i, j, dat.groundcover() ); | |
| } | |
| } | |
| } | |
| // j and i loop through appropriate hive-cell center squares | |
| const bool is_center = dat.t_nesw[0] == "hive" && dat.t_nesw[1] == "hive" && | |
| dat.t_nesw[2] == "hive" && dat.t_nesw[3] == "hive"; | |
| for( int j = 5; j < SEEY * 2 - 5; j += 6 ) { | |
| for( int i = ( j == 5 || j == 17 ? 3 : 6 ); i < SEEX * 2 - 5; i += 6 ) { | |
| if( !one_in( 8 ) ) { | |
| // Caps are always there | |
| m->ter_set( i, j - 5, t_wax ); | |
| m->ter_set( i, j + 5, t_wax ); | |
| for( int k = -2; k <= 2; k++ ) { | |
| for( int l = -1; l <= 1; l++ ) { | |
| m->ter_set( i + k, j + l, t_floor_wax ); | |
| } | |
| } | |
| m->add_spawn( mon_bee, 2, i, j ); | |
| m->add_spawn( mon_beekeeper, 1, i, j ); | |
| m->ter_set( i, j - 3, t_floor_wax ); | |
| m->ter_set( i, j + 3, t_floor_wax ); | |
| m->ter_set( i - 1, j - 2, t_floor_wax ); | |
| m->ter_set( i, j - 2, t_floor_wax ); | |
| m->ter_set( i + 1, j - 2, t_floor_wax ); | |
| m->ter_set( i - 1, j + 2, t_floor_wax ); | |
| m->ter_set( i, j + 2, t_floor_wax ); | |
| m->ter_set( i + 1, j + 2, t_floor_wax ); | |
| // Up to two of these get skipped; an entrance to the cell | |
| int skip1 = rng( 0, SEEX * 2 - 1 ); | |
| int skip2 = rng( 0, SEEY * 2 - 1 ); | |
| m->ter_set( i - 1, j - 4, t_wax ); | |
| m->ter_set( i, j - 4, t_wax ); | |
| m->ter_set( i + 1, j - 4, t_wax ); | |
| m->ter_set( i - 2, j - 3, t_wax ); | |
| m->ter_set( i - 1, j - 3, t_wax ); | |
| m->ter_set( i + 1, j - 3, t_wax ); | |
| m->ter_set( i + 2, j - 3, t_wax ); | |
| m->ter_set( i - 3, j - 2, t_wax ); | |
| m->ter_set( i - 2, j - 2, t_wax ); | |
| m->ter_set( i + 2, j - 2, t_wax ); | |
| m->ter_set( i + 3, j - 2, t_wax ); | |
| m->ter_set( i - 3, j - 1, t_wax ); | |
| m->ter_set( i - 3, j, t_wax ); | |
| m->ter_set( i - 3, j - 1, t_wax ); | |
| m->ter_set( i - 3, j + 1, t_wax ); | |
| m->ter_set( i - 3, j, t_wax ); | |
| m->ter_set( i - 3, j + 1, t_wax ); | |
| m->ter_set( i - 2, j + 3, t_wax ); | |
| m->ter_set( i - 1, j + 3, t_wax ); | |
| m->ter_set( i + 1, j + 3, t_wax ); | |
| m->ter_set( i + 2, j + 3, t_wax ); | |
| m->ter_set( i - 1, j + 4, t_wax ); | |
| m->ter_set( i, j + 4, t_wax ); | |
| m->ter_set( i + 1, j + 4, t_wax ); | |
| if( skip1 == 0 || skip2 == 0 ) { | |
| m->ter_set( i - 1, j - 4, t_floor_wax ); | |
| } | |
| if( skip1 == 1 || skip2 == 1 ) { | |
| m->ter_set( i, j - 4, t_floor_wax ); | |
| } | |
| if( skip1 == 2 || skip2 == 2 ) { | |
| m->ter_set( i + 1, j - 4, t_floor_wax ); | |
| } | |
| if( skip1 == 3 || skip2 == 3 ) { | |
| m->ter_set( i - 2, j - 3, t_floor_wax ); | |
| } | |
| if( skip1 == 4 || skip2 == 4 ) { | |
| m->ter_set( i - 1, j - 3, t_floor_wax ); | |
| } | |
| if( skip1 == 5 || skip2 == 5 ) { | |
| m->ter_set( i + 1, j - 3, t_floor_wax ); | |
| } | |
| if( skip1 == 6 || skip2 == 6 ) { | |
| m->ter_set( i + 2, j - 3, t_floor_wax ); | |
| } | |
| if( skip1 == 7 || skip2 == 7 ) { | |
| m->ter_set( i - 3, j - 2, t_floor_wax ); | |
| } | |
| if( skip1 == 8 || skip2 == 8 ) { | |
| m->ter_set( i - 2, j - 2, t_floor_wax ); | |
| } | |
| if( skip1 == 9 || skip2 == 9 ) { | |
| m->ter_set( i + 2, j - 2, t_floor_wax ); | |
| } | |
| if( skip1 == 10 || skip2 == 10 ) { | |
| m->ter_set( i + 3, j - 2, t_floor_wax ); | |
| } | |
| if( skip1 == 11 || skip2 == 11 ) { | |
| m->ter_set( i - 3, j - 1, t_floor_wax ); | |
| } | |
| if( skip1 == 12 || skip2 == 12 ) { | |
| m->ter_set( i - 3, j, t_floor_wax ); | |
| } | |
| if( skip1 == 13 || skip2 == 13 ) { | |
| m->ter_set( i - 3, j - 1, t_floor_wax ); | |
| } | |
| if( skip1 == 14 || skip2 == 14 ) { | |
| m->ter_set( i - 3, j + 1, t_floor_wax ); | |
| } | |
| if( skip1 == 15 || skip2 == 15 ) { | |
| m->ter_set( i - 3, j, t_floor_wax ); | |
| } | |
| if( skip1 == 16 || skip2 == 16 ) { | |
| m->ter_set( i - 3, j + 1, t_floor_wax ); | |
| } | |
| if( skip1 == 17 || skip2 == 17 ) { | |
| m->ter_set( i - 2, j + 3, t_floor_wax ); | |
| } | |
| if( skip1 == 18 || skip2 == 18 ) { | |
| m->ter_set( i - 1, j + 3, t_floor_wax ); | |
| } | |
| if( skip1 == 19 || skip2 == 19 ) { | |
| m->ter_set( i + 1, j + 3, t_floor_wax ); | |
| } | |
| if( skip1 == 20 || skip2 == 20 ) { | |
| m->ter_set( i + 2, j + 3, t_floor_wax ); | |
| } | |
| if( skip1 == 21 || skip2 == 21 ) { | |
| m->ter_set( i - 1, j + 4, t_floor_wax ); | |
| } | |
| if( skip1 == 22 || skip2 == 22 ) { | |
| m->ter_set( i, j + 4, t_floor_wax ); | |
| } | |
| if( skip1 == 23 || skip2 == 23 ) { | |
| m->ter_set( i + 1, j + 4, t_floor_wax ); | |
| } | |
| if( is_center ) { | |
| m->place_items( "hive_center", 90, i - 2, j - 2, i + 2, j + 2, false, turn ); | |
| } else { | |
| m->place_items( "hive", 80, i - 2, j - 2, i + 2, j + 2, false, turn ); | |
| } | |
| } | |
| } | |
| } | |
| if( is_center ) { | |
| m->place_npc( SEEX, SEEY, string_id<npc_template>( "apis" ) ); | |
| } | |
| } | |
| void mapgen_spider_pit( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| // First generate a forest | |
| dat.fill( 4 ); | |
| for( int i = 0; i < 4; i++ ) { | |
| if( dat.t_nesw[i] == "forest" || dat.t_nesw[i] == "forest_water" ) { | |
| dat.dir( i ) += 14; | |
| } else if( dat.t_nesw[i] == "forest_thick" ) { | |
| dat.dir( i ) += 18; | |
| } | |
| } | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| int forest_chance = 0; | |
| int num = 0; | |
| if( j < dat.n_fac ) { | |
| forest_chance += dat.n_fac - j; | |
| num++; | |
| } | |
| if( SEEX * 2 - 1 - i < dat.e_fac ) { | |
| forest_chance += dat.e_fac - ( SEEX * 2 - 1 - i ); | |
| num++; | |
| } | |
| if( SEEY * 2 - 1 - j < dat.s_fac ) { | |
| forest_chance += dat.s_fac - ( SEEX * 2 - 1 - j ); | |
| num++; | |
| } | |
| if( i < dat.w_fac ) { | |
| forest_chance += dat.w_fac - i; | |
| num++; | |
| } | |
| if( num > 0 ) { | |
| forest_chance /= num; | |
| } | |
| int rn = rng( 0, forest_chance ); | |
| if( ( forest_chance > 0 && rn > 13 ) || one_in( 100 - forest_chance ) ) { | |
| m->ter_set( i, j, t_tree ); | |
| } else if( ( forest_chance > 0 && rn > 10 ) || one_in( 100 - forest_chance ) ) { | |
| m->ter_set( i, j, t_tree_young ); | |
| } else if( ( forest_chance > 0 && rn > 9 ) || one_in( 100 - forest_chance ) ) { | |
| m->ter_set( i, j, t_underbrush ); | |
| } else { | |
| m->ter_set( i, j, dat.groundcover() ); | |
| } | |
| } | |
| } | |
| m->place_items( "forest", 60, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| // Next, place webs and sinkholes | |
| for( int i = 0; i < 4; i++ ) { | |
| int x = rng( 3, SEEX * 2 - 4 ), y = rng( 3, SEEY * 2 - 4 ); | |
| if( i == 0 ) { | |
| m->ter_set( x, y, t_slope_down ); | |
| } else { | |
| m->ter_set( x, y, dat.groundcover() ); | |
| mtrap_set( m, x, y, tr_sinkhole ); | |
| } | |
| for( int x1 = x - 3; x1 <= x + 3; x1++ ) { | |
| for( int y1 = y - 3; y1 <= y + 3; y1++ ) { | |
| madd_field( m, x1, y1, fd_web, rng( 2, 3 ) ); | |
| if( m->ter( x1, y1 ) != t_slope_down ) { | |
| m->ter_set( x1, y1, t_dirt ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void mapgen_fungal_bloom( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( one_in( rl_dist( i, j, 12, 12 ) * 4 ) ) { | |
| m->ter_set( i, j, t_marloss ); | |
| } else if( one_in( 10 ) ) { | |
| if( one_in( 3 ) ) { | |
| m->ter_set( i, j, t_tree_fungal ); | |
| } else { | |
| m->ter_set( i, j, t_tree_fungal_young ); | |
| } | |
| } else if( one_in( 5 ) ) { | |
| m->ter_set( i, j, t_shrub_fungal ); | |
| } else if( one_in( 10 ) ) { | |
| m->ter_set( i, j, t_fungus_mound ); | |
| } else { | |
| m->ter_set( i, j, t_fungus ); | |
| } | |
| } | |
| } | |
| square( m, t_fungus, SEEX - 2, SEEY - 2, SEEX + 2, SEEY + 2 ); | |
| m->add_spawn( mon_fungaloid_queen, 1, 12, 12 ); | |
| } | |
| void mapgen_fungal_tower( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( one_in( 8 ) ) { | |
| if( one_in( 3 ) ) { | |
| m->ter_set( i, j, t_tree_fungal ); | |
| } else { | |
| m->ter_set( i, j, t_tree_fungal_young ); | |
| } | |
| } else if( one_in( 10 ) ) { | |
| m->ter_set( i, j, t_fungus_mound ); | |
| } else { | |
| m->ter_set( i, j, t_fungus ); | |
| } | |
| } | |
| } | |
| square( m, t_fungus, SEEX - 2, SEEY - 2, SEEX + 2, SEEY + 2 ); | |
| m->add_spawn( mon_fungaloid_tower, 1, 12, 12 ); | |
| } | |
| void mapgen_fungal_flowers( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( one_in( rl_dist( i, j, 12, 12 ) * 6 ) ) { | |
| m->ter_set( i, j, t_fungus ); | |
| m->furn_set( i, j, f_flower_marloss ); | |
| } else if( one_in( 10 ) ) { | |
| if( one_in( 3 ) ) { | |
| m->ter_set( i, j, t_fungus_mound ); | |
| } else { | |
| m->ter_set( i, j, t_tree_fungal_young ); | |
| } | |
| } else if( one_in( 5 ) ) { | |
| m->ter_set( i, j, t_fungus ); | |
| m->furn_set( i, j, f_flower_fungal ); | |
| } else if( one_in( 10 ) ) { | |
| m->ter_set( i, j, t_shrub_fungal ); | |
| } else { | |
| m->ter_set( i, j, t_fungus ); | |
| } | |
| } | |
| } | |
| square( m, t_fungus, SEEX - 2, SEEY - 2, SEEX + 2, SEEY + 2 ); | |
| m->add_spawn( mon_fungaloid_seeder, 1, 12, 12 ); | |
| } | |
| int terrain_type_to_nesw_array( oter_id terrain_type, bool array[4] ) | |
| { | |
| // count and mark which directions the road goes | |
| const auto &oter( *terrain_type ); | |
| int num_dirs = 0; | |
| for( const auto dir : om_direction::all ) { | |
| num_dirs += ( array[static_cast<int>( dir )] = oter.has_connection( dir ) ); | |
| } | |
| return num_dirs; | |
| } | |
| // perform dist counterclockwise rotations on a nesw or neswx array | |
| template<typename T> | |
| void nesw_array_rotate( T *array, size_t len, size_t dist ) | |
| { | |
| if( len == 4 ) { | |
| while( dist-- ) { | |
| T temp = array[0]; | |
| array[0] = array[1]; | |
| array[1] = array[2]; | |
| array[2] = array[3]; | |
| array[3] = temp; | |
| } | |
| } else { | |
| while( dist-- ) { | |
| // N E S W NE SE SW NW | |
| T temp = array[0]; | |
| array[0] = array[4]; | |
| array[4] = array[1]; | |
| array[1] = array[5]; | |
| array[5] = array[2]; | |
| array[2] = array[6]; | |
| array[6] = array[3]; | |
| array[3] = array[7]; | |
| array[7] = temp; | |
| } | |
| } | |
| } | |
| // take x/y coordinates in a map and rotate them counterclockwise around the center | |
| static void coord_rotate_cw( int &x, int &y, int rot ) | |
| { | |
| for( ; rot--; ) { | |
| int temp = y; | |
| y = x; | |
| x = ( SEEY * 2 - 1 ) - temp; | |
| } | |
| } | |
| static bool compare_neswx( bool *a1, std::initializer_list<int> a2 ) | |
| { | |
| return std::equal( std::begin( a2 ), std::end( a2 ), a1, | |
| []( int a, bool b ) { | |
| return static_cast<bool>( a ) == b; | |
| } ); | |
| } | |
| // mapgen_road replaces previous mapgen_road_straight _end _curved _tee _four_way | |
| void mapgen_road( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| // start by filling the whole map with grass/dirt/etc | |
| dat.fill_groundcover(); | |
| // which and how many neighbors have sidewalks? | |
| bool sidewalks_neswx[8] = {}; | |
| int neighbor_sidewalks = 0; | |
| for( int dir = 0; dir < 8; dir++ ) { // N E S W NE SE SW NW | |
| sidewalks_neswx[dir] = dat.t_nesw[dir]->has_flag( has_sidewalk ); | |
| neighbor_sidewalks += sidewalks_neswx[dir]; | |
| } | |
| // which of the cardinal directions get roads? | |
| bool roads_nesw[4] = {}; | |
| int num_dirs = terrain_type_to_nesw_array( terrain_type, roads_nesw ); | |
| // if this is a dead end, extend past the middle of the tile | |
| int dead_end_extension = ( num_dirs == 1 ? 8 : 0 ); | |
| // which way should our roads curve, based on neighbor roads? | |
| int curvedir_nesw[4] = {}; | |
| for( int dir = 0; dir < 4; dir++ ) { // N E S W | |
| if( !roads_nesw[dir] || dat.t_nesw[dir]->get_type_id().str() != "road" ) { | |
| continue; | |
| } | |
| // n_* contain details about the neighbor being considered | |
| bool n_roads_nesw[4] = {}; | |
| // TODO: figure out how to call this function without creating a new oter_id object | |
| int n_num_dirs = terrain_type_to_nesw_array( dat.t_nesw[dir], n_roads_nesw ); | |
| // if 2-way neighbor has a road facing us | |
| if( n_num_dirs == 2 && n_roads_nesw[( dir + 2 ) % 4] ) { | |
| // curve towards the direction the neighbor turns | |
| if( n_roads_nesw[( dir - 1 + 4 ) % 4] ) { | |
| curvedir_nesw[dir]--; // our road curves counterclockwise | |
| } | |
| if( n_roads_nesw[( dir + 1 ) % 4] ) { | |
| curvedir_nesw[dir]++; // our road curves clockwise | |
| } | |
| } | |
| } | |
| // calculate how far to rotate the map so we can work with just one orientation | |
| // also keep track of diagonal roads and plazas | |
| int rot = 0; | |
| bool diag = false; | |
| int plaza_dir = -1; | |
| bool fourways_neswx[8] = {}; | |
| // TODO: reduce amount of logical/conditional constructs here | |
| // TODO: make plazas include adjacent tees | |
| switch( num_dirs ) { | |
| case 4: // 4-way intersection | |
| for( int dir = 0; dir < 8; dir++ ) { | |
| fourways_neswx[dir] = ( dat.t_nesw[dir].id() == "road_nesw" || | |
| dat.t_nesw[dir].id() == "road_nesw_manhole" ); | |
| } | |
| // is this the middle, or which side or corner, of a plaza? | |
| plaza_dir = compare_neswx( fourways_neswx, {1, 1, 1, 1, 1, 1, 1, 1} ) ? 8 : | |
| compare_neswx( fourways_neswx, {0, 1, 1, 0, 0, 1, 0, 0} ) ? 7 : | |
| compare_neswx( fourways_neswx, {1, 1, 0, 0, 1, 0, 0, 0} ) ? 6 : | |
| compare_neswx( fourways_neswx, {1, 0, 0, 1, 0, 0, 0, 1} ) ? 5 : | |
| compare_neswx( fourways_neswx, {0, 0, 1, 1, 0, 0, 1, 0} ) ? 4 : | |
| compare_neswx( fourways_neswx, {1, 1, 1, 0, 1, 1, 0, 0} ) ? 3 : | |
| compare_neswx( fourways_neswx, {1, 1, 0, 1, 1, 0, 0, 1} ) ? 2 : | |
| compare_neswx( fourways_neswx, {1, 0, 1, 1, 0, 0, 1, 1} ) ? 1 : | |
| compare_neswx( fourways_neswx, {0, 1, 1, 1, 0, 1, 1, 0} ) ? 0 : | |
| -1; | |
| if( plaza_dir > -1 ) { | |
| rot = plaza_dir % 4; | |
| } | |
| break; | |
| case 3: // tee | |
| if( !roads_nesw[0] ) { | |
| rot = 2; // E/S/W, rotate 180 degrees | |
| break; | |
| } | |
| if( !roads_nesw[1] ) { | |
| rot = 3; // N/S/W, rotate 270 degrees | |
| break; | |
| } | |
| if( !roads_nesw[3] ) { | |
| rot = 1; // N/E/S, rotate 90 degrees | |
| break; | |
| } | |
| break; // N/E/W, don't rotate | |
| case 2: // straight or diagonal | |
| if( roads_nesw[1] && roads_nesw[3] ) { | |
| rot = 1; // E/W, rotate 90 degrees | |
| break; | |
| } | |
| if( roads_nesw[1] && roads_nesw[2] ) { | |
| rot = 1; // E/S, rotate 90 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( roads_nesw[2] && roads_nesw[3] ) { | |
| rot = 2; // S/W, rotate 180 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( roads_nesw[3] && roads_nesw[0] ) { | |
| rot = 3; // W/N, rotate 270 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( roads_nesw[0] && roads_nesw[1] ) { | |
| diag = true; // N/E, don't rotate | |
| break; | |
| } | |
| break; // N/S, don't rotate | |
| case 1: // dead end | |
| if( roads_nesw[1] ) { | |
| rot = 1; // E, rotate 90 degrees | |
| break; | |
| } | |
| if( roads_nesw[2] ) { | |
| rot = 2; // S, rotate 180 degrees | |
| break; | |
| } | |
| if( roads_nesw[3] ) { | |
| rot = 3; // W, rotate 270 degrees | |
| break; | |
| } | |
| break; // N, don't rotate | |
| } | |
| // rotate the arrays left by rot steps | |
| nesw_array_rotate<bool>( sidewalks_neswx, 8, rot * 2 ); | |
| nesw_array_rotate<bool>( roads_nesw, 4, rot ); | |
| nesw_array_rotate<int> ( curvedir_nesw, 4, rot ); | |
| // now we have only these shapes: ' | '- -'- -|- | |
| if( diag ) { // diagonal roads get drawn differently from all other types | |
| // draw sidewalks if a S/SW/W neighbor has_sidewalk | |
| if( sidewalks_neswx[4] || sidewalks_neswx[5] || sidewalks_neswx[6] ) { | |
| for( int y = 0; y < SEEY * 2; y++ ) { | |
| for( int x = 0; x < SEEX * 2; x++ ) { | |
| if( x > y - 4 && ( x < 4 || y > SEEY * 2 - 5 || y >= x ) ) { | |
| m->ter_set( x, y, t_sidewalk ); | |
| } | |
| } | |
| } | |
| } | |
| // draw diagonal road | |
| for( int y = 0; y < SEEY * 2; y++ ) { | |
| for( int x = 0; x < SEEX * 2; x++ ) { | |
| if( x > y && // definitely only draw in the upper right half of the map | |
| ( ( x > 3 && y < ( SEEY * 2 - 4 ) ) || // middle, for both corners and diagonals | |
| ( x < 4 && curvedir_nesw[0] < 0 ) || // diagonal heading northwest | |
| ( y > ( SEEY * 2 - 5 ) && curvedir_nesw[1] > 0 ) ) ) { // diagonal heading southeast | |
| if( ( x + rot / 2 ) % 4 && ( x - y == SEEX - 1 + ( 1 - ( rot / 2 ) ) || | |
| x - y == SEEX + ( 1 - ( rot / 2 ) ) ) ) { | |
| m->ter_set( x, y, t_pavement_y ); | |
| } else { | |
| m->ter_set( x, y, t_pavement ); | |
| } | |
| } | |
| } | |
| } | |
| } else { // normal road drawing | |
| bool cul_de_sac = false; | |
| // dead ends become cul de sacs, 1/3 of the time, if a neighbor has_sidewalk | |
| if( num_dirs == 1 && one_in( 3 ) && neighbor_sidewalks ) { | |
| cul_de_sac = true; | |
| fill_background( m, t_sidewalk ); | |
| } | |
| // draw normal sidewalks | |
| for( int dir = 0; dir < 4; dir++ ) { | |
| if( roads_nesw[dir] ) { | |
| // sidewalk west of north road, etc | |
| if( sidewalks_neswx[( dir + 3 ) % 4 ] || // has_sidewalk west? | |
| sidewalks_neswx[( dir + 3 ) % 4 + 4 ] || // has_sidewalk northwest? | |
| sidewalks_neswx[ dir ] ) { // has_sidewalk north? | |
| int x1 = 0; | |
| int y1 = 0; | |
| int x2 = 3; | |
| int y2 = SEEY - 1 + dead_end_extension; | |
| coord_rotate_cw( x1, y1, dir ); | |
| coord_rotate_cw( x2, y2, dir ); | |
| square( m, t_sidewalk, x1, y1, x2, y2 ); | |
| } | |
| // sidewalk east of north road, etc | |
| if( sidewalks_neswx[( dir + 1 ) % 4 ] || // has_sidewalk east? | |
| sidewalks_neswx[ dir + 4 ] || // has_sidewalk northeast? | |
| sidewalks_neswx[ dir ] ) { // has_sidewalk north? | |
| int x1 = SEEX * 2 - 5; | |
| int y1 = 0; | |
| int x2 = SEEX * 2 - 1; | |
| int y2 = SEEY - 1 + dead_end_extension; | |
| coord_rotate_cw( x1, y1, dir ); | |
| coord_rotate_cw( x2, y2, dir ); | |
| square( m, t_sidewalk, x1, y1, x2, y2 ); | |
| } | |
| } | |
| } | |
| //draw dead end sidewalk | |
| if( dead_end_extension > 0 && sidewalks_neswx[ 2 ] ) { | |
| square( m, t_sidewalk, 0, SEEY + dead_end_extension, SEEX * 2 - 1, SEEY + dead_end_extension + 4 ); | |
| } | |
| // draw 16-wide pavement from the middle to the edge in each road direction | |
| // also corner pieces to curve towards diagonal neighbors | |
| for( int dir = 0; dir < 4; dir++ ) { | |
| if( roads_nesw[dir] ) { | |
| int x1 = 4; | |
| int y1 = 0; | |
| int x2 = SEEX * 2 - 1 - 4; | |
| int y2 = SEEY - 1 + dead_end_extension; | |
| coord_rotate_cw( x1, y1, dir ); | |
| coord_rotate_cw( x2, y2, dir ); | |
| square( m, t_pavement, x1, y1, x2, y2 ); | |
| if( curvedir_nesw[dir] != 0 ) { | |
| for( int x = 1; x < 4; x++ ) { | |
| for( int y = 0; y < x; y++ ) { | |
| int ty = y, tx = ( curvedir_nesw[dir] == -1 ? x : SEEX * 2 - 1 - x ); | |
| coord_rotate_cw( tx, ty, dir ); | |
| m->ter_set( tx, ty, t_pavement ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // draw yellow dots on the pavement | |
| for( int dir = 0; dir < 4; dir++ ) { | |
| if( roads_nesw[dir] ) { | |
| int max_y = SEEY; | |
| if( num_dirs == 4 || ( num_dirs == 3 && dir == 0 ) ) { | |
| max_y = 4; // dots don't extend into some intersections | |
| } | |
| for( int x = SEEX - 1; x <= SEEX; x++ ) { | |
| for( int y = 0; y < max_y; y++ ) { | |
| if( ( y + ( ( dir + rot ) / 2 % 2 ) ) % 4 ) { | |
| int xn = x; | |
| int yn = y; | |
| coord_rotate_cw( xn, yn, dir ); | |
| m->ter_set( xn, yn, t_pavement_y ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // draw round pavement for cul de sac late, to overdraw the yellow dots | |
| if( cul_de_sac ) { | |
| circle( m, t_pavement, double( SEEX ) - 0.5, double( SEEY ) - 0.5, 11.0 ); | |
| } | |
| // overwrite part of intersection with rotary/plaza | |
| if( plaza_dir > -1 ) { | |
| if( plaza_dir == 8 ) { // plaza center | |
| fill_background( m, t_sidewalk ); | |
| // TODO: something interesting here | |
| } else if( plaza_dir < 4 ) { // plaza side | |
| square( m, t_pavement, 0, SEEY - 10, SEEX * 2 - 1, SEEY - 1 ); | |
| square( m, t_sidewalk, 0, SEEY - 2, SEEX * 2 - 1, SEEY * 2 - 1 ); | |
| if( one_in( 3 ) ) { | |
| line( m, t_tree_young, 1, SEEY, SEEX * 2 - 2, SEEY ); | |
| } | |
| if( one_in( 3 ) ) { | |
| line_furn( m, f_bench, 2, SEEY + 2, 5, SEEY + 2 ); | |
| line_furn( m, f_bench, 10, SEEY + 2, 13, SEEY + 2 ); | |
| line_furn( m, f_bench, 18, SEEY + 2, 21, SEEY + 2 ); | |
| } | |
| } else { // plaza corner | |
| circle( m, t_pavement, 0, SEEY * 2 - 1, 21 ); | |
| circle( m, t_sidewalk, 0, SEEY * 2 - 1, 13 ); | |
| if( one_in( 3 ) ) { | |
| circle( m, t_tree_young, 0, SEEY * 2 - 1, 11 ); | |
| circle( m, t_sidewalk, 0, SEEY * 2 - 1, 10 ); | |
| } | |
| if( one_in( 3 ) ) { | |
| circle( m, t_water_sh, 4, SEEY * 2 - 5, 3 ); | |
| } | |
| } | |
| } | |
| } | |
| // spawn some vehicles | |
| if( plaza_dir != 8 ) { | |
| vspawn_id( neighbor_sidewalks ? "default_city" : "default_country" ).obj().apply( | |
| *m, | |
| num_dirs == 4 ? "road_four_way" : | |
| num_dirs == 3 ? "road_tee" : | |
| num_dirs == 1 ? "road_end" : | |
| diag ? "road_curved" : | |
| "road_straight" | |
| ); | |
| } | |
| // spawn some monsters | |
| if( neighbor_sidewalks ) { | |
| m->place_spawns( mongroup_id( "GROUP_ZOMBIE" ), 2, 0, 0, SEEX * 2 - 1, SEEX * 2 - 1, density ); | |
| // 1 per 10 overmaps | |
| if( one_in( 10000 ) ) { | |
| m->add_spawn( mon_zombie_jackson, 1, SEEX, SEEY ); | |
| } | |
| } | |
| // add some items | |
| bool plaza = ( plaza_dir > -1 ); | |
| m->place_items( plaza ? "trash" : "road", 5, 0, 0, SEEX * 2 - 1, SEEX * 2 - 1, plaza, turn ); | |
| // add a manhole if appropriate | |
| if( terrain_type == "road_nesw_manhole" ) { | |
| m->ter_set( rng( 6, SEEX * 2 - 6 ), rng( 6, SEEX * 2 - 6 ), t_manhole_cover ); | |
| } | |
| // finally, unrotate the map | |
| m->rotate( rot ); | |
| } | |
| /////////////////// | |
| void mapgen_subway( map *m, oter_id terrain_type, mapgendata dat, const time_point &, float ) | |
| { | |
| // start by filling the whole map with grass/dirt/etc | |
| dat.fill_groundcover(); | |
| // which of the cardinal directions get subway? | |
| bool subway_nesw[4] = {}; | |
| int num_dirs = terrain_type_to_nesw_array( terrain_type, subway_nesw ); | |
| for( int dir = 0; dir < 4; dir++ ) { // N E S W | |
| if( dat.t_nesw[dir]->has_flag( subway_connection ) && !subway_nesw[dir] ) { | |
| num_dirs++; | |
| subway_nesw[dir] = true; | |
| } | |
| } | |
| // which way should our subway curve, based on neighbor subway? | |
| int curvedir_nesw[4] = {}; | |
| for( int dir = 0; dir < 4; dir++ ) { // N E S W | |
| if( !subway_nesw[dir] ) { | |
| continue; | |
| } | |
| if( dat.t_nesw[dir]->get_type_id().str() != "subway" && | |
| !dat.t_nesw[dir]->has_flag( subway_connection ) ) { | |
| continue; | |
| } | |
| // n_* contain details about the neighbor being considered | |
| bool n_subway_nesw[4] = {}; | |
| // TODO: figure out how to call this function without creating a new oter_id object | |
| int n_num_dirs = terrain_type_to_nesw_array( dat.t_nesw[dir], n_subway_nesw ); | |
| for( int dir = 0; dir < 4; dir++ ) { | |
| if( dat.t_nesw[dir]->has_flag( subway_connection ) && !n_subway_nesw[dir] ) { | |
| n_num_dirs++; | |
| n_subway_nesw[dir] = true; | |
| } | |
| } | |
| // if 2-way neighbor has a subway facing us | |
| if( n_num_dirs == 2 && n_subway_nesw[( dir + 2 ) % 4] ) { | |
| // curve towards the direction the neighbor turns | |
| if( n_subway_nesw[( dir - 1 + 4 ) % 4] ) { | |
| curvedir_nesw[dir]--; // our subway curves counterclockwise | |
| } | |
| if( n_subway_nesw[( dir + 1 ) % 4] ) { | |
| curvedir_nesw[dir]++; // our subway curves clockwise | |
| } | |
| } | |
| } | |
| // calculate how far to rotate the map so we can work with just one orientation | |
| // also keep track of diagonal subway | |
| int rot = 0; | |
| bool diag = false; | |
| // TODO: reduce amount of logical/conditional constructs here | |
| switch( num_dirs ) { | |
| case 4: // 4-way intersection | |
| break; | |
| case 3: // tee | |
| if( !subway_nesw[0] ) { | |
| rot = 2; // E/S/W, rotate 180 degrees | |
| break; | |
| } | |
| if( !subway_nesw[1] ) { | |
| rot = 3; // N/S/W, rotate 270 degrees | |
| break; | |
| } | |
| if( !subway_nesw[3] ) { | |
| rot = 1; // N/E/S, rotate 90 degrees | |
| break; | |
| } | |
| break; // N/E/W, don't rotate | |
| case 2: // straight or diagonal | |
| if( subway_nesw[1] && subway_nesw[3] ) { | |
| rot = 1; // E/W, rotate 90 degrees | |
| break; | |
| } | |
| if( subway_nesw[1] && subway_nesw[2] ) { | |
| rot = 1; // E/S, rotate 90 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( subway_nesw[2] && subway_nesw[3] ) { | |
| rot = 2; // S/W, rotate 180 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( subway_nesw[3] && subway_nesw[0] ) { | |
| rot = 3; // W/N, rotate 270 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( subway_nesw[0] && subway_nesw[1] ) { | |
| diag = true; // N/E, don't rotate | |
| break; | |
| } | |
| break; // N/S, don't rotate | |
| case 1: // dead end | |
| if( subway_nesw[1] ) { | |
| rot = 1; // E, rotate 90 degrees | |
| break; | |
| } | |
| if( subway_nesw[2] ) { | |
| rot = 2; // S, rotate 180 degrees | |
| break; | |
| } | |
| if( subway_nesw[3] ) { | |
| rot = 3; // W, rotate 270 degrees | |
| break; | |
| } | |
| break; // N, don't rotate | |
| } | |
| // rotate the arrays left by rot steps | |
| nesw_array_rotate<bool>( subway_nesw, 4, rot ); | |
| nesw_array_rotate<int> ( curvedir_nesw, 4, rot ); | |
| // now we have only these shapes: ' | '- -'- -|- | |
| switch( num_dirs ) { | |
| case 4: // 4-way intersection | |
| mapf::formatted_set_simple( m, 0, 0, | |
| "..^/D^^/D^....^D/^^D/^..\n" | |
| ".^/DX^/DX......XD/^XD/^.\n" | |
| "^/D^X/D^X......X^D/X^D/^\n" | |
| "/D^^XD^.X......X.^DX^^D/\n" | |
| "DXXDDXXXXXXXXXXXXXXDDXXD\n" | |
| "^^/DX^^^X^^^^^^X^^^XD/^^\n" | |
| "^/D^X^^^X^^^^^^X^^^X^D/^\n" | |
| "/D^^X^^^X^^^^^^X^^^X^^D/\n" | |
| "DXXXXXXXXXXXXXXXXXXXXXXD\n" | |
| "^^^^X^^^X^^^^^^X^^^X^^^^\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "^^^^X^^^X^^^^^^X^^^X^^^^\n" | |
| "DXXXXXXXXXXXXXXXXXXXXXXD\n" | |
| "/D^^X^^^X^^^^^^X^^^X^^D/\n" | |
| "^/D^X^^^X^^^^^^X^^^X^D/^\n" | |
| "^^/DX^^^X^^^^^^X^^^XD/^^\n" | |
| "DXXDDXXXXXXXXXXXXXXDDDDD\n" | |
| "/D^^XD^.X......X.^DX^^D/\n" | |
| "^/D^X/D^X......X^D/X^D/^\n" | |
| ".^/DX^/DX......XD/^XD/^.\n" | |
| "..^/D^^/D^....^D/^^D/^..", | |
| mapf::ter_bind( ". # ^ / D X", | |
| t_rock_floor, | |
| t_rock, | |
| t_railroad_rubble, | |
| t_railroad_tie_d, | |
| t_railroad_track_d, | |
| t_railroad_track ), | |
| mapf::furn_bind( ". # ^ / D X", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| break; | |
| case 3: // tee | |
| mapf::formatted_set_simple( m, 0, 0, | |
| "..^/D^^/D^...^/D^^/D^...\n" | |
| ".^/D^^/D^...^/D^^/D^....\n" | |
| "^/D^^/D^...^/D^^/D^.....\n" | |
| "/D^^/D^^^^^/D^^/D^^^^^^^\n" | |
| "DXXXDXXXXXXDXXXDXXXXXXXX\n" | |
| "^^/D^^^^^^^^^/D^^^^^^^^^\n" | |
| "^/D^^^^^^^^^/D^^^^^^^^^^\n" | |
| "/D^^^^^^^^^/D^^^^^^^^^^^\n" | |
| "DXXXXXXDXXXDXXXXXXXXXXXX\n" | |
| "^^^^^/D^^/D^^^^^^^^^^^^^\n" | |
| "...^/D^^/D^.............\n" | |
| "..^/D^^/D^..............\n" | |
| ".^/D^^/D^...............\n" | |
| "^/D^^/D^................\n" | |
| "/D^^/D^^^^|^^|^^|^^|^^|^\n" | |
| "DXXXDXXXXXxXXxXXxXXxXXxX\n" | |
| "^^/D^^^^^^|^^|^^|^^|^^|^\n" | |
| "^/D^^^^^^^|^^|^^|^^|^^|^\n" | |
| "/D^^^^^^^^|^^|^^|^^|^^|^\n" | |
| "DXXXXXXXXXxXXxXXxXXxXXxX\n" | |
| "^^^^^^^^^^|^^|^^|^^|^^|^\n" | |
| "........................\n" | |
| "........................\n" | |
| "........................", | |
| mapf::ter_bind( ". # ^ | X x / D", | |
| t_rock_floor, | |
| t_rock, | |
| t_railroad_rubble, | |
| t_railroad_tie, | |
| t_railroad_track, | |
| t_railroad_track_on_tie, | |
| t_railroad_tie_d, | |
| t_railroad_track_d ), | |
| mapf::furn_bind( ". # ^ | X x / D", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| break; | |
| case 2: // straight or diagonal | |
| if( diag ) { // diagonal subway get drawn differently from all other types | |
| mapf::formatted_set_simple( m, 0, 0, | |
| "...^DD^^DD^...^DD^^DD^..\n" | |
| "....^DD^^DD^...^DD^^DD^.\n" | |
| ".....^DD^^DD^...^DD^^DD^\n" | |
| "......^DD^^DD^...^DD^^DD\n" | |
| ".......^DD^^DD^...^DD^^D\n" | |
| "#.......^DD^^DD^...^DD^^\n" | |
| "##.......^DD^^DD^...^DD^\n" | |
| "###.......^DD^^DD^...^DD\n" | |
| "####.......^DD^^DD^...^D\n" | |
| "#####.......^DD^^DD^...^\n" | |
| "######.......^DD^^DD^...\n" | |
| "#######.......^DD^^DD^..\n" | |
| "########.......^DD^^DD^.\n" | |
| "#########.......^DD^^DD^\n" | |
| "##########.......^DD^^DD\n" | |
| "###########.......^DD^^D\n" | |
| "############.......^DD^^\n" | |
| "#############.......^DD^\n" | |
| "##############.......^DD\n" | |
| "###############.......^D\n" | |
| "################.......^\n" | |
| "#################.......\n" | |
| "##################......\n" | |
| "###################.....", | |
| mapf::ter_bind( ". # ^ D", | |
| t_rock_floor, | |
| t_rock, | |
| t_railroad_rubble, | |
| t_railroad_track_d ), | |
| mapf::furn_bind( ". # ^ D", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| } else { // normal subway drawing | |
| mapf::formatted_set_simple( m, 0, 0, | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...", | |
| mapf::ter_bind( ". # ^ - X x", | |
| t_rock_floor, | |
| t_rock, | |
| t_railroad_rubble, | |
| t_railroad_tie, | |
| t_railroad_track, | |
| t_railroad_track_on_tie ), | |
| mapf::furn_bind( ". # ^ - X x", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| } | |
| break; | |
| case 1: // dead end | |
| mapf::formatted_set_simple( m, 0, 0, | |
| "...^X^^^X^..../D^^/D^...\n" | |
| "...-x---x-.../DX^/DX^...\n" | |
| "...^X^^^X^../D^X/D^X^...\n" | |
| "...^X^^^X^./D.^XD^^X^...\n" | |
| "...^X^^^X^/D../D^^^X^...\n" | |
| "...^X^^^X/D../DX^^^X^...\n" | |
| "...^X^^^XD../D^X^^^X^...\n" | |
| "...^X^^/D^./D.-x---x-...\n" | |
| "...^X^/DX^/D..^X^^^X^...\n" | |
| "...^X/D^X/D...^X^^^X^...\n" | |
| "...^XD^^XD....-x---x-...\n" | |
| "...^D^^^D^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...-x---x-....-x---x-...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^X^^^X^....^X^^^X^...\n" | |
| "...^S^^^S^....^S^^^S^...\n" | |
| "...^^^^^^^....^^^^^^^...\n" | |
| "#......................#\n" | |
| "##....................##\n" | |
| "########################", | |
| mapf::ter_bind( ". # S ^ - / D X x", | |
| t_rock_floor, | |
| t_rock, | |
| t_buffer_stop, | |
| t_railroad_rubble, | |
| t_railroad_tie, | |
| t_railroad_tie_d, | |
| t_railroad_track_d, | |
| t_railroad_track, | |
| t_railroad_track_on_tie ), | |
| mapf::furn_bind( ". # S ^ - / D X x", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| VehicleSpawn::apply( vspawn_id( "default_subway_deadend" ), *m, "subway" ); | |
| break; | |
| } | |
| // finally, unrotate the map | |
| m->rotate( rot ); | |
| } | |
| void mapgen_sewer_straight( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float ) | |
| { | |
| ( void )dat; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( i < SEEX - 2 || i > SEEX + 1 ) { | |
| m->ter_set( i, j, t_rock ); | |
| } else { | |
| m->ter_set( i, j, t_sewage ); | |
| } | |
| } | |
| } | |
| m->place_items( "sewer", 10, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| if( terrain_type == "sewer_ew" ) { | |
| m->rotate( 1 ); | |
| } | |
| } | |
| void mapgen_sewer_curved( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float ) | |
| { | |
| ( void )dat; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( i > SEEX + 1 && j < SEEY - 2 ) || i < SEEX - 2 || j > SEEY + 1 ) { | |
| m->ter_set( i, j, t_rock ); | |
| } else { | |
| m->ter_set( i, j, t_sewage ); | |
| } | |
| } | |
| } | |
| m->place_items( "sewer", 18, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| if( terrain_type == "sewer_es" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "sewer_sw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "sewer_wn" ) { | |
| m->rotate( 3 ); | |
| } | |
| } | |
| void mapgen_sewer_tee( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, float ) | |
| { | |
| ( void )dat; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( i < SEEX - 2 || ( i > SEEX + 1 && ( j < SEEY - 2 || j > SEEY + 1 ) ) ) { | |
| m->ter_set( i, j, t_rock ); | |
| } else { | |
| m->ter_set( i, j, t_sewage ); | |
| } | |
| } | |
| } | |
| m->place_items( "sewer", 23, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| if( terrain_type == "sewer_esw" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "sewer_nsw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "sewer_new" ) { | |
| m->rotate( 3 ); | |
| } | |
| } | |
| void mapgen_sewer_four_way( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| ( void )dat; | |
| int rn = rng( 0, 3 ); | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( i < SEEX - 2 || i > SEEX + 1 ) && ( j < SEEY - 2 || j > SEEY + 1 ) ) { | |
| m->ter_set( i, j, t_rock ); | |
| } else { | |
| m->ter_set( i, j, t_sewage ); | |
| } | |
| if( rn == 0 && ( trig_dist( i, j, SEEX - 1, SEEY - 1 ) <= 6 || | |
| trig_dist( i, j, SEEX - 1, SEEY ) <= 6 || | |
| trig_dist( i, j, SEEX, SEEY - 1 ) <= 6 || | |
| trig_dist( i, j, SEEX, SEEY ) <= 6 ) ) { | |
| m->ter_set( i, j, t_sewage ); | |
| } | |
| if( rn == 0 && ( i == SEEX - 1 || i == SEEX ) && ( j == SEEY - 1 || j == SEEY ) ) { | |
| m->ter_set( i, j, t_grate ); | |
| } | |
| } | |
| } | |
| m->place_items( "sewer", 28, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| } | |
| /////////////////// | |
| void mapgen_bridge( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, float ) | |
| { | |
| const auto is_river = [&]( const om_direction::type dir ) { | |
| return dat.t_nesw[static_cast<int>( om_direction::add( dir, | |
| terrain_type->get_dir() ) )]->is_river(); | |
| }; | |
| const bool river_west = is_river( om_direction::type::west ); | |
| const bool river_east = is_river( om_direction::type::east ); | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( i < 2 ) { | |
| m->ter_set( i, j, river_west ? t_water_moving_dp : grass_or_dirt() ); | |
| } else if( i >= SEEX * 2 - 2 ) { | |
| m->ter_set( i, j, river_east ? t_water_moving_dp : grass_or_dirt() ); | |
| } else if( i == 2 || i == SEEX * 2 - 3 ) { | |
| m->ter_set( i, j, t_guardrail_bg_dp ); | |
| } else if( i == 3 || i == SEEX * 2 - 4 ) { | |
| m->ter_set( i, j, t_sidewalk_bg_dp ); | |
| } else { | |
| if( ( i == SEEX - 1 || i == SEEX ) && j % 4 != 0 ) { | |
| m->ter_set( i, j, t_pavement_y_bg_dp ); | |
| } else { | |
| m->ter_set( i, j, t_pavement_bg_dp ); | |
| } | |
| } | |
| } | |
| } | |
| // spawn regular road out of fuel vehicles | |
| VehicleSpawn::apply( vspawn_id( "default_bridge" ), *m, "bridge" ); | |
| m->rotate( static_cast<int>( terrain_type->get_dir() ) ); | |
| m->place_items( "road", 5, 0, 0, SEEX * 2 - 1, SEEX * 2 - 1, false, turn ); | |
| } | |
| void mapgen_highway( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, float ) | |
| { | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( i < 3 || i >= SEEX * 2 - 3 ) { | |
| m->ter_set( i, j, dat.groundcover() ); | |
| } else if( i == 3 || i == SEEX * 2 - 4 ) { | |
| m->ter_set( i, j, t_railing ); | |
| } else { | |
| if( ( i == SEEX - 1 || i == SEEX ) && j % 4 != 0 ) { | |
| m->ter_set( i, j, t_pavement_y ); | |
| } else { | |
| m->ter_set( i, j, t_pavement ); | |
| } | |
| } | |
| } | |
| } | |
| // spawn regular road out of fuel vehicles | |
| VehicleSpawn::apply( vspawn_id( "default_highway" ), *m, "highway" ); | |
| if( terrain_type == "hiway_ew" ) { | |
| m->rotate( 1 ); | |
| } | |
| m->place_items( "road", 8, 0, 0, SEEX * 2 - 1, SEEX * 2 - 1, false, turn ); | |
| } | |
| // mapgen_railroad | |
| // TODO: Refactor and combine with other similiar functions (e.g. road). | |
| void mapgen_railroad( map *m, oter_id terrain_type, mapgendata dat, const time_point &, float ) | |
| { | |
| // start by filling the whole map with grass/dirt/etc | |
| dat.fill_groundcover(); | |
| // which of the cardinal directions get railroads? | |
| bool railroads_nesw[4] = {}; | |
| int num_dirs = terrain_type_to_nesw_array( terrain_type, railroads_nesw ); | |
| // which way should our railroads curve, based on neighbor railroads? | |
| int curvedir_nesw[4] = {}; | |
| for( int dir = 0; dir < 4; dir++ ) { // N E S W | |
| if( !railroads_nesw[dir] || dat.t_nesw[dir]->get_type_id().str() != "railroad" ) { | |
| continue; | |
| } | |
| // n_* contain details about the neighbor being considered | |
| bool n_railroads_nesw[4] = {}; | |
| // TODO: figure out how to call this function without creating a new oter_id object | |
| int n_num_dirs = terrain_type_to_nesw_array( dat.t_nesw[dir], n_railroads_nesw ); | |
| // if 2-way neighbor has a railroad facing us | |
| if( n_num_dirs == 2 && n_railroads_nesw[( dir + 2 ) % 4] ) { | |
| // curve towards the direction the neighbor turns | |
| if( n_railroads_nesw[( dir - 1 + 4 ) % 4] ) { | |
| curvedir_nesw[dir]--; // our railroad curves counterclockwise | |
| } | |
| if( n_railroads_nesw[( dir + 1 ) % 4] ) { | |
| curvedir_nesw[dir]++; // our railroad curves clockwise | |
| } | |
| } | |
| } | |
| // calculate how far to rotate the map so we can work with just one orientation | |
| // also keep track of diagonal railroads | |
| int rot = 0; | |
| bool diag = false; | |
| // TODO: reduce amount of logical/conditional constructs here | |
| switch( num_dirs ) { | |
| case 4: // 4-way intersection | |
| break; | |
| case 3: // tee | |
| if( !railroads_nesw[0] ) { | |
| rot = 2; // E/S/W, rotate 180 degrees | |
| break; | |
| } | |
| if( !railroads_nesw[1] ) { | |
| rot = 3; // N/S/W, rotate 270 degrees | |
| break; | |
| } | |
| if( !railroads_nesw[3] ) { | |
| rot = 1; // N/E/S, rotate 90 degrees | |
| break; | |
| } | |
| break; // N/E/W, don't rotate | |
| case 2: // straight or diagonal | |
| if( railroads_nesw[1] && railroads_nesw[3] ) { | |
| rot = 1; // E/W, rotate 90 degrees | |
| break; | |
| } | |
| if( railroads_nesw[1] && railroads_nesw[2] ) { | |
| rot = 1; // E/S, rotate 90 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( railroads_nesw[2] && railroads_nesw[3] ) { | |
| rot = 2; // S/W, rotate 180 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( railroads_nesw[3] && railroads_nesw[0] ) { | |
| rot = 3; // W/N, rotate 270 degrees | |
| diag = true; | |
| break; | |
| } | |
| if( railroads_nesw[0] && railroads_nesw[1] ) { | |
| diag = true; // N/E, don't rotate | |
| break; | |
| } | |
| break; // N/S, don't rotate | |
| case 1: // dead end | |
| if( railroads_nesw[1] ) { | |
| rot = 1; // E, rotate 90 degrees | |
| break; | |
| } | |
| if( railroads_nesw[2] ) { | |
| rot = 2; // S, rotate 180 degrees | |
| break; | |
| } | |
| if( railroads_nesw[3] ) { | |
| rot = 3; // W, rotate 270 degrees | |
| break; | |
| } | |
| break; // N, don't rotate | |
| } | |
| // rotate the arrays left by rot steps | |
| nesw_array_rotate<bool>( railroads_nesw, 4, rot ); | |
| nesw_array_rotate<int> ( curvedir_nesw, 4, rot ); | |
| // now we have only these shapes: ' | '- -'- -|- | |
| switch( num_dirs ) { | |
| case 4: // 4-way intersection | |
| mapf::formatted_set_simple( m, 0, 0, "\ | |
| .DD^^DD^........^DD^^DD.\n\ | |
| DD^^DD^..........^DD^^DD\n\ | |
| D^^DD^............^DD^^D\n\ | |
| ^^DD^..............^DD^^\n\ | |
| ^DD^................^DD^\n\ | |
| DD^..................^DD\n\ | |
| D^....................^D\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| D^....................^D\n\ | |
| DD^..................^DD\n\ | |
| ^DD^................^DD^\n\ | |
| ^^DD^..............^DD^^\n\ | |
| D^^DD^............^DD^^D\n\ | |
| DD^^DD^..........^DD^^DD\n\ | |
| .DD^^DD^........^DD^^DD.", | |
| mapf::ter_bind( ". ^ D", | |
| t_dirt, | |
| t_railroad_rubble, | |
| t_railroad_track_d ), | |
| mapf::furn_bind( ". ^ D", | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| break; | |
| case 3: // tee | |
| mapf::formatted_set_simple( m, 0, 0, "\ | |
| .DD^^DD^........^DD^^DD.\n\ | |
| DD^^DD^..........^DD^^DD\n\ | |
| D^^DD^............^DD^^D\n\ | |
| ^^DD^..............^DD^^\n\ | |
| ^DD^................^DD^\n\ | |
| DD^..................^DD\n\ | |
| D^....................^D\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ^|^^|^^|^^|^^|^^|^^|^^|^\n\ | |
| XxXXxXXxXXxXXxXXxXXxXXxX\n\ | |
| ^|^^|^^|^^|^^|^^|^^|^^|^\n\ | |
| ^|^^|^^|^^|^^|^^|^^|^^|^\n\ | |
| ^|^^|^^|^^|^^|^^|^^|^^|^\n\ | |
| XxXXxXXxXXxXXxXXxXXxXXxX\n\ | |
| ^|^^|^^|^^|^^|^^|^^|^^|^\n\ | |
| ........................", | |
| mapf::ter_bind( ". ^ | X x / D", | |
| t_dirt, | |
| t_railroad_rubble, | |
| t_railroad_tie, | |
| t_railroad_track, | |
| t_railroad_track_on_tie, | |
| t_railroad_tie_d, | |
| t_railroad_track_d ), | |
| mapf::furn_bind( ". ^ | X x / D", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| break; | |
| case 2: // straight or diagonal | |
| if( diag ) { // diagonal railroads get drawn differently from all other types | |
| mapf::formatted_set_simple( m, 0, 0, "\ | |
| .^DD^^DD^.......^DD^^DD^\n\ | |
| ..^DD^^DD^.......^DD^^DD\n\ | |
| ...^DD^^DD^.......^DD^^D\n\ | |
| ....^DD^^DD^.......^DD^^\n\ | |
| .....^DD^^DD^.......^DD^\n\ | |
| ......^DD^^DD^.......^DD\n\ | |
| .......^DD^^DD^.......^D\n\ | |
| ........^DD^^DD^.......^\n\ | |
| .........^DD^^DD^.......\n\ | |
| ..........^DD^^DD^......\n\ | |
| ...........^DD^^DD^.....\n\ | |
| ............^DD^^DD^....\n\ | |
| .............^DD^^DD^...\n\ | |
| ..............^DD^^DD^..\n\ | |
| ...............^DD^^DD^.\n\ | |
| ................^DD^^DD^\n\ | |
| .................^DD^^DD\n\ | |
| ..................^DD^^D\n\ | |
| ...................^DD^^\n\ | |
| ....................^DD^\n\ | |
| .....................^DD\n\ | |
| ......................^D\n\ | |
| .......................^\n\ | |
| ........................", | |
| mapf::ter_bind( ". ^ D", | |
| t_dirt, | |
| t_railroad_rubble, | |
| t_railroad_track_d ), | |
| mapf::furn_bind( ". ^ D", | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| } else { // normal railroads drawing | |
| mapf::formatted_set_simple( m, 0, 0, "\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.", | |
| mapf::ter_bind( ". ^ - X x", | |
| t_dirt, | |
| t_railroad_rubble, | |
| t_railroad_tie, | |
| t_railroad_track, | |
| t_railroad_track_on_tie ), | |
| mapf::furn_bind( ". ^ - X x", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| } | |
| break; | |
| case 1: // dead end | |
| mapf::formatted_set_simple( m, 0, 0, "\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .-x---x-........-x---x-.\n\ | |
| .^X^^^X^........^X^^^X^.\n\ | |
| .^S^^^S^........^S^^^S^.\n\ | |
| .^^^^^^^........^^^^^^^.\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................\n\ | |
| ........................", | |
| mapf::ter_bind( ". ^ S - X x", | |
| t_dirt, | |
| t_railroad_rubble, | |
| t_buffer_stop, | |
| t_railroad_tie, | |
| t_railroad_track, | |
| t_railroad_track_on_tie ), | |
| mapf::furn_bind( ". ^ S - X x", | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null, | |
| f_null ) ); | |
| break; | |
| } | |
| // finally, unrotate the map | |
| m->rotate( rot ); | |
| } | |
| /////////////////// | |
| void mapgen_railroad_bridge( map *m, oter_id terrain_type, mapgendata, const time_point &, float ) | |
| { | |
| mapf::formatted_set_simple( m, 0, 0, "\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r^X^^^X^________^X^^^X^r\n\ | |
| r-x---x-________-x---x-r\n\ | |
| r^X^^^X^________^X^^^X^r", | |
| mapf::ter_bind( ". _ r ^ - X x", t_dirt, t_concrete, t_railing, t_railroad_rubble, t_railroad_tie, | |
| t_railroad_track, t_railroad_track_on_tie ), | |
| mapf::furn_bind( ". _ r ^ - X x", f_null, f_null, f_null, f_null, f_null, f_null, f_null ) | |
| ); | |
| m->rotate( static_cast<int>( terrain_type->get_dir() ) ); | |
| } | |
| void mapgen_river_center( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| fill_background( m, t_water_moving_dp ); | |
| } | |
| void mapgen_river_curved_not( map *m, oter_id terrain_type, mapgendata dat, const time_point &, | |
| float ) | |
| { | |
| ( void )dat; | |
| fill_background( m, t_water_moving_dp ); | |
| // this is not_ne, so deep on all sides except ne corner, which is shallow | |
| // shallow is 20,0, 23,4 | |
| int north_edge = rng( 16, 18 ); | |
| int east_edge = rng( 4, 8 ); | |
| for( int x = north_edge; x < SEEX * 2; x++ ) { | |
| for( int y = 0; y < east_edge; y++ ) { | |
| int circle_edge = ( ( SEEX * 2 - x ) * ( SEEX * 2 - x ) ) + ( y * y ); | |
| if( circle_edge <= 8 ) { | |
| m->ter_set( x, y, grass_or_dirt() ); | |
| } | |
| if( circle_edge == 9 && one_in( 25 ) ) { | |
| m->ter_set( x, y, clay_or_sand() ); | |
| } else if( circle_edge <= 36 ) { | |
| m->ter_set( x, y, t_water_moving_sh ); | |
| } | |
| } | |
| } | |
| if( terrain_type == "river_c_not_se" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "river_c_not_sw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "river_c_not_nw" ) { | |
| m->rotate( 3 ); | |
| } | |
| } | |
| void mapgen_river_straight( map *m, oter_id terrain_type, mapgendata dat, const time_point &, | |
| float ) | |
| { | |
| ( void )dat; | |
| fill_background( m, t_water_moving_dp ); | |
| for( int x = 0; x < SEEX * 2; x++ ) { | |
| int ground_edge = rng( 1, 3 ); | |
| int shallow_edge = rng( 4, 6 ); | |
| line( m, grass_or_dirt(), x, 0, x, ground_edge ); | |
| if( one_in( 25 ) ) { | |
| m->ter_set( x, ++ground_edge, clay_or_sand() ); | |
| } | |
| line( m, t_water_moving_sh, x, ++ground_edge, x, shallow_edge ); | |
| } | |
| if( terrain_type == "river_east" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "river_south" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "river_west" ) { | |
| m->rotate( 3 ); | |
| } | |
| } | |
| void mapgen_river_curved( map *m, oter_id terrain_type, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| fill_background( m, t_water_moving_dp ); | |
| // NE corner deep, other corners are shallow. do 2 passes: one x, one y | |
| for( int x = 0; x < SEEX * 2; x++ ) { | |
| int ground_edge = rng( 1, 3 ); | |
| int shallow_edge = rng( 4, 6 ); | |
| line( m, grass_or_dirt(), x, 0, x, ground_edge ); | |
| if( one_in( 25 ) ) { | |
| m->ter_set( x, ++ground_edge, clay_or_sand() ); | |
| } | |
| line( m, t_water_moving_sh, x, ++ground_edge, x, shallow_edge ); | |
| } | |
| for( int y = 0; y < SEEY * 2; y++ ) { | |
| int ground_edge = rng( 19, 21 ); | |
| int shallow_edge = rng( 16, 18 ); | |
| line( m, grass_or_dirt(), ground_edge, y, SEEX * 2 - 1, y ); | |
| if( one_in( 25 ) ) { | |
| m->ter_set( --ground_edge, y, clay_or_sand() ); | |
| } | |
| line( m, t_water_moving_sh, shallow_edge, y, --ground_edge, y ); | |
| } | |
| if( terrain_type == "river_se" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "river_sw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "river_nw" ) { | |
| m->rotate( 3 ); | |
| } | |
| } | |
| void mapgen_parking_lot( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( j == 5 || j == 9 || j == 13 || j == 17 || j == 21 ) && | |
| ( ( i > 1 && i < 8 ) || ( i > 14 && i < SEEX * 2 - 2 ) ) ) { | |
| m->ter_set( i, j, t_pavement_y ); | |
| } else if( ( j < 2 && i > 7 && i < 17 ) || ( j >= 2 && j < SEEY * 2 - 2 && i > 1 && | |
| i < SEEX * 2 - 2 ) ) { | |
| m->ter_set( i, j, t_pavement ); | |
| } else { | |
| m->ter_set( i, j, dat.groundcover() ); | |
| } | |
| } | |
| } | |
| VehicleSpawn::apply( vspawn_id( "default_parkinglot" ), *m, "parkinglot" ); | |
| m->place_items( "road", 8, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, false, turn ); | |
| for( int i = 1; i < 4; i++ ) { | |
| const std::string &id = dat.t_nesw[i].id().str(); | |
| if( id.size() > 5 && id.find( "road_" ) == 0 ) { | |
| m->rotate( i ); | |
| } | |
| } | |
| } | |
| void house_room( map *m, room_type type, int x1, int y1, int x2, int y2, mapgendata &dat ) | |
| { | |
| // TODO: change this into a parameter | |
| const time_point turn = calendar::time_of_cataclysm; | |
| int pos_x1 = 0; | |
| int pos_y1 = 0; | |
| if( type == room_backyard ) { //processing it separately | |
| m->furn_set( x1 + 2, y1, f_chair ); | |
| m->furn_set( x1 + 2, y1 + 1, f_table ); | |
| for( int i = x1; i <= x2; i++ ) { | |
| for( int j = y1; j <= y2; j++ ) { | |
| if( ( i == x1 ) || ( i == x2 || ( j == y2 ) ) ) { | |
| m->ter_set( i, j, t_fence ); | |
| } else { | |
| m->ter_set( i, j, t_grass ); | |
| if( one_in( 35 ) && !m->has_furn( i, j ) ) { | |
| m->ter_set( i, j, t_tree_young ); | |
| } else if( one_in( 35 ) && !m->has_furn( i, j ) ) { | |
| m->ter_set( i, j, t_tree ); | |
| } else if( one_in( 25 ) ) { | |
| m->ter_set( i, j, t_dirt ); | |
| } | |
| } | |
| } | |
| } | |
| m->ter_set( ( x1 + x2 ) / 2, y2, t_fencegate_c ); | |
| return; | |
| } | |
| for( int i = x1; i <= x2; i++ ) { | |
| for( int j = y1; j <= y2; j++ ) { | |
| if( dat.is_groundcover( m->ter( i, j ) ) || | |
| //m->ter(i, j) == t_grass || m->ter(i, j) == t_dirt || | |
| m->ter( i, j ) == t_floor ) { | |
| if( j == y1 || j == y2 ) { | |
| m->ter_set( i, j, t_wall ); | |
| } else if( i == x1 || i == x2 ) { | |
| m->ter_set( i, j, t_wall ); | |
| } else { | |
| m->ter_set( i, j, t_floor ); | |
| } | |
| } | |
| } | |
| } | |
| for( int i = y1 + 1; i <= y2 - 1; i++ ) { | |
| m->ter_set( x1, i, t_wall ); | |
| m->ter_set( x2, i, t_wall ); | |
| } | |
| items_location placed = "none"; | |
| int chance = 0; | |
| int rn = 0; | |
| switch( type ) { | |
| case room_study: | |
| placed = "livingroom"; | |
| chance = 40; | |
| break; | |
| case room_living: | |
| placed = "livingroom"; | |
| chance = 83; | |
| //choose random wall | |
| switch( rng( 1, 4 ) ) { //some bookshelves | |
| case 1: | |
| pos_x1 = x1 + 2; | |
| pos_y1 = y1 + 1; | |
| m->furn_set( x1 + 2, y2 - 1, f_desk ); | |
| while( pos_x1 < x2 ) { | |
| pos_x1 += 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 += 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 += 2; | |
| } | |
| break; | |
| case 2: | |
| pos_x1 = x2 - 2; | |
| pos_y1 = y1 + 1; | |
| m->furn_set( x1 + 2, y2 - 1, f_desk ); | |
| while( pos_x1 > x1 ) { | |
| pos_x1 -= 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 -= 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 -= 2; | |
| } | |
| break; | |
| case 3: | |
| pos_x1 = x1 + 2; | |
| pos_y1 = y2 - 1; | |
| m->furn_set( x1 + 2, y2 - 1, f_desk ); | |
| while( pos_x1 < x2 ) { | |
| pos_x1 += 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 += 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 += 2; | |
| } | |
| break; | |
| case 4: | |
| pos_x1 = x2 - 2; | |
| pos_y1 = y2 - 1; | |
| m->furn_set( x1 + 2, y2 - 1, f_desk ); | |
| while( pos_x1 > x1 ) { | |
| pos_x1 -= 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 -= 1; | |
| if( m->ter( pos_x1, pos_y1 ) == t_wall ) { | |
| break; | |
| } | |
| m->furn_set( pos_x1, pos_y1, f_bookcase ); | |
| pos_x1 -= 2; | |
| } | |
| break; | |
| } | |
| break; | |
| case room_kitchen: { | |
| placed = "kitchen"; | |
| chance = 75; | |
| m->place_items( "cleaning", 58, x1 + 1, y1 + 1, x2 - 1, y2 - 2, false, turn ); | |
| m->place_items( "home_hw", 40, x1 + 1, y1 + 1, x2 - 1, y2 - 2, false, turn ); | |
| int oven_x = -1; | |
| int oven_y = -1; | |
| int cupboard_x = -1; | |
| int cupboard_y = -1; | |
| switch( rng( 1, 4 ) ) { //fridge, sink, oven and some cupboards near them | |
| case 1: | |
| m->furn_set( x1 + 2, y1 + 1, f_fridge ); | |
| m->place_items( "fridge", 82, x1 + 2, y1 + 1, x1 + 2, y1 + 1, false, turn ); | |
| m->furn_set( x1 + 1, y1 + 1, f_sink ); | |
| if( x1 + 4 < x2 ) { | |
| oven_x = x1 + 3; | |
| cupboard_x = x1 + 4; | |
| oven_y = cupboard_y = y1 + 1; | |
| } | |
| break; | |
| case 2: | |
| m->furn_set( x2 - 2, y1 + 1, f_fridge ); | |
| m->place_items( "fridge", 82, x2 - 2, y1 + 1, x2 - 2, y1 + 1, false, turn ); | |
| m->furn_set( x2 - 1, y1 + 1, f_sink ); | |
| if( x2 - 4 > x1 ) { | |
| oven_x = x2 - 3; | |
| cupboard_x = x2 - 4; | |
| oven_y = cupboard_y = y1 + 1; | |
| } | |
| break; | |
| case 3: | |
| m->furn_set( x1 + 2, y2 - 1, f_fridge ); | |
| m->place_items( "fridge", 82, x1 + 2, y2 - 1, x1 + 2, y2 - 1, false, turn ); | |
| m->furn_set( x1 + 1, y2 - 1, f_sink ); | |
| if( x1 + 4 < x2 ) { | |
| oven_x = x1 + 3; | |
| cupboard_x = x1 + 4; | |
| oven_y = cupboard_y = y2 - 1; | |
| } | |
| break; | |
| case 4: | |
| m->furn_set( x2 - 2, y2 - 1, f_fridge ); | |
| m->place_items( "fridge", 82, x2 - 2, y2 - 1, x2 - 2, y2 - 1, false, turn ); | |
| m->furn_set( x2 - 1, y2 - 1, f_sink ); | |
| if( x2 - 4 > x1 ) { | |
| oven_x = x2 - 3; | |
| cupboard_x = x2 - 4; | |
| oven_y = cupboard_y = y2 - 1; | |
| } | |
| break; | |
| } | |
| // oven and it's contents | |
| if( oven_x != -1 && oven_y != -1 ) { | |
| m->furn_set( oven_x, oven_y, f_oven ); | |
| m->place_items( "oven", 70, oven_x, oven_y, oven_x, oven_y, false, turn ); | |
| } | |
| // cupboard and it's contents | |
| if( cupboard_x != -1 && cupboard_y != -1 ) { | |
| m->furn_set( cupboard_x, cupboard_y, f_cupboard ); | |
| m->place_items( "cleaning", 30, cupboard_x, cupboard_y, cupboard_x, cupboard_y, false, turn ); | |
| m->place_items( "home_hw", 30, cupboard_x, cupboard_y, cupboard_x, cupboard_y, false, turn ); | |
| m->place_items( "cannedfood", 30, cupboard_x, cupboard_y, cupboard_x, cupboard_y, false, turn ); | |
| m->place_items( "pasta", 30, cupboard_x, cupboard_y, cupboard_x, cupboard_y, false, turn ); | |
| } | |
| if( one_in( 2 ) ) { //dining table in the kitchen | |
| square_furn( m, f_table, static_cast<int>( ( x1 + x2 ) / 2 ) - 1, | |
| static_cast<int>( ( y1 + y2 ) / 2 ) - 1, | |
| static_cast<int>( ( x1 + x2 ) / 2 ), | |
| static_cast<int>( ( y1 + y2 ) / 2 ) ); | |
| m->place_items( "dining", 20, static_cast<int>( ( x1 + x2 ) / 2 ) - 1, | |
| static_cast<int>( ( y1 + y2 ) / 2 ) - 1, | |
| static_cast<int>( ( x1 + x2 ) / 2 ), static_cast<int>( ( y1 + y2 ) / 2 ), false, turn ); | |
| } | |
| if( one_in( 2 ) ) { | |
| for( int i = 0; i <= 2; i++ ) { | |
| pos_x1 = rng( x1 + 2, x2 - 2 ); | |
| pos_y1 = rng( y1 + 1, y2 - 1 ); | |
| if( m->ter( pos_x1, pos_y1 ) == t_floor && !( m->furn( pos_x1, pos_y1 ) == f_cupboard || | |
| m->furn( pos_x1, pos_y1 ) == f_oven || m->furn( pos_x1, pos_y1 ) == f_sink || | |
| m->furn( pos_x1, pos_y1 ) == f_fridge ) ) { | |
| m->furn_set( pos_x1, pos_y1, f_chair ); | |
| } | |
| } | |
| } | |
| } | |
| break; | |
| case room_bedroom: | |
| placed = "bedroom"; | |
| chance = 78; | |
| if( one_in( 14 ) ) { | |
| m->place_items( "homeguns", 58, x1 + 1, y1 + 1, x2 - 1, y2 - 1, false, turn ); | |
| } | |
| if( one_in( 10 ) ) { | |
| m->place_items( "home_hw", 40, x1 + 1, y1 + 1, x2 - 1, y2 - 1, false, turn ); | |
| } | |
| switch( rng( 1, 5 ) ) { | |
| case 1: | |
| m->furn_set( x1 + 1, y1 + 2, f_bed ); | |
| m->furn_set( x1 + 1, y1 + 3, f_bed ); | |
| m->place_items( "bed", 60, x1 + 1, y1 + 2, x1 + 1, y1 + 2, false, turn ); | |
| m->place_items( "bed", 60, x1 + 1, y1 + 3, x1 + 1, y1 + 3, false, turn ); | |
| break; | |
| case 2: | |
| m->furn_set( x1 + 2, y2 - 1, f_bed ); | |
| m->furn_set( x1 + 3, y2 - 1, f_bed ); | |
| m->place_items( "bed", 60, x1 + 2, y2 - 1, x1 + 2, y2 - 1, false, turn ); | |
| m->place_items( "bed", 60, x1 + 2, y2 - 1, x1 + 2, y2 - 1, false, turn ); | |
| break; | |
| case 3: | |
| m->furn_set( x2 - 1, y2 - 3, f_bed ); | |
| m->furn_set( x2 - 1, y2 - 2, f_bed ); | |
| m->place_items( "bed", 60, x2 - 1, y2 - 3, x2 - 1, y2 - 3, false, turn ); | |
| m->place_items( "bed", 60, x2 - 1, y2 - 2, x2 - 1, y2 - 2, false, turn ); | |
| break; | |
| case 4: | |
| m->furn_set( x2 - 3, y1 + 1, f_bed ); | |
| m->furn_set( x2 - 2, y1 + 1, f_bed ); | |
| m->place_items( "bed", 60, x2 - 3, y1 + 1, x2 - 3, y1 + 1, false, turn ); | |
| m->place_items( "bed", 60, x2 - 2, y1 + 1, x2 - 2, y1 + 1, false, turn ); | |
| break; | |
| case 5: | |
| m->furn_set( static_cast<int>( ( x1 + x2 ) / 2 ), y2 - 1, f_bed ); | |
| m->furn_set( static_cast<int>( ( x1 + x2 ) / 2 ) + 1, y2 - 1, f_bed ); | |
| m->furn_set( static_cast<int>( ( x1 + x2 ) / 2 ), y2 - 2, f_bed ); | |
| m->furn_set( static_cast<int>( ( x1 + x2 ) / 2 ) + 1, y2 - 2, f_bed ); | |
| m->place_items( "bed", 60, static_cast<int>( ( x1 + x2 ) / 2 ), y2 - 1, | |
| static_cast<int>( ( x1 + x2 ) / 2 ), y2 - 1, false, | |
| turn ); | |
| m->place_items( "bed", 60, static_cast<int>( ( x1 + x2 ) / 2 ) + 1, y2 - 1, | |
| static_cast<int>( ( x1 + x2 ) / 2 ) + 1, y2 - 1, | |
| false, turn ); | |
| m->place_items( "bed", 60, static_cast<int>( ( x1 + x2 ) / 2 ), y2 - 2, | |
| static_cast<int>( ( x1 + x2 ) / 2 ), y2 - 2, false, | |
| turn ); | |
| m->place_items( "bed", 60, static_cast<int>( ( x1 + x2 ) / 2 ) + 1, y2 - 2, | |
| static_cast<int>( ( x1 + x2 ) / 2 ) + 1, y2 - 2, | |
| false, turn ); | |
| break; | |
| } | |
| switch( rng( 1, 4 ) ) { | |
| case 1: | |
| m->furn_set( x1 + 2, y1 + 1, f_dresser ); | |
| m->place_items( "dresser", 80, x1 + 2, y1 + 1, x1 + 2, y1 + 1, false, turn ); | |
| break; | |
| case 2: | |
| m->furn_set( x2 - 2, y2 - 1, f_dresser ); | |
| m->place_items( "dresser", 80, x2 - 2, y2 - 1, x2 - 2, y2 - 1, false, turn ); | |
| break; | |
| case 3: | |
| rn = static_cast<int>( ( x1 + x2 ) / 2 ); | |
| m->furn_set( rn, y1 + 1, f_dresser ); | |
| m->place_items( "dresser", 80, rn, y1 + 1, rn, y1 + 1, false, turn ); | |
| break; | |
| case 4: | |
| rn = static_cast<int>( ( y1 + y2 ) / 2 ); | |
| m->furn_set( x1 + 1, rn, f_dresser ); | |
| m->place_items( "dresser", 80, x1 + 1, rn, x1 + 1, rn, false, turn ); | |
| break; | |
| } | |
| break; | |
| case room_bathroom: | |
| m->place_toilet( x2 - 1, y2 - 1 ); | |
| m->place_items( "harddrugs", 18, x1 + 1, y1 + 1, x2 - 1, y2 - 2, false, turn ); | |
| m->place_items( "cleaning", 48, x1 + 1, y1 + 1, x2 - 1, y2 - 2, false, turn ); | |
| placed = "softdrugs"; | |
| chance = 72; | |
| m->furn_set( x2 - 1, y2 - 2, f_bathtub ); | |
| if( one_in( 3 ) && !( m->ter( x2 - 1, y2 - 3 ) == t_wall ) ) { | |
| m->furn_set( x2 - 1, y2 - 3, f_bathtub ); | |
| } | |
| if( !( ( m->furn( x1 + 1, y2 - 2 ) == f_toilet ) || ( m->furn( x1 + 1, y2 - 2 ) == f_bathtub ) ) ) { | |
| m->furn_set( x1 + 1, y2 - 2, f_sink ); | |
| } | |
| if( one_in( 4 ) ) { | |
| for( int x = x1 + 1; x <= x2 - 1; x++ ) { | |
| for( int y = y1 + 1; y <= y2 - 1; y++ ) { | |
| m->ter_set( x, y, t_linoleum_white ); | |
| } | |
| } | |
| } else if( one_in( 4 ) ) { | |
| for( int x = x1 + 1; x <= x2 - 1; x++ ) { | |
| for( int y = y1 + 1; y <= y2 - 1; y++ ) { | |
| m->ter_set( x, y, t_linoleum_gray ); | |
| } | |
| } | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| m->place_items( placed, chance, x1 + 1, y1 + 1, x2 - 1, y2 - 1, false, turn ); | |
| } | |
| void mapgen_generic_house_boxy( map *m, oter_id terrain_type, mapgendata dat, | |
| const time_point &turn, float density ) | |
| { | |
| mapgen_generic_house( m, terrain_type, dat, turn, density, 1 ); | |
| } | |
| void mapgen_generic_house_big_livingroom( map *m, oter_id terrain_type, mapgendata dat, | |
| const time_point &turn, float density ) | |
| { | |
| mapgen_generic_house( m, terrain_type, dat, turn, density, 2 ); | |
| } | |
| void mapgen_generic_house_center_hallway( map *m, oter_id terrain_type, mapgendata dat, | |
| const time_point &turn, float density ) | |
| { | |
| mapgen_generic_house( m, terrain_type, dat, turn, density, 3 ); | |
| } | |
| void mapgen_generic_house( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density, int variant ) | |
| { | |
| int rn = 0; | |
| int lw = 0; | |
| int rw = 0; | |
| int mw = 0; | |
| int tw = 0; | |
| int bw = 0; | |
| int cw = 0; | |
| int actual_house_height = 0; | |
| int bw_old = 0; | |
| lw = rng( 0, 4 ); // West external wall | |
| // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) | |
| mw = lw + rng( 7, 10 ); // Middle wall between bedroom & kitchen/bath | |
| rw = SEEX * 2 - rng( 1, 5 ); // East external wall | |
| tw = rng( 1, 6 ); // North external wall | |
| bw = SEEX * 2 - rng( 2, 5 ); // South external wall | |
| // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) | |
| cw = tw + rng( 4, 7 ); // Middle wall between living room & kitchen/bed | |
| actual_house_height = bw - rng( 4, | |
| 6 ); //reserving some space for backyard. Actual south external wall. | |
| bw_old = bw; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( i > lw && i < rw && j > tw && j < bw ) { | |
| m->ter_set( i, j, t_floor ); | |
| } else { | |
| m->ter_set( i, j, dat.groundcover() ); | |
| } | |
| if( i >= lw && i <= rw && ( j == tw || j == bw ) ) { //placing north and south walls | |
| m->ter_set( i, j, t_wall ); | |
| } | |
| if( ( i == lw || i == rw ) && j > tw && | |
| j < bw /*actual_house_height*/ ) { //placing west (lw) and east walls | |
| m->ter_set( i, j, t_wall ); | |
| } | |
| } | |
| } | |
| switch( variant ) { | |
| case 1: // Quadrants, essentially | |
| mw = rng( lw + 5, rw - 5 ); | |
| cw = tw + rng( 4, 7 ); | |
| house_room( m, room_living, mw, tw, rw, cw, dat ); | |
| house_room( m, room_kitchen, lw, tw, mw, cw, dat ); | |
| m->ter_set( mw, rng( tw + 2, cw - 2 ), ( one_in( 3 ) ? t_door_c : t_floor ) ); | |
| rn = rng( lw + 1, mw - 2 ); | |
| m->ter_set( rn, tw, t_window_domestic ); | |
| m->ter_set( rn + 1, tw, t_window_domestic ); | |
| rn = rng( mw + 1, rw - 2 ); | |
| m->ter_set( rn, tw, t_window_domestic ); | |
| m->ter_set( rn + 1, tw, t_window_domestic ); | |
| rn = rng( lw + 3, rw - 3 ); // Bottom part mw | |
| if( rn <= lw + 5 ) { | |
| // Bedroom on right, bathroom on left | |
| house_room( m, room_bedroom, rn, cw, rw, bw, dat ); | |
| // Put door between bedroom and living | |
| m->ter_set( rng( rw - 1, rn > mw ? rn + 1 : mw + 1 ), cw, t_door_c ); | |
| if( bw - cw >= 10 && rn - lw >= 6 ) { | |
| // All fits, placing bathroom and 2nd bedroom | |
| house_room( m, room_bathroom, lw, bw - 5, rn, bw, dat ); | |
| house_room( m, room_bedroom, lw, cw, rn, bw - 5, dat ); | |
| // Put door between bathroom and bedroom | |
| m->ter_set( rn, rng( bw - 4, bw - 1 ), t_door_c ); | |
| if( one_in( 3 ) ) { | |
| // Put door between 2nd bedroom and 1st bedroom | |
| m->ter_set( rn, rng( cw + 1, bw - 6 ), t_door_c ); | |
| } else { | |
| // ...Otherwise, between 2nd bedroom and kitchen | |
| m->ter_set( rng( lw + 1, rn > mw ? mw - 1 : rn - 1 ), cw, t_door_c ); | |
| } | |
| } else if( bw - cw > 4 ) { | |
| // Too big for a bathroom, not big enough for 2nd bedroom | |
| // Make it a bathroom anyway, but give the excess space back to | |
| // the kitchen. | |
| house_room( m, room_bathroom, lw, bw - 4, rn, bw, dat ); | |
| for( int i = lw + 1; i < mw && i < rn; i++ ) { | |
| m->ter_set( i, cw, t_floor ); | |
| } | |
| // Put door between excess space and bathroom | |
| m->ter_set( rng( lw + 1, rn - 1 ), bw - 4, t_door_c ); | |
| // Put door between excess space and bedroom | |
| m->ter_set( rn, rng( cw + 1, bw - 5 ), t_door_c ); | |
| } else { | |
| // Small enough to be a bathroom; make it one. | |
| house_room( m, room_bathroom, lw, cw, rn, bw, dat ); | |
| if( one_in( 5 ) ) { | |
| // Put door between bathroom and kitchen with low chance | |
| m->ter_set( rng( lw + 1, rn > mw ? mw - 1 : rn - 1 ), cw, t_door_c ); | |
| } else { | |
| // ...Otherwise, between bathroom and bedroom | |
| m->ter_set( rn, rng( cw + 1, bw - 1 ), t_door_c ); | |
| } | |
| } | |
| // Point on bedroom wall, for window | |
| rn = rng( rn + 2, rw - 2 ); | |
| } else { | |
| // Bedroom on left, bathroom on right | |
| house_room( m, room_bedroom, lw, cw, rn, bw, dat ); | |
| // Put door between bedroom and kitchen | |
| m->ter_set( rng( lw + 1, rn > mw ? mw - 1 : rn - 1 ), cw, t_door_c ); | |
| if( bw - cw >= 10 && rw - rn >= 6 ) { | |
| // All fits, placing bathroom and 2nd bedroom | |
| house_room( m, room_bathroom, rn, bw - 5, rw, bw, dat ); | |
| house_room( m, room_bedroom, rn, cw, rw, bw - 5, dat ); | |
| // Put door between bathroom and bedroom | |
| m->ter_set( rn, rng( bw - 4, bw - 1 ), t_door_c ); | |
| if( one_in( 3 ) ) { | |
| // Put door between 2nd bedroom and 1st bedroom | |
| m->ter_set( rn, rng( cw + 1, bw - 6 ), t_door_c ); | |
| } else { | |
| // ...Otherwise, between 2nd bedroom and living | |
| m->ter_set( rng( rw - 1, rn > mw ? rn + 1 : mw + 1 ), cw, t_door_c ); | |
| } | |
| } else if( bw - cw > 4 ) { | |
| // Too big for a bathroom, not big enough for 2nd bedroom | |
| // Make it a bathroom anyway, but give the excess space back to | |
| // the living. | |
| house_room( m, room_bathroom, rn, bw - 4, rw, bw, dat ); | |
| for( int i = rw - 1; i > rn && i > mw; i-- ) { | |
| m->ter_set( i, cw, t_floor ); | |
| } | |
| // Put door between excess space and bathroom | |
| m->ter_set( rng( rw - 1, rn + 1 ), bw - 4, t_door_c ); | |
| // Put door between excess space and bedroom | |
| m->ter_set( rn, rng( cw + 1, bw - 5 ), t_door_c ); | |
| } else { | |
| // Small enough to be a bathroom; make it one. | |
| house_room( m, room_bathroom, rn, cw, rw, bw, dat ); | |
| if( one_in( 5 ) ) { | |
| // Put door between bathroom and living with low chance | |
| m->ter_set( rng( rw - 1, rn > mw ? rn + 1 : mw + 1 ), cw, t_door_c ); | |
| } else { | |
| // ...Otherwise, between bathroom and bedroom | |
| m->ter_set( rn, rng( cw + 1, bw - 1 ), t_door_c ); | |
| } | |
| } | |
| // Point on bedroom wall, for window | |
| rn = rng( lw + 2, rn - 2 ); | |
| } | |
| m->ter_set( rn, bw, t_window_domestic ); | |
| m->ter_set( rn + 1, bw, t_window_domestic ); | |
| if( !one_in( 3 ) && rw < SEEX * 2 - 1 ) { // Potential side windows | |
| rn = rng( tw + 2, bw - 6 ); | |
| m->ter_set( rw, rn, t_window_domestic ); | |
| m->ter_set( rw, rn + 4, t_window_domestic ); | |
| } | |
| if( !one_in( 3 ) && lw > 0 ) { // Potential side windows | |
| rn = rng( tw + 2, bw - 6 ); | |
| m->ter_set( lw, rn, t_window_domestic ); | |
| m->ter_set( lw, rn + 4, t_window_domestic ); | |
| } | |
| if( one_in( 2 ) ) { // Placement of the main door | |
| m->ter_set( rng( lw + 2, mw - 1 ), tw, | |
| ( one_in( 6 ) ? ( one_in( 6 ) ? t_door_c : t_door_c_peep ) : ( one_in( | |
| 6 ) ? t_door_locked : t_door_locked_peep ) ) ); | |
| if( one_in( 5 ) ) { // Placement of side door | |
| m->ter_set( rw, rng( tw + 2, cw - 2 ), ( one_in( 6 ) ? t_door_c : t_door_locked ) ); | |
| } | |
| } else { | |
| m->ter_set( rng( mw + 1, rw - 2 ), tw, | |
| ( one_in( 6 ) ? ( one_in( 6 ) ? t_door_c : t_door_c_peep ) : ( one_in( | |
| 6 ) ? t_door_locked : t_door_locked_peep ) ) ); | |
| if( one_in( 5 ) ) { | |
| m->ter_set( lw, rng( tw + 2, cw - 2 ), ( one_in( 6 ) ? t_door_c : t_door_locked ) ); | |
| } | |
| } | |
| break; | |
| case 2: // Old-style; simple; | |
| //Modified by Jovan in 28 Aug 2013 | |
| //Long narrow living room in front, big kitchen and HUGE bedroom | |
| bw = SEEX * 2 - 2; | |
| cw = tw + rng( 3, 6 ); | |
| mw = rng( lw + 7, rw - 4 ); | |
| //int actual_house_height=bw-rng(4,6); | |
| //in some rare cases some rooms (especially kitchen and living room) may get rather small | |
| if( ( tw <= 3 ) && ( abs( ( actual_house_height - 3 ) - cw ) >= 3 ) ) { | |
| //everything is fine | |
| house_room( m, room_backyard, lw, actual_house_height + 1, rw, bw, dat ); | |
| //door from bedroom to backyard | |
| m->ter_set( ( lw + mw ) / 2, actual_house_height, t_door_c ); | |
| } else { //using old layout | |
| actual_house_height = bw_old; | |
| } | |
| // Plop down the rooms | |
| house_room( m, room_living, lw, tw, rw, cw, dat ); | |
| house_room( m, room_kitchen, mw, cw, rw, actual_house_height - 3, dat ); | |
| house_room( m, room_bedroom, lw, cw, mw, actual_house_height, dat ); //making bedroom smaller | |
| house_room( m, room_bathroom, mw, actual_house_height - 3, rw, actual_house_height, dat ); | |
| // Space between kitchen & living room: | |
| rn = rng( mw + 1, rw - 3 ); | |
| m->ter_set( rn, cw, t_floor ); | |
| m->ter_set( rn + 1, cw, t_floor ); | |
| // Front windows | |
| rn = rng( 2, 5 ); | |
| m->ter_set( lw + rn, tw, t_window_domestic ); | |
| m->ter_set( lw + rn + 1, tw, t_window_domestic ); | |
| m->ter_set( rw - rn, tw, t_window_domestic ); | |
| m->ter_set( rw - rn + 1, tw, t_window_domestic ); | |
| // Front door | |
| m->ter_set( rng( lw + 4, rw - 4 ), tw, ( one_in( 6 ) ? t_door_c : t_door_locked ) ); | |
| if( one_in( 3 ) ) { // Kitchen windows | |
| rn = rng( cw + 1, actual_house_height - 5 ); | |
| m->ter_set( rw, rn, t_window_domestic ); | |
| m->ter_set( rw, rn + 1, t_window_domestic ); | |
| } | |
| if( one_in( 3 ) ) { // Bedroom windows | |
| rn = rng( cw + 1, actual_house_height - 2 ); | |
| m->ter_set( lw, rn, t_window_domestic ); | |
| m->ter_set( lw, rn + 1, t_window_domestic ); | |
| } | |
| // Door to bedroom | |
| if( one_in( 4 ) ) { | |
| m->ter_set( rng( lw + 1, mw - 1 ), cw, t_door_c ); | |
| } else { | |
| m->ter_set( mw, rng( cw + 3, actual_house_height - 4 ), t_door_c ); | |
| } | |
| // Door to bathroom | |
| if( one_in( 4 ) ) { | |
| m->ter_set( mw, actual_house_height - 1, t_door_c ); | |
| } else { | |
| m->ter_set( rng( mw + 2, rw - 2 ), actual_house_height - 3, t_door_c ); | |
| } | |
| // Back windows | |
| rn = rng( lw + 1, mw - 2 ); | |
| m->ter_set( rn, actual_house_height, t_window_domestic ); | |
| m->ter_set( rn + 1, actual_house_height, t_window_domestic ); | |
| rn = rng( mw + 1, rw - 1 ); | |
| m->ter_set( rn, actual_house_height, t_window_domestic ); | |
| break; | |
| case 3: // Long center hallway, kitchen, living room and office | |
| mw = static_cast<int>( ( lw + rw ) / 2 ); | |
| cw = bw - rng( 5, 7 ); | |
| // Hallway doors and windows | |
| m->ter_set( mw, tw, ( one_in( 6 ) ? t_door_c : t_door_locked ) ); | |
| if( one_in( 4 ) ) { | |
| m->ter_set( mw - 1, tw, t_window_domestic ); | |
| m->ter_set( mw + 1, tw, t_window_domestic ); | |
| } | |
| for( int i = tw + 1; i < cw; i++ ) { // Hallway walls | |
| m->ter_set( mw - 2, i, t_wall ); | |
| m->ter_set( mw + 2, i, t_wall ); | |
| } | |
| if( one_in( 2 ) ) { // Front rooms are kitchen or living room | |
| house_room( m, room_living, lw, tw, mw - 2, cw, dat ); | |
| house_room( m, room_kitchen, mw + 2, tw, rw, cw, dat ); | |
| } else { | |
| house_room( m, room_kitchen, lw, tw, mw - 2, cw, dat ); | |
| house_room( m, room_living, mw + 2, tw, rw, cw, dat ); | |
| } | |
| // Front windows | |
| rn = rng( lw + 1, mw - 4 ); | |
| m->ter_set( rn, tw, t_window_domestic ); | |
| m->ter_set( rn + 1, tw, t_window_domestic ); | |
| rn = rng( mw + 3, rw - 2 ); | |
| m->ter_set( rn, tw, t_window_domestic ); | |
| m->ter_set( rn + 1, tw, t_window_domestic ); | |
| if( one_in( 3 ) && lw > 0 ) { // Side windows? | |
| rn = rng( tw + 1, cw - 2 ); | |
| m->ter_set( lw, rn, t_window_domestic ); | |
| m->ter_set( lw, rn + 1, t_window_domestic ); | |
| } | |
| if( one_in( 3 ) && rw < SEEX * 2 - 1 ) { // Side windows? | |
| rn = rng( tw + 1, cw - 2 ); | |
| m->ter_set( rw, rn, t_window_domestic ); | |
| m->ter_set( rw, rn + 1, t_window_domestic ); | |
| } | |
| if( one_in( 2 ) ) { // Bottom rooms are bedroom or bathroom | |
| //bathroom to the left (eastern wall), study to the right | |
| //house_room(m, room_bedroom, lw, cw, rw - 3, bw, dat); | |
| house_room( m, room_bedroom, mw - 2, cw, rw - 3, bw, dat ); | |
| house_room( m, room_bathroom, rw - 3, cw, rw, bw, dat ); | |
| house_room( m, room_study, lw, cw, mw - 2, bw, dat ); | |
| //===Study Room Furniture== | |
| m->ter_set( mw - 2, ( bw + cw ) / 2, t_door_o ); | |
| m->furn_set( lw + 1, cw + 1, f_chair ); | |
| m->furn_set( lw + 1, cw + 2, f_table ); | |
| m->ter_set( lw + 1, cw + 3, t_console_broken ); | |
| m->furn_set( lw + 3, bw - 1, f_bookcase ); | |
| m->place_items( "magazines", 30, lw + 3, bw - 1, lw + 3, bw - 1, false, turn ); | |
| m->place_items( "novels", 40, lw + 3, bw - 1, lw + 3, bw - 1, false, turn ); | |
| m->place_items( "alcohol", 20, lw + 3, bw - 1, lw + 3, bw - 1, false, turn ); | |
| m->place_items( "manuals", 30, lw + 3, bw - 1, lw + 3, bw - 1, false, turn ); | |
| //========================= | |
| m->ter_set( rng( lw + 2, mw - 3 ), cw, t_door_c ); | |
| if( one_in( 4 ) ) { | |
| m->ter_set( rng( rw - 2, rw - 1 ), cw, t_door_c ); | |
| } else { | |
| m->ter_set( rw - 3, rng( cw + 2, bw - 2 ), t_door_c ); | |
| } | |
| rn = rng( mw, rw - 5 ); //bedroom windows | |
| m->ter_set( rn, bw, t_window_domestic ); | |
| m->ter_set( rn + 1, bw, t_window_domestic ); | |
| m->ter_set( rng( lw + 2, mw - 3 ), bw, t_window_domestic ); //study window | |
| if( one_in( 4 ) ) { | |
| m->ter_set( rng( rw - 2, rw - 1 ), bw, t_window_domestic ); | |
| } else { | |
| m->ter( rw, rng( cw + 1, bw - 1 ) ); | |
| } | |
| } else { //bathroom to the right | |
| house_room( m, room_bathroom, lw, cw, lw + 3, bw, dat ); | |
| //house_room(m, room_bedroom, lw + 3, cw, rw, bw, dat); | |
| house_room( m, room_bedroom, lw + 3, cw, mw + 2, bw, dat ); | |
| house_room( m, room_study, mw + 2, cw, rw, bw, dat ); | |
| //===Study Room Furniture== | |
| m->ter_set( mw + 2, ( bw + cw ) / 2, t_door_c ); | |
| m->furn_set( rw - 1, cw + 1, f_chair ); | |
| m->furn_set( rw - 1, cw + 2, f_table ); | |
| m->ter_set( rw - 1, cw + 3, t_console_broken ); | |
| m->furn_set( rw - 3, bw - 1, f_bookcase ); | |
| m->place_items( "magazines", 40, rw - 3, bw - 1, rw - 3, bw - 1, false, turn ); | |
| m->place_items( "novels", 40, rw - 3, bw - 1, rw - 3, bw - 1, false, turn ); | |
| m->place_items( "alcohol", 20, rw - 3, bw - 1, rw - 3, bw - 1, false, turn ); | |
| m->place_items( "manuals", 20, rw - 3, bw - 1, rw - 3, bw - 1, false, turn ); | |
| //========================= | |
| if( one_in( 4 ) ) { | |
| m->ter_set( rng( lw + 1, lw + 2 ), cw, t_door_c ); | |
| } else { | |
| m->ter_set( lw + 3, rng( cw + 2, bw - 2 ), t_door_c ); | |
| } | |
| rn = rng( lw + 4, mw ); //bedroom windows | |
| m->ter_set( rn, bw, t_window_domestic ); | |
| m->ter_set( rn + 1, bw, t_window_domestic ); | |
| m->ter_set( rng( mw + 3, rw - 1 ), bw, t_window_domestic ); //study window | |
| if( one_in( 4 ) ) { | |
| m->ter_set( rng( lw + 1, lw + 2 ), bw, t_window_domestic ); | |
| } else { | |
| m->ter( lw, rng( cw + 1, bw - 1 ) ); | |
| } | |
| } | |
| // Doors off the sides of the hallway | |
| m->ter_set( mw - 2, rng( tw + 3, cw - 3 ), t_door_c ); | |
| m->ter_set( mw + 2, rng( tw + 3, cw - 3 ), t_door_c ); | |
| m->ter_set( mw, cw, t_door_c ); | |
| break; | |
| } // Done with the various house structures | |
| ////// | |
| if( rng( 2, 7 ) < tw ) { // Big front yard has a chance for a fence | |
| for( int i = lw; i <= rw; i++ ) { | |
| m->ter_set( i, 0, t_fence ); | |
| } | |
| for( int i = 1; i < tw; i++ ) { | |
| m->ter_set( lw, i, t_fence ); | |
| } | |
| int hole = rng( SEEX - 3, SEEX + 2 ); | |
| m->ter_set( hole, 0, t_dirt ); | |
| m->ter_set( hole + 1, 0, t_dirt ); | |
| if( one_in( tw ) ) { | |
| m->ter_set( hole - 1, 1, t_tree_young ); | |
| m->ter_set( hole + 2, 1, t_tree_young ); | |
| } | |
| } | |
| place_stairs( m, terrain_type, dat ); | |
| // Just boring old zombies | |
| m->place_spawns( mongroup_id( "GROUP_ZOMBIE" ), 2, 0, 0, SEEX * 2 - 1, SEEX * 2 - 1, density ); | |
| m->rotate( static_cast<int>( terrain_type->get_dir() ) ); | |
| } | |
| /////////////////////////////////////////////////////////// | |
| void mapgen_basement_generic_layout( map *m, oter_id, mapgendata, const time_point &, float ) | |
| { | |
| const ter_id t_rock_smooth( "t_rock_smooth" ); | |
| const int up = 0; | |
| const int left = 0; | |
| const int down = SEEY * 2 - 5; | |
| const int right = SEEX * 2 - 1; | |
| square( m, t_rock, left, down, right, SEEY * 2 - 1 ); | |
| square( m, t_rock_floor, 1, 1, right - 1, down - 1 ); | |
| line( m, t_rock_smooth, left, up, right, up ); | |
| line( m, t_rock_smooth, left, down, right, down ); | |
| line( m, t_rock_smooth, left, up, left, down ); | |
| line( m, t_rock_smooth, right, up, right, down ); | |
| m->ter_set( SEEX - 1, down - 1, t_stairs_up ); | |
| m->ter_set( SEEX, down - 1, t_stairs_up ); | |
| line( m, t_rock_smooth, SEEX - 2, down - 1, SEEX - 2, down - 3 ); | |
| line( m, t_rock_smooth, SEEX + 1, down - 1, SEEX + 1, down - 3 ); | |
| line( m, t_door_locked, SEEX - 1, down - 3, SEEX, down - 3 ); | |
| // Rotate randomly, now that other basements are more generic | |
| m->rotate( rng( 0, 3 ) ); | |
| } | |
| namespace furn_space | |
| { | |
| static bool clear( const map &m, const tripoint &from, const tripoint &to ) | |
| { | |
| for( const auto &p : m.points_in_rectangle( from, to ) ) { | |
| if( m.ter( p ).obj().movecost == 0 ) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| static point best_expand( const map &m, const tripoint &from, int maxx, int maxy ) | |
| { | |
| if( clear( m, from, from + point( maxx, maxy ) ) ) { | |
| // Common case | |
| return point( maxx, maxy ); | |
| } | |
| // Brute force all the combinations for the one with biggest area | |
| int best_area = 0; | |
| point best; | |
| for( int x = 0; x <= maxx; x++ ) { | |
| for( int y = 0; y <= maxy; y++ ) { | |
| int area = x * y; | |
| if( area <= best_area ) { | |
| continue; | |
| } | |
| if( clear( m, from, from + point( x, y ) ) ) { | |
| best_area = area; | |
| best = point( x, y ); | |
| } | |
| } | |
| } | |
| return best; | |
| } | |
| } // namespace furn_space | |
| void mapgen_basement_junk( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| // Junk! | |
| mapgen_basement_generic_layout( m, terrain_type, dat, turn, density ); | |
| //makes a square of randomly thrown around furniture and places stuff. | |
| const int z = m->get_abs_sub().z; | |
| for( const auto &p : m->points_in_rectangle( tripoint( 1, 1, z ), tripoint( 22, 22, z ) ) ) { | |
| if( m->ter( p ).obj().movecost == 0 ) { | |
| // Wall, skip | |
| continue; | |
| } | |
| if( one_in( 1600 ) ) { | |
| m->furn_set( p, furn_str_id( "f_gun_safe_el" ) ); | |
| m->place_items( "basement_op_guns", 96, p.x, p.y, p.x, p.y, false, turn ); | |
| m->place_items( "ammo", 90, p.x, p.y, p.x, p.y, false, turn ); | |
| } | |
| if( one_in( 20 ) ) { | |
| int rn = rng( 1, 8 ); | |
| if( rn == 1 ) { | |
| m->furn_set( p, f_dresser ); | |
| m->place_items( "dresser", 30, p.x, p.y, p.x, p.y, false, turn ); | |
| m->place_items( "trash_forest", 60, p.x, p.y, p.x, p.y, false, turn ); | |
| } else if( rn == 2 ) { | |
| m->furn_set( p, f_chair ); | |
| } else if( rn == 3 ) { | |
| m->furn_set( p, f_cupboard ); | |
| m->place_items( "trash", 60, p.x, p.y, p.x, p.y, false, turn ); | |
| m->place_items( "dining", 40, p.x, p.y, p.x, p.y, false, turn ); | |
| } else if( rn == 4 ) { | |
| tripoint rs = p + furn_space::best_expand( *m, p, rng( 0, 4 ), 0 ); | |
| square_furn( m, f_bookcase, p.x, p.y, rs.x, rs.y ); | |
| m->place_items( "novels", 60, p.x, p.y, rs.x, rs.y, false, turn ); | |
| m->place_items( "magazines", 20, p.x, p.y, rs.x, rs.y, false, turn ); | |
| } else if( rn == 5 ) { | |
| tripoint rs = p + furn_space::best_expand( *m, p, 0, rng( 0, 4 ) ); | |
| square_furn( m, f_bookcase, p.x, p.y, rs.x, rs.y ); | |
| m->place_items( "novels", 60, p.x, p.y, rs.x, rs.y, false, turn ); | |
| m->place_items( "magazines", 20, p.x, p.y, rs.x, rs.y, false, turn ); | |
| } else if( rn == 6 ) { | |
| tripoint rs = p + furn_space::best_expand( *m, p, rng( 0, 2 ), 0 ); | |
| square_furn( m, f_locker, p.x, p.y, rs.x, rs.y ); | |
| m->place_items( "trash", 60, p.x, p.y, rs.x, rs.y, false, turn ); | |
| m->place_items( "home_hw", 20, p.x, p.y, rs.x, rs.y, false, turn ); | |
| } else if( rn == 7 ) { | |
| tripoint rs = p + furn_space::best_expand( *m, p, 0, rng( 0, 2 ) ); | |
| square_furn( m, f_locker, p.x, p.y, rs.x, rs.y ); | |
| m->place_items( "trash", 60, p.x, p.y, rs.x, rs.y, false, turn ); | |
| m->place_items( "home_hw", 20, p.x, p.y, rs.x, rs.y, false, turn ); | |
| } else { | |
| tripoint rs = p + furn_space::best_expand( *m, p, rng( 0, 2 ), rng( 0, 2 ) ); | |
| square_furn( m, f_table, p.x, p.y, rs.x, rs.y ); | |
| } | |
| } | |
| } | |
| m->place_items( "bedroom", 60, 1, 1, SEEX * 2 - 2, SEEY * 2 - 2, false, turn ); | |
| m->place_items( "home_hw", 80, 1, 1, SEEX * 2 - 2, SEEY * 2 - 2, false, turn ); | |
| m->place_items( "homeguns", 10, 1, 1, SEEX * 2 - 2, SEEY * 2 - 2, false, turn ); | |
| // Chance of zombies in the basement | |
| m->place_spawns( mongroup_id( "GROUP_ZOMBIE" ), 2, 1, 1, SEEX * 2 - 2, SEEY * 2 - 2, density ); | |
| } | |
| void mapgen_basement_spiders( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| // Oh no! A spider nest! | |
| mapgen_basement_junk( m, terrain_type, dat, turn, density ); | |
| auto spider_type = mon_spider_widow_giant; | |
| auto egg_type = f_egg_sackbw; | |
| if( one_in( 2 ) ) { | |
| spider_type = mon_spider_cellar_giant; | |
| egg_type = f_egg_sackcs; | |
| } | |
| for( int i = 1; i < 22; i++ ) { | |
| for( int j = 1; j < 22; j++ ) { | |
| if( m->ter( i, j ).obj().movecost == 0 ) { | |
| // Wall, skip | |
| continue; | |
| } | |
| if( !one_in( 3 ) ) { | |
| madd_field( m, i, j, fd_web, rng( 1, 3 ) ); | |
| } | |
| if( one_in( 30 ) && m->passable( i, j ) ) { | |
| m->furn_set( i, j, egg_type ); | |
| m->add_spawn( spider_type, rng( 1, 2 ), i, j ); //hope you like'em spiders | |
| m->remove_field( { i, j, m->get_abs_sub().z }, fd_web ); | |
| } | |
| } | |
| } | |
| m->place_items( "rare", 70, 1, 1, SEEX * 2 - 1, SEEY * 2 - 5, false, turn ); | |
| } | |
| void mapgen_cave( map *m, oter_id, mapgendata dat, const time_point &turn, float density ) | |
| { | |
| if( dat.above() == "cave" ) { | |
| // We're underground! // FIXME: y u no use z-level | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| bool floorHere = ( rng( 0, 6 ) < i || SEEX * 2 - rng( 1, 7 ) > i || | |
| rng( 0, 6 ) < j || SEEY * 2 - rng( 1, 7 ) > j ); | |
| if( floorHere ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } else { | |
| m->ter_set( i, j, t_rock ); | |
| } | |
| } | |
| } | |
| square( m, t_slope_up, SEEX - 1, SEEY - 1, SEEX, SEEY ); | |
| switch( rng( 1, 10 ) ) { | |
| case 1: | |
| // natural refuse, chance of minerals | |
| m->place_items( "cave_minerals", 50, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| m->place_items( "monparts", 80, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| break; | |
| case 2: | |
| // trash, minerals less likely | |
| m->place_items( "cave_minerals", 25, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| m->place_items( "trash", 70, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| break; | |
| case 3: | |
| // bat corpses | |
| m->place_items( "cave_minerals", 50, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| for( int i = rng( 1, 12 ); i > 0; i-- ) { | |
| m->add_item_or_charges( rng( 1, SEEX * 2 - 1 ), rng( 1, SEEY * 2 - 1 ), | |
| item::make_corpse( mon_bat ) ); | |
| } | |
| break; | |
| case 4: | |
| // ant food, chance of 80 | |
| m->place_items( "cave_minerals", 25, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| m->place_items( "ant_food", 85, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| break; | |
| case 5: { | |
| // hermitage | |
| int origx = rng( SEEX - 1, SEEX ), | |
| origy = rng( SEEY - 1, SEEY ), | |
| hermx = rng( SEEX - 6, SEEX + 5 ), | |
| hermy = rng( SEEX - 6, SEEY + 5 ); | |
| std::vector<point> bloodline = line_to( origx, origy, hermx, hermy, 0 ); | |
| for( auto &ii : bloodline ) { | |
| madd_field( m, ii.x, ii.y, fd_blood, 2 ); | |
| } | |
| m->add_item_or_charges( hermx, hermy, item::make_corpse() ); | |
| // This seems verbose. Maybe a function to spawn from a list of item groups? | |
| m->place_items( "stash_food", 50, hermx - 1, hermy - 1, hermx + 1, hermy + 1, true, turn ); | |
| m->place_items( "gear_survival", 50, hermx - 1, hermy - 1, hermx + 1, hermy + 1, true, turn ); | |
| m->place_items( "survival_armor", 50, hermx - 1, hermy - 1, hermx + 1, hermy + 1, true, turn ); | |
| m->place_items( "weapons", 40, hermx - 1, hermy - 1, hermx + 1, hermy + 1, true, turn ); | |
| m->place_items( "magazines", 40, hermx - 1, hermy - 1, hermx + 1, hermy + 1, true, turn ); | |
| m->place_items( "rare", 30, hermx - 1, hermy - 1, hermx + 1, hermy + 1, true, turn ); | |
| break; | |
| } | |
| default: | |
| // nothing except maybe minerals, default occurs half the time | |
| m->place_items( "cave_minerals", 50, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| break; | |
| } | |
| m->place_spawns( mongroup_id( "GROUP_CAVE" ), 2, 6, 6, 18, 18, 1.0 ); | |
| } else { // We're above ground! | |
| // First, draw a forest | |
| /* | |
| draw_map(oter_id("forest"), dat.north(), dat.east(), dat.south(), dat.west(), dat.neast(), dat.seast(), dat.nwest(), dat.swest(), | |
| dat.above(), turn, g, density, dat.zlevel); | |
| */ | |
| mapgen_forest( m, oter_str_id( "forest" ).id(), dat, turn, density ); | |
| // Clear the center with some rocks | |
| square( m, t_rock, SEEX - 6, SEEY - 6, SEEX + 5, SEEY + 5 ); | |
| int pathx = 0; | |
| int pathy = 0; | |
| if( one_in( 2 ) ) { | |
| pathx = rng( SEEX - 6, SEEX + 5 ); | |
| pathy = ( one_in( 2 ) ? SEEY - 8 : SEEY + 7 ); | |
| } else { | |
| pathx = ( one_in( 2 ) ? SEEX - 8 : SEEX + 7 ); | |
| pathy = rng( SEEY - 6, SEEY + 5 ); | |
| } | |
| std::vector<point> pathline = line_to( pathx, pathy, SEEX - 1, SEEY - 1, 0 ); | |
| for( auto &ii : pathline ) { | |
| square( m, t_dirt, ii.x, ii.y, | |
| ii.x + 1, ii.y + 1 ); | |
| } | |
| while( !one_in( 8 ) ) { | |
| m->ter_set( rng( SEEX - 6, SEEX + 5 ), rng( SEEY - 6, SEEY + 5 ), t_dirt ); | |
| } | |
| square( m, t_slope_down, SEEX - 1, SEEY - 1, SEEX, SEEY ); | |
| } | |
| } | |
| void mapgen_cave_rat( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| fill_background( m, t_rock ); | |
| if( dat.above() == "cave_rat" ) { // Finale | |
| rough_circle( m, t_rock_floor, SEEX, SEEY, 8 ); | |
| square( m, t_rock_floor, SEEX - 1, SEEY, SEEX, SEEY * 2 - 2 ); | |
| line( m, t_slope_up, SEEX - 1, SEEY * 2 - 3, SEEX, SEEY * 2 - 2 ); | |
| for( int i = SEEX - 4; i <= SEEX + 4; i++ ) { | |
| for( int j = SEEY - 4; j <= SEEY + 4; j++ ) { | |
| if( ( i <= SEEX - 2 || i >= SEEX + 2 ) && ( j <= SEEY - 2 || j >= SEEY + 2 ) ) { | |
| m->add_spawn( mon_sewer_rat, 1, i, j ); | |
| } | |
| } | |
| } | |
| m->add_spawn( mon_rat_king, 1, SEEX, SEEY ); | |
| m->place_items( "rare", 75, SEEX - 4, SEEY - 4, SEEX + 4, SEEY + 4, true, turn ); | |
| } else { // Level 1 | |
| int cavex = SEEX; | |
| int cavey = SEEY * 2 - 3; | |
| int stairsx = SEEX - 1, stairsy = 1; // Default stairs location--may change | |
| int centerx = 0; | |
| do { | |
| cavex += rng( -1, 1 ); | |
| cavey -= rng( 0, 1 ); | |
| for( int cx = cavex - 1; cx <= cavex + 1; cx++ ) { | |
| for( int cy = cavey - 1; cy <= cavey + 1; cy++ ) { | |
| m->ter_set( cx, cy, t_rock_floor ); | |
| if( one_in( 10 ) ) { | |
| madd_field( m, cx, cy, fd_blood, rng( 1, 3 ) ); | |
| } | |
| if( one_in( 20 ) ) { | |
| m->add_spawn( mon_sewer_rat, 1, cx, cy ); | |
| } | |
| } | |
| } | |
| if( cavey == SEEY - 1 ) { | |
| centerx = cavex; | |
| } | |
| } while( cavey > 2 ); | |
| // Now draw some extra passages! | |
| do { | |
| int tox = ( one_in( 2 ) ? 2 : SEEX * 2 - 3 ), toy = rng( 2, SEEY * 2 - 3 ); | |
| std::vector<point> path = line_to( centerx, SEEY - 1, tox, toy, 0 ); | |
| for( auto &i : path ) { | |
| for( int cx = i.x - 1; cx <= i.x + 1; cx++ ) { | |
| for( int cy = i.y - 1; cy <= i.y + 1; cy++ ) { | |
| m->ter_set( cx, cy, t_rock_floor ); | |
| if( one_in( 10 ) ) { | |
| madd_field( m, cx, cy, fd_blood, rng( 1, 3 ) ); | |
| } | |
| if( one_in( 20 ) ) { | |
| m->add_spawn( mon_sewer_rat, 1, cx, cy ); | |
| } | |
| } | |
| } | |
| } | |
| if( one_in( 2 ) ) { | |
| stairsx = tox; | |
| stairsy = toy; | |
| } | |
| } while( one_in( 2 ) ); | |
| // Finally, draw the stairs up and down. | |
| m->ter_set( SEEX - 1, SEEX * 2 - 2, t_slope_up ); | |
| m->ter_set( SEEX, SEEX * 2 - 2, t_slope_up ); | |
| m->ter_set( stairsx, stairsy, t_slope_down ); | |
| } | |
| } | |
| void mapgen_cavern( map *m, oter_id, mapgendata dat, const time_point &turn, float ) | |
| { | |
| for( int i = 0; i < 4; | |
| i++ ) { // FIXME: don't look at me like that, this was messed up before I touched it :P - AD | |
| dat.set_dir( i, | |
| ( dat.t_nesw[i] == "cavern" || dat.t_nesw[i] == "subway_ns" || | |
| dat.t_nesw[i] == "subway_ew" ? 0 : 3 ) | |
| ); | |
| } | |
| dat.e_fac = SEEX * 2 - 1 - dat.e_fac; | |
| dat.s_fac = SEEY * 2 - 1 - dat.s_fac; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( j < dat.n_fac || j > dat.s_fac || i < dat.w_fac || i > dat.e_fac ) && | |
| ( !one_in( 3 ) || j == 0 || j == SEEY * 2 - 1 || i == 0 || i == SEEX * 2 - 1 ) ) { | |
| m->ter_set( i, j, t_rock ); | |
| } else { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| int rn = rng( 0, 2 ) * rng( 0, 3 ) + rng( 0, 1 ); // Number of pillars | |
| for( int n = 0; n < rn; n++ ) { | |
| int px = rng( 5, SEEX * 2 - 6 ); | |
| int py = rng( 5, SEEY * 2 - 6 ); | |
| for( int i = px - 1; i <= px + 1; i++ ) { | |
| for( int j = py - 1; j <= py + 1; j++ ) { | |
| m->ter_set( i, j, t_rock ); | |
| } | |
| } | |
| } | |
| if( connects_to( dat.north(), 2 ) ) { | |
| for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { | |
| for( int j = 0; j <= SEEY; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( connects_to( dat.east(), 3 ) ) { | |
| for( int i = SEEX; i <= SEEX * 2 - 1; i++ ) { | |
| for( int j = SEEY - 2; j <= SEEY + 3; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( connects_to( dat.south(), 0 ) ) { | |
| for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { | |
| for( int j = SEEY; j <= SEEY * 2 - 1; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( connects_to( dat.west(), 1 ) ) { | |
| for( int i = 0; i <= SEEX; i++ ) { | |
| for( int j = SEEY - 2; j <= SEEY + 3; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| m->place_items( "cavern", 60, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, false, turn ); | |
| if( one_in( 6 ) ) { // Miner remains | |
| int x = 0; | |
| int y = 0; | |
| do { | |
| x = rng( 0, SEEX * 2 - 1 ); | |
| y = rng( 0, SEEY * 2 - 1 ); | |
| } while( m->impassable( x, y ) ); | |
| if( !one_in( 3 ) ) { | |
| m->spawn_item( x, y, "jackhammer" ); | |
| } | |
| if( one_in( 3 ) ) { | |
| m->spawn_item( x, y, "mask_dust" ); | |
| } | |
| if( one_in( 2 ) ) { | |
| m->spawn_item( x, y, "hat_hard" ); | |
| } | |
| while( !one_in( 3 ) ) { | |
| for( int i = 0; i < 3; ++i ) { | |
| m->put_items_from_loc( "cannedfood", tripoint( x, y, m->get_abs_sub().z ), turn ); | |
| } | |
| } | |
| } | |
| } | |
| void mapgen_rock_partial( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| fill_background( m, t_rock ); | |
| for( int i = 0; i < 4; i++ ) { | |
| if( dat.t_nesw[i] == "cavern" || dat.t_nesw[i] == "slimepit" || | |
| dat.t_nesw[i] == "slimepit_down" ) { | |
| dat.dir( i ) = 6; | |
| } else { | |
| dat.dir( i ) = 0; | |
| } | |
| } | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( rng( 0, dat.n_fac ) > j || rng( 0, dat.s_fac ) > SEEY * 2 - 1 - j || | |
| rng( 0, dat.w_fac ) > i || rng( 0, dat.e_fac ) > SEEX * 2 - 1 - i ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| } | |
| void mapgen_rock( map *m, oter_id, mapgendata, const time_point &, float ) | |
| { | |
| fill_background( m, t_rock ); | |
| } | |
| void mapgen_open_air( map *m, oter_id, mapgendata, const time_point &, float ) | |
| { | |
| fill_background( m, t_open_air ); | |
| } | |
| void mapgen_rift( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| if( dat.north() != "rift" && dat.north() != "hellmouth" ) { | |
| if( connects_to( dat.north(), 2 ) ) { | |
| dat.n_fac = rng( -6, -2 ); | |
| } else { | |
| dat.n_fac = rng( 2, 6 ); | |
| } | |
| } | |
| if( dat.east() != "rift" && dat.east() != "hellmouth" ) { | |
| if( connects_to( dat.east(), 3 ) ) { | |
| dat.e_fac = rng( -6, -2 ); | |
| } else { | |
| dat.e_fac = rng( 2, 6 ); | |
| } | |
| } | |
| if( dat.south() != "rift" && dat.south() != "hellmouth" ) { | |
| if( connects_to( dat.south(), 0 ) ) { | |
| dat.s_fac = rng( -6, -2 ); | |
| } else { | |
| dat.s_fac = rng( 2, 6 ); | |
| } | |
| } | |
| if( dat.west() != "rift" && dat.west() != "hellmouth" ) { | |
| if( connects_to( dat.west(), 1 ) ) { | |
| dat.w_fac = rng( -6, -2 ); | |
| } else { | |
| dat.w_fac = rng( 2, 6 ); | |
| } | |
| } | |
| // Negative *_fac values indicate rock floor connection, otherwise solid rock | |
| // Of course, if we connect to a rift, *_fac = 0, and thus lava extends all the | |
| // way. | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( dat.n_fac < 0 && j < dat.n_fac * -1 ) || ( dat.s_fac < 0 && j >= SEEY * 2 - dat.s_fac ) || | |
| ( dat.w_fac < 0 && i < dat.w_fac * -1 ) || ( dat.e_fac < 0 && i >= SEEX * 2 - dat.e_fac ) ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } else if( j < dat.n_fac || j >= SEEY * 2 - dat.s_fac || | |
| i < dat.w_fac || i >= SEEX * 2 - dat.e_fac ) { | |
| m->ter_set( i, j, t_rock ); | |
| } else { | |
| m->ter_set( i, j, t_lava ); | |
| } | |
| } | |
| } | |
| } | |
| void mapgen_hellmouth( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| // what is this, doom? | |
| // .. seriously, though... | |
| for( int i = 0; i < 4; i++ ) { | |
| if( dat.t_nesw[i] != "rift" && dat.t_nesw[i] != "hellmouth" ) { | |
| dat.dir( i ) = 6; | |
| } | |
| } | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( j < dat.n_fac || j >= SEEY * 2 - dat.s_fac || i < dat.w_fac || i >= SEEX * 2 - dat.e_fac || | |
| ( i >= 6 && i < SEEX * 2 - 6 && j >= 6 && j < SEEY * 2 - 6 ) ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } else { | |
| m->ter_set( i, j, t_lava ); | |
| } | |
| if( i >= SEEX - 1 && i <= SEEX && j >= SEEY - 1 && j <= SEEY ) { | |
| m->ter_set( i, j, t_slope_down ); | |
| } | |
| } | |
| } | |
| switch( rng( 0, 4 ) ) { // Randomly chosen "altar" design | |
| case 0: | |
| for( int i = 7; i <= 16; i += 3 ) { | |
| m->ter_set( i, 6, t_rock ); | |
| m->ter_set( i, 17, t_rock ); | |
| m->ter_set( 6, i, t_rock ); | |
| m->ter_set( 17, i, t_rock ); | |
| if( i > 7 && i < 16 ) { | |
| m->ter_set( i, 10, t_rock ); | |
| m->ter_set( i, 13, t_rock ); | |
| } else { | |
| m->ter_set( i - 1, 6, t_rock ); | |
| m->ter_set( i - 1, 10, t_rock ); | |
| m->ter_set( i - 1, 13, t_rock ); | |
| m->ter_set( i - 1, 17, t_rock ); | |
| } | |
| } | |
| break; | |
| case 1: | |
| for( int i = 6; i < 11; i++ ) { | |
| m->ter_set( i, i, t_lava ); | |
| m->ter_set( SEEX * 2 - 1 - i, i, t_lava ); | |
| m->ter_set( i, SEEY * 2 - 1 - i, t_lava ); | |
| m->ter_set( SEEX * 2 - 1 - i, SEEY * 2 - 1 - i, t_lava ); | |
| if( i < 10 ) { | |
| m->ter_set( i + 1, i, t_lava ); | |
| m->ter_set( SEEX * 2 - i, i, t_lava ); | |
| m->ter_set( i + 1, SEEY * 2 - 1 - i, t_lava ); | |
| m->ter_set( SEEX * 2 - i, SEEY * 2 - 1 - i, t_lava ); | |
| m->ter_set( i, i + 1, t_lava ); | |
| m->ter_set( SEEX * 2 - 1 - i, i + 1, t_lava ); | |
| m->ter_set( i, SEEY * 2 - i, t_lava ); | |
| m->ter_set( SEEX * 2 - 1 - i, SEEY * 2 - i, t_lava ); | |
| } | |
| if( i < 9 ) { | |
| m->ter_set( i + 2, i, t_rock ); | |
| m->ter_set( SEEX * 2 - i + 1, i, t_rock ); | |
| m->ter_set( i + 2, SEEY * 2 - 1 - i, t_rock ); | |
| m->ter_set( SEEX * 2 - i + 1, SEEY * 2 - 1 - i, t_rock ); | |
| m->ter_set( i, i + 2, t_rock ); | |
| m->ter_set( SEEX * 2 - 1 - i, i + 2, t_rock ); | |
| m->ter_set( i, SEEY * 2 - i + 1, t_rock ); | |
| m->ter_set( SEEX * 2 - 1 - i, SEEY * 2 - i + 1, t_rock ); | |
| } | |
| } | |
| break; | |
| case 2: | |
| for( int i = 7; i < 17; i++ ) { | |
| m->ter_set( i, 6, t_rock ); | |
| m->ter_set( 6, i, t_rock ); | |
| m->ter_set( i, 17, t_rock ); | |
| m->ter_set( 17, i, t_rock ); | |
| if( i != 7 && i != 16 && i != 11 && i != 12 ) { | |
| m->ter_set( i, 8, t_rock ); | |
| m->ter_set( 8, i, t_rock ); | |
| m->ter_set( i, 15, t_rock ); | |
| m->ter_set( 15, i, t_rock ); | |
| } | |
| if( i == 11 || i == 12 ) { | |
| m->ter_set( i, 10, t_rock ); | |
| m->ter_set( 10, i, t_rock ); | |
| m->ter_set( i, 13, t_rock ); | |
| m->ter_set( 13, i, t_rock ); | |
| } | |
| } | |
| break; | |
| case 3: | |
| for( int i = 6; i < 11; i++ ) { | |
| for( int j = 6; j < 11; j++ ) { | |
| m->ter_set( i, j, t_lava ); | |
| m->ter_set( SEEX * 2 - 1 - i, j, t_lava ); | |
| m->ter_set( i, SEEY * 2 - 1 - j, t_lava ); | |
| m->ter_set( SEEX * 2 - 1 - i, SEEY * 2 - 1 - j, t_lava ); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| void mapgen_ants_curved( map *m, oter_id terrain_type, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| int x = SEEX; | |
| int y = 1; | |
| int rn = 0; | |
| // First, set it all to rock | |
| fill_background( m, t_rock ); | |
| for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { | |
| m->ter_set( i, 0, t_rock_floor ); | |
| m->ter_set( i, 1, t_rock_floor ); | |
| m->ter_set( i, 2, t_rock_floor ); | |
| m->ter_set( SEEX * 2 - 1, i, t_rock_floor ); | |
| m->ter_set( SEEX * 2 - 2, i, t_rock_floor ); | |
| m->ter_set( SEEX * 2 - 3, i, t_rock_floor ); | |
| } | |
| do { | |
| for( int i = x - 2; i <= x + 3; i++ ) { | |
| for( int j = y - 2; j <= y + 3; j++ ) { | |
| if( i > 0 && i < SEEX * 2 - 1 && j > 0 && j < SEEY * 2 - 1 ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( rn < SEEX ) { | |
| x += rng( -1, 1 ); | |
| y++; | |
| } else { | |
| x++; | |
| if( !one_in( x - SEEX ) ) { | |
| y += rng( -1, 1 ); | |
| } else if( y < SEEY ) { | |
| y++; | |
| } else if( y > SEEY ) { | |
| y--; | |
| } | |
| } | |
| rn++; | |
| } while( x < SEEX * 2 - 1 || y != SEEY ); | |
| for( int i = x - 2; i <= x + 3; i++ ) { | |
| for( int j = y - 2; j <= y + 3; j++ ) { | |
| if( i > 0 && i < SEEX * 2 - 1 && j > 0 && j < SEEY * 2 - 1 ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( terrain_type == "ants_es" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "ants_sw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "ants_wn" ) { | |
| m->rotate( 3 ); | |
| } | |
| } | |
| void mapgen_ants_four_way( map *m, oter_id, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| fill_background( m, t_rock ); | |
| int x = SEEX; | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| for( int i = x - 2; i <= x + 3; i++ ) { | |
| if( i >= 1 && i < SEEX * 2 - 1 ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| x += rng( -1, 1 ); | |
| while( abs( SEEX - x ) > SEEY * 2 - j - 1 ) { | |
| if( x < SEEX ) { | |
| x++; | |
| } | |
| if( x > SEEX ) { | |
| x--; | |
| } | |
| } | |
| } | |
| int y = SEEY; | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = y - 2; j <= y + 3; j++ ) { | |
| if( j >= 1 && j < SEEY * 2 - 1 ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| y += rng( -1, 1 ); | |
| while( abs( SEEY - y ) > SEEX * 2 - i - 1 ) { | |
| if( y < SEEY ) { | |
| y++; | |
| } | |
| if( y > SEEY ) { | |
| y--; | |
| } | |
| } | |
| } | |
| } | |
| void mapgen_ants_straight( map *m, oter_id terrain_type, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| int x = SEEX; | |
| fill_background( m, t_rock ); | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| for( int i = x - 2; i <= x + 3; i++ ) { | |
| if( i >= 1 && i < SEEX * 2 - 1 ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| x += rng( -1, 1 ); | |
| while( abs( SEEX - x ) > SEEX * 2 - j - 1 ) { | |
| if( x < SEEX ) { | |
| x++; | |
| } | |
| if( x > SEEX ) { | |
| x--; | |
| } | |
| } | |
| } | |
| if( terrain_type == "ants_ew" ) { | |
| m->rotate( 1 ); | |
| } | |
| } | |
| void mapgen_ants_tee( map *m, oter_id terrain_type, mapgendata dat, const time_point &, float ) | |
| { | |
| ( void )dat; | |
| fill_background( m, t_rock ); | |
| int x = SEEX; | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| for( int i = x - 2; i <= x + 3; i++ ) { | |
| if( i >= 1 && i < SEEX * 2 - 1 ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| x += rng( -1, 1 ); | |
| while( abs( SEEX - x ) > SEEY * 2 - j - 1 ) { | |
| if( x < SEEX ) { | |
| x++; | |
| } | |
| if( x > SEEX ) { | |
| x--; | |
| } | |
| } | |
| } | |
| int y = SEEY; | |
| for( int i = SEEX; i < SEEX * 2; i++ ) { | |
| for( int j = y - 2; j <= y + 3; j++ ) { | |
| if( j >= 1 && j < SEEY * 2 - 1 ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| y += rng( -1, 1 ); | |
| while( abs( SEEY - y ) > SEEX * 2 - 1 - i ) { | |
| if( y < SEEY ) { | |
| y++; | |
| } | |
| if( y > SEEY ) { | |
| y--; | |
| } | |
| } | |
| } | |
| if( terrain_type == "ants_new" ) { | |
| m->rotate( 3 ); | |
| } | |
| if( terrain_type == "ants_nsw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "ants_esw" ) { | |
| m->rotate( 1 ); | |
| } | |
| } | |
| static void mapgen_ants_generic( map *m, oter_id terrain_type, mapgendata dat, | |
| const time_point &turn, float ) | |
| { | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( i < SEEX - 4 || i > SEEX + 5 || j < SEEY - 4 || j > SEEY + 5 ) { | |
| m->ter_set( i, j, t_rock ); | |
| } else { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| int rn = rng( 10, 20 ); | |
| int x = 0; | |
| int y = 0; | |
| for( int n = 0; n < rn; n++ ) { | |
| int cw = rng( 1, 8 ); | |
| do { | |
| x = rng( 1 + cw, SEEX * 2 - 2 - cw ); | |
| y = rng( 1 + cw, SEEY * 2 - 2 - cw ); | |
| } while( m->ter( x, y ) == t_rock ); | |
| for( int i = x - cw; i <= x + cw; i++ ) { | |
| for( int j = y - cw; j <= y + cw; j++ ) { | |
| if( trig_dist( x, y, i, j ) <= cw ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| } | |
| if( connects_to( dat.north(), 2 ) || | |
| is_ot_match( "ants_lab", dat.north(), ot_match_type::contains ) ) { | |
| for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { | |
| for( int j = 0; j <= SEEY; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( connects_to( dat.east(), 3 ) || | |
| is_ot_match( "ants_lab", dat.east(), ot_match_type::contains ) ) { | |
| for( int i = SEEX; i <= SEEX * 2 - 1; i++ ) { | |
| for( int j = SEEY - 2; j <= SEEY + 3; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( connects_to( dat.south(), 0 ) || | |
| is_ot_match( "ants_lab", dat.south(), ot_match_type::contains ) ) { | |
| for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { | |
| for( int j = SEEY; j <= SEEY * 2 - 1; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( connects_to( dat.west(), 1 ) || | |
| is_ot_match( "ants_lab", dat.west(), ot_match_type::contains ) ) { | |
| for( int i = 0; i <= SEEX; i++ ) { | |
| for( int j = SEEY - 2; j <= SEEY + 3; j++ ) { | |
| m->ter_set( i, j, t_rock_floor ); | |
| } | |
| } | |
| } | |
| if( terrain_type == "ants_food" ) { | |
| m->place_items( "ant_food", 92, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| } else { | |
| m->place_items( "ant_egg", 98, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| } | |
| if( terrain_type == "ants_queen" ) { | |
| m->add_spawn( mon_ant_queen, 1, SEEX, SEEY ); | |
| } else if( terrain_type == "ants_larvae" ) { | |
| m->add_spawn( mon_ant_larva, 10, SEEX, SEEY ); | |
| } | |
| } | |
| void mapgen_ants_food( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| mapgen_ants_generic( m, terrain_type, dat, turn, density ); | |
| m->place_items( "ant_food", 92, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| } | |
| void mapgen_ants_larvae( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| mapgen_ants_generic( m, terrain_type, dat, turn, density ); | |
| m->place_items( "ant_egg", 98, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| m->add_spawn( mon_ant_larva, 10, SEEX, SEEY ); | |
| } | |
| void mapgen_ants_queen( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| mapgen_ants_generic( m, terrain_type, dat, turn, density ); | |
| m->place_items( "ant_egg", 98, 0, 0, SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| m->add_spawn( mon_ant_queen, 1, SEEX, SEEY ); | |
| } | |
| void mapgen_tutorial( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| ( void ) density; // Not used, no normally generated zombies here | |
| ( void ) terrain_type; // Not used, should always be "tutorial" | |
| ( void ) turn; // Not used for tutorial | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( j == 0 || j == SEEY * 2 - 1 ) { | |
| m->ter_set( i, j, t_wall ); | |
| } else if( i == 0 || i == SEEX * 2 - 1 ) { | |
| m->ter_set( i, j, t_wall ); | |
| } else if( j == SEEY ) { | |
| if( i % 4 == 2 ) { | |
| m->ter_set( i, j, t_door_c ); | |
| } else if( i % 5 == 3 ) { | |
| m->ter_set( i, j, t_window_domestic ); | |
| } else { | |
| m->ter_set( i, j, t_wall ); | |
| } | |
| } else { | |
| m->ter_set( i, j, t_floor ); | |
| } | |
| } | |
| } | |
| m->furn_set( 7, SEEY * 2 - 4, f_rack ); | |
| m->place_gas_pump( SEEX * 2 - 2, SEEY * 2 - 4, rng( 500, 1000 ) ); | |
| if( dat.zlevel < 0 ) { | |
| m->ter_set( SEEX - 2, SEEY + 2, t_stairs_up ); | |
| m->ter_set( 2, 2, t_water_sh ); | |
| m->ter_set( 2, 3, t_water_sh ); | |
| m->ter_set( 3, 2, t_water_sh ); | |
| m->ter_set( 3, 3, t_water_sh ); | |
| } else { | |
| m->spawn_item( 5, SEEY + 1, "helmet_bike" ); | |
| m->spawn_item( 4, SEEY + 1, "backpack" ); | |
| m->spawn_item( 3, SEEY + 1, "pants_cargo" ); | |
| m->spawn_item( 7, SEEY * 2 - 4, "machete" ); | |
| m->spawn_item( 7, SEEY * 2 - 4, "9mm" ); | |
| m->spawn_item( 7, SEEY * 2 - 4, "9mmP" ); | |
| m->spawn_item( 7, SEEY * 2 - 4, "uzi" ); | |
| m->spawn_item( 7, SEEY * 2 - 4, "uzimag" ); | |
| m->spawn_item( SEEX * 2 - 2, SEEY + 5, "bubblewrap" ); | |
| m->spawn_item( SEEX * 2 - 2, SEEY + 6, "grenade" ); | |
| m->spawn_item( SEEX * 2 - 3, SEEY + 6, "flashlight" ); | |
| m->spawn_item( SEEX * 2 - 2, SEEY + 7, "cig" ); | |
| m->spawn_item( SEEX * 2 - 2, SEEY + 7, "codeine" ); | |
| m->spawn_item( SEEX * 2 - 3, SEEY + 7, "water" ); | |
| m->ter_set( SEEX - 2, SEEY + 2, t_stairs_down ); | |
| } | |
| } | |
| void mapgen_forest( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float ) | |
| { | |
| // Adjacency factor is basically used to weight the frequency of a feature | |
| // being placed by the relative sparseness of the current terrain to its | |
| // neighbors. For example, a forest_thick surrounded by forest_thick on | |
| // all sides can be much more dense than a forest_water surrounded by | |
| // fields on all sides. It's a little magic-number-y but somewhat replicates | |
| // the behavior of the previous forest mapgen when fading forest terrains | |
| // into each other and non-forest terrains. | |
| const auto get_sparseness_adjacency_factor = [&dat]( const oter_id & ot ) { | |
| const auto biome = dat.region.forest_composition.biomes.find( ot ); | |
| if( biome == dat.region.forest_composition.biomes.end() ) { | |
| // If there is no defined biome for this oter, use 0. It's possible | |
| // to specify biomes in the forest regional settings that are not | |
| // rendered by this forest map gen method, in order to control | |
| // how terrains are blended together (e.g. specify roads with an equal | |
| // sparsness adjacency factor to forests so that forests don't fade out | |
| // as they transition to roads. | |
| return 0; | |
| } | |
| return biome->second.sparseness_adjacency_factor; | |
| }; | |
| const auto fill_adjacency_factor = [&dat, &get_sparseness_adjacency_factor]( int self_factor ) { | |
| dat.fill( self_factor ); | |
| for( int i = 0; i < 4; i++ ) { | |
| dat.dir( i ) += get_sparseness_adjacency_factor( dat.t_nesw[i] ); | |
| } | |
| }; | |
| const ter_furn_id no_ter_furn = ter_furn_id(); | |
| const auto get_feature_for_neighbor = [&dat, | |
| &no_ter_furn]( const std::map<oter_id, ter_furn_id> &biome_features, | |
| const om_direction::type dir ) { | |
| const oter_id dir_ot = dat.neighbor_at( dir ); | |
| const auto feature = biome_features.find( dir_ot ); | |
| if( feature == biome_features.end() ) { | |
| // If we have no biome for this neighbor, then we just return any empty feature. | |
| // As with the sparseness adjacency factor, it's possible to define non-forest | |
| // biomes in the regional settings so that they provide neighbor features | |
| // here for blending purposes (e.g. define dirt terrain for roads so that the | |
| // ground fades from forest ground cover to dirt as blends with roads. | |
| return no_ter_furn; | |
| } | |
| return feature->second; | |
| }; | |
| // The max sparseness is calculated across all the possible biomes, not just the adjacent ones. | |
| const auto get_max_sparseness_adjacency_factor = [&dat]() { | |
| if( dat.region.forest_composition.biomes.empty() ) { | |
| return 0; | |
| } | |
| std::vector<int> factors; | |
| for( auto &b : dat.region.forest_composition.biomes ) { | |
| factors.push_back( b.second.sparseness_adjacency_factor ); | |
| } | |
| return *max_element( std::begin( factors ), std::end( factors ) ); | |
| }; | |
| // Get the sparesness factor for this terrain, and fill it. | |
| const int factor = get_sparseness_adjacency_factor( terrain_type ); | |
| fill_adjacency_factor( factor ); | |
| const int max_factor = get_max_sparseness_adjacency_factor(); | |
| // Our margins for blending divide the overmap terrain into nine sections. | |
| static constexpr int margin_x = SEEX * 2 / 3; | |
| static constexpr int margin_y = SEEY * 2 / 3; | |
| const auto get_blended_feature = [&no_ter_furn, &max_factor, &factor, | |
| &get_feature_for_neighbor, &terrain_type, &dat]( int x, int y ) { | |
| // Pick one random feature from each biome according to the biome defs and save it into a lookup. | |
| // We'll blend these features together below based on the current and adjacent terrains. | |
| std::map<oter_id, ter_furn_id> biome_features; | |
| for( auto &b : dat.region.forest_composition.biomes ) { | |
| biome_features[b.first] = b.second.pick(); | |
| } | |
| // Get a feature for ourself and each of the adjacent overmap terrains. | |
| const ter_furn_id east_feature = get_feature_for_neighbor( biome_features, | |
| om_direction::type::east ); | |
| const ter_furn_id west_feature = get_feature_for_neighbor( biome_features, | |
| om_direction::type::west ); | |
| const ter_furn_id north_feature = get_feature_for_neighbor( biome_features, | |
| om_direction::type::north ); | |
| const ter_furn_id south_feature = get_feature_for_neighbor( biome_features, | |
| om_direction::type::south ); | |
| const ter_furn_id self_feature = biome_features[terrain_type]; | |
| // We'll use our margins and the four adjacent overmap terrains to pick a blended | |
| // feature based on the features we picked above and a linear weight as we | |
| // transition through the margins. | |
| // | |
| // (0,0) NORTH | |
| // --------------- | |
| // | NW | W | NE | | |
| // |----|---|----| | |
| // WEST | W | | E | EAST | |
| // |----|---|----| | |
| // | SW | S | SE | | |
| // --------------- | |
| // SOUTH (SEEX * 2, SEEY * 2) | |
| const int west_weight = std::max( margin_x - x, 0 ); | |
| const int east_weight = std::max( x - ( SEEX * 2 - margin_x ) + 1, 0 ); | |
| const int north_weight = std::max( margin_y - y, 0 ); | |
| const int south_weight = std::max( y - ( SEEY * 2 - margin_y ) + 1, 0 ); | |
| // We'll build a weighted list of features to pull from at the end. | |
| weighted_int_list<const ter_furn_id> feature_pool; | |
| // W sections | |
| if( x < margin_x ) { | |
| // NW corner - blend N, W, and self | |
| if( y < margin_y ) { | |
| feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.n_fac + dat.w_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, 1 ); | |
| feature_pool.add( west_feature, west_weight ); | |
| feature_pool.add( north_feature, north_weight ); | |
| } | |
| // SW corner - blend S, W, and self | |
| else if( y > SEEY * 2 - margin_y ) { | |
| feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.s_fac + dat.w_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, factor ); | |
| feature_pool.add( west_feature, west_weight ); | |
| feature_pool.add( south_feature, south_weight ); | |
| } | |
| // W edge - blend W and self | |
| else { | |
| feature_pool.add( no_ter_furn, 2 * max_factor - ( dat.w_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, factor ); | |
| feature_pool.add( west_feature, west_weight ); | |
| } | |
| } | |
| // E sections | |
| else if( x > SEEX * 2 - margin_x ) { | |
| // NE corner - blend N, E, and self | |
| if( y < margin_y ) { | |
| feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.n_fac + dat.e_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, factor ); | |
| feature_pool.add( east_feature, east_weight ); | |
| feature_pool.add( north_feature, north_weight ); | |
| } | |
| // SE corner - blend S, E, and self | |
| else if( y > SEEY * 2 - margin_y ) { | |
| feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.s_fac + dat.e_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, factor ); | |
| feature_pool.add( east_feature, east_weight ); | |
| feature_pool.add( south_feature, south_weight ); | |
| } | |
| // E edge - blend E and self | |
| else { | |
| feature_pool.add( no_ter_furn, 2 * max_factor - ( dat.e_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, factor ); | |
| feature_pool.add( east_feature, east_weight ); | |
| } | |
| } | |
| // Central sections | |
| else { | |
| // N edge - blend N and self | |
| if( y < margin_y ) { | |
| feature_pool.add( no_ter_furn, 2 * max_factor - ( dat.n_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, factor ); | |
| feature_pool.add( north_feature, north_weight ); | |
| } | |
| // S edge - blend S, and self | |
| else if( y > SEEY * 2 - margin_y ) { | |
| feature_pool.add( no_ter_furn, 2 * max_factor - ( dat.s_fac + factor * 2 ) ); | |
| feature_pool.add( self_feature, factor ); | |
| feature_pool.add( south_feature, south_weight ); | |
| } | |
| // center - no blending | |
| else { | |
| feature_pool.add( no_ter_furn, max_factor - factor * 2 ); | |
| feature_pool.add( self_feature, factor ); | |
| } | |
| } | |
| // Pick a single feature from the pool we built above and return it. | |
| const ter_furn_id *feature = feature_pool.pick(); | |
| if( feature == nullptr ) { | |
| return no_ter_furn; | |
| } else { | |
| return *feature; | |
| } | |
| }; | |
| // Get the current biome def for this terrain. | |
| const auto current_biome_def_it = dat.region.forest_composition.biomes.find( terrain_type ); | |
| // If there is no biome def for this terrain, fill in with the region's default ground cover | |
| // and bail--nothing more to be done. | |
| if( current_biome_def_it == dat.region.forest_composition.biomes.end() ) { | |
| dat.fill_groundcover(); | |
| return; | |
| } | |
| const forest_biome current_biome_def = current_biome_def_it->second; | |
| // If this biome does not define its own groundcover, then fill with the region's ground | |
| // cover. Otherwise, fill with the biome defs groundcover. | |
| if( current_biome_def.groundcover.empty() ) { | |
| dat.fill_groundcover(); | |
| } else { | |
| m->draw_fill_background( current_biome_def.groundcover ); | |
| } | |
| // There is a chance of placing terrain dependent furniture, e.g. f_cattails on t_water_sh. | |
| const auto set_terrain_dependent_furniture = [¤t_biome_def, &m]( const ter_id & tid, | |
| const int x, const int y ) { | |
| const auto terrain_dependent_furniture_it = current_biome_def.terrain_dependent_furniture.find( | |
| tid ); | |
| if( terrain_dependent_furniture_it == current_biome_def.terrain_dependent_furniture.end() ) { | |
| // No terrain dependent furnitures for this terrain, so bail. | |
| return; | |
| } | |
| const forest_biome_terrain_dependent_furniture tdf = terrain_dependent_furniture_it->second; | |
| if( tdf.furniture.get_weight() <= 0 ) { | |
| // We've got furnitures, but their weight is 0 or less, so bail. | |
| return; | |
| } | |
| if( one_in( tdf.chance ) ) { | |
| // Pick a furniture and set it on the map right now. | |
| const auto fid = tdf.furniture.pick(); | |
| m->furn_set( x, y, *fid ); | |
| } | |
| }; | |
| // Loop through each location in this overmap terrain and attempt to place a feature and | |
| // terrain dependent furniture. | |
| for( int x = 0; x < SEEX * 2; x++ ) { | |
| for( int y = 0; y < SEEY * 2; y++ ) { | |
| const ter_furn_id feature = get_blended_feature( x, y ); | |
| ter_or_furn_set( m, x, y, feature ); | |
| set_terrain_dependent_furniture( feature.ter, x, y ); | |
| } | |
| } | |
| // Place items on this terrain as defined in the biome. | |
| for( int i = 0; i < current_biome_def.item_spawn_iterations; i++ ) { | |
| m->place_items( current_biome_def.item_group, current_biome_def.item_group_chance, 0, 0, | |
| SEEX * 2 - 1, SEEY * 2 - 1, true, turn ); | |
| } | |
| } | |
| void mapgen_forest_trail_straight( map *m, oter_id terrain_type, mapgendata dat, | |
| const time_point &turn, | |
| float density ) | |
| { | |
| mapgen_forest( m, oter_str_id( "forest_thick" ).id(), dat, turn, density ); | |
| const auto center_offset = [&dat]() { | |
| return rng( -dat.region.forest_trail.trail_center_variance, | |
| dat.region.forest_trail.trail_center_variance ); | |
| }; | |
| const auto width_offset = [&dat]() { | |
| return rng( dat.region.forest_trail.trail_width_offset_min, | |
| dat.region.forest_trail.trail_width_offset_max ); | |
| }; | |
| int center_x = SEEX + center_offset(); | |
| int center_y = SEEY + center_offset(); | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( i > center_x - width_offset() && i < center_x + width_offset() ) { | |
| m->furn_set( i, j, f_null ); | |
| m->ter_set( i, j, *dat.region.forest_trail.trail_terrain.pick() ); | |
| } | |
| } | |
| } | |
| if( terrain_type == "forest_trail_ew" || terrain_type == "forest_trail_end_east" || | |
| terrain_type == "forest_trail_end_west" ) { | |
| m->rotate( 1 ); | |
| } | |
| m->place_items( "forest_trail", 75, center_x - 2, center_y - 2, center_x + 2, center_y + 2, true, | |
| turn ); | |
| } | |
| void mapgen_forest_trail_curved( map *m, oter_id terrain_type, mapgendata dat, | |
| const time_point &turn, | |
| float density ) | |
| { | |
| mapgen_forest( m, oter_str_id( "forest_thick" ).id(), dat, turn, density ); | |
| const auto center_offset = [&dat]() { | |
| return rng( -dat.region.forest_trail.trail_center_variance, | |
| dat.region.forest_trail.trail_center_variance ); | |
| }; | |
| const auto width_offset = [&dat]() { | |
| return rng( dat.region.forest_trail.trail_width_offset_min, | |
| dat.region.forest_trail.trail_width_offset_max ); | |
| }; | |
| int center_x = SEEX + center_offset(); | |
| int center_y = SEEY + center_offset(); | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( i > center_x - width_offset() && i < center_x + width_offset() && | |
| j < center_y + width_offset() ) || | |
| ( j > center_y - width_offset() && j < center_y + width_offset() && | |
| i > center_x - width_offset() ) ) { | |
| m->furn_set( i, j, f_null ); | |
| m->ter_set( i, j, *dat.region.forest_trail.trail_terrain.pick() ); | |
| } | |
| } | |
| } | |
| if( terrain_type == "forest_trail_es" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "forest_trail_sw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "forest_trail_wn" ) { | |
| m->rotate( 3 ); | |
| } | |
| m->place_items( "forest_trail", 75, center_x - 2, center_y - 2, center_x + 2, center_y + 2, true, | |
| turn ); | |
| } | |
| void mapgen_forest_trail_tee( map *m, oter_id terrain_type, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| mapgen_forest( m, oter_str_id( "forest_thick" ).id(), dat, turn, density ); | |
| const auto center_offset = [&dat]() { | |
| return rng( -dat.region.forest_trail.trail_center_variance, | |
| dat.region.forest_trail.trail_center_variance ); | |
| }; | |
| const auto width_offset = [&dat]() { | |
| return rng( dat.region.forest_trail.trail_width_offset_min, | |
| dat.region.forest_trail.trail_width_offset_max ); | |
| }; | |
| int center_x = SEEX + center_offset(); | |
| int center_y = SEEY + center_offset(); | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( i > center_x - width_offset() && i < center_x + width_offset() ) || | |
| ( j > center_y - width_offset() && | |
| j < center_y + width_offset() && i > center_x - width_offset() ) ) { | |
| m->furn_set( i, j, f_null ); | |
| m->ter_set( i, j, *dat.region.forest_trail.trail_terrain.pick() ); | |
| } | |
| } | |
| } | |
| if( terrain_type == "forest_trail_esw" ) { | |
| m->rotate( 1 ); | |
| } | |
| if( terrain_type == "forest_trail_nsw" ) { | |
| m->rotate( 2 ); | |
| } | |
| if( terrain_type == "forest_trail_new" ) { | |
| m->rotate( 3 ); | |
| } | |
| m->place_items( "forest_trail", 75, center_x - 2, center_y - 2, center_x + 2, center_y + 2, true, | |
| turn ); | |
| } | |
| void mapgen_forest_trail_four_way( map *m, oter_id, mapgendata dat, const time_point &turn, | |
| float density ) | |
| { | |
| mapgen_forest( m, oter_str_id( "forest_thick" ).id(), dat, turn, density ); | |
| const auto center_offset = [&dat]() { | |
| return rng( -dat.region.forest_trail.trail_center_variance, | |
| dat.region.forest_trail.trail_center_variance ); | |
| }; | |
| const auto width_offset = [&dat]() { | |
| return rng( dat.region.forest_trail.trail_width_offset_min, | |
| dat.region.forest_trail.trail_width_offset_max ); | |
| }; | |
| int center_x = SEEX + center_offset(); | |
| int center_y = SEEY + center_offset(); | |
| for( int i = 0; i < SEEX * 2; i++ ) { | |
| for( int j = 0; j < SEEY * 2; j++ ) { | |
| if( ( i > center_x - width_offset() && i < center_x + width_offset() ) || | |
| ( j > center_y - width_offset() && | |
| j < center_y + width_offset() ) ) { | |
| m->furn_set( i, j, f_null ); | |
| m->ter_set( i, j, *dat.region.forest_trail.trail_terrain.pick() ); | |
| } | |
| } | |
| } | |
| m->place_items( "forest_trail", 75, center_x - 2, center_y - 2, center_x + 2, center_y + 2, true, | |
| turn ); | |
| } | |
| void mapgen_lake_shore( map *m, oter_id, mapgendata dat, const time_point &turn, float density ) | |
| { | |
| // Our lake shores may "extend" adjacent terrain, if the adjacent types are defined as being | |
| // extendable in our regional settings. What this effectively means is that if the lake shore is | |
| // adjacent to one of these, e.g. a forest, then rather than the lake shore simply having the | |
| // region's default groundcover for the land parts of the terrain, instead we run the mapgen | |
| // for this location as if it were the adjacent terrain, and then carve our water out of it as | |
| // per usual. I think it looks a lot nicer, e.g. in the case of a forest, to have the trees and | |
| // ground clutter of the forest abutting the water rather than simply some empty ground. | |
| // To accomplish this extension, we simply count up the adjacent terrains that are in the | |
| // defined extendable terrain setting, choose the most common one, and then run its mapgen. | |
| bool did_extend_adjacent_terrain = false; | |
| if( !dat.region.overmap_lake.shore_extendable_overmap_terrain.empty() ) { | |
| std::map<oter_id, int> adjacent_type_count; | |
| for( auto &adjacent : dat.t_nesw ) { | |
| if( std::find( dat.region.overmap_lake.shore_extendable_overmap_terrain.begin(), | |
| dat.region.overmap_lake.shore_extendable_overmap_terrain.end(), | |
| adjacent ) != dat.region.overmap_lake.shore_extendable_overmap_terrain.end() ) { | |
| adjacent_type_count[adjacent] += 1; | |
| } | |
| } | |
| if( !adjacent_type_count.empty() ) { | |
| const auto most_common_adjacent = std::max_element( std::begin( adjacent_type_count ), | |
| std::end( adjacent_type_count ), []( const std::pair<oter_id, int> &p1, | |
| const std::pair<oter_id, int> &p2 ) { | |
| return p1.second < p2.second; | |
| } ); | |
| did_extend_adjacent_terrain = run_mapgen_func( most_common_adjacent->first.id().str(), m, | |
| most_common_adjacent->first, dat, turn, density ); | |
| // One fun side effect of running another mapgen here is that it may have placed items in locations | |
| // that we're later going to turn into water. Let's just remove all items. | |
| if( did_extend_adjacent_terrain ) { | |
| for( int x = 0; x < SEEX * 2; x++ ) { | |
| for( int y = 0; y < SEEY * 2; y++ ) { | |
| m->i_clear( x, y ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // If we didn't extend an adjacent terrain, then just fill this entire location with the default | |
| // groundcover for the region. | |
| if( !did_extend_adjacent_terrain ) { | |
| dat.fill_groundcover(); | |
| } | |
| const oter_id river_center( "river_center" ); | |
| auto is_lake = [&]( const oter_id & id ) { | |
| // We want to consider river_center as a lake as well, so that the confluence of a | |
| // river and a lake is a continuous water body. | |
| return id.obj().is_lake() || id == river_center; | |
| }; | |
| const auto is_shore = [&]( const oter_id & id ) { | |
| return id.obj().is_lake_shore(); | |
| }; | |
| const auto is_river_bank = [&]( const oter_id & id ) { | |
| return id != river_center && id.obj().is_river(); | |
| }; | |
| const bool n_lake = is_lake( dat.north() ); | |
| const bool e_lake = is_lake( dat.east() ); | |
| const bool s_lake = is_lake( dat.south() ); | |
| const bool w_lake = is_lake( dat.west() ); | |
| const bool nw_lake = is_lake( dat.nwest() ); | |
| const bool ne_lake = is_lake( dat.neast() ); | |
| const bool se_lake = is_lake( dat.seast() ); | |
| const bool sw_lake = is_lake( dat.swest() ); | |
| // If we don't have any adjacent lakes, then we don't need to worry about a shoreline, | |
| // and are done at this point. | |
| const bool no_adjacent_water = !n_lake && !e_lake && !s_lake && !w_lake && !nw_lake && !ne_lake && | |
| !se_lake && !sw_lake; | |
| if( no_adjacent_water ) { | |
| return; | |
| } | |
| // I'm pretty unhappy with this block of if statements that follows, but got frustrated/sidetracked | |
| // in finding a more elegant solution. This is functional, but improvements that maintain the result | |
| // are welcome. The basic jist is as follows: | |
| // | |
| // Given our current location and the 8 adjacent locations, we classify them all as lake, lake shore, | |
| // river bank, or something else that we don't care about. We then create a polygon with four points, | |
| // one in each corner of our location. Then, based on the permutations of possible adjacent location | |
| // types, we manipulate the four points of our polygon to generate the rough outline of our shore. The | |
| // area inside the polygon will retain our ground we generated, while the area outside will get turned | |
| // into the shoreline with shallow and deep water. | |
| // | |
| // For example, if we have forests to the west, the lake to the east, and more shore north and south of | |
| // us, like this... | |
| // | |
| // | --- | --- | --- | | |
| // | F | S | L | | |
| // | --- | --- | --- | | |
| // | F | S | L | | |
| // | --- | --- | --- | | |
| // | F | S | L | | |
| // | --- | --- | --- | | |
| // | |
| // ...then what we want to do with our polygon is push our eastern points to the west. If the north location | |
| // were instead a continuation of the lake, with a commensurate shoreline like this... | |
| // | |
| // | --- | --- | --- | | |
| // | S | L | L | | |
| // | --- | --- | --- | | |
| // | S | S | L | | |
| // | --- | --- | --- | | |
| // | F | S | L | | |
| // | --- | --- | --- | | |
| // | |
| // ...then we still need to push our eastern points to the west, but we also need to push our northern | |
| // points south, and since we don't want such a blocky shoreline at our corners, we also want to | |
| // push the north-eastern point even further south-west. | |
| // | |
| // Things get even more complicated when we transition into a river bank--they have their own style of | |
| // mapgen, and while things don't have to be seamless, I did want the lake shores to fairly smoothly | |
| // transition into them, so if we have a river bank adjacent like this... | |
| // | |
| // | --- | --- | --- | | |
| // | F | R | L | | |
| // | --- | --- | --- | | |
| // | F | S | L | | |
| // | --- | --- | --- | | |
| // | F | S | L | | |
| // | --- | --- | --- | | |
| // | |
| // ...then we need to push our adjacent corners in even more for a good transition. | |
| // | |
| // At the end of all this, we'll have our basic four point polygon that we'll then inspect and use | |
| // to create the line-segments that will form our shoreline, but more on in a bit. | |
| const bool n_shore = is_shore( dat.north() ); | |
| const bool e_shore = is_shore( dat.east() ); | |
| const bool s_shore = is_shore( dat.south() ); | |
| const bool w_shore = is_shore( dat.west() ); | |
| const bool n_river_bank = is_river_bank( dat.north() ); | |
| const bool e_river_bank = is_river_bank( dat.east() ); | |
| const bool s_river_bank = is_river_bank( dat.south() ); | |
| const bool w_river_bank = is_river_bank( dat.west() ); | |
| // This is length we end up pushing things about by as a baseline. | |
| const int sector_length = SEEX * 2 / 3; | |
| // Define the corners of the map. These won't change. | |
| static constexpr point nw_corner( 0, 0 ); | |
| static constexpr point ne_corner( SEEX * 2 - 1, 0 ); | |
| static constexpr point se_corner( SEEX * 2 - 1, SEEY * 2 - 1 ); | |
| static constexpr point sw_corner( 0, SEEY * 2 - 1 ); | |
| // Define the four points that make up our polygon that we'll later pull line segments from for | |
| // the actual shoreline. | |
| point nw = nw_corner; | |
| point ne = ne_corner; | |
| point se = se_corner; | |
| point sw = sw_corner; | |
| std::vector<std::vector<point>> line_segments; | |
| // This section is about pushing the straight N, S, E, or W borders inward when adjacent to an actual lake. | |
| if( n_lake ) { | |
| nw.y += sector_length; | |
| ne.y += sector_length; | |
| } | |
| if( s_lake ) { | |
| sw.y -= sector_length; | |
| se.y -= sector_length; | |
| } | |
| if( w_lake ) { | |
| nw.x += sector_length; | |
| sw.x += sector_length; | |
| } | |
| if( e_lake ) { | |
| ne.x -= sector_length; | |
| se.x -= sector_length; | |
| } | |
| // This section is about pushing the corners inward when adjacent to a lake that curves into a river bank. | |
| if( n_river_bank ) { | |
| if( w_lake && nw_lake ) { | |
| nw.x += sector_length; | |
| } | |
| if( e_lake && ne_lake ) { | |
| ne.x -= sector_length; | |
| } | |
| } | |
| if( e_river_bank ) { | |
| if( s_lake && se_lake ) { | |
| se.y -= sector_length; | |
| } | |
| if( n_lake && ne_lake ) { | |
| ne.y += sector_length; | |
| } | |
| } | |
| if( s_river_bank ) { | |
| if( w_lake && sw_lake ) { | |
| sw.x += sector_length; | |
| } | |
| if( e_lake && se_lake ) { | |
| se.x -= sector_length; | |
| } | |
| } | |
| if( w_river_bank ) { | |
| if( s_lake && sw_lake ) { | |
| sw.y -= sector_length; | |
| } | |
| if( n_lake && nw_lake ) { | |
| nw.y += sector_length; | |
| } | |
| } | |
| // This section is about pushing the corners inward when we've got a lake in the corner that | |
| // either has lake adjacent to it and us, or more shore adjacent to it and us. Note that in the | |
| // case of having two shores adjacent, we end up adding a new line segment that isn't part of | |
| // our original set--we end up cutting the corner off our polygonal box. | |
| if( nw_lake ) { | |
| if( n_lake && w_lake ) { | |
| nw.x += sector_length / 2; | |
| nw.y += sector_length / 2; | |
| } else if( n_shore && w_shore ) { | |
| point n = nw_corner; | |
| point w = nw_corner; | |
| n.x += sector_length; | |
| w.y += sector_length; | |
| line_segments.push_back( { n, w } ); | |
| } | |
| } | |
| if( ne_lake ) { | |
| if( n_lake && e_lake ) { | |
| ne.x -= sector_length / 2; | |
| ne.y += sector_length / 2; | |
| } else if( n_shore && e_shore ) { | |
| point n = ne_corner; | |
| point e = ne_corner; | |
| n.x -= sector_length; | |
| e.y += sector_length; | |
| line_segments.push_back( { n, e } ); | |
| } | |
| } | |
| if( sw_lake ) { | |
| if( s_lake && w_lake ) { | |
| sw.x += sector_length / 2; | |
| sw.y -= sector_length / 2; | |
| } else if( s_shore && w_shore ) { | |
| point s = sw_corner; | |
| point w = sw_corner; | |
| s.x += sector_length; | |
| w.y -= sector_length; | |
| line_segments.push_back( { s, w } ); | |
| } | |
| } | |
| if( se_lake ) { | |
| if( s_lake && e_lake ) { | |
| se.x -= sector_length / 2; | |
| se.y -= sector_length / 2; | |
| } else if( s_shore && e_shore ) { | |
| point s = se_corner; | |
| point e = se_corner; | |
| s.x -= sector_length; | |
| e.y -= sector_length; | |
| line_segments.push_back( { s, e } ); | |
| } | |
| } | |
| // Ok, all of the fiddling with the polygon corners is done. | |
| // At this point we've got four points that make up four line segments that started out | |
| // at the map boundaries, but have subsequently been perturbed by the adjacent terrains. | |
| // Let's look at them and see which ones differ from their original state and should | |
| // form our shoreline. | |
| if( nw.y != nw_corner.y || ne.y != ne_corner.y ) { | |
| line_segments.push_back( { nw, ne } ); | |
| } | |
| if( ne.x != ne_corner.x || se.x != se_corner.x ) { | |
| line_segments.push_back( { ne, se } ); | |
| } | |
| if( se.y != se_corner.y || sw.y != sw_corner.y ) { | |
| line_segments.push_back( { se, sw } ); | |
| } | |
| if( sw.x != sw_corner.x || nw.x != nw_corner.x ) { | |
| line_segments.push_back( { sw, nw } ); | |
| } | |
| static constexpr rectangle map_boundaries( nw_corner, se_corner ); | |
| // This will draw our shallow water coastline from the "from" point to the "to" point. | |
| // It buffers the points a bit for a thicker line. It also clears any furniture that might | |
| // be in the location as a result of our extending adjacent mapgen. | |
| const auto draw_shallow_water = [&]( const point & from, const point & to ) { | |
| std::vector<point> points = line_to( from, to ); | |
| for( auto &p : points ) { | |
| std::vector<point> buffered_points = closest_points_first( 1, p.x, p.y ); | |
| for( const point &bp : buffered_points ) { | |
| if( !map_boundaries.contains_inclusive( bp ) ) { | |
| continue; | |
| } | |
| // Use t_null for now instead of t_water_sh, because sometimes our extended terrain | |
| // has put down a t_water_sh, and we need to be able to flood-fill over that. | |
| m->ter_set( bp.x, bp.y, t_null ); | |
| m->furn_set( bp.x, bp.y, f_null ); | |
| } | |
| } | |
| }; | |
| // Given two points, return a point that is midway between the two points and then | |
| // jittered by a random amount in proportion to the length of the line segment. | |
| const auto jittered_midpoint = [&]( const point & from, const point & to ) { | |
| const int jitter = rl_dist( from, to ) / 4; | |
| const point midpoint( ( from.x + to.x ) / 2 + rng( -jitter, jitter ), | |
| ( from.y + to.y ) / 2 + rng( -jitter, jitter ) ); | |
| return midpoint; | |
| }; | |
| // For each of our valid shoreline line segments, generate a slightly more interesting | |
| // set of line segments by splitting the line into four segments with jittered | |
| // midpoints, and then draw shallow water for four each of those. | |
| for( auto &ls : line_segments ) { | |
| const point mp1 = jittered_midpoint( ls[0], ls[1] ); | |
| const point mp2 = jittered_midpoint( ls[0], mp1 ); | |
| const point mp3 = jittered_midpoint( mp1, ls[1] ); | |
| draw_shallow_water( ls[0], mp2 ); | |
| draw_shallow_water( mp2, mp1 ); | |
| draw_shallow_water( mp1, mp3 ); | |
| draw_shallow_water( mp3, ls[1] ); | |
| } | |
| // Now that we've done our ground mapgen and laid down a contiguous shoreline of shallow water, | |
| // we'll floodfill the sections adjacent to the lake with deep water. As before, we also clear | |
| // out any furniture that we placed by the extended mapgen. | |
| std::unordered_set<point> visited; | |
| const auto fill_deep_water = [&]( const point & starting_point ) { | |
| std::queue<point> to_check; | |
| to_check.push( starting_point ); | |
| while( !to_check.empty() ) { | |
| const point current_point = to_check.front(); | |
| to_check.pop(); | |
| if( visited.find( current_point ) != visited.end() ) { | |
| continue; | |
| } | |
| visited.emplace( current_point ); | |
| if( !map_boundaries.contains_inclusive( current_point ) ) { | |
| continue; | |
| } | |
| if( m->ter( current_point.x, current_point.y ) != t_null ) { | |
| m->ter_set( current_point.x, current_point.y, t_water_dp ); | |
| m->furn_set( current_point.x, current_point.y, f_null ); | |
| to_check.push( point( current_point.x, current_point.y + 1 ) ); | |
| to_check.push( point( current_point.x, current_point.y - 1 ) ); | |
| to_check.push( point( current_point.x + 1, current_point.y ) ); | |
| to_check.push( point( current_point.x - 1, current_point.y ) ); | |
| } | |
| } | |
| }; | |
| // We'll flood fill from the four corners, using the corner if any of the locations | |
| // adjacent to it were a lake. | |
| if( n_lake || nw_lake || w_lake ) { | |
| fill_deep_water( nw_corner ); | |
| } | |
| if( s_lake || sw_lake || w_lake ) { | |
| fill_deep_water( sw_corner ); | |
| } | |
| if( n_lake || ne_lake || e_lake ) { | |
| fill_deep_water( ne_corner ); | |
| } | |
| if( s_lake || se_lake || e_lake ) { | |
| fill_deep_water( se_corner ); | |
| } | |
| // We previously placed our shallow water but actually did a t_null instead to make sure that we didn't | |
| // pick up shallow water from our extended terrain. Now turn those nulls into t_water_sh. | |
| m->translate( t_null, t_water_sh ); | |
| } | |
| void mremove_trap( map *m, int x, int y ) | |
| { | |
| tripoint actual_location( x, y, m->get_abs_sub().z ); | |
| m->remove_trap( actual_location ); | |
| } | |
| void mtrap_set( map *m, int x, int y, trap_id type ) | |
| { | |
| tripoint actual_location( x, y, m->get_abs_sub().z ); | |
| m->trap_set( actual_location, type ); | |
| } | |
| void madd_field( map *m, int x, int y, field_type_id type, int intensity ) | |
| { | |
| tripoint actual_location( x, y, m->get_abs_sub().z ); | |
| m->add_field( actual_location, type, intensity, 0_turns ); | |
| } | |
| static bool is_suitable_for_stairs( const map *const m, const tripoint &p ) | |
| { | |
| const ter_t &p_ter = m->ter( p ).obj(); | |
| return | |
| p_ter.has_flag( "INDOORS" ) && | |
| p_ter.has_flag( "FLAT" ) && | |
| m->furn( p ) == f_null; | |
| } | |
| static void stairs_debug_log( const map *const m, const std::string &msg, const tripoint &p, | |
| DebugLevel level = D_INFO ) | |
| { | |
| const ter_t &p_ter = m->ter( p ).obj(); | |
| dbg( level ) | |
| << msg | |
| << " tripoint: " << p | |
| << " terrain: " << p_ter.name() | |
| << " movecost: " << p_ter.movecost | |
| << " furniture: " << m->furn( p ) | |
| << " indoors: " << p_ter.has_flag( "INDOORS" ) | |
| << " flat: " << p_ter.has_flag( "FLAT" ) | |
| ; | |
| } | |
| void place_stairs( map *m, oter_id terrain_type, mapgendata dat ) | |
| { | |
| if( !dat.has_basement() ) { | |
| return; | |
| } | |
| const bool force = get_option<bool>( "ALIGN_STAIRS" ); | |
| const tripoint abs_sub_here = m->get_abs_sub(); | |
| tinymap basement; | |
| basement.load( abs_sub_here.x, abs_sub_here.y, abs_sub_here.z - 1, false ); | |
| const tripoint down( 0, 0, -1 ); | |
| const tripoint from( 0, 0, abs_sub_here.z ); | |
| const tripoint to( SEEX * 2, SEEY * 2, abs_sub_here.z ); | |
| tripoint_range tr = m->points_in_rectangle( from, to ); | |
| std::vector<tripoint> stairs; | |
| std::vector<tripoint> tripoints; | |
| // Find the basement's stairs first. | |
| for( auto &&p : tr ) { // *NOPAD* | |
| if( basement.has_flag( TFLAG_GOES_UP, p + down ) ) { | |
| const tripoint rotated = om_direction::rotate( p, terrain_type->get_dir() ); | |
| stairs.emplace_back( rotated ); | |
| stairs_debug_log( m, "basement stairs:", rotated ); | |
| } | |
| if( is_suitable_for_stairs( m, p ) ) { | |
| tripoints.emplace_back( p ); | |
| } | |
| } | |
| if( stairs.empty() ) { | |
| dbg( D_INFO ) << "no stairs found downstairs"; | |
| return; | |
| } | |
| // Shuffle tripoints so that the stairs are not always similarly placed. | |
| std::shuffle( std::begin( tripoints ), std::end( tripoints ), rng_get_engine() ); | |
| bool all_can_be_placed = false; | |
| tripoint shift( 0, 0, 0 ); | |
| int match_count = 0; | |
| // Find a tripoint where all the underground tripoints for stairs are on | |
| // suitable locations aboveground. | |
| for( auto &&p : tripoints ) { // *NOPAD* | |
| int count = 1; | |
| all_can_be_placed = true; | |
| stairs_debug_log( m, "ok first:", p ); | |
| // First element can be ignored as p is already ok. | |
| for( auto it = stairs.begin() + 1; it != stairs.end(); ++it ) { | |
| // Align tripoint with the first underground tripoint. | |
| const tripoint &stair = *it - stairs.front() + p; | |
| if( !is_suitable_for_stairs( m, stair ) ) { | |
| stairs_debug_log( m, "not ok:", stair ); | |
| all_can_be_placed = false; | |
| if( match_count < count ) { | |
| match_count = count; | |
| shift = p - stairs.front(); | |
| dbg( D_INFO ) << "partial match shift tripoint: " << shift; | |
| } | |
| break; | |
| } | |
| stairs_debug_log( m, "ok:", stair ); | |
| ++count; | |
| } | |
| if( all_can_be_placed ) { | |
| shift = p - stairs.front(); | |
| dbg( D_INFO ) << "full match shift tripoint: " << shift; | |
| break; | |
| } | |
| } | |
| if( !( all_can_be_placed || force ) ) { | |
| dbg( D_WARNING ) << "no stairs were placed"; | |
| return; | |
| } | |
| if( !all_can_be_placed ) { | |
| dbg( D_WARNING ) << "Some stairs can be placed to suitable locations " | |
| << "and the rest may end up in odd locations."; | |
| } | |
| for( auto &&p : stairs ) { // *NOPAD* | |
| tripoint stair = p + shift; | |
| if( m->ter_set( stair, t_stairs_down ) ) { | |
| stairs_debug_log( m, "stairs placed:", stair ); | |
| } else { | |
| stairs_debug_log( m, "stairs not placed:", stair, D_WARNING ); | |
| } | |
| } | |
| } |