How is experience for Combat Skills calculated?

This has always eluded me. How does the game determine how much experience to give for Combat Skills when fighting enemies? When is this experience rewarded? Is it when an enemy is attacked? Killed?

I’m playing a character with both Apex Predator and Faster Learner, and I’m trying to get my unarmed as high as possible. So I’m trying to figure out how to do that best, because unlike my Submachine Gun skill, many enemies say they “can’t increase my Unarmed Level past 12” despite the fact I’m still gaining EXP for the skill.

Can anyone clue me into this mystery?

XP calculations are done in game.cpp and melee.cpp.

There are following formulas:

  • 25% chance to get 1 xp/75% chance to get 0 xp for skill_melee per successful smash (via void game::smash())
        if (u.get_skill_level( skill_melee ) == 0) {
            u.practice( skill_melee, rng(0, 1) * rng(0, 1));
        }
  • Practice melee skill_melee, skill_bashing, skill_cutting, skill_stabbing and skill_unarmed proportional to damage dealt (via static void melee_train( player &p, int lo, int hi, const item &weap ))
static void melee_train( player &p, int lo, int hi, const item &weap ) {
    p.practice( skill_melee, ceil( rng( lo, hi ) / 2.0 ), hi );

    // allocate XP proportional to damage stats
    // Pure unarmed needs a special case because it has 0 weapon damage
    int cut  = weap.damage_melee( DT_CUT );
    int stab = weap.damage_melee( DT_STAB );
    int bash = weap.damage_melee( DT_BASH ) + ( weap.is_null() ? 1 : 0 );

    float total = std::max( cut + stab + bash, 1 );
    p.practice( skill_cutting,  ceil( cut  / total * rng( lo, hi ) ), hi );
    p.practice( skill_stabbing, ceil( stab / total * rng( lo, hi ) ), hi );

    // Unarmed skill scaled bashing damage and so scales with bashing damage
    p.practice( weap.has_flag( "UNARMED_WEAPON" ) ? skill_unarmed : skill_bashing,
                ceil( bash / total * rng( lo, hi ) ), hi );
}
  • When CQB bionic is not active train skill_melee (via void player::melee_attack(Creature &t, bool allow_special, const matec_id &force_technique, int hit_spread))

When hit_spread is less than 0:

        // Practice melee and relevant weapon skill (if any) except when using CQB bionic
        if( !has_active_bionic( bio_cqb ) ) {
            melee_train( *this, 2, 5, cur_weapon );
        }

When hit_spread is more than or equal to 0:

            // Practice melee and relevant weapon skill (if any) except when using CQB bionic
            if( !has_active_bionic( bio_cqb ) ) {
                melee_train( *this, 5, 10, cur_weapon );
            }
  • Gain 0 to 10 xp with equal chance with skill_unarmed once per combo (via void player::perform_special_attacks(Creature &t))
            if( !practiced ) {
                // Practice unarmed, at most once per combo
                practiced = true;
                practice( skill_unarmed, rng( 0, 10 ) );
            }

okay so why do so many enemies generate a “can’t gain XP” message?

I dunno as I’ve never seen such message.

What’s the exact text?

“this task is too simple to gain any experience” or there abouts. think it usually happens soonest with something like dodging a zombie child.

dodging is not a combat skill in game definitions