void overmapbuffer::fix_npcs( overmap &new_overmap )
{
    // First step: move all npcs that are located outside of the given overmap
    // into a separate container. After that loop, new_overmap.npcs is no
    // accessed anymore!
    decltype( overmap::npcs ) to_relocate;
    for( auto it = new_overmap.npcs.begin(); it != new_overmap.npcs.end(); ) {
        npc &np = **it;
        const tripoint npc_omt_pos = np.global_omt_location();
        const point npc_om_pos = omt_to_om_copy( npc_omt_pos.x, npc_omt_pos.y );
        const point &loc = new_overmap.pos();
        if( npc_om_pos == loc ) {
            // Nothing to do
            ++it;
            continue;
        }
        to_relocate.push_back( *it );
        it = new_overmap.npcs.erase( it );
    }
    // Second step: put them back where they belong. This step involves loading
    // new overmaps (via `get`), which does in turn call this function for the
    // newly loaded overmaps. This in turn may move NPCs from the second overmap
    // back into the first overmap. This messes up the iteration of it. The
    // iteration is therefore done in a separate step above (which does *not*
    // involve loading new overmaps).
    for( auto &ptr : to_relocate ) {
        npc &np = *ptr;
        const tripoint npc_omt_pos = np.global_omt_location();
        const point npc_om_pos = omt_to_om_copy( npc_omt_pos.x, npc_omt_pos.y );
        const point &loc = new_overmap.pos();
        if( !has( npc_om_pos.x, npc_om_pos.y ) ) {
            // This can't really happen without save editing
            // We have no sane option here, just place the NPC on the edge
            debugmsg( "NPC %s is out of bounds, on non-generated overmap %d,%d",
                      np.name.c_str(), loc.x, loc.y );
            point npc_sm = om_to_sm_copy( npc_om_pos );
            point min = om_to_sm_copy( loc );
            point max = om_to_sm_copy( loc + point( 1, 1 ) ) - point( 1, 1 );
            npc_sm.x = clamp( npc_sm.x, min.x, max.x );
            npc_sm.y = clamp( npc_sm.y, min.y, max.y );
            np.spawn_at_sm( npc_sm.x, npc_sm.y, np.posz() );
            new_overmap.npcs.push_back( ptr );
            continue;
        }

        // Simplest case: just move the pointer
        get( npc_om_pos.x, npc_om_pos.y ).insert_npc( ptr );
    }
}
void overmapbuffer::fix_mongroups(overmap &new_overmap)
{
    for( auto it = new_overmap.zg.begin(); it != new_overmap.zg.end(); ) {
        auto &mg = it->second;
        // spawn related code simply sets population to 0 when they have been
        // transformed into spawn points on a submap, the group can then be removed
        if( mg.population <= 0 ) {
            new_overmap.zg.erase( it++ );
            continue;
        }
        // Inside the bounds of the overmap?
        if( mg.posx >= 0 && mg.posy >= 0 && mg.posx < OMAPX * 2 && mg.posy < OMAPY * 2 ) {
            ++it;
            continue;
        }
        point smabs( mg.posx + new_overmap.pos().x * OMAPX * 2,
                     mg.posy + new_overmap.pos().y * OMAPY * 2 );
        point omp = sm_to_om_remain( smabs );
        if( !has( omp.x, omp.y ) ) {
            // Don't generate new overmaps, as this can be called from the
            // overmap-generating code.
            ++it;
            continue;
        }
        overmap &om = get( omp.x, omp.y );
        mg.posx = smabs.x;
        mg.posy = smabs.y;
        om.add_mon_group( mg );
        new_overmap.zg.erase( it++ );
    }
}
radio_tower_reference create_radio_tower_reference( overmap &om, radio_tower &t, const tripoint &center )
{
    // global submap coordinates, same as center is
    const point pos = point( t.x, t.y ) + overmapbuffer::om_to_sm_copy( om.pos() );
    const int strength = t.strength - rl_dist( tripoint( pos, 0 ), center );
    return radio_tower_reference{ &om, &t, pos, strength };
}
Exemple #4
0
void check_test_overmap_data( const overmap &test_map )
{
    // Spot-check a bunch of terrain values.
    // Bottom level, "L 0" in the save
    REQUIRE(test_map.get_ter(0, 0, -10).id() == "empty_rock");
    REQUIRE(test_map.get_ter(47, 3, -10).id() == "empty_rock");
    REQUIRE(test_map.get_ter(48, 3, -10).id() == "rock");
    REQUIRE(test_map.get_ter(49, 3, -10).id() == "rock");
    REQUIRE(test_map.get_ter(50, 3, -10).id() == "rock");
    REQUIRE(test_map.get_ter(51, 3, -10).id() == "empty_rock");
    REQUIRE(test_map.get_ter(45, 4, -10).id() == "empty_rock");
    REQUIRE(test_map.get_ter(46, 4, -10).id() == "rock");
    REQUIRE(test_map.get_ter(47, 4, -10).id() == "rock");
    REQUIRE(test_map.get_ter(48, 4, -10).id() == "slimepit");
    REQUIRE(test_map.get_ter(49, 4, -10).id() == "slimepit");
    REQUIRE(test_map.get_ter(50, 4, -10).id() == "slimepit");
    REQUIRE(test_map.get_ter(51, 4, -10).id() == "rock");
    REQUIRE(test_map.get_ter(52, 4, -10).id() == "empty_rock");

    REQUIRE(test_map.get_ter(179, 179, -10).id() == "empty_rock");
    // Level -9, "L 1" in the save
    REQUIRE(test_map.get_ter(0, 0, -9).id() == "empty_rock");
    REQUIRE(test_map.get_ter(44, 1, -9).id() == "empty_rock");
    REQUIRE(test_map.get_ter(45, 1, -9).id() == "rock");
    REQUIRE(test_map.get_ter(46, 1, -9).id() == "rock");
    REQUIRE(test_map.get_ter(47, 1, -9).id() == "empty_rock");
    REQUIRE(test_map.get_ter(48, 1, -9).id() == "rock");
    REQUIRE(test_map.get_ter(49, 1, -9).id() == "rock");
    REQUIRE(test_map.get_ter(50, 1, -9).id() == "empty_rock");

    REQUIRE(test_map.get_ter(43, 2, -9).id() == "empty_rock");
    REQUIRE(test_map.get_ter(44, 2, -9).id() == "rock");
    REQUIRE(test_map.get_ter(45, 2, -9).id() == "slimepit");
    REQUIRE(test_map.get_ter(46, 2, -9).id() == "slimepit");
    REQUIRE(test_map.get_ter(47, 2, -9).id() == "rock");
    REQUIRE(test_map.get_ter(48, 2, -9).id() == "slimepit");
    REQUIRE(test_map.get_ter(49, 2, -9).id() == "slimepit");
    REQUIRE(test_map.get_ter(50, 2, -9).id() == "rock");
    REQUIRE(test_map.get_ter(51, 2, -9).id() == "empty_rock");

    // Level -3, "L 7" in save
    REQUIRE(test_map.get_ter(0, 0, -3).id() == "empty_rock");
    REQUIRE(test_map.get_ter(156, 0, -3).id() == "empty_rock");
    REQUIRE(test_map.get_ter(157, 0, -3).id() == "temple_stairs");
    REQUIRE(test_map.get_ter(158, 0, -3).id() == "empty_rock");
    REQUIRE(test_map.get_ter(45, 5, -3).id() == "empty_rock");
    REQUIRE(test_map.get_ter(46, 5, -3).id() == "rock");
    REQUIRE(test_map.get_ter(47, 5, -3).id() == "rock");
    REQUIRE(test_map.get_ter(48, 5, -3).id() == "rock");
    REQUIRE(test_map.get_ter(49, 5, -3).id() == "rock");
    REQUIRE(test_map.get_ter(50, 5, -3).id() == "empty_rock");
    REQUIRE(test_map.get_ter(133, 5, -3).id() == "empty_rock");
    REQUIRE(test_map.get_ter(134, 5, -3).id() == "mine");
    REQUIRE(test_map.get_ter(135, 5, -3).id() == "empty_rock");

    // Ground level
    REQUIRE(test_map.get_ter(0, 0, 0).id() == "field");
    REQUIRE(test_map.get_ter(23, 0, 0).id() == "field");
    REQUIRE(test_map.get_ter(24, 0, 0).id() == "forest_thick");
    REQUIRE(test_map.get_ter(25, 0, 0).id() == "forest_thick");
    REQUIRE(test_map.get_ter(26, 0, 0).id() == "forest_thick");
    REQUIRE(test_map.get_ter(27, 0, 0).id() == "forest");
    REQUIRE(test_map.get_ter(28, 0, 0).id() == "forest");
    REQUIRE(test_map.get_ter(29, 0, 0).id() == "forest");
    REQUIRE(test_map.get_ter(30, 0, 0).id() == "forest");

    // Sky
    REQUIRE(test_map.get_ter(0, 0, 1).id() == "open_air");
    REQUIRE(test_map.get_ter(179, 179, 1).id() == "open_air");

    REQUIRE(test_map.get_ter(0, 0, 2).id() == "open_air");
    REQUIRE(test_map.get_ter(179, 179, 2).id() == "open_air");

    REQUIRE(test_map.get_ter(0, 0, 10).id() == "open_air");
    REQUIRE(test_map.get_ter(179, 179, 10).id() == "open_air");

    // Spot-check a few of the monster groups.
    std::vector<mongroup> expected_groups{
      {"GROUP_ANT", {0, 0, -1}, 1, 3, {0, 0, 0}, 0, false, false, false},
      {"GROUP_TRIFFID", {0, 132, 0}, 1, 1, {0, 0, 0}, 0, false, false, false},
      {"GROUP_ANT", {0, 189, 0}, 1, 1, {0, 0, 0}, 0, false, false, false},
      {"GROUP_FUNGI", {0, 288, 0}, 1, 1, {0, 0, 0}, 0, false, false, false},
      {"GROUP_ANT", {1, 0, -1}, 1, 3, {0, 0, 0}, 0, false, false, false},
      {"GROUP_ANT", {1, 0, 0}, 1, 2, {0, 0, 0}, 0, false, false, false},
      {"GROUP_TRIFFID", {1, 137, 0}, 1, 2, {0, 0, 0}, 0, false, false, false},
      {"GROUP_WORM", {2, 67, 0}, 1, 1, {0, 0, 0}, 0, false, false, false},
      {"GROUP_FUNGI_FLOWERS", {2, 150, 0}, 1, 1, {0, 0, 0}, 0, false, false, false},
      {"GROUP_FUNGI_FLOWERS", {5, 150, 0}, 1, 1, {0, 0, 0}, 0, false, false, false},
      {"GROUP_ANT", {6, 365, -1}, 1, 1, {0, 0, 0}, 0, false, false, false},
      {"GROUP_ANT", {1, 8, -2}, 1, 2, {100, 50, 0}, 0, true, false, false},
      {"GROUP_GOO", {2, 9, -1}, 1, 4, {25, 75, 0}, 10, false, true, false},
      {"GROUP_BEE", {3, 10, 0}, 1, 6, {92, 64, 0}, 20, false, false, true},
      {"GROUP_CHUD", {4, 11, -2}, 1, 8, {88, 55, 0}, 30, true, true, false},
      {"GROUP_SPIRAL", {5, 12, -1}, 1, 10, {62, 47, 0}, 40, false, true, true},
      {"GROUP_RIVER", {6, 13, 0}, 1, 12, {94, 72, 0}, 50, true, false, true},
      {"GROUP_SWAMP", {7, 14, -2}, 1, 14, {37, 85, 0}, 60, true, true, true}
    };

    for( auto group : expected_groups ) {
        REQUIRE(test_map.mongroup_check(group));
    }

    // Only a few cities, so check them all.
    std::vector<city> expected_cities {{145, 53, 9},{24,60,7},{90,114,2},{108,129,9},{83,26,10},
                                     {140,89,2},{71,33,2},{67,111,2},{97,144,9},{96,166,2}};
    REQUIRE(test_map.cities.size() == expected_cities.size());
    for( const auto &candidate_city : test_map.cities ) {
      REQUIRE(std::find(expected_cities.begin(), expected_cities.end(), candidate_city) != expected_cities.end() );
    }
    // Check all the roads too.
    // Roads are getting size set to 0, but I expect -1.
    std::vector<city> expected_roads = {{179,126, -1},{136,179, -1}};
    REQUIRE(test_map.roads_out.size() == expected_roads.size());
    for( const auto &candidate_road : test_map.roads_out ) {
        REQUIRE(std::find(expected_roads.begin(), expected_roads.end(), candidate_road) != expected_roads.end() );
    }
    // Check the radio towers.
    std::vector<radio_tower> expected_towers{
      {2,42,122,"This is FEMA camp 121.  Supplies are limited, please bring supplemental food, water, and bedding.  This is FEMA camp 121.  A designated long-term emergency shelter.",MESSAGE_BROADCAST},
      {36,300,193,"This is FEMA camp 18150.  Supplies are limited, please bring supplemental food, water, and bedding.  This is FEMA camp 18150.  A designated long-term emergency shelter.",MESSAGE_BROADCAST},
      {56,194,92,"This is automated emergency shelter beacon 2897.  Supplies, amenities and shelter are stocked.",MESSAGE_BROADCAST},
      {62,208,176,"This is FEMA camp 31104.  Supplies are limited, please bring supplemental food, water, and bedding.  This is FEMA camp 31104.  A designated long-term emergency shelter.",MESSAGE_BROADCAST},
      {64,42,190,"",WEATHER_RADIO},{92,146,100,"",WEATHER_RADIO},
      {126,194,112,"This is FEMA camp 6397.  Supplies are limited, please bring supplemental food, water, and bedding.  This is FEMA camp 6397.  A designated long-term emergency shelter.", MESSAGE_BROADCAST},
      {142,128,114,"This is FEMA camp 7164.  Supplies are limited, please bring supplemental food, water, and bedding.  This is FEMA camp 7164.  A designated long-term emergency shelter.", MESSAGE_BROADCAST},
      {236,168,115,"",WEATHER_RADIO},
      {240,352,95,"This is automated emergency shelter beacon 120176.  Supplies, amenities and shelter are stocked.", MESSAGE_BROADCAST},
      {244,162,150,"This is emergency broadcast station 12281.  Please proceed quickly and calmly to your designated evacuation point.", MESSAGE_BROADCAST},
      {282,48,190,"This is emergency broadcast station 14124.  Please proceed quickly and calmly to your designated evacuation point.", MESSAGE_BROADCAST},
      {306,66,90,"This is emergency broadcast station 15333.  Please proceed quickly and calmly to your designated evacuation point.", MESSAGE_BROADCAST}};
    REQUIRE(test_map.radios.size() == expected_towers.size());
    for( const auto &candidate_tower : test_map.radios ) {
      REQUIRE(std::find(expected_towers.begin(), expected_towers.end(), candidate_tower) != expected_towers.end() );
    }
    // Spot-check some monsters.
    std::vector<std::pair<tripoint, monster>> expected_monsters{
        {{251, 86, 0},{ mtype_id("mon_zombie"), {140, 23, 0}}},
        {{253, 87, 0},{ mtype_id("mon_zombie"), {136, 25, 0}}},
        {{259, 95, 0},{ mtype_id("mon_zombie"), {143, 122, 0}}},
        {{259, 94, 0},{ mtype_id("mon_zombie"), {139, 109, 0}}},
        {{259, 91, 0},{ mtype_id("mon_dog"), {139, 82, 0}}},
        {{194, 87, -3},{ mtype_id("mon_irradiated_wanderer_4"), {119, 73, -3}}},
        {{194, 87, -3},{ mtype_id("mon_charred_nightmare"), {117, 83, -3}}},
        {{142, 96, 0},{ mtype_id("mon_deer"), {16, 109, 0}}},
        {{196, 66, -1},{ mtype_id("mon_turret"), {17, 65, -1}}},
        {{196, 63, -1},{ mtype_id("mon_broken_cyborg"), {19, 26, -1}}}
    };
    for( auto candidate_monster : expected_monsters ) {
        REQUIRE(test_map.monster_check(candidate_monster));
    }
    // Check NPCs.  They're complicated enough that I'm just going to spot-check some stats.
    for( const npc *test_npc : test_map.npcs ) {
        if( test_npc->disp_name() == "Felix Brandon" ) {
            REQUIRE(test_npc->get_str() == 7);
            REQUIRE(test_npc->get_dex() == 8);
            REQUIRE(test_npc->get_int() == 7);
            REQUIRE(test_npc->get_per() == 10);
            REQUIRE(test_npc->get_skill_level(skill_id("barter")) == 4);
            REQUIRE(test_npc->get_skill_level(skill_id("driving")) == 2);
            REQUIRE(test_npc->get_skill_level(skill_id("firstaid")) == 7);
            REQUIRE(test_npc->get_skill_level(skill_id("mechanics")) == 5);
            REQUIRE(test_npc->get_skill_level(skill_id("dodge")) == 3);
            REQUIRE(test_npc->get_skill_level(skill_id("launcher")) == 3);
            REQUIRE(test_npc->pos() == tripoint(168, 66, 0));
        } else if( test_npc->disp_name() == "Mariann Araujo" ) {
            REQUIRE(test_npc->get_str() == 11);
            REQUIRE(test_npc->get_dex() == 9);
            REQUIRE(test_npc->get_int() == 10);
            REQUIRE(test_npc->get_per() == 10);
            REQUIRE(test_npc->get_skill_level(skill_id("barter")) == 4);
            REQUIRE(test_npc->get_skill_level(skill_id("driving")) == 0);
            REQUIRE(test_npc->get_skill_level(skill_id("firstaid")) == 5);
            REQUIRE(test_npc->get_skill_level(skill_id("bashing")) == 5);
            REQUIRE(test_npc->get_skill_level(skill_id("dodge")) == 4);
            REQUIRE(test_npc->pos() == tripoint(72, 54, 0));
        } else {
            // Unrecognized NPC, fail.
            REQUIRE(false);
        }
    }
}