From ce06b54674d329b4a2dc21877f080277cab8ec19 Mon Sep 17 00:00:00 2001 From: RenechCDDA <84619419+RenechCDDA@users.noreply.github.com> Date: Wed, 22 May 2024 18:29:21 -0400 Subject: [PATCH] Recursive proficiency learning for NPC class definitions --- doc/NPCs.md | 3 ++- src/character.h | 3 ++- src/character_proficiency.cpp | 5 +++-- src/npc.cpp | 2 +- src/proficiency.cpp | 6 ++++-- src/proficiency.h | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/doc/NPCs.md b/doc/NPCs.md index 8a2fb034e04b7..c9493dbf32179 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -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. @@ -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 } ``` diff --git a/src/character.h b/src/character.h index deac8661b6127..8f68a4027eab9 100644 --- a/src/character.h +++ b/src/character.h @@ -2458,7 +2458,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 &max = std::nullopt ); diff --git a/src/character_proficiency.cpp b/src/character_proficiency.cpp index b408d0a79c4c1..d159c6b9223cb 100644 --- a/src/character_proficiency.cpp +++ b/src/character_proficiency.cpp @@ -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 ) diff --git a/src/npc.cpp b/src/npc.cpp index 6fd12f89d5e56..5d5abc6f5bef1 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -688,7 +688,7 @@ void npc::randomize( const npc_class_id &type, const npc_template_id &tem_id ) } // Add proficiencies for( const proficiency_id &prof : myclass->_starting_proficiencies ) { - add_proficiency( prof ); + add_proficiency( prof, false, true ); } if( myclass->is_common() ) { add_default_background(); diff --git a/src/proficiency.cpp b/src/proficiency.cpp index 40f3f87a0e5ab..046f95576b402 100644 --- a/src/proficiency.cpp +++ b/src/proficiency.cpp @@ -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 ); diff --git a/src/proficiency.h b/src/proficiency.h index 666c425c2e41d..2e44afdb3449d 100644 --- a/src/proficiency.h +++ b/src/proficiency.h @@ -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 &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