Example #1
0
bool tutorial_game::init()
{
    // TODO: clean up old tutorial

    calendar::turn = HOURS( 12 ); // Start at noon
    for( auto &elem : tutorials_seen ) {
        elem = false;
    }
    g->scent.reset();
    g->temperature = 65;
    // We use a Z-factor of 10 so that we don't plop down tutorial rooms in the
    // middle of the "real" game world
    g->u.normalize();
    g->u.str_cur = g->u.str_max;
    g->u.per_cur = g->u.per_max;
    g->u.int_cur = g->u.int_max;
    g->u.dex_cur = g->u.dex_max;

    for( int i = 0; i < num_hp_parts; i++ ) {
        g->u.hp_cur[i] = g->u.hp_max[i];
    }

    const oter_id rock( "rock" );
    //~ default name for the tutorial
    g->u.name = _( "John Smith" );
    g->u.prof = profession::generic();
    // overmap terrain coordinates
    const int lx = 50;
    const int ly = 50;
    auto &starting_om = overmap_buffer.get( 0, 0 );
    for( int i = 0; i < OMAPX; i++ ) {
        for( int j = 0; j < OMAPY; j++ ) {
            starting_om.ter( i, j, -1 ) = rock;
            // Start with the overmap revealed
            starting_om.seen( i, j, 0 ) = true;
        }
    }
    starting_om.ter( lx, ly, 0 ) = oter_id( "tutorial" );
    starting_om.ter( lx, ly, -1 ) = oter_id( "tutorial" );
    starting_om.clear_mon_groups();

    g->u.toggle_trait( trait_id( "QUICK" ) );
    item lighter( "lighter", 0 );
    lighter.invlet = 'e';
    g->u.inv.add_item( lighter, true, false );
    g->u.set_skill_level( skill_id( "gun" ), 5 );
    g->u.set_skill_level( skill_id( "melee" ), 5 );
    g->load_map( omt_to_sm_copy( tripoint( lx, ly, 0 ) ) );
    g->u.setx( 2 );
    g->u.sety( 4 );

    // This shifts the view to center the players pos
    g->update_map( g->u );
    return true;
}
Example #2
0
void fault::load_fault( JsonObject &jo )
{
    fault f;

    f.id_ = fault_id( jo.get_string( "id" ) );
    f.name_ = _( jo.get_string( "name" ) );
    f.description_ = _( jo.get_string( "description" ) );

    f.time_ = jo.get_int( "time" );

    auto sk = jo.get_array( "skills" );
    while( sk.has_more() ) {
        auto cur = sk.next_array();
        f.skills_.emplace( skill_id( cur.get_string( 0 ) ), cur.size() >= 2 ? cur.get_int( 1 ) : 1 );
    }

    if( jo.has_string( "requirements" ) ) {
        f.requirements_ = requirement_id( jo.get_string( "requirements" ) );

    } else {
        auto req = jo.get_object( "requirements" );
        const requirement_id req_id( std::string( "inline_fault_" ) + f.id_.str() );
        requirement_data::load_requirement( req, req_id );
        f.requirements_ = req_id;
    }

    if( faults_all.find( f.id_ ) != faults_all.end() ) {
        jo.throw_error( "parsed fault overwrites existing definition", "id" );
    } else {
        faults_all[ f.id_ ] = f;
    }
}
Example #3
0
void npc_class::load( JsonObject &jo, const std::string & )
{
    mandatory( jo, was_loaded, "name", name, translated_string_reader );
    mandatory( jo, was_loaded, "job_description", job_description, translated_string_reader );

    optional( jo, was_loaded, "common", common, true );
    bonus_str = load_distribution( jo, "bonus_str" );
    bonus_dex = load_distribution( jo, "bonus_dex" );
    bonus_int = load_distribution( jo, "bonus_int" );
    bonus_per = load_distribution( jo, "bonus_per" );

    optional( jo, was_loaded, "shopkeeper_item_group", shopkeeper_item_group, "EMPTY_GROUP" );
    optional( jo, was_loaded, "worn_override", worn_override );
    optional( jo, was_loaded, "carry_override", carry_override );
    optional( jo, was_loaded, "weapon_override", weapon_override );

    if( jo.has_array( "traits" ) ) {
        JsonArray jarr = jo.get_array( "traits" );
        while( jarr.has_more() ) {
            JsonArray jarr_in = jarr.next_array();
            traits[ trait_id( jarr_in.get_string( 0 ) ) ] = jarr_in.get_int( 1 );
        }
    }

    if( jo.has_array( "skills" ) ) {
        JsonArray jarr = jo.get_array( "skills" );
        while( jarr.has_more() ) {
            JsonObject skill_obj = jarr.next_object();
            auto skill_ids = skill_obj.get_tags( "skill" );
            if( skill_obj.has_object( "level" ) ) {
                distribution dis = load_distribution( skill_obj, "level" );
                for( const auto &sid : skill_ids ) {
                    skills[ skill_id( sid ) ] = dis;
                }
            } else {
                distribution dis = load_distribution( skill_obj, "bonus" );
                for( const auto &sid : skill_ids ) {
                    bonus_skills[ skill_id( sid ) ] = dis;
                }
            }
        }
    }
}
void fungal_effects::fungalize( const tripoint &sporep, Creature *origin, double spore_chance )
{
    int mondex = gm.mon_at( sporep );
    if( mondex != -1 ) { // Spores hit a monster
        if( gm.u.sees( sporep ) &&
            !gm.zombie( mondex ).type->in_species( FUNGUS ) ) {
            add_msg( _( "The %s is covered in tiny spores!" ),
                     gm.zombie( mondex ).name().c_str() );
        }
        monster &critter = gm.zombie( mondex );
        if( !critter.make_fungus() ) {
            // Don't insta-kill non-fungables. Jabberwocks, for example
            critter.add_effect( effect_stunned, rng( 1, 3 ) );
            critter.apply_damage( origin, bp_torso, rng( 25, 50 ) );
        }
    } else if( gm.u.pos() == sporep ) {
        player &pl = gm.u; // TODO: Make this accept NPCs when they understand fungals
        ///\EFFECT_DEX increases chance of knocking fungal spores away with your TAIL_CATTLE

        ///\EFFECT_MELEE increases chance of knocking fungal sports away with your TAIL_CATTLE
        if( pl.has_trait( trait_id( "TAIL_CATTLE" ) ) &&
            one_in( 20 - pl.dex_cur - pl.get_skill_level( skill_id( "melee" ) ) ) ) {
            pl.add_msg_if_player(
                _( "The spores land on you, but you quickly swat them off with your tail!" ) );
            return;
        }
        // Spores hit the player--is there any hope?
        bool hit = false;
        hit |= one_in( 4 ) && pl.add_env_effect( effect_spores, bp_head, 3, 90, bp_head );
        hit |= one_in( 2 ) && pl.add_env_effect( effect_spores, bp_torso, 3, 90, bp_torso );
        hit |= one_in( 4 ) && pl.add_env_effect( effect_spores, bp_arm_l, 3, 90, bp_arm_l );
        hit |= one_in( 4 ) && pl.add_env_effect( effect_spores, bp_arm_r, 3, 90, bp_arm_r );
        hit |= one_in( 4 ) && pl.add_env_effect( effect_spores, bp_leg_l, 3, 90, bp_leg_l );
        hit |= one_in( 4 ) && pl.add_env_effect( effect_spores, bp_leg_r, 3, 90, bp_leg_r );
        if( hit ) {
            add_msg( m_warning, _( "You're covered in tiny spores!" ) );
        }
    } else if( gm.num_zombies() < 250 && x_in_y( spore_chance, 1.0 ) ) { // Spawn a spore
        if( gm.summon_mon( mon_spore, sporep ) ) {
            monster *spore = gm.monster_at( sporep );
            monster *origin_mon = dynamic_cast<monster *>( origin );
            if( origin_mon != nullptr ) {
                spore->make_ally( origin_mon );
            } else if( origin != nullptr && origin->is_player() &&
                       gm.u.has_trait( trait_id( "THRESH_MYCUS" ) ) ) {
                spore->friendly = 1000;
            }
        }
    } else {
        spread_fungus( sporep );
    }
}
Example #5
0
void Skill::load_skill(JsonObject &jsobj)
{
    skill_id ident = skill_id( jsobj.get_string("ident") );
    skills.erase(std::remove_if(begin(skills), end(skills), [&](Skill const &s) {
        return s._ident == ident; }), end(skills));

    std::string name           = _(jsobj.get_string("name").c_str());
    std::string description    = _(jsobj.get_string("description").c_str());
    std::set<std::string> tags = jsobj.get_tags("tags");

    DebugLog( D_INFO, DC_ALL ) << "Loaded skill: " << name;

    skills.emplace_back(std::move(ident), std::move(name), std::move(description),
                        std::move(tags));
}
Example #6
0
void Skill::load_skill( JsonObject &jsobj )
{
    skill_id ident = skill_id( jsobj.get_string( "ident" ) );
    skills.erase( std::remove_if( begin( skills ), end( skills ), [&]( const Skill & s ) {
        return s._ident == ident;
    } ), end( skills ) );

    const Skill sk( ident, _( jsobj.get_string( "name" ) ), _( jsobj.get_string( "description" ) ),
                    jsobj.get_tags( "tags" ) );

    if( sk.is_contextual_skill() ) {
        contextual_skills[sk.ident()] = sk;
    } else {
        skills.push_back( sk );
    }
}
void gun_actor::load( JsonObject &obj )
{
    // Mandatory
    gun_type = obj.get_string( "gun_type" );
    ammo_type = obj.get_string( "ammo_type" );

    JsonArray jarr = obj.get_array( "fake_skills" );
    while( jarr.has_more() ) {
        JsonArray cur = jarr.next_array();
        fake_skills[skill_id( cur.get_string( 0 ) )] = cur.get_int( 1 );
    }

    range = obj.get_float( "range" );
    description = obj.get_string( "description" );
    move_cost = obj.get_int( "move_cost" );
    targeting_cost = obj.get_int( "targeting_cost" );

    // Optional:
    max_ammo = obj.get_int( "max_ammo", INT_MAX );

    fake_str = obj.get_int( "fake_str", 8 );
    fake_dex = obj.get_int( "fake_dex", 8 );
    fake_int = obj.get_int( "fake_int", 8 );
    fake_per = obj.get_int( "fake_per", 8 );

    require_targeting_player = obj.get_bool( "require_targeting_player", true );
    require_targeting_npc = obj.get_bool( "require_targeting_npc", false );
    require_targeting_monster = obj.get_bool( "require_targeting_monster", false );
    targeting_timeout = obj.get_int( "targeting_timeout", 8 );
    targeting_timeout_extend = obj.get_int( "targeting_timeout_extend", 3 );

    burst_limit = obj.get_int( "burst_limit", INT_MAX );

    laser_lock = obj.get_bool( "laser_lock", false );

    range_no_burst = obj.get_float( "range_no_burst", range + 1 );

    if( obj.has_member( "targeting_sound" ) || obj.has_member( "targeting_volume" ) ) {
        // Both or neither, but not just one
        targeting_sound = obj.get_string( "targeting_sound" );
        targeting_volume = obj.get_int( "targeting_volume" );
    }

    // Sound of no ammo
    no_ammo_sound = obj.get_string( "no_ammo_sound", "" );
}
bool player::can_arm_block() const
{
    const martialart &ma = style_selected.obj();
    ///\EFFECT_UNARMED increases ability to perform arm block
    int unarmed_skill = has_active_bionic("bio_cqb") ? 5 : (int)get_skill_level(skill_id("unarmed"));

    // Success conditions.
    if (hp_cur[hp_arm_l] > 0 || hp_cur[hp_arm_r] > 0) {
        if( unarmed_skill >= ma.arm_block ) {
            return true;
        } else if( ma.arm_block_with_bio_armor_arms && has_bionic("bio_armor_arms") ) {
            return true;
        }
    }
    // if not above, can't block.
    return false;
}
Example #9
0
static void parse_vp_reqs( JsonObject &obj, const std::string &id, const std::string &key,
                           std::vector<std::pair<requirement_id, int>> &reqs,
                           std::map<skill_id, int> &skills, int &moves )
{

    if( !obj.has_object( key ) ) {
        return;
    }
    auto src = obj.get_object( key );

    auto sk = src.get_array( "skills" );
    if( !sk.empty() ) {
        skills.clear();
    }
    while( sk.has_more() ) {
        auto cur = sk.next_array();
        skills.emplace( skill_id( cur.get_string( 0 ) ), cur.size() >= 2 ? cur.get_int( 1 ) : 1 );
    }

    assign( src, "time", moves );

    if( src.has_string( "using" ) ) {
        reqs = { { requirement_id( src.get_string( "using" ) ), 1 } };

    } else if( src.has_array( "using" ) ) {
        auto arr = src.get_array( "using" );
        while( arr.has_more() ) {
            auto cur = arr.next_array();
            reqs.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) );
        }

    } else {
        const requirement_id req_id( string_format( "inline_%s_%s", key.c_str(), id.c_str() ) );
        requirement_data::load_requirement( src, req_id );
        reqs = { { req_id, 1 } };
    }
}
Example #10
0
void fault::load_fault( JsonObject &jo )
{
    fault f;

    f.id_ = fault_id( jo.get_string( "id" ) );
    f.name_ = _( jo.get_string( "name" ).c_str() );
    f.description_ = _( jo.get_string( "description" ).c_str() );

    auto sk = jo.get_array( "skills" );
    while( sk.has_more() ) {
        auto cur = sk.next_array();
        f.skills_.emplace( skill_id( cur.get_string( 0 ) ) , cur.size() >= 2 ? cur.get_int( 1 ) : 1 );
    }

    auto req = jo.get_object( "requirements" );
    f.requirements_.load( req );

    if( faults_all.find( f.id_ ) != faults_all.end() ) {
        jo.throw_error( "parsed fault overwrites existing definition", "id" );
    } else {
        faults_all[ f.id_ ] = f;
        DebugLog( D_INFO, DC_ALL ) << "Loaded fault: " << f.name_;
    }
}
Example #11
0
/**
 * Reads in a vehicle part from a JsonObject.
 */
void vpart_info::load( JsonObject &jo, const std::string &src )
{
    vpart_info def;

    if( jo.has_string( "copy-from" ) ) {
        auto const base = vehicle_part_types.find( vpart_str_id( jo.get_string( "copy-from" ) ) );
        auto const ab = abstract_parts.find( vpart_str_id( jo.get_string( "copy-from" ) ) );
        if( base != vehicle_part_types.end() ) {
            def = base->second;
        } else if( ab != abstract_parts.end() ) {
            def = ab->second;
        } else {
            deferred.emplace_back( jo.str(), src );
        }
    }

    if( jo.has_string( "abstract" ) ) {
        def.id = vpart_str_id( jo.get_string( "abstract" ) );
    } else {
        def.id = vpart_str_id( jo.get_string( "id" ) );
    }

    assign( jo, "name", def.name_ );
    assign( jo, "item", def.item );
    assign( jo, "location", def.location );
    assign( jo, "durability", def.durability );
    assign( jo, "damage_modifier", def.dmg_mod );
    assign( jo, "power", def.power );
    assign( jo, "epower", def.epower );
    assign( jo, "fuel_type", def.fuel_type );
    assign( jo, "folded_volume", def.folded_volume );
    assign( jo, "size", def.size );
    assign( jo, "difficulty", def.difficulty );
    assign( jo, "bonus", def.bonus );
    assign( jo, "flags", def.flags );

    auto reqs = jo.get_object( "requirements" );
    if( reqs.has_object( "install" ) ) {
        auto ins = reqs.get_object( "install" );

        auto sk = ins.get_array( "skills" );
        if( !sk.empty() ) {
            def.install_skills.clear();
        }
        while( sk.has_more() ) {
            auto cur = sk.next_array();
            def.install_skills.emplace( skill_id( cur.get_string( 0 ) ) , cur.size() >= 2 ? cur.get_int( 1 ) : 1 );
        }

        assign( ins, "time", def.install_moves );

        if( ins.has_string( "using" ) ) {
            def.install_reqs = { { requirement_id( ins.get_string( "using" ) ), 1 } };

        } else if( ins.has_array( "using" ) ) {
            auto arr = ins.get_array( "using" );
            while( arr.has_more() ) {
                auto cur = arr.next_array();
                def.install_reqs.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) );
            }

        } else {
            auto req_id = std::string( "inline_vehins_" ) += def.id.str();
            requirement_data::load_requirement( ins, req_id );
            def.install_reqs = { { requirement_id( req_id ), 1 } };
        }

        def.legacy = false;
    }
    if( reqs.has_object( "removal" ) ) {
        auto rem = reqs.get_object( "removal" );

        auto sk = rem.get_array( "skills" );
        if( !sk.empty() ) {
            def.removal_skills.clear();
        }
        while( sk.has_more() ) {
            auto cur = sk.next_array();
            def.removal_skills.emplace( skill_id( cur.get_string( 0 ) ) , cur.size() >= 2 ? cur.get_int( 1 ) : 1 );
        }

        assign( rem, "time", def.removal_moves );

        if( rem.has_string( "using" ) ) {
            def.removal_reqs = { { requirement_id( rem.get_string( "using" ) ), 1 } };

        } else if( rem.has_array( "using" ) ) {
            auto arr = rem.get_array( "using" );
            while( arr.has_more() ) {
                auto cur = arr.next_array();
                def.removal_reqs.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) );
            }

        } else {
            auto req_id = std::string( "inline_vehins_" ) += def.id.str();
            requirement_data::load_requirement( rem, req_id );
            def.removal_reqs = { { requirement_id( req_id ), 1 } };
        }

        def.legacy = false;
    }

    if( jo.has_member( "symbol" ) ) {
        def.sym = jo.get_string( "symbol" )[ 0 ];
    }
    if( jo.has_member( "broken_symbol" ) ) {
        def.sym_broken = jo.get_string( "broken_symbol" )[ 0 ];
    }

    if( jo.has_member( "color" ) ) {
        def.color = color_from_string( jo.get_string( "color" ) );
    }
    if( jo.has_member( "broken_color" ) ) {
        def.color_broken = color_from_string( jo.get_string( "broken_color" ) );
    }

    if( jo.has_member( "breaks_into" ) ) {
        JsonIn& stream = *jo.get_raw( "breaks_into" );
        def.breaks_into_group = item_group::load_item_group( stream, "collection" );
    }

    auto qual = jo.get_array( "qualities" );
    if( !qual.empty() ) {
        def.qualities.clear();
        while( qual.has_more() ) {
            auto pair = qual.next_array();
            def.qualities[ quality_id( pair.get_string( 0 ) ) ] = pair.get_int( 1 );
        }
    }

    if( jo.has_member( "damage_reduction" ) ) {
        JsonObject dred = jo.get_object( "damage_reduction" );
        def.damage_reduction = load_damage_array( dred );
    } else {
        def.damage_reduction.fill( 0.0f );
    }

    if( jo.has_string( "abstract" ) ) {
        abstract_parts[ def.id ] = def;
        return;
    }

    auto const iter = vehicle_part_types.find( def.id );
    if( iter != vehicle_part_types.end() ) {
        // Entry in the map already exists, so the pointer in the vector is already correct
        // and does not need to be changed, only the int-id needs to be taken from the old entry.
        def.loadid = iter->second.loadid;
        iter->second = def;
    } else {
        // The entry is new, "generate" a new int-id and link the new entry from the vector.
        def.loadid = vpart_id( vehicle_part_int_types.size() );
        vpart_info &new_entry = vehicle_part_types[ def.id ];
        new_entry = def;
        vehicle_part_int_types.push_back( &new_entry );
    }    
}
Example #12
0
int vehicle::automatic_fire_turret( vehicle_part &pt )
{
    auto gun = turret_query( pt );
    if( gun.query() != turret_data::status::ready ) {
        return 0;
    }

    tripoint pos = global_part_pos3( pt );

    npc tmp;
    tmp.set_fake( true );
    tmp.name = rmp_format( _( "<veh_player>The %s" ), pt.name().c_str() );
    tmp.set_skill_level( gun.base()->gun_skill(), 8 );
    tmp.set_skill_level( skill_id( "gun" ), 4 );
    tmp.recoil = abs( velocity ) / 100 / 4;
    tmp.setpos( pos );
    tmp.str_cur = 16;
    tmp.dex_cur = 8;
    tmp.per_cur = 12;
    // Assume vehicle turrets are friendly to the player.
    tmp.attitude = NPCATT_FOLLOW;

    int area = aoe_size( gun.ammo_effects() );
    if( area > 0 ) {
        area += area == 1 ? 1 : 2; // Pad a bit for less friendly fire
    }

    tripoint targ = pos;
    auto &target = pt.target;
    if( target.first == target.second ) {
        // Manual target not set, find one automatically
        const bool u_see = g->u.sees( pos );
        int boo_hoo;

        // @todo calculate chance to hit and cap range based upon this
        int range = std::min( gun.range(), 12 );
        Creature *auto_target = tmp.auto_find_hostile_target( range, boo_hoo, area );
        if( auto_target == nullptr ) {
            if( u_see && boo_hoo ) {
                add_msg( m_warning, ngettext( "%s points in your direction and emits an IFF warning beep.",
                                              "%s points in your direction and emits %d annoyed sounding beeps.",
                                              boo_hoo ),
                         tmp.name.c_str(), boo_hoo );
            }
            return 0;
        }

        targ = auto_target->pos();
    } else if( target.first != target.second ) {
        // Target set manually
        // Make sure we didn't move between aiming and firing (it's a bug if we did)
        if( targ != target.first ) {
            target.second = target.first;
            return 0;
        }

        targ = target.second;
        // Remove the target
        target.second = target.first;
    } else {
        // Shouldn't happen
        target.first = target.second;
        return 0;
    }

    auto shots = gun.fire( tmp, targ );

    if( g->u.sees( pos ) && shots ) {
        add_msg( _( "The %1$s fires its %2$s!" ), name.c_str(), pt.name().c_str() );
    }

    return shots;
}
Example #13
0
void recipe::load( JsonObject &jo, const std::string &src )
{
    bool strict = src == "dda";

    abstract = jo.has_string( "abstract" );

    if( abstract ) {
        ident_ = recipe_id( jo.get_string( "abstract" ) );
    } else {
        result_ = jo.get_string( "result" );
        ident_ = recipe_id( result_ );
    }

    assign( jo, "time", time, strict, 0 );
    assign( jo, "difficulty", difficulty, strict, 0, MAX_SKILL );
    assign( jo, "flags", flags );

    // automatically set contained if we specify as container
    assign( jo, "contained", contained, strict );
    contained |= assign( jo, "container", container, strict );

    if( jo.has_array( "batch_time_factors" ) ) {
        auto batch = jo.get_array( "batch_time_factors" );
        batch_rscale = batch.get_int( 0 ) / 100.0;
        batch_rsize  = batch.get_int( 1 );
    }

    assign( jo, "charges", charges );
    assign( jo, "result_mult", result_mult );

    assign( jo, "skill_used", skill_used, strict );

    if( jo.has_member( "skills_required" ) ) {
        auto sk = jo.get_array( "skills_required" );
        required_skills.clear();

        if( sk.empty() ) {
            // clear all requirements

        } else if( sk.has_array( 0 ) ) {
            // multiple requirements
            while( sk.has_more() ) {
                auto arr = sk.next_array();
                required_skills[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 );
            }

        } else {
            // single requirement
            required_skills[skill_id( sk.get_string( 0 ) )] = sk.get_int( 1 );
        }
    }

    // simplified autolearn sets requirements equal to required skills at finalization
    if( jo.has_bool( "autolearn" ) ) {
        assign( jo, "autolearn", autolearn );

    } else if( jo.has_array( "autolearn" ) ) {
        autolearn = true;
        auto sk = jo.get_array( "autolearn" );
        while( sk.has_more() ) {
            auto arr = sk.next_array();
            autolearn_requirements[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 );
        }
    }

    if( jo.has_member( "decomp_learn" ) ) {
        learn_by_disassembly.clear();

        if( jo.has_int( "decomp_learn" ) ) {
            if( !skill_used ) {
                jo.throw_error( "decomp_learn specified with no skill_used" );
            }
            assign( jo, "decomp_learn", learn_by_disassembly[skill_used] );

        } else if( jo.has_array( "decomp_learn" ) ) {
            auto sk = jo.get_array( "decomp_learn" );
            while( sk.has_more() ) {
                auto arr = sk.next_array();
                learn_by_disassembly[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 );
            }
        }
    }

    if( jo.has_member( "book_learn" ) ) {
        auto bk = jo.get_array( "book_learn" );
        booksets.clear();

        while( bk.has_more() ) {
            auto arr = bk.next_array();
            booksets.emplace( arr.get_string( 0 ), arr.size() > 1 ? arr.get_int( 1 ) : -1 );
        }
    }

    // recipes not specifying any external requirements inherit from their parent recipe (if any)
    if( jo.has_string( "using" ) ) {
        reqs_external = { { requirement_id( jo.get_string( "using" ) ), 1 } };

    } else if( jo.has_array( "using" ) ) {
        auto arr = jo.get_array( "using" );
        reqs_external.clear();

        while( arr.has_more() ) {
            auto cur = arr.next_array();
            reqs_external.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) );
        }
    }

    const std::string type = jo.get_string( "type" );

    if( type == "recipe" ) {
        if( jo.has_string( "id_suffix" ) ) {
            if( abstract ) {
                jo.throw_error( "abstract recipe cannot specify id_suffix", "id_suffix" );
            }
            ident_ = recipe_id( ident_.str() + "_" + jo.get_string( "id_suffix" ) );
        }

        assign( jo, "category", category, strict );
        assign( jo, "subcategory", subcategory, strict );
        assign( jo, "reversible", reversible, strict );

        if( jo.has_member( "byproducts" ) ) {
            auto bp = jo.get_array( "byproducts" );
            byproducts.clear();
            while( bp.has_more() ) {
                auto arr = bp.next_array();
                byproducts[ arr.get_string( 0 ) ] += arr.size() == 2 ? arr.get_int( 1 ) : 1;
            }
        }
    } else if( type == "uncraft" ) {
        reversible = true;
    } else {
        jo.throw_error( "unknown recipe type", "type" );
    }

    // inline requirements are always replaced (cannot be inherited)
    const auto req_id = string_format( "inline_%s_%s", type.c_str(), ident_.c_str() );
    requirement_data::load_requirement( jo, req_id );
    reqs_internal = { { requirement_id( req_id ), 1 } };
}
Example #14
0
void npc_class::load( JsonObject &jo, const std::string & )
{
    mandatory( jo, was_loaded, "name", name, translated_string_reader );
    mandatory( jo, was_loaded, "job_description", job_description, translated_string_reader );

    optional( jo, was_loaded, "common", common, true );
    bonus_str = load_distribution( jo, "bonus_str" );
    bonus_dex = load_distribution( jo, "bonus_dex" );
    bonus_int = load_distribution( jo, "bonus_int" );
    bonus_per = load_distribution( jo, "bonus_per" );

    optional( jo, was_loaded, "shopkeeper_item_group", shopkeeper_item_group, "EMPTY_GROUP" );
    optional( jo, was_loaded, "worn_override", worn_override );
    optional( jo, was_loaded, "carry_override", carry_override );
    optional( jo, was_loaded, "weapon_override", weapon_override );

    if( jo.has_array( "traits" ) ) {
        traits = trait_group::load_trait_group( *jo.get_raw( "traits" ), "collection" );
    }

    /* Mutation rounds can be specified as follows:
     *   "mutation_rounds": {
     *     "ANY" : { "constant": 1 },
     *     "INSECT" : { "rng": [1, 3] }
     *   }
     */
    if( jo.has_object( "mutation_rounds" ) ) {
        const std::map<std::string, mutation_category_trait> &mutation_categories =
            mutation_category_trait::get_all();
        auto jo2 = jo.get_object( "mutation_rounds" );
        for( auto &mutation : jo2.get_member_names() ) {
            auto category_match = [&mutation]( std::pair<const std::string, mutation_category_trait> p ) {
                return p.second.id == mutation;
            };
            if( std::find_if( mutation_categories.begin(), mutation_categories.end(),
                              category_match ) == mutation_categories.end() ) {
                debugmsg( "Unrecognized mutation category %s", mutation );
                continue;
            }
            auto distrib = jo2.get_object( mutation );
            mutation_rounds[mutation] = load_distribution( distrib );
        }
    }

    if( jo.has_array( "skills" ) ) {
        JsonArray jarr = jo.get_array( "skills" );
        while( jarr.has_more() ) {
            JsonObject skill_obj = jarr.next_object();
            auto skill_ids = skill_obj.get_tags( "skill" );
            if( skill_obj.has_object( "level" ) ) {
                distribution dis = load_distribution( skill_obj, "level" );
                for( const auto &sid : skill_ids ) {
                    skills[ skill_id( sid ) ] = dis;
                }
            } else {
                distribution dis = load_distribution( skill_obj, "bonus" );
                for( const auto &sid : skill_ids ) {
                    bonus_skills[ skill_id( sid ) ] = dis;
                }
            }
        }
    }
}
Example #15
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);
        }
    }
}
Example #16
0
bool game::dump_stats( const std::string &what, dump_mode mode,
                       const std::vector<std::string> &opts )
{
    try {
        loading_ui ui( false );
        load_core_data( ui );
        load_packs( _( "Loading content packs" ), { mod_id( "dda" ) }, ui );
        DynamicDataLoader::get_instance().finalize_loaded_data( ui );
    } catch( const std::exception &err ) {
        std::cerr << "Error loading data from json: " << err.what() << std::endl;
        return false;
    }

    std::vector<std::string> header;
    std::vector<std::vector<std::string>> rows;

    int scol = 0; // sorting column

    std::map<std::string, standard_npc> test_npcs;
    test_npcs[ "S1" ] = standard_npc( "S1", { "gloves_survivor", "mask_lsurvivor" }, 4, 8, 10, 8,
                                      10 /* DEX 10, PER 10 */ );
    test_npcs[ "S2" ] = standard_npc( "S2", { "gloves_fingerless", "sunglasses" }, 4, 8, 8, 8,
                                      10 /* PER 10 */ );
    test_npcs[ "S3" ] = standard_npc( "S3", { "gloves_plate", "helmet_plate" },  4, 10, 8, 8,
                                      8 /* STAT 10 */ );
    test_npcs[ "S4" ] = standard_npc( "S4", {}, 0, 8, 10, 8, 10 /* DEX 10, PER 10 */ );
    test_npcs[ "S5" ] = standard_npc( "S5", {}, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ );
    test_npcs[ "S6" ] = standard_npc( "S6", { "gloves_hsurvivor", "mask_hsurvivor" }, 4, 8, 10, 8,
                                      10 /* DEX 10, PER 10 */ );

    std::map<std::string, item> test_items;
    test_items[ "G1" ] = item( "glock_19" ).ammo_set( "9mm" );
    test_items[ "G2" ] = item( "hk_mp5" ).ammo_set( "9mm" );
    test_items[ "G3" ] = item( "ar15" ).ammo_set( "223" );
    test_items[ "G4" ] = item( "remington_700" ).ammo_set( "270" );
    test_items[ "G4" ].emplace_back( "rifle_scope" );

    if( what == "AMMO" ) {
        header = {
            "Name", "Ammo", "Volume", "Weight", "Stack",
            "Range", "Dispersion", "Recoil", "Damage", "Pierce"
        };
        auto dump = [&rows]( const item & obj ) {
            // a common task is comparing ammo by type so ammo has multiple repeat the entry
            for( const auto &e : obj.type->ammo->type ) {
                std::vector<std::string> r;
                r.push_back( obj.tname( 1, false ) );
                r.push_back( e.str() );
                r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) );
                r.push_back( to_string( to_gram( obj.weight() ) ) );
                r.push_back( to_string( obj.type->stack_size ) );
                r.push_back( to_string( obj.type->ammo->range ) );
                r.push_back( to_string( obj.type->ammo->dispersion ) );
                r.push_back( to_string( obj.type->ammo->recoil ) );
                damage_instance damage = obj.type->ammo->damage;
                r.push_back( to_string( damage.total_damage() ) );
                r.push_back( to_string( damage.empty() ? 0 : ( *damage.begin() ).res_pen ) );
                rows.push_back( r );
            }
        };
        for( const itype *e : item_controller->all() ) {
            if( e->ammo ) {
                dump( item( e, calendar::turn, item::solitary_tag {} ) );
            }
        }

    } else if( what == "ARMOR" ) {
        header = {
            "Name", "Encumber (fit)", "Warmth", "Weight", "Storage", "Coverage", "Bash", "Cut", "Acid", "Fire"
        };
        auto dump = [&rows]( const item & obj ) {
            std::vector<std::string> r;
            r.push_back( obj.tname( 1, false ) );
            r.push_back( to_string( obj.get_encumber() ) );
            r.push_back( to_string( obj.get_warmth() ) );
            r.push_back( to_string( to_gram( obj.weight() ) ) );
            r.push_back( to_string( obj.get_storage() / units::legacy_volume_factor ) );
            r.push_back( to_string( obj.get_coverage() ) );
            r.push_back( to_string( obj.bash_resist() ) );
            r.push_back( to_string( obj.cut_resist() ) );
            r.push_back( to_string( obj.acid_resist() ) );
            r.push_back( to_string( obj.fire_resist() ) );
            rows.push_back( r );
        };

        body_part bp = opts.empty() ? num_bp : get_body_part_token( opts.front() );

        for( const itype *e : item_controller->all() ) {
            if( e->armor ) {
                item obj( e );
                if( bp == num_bp || obj.covers( bp ) ) {
                    if( obj.has_flag( "VARSIZE" ) ) {
                        obj.item_tags.insert( "FIT" );
                    }
                    dump( obj );
                }
            }
        }

    } else if( what == "EDIBLE" ) {
        header = {
            "Name", "Volume", "Weight", "Stack", "Calories", "Quench", "Healthy"
        };
        for( const auto &v : vitamin::all() ) {
            header.push_back( v.second.name() );
        }
        auto dump = [&rows]( const item & obj ) {
            std::vector<std::string> r;
            r.push_back( obj.tname( false ) );
            r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) );
            r.push_back( to_string( to_gram( obj.weight() ) ) );
            r.push_back( to_string( obj.type->stack_size ) );
            r.push_back( to_string( obj.type->comestible->get_calories() ) );
            r.push_back( to_string( obj.type->comestible->quench ) );
            r.push_back( to_string( obj.type->comestible->healthy ) );
            auto vits = g->u.vitamins_from( obj );
            for( const auto &v : vitamin::all() ) {
                r.push_back( to_string( vits[ v.first ] ) );
            }
            rows.push_back( r );
        };

        for( const itype *e : item_controller->all() ) {
            item food( e, calendar::turn, item::solitary_tag {} );

            if( food.is_food() && g->u.can_eat( food ).success() ) {
                dump( food );
            }
        }

    } else if( what == "GUN" ) {
        header = {
            "Name", "Ammo", "Volume", "Weight", "Capacity",
            "Range", "Dispersion", "Effective recoil", "Damage", "Pierce",
            "Aim time", "Effective range", "Snapshot range", "Max range"
        };

        std::set<std::string> locations;
        for( const itype *e : item_controller->all() ) {
            if( e->gun ) {
                std::transform( e->gun->valid_mod_locations.begin(),
                                e->gun->valid_mod_locations.end(),
                                std::inserter( locations, locations.begin() ),
                []( const std::pair<gunmod_location, int> &q ) {
                    return q.first.name();
                } );
            }
        }
        for( const auto &e : locations ) {
            header.push_back( e );
        }

        auto dump = [&rows, &locations]( const standard_npc & who, const item & obj ) {
            std::vector<std::string> r;
            r.push_back( obj.tname( 1, false ) );
            r.push_back( obj.ammo_type() ? obj.ammo_type().str() : "" );
            r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) );
            r.push_back( to_string( to_gram( obj.weight() ) ) );
            r.push_back( to_string( obj.ammo_capacity() ) );
            r.push_back( to_string( obj.gun_range() ) );
            r.push_back( to_string( obj.gun_dispersion() ) );
            r.push_back( to_string( obj.gun_recoil( who ) ) );
            damage_instance damage = obj.gun_damage();
            r.push_back( to_string( damage.total_damage() ) );
            r.push_back( to_string( damage.empty() ? 0 : ( *damage.begin() ).res_pen ) );

            r.push_back( to_string( who.gun_engagement_moves( obj ) ) );

            for( const auto &e : locations ) {
                const auto &vml = obj.type->gun->valid_mod_locations;
                const auto iter = vml.find( e );
                r.push_back( to_string( iter != vml.end() ? iter->second : 0 ) );
            }
            rows.push_back( r );
        };
        for( const itype *e : item_controller->all() ) {
            if( e->gun ) {
                item gun( e );
                if( !gun.magazine_integral() ) {
                    gun.emplace_back( gun.magazine_default() );
                }
                gun.ammo_set( gun.ammo_type()->default_ammotype(), gun.ammo_capacity() );

                dump( test_npcs[ "S1" ], gun );

                if( gun.type->gun->barrel_length > 0 ) {
                    gun.emplace_back( "barrel_small" );
                    dump( test_npcs[ "S1" ], gun );
                }
            }
        }

    } else if( what == "RECIPE" ) {

        // optionally filter recipes to include only those using specified skills
        recipe_subset dict;
        for( const auto &r : recipe_dict ) {
            if( opts.empty() || std::any_of( opts.begin(), opts.end(), [&r]( const std::string & s ) {
            if( r.second.skill_used == skill_id( s ) && r.second.difficulty > 0 ) {
                    return true;
                }
                auto iter = r.second.required_skills.find( skill_id( s ) );
                return iter != r.second.required_skills.end() && iter->second > 0;
            } ) ) {
                dict.include( &r.second );
            }
        }

        // only consider skills that are required by at least one recipe
        std::vector<Skill> sk;
        std::copy_if( Skill::skills.begin(), Skill::skills.end(),
        std::back_inserter( sk ), [&dict]( const Skill & s ) {
            return std::any_of( dict.begin(), dict.end(), [&s]( const recipe * r ) {
                return r->skill_used == s.ident() ||
                       r->required_skills.find( s.ident() ) != r->required_skills.end();
            } );
        } );

        header = { "Result" };

        for( const auto &e : sk ) {
            header.push_back( e.ident().str() );
        }

        for( const recipe *e : dict ) {
            std::vector<std::string> r;
            r.push_back( e->result_name() );
            for( const auto &s : sk ) {
                if( e->skill_used == s.ident() ) {
                    r.push_back( to_string( e->difficulty ) );
                } else {
                    auto iter = e->required_skills.find( s.ident() );
                    r.push_back( to_string( iter != e->required_skills.end() ? iter->second : 0 ) );
                }
            }
            rows.push_back( r );
        }

    } else if( what == "VEHICLE" ) {
        header = {
            "Name", "Weight (empty)", "Weight (fueled)",
            "Max velocity (mph)", "Safe velocity (mph)", "Acceleration (mph/turn)",
            "Mass coeff %", "Aerodynamics coeff %", "Friction coeff %",
            "Traction coeff % (grass)"
        };
        auto dump = [&rows]( const vproto_id & obj ) {
            auto veh_empty = vehicle( obj, 0, 0 );
            auto veh_fueled = vehicle( obj, 100, 0 );

            std::vector<std::string> r;
            r.push_back( veh_empty.name );
            r.push_back( to_string( to_kilogram( veh_empty.total_mass() ) ) );
            r.push_back( to_string( to_kilogram( veh_fueled.total_mass() ) ) );
            r.push_back( to_string( veh_fueled.max_velocity() / 100 ) );
            r.push_back( to_string( veh_fueled.safe_velocity() / 100 ) );
            r.push_back( to_string( veh_fueled.acceleration() / 100 ) );
            r.push_back( to_string( ( int )( 100 * veh_fueled.k_mass() ) ) );
            r.push_back( to_string( ( int )( 100 * veh_fueled.k_aerodynamics() ) ) );
            r.push_back( to_string( ( int )( 100 * veh_fueled.k_friction() ) ) );
            r.push_back( to_string( ( int )( 100 * veh_fueled.k_traction( veh_fueled.wheel_area(
                                                 false ) / 2.0f ) ) ) );
            rows.push_back( r );
        };
        for( auto &e : vehicle_prototype::get_all() ) {
            dump( e );
        }

    } else if( what == "VPART" ) {
        header = {
            "Name", "Location", "Weight", "Size"
        };
        auto dump = [&rows]( const vpart_info & obj ) {
            std::vector<std::string> r;
            r.push_back( obj.name() );
            r.push_back( obj.location );
            r.push_back( to_string( int( ceil( to_gram( item( obj.item ).weight() ) / 1000.0 ) ) ) );
            r.push_back( to_string( obj.size / units::legacy_volume_factor ) );
            rows.push_back( r );
        };
        for( const auto &e : vpart_info::all() ) {
            dump( e.second );
        }

    } else {
        std::cerr << "unknown argument: " << what << std::endl;
        return false;
    }

    rows.erase( std::remove_if( rows.begin(), rows.end(), []( const std::vector<std::string> &e ) {
        return e.empty();
    } ), rows.end() );

    if( scol >= 0 ) {
        std::sort( rows.begin(), rows.end(), [&scol]( const std::vector<std::string> &lhs,
        const std::vector<std::string> &rhs ) {
            return lhs[ scol ] < rhs[ scol ];
        } );
    }

    rows.erase( std::unique( rows.begin(), rows.end() ), rows.end() );

    switch( mode ) {
        case dump_mode::TSV:
            rows.insert( rows.begin(), header );
            for( const auto &r : rows ) {
                std::copy( r.begin(), r.end() - 1, std::ostream_iterator<std::string>( std::cout, "\t" ) );
                std::cout << r.back() << "\n";
            }
            break;

        case dump_mode::HTML:
            std::cout << "<table>";

            std::cout << "<thead>";
            std::cout << "<tr>";
            for( const auto &col : header ) {
                std::cout << "<th>" << col << "</th>";
            }
            std::cout << "</tr>";
            std::cout << "</thead>";

            std::cout << "<tdata>";
            for( const auto &r : rows ) {
                std::cout << "<tr>";
                for( const auto &col : r ) {
                    std::cout << "<td>" << col << "</td>";
                }
                std::cout << "</tr>";
            }
            std::cout << "</tdata>";

            std::cout << "</table>";
            break;
    }

    return true;
}
Example #17
0
void gun_actor::load_internal( JsonObject &obj, const std::string & )
{
    gun_type = obj.get_string( "gun_type" );

    obj.read( "ammo_type", ammo_type );

    if( obj.has_array( "fake_skills" ) ) {
        JsonArray jarr = obj.get_array( "fake_skills" );
        while( jarr.has_more() ) {
            JsonArray cur = jarr.next_array();
            fake_skills[skill_id( cur.get_string( 0 ) )] = cur.get_int( 1 );
        }
    }

    obj.read( "fake_str", fake_str );
    obj.read( "fake_dex", fake_dex );
    obj.read( "fake_int", fake_int );
    obj.read( "fake_per", fake_per );

    auto arr = obj.get_array( "ranges" );
    while( arr.has_more() ) {
        auto mode = arr.next_array();
        if( mode.size() < 2 || mode.get_int( 0 ) > mode.get_int( 1 ) ) {
            obj.throw_error( "incomplete or invalid range specified", "ranges" );
        }
        ranges.emplace( std::make_pair<int, int>( mode.get_int( 0 ), mode.get_int( 1 ) ),
                        gun_mode_id( mode.size() > 2 ? mode.get_string( 2 ) : "" ) );
    }

    obj.read( "max_ammo", max_ammo );

    obj.read( "move_cost", move_cost );

    if( obj.read( "description", description ) ) {
        description = _( description );
    }
    if( obj.read( "failure_msg", failure_msg ) ) {
        failure_msg = _( failure_msg );
    }
    if( obj.read( "no_ammo_sound", no_ammo_sound ) ) {
        no_ammo_sound = _( no_ammo_sound );
    } else {
        no_ammo_sound = _( "Click." );
    }

    obj.read( "targeting_cost", targeting_cost );

    obj.read( "require_targeting_player", require_targeting_player );
    obj.read( "require_targeting_npc", require_targeting_npc );
    obj.read( "require_targeting_monster", require_targeting_monster );

    obj.read( "targeting_timeout", targeting_timeout );
    obj.read( "targeting_timeout_extend", targeting_timeout_extend );

    if( obj.read( "targeting_sound", targeting_sound ) ) {
        targeting_sound = _( targeting_sound );
    } else {
        targeting_sound = _( "Beep." );
    }

    obj.read( "targeting_volume", targeting_volume );

    obj.get_bool( "laser_lock", laser_lock );

    obj.read( "require_sunlight", require_sunlight );
}
Example #18
0
 void erase_next( JsonIn &jin, C &container ) const {
     const skill_id id = skill_id( jin.get_string() );
     reader_detail::handler<C>().erase_if( container, [&id]( const std::pair<skill_id, int> &e ) {
         return e.first == id;
     } );
 }
Example #19
0
void recipe_dictionary::load( JsonObject &jo, const std::string &src, bool uncraft )
{
    bool strict = src == "core";

    recipe r;

    // defer entries dependent upon as-yet unparsed definitions
    if( jo.has_string( "copy-from" ) ) {
        auto base = jo.get_string( "copy-from" );
        if( uncraft ) {
            if( !recipe_dict.uncraft.count( base ) ) {
                deferred.emplace_back( jo.str(), src );
                return;
            }
            r = recipe_dict.uncraft[ base ];
        } else {
            if( !recipe_dict.recipes.count( base ) ) {
                deferred.emplace_back( jo.str(), src );
                return;
            }
            r = recipe_dict.recipes[ base ];
        }
    }

    if( jo.has_string( "abstract" ) ) {
        r.ident_ = jo.get_string( "abstract" );
        r.abstract = true;
    } else {
        r.ident_ = r.result = jo.get_string( "result" );
        r.abstract = false;
    }

    if( !uncraft ) {
        if( jo.has_string( "id_suffix" ) ) {
            if( r.abstract ) {
                jo.throw_error( "abstract recipe cannot specify id_suffix", "id_suffix" );
            }
            r.ident_ += "_" + jo.get_string( "id_suffix" );
        }
        assign( jo, "category", r.category, strict );
        assign( jo, "subcategory", r.subcategory, strict );
        assign( jo, "reversible", r.reversible, strict );
    } else {
        r.reversible = true;
    }

    assign( jo, "time", r.time, strict, 0 );
    assign( jo, "difficulty", r.difficulty, strict, 0, MAX_SKILL );
    assign( jo, "flags", r.flags );

    // automatically set contained if we specify as container
    assign( jo, "contained", r.contained, strict );
    r.contained |= assign( jo, "container", r.container, strict );

    if( jo.has_array( "batch_time_factors" ) ) {
        auto batch = jo.get_array( "batch_time_factors" );
        r.batch_rscale = batch.get_int( 0 ) / 100.0;
        r.batch_rsize  = batch.get_int( 1 );
    }

    assign( jo, "charges", r.charges );
    assign( jo, "result_mult", r.result_mult );

    assign( jo, "skill_used", r.skill_used, strict );

    if( jo.has_member( "skills_required" ) ) {
        auto sk = jo.get_array( "skills_required" );
        r.required_skills.clear();

        if( sk.empty() ) {
            // clear all requirements

        } else if( sk.has_array( 0 ) ) {
            // multiple requirements
            while( sk.has_more() ) {
                auto arr = sk.next_array();
                r.required_skills[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 );
            }

        } else {
            // single requirement
            r.required_skills[skill_id( sk.get_string( 0 ) )] = sk.get_int( 1 );
        }
    }

    // simplified autolearn sets requirements equal to required skills at finalization
    if( jo.has_bool( "autolearn" ) ) {
        assign( jo, "autolearn", r.autolearn );

    } else if( jo.has_array( "autolearn" ) ) {
        r.autolearn = true;
        auto sk = jo.get_array( "autolearn" );
        while( sk.has_more() ) {
            auto arr = sk.next_array();
            r.autolearn_requirements[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 );
        }
    }

    if( jo.has_member( "decomp_learn" ) ) {
        r.learn_by_disassembly.clear();

        if( jo.has_int( "decomp_learn" ) ) {
            if( !r.skill_used ) {
                jo.throw_error( "decomp_learn specified with no skill_used" );
            }
            assign( jo, "decomp_learn", r.learn_by_disassembly[r.skill_used] );

        } else if( jo.has_array( "decomp_learn" ) ) {
            auto sk = jo.get_array( "decomp_learn" );
            while( sk.has_more() ) {
                auto arr = sk.next_array();
                r.learn_by_disassembly[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 );
            }
        }
    }

    if( !uncraft && jo.has_member( "byproducts" ) ) {
        auto bp = jo.get_array( "byproducts" );
        r.byproducts.clear();
        while( bp.has_more() ) {
            auto arr = bp.next_array();
            r.byproducts[ arr.get_string( 0 ) ] += arr.size() == 2 ? arr.get_int( 1 ) : 1;
        }
    }

    if( jo.has_member( "book_learn" ) ) {
        auto bk = jo.get_array( "book_learn" );
        r.booksets.clear();

        while( bk.has_more() ) {
            auto arr = bk.next_array();
            r.booksets.emplace( arr.get_string( 0 ), arr.get_int( 1 ) );
        }
    }

    // recipes not specifying any external requirements inherit from their parent recipe (if any)
    if( jo.has_string( "using" ) ) {
        r.reqs_external = { { requirement_id( jo.get_string( "using" ) ), 1 } };

    } else if( jo.has_array( "using" ) ) {
        auto arr = jo.get_array( "using" );
        r.reqs_external.clear();

        while( arr.has_more() ) {
            auto cur = arr.next_array();
            r.reqs_external.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) );
        }
    }

    // inline requirements are always replaced (cannot be inherited)
    auto req_id = string_format( "inline_%s_%s", uncraft ? "uncraft" : "recipe", r.ident_.c_str() );
    requirement_data::load_requirement( jo, req_id );
    r.reqs_internal = { { requirement_id( req_id ), 1 } };

    if( uncraft ) {
        recipe_dict.uncraft[ r.ident_ ] = r;
    } else {
        recipe_dict.recipes[ r.ident_ ] = r;
    }
}
Example #20
0
void load_construction(JsonObject &jo)
{
    construction con;
    con.id = constructions.size();

    con.description = _(jo.get_string("description").c_str());
    con.skill = skill_id( jo.get_string( "skill", skill_carpentry.str() ) );
    con.difficulty = jo.get_int("difficulty");
    con.category = jo.get_string("category", "OTHER");
    // constructions use different time units in json, this makes it compatible
    // with recipes/requirements, TODO: should be changed in json
    con.time = jo.get_int("time") * 1000;

    if( jo.has_string( "using" ) ) {
        con.requirements = requirement_id( jo.get_string( "using" ) );
    } else {
        // Warning: the IDs may change!
        std::string req_id = string_format( "inline_construction_%i", con.id );
        requirement_data::load_requirement( jo, req_id );
        con.requirements = requirement_id( req_id );
    }

    con.pre_terrain = jo.get_string("pre_terrain", "");
    if (con.pre_terrain.size() > 1
        && con.pre_terrain[0] == 'f'
        && con.pre_terrain[1] == '_') {
        con.pre_is_furniture = true;
    } else {
        con.pre_is_furniture = false;
    }

    con.post_terrain = jo.get_string("post_terrain", "");
    if (con.post_terrain.size() > 1
        && con.post_terrain[0] == 'f'
        && con.post_terrain[1] == '_') {
        con.post_is_furniture = true;
    } else {
        con.post_is_furniture = false;
    }

    con.pre_flags = jo.get_tags("pre_flags");

    std::string prefunc = jo.get_string("pre_special", "");
    if (prefunc == "check_empty") {
        con.pre_special = &construct::check_empty;
    } else if (prefunc == "check_support") {
        con.pre_special = &construct::check_support;
    } else if (prefunc == "check_deconstruct") {
        con.pre_special = &construct::check_deconstruct;
    } else if (prefunc == "check_up_OK") {
        con.pre_special = &construct::check_up_OK;
    } else if (prefunc == "check_down_OK") {
        con.pre_special = &construct::check_down_OK;
    } else {
        if (prefunc != "") {
            debugmsg("Unknown pre_special function: %s", prefunc.c_str());
        }
        con.pre_special = &construct::check_nothing;
    }

    std::string postfunc = jo.get_string("post_special", "");
    if (postfunc == "done_tree") {
        con.post_special = &construct::done_tree;
    } else if (postfunc == "done_trunk_log") {
        con.post_special = &construct::done_trunk_log;
    } else if (postfunc == "done_trunk_plank") {
        con.post_special = &construct::done_trunk_plank;
    } else if (postfunc == "done_vehicle") {
        con.post_special = &construct::done_vehicle;
    } else if (postfunc == "done_deconstruct") {
        con.post_special = &construct::done_deconstruct;
    } else if (postfunc == "done_dig_stair") {
        con.post_special = &construct::done_dig_stair;
    } else if (postfunc == "done_mine_downstair") {
        con.post_special = &construct::done_mine_downstair;
    } else if (postfunc == "done_mine_upstair") {
        con.post_special = &construct::done_mine_upstair;
    } else if (postfunc == "done_window_curtains") {
        con.post_special = &construct::done_window_curtains;
    } else {
        if (postfunc != "") {
            debugmsg("Unknown post_special function: %s", postfunc.c_str());
        }
        con.post_special = &construct::done_nothing;
    }

    constructions.push_back(con);
}
Example #21
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;
    }
}
Example #22
0
 std::pair<skill_id, int> get_next( JsonIn &jin ) const {
     JsonObject jo = jin.get_object();
     return std::pair<skill_id, int>( skill_id( jo.get_string( "name" ) ), jo.get_int( "level" ) );
 }
Example #23
0
skill_id Skill::from_legacy_int( const int legacy_id )
{
    static const std::array<skill_id, 28> legacy_skills = { {
            skill_id::NULL_ID(), skill_id( "dodge" ), skill_id( "melee" ), skill_id( "unarmed" ),
            skill_id( "bashing" ), skill_id( "cutting" ), skill_id( "stabbing" ), skill_id( "throw" ),
            skill_id( "gun" ), skill_id( "pistol" ), skill_id( "shotgun" ), skill_id( "smg" ),
            skill_id( "rifle" ), skill_id( "archery" ), skill_id( "launcher" ), skill_id( "mechanics" ),
            skill_id( "electronics" ), skill_id( "cooking" ), skill_id( "tailor" ), skill_id::NULL_ID(),
            skill_id( "firstaid" ), skill_id( "speech" ), skill_id( "barter" ), skill_id( "computer" ),
            skill_id( "survival" ), skill_id( "traps" ), skill_id( "swimming" ), skill_id( "driving" ),
        }
    };
    if( static_cast<size_t>( legacy_id ) < legacy_skills.size() ) {
        return legacy_skills[legacy_id];
    }
    debugmsg( "legacy skill id %d is invalid", legacy_id );
    return skills.front().ident(); // return a non-null id because callers might not expect a null-id
}
Example #24
0
void player_activity::do_turn( player *p )
{
    switch( type ) {
        case ACT_WAIT:
        case ACT_WAIT_NPC:
        case ACT_WAIT_WEATHER:
            // Based on time, not speed
            moves_left -= 100;
            p->rooted();
            p->pause();
            break;
        case ACT_PICKAXE:
            // Based on speed, not time
            if( p->moves <= moves_left ) {
                moves_left -= p->moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left;
                moves_left = 0;
            }
            activity_handlers::pickaxe_do_turn( this, p );
            break;
        case ACT_BURROW:
            // Based on speed, not time
            if( p->moves <= moves_left ) {
                moves_left -= p->moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left;
                moves_left = 0;
            }
            activity_handlers::burrow_do_turn( this, p );
            break;
        case ACT_AIM:
            if( index == 0 ) {
                if( !p->weapon.is_gun() ) {
                    // We lost our gun somehow, bail out.
                    type = ACT_NULL;
                    break;
                }

                g->m.build_map_cache( g->get_levz() );
                g->plfire();
            }
            break;
        case ACT_GAME:
            // Takes care of u.activity.moves_left
            activity_handlers::game_do_turn( this, p );
            break;
        case ACT_VIBE:
            // Takes care of u.activity.moves_left
            activity_handlers::vibe_do_turn( this, p );
            break;
        case ACT_REFILL_VEHICLE:
            type = ACT_NULL; // activity is not used anymore.
            break;
        case ACT_PULP:
            // does not really use u.activity.moves_left, stops itself when finished
            activity_handlers::pulp_do_turn( this, p );
            break;
        case ACT_FISH:
            // Based on time, not speed--or it should be
            // (Being faster doesn't make the fish bite quicker)
            moves_left -= 100;
            p->rooted();
            p->pause();
            break;
        case ACT_DROP:
            activity_on_turn_drop();
            break;
        case ACT_STASH:
            activity_on_turn_stash();
            break;
        case ACT_PICKUP:
            activity_on_turn_pickup();
            break;
        case ACT_MOVE_ITEMS:
            activity_on_turn_move_items();
            break;
        case ACT_ADV_INVENTORY:
            p->cancel_activity();
            advanced_inv();
            break;
        case ACT_ARMOR_LAYERS:
            p->cancel_activity();
            p->sort_armor();
            break;
        case ACT_START_FIRE:
            moves_left -= 100; // based on time
            if( p->i_at(
                    position ).has_flag( "LENS" ) ) { // if using a lens, handle potential changes in weather
                activity_handlers::start_fire_lens_do_turn( this, p );
            }
            p->rooted();
            p->pause();
            break;
        case ACT_OPEN_GATE:
            // Based on speed, not time
            if( p->moves <= moves_left ) {
                moves_left -= p->moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left;
                moves_left = 0;
            }
            break;
        case ACT_FILL_LIQUID:
            activity_handlers::fill_liquid_do_turn( this, p );
            break;
        case ACT_ATM:
            iexamine::atm( *p, p->pos() );
            break;
        case ACT_START_ENGINES:
            moves_left -= 100;
            p->rooted();
            p->pause();
            break;
        case ACT_OXYTORCH:
            if( p->moves <= moves_left ) {
                moves_left -= p->moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left;
                moves_left = 0;
            }
            if( values[0] > 0 ) {
                activity_handlers::oxytorch_do_turn( this, p );
            }
            break;
        case ACT_CRACKING:
            if( !( p->has_amount( "stethoscope", 1 ) || p->has_bionic( "bio_ears" ) ) ) {
                // We lost our cracking tool somehow, bail out.
                type = ACT_NULL;
                break;
            }
            // Based on speed, not time
            if( p->moves <= moves_left ) {
                moves_left -= p->moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left;
                moves_left = 0;
            }
            p->practice( skill_id( "mechanics" ), 1 );
            break;
        case ACT_REPAIR_ITEM: {
            // Based on speed * detail vision
            const int effective_moves = p->moves / p->fine_detail_vision_mod();
            if( effective_moves <= moves_left ) {
                moves_left -= effective_moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left * p->fine_detail_vision_mod();
                moves_left = 0;
            }
        }

        break;

        case ACT_BUTCHER:
            // Drain some stamina
            p->mod_stat( "stamina", -20.0f * p->stamina / p->get_stamina_max() );
            // Based on speed, not time
            if( p->moves <= moves_left ) {
                moves_left -= p->moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left;
                moves_left = 0;
            }
            break;

        default:
            // Based on speed, not time
            if( p->moves <= moves_left ) {
                moves_left -= p->moves;
                p->moves = 0;
            } else {
                p->moves -= moves_left;
                moves_left = 0;
            }
    }

    if( is_complete() ) {
        finish( p );
    }
}
Example #25
0
void player::complete_craft()
{
    const recipe *making = &recipe_dict[ activity.name ]; // Which recipe is it?
    int batch_size = activity.values.front();
    if( making == nullptr ) {
        debugmsg( "no recipe with id %s found", activity.name.c_str() );
        activity.set_to_null();
        return;
    }

    int secondary_dice = 0;
    int secondary_difficulty = 0;
    for( const auto &pr : making->required_skills ) {
        secondary_dice += get_skill_level( pr.first );
        secondary_difficulty += pr.second;
    }

    // # of dice is 75% primary skill, 25% secondary (unless secondary is null)
    int skill_dice;
    if( secondary_difficulty > 0 ) {
        skill_dice = get_skill_level( making->skill_used ) * 3 + secondary_dice;
    } else {
        skill_dice = get_skill_level( making->skill_used ) * 4;
    }

    auto helpers = g->u.get_crafting_helpers();
    for( const npc *np : helpers ) {
        if( np->get_skill_level( making->skill_used ) >=
            get_skill_level( making->skill_used ) ) {
            // NPC assistance is worth half a skill level
            skill_dice += 2;
            add_msg( m_info, _( "%s helps with crafting..." ), np->name.c_str() );
            break;
        }
    }

    // farsightedness can impose a penalty on electronics and tailoring success
    // it's equivalent to a 2-rank electronics penalty, 1-rank tailoring
    if( has_trait( trait_id( "HYPEROPIC" ) ) && !is_wearing( "glasses_reading" ) &&
        !is_wearing( "glasses_bifocal" ) && !has_effect( effect_contacts ) ) {
        int main_rank_penalty = 0;
        if( making->skill_used == skill_id( "electronics" ) ) {
            main_rank_penalty = 2;
        } else if( making->skill_used == skill_id( "tailor" ) ) {
            main_rank_penalty = 1;
        }
        skill_dice -= main_rank_penalty * 4;
    }

    // It's tough to craft with paws.  Fortunately it's just a matter of grip and fine-motor,
    // not inability to see what you're doing
    if( has_trait( trait_PAWS ) || has_trait( trait_PAWS_LARGE ) ) {
        int paws_rank_penalty = 0;
        if( has_trait( trait_PAWS_LARGE ) ) {
            paws_rank_penalty += 1;
        }
        if( making->skill_used == skill_id( "electronics" )
            || making->skill_used == skill_id( "tailor" )
            || making->skill_used == skill_id( "mechanics" ) ) {
            paws_rank_penalty += 1;
        }
        skill_dice -= paws_rank_penalty * 4;
    }

    // Sides on dice is 16 plus your current intelligence
    ///\EFFECT_INT increases crafting success chance
    int skill_sides = 16 + int_cur;

    int diff_dice;
    if( secondary_difficulty > 0 ) {
        diff_dice = making->difficulty * 3 + secondary_difficulty;
    } else {
        // Since skill level is * 4 also
        diff_dice = making->difficulty * 4;
    }

    int diff_sides = 24; // 16 + 8 (default intelligence)

    int skill_roll = dice( skill_dice, skill_sides );
    int diff_roll  = dice( diff_dice,  diff_sides );

    if( making->skill_used ) {
        const double batch_mult = 1 + time_to_craft( *making, batch_size ) / 30000.0;
        //normalize experience gain to crafting time, giving a bonus for longer crafting
        practice( making->skill_used, ( int )( ( making->difficulty * 15 + 10 ) * batch_mult ),
                  ( int )making->difficulty * 1.25 );

        //NPCs assisting or watching should gain experience...
        for( auto &elem : helpers ) {
            //If the NPC can understand what you are doing, they gain more exp
            if( elem->get_skill_level( making->skill_used ) >= making->difficulty ) {
                elem->practice( making->skill_used,
                                ( int )( ( making->difficulty * 15 + 10 ) * batch_mult *
                                         .50 ), ( int )making->difficulty * 1.25 );
                if( batch_size > 1 ) {
                    add_msg( m_info, _( "%s assists with crafting..." ), elem->name.c_str() );
                }
                if( batch_size == 1 ) {
                    add_msg( m_info, _( "%s could assist you with a batch..." ), elem->name.c_str() );
                }
                //NPCs around you understand the skill used better
            } else {
                elem->practice( making->skill_used,
                                ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * .15 ),
                                ( int )making->difficulty * 1.25 );
                add_msg( m_info, _( "%s watches you craft..." ), elem->name.c_str() );
            }
        }

    }

    // Messed up badly; waste some components.
    if( making->difficulty != 0 && diff_roll > skill_roll * ( 1 + 0.1 * rng( 1, 5 ) ) ) {
        add_msg( m_bad, _( "You fail to make the %s, and waste some materials." ),
                 item::nname( making->result ).c_str() );
        if( last_craft->has_cached_selections() ) {
            last_craft->consume_components();
        } else {
            // @todo Guarantee that selections are cached
            const auto &req = making->requirements();
            for( const auto &it : req.get_components() ) {
                consume_items( it, batch_size );
            }
            for( const auto &it : req.get_tools() ) {
                consume_tools( it, batch_size );
            }
        }
        activity.set_to_null();
        return;
        // Messed up slightly; no components wasted.
    } else if( diff_roll > skill_roll ) {
        add_msg( m_neutral, _( "You fail to make the %s, but don't waste any materials." ),
                 item::nname( making->result ).c_str() );
        //this method would only have been called from a place that nulls activity.type,
        //so it appears that it's safe to NOT null that variable here.
        //rationale: this allows certain contexts (e.g. ACT_LONGCRAFT) to distinguish major and minor failures
        return;
    }

    // If we're here, the craft was a success!
    // Use up the components and tools
    std::list<item> used;
    if( !last_craft->has_cached_selections() ) {
        // This should fail and return, but currently crafting_command isn't saved
        // Meaning there are still cases where has_cached_selections will be false
        // @todo Allow saving last_craft and debugmsg+fail craft if selection isn't cached
        if( !has_trait( trait_id( "DEBUG_HS" ) ) ) {
            const auto &req = making->requirements();
            for( const auto &it : req.get_components() ) {
                std::list<item> tmp = consume_items( it, batch_size );
                used.splice( used.end(), tmp );
            }
            for( const auto &it : req.get_tools() ) {
                consume_tools( it, batch_size );
            }
        }
    } else if( !has_trait( trait_id( "DEBUG_HS" ) ) ) {
        used = last_craft->consume_components();
        if( used.empty() ) {
            return;
        }
    }

    // Set up the new item, and assign an inventory letter if available
    std::vector<item> newits = making->create_results( batch_size );
    bool first = true;
    float used_age_tally = 0;
    int used_age_count = 0;
    size_t newit_counter = 0;
    for( item &newit : newits ) {
        // messages, learning of recipe, food spoilage calc only once
        if( first ) {
            first = false;
            if( knows_recipe( making ) ) {
                add_msg( _( "You craft %s from memory." ), newit.type_name( 1 ).c_str() );
            } else {
                add_msg( _( "You craft %s using a book as a reference." ), newit.type_name( 1 ).c_str() );
                // If we made it, but we don't know it,
                // we're making it from a book and have a chance to learn it.
                // Base expected time to learn is 1000*(difficulty^4)/skill/int moves.
                // This means time to learn is greatly decreased with higher skill level,
                // but also keeps going up as difficulty goes up.
                // Worst case is lvl 10, which will typically take
                // 10^4/10 (1,000) minutes, or about 16 hours of crafting it to learn.
                int difficulty = has_recipe( making, crafting_inventory(), helpers );
                ///\EFFECT_INT increases chance to learn recipe when crafting from a book
                if( x_in_y( making->time, ( 1000 * 8 *
                                            ( difficulty * difficulty * difficulty * difficulty ) ) /
                            ( std::max( get_skill_level( making->skill_used ).level(), 1 ) * std::max( get_int(), 1 ) ) ) ) {
                    learn_recipe( ( recipe * )making );
                    add_msg( m_good, _( "You memorized the recipe for %s!" ),
                             newit.type_name( 1 ).c_str() );
                }
            }

            for( auto &elem : used ) {
                if( elem.goes_bad() ) {
                    used_age_tally += elem.get_relative_rot();
                    ++used_age_count;
                }
            }
        }

        // Don't store components for things made by charges,
        // don't store components for things that can't be uncrafted.
        if( recipe_dictionary::get_uncraft( making->result ) && !newit.count_by_charges() ) {
            // Setting this for items counted by charges gives only problems:
            // those items are automatically merged everywhere (map/vehicle/inventory),
            // which would either loose this information or merge it somehow.
            set_components( newit.components, used, batch_size, newit_counter );
            newit_counter++;
        }
        finalize_crafted_item( newit, used_age_tally, used_age_count );
        set_item_inventory( newit );
    }

    if( making->has_byproducts() ) {
        std::vector<item> bps = making->create_byproducts( batch_size );
        for( auto &bp : bps ) {
            finalize_crafted_item( bp, used_age_tally, used_age_count );
            set_item_inventory( bp );
        }
    }

    inv.restack( this );
}