Skip to content

Commit

Permalink
Merge pull request #74013 from RenechCDDA/NPC_proficiencies
Browse files Browse the repository at this point in the history
Random NPCs have the same starting skills and proficiencies as the average new character
  • Loading branch information
dseguin authored May 24, 2024
2 parents e279993 + 2025433 commit 92260da
Show file tree
Hide file tree
Showing 12 changed files with 95 additions and 23 deletions.
1 change: 1 addition & 0 deletions data/json/profession_groups.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
"type": "profession_group",
"id": "adult_basic_background",
"//": "Default selection when creating new characters. Also applied to randomized NPCs. Hardcoded reference in Character::add_default_background()",
"professions": [
"driving_license",
"simple_home_cooking",
Expand Down
25 changes: 25 additions & 0 deletions data/mods/Aftershock/profession_groups.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"type": "profession_group",
"id": "adult_basic_background",
"professions": [
"driving_license",
"simple_home_cooking",
"computer_literate",
"social_skills",
"high_school_graduate",
"mundane_survival",
"bionic_interface"
]
},
{
"type": "profession",
"subtype": "hobby",
"id": "bionic_interface",
"name": "Standard Bionic Interface",
"//": "Universal data link is just fluff, to explain why you have the other bits. Does not exist ingame.",
"description": "Like most people on Earth you have previously received cybernetic enhancement to make life easier. Your enhancements include a universal data link, some basic power storage, and a charging cable to top up.",
"CBMs": [ "bio_power_storage_mkII", "bio_cable" ],
"points": 0
}
]
25 changes: 25 additions & 0 deletions data/mods/aftershock_exoplanet/profession_groups.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"type": "profession_group",
"id": "adult_basic_background",
"professions": [
"driving_license",
"simple_home_cooking",
"computer_literate",
"social_skills",
"high_school_graduate",
"mundane_survival",
"bionic_interface"
]
},
{
"type": "profession",
"subtype": "hobby",
"id": "bionic_interface",
"name": "Standard Bionic Interface",
"//": "Universal data link is just fluff, to explain why you have the other bits. Does not exist ingame.",
"description": "Like most people on Salus IV you have previously received cybernetic enhancement to make life easier. Your enhancements include a universal data link, some basic power storage, and a charging cable to top up.",
"CBMs": [ "bio_power_storage_mkII", "bio_cable" ],
"points": 0
}
]
3 changes: 2 additions & 1 deletion doc/NPCs.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Format:
"id": "NC_EXAMPLE", // Mandatory, unique id that refers to this class.
"name": { "str": "Example NPC" }, // Mandatory, display name for this class.
"job_description": "I'm helping you learn the game.", // Mandatory
"common": false, // Optional. Whether or not this class can appear via random generation.
"common": false, // Optional, defaults true. Whether or not this class can appear via random generation. Randomly generated NPCs will have skills, proficiencies, and bionics applied to them as a default new player character would.
"sells_belongings": false, // Optional. See [Shopkeeper NPC configuration](#shopkeeper-npc-configuration)
"bonus_str": { "rng": [ -4, 0 ] }, // Optional. Modifies stat by the given value. This example shows a random distribution between -4 and 0.
"bonus_dex": 100, // Optional. This example always adds exactly 100 to the stat.
Expand Down Expand Up @@ -72,6 +72,7 @@ Format:
],
"shopkeeper_blacklist": "test_blacklist",
"restock_interval": "6 days",
"proficiencies": [ "prof_gunsmithing_basic", "prof_spotting" ], // Optional. Note that prereqs do not need to be defined. NPCs of this class will learn this proficiency *and all pre-requesite proficiencies*.
"traits": [ { "group": "BG_survival_story_EVACUEE" }, { "group": "NPC_starting_traits" }, { "group": "Appearance_demographics" } ] // Optional
}
```
Expand Down
5 changes: 3 additions & 2 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -2446,7 +2446,7 @@ class Character : public Creature, public visitable
/** Returns a value used when attempting to intimidate NPC's */
int intimidation() const;

void set_skills_from_hobbies();
void set_skills_from_hobbies( bool no_override = false );

void set_bionics_from_hobbies();

Expand All @@ -2455,7 +2455,8 @@ class Character : public Creature, public visitable
float get_proficiency_practice( const proficiency_id &prof ) const;
time_duration get_proficiency_practiced_time( const proficiency_id &prof ) const;
bool has_prof_prereqs( const proficiency_id &prof ) const;
void add_proficiency( const proficiency_id &prof, bool ignore_requirements = false );
void add_proficiency( const proficiency_id &prof, bool ignore_requirements = false,
bool recursive = false );
void lose_proficiency( const proficiency_id &prof, bool ignore_requirements = false );
bool practice_proficiency( const proficiency_id &prof, const time_duration &amount,
const std::optional<time_duration> &max = std::nullopt );
Expand Down
5 changes: 3 additions & 2 deletions src/character_proficiency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ bool Character::has_prof_prereqs( const proficiency_id &prof ) const
return _proficiencies->has_prereqs( prof );
}

void Character::add_proficiency( const proficiency_id &prof, bool ignore_requirements )
void Character::add_proficiency( const proficiency_id &prof, bool ignore_requirements,
bool recursive )
{
if( ignore_requirements ) {
_proficiencies->direct_learn( prof );
return;
}
_proficiencies->learn( prof );
_proficiencies->learn( prof, recursive );
}

void Character::lose_proficiency( const proficiency_id &prof, bool ignore_requirements )
Expand Down
5 changes: 4 additions & 1 deletion src/newcharacter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ bool avatar::create( character_type type, const std::string &tempname )
return true;
}

void Character::set_skills_from_hobbies()
void Character::set_skills_from_hobbies( bool no_override )
{
// 2 for an average person
float catchup_modifier = 1.0f + ( 2.0f * get_int() + get_per() ) / 24.0f;
Expand All @@ -805,6 +805,9 @@ void Character::set_skills_from_hobbies()
// Grab skills from hobbies and train
for( const profession *profession : hobbies ) {
for( const profession::StartingSkill &e : profession->skills() ) {
if( no_override && get_skill_level( e.first ) != 0 ) {
continue;
}
// Train our skill
const int skill_xp_bonus = calculate_cumulative_experience( e.second );
get_skill_level_object( e.first ).train( skill_xp_bonus, catchup_modifier,
Expand Down
34 changes: 20 additions & 14 deletions src/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,16 +607,15 @@ void npc::randomize( const npc_class_id &type, const npc_template_id &tem_id )
myclass = type;
}

const npc_class &the_class = myclass.obj();
str_max += the_class.roll_strength();
dex_max += the_class.roll_dexterity();
int_max += the_class.roll_intelligence();
per_max += the_class.roll_perception();
str_max += myclass->roll_strength();
dex_max += myclass->roll_dexterity();
int_max += myclass->roll_intelligence();
per_max += myclass->roll_perception();

personality.aggression += the_class.roll_aggression();
personality.bravery += the_class.roll_bravery();
personality.collector += the_class.roll_collector();
personality.altruism += the_class.roll_altruism();
personality.aggression += myclass->roll_aggression();
personality.bravery += myclass->roll_bravery();
personality.collector += myclass->roll_collector();
personality.altruism += myclass->roll_altruism();

personality.aggression = std::clamp( personality.aggression, NPC_PERSONALITY_MIN,
NPC_PERSONALITY_MAX );
Expand Down Expand Up @@ -674,27 +673,34 @@ void npc::randomize( const npc_class_id &type, const npc_template_id &tem_id )
generate_personality_traits();

// Run mutation rounds
for( const auto &mr : type->mutation_rounds ) {
for( const auto &mr : myclass->mutation_rounds ) {
int rounds = mr.second.roll();
for( int i = 0; i < rounds; ++i ) {
mutate_category( mr.first );
}
}
// Add bionics
for( const auto &bl : type->bionic_list ) {
for( const auto &bl : myclass->bionic_list ) {
int chance = bl.second;
if( rng( 0, 100 ) <= chance ) {
add_bionic( bl.first );
}
}
// Add proficiencies
for( const proficiency_id &prof : type->_starting_proficiencies ) {
add_proficiency( prof );
for( const proficiency_id &prof : myclass->_starting_proficiencies ) {
add_proficiency( prof, false, true );
}
if( myclass->is_common() ) {
add_default_background();
set_skills_from_hobbies( true ); // Only trains skills that are still at 0 at this point
set_proficiencies_from_hobbies();
set_bionics_from_hobbies(); // Just in case, for mods
}

// Add martial arts
learn_ma_styles_from_traits();
// Add spells for magiclysm mod
for( std::pair<spell_id, int> spell_pair : type->_starting_spells ) {
for( std::pair<spell_id, int> spell_pair : myclass->_starting_spells ) {
this->magic->learn_spell( spell_pair.first, *this, true );
spell &sp = this->magic->get_spell( spell_pair.first );
while( sp.get_level() < spell_pair.second && !sp.is_max_level( *this ) ) {
Expand Down
5 changes: 5 additions & 0 deletions src/npc_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ void npc_class::check_consistency()
}
}

bool npc_class::is_common() const
{
return common;
}

static distribution load_distribution( const JsonObject &jo )
{
if( jo.has_float( "constant" ) ) {
Expand Down
2 changes: 2 additions & 0 deletions src/npc_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ class npc_class
const time_duration &get_shop_restock_interval() const;
faction_price_rule const *get_price_rules( item const &it, npc const &guy ) const;

bool is_common() const;

void load( const JsonObject &jo, std::string_view src );

static const npc_class_id &from_legacy_int( int i );
Expand Down
6 changes: 4 additions & 2 deletions src/proficiency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,13 @@ void proficiency_set::set_time_practiced( const proficiency_id &practicing,
current.practiced = amount;
}

void proficiency_set::learn( const proficiency_id &learned )
void proficiency_set::learn( const proficiency_id &learned, bool recursive )
{
for( const proficiency_id &req : learned->required_proficiencies() ) {
if( !has_learned( req ) ) {
if( !has_learned( req ) && !recursive ) {
return;
} else if( recursive ) {
learn( req, recursive );
}
}
known.insert( learned );
Expand Down
2 changes: 1 addition & 1 deletion src/proficiency.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class proficiency_set
// True if the proficiency is learned;
bool practice( const proficiency_id &practicing, const time_duration &amount,
float remainder, const std::optional<time_duration> &max );
void learn( const proficiency_id &learned );
void learn( const proficiency_id &learned, bool recursive = false );
void remove( const proficiency_id &lost );

// Ignore requirements, made for debugging
Expand Down

0 comments on commit 92260da

Please sign in to comment.