void MonsterGroupManager::LoadMonsterGroup(JsonObject &jo)
{
    MonsterGroup g;

    g.name = mongroup_id( jo.get_string("name") );
    g.defaultMonster = mtype_id( jo.get_string("default") );
    if (jo.has_array("monsters")) {
        JsonArray monarr = jo.get_array("monsters");

        while (monarr.has_more()) {
            JsonObject mon = monarr.next_object();
            const mtype_id name = mtype_id( mon.get_string("monster") );
            int freq = mon.get_int("freq");
            int cost = mon.get_int("cost_multiplier");
            int pack_min = 1;
            int pack_max = 1;
            if(mon.has_member("pack_size")) {
                JsonArray packarr = mon.get_array("pack_size");
                pack_min = packarr.next_int();
                pack_max = packarr.next_int();
            }
            int starts = 0;
            int ends = 0;
            if(mon.has_member("starts")) {
                if (ACTIVE_WORLD_OPTIONS["MONSTER_UPGRADE_FACTOR"] > 0) {
                    starts = mon.get_int("starts") * ACTIVE_WORLD_OPTIONS["MONSTER_UPGRADE_FACTOR"];
                } else {
                    // Default value if the monster upgrade factor is set to 0.0 - off
                    starts = mon.get_int("starts");
                }
            }
            if(mon.has_member("ends")) {
                if (ACTIVE_WORLD_OPTIONS["MONSTER_UPGRADE_FACTOR"] > 0) {
                    ends = mon.get_int("ends") * ACTIVE_WORLD_OPTIONS["MONSTER_UPGRADE_FACTOR"];
                } else {
                    // Default value if the monster upgrade factor is set to 0.0 - off
                    ends = mon.get_int("ends");
                }
            }
            MonsterGroupEntry new_mon_group = MonsterGroupEntry(name, freq, cost, pack_min, pack_max, starts,
                                              ends);
            if(mon.has_member("conditions")) {
                JsonArray conditions_arr = mon.get_array("conditions");
                while(conditions_arr.has_more()) {
                    new_mon_group.conditions.push_back(conditions_arr.next_string());
                }
            }



            g.monsters.push_back(new_mon_group);
        }
    }
    g.replace_monster_group = jo.get_bool("replace_monster_group", false);
    g.new_monster_group = mongroup_id( jo.get_string("new_monster_group_id", mongroup_id::NULL_ID.str() ) );
    g.monster_group_time = jo.get_int("replacement_time", 0);
    g.is_safe = jo.get_bool( "is_safe", false );

    monsterGroupMap[g.name] = g;
}
void MonsterGroupManager::FinalizeMonsterGroups()
{
    const MonsterGenerator &gen = MonsterGenerator::generator();
    for( auto &mtid : monster_whitelist ) {
        if( !gen.has_mtype( mtype_id( mtid ) ) ) {
            debugmsg( "monster on whitelist %s does not exist", mtid.c_str() );
        }
    }
    for( auto &mtid : monster_blacklist ) {
        if( !gen.has_mtype( mtype_id( mtid ) ) ) {
            debugmsg( "monster on blacklist %s does not exist", mtid.c_str() );
        }
    }
    for( auto &elem : monsterGroupMap ) {
        MonsterGroup &mg = elem.second;
        for(FreqDef::iterator c = mg.monsters.begin(); c != mg.monsters.end(); ) {
            if(MonsterGroupManager::monster_is_blacklisted( c->name )) {
                c = mg.monsters.erase(c);
            } else {
                ++c;
            }
        }
        if(MonsterGroupManager::monster_is_blacklisted( mg.defaultMonster )) {
            mg.defaultMonster = NULL_ID;
        }
    }
}
int turns_to_destination( const std::string &monster_type,
                          const tripoint &start, const tripoint &end )
{
    monster temp_monster( mtype_id(monster_type), start);
    // Bypassing game::add_zombie() since it sometimes upgrades the monster instantly.
    g->critter_tracker->add( temp_monster );
    monster &test_monster = g->critter_tracker->find( 0 );
    test_monster.set_dest( end );
    // Get it riled up.
    test_monster.anger = 100;
    test_monster.set_moves( 0 );
    const int monster_speed = test_monster.get_speed();
    int moves_spent = 0;
    for( int turn = 0; turn < 1000; ++turn ) {
        test_monster.mod_moves( monster_speed );
        while( test_monster.moves >= 0 ) {
            int moves_before = test_monster.moves;
            test_monster.move();
            moves_spent += moves_before - test_monster.moves;
            if( test_monster.pos() == test_monster.move_target() ) {
                g->remove_zombie( 0 );
                return moves_spent;
            }
        }
    }
    g->remove_zombie( 0 );
    // Return an unreasonably high number.
    return 100000;
}
static monster &spawn_test_monster( const std::string &monster_type, const tripoint &start )
{
    monster temp_monster( mtype_id(monster_type), start);
    // Bypassing game::add_zombie() since it sometimes upgrades the monster instantly.
    g->critter_tracker->add( temp_monster );
    return g->critter_tracker->find( 0 );
}
Exemple #5
0
item Single_item_creator::create_single(int birthday, RecursionList &rec) const
{
    item tmp;
    if (type == S_ITEM) {
        if (id == "corpse") {
            tmp.make_corpse( mtype_id( "mon_null" ), birthday );
        } else {
            tmp = item(id, birthday);
        }
    } else if (type == S_ITEM_GROUP) {
        if (std::find(rec.begin(), rec.end(), id) != rec.end()) {
            debugmsg("recursion in item spawn list %s", id.c_str());
            return item(null_item_id, birthday);
        }
        rec.push_back(id);
        Item_spawn_data *isd = item_controller->get_group(id);
        if (isd == NULL) {
            debugmsg("unknown item spawn list %s", id.c_str());
            return item(null_item_id, birthday);
        }
        tmp = isd->create_single(birthday, rec);
        rec.erase( rec.end() - 1 );
    } else if (type == S_NONE) {
        return item(null_item_id, birthday);
    }
    if( one_in( 3 ) && tmp.has_flag( "VARSIZE" ) ) {
        tmp.item_tags.insert( "FIT" );
    }
    if (modifier.get() != NULL) {
        modifier->modify(tmp);
    }
    // TODO: change the spawn lists to contain proper references to containers
    tmp = tmp.in_its_container();
    return tmp;
}
void MonsterGroupManager::FinalizeMonsterGroups()
{
    for( auto &mtid : monster_whitelist ) {
        if( !mtype_id( mtid ).is_valid() ) {
            debugmsg( "monster on whitelist %s does not exist", mtid.c_str() );
        }
    }
    for( auto &mtid : monster_blacklist ) {
        if( !mtype_id( mtid ).is_valid() ) {
            debugmsg( "monster on blacklist %s does not exist", mtid.c_str() );
        }
    }
    // If we have the classic zombies option, remove non-conforming monsters
    if( get_option<bool>( "CLASSIC_ZOMBIES" ) ) {
        for( auto &elem : monsterGroupMap ) {
            MonsterGroup &mg = elem.second;
            for( FreqDef::iterator c = mg.monsters.begin(); c != mg.monsters.end(); ) {
                // Test mon
                const mtype &mt = c->name.obj();

                if( !( mt.in_category( "CLASSIC" ) || mt.in_category( "WILDLIFE" ) ) ) {
                    c = mg.monsters.erase( c );
                } else {
                    ++c;
                }
            }
            const mtype &mt = mg.defaultMonster.obj();
            if( !( mt.in_category( "CLASSIC" ) || mt.in_category( "WILDLIFE" ) ) ) {
                mg.defaultMonster = mtype_id::NULL_ID();
            }
        }
    }
    // Further, remove all blacklisted monsters
    for( auto &elem : monsterGroupMap ) {
        MonsterGroup &mg = elem.second;
        for( FreqDef::iterator c = mg.monsters.begin(); c != mg.monsters.end(); ) {
            if( MonsterGroupManager::monster_is_blacklisted( c->name ) ) {
                c = mg.monsters.erase( c );
            } else {
                ++c;
            }
        }
        if( MonsterGroupManager::monster_is_blacklisted( mg.defaultMonster ) ) {
            mg.defaultMonster = mtype_id::NULL_ID();
        }
    }
}
// Verify that the named monster has the expected effective speed, which is greatly reduced
// due to wasted motion from shambling.
// This is an assertion that an average (i.e. no fleet) survivor with no encumbrance
// will be able to out-walk (not run, walk) the given monster
// if their speed is higher than the monster's speed stat.
void check_shamble_speed( const std::string monster_type, const tripoint &destination )
{
    // Scale the scaling factor based on the ratio of diagonal to cardinal steps.
    const float slope = (destination.x < destination.y) ?
        (destination.x / destination.y) : (destination.y / destination.x);
    const float diagonal_multiplier = 1.0 + (OPTIONS["CIRCLEDIST"] ? (slope * 0.41) : 0.0);
    const float mon_speed = (float)monster( mtype_id( monster_type ) ).get_speed();
    INFO( monster_type << " " << destination );
    // Wandering makes things nondeterministic, so look at the distribution rather than a target number.
    statistics move_stats;
    for( int i = 0; i < 10; ++i ) {
        move_stats.add( turns_to_destination( monster_type, {0, 0, 0}, destination ) );
        if( ((move_stats.avg() * mon_speed) / (10000.0 * diagonal_multiplier)) ==
            Approx(1.0).epsilon(0.04) ) {
            break;
        }
    }
    CHECK( ((move_stats.avg() * mon_speed) / (10000.0 * diagonal_multiplier)) ==
           Approx(1.0).epsilon(0.04) );
}
void mission_type::load( JsonObject &jo, const std::string &src )
{
    const bool strict = src == "dda";

    mandatory( jo, was_loaded, "name", name );

    mandatory( jo, was_loaded, "difficulty", difficulty );
    mandatory( jo, was_loaded, "value", value );

    if( jo.has_member( "origins" ) ) {
        origins.clear();
        for( auto &m : jo.get_tags( "origins" ) ) {
            origins.emplace_back( io::string_to_enum_look_up( io::origin_map, m ) );
        }
    }

    if( std::any_of( origins.begin(), origins.end(), []( mission_origin origin ) {
    return origin == ORIGIN_ANY_NPC || origin == ORIGIN_OPENER_NPC || origin == ORIGIN_SECONDARY;
} ) ) {
        auto djo = jo.get_object( "dialogue" );
        // TODO: There should be a cleaner way to do it
        mandatory( djo, was_loaded, "describe", dialogue[ "describe" ] );
        mandatory( djo, was_loaded, "offer", dialogue[ "offer" ] );
        mandatory( djo, was_loaded, "accepted", dialogue[ "accepted" ] );
        mandatory( djo, was_loaded, "rejected", dialogue[ "rejected" ] );
        mandatory( djo, was_loaded, "advice", dialogue[ "advice" ] );
        mandatory( djo, was_loaded, "inquire", dialogue[ "inquire" ] );
        mandatory( djo, was_loaded, "success", dialogue[ "success" ] );
        mandatory( djo, was_loaded, "success_lie", dialogue[ "success_lie" ] );
        mandatory( djo, was_loaded, "failure", dialogue[ "failure" ] );
    }

    optional( jo, was_loaded, "urgent", urgent );
    optional( jo, was_loaded, "item", item_id );
    optional( jo, was_loaded, "count", item_count, 1 );

    goal = jo.get_enum_value<decltype( goal )>( "goal" );

    assign_function( jo, "place", place, tripoint_function_map );
    if( jo.has_string( "start" ) ) {
        assign_function( jo, "start", start, mission_function_map );
    } else if( jo.has_member( "start" ) ) {
        JsonObject j_start = jo.get_object( "start" );
        parse_start( j_start );
    }
    assign_function( jo, "end", end, mission_function_map );
    assign_function( jo, "fail", fail, mission_function_map );

    assign( jo, "deadline_low", deadline_low, false, 1_days );
    assign( jo, "deadline_high", deadline_high, false, 1_days );

    if( jo.has_member( "followup" ) ) {
        follow_up = mission_type_id( jo.get_string( "followup" ) );
    }

    if( jo.has_member( "monster_species" ) ) {
        monster_species = species_id( jo.get_string( "monster_species" ) );
    }
    if( jo.has_member( "monster_type" ) ) {
        monster_type = mtype_id( jo.get_string( "monster_type" ) );
    }

    if( jo.has_member( "monster_kill_goal" ) ) {
        monster_kill_goal = jo.get_int( "monster_kill_goal" );
    }

    assign( jo, "destination", target_id, strict );
}
Exemple #9
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);
        }
    }
}
void MonsterGroupManager::LoadMonsterGroup( JsonObject &jo )
{
    float mon_upgrade_factor = get_option<float>( "MONSTER_UPGRADE_FACTOR" );

    MonsterGroup g;

    g.name = mongroup_id( jo.get_string( "name" ) );
    bool extending = false;  //If already a group with that name, add to it instead of overwriting it
    if( monsterGroupMap.count( g.name ) != 0 && !jo.get_bool( "override", false ) ) {
        g = monsterGroupMap[g.name];
        extending = true;
    }
    if( !extending
        || jo.has_string( "default" ) ) { //Not mandatory to specify default if extending existing group
        g.defaultMonster = mtype_id( jo.get_string( "default" ) );
    }
    g.is_animal = jo.get_bool( "is_animal", false );
    if( jo.has_array( "monsters" ) ) {
        JsonArray monarr = jo.get_array( "monsters" );

        while( monarr.has_more() ) {
            JsonObject mon = monarr.next_object();
            const mtype_id name = mtype_id( mon.get_string( "monster" ) );

            int freq = mon.get_int( "freq" );
            int cost = mon.get_int( "cost_multiplier" );
            int pack_min = 1;
            int pack_max = 1;
            if( mon.has_member( "pack_size" ) ) {
                JsonArray packarr = mon.get_array( "pack_size" );
                pack_min = packarr.next_int();
                pack_max = packarr.next_int();
            }
            static const time_duration tdfactor = 1_hours;
            time_duration starts = 0_turns;
            time_duration ends = 0_turns;
            if( mon.has_member( "starts" ) ) {
                starts = tdfactor * mon.get_int( "starts" ) * ( mon_upgrade_factor > 0 ? mon_upgrade_factor : 1 );
            }
            if( mon.has_member( "ends" ) ) {
                ends = tdfactor * mon.get_int( "ends" ) * ( mon_upgrade_factor > 0 ? mon_upgrade_factor : 1 );
            }
            MonsterGroupEntry new_mon_group = MonsterGroupEntry( name, freq, cost, pack_min, pack_max, starts,
                                              ends );
            if( mon.has_member( "conditions" ) ) {
                JsonArray conditions_arr = mon.get_array( "conditions" );
                while( conditions_arr.has_more() ) {
                    new_mon_group.conditions.push_back( conditions_arr.next_string() );
                }
            }

            g.monsters.push_back( new_mon_group );
        }
    }
    g.replace_monster_group = jo.get_bool( "replace_monster_group", false );
    g.new_monster_group = mongroup_id( jo.get_string( "new_monster_group_id",
                                       mongroup_id::NULL_ID().str() ) );
    assign( jo, "replacement_time", g.monster_group_time, false, 1_days );
    g.is_safe = jo.get_bool( "is_safe", false );

    g.freq_total = jo.get_int( "freq_total", ( extending ? g.freq_total : 1000 ) );
    if( jo.get_bool( "auto_total", false ) ) { //Fit the max size to the sum of all freqs
        int total = 0;
        for( MonsterGroupEntry &mon : g.monsters ) {
            total += mon.frequency;
        }
        g.freq_total = total;
    }

    monsterGroupMap[g.name] = g;
}
bool mission::is_complete( const int _npc_id ) const
{
    if( status == mission_status::success ) {
        return true;
    }

    auto &u = g->u;
    switch( type->goal ) {
        case MGOAL_GO_TO:
            {
                const tripoint cur_pos = g->u.global_omt_location();
                return ( rl_dist( cur_pos, target ) <= 1 );
            }
            break;

        case MGOAL_GO_TO_TYPE:
            {
                const auto cur_ter = overmap_buffer.ter( g->u.global_omt_location() );
                return cur_ter == type->target_id;
            }
            break;

        case MGOAL_FIND_ITEM:
        {
            inventory tmp_inv = u.crafting_inventory();
            // TODO: check for count_by_charges and use appropriate player::has_* function
            if (!tmp_inv.has_amount(type->item_id, item_count)) {
                return tmp_inv.has_amount( type->item_id, 1 ) && tmp_inv.has_charges( type->item_id, item_count );
            }
            if( npc_id != -1 && npc_id != _npc_id ) {
                return false;
            }
        }
            return true;

        case MGOAL_FIND_ANY_ITEM:
            return u.has_mission_item( uid ) && ( npc_id == -1 || npc_id == _npc_id );

        case MGOAL_FIND_MONSTER:
            if( npc_id != -1 && npc_id != _npc_id ) {
                return false;
            }
            for( size_t i = 0; i < g->num_zombies(); i++ ) {
                if( g->zombie( i ).mission_id == uid ) {
                    return true;
                }
            }
            return false;

        case MGOAL_RECRUIT_NPC:
            {
                npc *p = g->find_npc( target_npc_id );
                return p != nullptr && p->attitude == NPCATT_FOLLOW;
            }

        case MGOAL_RECRUIT_NPC_CLASS:
            {
                const auto npcs = overmap_buffer.get_npcs_near_player( 100 );
                for( auto & npc : npcs ) {
                    if( npc->myclass == recruit_class && npc->attitude == NPCATT_FOLLOW ) {
                        return true;
                    }
                }
                return false;
            }

        case MGOAL_FIND_NPC:
            return npc_id == _npc_id;

        case MGOAL_ASSASSINATE:
            return step >= 1;

        case MGOAL_KILL_MONSTER:
            return step >= 1;

        case MGOAL_KILL_MONSTER_TYPE:
            return g->kill_count( mtype_id( monster_type ) ) >= monster_kill_goal;

        case MGOAL_COMPUTER_TOGGLE:
            return step >= 1;

        default:
            return false;
    }
    return false;
}
Exemple #12
0
// We're reading in way too many entities here to mess around with creating sub-objects and
// seeking around in them, so we're using the json streaming API.
submap *mapbuffer::unserialize_submaps( const tripoint &p )
{
    // Map the tripoint to the submap quad that stores it.
    const tripoint om_addr = overmapbuffer::sm_to_omt_copy( p );
    const tripoint segment_addr = overmapbuffer::omt_to_seg_copy( om_addr );
    std::stringstream quad_path;
    quad_path << world_generator->active_world->world_path << "/maps/" <<
              segment_addr.x << "." << segment_addr.y << "." << segment_addr.z << "/" <<
              om_addr.x << "." << om_addr.y << "." << om_addr.z << ".map";

    std::ifstream fin( quad_path.str().c_str() );
    if( !fin.is_open() ) {
        // If it doesn't exist, trigger generating it.
        return NULL;
    }

    JsonIn jsin( fin );
    jsin.start_array();
    while( !jsin.end_array() ) {
        std::unique_ptr<submap> sm(new submap());
        tripoint submap_coordinates;
        jsin.start_object();
        bool rubpow_update = false;
        while( !jsin.end_object() ) {
            std::string submap_member_name = jsin.get_member_name();
            if( submap_member_name == "version" ) {
                if (jsin.get_int() < 22) {
                    rubpow_update = true;
                }
            } else if( submap_member_name == "coordinates" ) {
                jsin.start_array();
                int locx = jsin.get_int();
                int locy = jsin.get_int();
                int locz = jsin.get_int();
                jsin.end_array();
                submap_coordinates = tripoint( locx, locy, locz );
            } else if( submap_member_name == "turn_last_touched" ) {
                sm->turn_last_touched = jsin.get_int();
            } else if( submap_member_name == "temperature" ) {
                sm->temperature = jsin.get_int();
            } else if( submap_member_name == "terrain" ) {
                // TODO: try block around this to error out if we come up short?
                jsin.start_array();
                // Small duplication here so that the update check is only performed once
                if (rubpow_update) {
                    std::string ter_string;
                    item rock = item("rock", 0);
                    item chunk = item("steel_chunk", 0);
                    for( int j = 0; j < SEEY; j++ ) {
                        for( int i = 0; i < SEEX; i++ ) {
                            ter_string = jsin.get_string();
                            if (ter_string == "t_rubble") {
                                sm->ter[i][j] = termap[ "t_dirt" ].loadid;
                                sm->frn[i][j] = furnmap[ "f_rubble" ].loadid;
                                sm->itm[i][j].push_back( rock );
                                sm->itm[i][j].push_back( rock );
                            } else if (ter_string == "t_wreckage"){
                                sm->ter[i][j] = termap[ "t_dirt" ].loadid;
                                sm->frn[i][j] = furnmap[ "f_wreckage" ].loadid;
                                sm->itm[i][j].push_back( chunk );
                                sm->itm[i][j].push_back( chunk );
                            } else if (ter_string == "t_ash"){
                                sm->ter[i][j] = termap[ "t_dirt" ].loadid;
                                sm->frn[i][j] = furnmap[ "f_ash" ].loadid;
                            } else if (ter_string == "t_pwr_sb_support_l"){
                                sm->ter[i][j] = termap[ "t_support_l" ].loadid;
                            } else if (ter_string == "t_pwr_sb_switchgear_l"){
                                sm->ter[i][j] = termap[ "t_switchgear_l" ].loadid;
                            } else if (ter_string == "t_pwr_sb_switchgear_s"){
                                sm->ter[i][j] = termap[ "t_switchgear_s" ].loadid;
                            } else {
                                sm->ter[i][j] = terfind( ter_string );
                            }
                        }
                    }
                } else {
                    for( int j = 0; j < SEEY; j++ ) {
                        for( int i = 0; i < SEEX; i++ ) {
                            sm->ter[i][j] = terfind( jsin.get_string() );
                        }
                    }
                }
                jsin.end_array();
            } else if( submap_member_name == "radiation" ) {
                int rad_cell = 0;
                jsin.start_array();
                while( !jsin.end_array() ) {
                    int rad_strength = jsin.get_int();
                    int rad_num = jsin.get_int();
                    for( int i = 0; i < rad_num; ++i ) {
                        // A little array trick here, assign to it as a 1D array.
                        // If it's not in bounds we're kinda hosed anyway.
                        sm->set_radiation(0, rad_cell, rad_strength);
                        rad_cell++;
                    }
                }
            } else if( submap_member_name == "furniture" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    sm->frn[i][j] = furnmap[ jsin.get_string() ].loadid;
                    jsin.end_array();
                }
            } else if( submap_member_name == "items" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.start_array();
                    while( !jsin.end_array() ) {
                        item tmp;
                        jsin.read( tmp );

                        if( tmp.is_emissive() ) {
                            sm->update_lum_add(tmp, i, j);
                        }

                        tmp.visit_items([&sm,i,j]( item *it ) {
                            for( auto& e: it->magazine_convert() ) {
                                sm->itm[i][j].push_back( e );
                            }
                            return VisitResponse::NEXT;
                        } );

                        sm->itm[i][j].push_back( tmp );
                        if( tmp.needs_processing() ) {
                            sm->active_items.add( std::prev(sm->itm[i][j].end()), point( i, j ) );
                        }
                    }
                }
            } else if( submap_member_name == "traps" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    // TODO: jsin should support returning an id like jsin.get_id<trap>()
                    sm->trp[i][j] = trap_str_id( jsin.get_string() );
                    jsin.end_array();
                }
            } else if( submap_member_name == "fields" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    // Coordinates loop
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.start_array();
                    while( !jsin.end_array() ) {
                        int type = jsin.get_int();
                        int density = jsin.get_int();
                        int age = jsin.get_int();
                        if (sm->fld[i][j].findField(field_id(type)) == NULL) {
                            sm->field_count++;
                        }
                        sm->fld[i][j].addField(field_id(type), density, age);
                    }
                }
            } else if( submap_member_name == "graffiti" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    sm->set_graffiti( i, j, jsin.get_string() );
                    jsin.end_array();
                }
            } else if(submap_member_name == "cosmetics") {
                jsin.start_array();
                while (!jsin.end_array()) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.read(sm->cosmetics[i][j]);
                    jsin.end_array();
                }
            } else if( submap_member_name == "spawns" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    const mtype_id type = mtype_id( jsin.get_string() ); // TODO: json should know how to read an string_id
                    int count = jsin.get_int();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    int faction_id = jsin.get_int();
                    int mission_id = jsin.get_int();
                    bool friendly = jsin.get_bool();
                    std::string name = jsin.get_string();
                    jsin.end_array();
                    spawn_point tmp( type, count, i, j, faction_id, mission_id, friendly, name );
                    sm->spawns.push_back( tmp );
                }
            } else if( submap_member_name == "vehicles" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    vehicle *tmp = new vehicle();
                    jsin.read( *tmp );
                    sm->vehicles.push_back( tmp );
                }
            } else if( submap_member_name == "computers" ) {
                std::string computer_data = jsin.get_string();
                sm->comp.load_data( computer_data );
            } else if( submap_member_name == "camp" ) {
                std::string camp_data = jsin.get_string();
                sm->camp.load_data( camp_data );
            } else {
                jsin.skip_value();
            }
        }
        if( !add_submap( submap_coordinates, sm ) ) {
            debugmsg( "submap %d,%d,%d was already loaded", submap_coordinates.x, submap_coordinates.y,
                      submap_coordinates.z );
        }
    }
    if( submaps.count( p ) == 0 ) {
        debugmsg("file %s did not contain the expected submap %d,%d,%d", quad_path.str().c_str(), p.x, p.y,
                 p.z);
        return NULL;
    }
    return submaps[ p ];
}
Exemple #13
0
bool mission::is_complete( const int _npc_id ) const
{
    if( status == mission_status::success ) {
        return true;
    }

    auto &u = g->u;
    switch( type->goal ) {
        case MGOAL_GO_TO: {
            const tripoint cur_pos = g->u.global_omt_location();
            return ( rl_dist( cur_pos, target ) <= 1 );
        }

        case MGOAL_GO_TO_TYPE: {
            const auto cur_ter = overmap_buffer.ter( g->u.global_omt_location() );
            return is_ot_type( type->target_id.str(), cur_ter );
        }

        case MGOAL_FIND_ITEM: {
            inventory tmp_inv = u.crafting_inventory();
            // TODO: check for count_by_charges and use appropriate player::has_* function
            if( !tmp_inv.has_amount( type->item_id, item_count ) ) {
                return tmp_inv.has_amount( type->item_id, 1 ) && tmp_inv.has_charges( type->item_id, item_count );
            }
            if( npc_id != -1 && npc_id != _npc_id ) {
                return false;
            }
        }
        return true;

        case MGOAL_FIND_ANY_ITEM:
            return u.has_mission_item( uid ) && ( npc_id == -1 || npc_id == _npc_id );

        case MGOAL_FIND_MONSTER:
            if( npc_id != -1 && npc_id != _npc_id ) {
                return false;
            }
            return g->get_creature_if( [&]( const Creature & critter ) {
                const monster *const mon_ptr = dynamic_cast<const monster *>( &critter );
                return mon_ptr && mon_ptr->mission_id == uid;
            } );

        case MGOAL_RECRUIT_NPC: {
            npc *p = g->find_npc( target_npc_id );
            return p != nullptr && p->get_attitude() == NPCATT_FOLLOW;
        }

        case MGOAL_RECRUIT_NPC_CLASS: {
            const auto npcs = overmap_buffer.get_npcs_near_player( 100 );
            for( auto &npc : npcs ) {
                if( npc->myclass == recruit_class && npc->get_attitude() == NPCATT_FOLLOW ) {
                    return true;
                }
            }
            return false;
        }

        case MGOAL_FIND_NPC:
            return npc_id == _npc_id;

        case MGOAL_ASSASSINATE:
            return step >= 1;

        case MGOAL_KILL_MONSTER:
            return step >= 1;

        case MGOAL_KILL_MONSTER_TYPE:
            return g->kill_count( mtype_id( monster_type ) ) >= kill_count_to_reach;

        case MGOAL_KILL_MONSTER_SPEC:
            return g->kill_count( monster_species ) >= kill_count_to_reach;

        case MGOAL_COMPUTER_TOGGLE:
            return step >= 1;

        default:
            return false;
    }
}
void player::activate_mutation( const trait_id &mut )
{
    const mutation_branch &mdata = mut.obj();
    auto &tdata = my_mutations[mut];
    int cost = mdata.cost;
    // You can take yourself halfway to Near Death levels of hunger/thirst.
    // Fatigue can go to Exhausted.
    if ((mdata.hunger && get_hunger() >= 700) || (mdata.thirst && get_thirst() >= 260) ||
      (mdata.fatigue && get_fatigue() >= EXHAUSTED)) {
      // Insufficient Foo to *maintain* operation is handled in player::suffer
        add_msg_if_player(m_warning, _("You feel like using your %s would kill you!"), mdata.name.c_str());
        return;
    }
    if (tdata.powered && tdata.charge > 0) {
        // Already-on units just lose a bit of charge
        tdata.charge--;
    } else {
        // Not-on units, or those with zero charge, have to pay the power cost
        if (mdata.cooldown > 0) {
            tdata.charge = mdata.cooldown - 1;
        }
        if (mdata.hunger){
            mod_hunger(cost);
        }
        if (mdata.thirst){
            mod_thirst(cost);
        }
        if (mdata.fatigue){
            mod_fatigue(cost);
        }
        tdata.powered = true;

        // Handle stat changes from activation
        apply_mods(mut, true);
        recalc_sight_limits();
    }

    if( mut == trait_WEB_WEAVER ) {
        g->m.add_field( pos(), fd_web, 1 );
        add_msg_if_player(_("You start spinning web with your spinnerets!"));
    } else if (mut == "BURROW"){
        if( is_underwater() ) {
            add_msg_if_player(m_info, _("You can't do that while underwater."));
            tdata.powered = false;
            return;
        }
        tripoint dirp;
        if (!choose_adjacent(_("Burrow where?"), dirp)) {
            tdata.powered = false;
            return;
        }

        if( dirp == pos() ) {
            add_msg_if_player(_("You've got places to go and critters to beat."));
            add_msg_if_player(_("Let the lesser folks eat their hearts out."));
            tdata.powered = false;
            return;
        }
        time_duration time_to_do = 0_turns;
        if (g->m.is_bashable(dirp) && g->m.has_flag("SUPPORTS_ROOF", dirp) &&
            g->m.ter(dirp) != t_tree) {
            // Being better-adapted to the task means that skillful Survivors can do it almost twice as fast.
            time_to_do = 30_minutes;
        } else if (g->m.move_cost(dirp) == 2 && g->get_levz() == 0 &&
                   g->m.ter(dirp) != t_dirt && g->m.ter(dirp) != t_grass) {
            time_to_do = 10_minutes;
        } else {
            add_msg_if_player(m_info, _("You can't burrow there."));
            tdata.powered = false;
            return;
        }
        assign_activity( activity_id( "ACT_BURROW" ), to_moves<int>( time_to_do ), -1, 0 );
        activity.placement = dirp;
        add_msg_if_player(_("You tear into the %s with your teeth and claws."),
                          g->m.tername(dirp).c_str());
        tdata.powered = false;
        return; // handled when the activity finishes
    } else if( mut == trait_SLIMESPAWNER ) {
        std::vector<tripoint> valid;
        for( const tripoint &dest : g->m.points_in_radius( pos(), 1 ) ) {
            if (g->is_empty(dest)) {
                valid.push_back( dest );
            }
        }
        // Oops, no room to divide!
        if( valid.empty() ) {
            add_msg_if_player(m_bad, _("You focus, but are too hemmed in to birth a new slimespring!"));
            tdata.powered = false;
            return;
        }
        add_msg_if_player(m_good, _("You focus, and with a pleasant splitting feeling, birth a new slimespring!"));
        int numslime = 1;
        for (int i = 0; i < numslime && !valid.empty(); i++) {
            const tripoint target = random_entry_removed( valid );
            if( monster * const slime = g->summon_mon( mtype_id( "mon_player_blob" ), target ) ) {
                slime->friendly = -1;
            }
        }
        if (one_in(3)) {
            //~ Usual enthusiastic slimespring small voices! :D
            add_msg_if_player(m_good, _("wow! you look just like me! we should look out for each other!"));
        } else if (one_in(2)) {
            //~ Usual enthusiastic slimespring small voices! :D
            add_msg_if_player(m_good, _("come on, big me, let's go!"));
        } else {
            //~ Usual enthusiastic slimespring small voices! :D
            add_msg_if_player(m_good, _("we're a team, we've got this!"));
        }
        tdata.powered = false;
        return;
    } else if( mut == trait_NAUSEA || mut == trait_VOMITOUS ) {
        vomit();
        tdata.powered = false;
        return;
    } else if( mut == trait_M_FERTILE ) {
        spores();
        tdata.powered = false;
        return;
    } else if( mut == trait_M_BLOOM ) {
        blossoms();
        tdata.powered = false;
        return;
    } else if( mut == trait_SELFAWARE ) {
        print_health();
        tdata.powered = false;
        return;
    } else if( !mdata.spawn_item.empty() ) {
        item tmpitem( mdata.spawn_item );
        i_add_or_drop( tmpitem );
        add_msg_if_player( _( mdata.spawn_item_message.c_str() ) );
        tdata.powered = false;
        return;
    }
}
void MonsterGenerator::load_monster(JsonObject &jo)
{
    const mtype_id mid = mtype_id( jo.get_string("id") );
        if (mon_templates.count(mid) > 0) {
            delete mon_templates[mid];
        }

        mtype *newmon = new mtype;

        newmon->id = mid;
        newmon->name = jo.get_string("name").c_str();
        if(jo.has_member("name_plural")) {
            newmon->name_plural = jo.get_string("name_plural");
        } else {
            // default behaviour: Assume the regular plural form (appending an “s”)
            newmon->name_plural = newmon->name + "s";
        }
        newmon->description = _(jo.get_string("description").c_str());

        // Have to overwrite the default { "hflesh" } here
        newmon->mat = { jo.get_string("material") };

        for( auto &s : jo.get_tags( "species" ) ) {
            newmon->species.insert( species_id( s ) );
        }
        newmon->categories = jo.get_tags("categories");

        // See monfaction.cpp
        newmon->default_faction =
            monfactions::get_or_add_faction( mfaction_str_id( jo.get_string("default_faction") ) );

        newmon->sym = jo.get_string("symbol");
        if( utf8_wrapper( newmon->sym ).display_width() != 1 ) {
            jo.throw_error( "monster symbol should be exactly one console cell width", "symbol" );
        }
        newmon->color = color_from_string(jo.get_string("color"));
        newmon->size = get_from_string(jo.get_string("size", "MEDIUM"), Creature::size_map, MS_MEDIUM);
        newmon->phase = get_from_string(jo.get_string("phase", "SOLID"), phase_map, SOLID);

        newmon->difficulty = jo.get_int("diff", 0);
        newmon->agro = jo.get_int("aggression", 0);
        newmon->morale = jo.get_int("morale", 0);
        newmon->speed = jo.get_int("speed", 0);
        newmon->attack_cost = jo.get_int("attack_cost", 100);
        newmon->melee_skill = jo.get_int("melee_skill", 0);
        newmon->melee_dice = jo.get_int("melee_dice", 0);
        newmon->melee_sides = jo.get_int("melee_dice_sides", 0);
        newmon->melee_cut = jo.get_int("melee_cut", 0);
        newmon->sk_dodge = jo.get_int("dodge", 0);
        newmon->armor_bash = jo.get_int("armor_bash", 0);
        newmon->armor_cut = jo.get_int("armor_cut", 0);
        newmon->hp = jo.get_int("hp", 0);
        jo.read("starting_ammo", newmon->starting_ammo);
        newmon->luminance = jo.get_float("luminance", 0);
        newmon->revert_to_itype = jo.get_string( "revert_to_itype", "" );
        newmon->vision_day = jo.get_int("vision_day", 40);
        newmon->vision_night = jo.get_int("vision_night", 1);

        if (jo.has_array("attack_effs")) {
            JsonArray jsarr = jo.get_array("attack_effs");
            while (jsarr.has_more()) {
                JsonObject e = jsarr.next_object();
                mon_effect_data new_eff(e.get_string("id", "null"), e.get_int("duration", 0),
                                    get_body_part_token( e.get_string("bp", "NUM_BP") ), e.get_bool("permanent", false),
                                    e.get_int("chance", 100));
                newmon->atk_effs.push_back(new_eff);
            }
        }

        if( jo.has_member( "death_drops" ) ) {
            JsonIn& stream = *jo.get_raw( "death_drops" );
            newmon->death_drops = item_group::load_item_group( stream, "distribution" );
        }

        newmon->dies = get_death_functions(jo, "death_function");
        load_special_defense(newmon, jo, "special_when_hit");
        load_special_attacks(newmon, jo, "special_attacks");

        if (jo.has_member("upgrades")) {
            JsonObject upgrades = jo.get_object("upgrades");
            newmon->half_life = upgrades.get_int("half_life", -1);
            newmon->upgrade_group = mongroup_id( upgrades.get_string("into_group", mongroup_id::NULL_ID.str() ) );
            newmon->upgrade_into = mtype_id( upgrades.get_string("into", mtype_id::NULL_ID.str() ) );
            newmon->upgrades = true;
        }

        std::set<std::string> flags, anger_trig, placate_trig, fear_trig;
        flags = jo.get_tags("flags");
        anger_trig = jo.get_tags("anger_triggers");
        placate_trig = jo.get_tags("placate_triggers");
        fear_trig = jo.get_tags("fear_triggers");

        newmon->flags = get_set_from_tags(flags, flag_map, MF_NULL);
        newmon->anger = get_set_from_tags(anger_trig, trigger_map, MTRIG_NULL);
        newmon->fear = get_set_from_tags(fear_trig, trigger_map, MTRIG_NULL);
        newmon->placate = get_set_from_tags(placate_trig, trigger_map, MTRIG_NULL);

        mon_templates[mid] = newmon;
}
void MonsterGenerator::check_monster_definitions() const
{
    for( const auto &mon : mon_templates->get_all() ) {
        if( mon.harvest == "null" && !mon.has_flag( MF_ELECTRONIC ) && mon.id != mtype_id( "mon_null" ) ) {
            debugmsg( "monster %s has no harvest entry", mon.id.c_str(), mon.harvest.c_str() );
        }
        for( auto &spec : mon.species ) {
            if( !spec.is_valid() ) {
                debugmsg( "monster %s has invalid species %s", mon.id.c_str(), spec.c_str() );
            }
        }
        if( !mon.death_drops.empty() && !item_group::group_is_defined( mon.death_drops ) ) {
            debugmsg( "monster %s has unknown death drop item group: %s", mon.id.c_str(),
                      mon.death_drops.c_str() );
        }
        for( auto &m : mon.mat ) {
            if( m.str() == "null" || !m.is_valid() ) {
                debugmsg( "monster %s has unknown material: %s", mon.id.c_str(), m.c_str() );
            }
        }
        if( !mon.revert_to_itype.empty() && !item::type_is_defined( mon.revert_to_itype ) ) {
            debugmsg( "monster %s has unknown revert_to_itype: %s", mon.id.c_str(),
                      mon.revert_to_itype.c_str() );
        }
        for( auto &s : mon.starting_ammo ) {
            if( !item::type_is_defined( s.first ) ) {
                debugmsg( "starting ammo %s of monster %s is unknown", s.first.c_str(), mon.id.c_str() );
            }
        }
        for( auto &e : mon.atk_effs ) {
            if( !e.id.is_valid() ) {
                debugmsg( "attack effect %s of monster %s is unknown", e.id.c_str(), mon.id.c_str() );
            }
        }

        for( const auto &e : mon.emit_fields ) {
            if( !e.is_valid() ) {
                debugmsg( "monster %s has invalid emit source %s", mon.id.c_str(), e.c_str() );
            }
        }

        if( mon.upgrades ) {
            if( mon.half_life < 0 && mon.age_grow < 0 ) {
                debugmsg( "half_life %d and age_grow %d (<0) of monster %s is invalid",
                          mon.half_life, mon.age_grow, mon.id.c_str() );
            }
            if( !mon.upgrade_into && !mon.upgrade_group ) {
                debugmsg( "no into nor into_group defined for monster %s", mon.id.c_str() );
            }
            if( mon.upgrade_into && mon.upgrade_group ) {
                debugmsg( "both into and into_group defined for monster %s", mon.id.c_str() );
            }
            if( !mon.upgrade_into.is_valid() ) {
                debugmsg( "upgrade_into %s of monster %s is not a valid monster id",
                          mon.upgrade_into.c_str(), mon.id.c_str() );
            }
            if( !mon.upgrade_group.is_valid() ) {
                debugmsg( "upgrade_group %s of monster %s is not a valid monster group id",
                          mon.upgrade_group.c_str(), mon.id.c_str() );
            }
        }

        if( mon.reproduces ) {
            if( mon.baby_timer < 1 ) {
                debugmsg( "Time between reproductions (%d) is invalid for %s",
                          mon.baby_timer, mon.id.c_str() );
            }
            if( mon.baby_count < 1 ) {
                debugmsg( "Number of children (%d) is invalid for %s",
                          mon.baby_count, mon.id.c_str() );
            }
            if( !mon.baby_monster && mon.baby_egg == "null" ) {
                debugmsg( "No baby or egg defined for monster %s", mon.id.c_str() );
            }
            if( mon.baby_monster && mon.baby_egg != "null" ) {
                debugmsg( "Both an egg and a live birth baby are defined for %s", mon.id.c_str() );
            }
            if( !mon.baby_monster.is_valid() ) {
                debugmsg( "baby_monster %s of monster %s is not a valid monster id",
                          mon.baby_monster.c_str(), mon.id.c_str() );
            }
            if( !item::type_is_defined( mon.baby_egg ) ) {
                debugmsg( "item_id %s of monster %s is not a valid item id",
                          mon.baby_egg.c_str(), mon.id.c_str() );
            }
        }

        if( mon.biosignatures ) {
            if( mon.biosig_timer < 1 ) {
                debugmsg( "Time between biosignature drops (%d) is invalid for %s",
                          mon.biosig_timer, mon.id.c_str() );
            }
            if( mon.biosig_item == "null" ) {
                debugmsg( "No biosignature drop defined for monster %s", mon.id.c_str() );
            }
            if( !item::type_is_defined( mon.biosig_item ) ) {
                debugmsg( "item_id %s of monster %s is not a valid item id",
                          mon.biosig_item.c_str(), mon.id.c_str() );
            }
        }
    }
}
Exemple #17
0
void player::activate_mutation( const std::string &mut )
{
    const auto &mdata = mutation_branch::get( mut );
    auto &tdata = my_mutations[mut];
    int cost = mdata.cost;
    // You can take yourself halfway to Near Death levels of hunger/thirst.
    // Fatigue can go to Exhausted.
    if ((mdata.hunger && get_hunger() >= 700) || (mdata.thirst && get_thirst() >= 260) ||
      (mdata.fatigue && get_fatigue() >= EXHAUSTED)) {
      // Insufficient Foo to *maintain* operation is handled in player::suffer
        add_msg_if_player(m_warning, _("You feel like using your %s would kill you!"), mdata.name.c_str());
        return;
    }
    if (tdata.powered && tdata.charge > 0) {
        // Already-on units just lose a bit of charge
        tdata.charge--;
    } else {
        // Not-on units, or those with zero charge, have to pay the power cost
        if (mdata.cooldown > 0) {
            tdata.charge = mdata.cooldown - 1;
        }
        if (mdata.hunger){
            mod_hunger(cost);
        }
        if (mdata.thirst){
            mod_thirst(cost);
        }
        if (mdata.fatigue){
            mod_fatigue(cost);
        }
        tdata.powered = true;

        // Handle stat changes from activation
        apply_mods(mut, true);
        recalc_sight_limits();
    }

    if( mut == "WEB_WEAVER" ) {
        g->m.add_field(pos(), fd_web, 1, 0);
        add_msg_if_player(_("You start spinning web with your spinnerets!"));
    } else if (mut == "BURROW"){
        if( is_underwater() ) {
            add_msg_if_player(m_info, _("You can't do that while underwater."));
            tdata.powered = false;
            return;
        }
        tripoint dirp;
        if (!choose_adjacent(_("Burrow where?"), dirp)) {
            tdata.powered = false;
            return;
        }

        if( dirp == pos() ) {
            add_msg_if_player(_("You've got places to go and critters to beat."));
            add_msg_if_player(_("Let the lesser folks eat their hearts out."));
            tdata.powered = false;
            return;
        }
        int turns;
        if (g->m.is_bashable(dirp) && g->m.has_flag("SUPPORTS_ROOF", dirp) &&
            g->m.ter(dirp) != t_tree) {
            // Takes about 100 minutes (not quite two hours) base time.
            // Being better-adapted to the task means that skillful Survivors can do it almost twice as fast.
            ///\EFFECT_CARPENTRY speeds up burrowing
            turns = (100000 - 5000 * skillLevel( skill_id( "carpentry" ) ));
        } else if (g->m.move_cost(dirp) == 2 && g->get_levz() == 0 &&
                   g->m.ter(dirp) != t_dirt && g->m.ter(dirp) != t_grass) {
            turns = 18000;
        } else {
            add_msg_if_player(m_info, _("You can't burrow there."));
            tdata.powered = false;
            return;
        }
        assign_activity(ACT_BURROW, turns, -1, 0);
        activity.placement = dirp;
        add_msg_if_player(_("You tear into the %s with your teeth and claws."),
                          g->m.tername(dirp).c_str());
        tdata.powered = false;
        return; // handled when the activity finishes
    } else if (mut == "SLIMESPAWNER") {
        std::vector<tripoint> valid;
        for (int x = posx() - 1; x <= posx() + 1; x++) {
            for (int y = posy() - 1; y <= posy() + 1; y++) {
                tripoint dest(x, y, posz());
                if (g->is_empty(dest)) {
                    valid.push_back( dest );
                }
            }
        }
        // Oops, no room to divide!
        if (valid.size() == 0) {
            add_msg_if_player(m_bad, _("You focus, but are too hemmed in to birth a new slimespring!"));
            tdata.powered = false;
            return;
        }
        add_msg_if_player(m_good, _("You focus, and with a pleasant splitting feeling, birth a new slimespring!"));
        int numslime = 1;
        for (int i = 0; i < numslime && !valid.empty(); i++) {
            const tripoint target = random_entry_removed( valid );
            if (g->summon_mon(mtype_id( "mon_player_blob" ), target)) {
                monster *slime = g->monster_at( target );
                slime->friendly = -1;
            }
        }
        if (one_in(3)) {
            //~ Usual enthusiastic slimespring small voices! :D
            add_msg_if_player(m_good, _("wow! you look just like me! we should look out for each other!"));
        } else if (one_in(2)) {
            //~ Usual enthusiastic slimespring small voices! :D
            add_msg_if_player(m_good, _("come on, big me, let's go!"));
        } else {
            //~ Usual enthusiastic slimespring small voices! :D
            add_msg_if_player(m_good, _("we're a team, we've got this!"));
        }
        tdata.powered = false;
        return;
    } else if ((mut == "NAUSEA") || (mut == "VOMITOUS") ){
        vomit();
        tdata.powered = false;
        return;
    } else if (mut == "M_FERTILE"){
        spores();
        tdata.powered = false;
        return;
    } else if (mut == "M_BLOOM"){
        blossoms();
        tdata.powered = false;
        return;
    } else if (mut == "VINES3"){
        item newit( "vine_30", calendar::turn );
        if (!can_pickVolume(newit.volume())) { //Accounts for result_mult
            add_msg_if_player(_("You detach a vine but don't have room to carry it, so you drop it."));
            g->m.add_item_or_charges(pos(), newit);
        } else if (!can_pickWeight(newit.weight(), !OPTIONS["DANGEROUS_PICKUPS"])) {
            add_msg_if_player(_("Your freshly-detached vine is too heavy to carry, so you drop it."));
            g->m.add_item_or_charges(pos(), newit);
        } else {
            inv.assign_empty_invlet(newit);
            newit = i_add(newit);
            add_msg_if_player(m_info, "%c - %s", newit.invlet == 0 ? ' ' : newit.invlet, newit.tname().c_str());
        }
        tdata.powered = false;
        return;
    } else if( mut == "SELFAWARE" ) {
        print_health();
        tdata.powered = false;
        return;
    }
}