Skip to content

Commit

Permalink
Tweaking skill rust to fix learning overall (CleverRaven#50176)
Browse files Browse the repository at this point in the history
* better learning!
phase 1: Track highest xp ever reached, recover rust faster if you are below it, and also gain xp while you regain rust.
increment highestlevel if your highestexperience gets there.
* books only train theory
* rename "highest" to "knowledge" save confusion in long run
* recipe requirements can be theory based
* Books increase theory
and your ability to read a book is based on your theory level
* rust rate drops as you accumulate rust
* make book larnin' show theory experience
Run rust once per day, not at random times. Change the amount of xp lost instead of the frequency you do rust. It's a lot easier to calculate.
* book theory gain falls off as level gap increases
* NPC skill affects their teaching

Co-authored-by: actual-nh <[email protected]>
Co-authored-by: Kevin Granade <[email protected]>
  • Loading branch information
3 people authored Aug 12, 2021
1 parent 216401f commit 4814255
Show file tree
Hide file tree
Showing 22 changed files with 358 additions and 142 deletions.
39 changes: 21 additions & 18 deletions src/activity_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,7 @@ void read_activity_actor::read_book( Character &learner,
const cata::value_ptr<islot_book> &islotbook,
SkillLevel &skill_level, double penalty )
{
const int originalSkillLevel = skill_level.level();
const int originalSkillLevel = skill_level.knowledgeLevel();

// Calculate experience gained
/** @EFFECT_INT increases reading comprehension */
Expand Down Expand Up @@ -1263,25 +1263,27 @@ bool read_activity_actor::player_read( avatar &you )
}

if( skill &&
learner->get_skill_level( skill ) < islotbook->level &&
learner->get_knowledge_level( skill ) < islotbook->level &&
learner->get_skill_level_object( skill ).can_train() ) {

SkillLevel &skill_level = learner->get_skill_level_object( skill );
std::string skill_name = skill.obj().name();
const int originalSkillLevel = skill_level.level();
const int originalSkillLevel = skill_level.knowledgeLevel();

read_book( *learner, islotbook, skill_level, penalty );
const int newSkillLevel = skill_level.knowledgeLevel();


// levels up the skill
if( skill_level != originalSkillLevel ) {
if( newSkillLevel != originalSkillLevel ) {
get_event_bus().send<event_type::gains_skill_level>(
learner->getID(), skill, skill_level.level() );
learner->getID(), skill, skill_level.knowledgeLevel() );

if( learner->is_avatar() ) {
add_msg( m_good, _( "You increase %s to level %d." ), skill.obj().name(),
add_msg( m_good, _( "You theoretical knowledge of %s increases to level %d." ), skill.obj().name(),
originalSkillLevel + 1 );
} else {
add_msg( m_good, _( "%s increases their %s level." ), learner->disp_name(),
add_msg( m_good, _( "%s increases their %s knowledge." ), learner->disp_name(),
skill.obj().name() );
}

Expand All @@ -1292,7 +1294,7 @@ bool read_activity_actor::player_read( avatar &you )
} else {
if( learner->is_avatar() ) {
add_msg( m_info, _( "You learn a little about %s! (%d%%)" ), skill_name,
skill_level.exercise() );
skill_level.knowledgeExperience() );
} else {
little_learned.push_back( learner->disp_name() );
}
Expand Down Expand Up @@ -1355,7 +1357,7 @@ bool read_activity_actor::player_readma( avatar &you )
skill_id skill_used = style_to_learn->primary_skill;

int difficulty = std::max( 1, style_to_learn->learn_difficulty );
difficulty = std::max( 1, 20 + difficulty * 2 - you.get_skill_level( skill_used ) * 2 );
difficulty = std::max( 1, 20 + difficulty * 2 - you.get_knowledge_level( skill_used ) * 2 );
add_msg_debug( debugmode::DF_ACT_READ, "Chance to learn one in: %d", difficulty );

if( one_in( difficulty ) ) {
Expand Down Expand Up @@ -1409,18 +1411,19 @@ bool read_activity_actor::npc_read( npc &learner )
book->mark_chapter_as_read( learner );

if( skill &&
learner.get_skill_level( skill ) < islotbook->level &&
learner.get_knowledge_level( skill ) < islotbook->level &&
learner.get_skill_level_object( skill ).can_train() ) {

SkillLevel &skill_level = learner.get_skill_level_object( skill );
std::string skill_name = skill.obj().name();
const int originalSkillLevel = skill_level.level();
const int originalSkillLevel = skill_level.knowledgeLevel();

read_book( learner, islotbook, skill_level, 1.0 );

if( skill_level != originalSkillLevel ) {
const int newSkillLevel = skill_level.knowledgeLevel();
if( newSkillLevel != originalSkillLevel ) {
get_event_bus().send<event_type::gains_skill_level>(
learner.getID(), skill, skill_level.level() );
learner.getID(), skill, skill_level.knowledgeLevel() );

if( display_messages ) {
add_msg( m_good, _( "%s increases their %s level." ), learner.disp_name(),
Expand Down Expand Up @@ -1544,16 +1547,16 @@ std::string read_activity_actor::get_progress_message( const player_activity & )
const skill_id &skill = islotbook->skill;

if( skill &&
you.get_skill_level( skill ) < islotbook->level &&
you.get_knowledge_level( skill ) < islotbook->level &&
you.get_skill_level_object( skill ).can_train() &&
you.has_identified( book->typeId() ) ) {
const SkillLevel &skill_level = you.get_skill_level_object( skill );
//~ skill_name current_skill_level -> next_skill_level (% to next level)
return string_format( pgettext( "reading progress", "%1$s %2$d -> %3$d (%4$d%%)" ),
skill.obj().name(),
skill_level.level(),
skill_level.level() + 1,
skill_level.exercise() );
skill_level.knowledgeLevel(),
skill_level.knowledgeLevel() + 1,
skill_level.knowledgeExperience() );
}

return std::string();
Expand Down Expand Up @@ -3484,7 +3487,7 @@ void disable_activity_actor::finish( player_activity &act, Character &/*who*/ )

bool disable_activity_actor::can_disable_or_reprogram( const monster &monster )
{
if( get_avatar().get_skill_level( skill_electronics ) + get_avatar().get_skill_level(
if( get_avatar().get_knowledge_level( skill_electronics ) + get_avatar().get_knowledge_level(
skill_mechanics ) <= 0 ) {
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2000,9 +2000,9 @@ void activity_handlers::train_finish( player_activity *act, player *p )
if( sk.is_valid() ) {
const Skill &skill = sk.obj();
std::string skill_name = skill.name();
int old_skill_level = p->get_skill_level( sk );
int old_skill_level = p->get_knowledge_level( sk );
p->practice( sk, 100, old_skill_level + 2 );
int new_skill_level = p->get_skill_level( sk );
int new_skill_level = p->get_knowledge_level( sk );
if( old_skill_level != new_skill_level ) {
add_msg( m_good, _( "You finish training %s to level %d." ),
skill_name, new_skill_level );
Expand Down
8 changes: 4 additions & 4 deletions src/avatar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ bool avatar::read( item_location &book, item_location ereader )

auto get_text =
[&]( const std::map<npc *, std::string> &m, const std::pair<npc *, std::string> &elem ) {
const int lvl = elem.first->get_skill_level( skill );
const int lvl = elem.first->get_knowledge_level( skill );
const std::string lvl_text = skill ? string_format( _( " | current level: %d" ), lvl ) : "";
const std::string name_text = elem.first->disp_name() + elem.second;
return string_format( "%s%s", left_justify( name_text, max_length( m ) ), lvl_text );
Expand All @@ -407,7 +407,7 @@ bool avatar::read( item_location &book, item_location ereader )

menu.addentry( 0, true, '0', _( "Read once" ) );

const int lvl = get_skill_level( skill );
const int lvl = get_knowledge_level( skill );
menu.addentry( 2 + getID().get_value(), lvl < type->level, '1',
string_format( _( "Read until you gain a level | current level: %d" ), lvl ) );

Expand Down Expand Up @@ -611,10 +611,10 @@ void avatar::identify( const item &item )

add_msg( _( "You skim %s to find out what's in it." ), book.type_name() );
if( skill && get_skill_level_object( skill ).can_train() ) {
add_msg( m_info, _( "Can bring your %s skill to %d." ),
add_msg( m_info, _( "Can bring your %s knowledge to %d." ),
skill.obj().name(), reading->level );
if( reading->req != 0 ) {
add_msg( m_info, _( "Requires %s level %d to understand." ),
add_msg( m_info, _( "Requires %s knoweldge level %d to understand." ),
skill.obj().name(), reading->req );
}
}
Expand Down
86 changes: 59 additions & 27 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2409,6 +2409,13 @@ void Character::practice( const skill_id &id, int amount, int cap, bool suppress
return;
}

// Your ability to "catch up" skill experience to knowledge is mostly a function of intelligence,
// but perception also plays a role, representing both memory/attentiveness and catching on to how
// the two apply to each other.
float catchup_modifier = 1.0f + ( 2.0f * get_int() + get_per() ) / 24.0f; // 2 for an average person
float knowledge_modifier = 1.0f + get_int() /
40.0f; // 1.2 for an average person, always a bit higher than base amount

const auto highest_skill = [&]() {
std::pair<skill_id, int> result( skill_id::NULL_ID(), -1 );
for( const auto &pair : *_skills ) {
Expand All @@ -2426,25 +2433,21 @@ void Character::practice( const skill_id &id, int amount, int cap, bool suppress
amount = adjust_for_focus( amount );

if( has_trait( trait_PACIFIST ) && skill.is_combat_skill() ) {
if( !one_in( 3 ) ) {
amount = 0;
}
amount /= 3.0f;
}
if( has_trait_flag( json_flag_PRED2 ) && skill.is_combat_skill() ) {
if( one_in( 3 ) ) {
amount *= 2;
}
catchup_modifier *= 2.0f;
}
if( has_trait_flag( json_flag_PRED3 ) && skill.is_combat_skill() ) {
amount *= 2;
catchup_modifier *= 2.0f;
}

if( has_trait_flag( json_flag_PRED4 ) && skill.is_combat_skill() ) {
amount *= 3;
catchup_modifier *= 3.0f;
}

if( isSavant && id != savantSkill ) {
amount /= 2;
amount *= 0.5f;
}

if( amount > 0 && get_skill_level( id ) > cap ) { //blunt grinding cap implementation for crafting
Expand All @@ -2454,17 +2457,24 @@ void Character::practice( const skill_id &id, int amount, int cap, bool suppress
}
}
if( amount > 0 && level.isTraining() ) {
int oldLevel = get_skill_level( id );
get_skill_level_object( id ).train( amount );
int newLevel = get_skill_level( id );
int old_practical_level = get_skill_level( id );
int old_theoretical_level = get_knowledge_level( id );
get_skill_level_object( id ).train( amount, catchup_modifier, knowledge_modifier );
int new_practical_level = get_skill_level( id );
int new_theoretical_level = get_knowledge_level( id );
std::string skill_name = skill.name();
if( newLevel > oldLevel ) {
get_event_bus().send<event_type::gains_skill_level>( getID(), id, newLevel );
if( new_practical_level > old_practical_level ) {
get_event_bus().send<event_type::gains_skill_level>( getID(), id, new_practical_level );
}
if( is_avatar() && newLevel > oldLevel ) {
add_msg( m_good, _( "Your skill in %s has increased to %d!" ), skill_name, newLevel );
if( is_avatar() && new_practical_level > old_practical_level ) {
add_msg( m_good, _( "Your practical skill in %s has increased to %d!" ), skill_name,
new_practical_level );
}
if( is_avatar() && newLevel > cap ) {
if( is_avatar() && new_theoretical_level > old_theoretical_level ) {
add_msg( m_good, _( "Your theoretical understanding of %s has increased to %d!" ), skill_name,
new_theoretical_level );
}
if( is_avatar() && new_practical_level > cap ) {
//inform player immediately that the current recipe can't be used to train further
add_msg( m_info, _( "You feel that %s tasks of this level are becoming trivial." ),
skill_name );
Expand Down Expand Up @@ -4240,6 +4250,15 @@ int Character::get_skill_level( const skill_id &ident, const item &context ) con
return _skills->get_skill_level( ident, context );
}

int Character::get_knowledge_level( const skill_id &ident ) const
{
return _skills->get_knowledge_level( ident );
}

int Character::get_knowledge_level( const skill_id &ident, const item &context ) const
{
return _skills->get_knowledge_level( ident, context );
}
void Character::set_skill_level( const skill_id &ident, const int level )
{
get_skill_level_object( ident ).level( level );
Expand All @@ -4249,6 +4268,16 @@ void Character::mod_skill_level( const skill_id &ident, const int delta )
{
_skills->mod_skill_level( ident, delta );
}
void Character::set_knowledge_level( const skill_id &ident, const int level )
{
get_skill_level_object( ident ).knowledgeLevel( level );
}

void Character::mod_knowledge_level( const skill_id &ident, const int delta )
{
_skills->mod_skill_level( ident, delta );
}


std::string Character::enumerate_unmet_requirements( const item &it, const item &context ) const
{
Expand Down Expand Up @@ -4326,7 +4355,7 @@ bool Character::meets_skill_requirements( const construction &con ) const
{
return std::all_of( con.required_skills.begin(), con.required_skills.end(),
[&]( const std::pair<skill_id, int> &pr ) {
return get_skill_level( pr.first ) >= pr.second;
return get_knowledge_level( pr.first ) >= pr.second;
} );
}

Expand Down Expand Up @@ -4408,7 +4437,6 @@ void Character::apply_skill_boost()

void Character::do_skill_rust()
{
const int rust_rate_tmp = rust_rate();
for( std::pair<const skill_id, SkillLevel> &pair : *_skills ) {
const Skill &aSkill = *pair.first;
SkillLevel &skill_level_obj = pair.second;
Expand Down Expand Up @@ -4436,14 +4464,16 @@ void Character::do_skill_rust()

const int rust_resist = enchantment_cache->modify_value( enchant_vals::mod::READING_EXP, 0 );
const int oldSkillLevel = skill_level_obj.level();
if( skill_level_obj.rust( rust_resist, rust_rate_tmp ) ) {
if( skill_level_obj.rust( rust_resist ) ) {
add_msg_if_player( m_warning,
_( "Your knowledge of %s begins to fade, but your memory banks retain it!" ), aSkill.name() );
mod_power_level( -bio_memory->power_trigger );
}
const int newSkill = skill_level_obj.level();
if( newSkill < oldSkillLevel ) {
add_msg_if_player( m_bad, _( "Your skill in %s has reduced to %d!" ), aSkill.name(), newSkill );
add_msg_if_player( m_bad,
_( "Your practical skill in %s may need some refreshing. It has dropped to %d." ), aSkill.name(),
newSkill );
}
}
}
Expand Down Expand Up @@ -5770,7 +5800,9 @@ void Character::update_body( const time_point &from, const time_point &to )
as_avatar()->advance_daily_calories();
}

do_skill_rust();
if( calendar::once_every( 24_hours ) ) {
do_skill_rust();
}
}

item *Character::best_quality_item( const quality_id &qual )
Expand Down Expand Up @@ -13352,7 +13384,7 @@ book_mastery Character::get_book_mastery( const item &book ) const
return book_mastery::MASTERED;
}

const int skill_level = get_skill_level( skill );
const int skill_level = get_knowledge_level( skill );
const int skill_requirement = type->req;
const int max_skill_learnable = type->level;

Expand Down Expand Up @@ -13566,9 +13598,9 @@ const Character *Character::get_book_reader( const item &book,
}
if( get_book_mastery( book ) == book_mastery::CANT_UNDERSTAND ) {
reasons.push_back( is_avatar() ? string_format( _( "%s %d needed to understand. You have %d" ),
book_skill->name(), book_skill_requirement, get_skill_level( book_skill ) ) :
book_skill->name(), book_skill_requirement, get_knowledge_level( book_skill ) ) :
string_format( _( "%s %d needed to understand. %s has %d" ), book_skill->name(),
book_skill_requirement, disp_name(), get_skill_level( book_skill ) ) );
book_skill_requirement, disp_name(), get_knowledge_level( book_skill ) ) );
return nullptr;
}

Expand Down Expand Up @@ -13613,7 +13645,7 @@ const Character *Character::get_book_reader( const item &book,
} else if( elem->get_book_mastery( book ) == book_mastery::CANT_UNDERSTAND ) {
reasons.push_back( string_format( _( "%s %d needed to understand. %s has %d" ),
book_skill->name(), book_skill_requirement, elem->disp_name(),
elem->get_skill_level( book_skill ) ) );
elem->get_knowledge_level( book_skill ) ) );
} else if( elem->has_trait( trait_HYPEROPIC ) &&
!elem->worn_with_flag( STATIC( flag_id( "FIX_FARSIGHT" ) ) ) &&
!elem->has_effect( effect_contacts ) ) {
Expand Down Expand Up @@ -13652,7 +13684,7 @@ int Character::time_to_read( const item &book, const Character &reader,
// The reader's reading speed has an effect only if they're trying to understand the book as they read it
// Reading speed is assumed to be how well you learn from books (as opposed to hands-on experience)
const bool try_understand = reader.fun_to_read( book ) ||
reader.get_skill_level( skill ) < type->level;
reader.get_knowledge_level( skill ) < type->level;
int reading_speed = try_understand ? std::max( reader.read_speed(), read_speed() ) : read_speed();
if( learner ) {
reading_speed = std::max( reading_speed, learner->read_speed() );
Expand Down
4 changes: 4 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -1825,13 +1825,17 @@ class Character : public Creature, public visitable
// --------------- Skill Stuff ---------------
int get_skill_level( const skill_id &ident ) const;
int get_skill_level( const skill_id &ident, const item &context ) const;
int get_knowledge_level( const skill_id &ident ) const;
int get_knowledge_level( const skill_id &ident, const item &context ) const;

const SkillLevelMap &get_all_skills() const;
SkillLevel &get_skill_level_object( const skill_id &ident );
const SkillLevel &get_skill_level_object( const skill_id &ident ) const;

void set_skill_level( const skill_id &ident, int level );
void mod_skill_level( const skill_id &ident, int delta );
void set_knowledge_level( const skill_id &ident, int level );
void mod_knowledge_level( const skill_id &ident, int delta );
/** Checks whether the character's skills meet the required */
bool meets_skill_requirements( const std::map<skill_id, int> &req,
const item &context = item() ) const;
Expand Down
4 changes: 2 additions & 2 deletions src/character_crafting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ recipe_subset Character::get_available_recipes( const inventory &crafting_inv,
res.include( get_recipes_from_books( *np->inv ) );
// Being told what to do
res.include_if( np->get_learned_recipes(), [ this ]( const recipe & r ) {
return get_skill_level( r.skill_used ) >= static_cast<int>( r.difficulty *
return get_knowledge_level( r.skill_used ) >= static_cast<int>( r.difficulty *
0.8f ); // Skilled enough to understand
} );
}
Expand All @@ -153,7 +153,7 @@ std::set<itype_id> Character::get_books_for_recipe( const inventory &crafting_in
const recipe *r ) const
{
std::set<itype_id> book_ids;
const int skill_level = get_skill_level( r->skill_used );
const int skill_level = get_knowledge_level( r->skill_used );
for( const auto &book_lvl : r->booksets ) {
itype_id book_id = book_lvl.first;
int required_skill_level = book_lvl.second.skill_req;
Expand Down
Loading

0 comments on commit 4814255

Please sign in to comment.