Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NPC reads books repeatedly #67301

Merged
merged 10 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions data/json/npcs/common_chat/TALK_COMMON_ALLY.json
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,12 @@
"condition": { "and": [ { "not": "npc_has_activity" }, { "not": { "npc_has_trait": "HALLUCINATION" } } ] },
"effect": "do_eread"
},
{
"text": "Please study from books you have in order.",
"topic": "TALK_DONE",
"condition": { "and": [ { "not": "npc_has_activity" }, { "not": { "npc_has_trait": "HALLUCINATION" } } ] },
"effect": "do_read_repeatedly"
},
{
"text": "Please start deconstructing any vehicles in a deconstruction zone.",
"topic": "TALK_DONE",
Expand Down
11 changes: 11 additions & 0 deletions data/json/player_activities.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@
"no_resume": true,
"multi_activity": true
},
{
"id": "ACT_MULTIPLE_READ",
"type": "activity_type",
"activity_level": "NO_EXERCISE",
"verb": "reading",
"based_on": "neither",
"suspendable": false,
"no_resume": true,
"multi_activity": true,
"auto_needs": true
},
{
"id": "ACT_MOP",
"type": "activity_type",
Expand Down
8 changes: 8 additions & 0 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ static const activity_id ACT_MULTIPLE_FARM( "ACT_MULTIPLE_FARM" );
static const activity_id ACT_MULTIPLE_FISH( "ACT_MULTIPLE_FISH" );
static const activity_id ACT_MULTIPLE_MINE( "ACT_MULTIPLE_MINE" );
static const activity_id ACT_MULTIPLE_MOP( "ACT_MULTIPLE_MOP" );
static const activity_id ACT_MULTIPLE_READ( "ACT_MULTIPLE_READ" );
static const activity_id ACT_OPERATION( "ACT_OPERATION" );
static const activity_id ACT_PICKAXE( "ACT_PICKAXE" );
static const activity_id ACT_PLANT_SEED( "ACT_PLANT_SEED" );
Expand Down Expand Up @@ -279,6 +280,7 @@ activity_handlers::do_turn_functions = {
{ ACT_STUDY_SPELL, study_spell_do_turn },
{ ACT_WAIT_STAMINA, wait_stamina_do_turn },
{ ACT_MULTIPLE_DIS, multiple_dis_do_turn },
{ ACT_MULTIPLE_READ, multiple_read_do_turn },
};

const std::map< activity_id, std::function<void( player_activity *, Character * )> >
Expand Down Expand Up @@ -3273,11 +3275,17 @@ void activity_handlers::multiple_butcher_do_turn( player_activity *act, Characte
{
generic_multi_activity_handler( *act, *you );
}

void activity_handlers::multiple_dis_do_turn( player_activity *act, Character *you )
{
generic_multi_activity_handler( *act, *you );
}

void activity_handlers::multiple_read_do_turn( player_activity *act, Character *you )
{
generic_multi_activity_handler( *act, *you );
}

void activity_handlers::vehicle_deconstruction_do_turn( player_activity *act, Character *you )
{
generic_multi_activity_handler( *act, *you );
Expand Down
2 changes: 2 additions & 0 deletions src/activity_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ enum class do_activity_reason : int {
NEEDS_PLANTING, // For farming - tile can be planted
NEEDS_TILLING, // For farming - tile can be tilled
BLOCKING_TILE, // Something has made it's way onto the tile, so the activity cannot proceed
NEEDS_BOOK_TO_LEARN, // There is book to learn
NEEDS_CHOPPING, // There is wood there to be chopped
NEEDS_TREE_CHOPPING, // There is a tree there that needs to be chopped
NEEDS_BIG_BUTCHERING, // There is at least one corpse there to butcher, and it's a big one
Expand Down Expand Up @@ -180,6 +181,7 @@ void multiple_construction_do_turn( player_activity *act, Character *you );
void multiple_dis_do_turn( player_activity *act, Character *you );
void multiple_farm_do_turn( player_activity *act, Character *you );
void multiple_fish_do_turn( player_activity *act, Character *you );
void multiple_read_do_turn( player_activity *act, Character *you );
void multiple_mine_do_turn( player_activity *act, Character *you );
void multiple_mop_do_turn( player_activity *act, Character *you );
void operation_do_turn( player_activity *act, Character *you );
Expand Down
30 changes: 30 additions & 0 deletions src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ static const activity_id ACT_MULTIPLE_CONSTRUCTION( "ACT_MULTIPLE_CONSTRUCTION"
static const activity_id ACT_MULTIPLE_DIS( "ACT_MULTIPLE_DIS" );
static const activity_id ACT_MULTIPLE_FARM( "ACT_MULTIPLE_FARM" );
static const activity_id ACT_MULTIPLE_FISH( "ACT_MULTIPLE_FISH" );
static const activity_id ACT_MULTIPLE_READ( "ACT_MULTIPLE_READ" );
static const activity_id ACT_MULTIPLE_MINE( "ACT_MULTIPLE_MINE" );
static const activity_id ACT_MULTIPLE_MOP( "ACT_MULTIPLE_MOP" );
static const activity_id ACT_PICKAXE( "ACT_PICKAXE" );
Expand Down Expand Up @@ -1219,6 +1220,17 @@ static activity_reason_info can_do_activity_there( const activity_id &act, Chara
}
return activity_reason_info::fail( do_activity_reason::NO_ZONE );
}
if( act == ACT_MULTIPLE_READ ) {
std::vector<std::string> dummy;
const item_filter filter = [ &you, &dummy ]( const item & i ) {
return you.can_read( i, dummy );
};
if( !you.items_with( filter ).empty() ) {
return activity_reason_info::ok( do_activity_reason::NEEDS_BOOK_TO_LEARN );
}
// TODO: find books from zone?
return activity_reason_info::fail( do_activity_reason::ALREADY_DONE );
}
if( act == ACT_MULTIPLE_CHOP_PLANKS ) {
//are there even any logs there?
for( item &i : here.i_at( src_loc ) ) {
Expand Down Expand Up @@ -2503,6 +2515,10 @@ static std::unordered_set<tripoint_abs_ms> generic_multi_activity_locations(
}
}
}
} else if( act_id == ACT_MULTIPLE_READ ) {
for( const tripoint_bub_ms &elem : here.points_in_radius( localpos, ACTIVITY_SEARCH_DISTANCE ) ) {
src_set.insert( here.getglobal( elem ) );
}
} else if( act_id != ACT_FETCH_REQUIRED ) {
zone_type_id zone_type = get_zone_for_act( tripoint_bub_ms{}, mgr, act_id, _fac_id( you ) );
src_set = mgr.get_near( zone_type, abspos, ACTIVITY_SEARCH_DISTANCE, nullptr, _fac_id( you ) );
Expand Down Expand Up @@ -2876,6 +2892,20 @@ static bool generic_multi_activity_do(
you.backlog.emplace_front( act_id );
return false;
}
} else if( reason == do_activity_reason::NEEDS_BOOK_TO_LEARN ) {
std::vector<std::string> dummy;
const item_filter filter = [ &you, &dummy ]( const item & i ) {
return you.can_read( i, dummy );
};
std::vector<item *> books = you.items_with( filter );
if( !books.empty() && books[0] ) {
const time_duration time_taken = you.time_to_read( *books[0], you );
item_location book = item_location( you, books[0] );
item_location ereader;
you.backlog.emplace_front( act_id );
you.assign_activity( read_activity_actor( time_taken, book, ereader, true ) );
return false;
}
} else if( reason == do_activity_reason::CAN_DO_CONSTRUCTION ) {
if( here.partial_con_at( src_loc ) ) {
you.backlog.emplace_front( act_id );
Expand Down
38 changes: 38 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3128,6 +3128,44 @@ bool Character::can_use( const item &it, const item &context ) const
return true;
}

bool Character::can_read( const item &book, std::vector<std::string> &fail_reasons )
{
if( !book.is_book() ) {
fail_reasons.push_back( string_format( _( "This %s is not good reading material." ),
book.tname() ) );
return false;
}
Character *pl = dynamic_cast<Character *>( this );
if( !pl ) {
return false;
}
const auto &type = book.type->book;
const skill_id &skill = type->skill;
const int skill_level = pl->get_knowledge_level( skill );
if( skill && skill_level < type->req ) {
fail_reasons.push_back( string_format( _( "I'm not smart enough to read this book." ) ) );
return false;
}
if( !skill || skill_level >= type->level ) {
fail_reasons.push_back( string_format( _( "I won't learn anything from this book." ) ) );
return false;
}

// Check for conditions that disqualify us
if( type->intel > 0 && has_trait( trait_ILLITERATE ) ) {
fail_reasons.emplace_back( _( "I can't read!" ) );
} else if( has_flag( json_flag_HYPEROPIC ) && !worn_with_flag( flag_FIX_FARSIGHT ) &&
!has_effect( effect_contacts ) &&
!has_flag( STATIC( json_character_flag( "ENHANCED_VISION" ) ) ) ) {
fail_reasons.emplace_back( _( "I can't read without my glasses." ) );
} else if( fine_detail_vision_mod() > 4 ) {
// Too dark to read only applies if the player can read to himself
fail_reasons.emplace_back( _( "It's too dark to read!" ) );
return false;
}
return true;
}

ret_val<void> Character::can_unwield( const item &it ) const
{
if( it.has_flag( flag_NO_UNWIELD ) ) {
Expand Down
6 changes: 6 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -2148,6 +2148,12 @@ class Character : public Creature, public visitable
* @param with_equip_change If true returns if it could be worn if things were taken off
*/
ret_val<void> can_wear( const item &it, bool with_equip_change = false ) const;
/**
* Check if the character needs and be able to read a book.
* @param book Thing to be read.
* @param fail_reasons Why the character cannot read.
*/
bool can_read( const item &book, std::vector<std::string> &fail_reasons );
/**
* Returns true if the character is wielding something.
* Note: this item may not actually be used to attack.
Expand Down
38 changes: 0 additions & 38 deletions src/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1299,44 +1299,6 @@ void npc::starting_weapon( const npc_class_id &type )
}
}

bool npc::can_read( const item &book, std::vector<std::string> &fail_reasons )
{
if( !book.is_book() ) {
fail_reasons.push_back( string_format( _( "This %s is not good reading material." ),
book.tname() ) );
return false;
}
Character *pl = dynamic_cast<Character *>( this );
if( !pl ) {
return false;
}
const auto &type = book.type->book;
const skill_id &skill = type->skill;
const int skill_level = pl->get_knowledge_level( skill );
if( skill && skill_level < type->req ) {
fail_reasons.push_back( string_format( _( "I'm not smart enough to read this book." ) ) );
return false;
}
if( !skill || skill_level >= type->level ) {
fail_reasons.push_back( string_format( _( "I won't learn anything from this book." ) ) );
return false;
}

// Check for conditions that disqualify us
if( type->intel > 0 && has_trait( trait_ILLITERATE ) ) {
fail_reasons.emplace_back( _( "I can't read!" ) );
} else if( has_flag( json_flag_HYPEROPIC ) && !worn_with_flag( flag_FIX_FARSIGHT ) &&
!has_effect( effect_contacts ) &&
!has_flag( STATIC( json_character_flag( "ENHANCED_VISION" ) ) ) ) {
fail_reasons.emplace_back( _( "I can't read without my glasses." ) );
} else if( fine_detail_vision_mod() > 4 ) {
// Too dark to read only applies if the player can read to himself
fail_reasons.emplace_back( _( "It's too dark to read!" ) );
return false;
}
return true;
}

time_duration npc::time_to_read( const item &book, const Character &reader ) const
{
const auto &type = book.type->book;
Expand Down
1 change: 0 additions & 1 deletion src/npc.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,6 @@ class npc : public Character
double value( const item &it, double market_price ) const;
faction_price_rule const *get_price_rules( item const &it ) const;
bool wear_if_wanted( const item &it, std::string &reason );
bool can_read( const item &book, std::vector<std::string> &fail_reasons );
time_duration time_to_read( const item &book, const Character &reader ) const;
void do_npc_read( bool ebook = false );
void stow_item( item &it );
Expand Down
7 changes: 7 additions & 0 deletions src/npctalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ enum npc_chat_menu {
NPC_CHAT_ACTIVITIES_FISHING,
NPC_CHAT_ACTIVITIES_MINING,
NPC_CHAT_ACTIVITIES_MOPPING,
NPC_CHAT_ACTIVITIES_READ_REPEATEDLY,
NPC_CHAT_ACTIVITIES_VEHICLE_DECONSTRUCTION,
NPC_CHAT_ACTIVITIES_VEHICLE_REPAIR,
NPC_CHAT_ACTIVITIES_UNASSIGN
Expand Down Expand Up @@ -591,6 +592,7 @@ static int npc_activities_menu()
nmenu.addentry( NPC_CHAT_ACTIVITIES_FISHING, true, 'F', _( "Fishing in a zone" ) );
nmenu.addentry( NPC_CHAT_ACTIVITIES_MINING, true, 'M', _( "Mining out tiles" ) );
nmenu.addentry( NPC_CHAT_ACTIVITIES_MOPPING, true, 'm', _( "Mopping up stains" ) );
nmenu.addentry( NPC_CHAT_ACTIVITIES_READ_REPEATEDLY, true, 'R', _( "Study from books you have in order" ) );
lispcoc marked this conversation as resolved.
Show resolved Hide resolved
nmenu.addentry( NPC_CHAT_ACTIVITIES_VEHICLE_DECONSTRUCTION, true, 'v',
_( "Deconstructing vehicles" ) );
nmenu.addentry( NPC_CHAT_ACTIVITIES_VEHICLE_REPAIR, true, 'V', _( "Repairing vehicles" ) );
Expand Down Expand Up @@ -1038,6 +1040,10 @@ void game::chat()
talk_function::do_fishing( *selected_npc );
break;
}
case NPC_CHAT_ACTIVITIES_READ_REPEATEDLY: {
talk_function::do_read_repeatedly( *selected_npc );
break;
}
case NPC_CHAT_ACTIVITIES_MINING: {
talk_function::do_mining( *selected_npc );
break;
Expand Down Expand Up @@ -5398,6 +5404,7 @@ void talk_effect_t::parse_string_effect( const std::string &effect_id, const Jso
WRAP( do_mopping ),
WRAP( do_read ),
WRAP( do_eread ),
WRAP( do_read_repeatedly ),
WRAP( do_butcher ),
WRAP( do_farming ),
WRAP( assign_guard ),
Expand Down
1 change: 1 addition & 0 deletions src/npctalk.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ void do_mining( npc & );
void do_mopping( npc & );
void do_read( npc & );
void do_eread( npc & );
void do_read_repeatedly( npc & );
void do_chop_plank( npc & );
void do_vehicle_deconstruct( npc & );
void do_vehicle_repair( npc & );
Expand Down
6 changes: 6 additions & 0 deletions src/npctalk_funcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ static const activity_id ACT_MULTIPLE_CONSTRUCTION( "ACT_MULTIPLE_CONSTRUCTION"
static const activity_id ACT_MULTIPLE_DIS( "ACT_MULTIPLE_DIS" );
static const activity_id ACT_MULTIPLE_FARM( "ACT_MULTIPLE_FARM" );
static const activity_id ACT_MULTIPLE_FISH( "ACT_MULTIPLE_FISH" );
static const activity_id ACT_MULTIPLE_READ( "ACT_MULTIPLE_READ" );
static const activity_id ACT_MULTIPLE_MINE( "ACT_MULTIPLE_MINE" );
static const activity_id ACT_MULTIPLE_MOP( "ACT_MULTIPLE_MOP" );
static const activity_id ACT_SOCIALIZE( "ACT_SOCIALIZE" );
Expand Down Expand Up @@ -267,6 +268,11 @@ void talk_function::do_eread( npc &p )
p.do_npc_read( true );
}

void talk_function::do_read_repeatedly( npc &p )
{
p.assign_activity( ACT_MULTIPLE_READ );
}

void talk_function::dismount( npc &p )
{
p.npc_dismount();
Expand Down