From 62a4a9412bd4573c51ca162d94d839cda6a525c6 Mon Sep 17 00:00:00 2001 From: anothersimulacrum Date: Wed, 8 Apr 2020 17:04:29 -0700 Subject: [PATCH] STS: prevent reading your way to STR, DEX, and PER For all the skills but intelligence, it doesn't make much sense to learn skills by reading. It probably doesn't make some sense for a bit of the intelligence skills, but someone else can easily change that in the future. Because stat bonuses are now based on skill experience, as opposed to number of skills, Stats Through Skills now emphasizes fewer, higher skills, as opposed to more, lower skills. The new formula: cbrt( 0.9 * experience - 0.5 ) Roughly matches the old formula sqrt( skills - 3 ) ^ 0.4 in the amount of experience required to get each stat bonus. It does, however gain slightly faster than the old one. Because I basically reinvented Stats Through Skills, I put my name in authors. --- data/mods/StatsThroughSkills/modinfo.json | 38 +++++++++++++++-------- src/character.cpp | 30 +++++++++++++----- src/skill.cpp | 19 ++++++++++++ src/skill.h | 5 ++- src/skill_boost.cpp | 21 +++++++++---- src/skill_boost.h | 12 ++++--- 6 files changed, 93 insertions(+), 32 deletions(-) diff --git a/data/mods/StatsThroughSkills/modinfo.json b/data/mods/StatsThroughSkills/modinfo.json index 9bc42859de70b..5fb7dfd3258b3 100644 --- a/data/mods/StatsThroughSkills/modinfo.json +++ b/data/mods/StatsThroughSkills/modinfo.json @@ -3,7 +3,7 @@ "type": "MOD_INFO", "ident": "StatsThroughSkills", "name": "Stats Through Skills", - "authors": [ "Ryan \"DeNarr\" Saige", "Kevin Granade" ], + "authors": [ "Ryan \"DeNarr\" Saige", "Kevin Granade", "anothersimulacrum" ], "description": "Allows stats to raise via skill progression.", "category": "rebalance", "dependencies": [ "dda" ] @@ -11,29 +11,41 @@ { "type": "skill_boost", "stat": "str", - "skills": [ "mechanics", "swimming", "bashing", "cutting", "melee", "throw" ], - "skill_offset": -3, - "scaling_power": 0.4 + "skills_practice": [ "mechanics", "swimming", "bashing", "cutting", "melee", "throw" ], + "skills_knowledge": [ ], + "skill_offset": -0.5, + "scaling_power": 0.33, + "coefficient": 0.9, + "max_stat": 5 }, { "type": "skill_boost", "stat": "dex", - "skills": [ "driving", "survival", "tailor", "traps", "dodge", "stabbing", "unarmed" ], - "skill_offset": -3, - "scaling_power": 0.4 + "skills_practice": [ "driving", "survival", "tailor", "traps", "dodge", "stabbing", "unarmed" ], + "skills_knowledge": [ ], + "skill_offset": -0.5, + "scaling_power": 0.33, + "coefficient": 0.9, + "max_stat": 5 }, { "type": "skill_boost", "stat": "int", - "skills": [ "barter", "computer", "cooking", "electronics", "fabrication", "firstaid", "speech" ], - "skill_offset": -3, - "scaling_power": 0.4 + "skills_practice": [ "barter", "computer", "cooking", "electronics", "fabrication", "firstaid", "speech" ], + "skills_knowledge": [ "barter", "computer", "cooking", "electronics", "fabrication", "firstaid", "speech" ], + "skill_offset": -0.5, + "scaling_power": 0.33, + "coefficient": 0.9, + "max_stat": 5 }, { "type": "skill_boost", "stat": "per", - "skills": [ "archery", "gun", "launcher", "pistol", "rifle", "shotgun", "smg" ], - "skill_offset": -3, - "scaling_power": 0.4 + "skills_practice": [ "archery", "gun", "launcher", "pistol", "rifle", "shotgun", "smg" ], + "skills_knowledge": [ ], + "skill_offset": -0.5, + "scaling_power": 0.33, + "coefficient": 0.9, + "max_stat": 5 } ] diff --git a/src/character.cpp b/src/character.cpp index 9bcc8f0646169..901947b53241b 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -1544,11 +1544,18 @@ void Character::recalc_hp() int str_boost_val = 0; cata::optional str_boost = skill_boost::get( "str" ); if( str_boost ) { - int skill_total = 0; - for( const std::string &skill_str : str_boost->skills() ) { - skill_total += get_skill_level( skill_id( skill_str ) ); + int exercise_total = 0; + for( const std::string &skill_str : str_boost->skills_practice() ) { + const SkillLevel &lvl = get_skill_level_object( skill_id( skill_str ) ); + exercise_total += lvl.total_exercise( PRACTICE ); } - str_boost_val = str_boost->calc_bonus( skill_total ); + + for( const std::string &skill_str : str_boost->skills_knowledge() ) { + const SkillLevel &lvl = get_skill_level_object( skill_id( skill_str ) ); + exercise_total += lvl.total_exercise( KNOWLEDGE ); + } + + str_boost_val = str_boost->calc_bonus( std::floor( exercise_total / 1000 ) ); } // Mutated toughness stacks with starting, by design. float hp_mod = 1.0f + mutation_value( "hp_modifier" ) + mutation_value( "hp_modifier_secondary" ); @@ -3210,11 +3217,18 @@ void Character::apply_skill_boost() remove_value( bonus_name ); } // End migration code - int skill_total = 0; - for( const std::string &skill_str : boost.skills() ) { - skill_total += get_skill_level( skill_id( skill_str ) ); + int experience_total = 0; + for( const std::string &skill_str : boost.skills_practice() ) { + const SkillLevel &lvl = get_skill_level_object( skill_id( skill_str ) ); + experience_total += lvl.total_exercise( PRACTICE ); } - mod_stat( boost.stat(), boost.calc_bonus( skill_total ) ); + + for( const std::string &skill_str : boost.skills_knowledge() ) { + const SkillLevel &lvl = get_skill_level_object( skill_id( skill_str ) ); + experience_total += lvl.total_exercise( KNOWLEDGE ); + } + + mod_stat( boost.stat(), boost.calc_bonus( std::floor( experience_total / 1000 ) ) ); if( boost.stat() == "str" ) { recalc_hp(); } diff --git a/src/skill.cpp b/src/skill.cpp index 7c20e911248d3..c4ebbf4497489 100644 --- a/src/skill.cpp +++ b/src/skill.cpp @@ -245,6 +245,24 @@ void SkillLevel::train( int amount, skill_exercise_type type, bool skip_scaling } } +int SkillLevel::total_exercise( skill_exercise_type type ) const +{ + int all_exercise = 0; + for( int i = 1; i <= _level; ++i ) { + all_exercise += static_cast( std::pow( i, 2 ) * 100 ); + } + + switch( type ) { + case PRACTICE: + return _practice + static_cast( _practice_ratio * all_exercise ); + case KNOWLEDGE: + return _knowledge + static_cast( ( 1 - _practice_ratio ) * all_exercise ); + case NUM_SKILL_EXERCISE_TYPE: + return _practice + _knowledge + all_exercise; + } + return 0; +} + namespace { time_duration rustRate( int level ) @@ -454,3 +472,4 @@ double price_adjustment( int barter_skill ) return 1.0; } } + diff --git a/src/skill.h b/src/skill.h index 53e83482c61d4..ab051f604d871 100644 --- a/src/skill.h +++ b/src/skill.h @@ -23,7 +23,8 @@ template class string_id; enum skill_exercise_type : int { PRACTICE, - KNOWLEDGE + KNOWLEDGE, + NUM_SKILL_EXERCISE_TYPE }; struct time_info_t { @@ -189,6 +190,8 @@ class SkillLevel void readBook( int minimumGain, int maximumGain, int maximumLevel = -1 ); + int total_exercise( skill_exercise_type type = NUM_SKILL_EXERCISE_TYPE ) const; + bool operator==( const SkillLevel &b ) const { return _level == b._level && _knowledge == b._knowledge && _practice == b._practice; } diff --git a/src/skill_boost.cpp b/src/skill_boost.cpp index 2f9b2457d243d..27ec12a3b338c 100644 --- a/src/skill_boost.cpp +++ b/src/skill_boost.cpp @@ -34,9 +34,12 @@ void skill_boost::load_boost( const JsonObject &jo, const std::string &src ) void skill_boost::load( const JsonObject &jo, const std::string & ) { - mandatory( jo, was_loaded, "skills", _skills ); + mandatory( jo, was_loaded, "skills_practice", _skills_practice ); + mandatory( jo, was_loaded, "skills_knowledge", _skills_knowledge ); + mandatory( jo, was_loaded, "coefficient", _coefficient ); mandatory( jo, was_loaded, "skill_offset", _offset ); mandatory( jo, was_loaded, "scaling_power", _power ); + mandatory( jo, was_loaded, "max_stat", _max_stat ); } void skill_boost::reset() @@ -49,15 +52,21 @@ std::string skill_boost::stat() const return id.str(); } -const std::vector &skill_boost::skills() const +const std::vector &skill_boost::skills_practice() const { - return _skills; + return _skills_practice; } -float skill_boost::calc_bonus( int skill_total ) const +const std::vector &skill_boost::skills_knowledge() const { - if( skill_total + _offset <= 0 ) { + return _skills_knowledge; +} + +float skill_boost::calc_bonus( int exp_total ) const +{ + if( ( _coefficient * exp_total ) + _offset <= 0 ) { return 0.0; } - return std::max( 0.0, std::floor( std::pow( skill_total + _offset, _power ) ) ); + return clamp( std::floor( std::pow( ( _coefficient * exp_total ) + _offset, _power ) ), 0.0f, + _max_stat ); } diff --git a/src/skill_boost.h b/src/skill_boost.h index 471006c9626fb..1f7812ee52a05 100644 --- a/src/skill_boost.h +++ b/src/skill_boost.h @@ -19,8 +19,9 @@ class skill_boost skill_boost() = default; std::string stat() const; - const std::vector &skills() const; - float calc_bonus( int skill_total ) const; + const std::vector &skills_practice() const; + const std::vector &skills_knowledge() const; + float calc_bonus( int exp_total ) const; static void load_boost( const JsonObject &jo, const std::string &src ); static void reset(); @@ -32,9 +33,12 @@ class skill_boost friend class generic_factory; string_id id; bool was_loaded = false; - std::vector _skills; - int _offset = 0; + std::vector _skills_practice; + std::vector _skills_knowledge; + float _coefficient = 0.0f; + float _offset = 0.0f; float _power = 0.0f; + float _max_stat = 0.0f; void load( const JsonObject &jo, const std::string &src ); };