From 6a8bdff204648fc9ae23efca5f31eb5c12d42bbf Mon Sep 17 00:00:00 2001 From: David Seguin Date: Fri, 8 Apr 2022 01:38:18 -0400 Subject: [PATCH 1/4] Monster lore: keep a list of known monsters per character --- src/character.cpp | 25 +++++++++++++++++++++++++ src/character.h | 14 ++++++++++++++ src/savegame_json.cpp | 5 +++++ 3 files changed, 44 insertions(+) diff --git a/src/character.cpp b/src/character.cpp index 6b3c88e7da9f7..f004a4781984d 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7852,6 +7852,31 @@ bool Character::is_hauling() const return hauling; } +bool Character::knows_creature_type( const Creature *c ) const +{ + if( const monster *mon = dynamic_cast( c ) ) { + return knows_creature_type( mon->type->id ); + } + return false; +} + +bool Character::knows_creature_type( const mtype_id &c ) const +{ + return known_monsters.count( c ) > 0; +} + +void Character::set_knows_creature_type( const Creature *c ) +{ + if( const monster *mon = dynamic_cast( c ) ) { + set_knows_creature_type( mon->type->id ); + } +} + +void Character::set_knows_creature_type( const mtype_id &c ) +{ + known_monsters.emplace( c ); +} + void Character::assign_activity( const activity_id &type, int moves, int index, int pos, const std::string &name ) { diff --git a/src/character.h b/src/character.h index 406b12fd703e1..91993f99252bd 100644 --- a/src/character.h +++ b/src/character.h @@ -2378,6 +2378,7 @@ class Character : public Creature, public visitable } protected: int focus_pool = 0; + std::set known_monsters; public: int cash = 0; weak_ptr_fast last_target; @@ -2399,6 +2400,19 @@ class Character : public Creature, public visitable /** Returns the intensity of the specified addiction */ int addiction_level( add_type type ) const; + /** Returns true if the character is familiar with the given creature type **/ + bool knows_creature_type( const Creature *c ) const; + /** Returns true if the character is familiar with the given creature type **/ + bool knows_creature_type( const mtype_id &c ) const; + /** This character becomes familiar with creatures of the given type **/ + void set_knows_creature_type( const Creature *c ); + /** This character becomes familiar with creatures of the given type **/ + void set_knows_creature_type( const mtype_id &c ); + /** Returns a list of all monster types known by this character **/ + const std::set &get_known_monsters() const { + return known_monsters; + } + shared_ptr_fast mounted_creature; // for loading NPC mounts int mounted_creature_id = 0; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 9154a4db4b9ca..d76257fab2616 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -775,6 +775,8 @@ void Character::load( const JsonObject &data ) data.read( "my_bionics", *my_bionics ); + data.read( "known_monsters", known_monsters ); + invalidate_pseudo_items(); update_bionic_power_capacity(); data.read( "death_eocs", death_eocs ); @@ -1250,6 +1252,9 @@ void Character::store( JsonOut &json ) const json.member_as_string( "move_mode", move_mode ); + // monsters recorded by the character + json.member( "known_monsters", known_monsters ); + // storing the mount if( is_mounted() ) { json.member( "mounted_creature", g->critter_tracker->temporary_id( *mounted_creature ) ); From fb9425b6cc915cf909dd90618af9be73664c3fe3 Mon Sep 17 00:00:00 2001 From: David Seguin Date: Fri, 8 Apr 2022 01:45:17 -0400 Subject: [PATCH 2/4] Monster lore: show list of recorded monsters in faction ui --- src/faction.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/faction.cpp b/src/faction.cpp index c8d44c3d1405c..98c2ba29701ba 100644 --- a/src/faction.cpp +++ b/src/faction.cpp @@ -28,6 +28,7 @@ #include "line.h" #include "localized_comparator.h" #include "memory_fast.h" +#include "mtype.h" #include "npc.h" #include "optional.h" #include "output.h" @@ -730,6 +731,7 @@ void faction_manager::display() const TAB_FOLLOWERS, TAB_OTHERFACTIONS, TAB_LORE, + TAB_CREATURES, NUM_TABS, FIRST_TAB = 0, LAST_TAB = NUM_TABS - 1 @@ -759,6 +761,8 @@ void faction_manager::display() const size_t active_vec_size = 0; std::vector> lore; // Lore we have seen std::pair *snippet = nullptr; + std::vector creatures; // Creatures we've recorded + mtype_id cur_creature = mtype_id::NULL_ID(); ui.on_redraw( [&]( const ui_adaptor & ) { werase( w_missions ); @@ -772,6 +776,7 @@ void faction_manager::display() const { tab_mode::TAB_FOLLOWERS, _( "YOUR FOLLOWERS" ) }, { tab_mode::TAB_OTHERFACTIONS, _( "OTHER FACTIONS" ) }, { tab_mode::TAB_LORE, _( "LORE" ) }, + { tab_mode::TAB_CREATURES, _( "CREATURES" ) }, }; draw_tabs( w_missions, tabs, tab ); draw_border_below_tabs( w_missions ); @@ -876,6 +881,29 @@ void faction_manager::display() const } } break; + case tab_mode::TAB_CREATURES: { + const std::string no_creatures = + _( "You haven't recorded sightings of any creatures. Taking photos can be a good way to keep track of them." ); + const int w = getmaxx( w_missions ) - 31 - 2; + if( active_vec_size > 0 ) { + draw_scrollbar( w_missions, selection, entries_per_page, active_vec_size, + point( 0, 3 ) ); + for( size_t i = top_of_page; i < active_vec_size && i < top_of_page + entries_per_page; i++ ) { + const int y = i - top_of_page + 3; + trim_and_print( w_missions, point( 1, y ), 28, selection == i ? hilite( col ) : col, + creatures[i]->nname() ); + } + if( !cur_creature.is_null() ) { + int y = 2; + fold_and_print( w_missions, point( 31, ++y ), w, c_light_gray, cur_creature->get_description() ); + } else { + fold_and_print( w_missions, point( 31, 4 ), w, c_light_red, no_creatures ); + } + break; + } else { + fold_and_print( w_missions, point( 31, 4 ), w, c_light_red, no_creatures ); + } + } default: break; } @@ -903,6 +931,7 @@ void faction_manager::display() const guy = nullptr; cur_fac = nullptr; snippet = nullptr; + cur_creature = mtype_id::NULL_ID(); interactable = false; radio_interactable = false; camp = nullptr; @@ -935,6 +964,13 @@ void faction_manager::display() const debugmsg( "The sanity check failed because tab=%d", static_cast( tab ) ); tab = tab_mode::FIRST_TAB; } + creatures.clear(); + creatures.reserve( player_character.get_known_monsters().size() ); + creatures.insert( creatures.end(), player_character.get_known_monsters().begin(), + player_character.get_known_monsters().end() ); + std::sort( creatures.begin(), creatures.end(), []( const mtype_id & a, const mtype_id & b ) { + return localized_compare( a->nname(), b->nname() ); + } ); active_vec_size = camps.size(); if( tab == tab_mode::TAB_FOLLOWERS ) { if( selection < followers.size() ) { @@ -956,6 +992,11 @@ void faction_manager::display() const snippet = &lore[selection]; } active_vec_size = lore.size(); + } else if( tab == tab_mode::TAB_CREATURES ) { + if( selection < creatures.size() ) { + cur_creature = creatures[selection]; + } + active_vec_size = creatures.size(); } ui_manager::redraw(); From f3532d5ad76dfaf7cdca71ebee0207b222480327 Mon Sep 17 00:00:00 2001 From: David Seguin Date: Fri, 8 Apr 2022 01:54:27 -0400 Subject: [PATCH 3/4] Monster lore: record monster type when snapping a photo --- src/iuse.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/iuse.cpp b/src/iuse.cpp index 0491b41eb11a8..4cd0660967564 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -7376,6 +7376,9 @@ static void item_save_monsters( Character &p, item &it, const std::vector const size_t mon_str_pos = monster_photos.find( "," + mtype + "," ); + // monster gets recorded by the character, add to known types + p.set_knows_creature_type( monster_p->type->id ); + if( mon_str_pos == std::string::npos ) { // new monster monster_photos += string_format( "%s,%d,", mtype, photo_quality ); } else { // replace quality character, if new photo is better From 60a14f6cad916b0c952a90ec7327d73663bb97f8 Mon Sep 17 00:00:00 2001 From: David Seguin Date: Fri, 8 Apr 2022 16:10:16 -0400 Subject: [PATCH 4/4] Monster lore: display additional monster details --- src/faction.cpp | 7 ++-- src/mtype.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ src/mtype.h | 1 + 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/src/faction.cpp b/src/faction.cpp index 98c2ba29701ba..fe398f136810b 100644 --- a/src/faction.cpp +++ b/src/faction.cpp @@ -891,11 +891,12 @@ void faction_manager::display() const for( size_t i = top_of_page; i < active_vec_size && i < top_of_page + entries_per_page; i++ ) { const int y = i - top_of_page + 3; trim_and_print( w_missions, point( 1, y ), 28, selection == i ? hilite( col ) : col, - creatures[i]->nname() ); + string_format( "%s %s", colorize( creatures[i]->sym, + selection == i ? hilite( creatures[i]->color ) : creatures[i]->color ), + creatures[i]->nname() ) ); } if( !cur_creature.is_null() ) { - int y = 2; - fold_and_print( w_missions, point( 31, ++y ), w, c_light_gray, cur_creature->get_description() ); + cur_creature->faction_display( w_missions, point( 31, 3 ), w ); } else { fold_and_print( w_missions, point( 31, 4 ), w, c_light_red, no_creatures ); } diff --git a/src/mtype.cpp b/src/mtype.cpp index 66d9b829b24b3..d5cc636b9ae67 100644 --- a/src/mtype.cpp +++ b/src/mtype.cpp @@ -10,8 +10,10 @@ #include "field_type.h" #include "item.h" #include "itype.h" +#include "mod_manager.h" #include "mondeath.h" #include "monstergenerator.h" +#include "output.h" #include "translations.h" #include "units.h" #include "weakpoint.h" @@ -279,3 +281,97 @@ const behavior::node_t *mtype::get_goals() const { return &goals; } + +void mtype::faction_display( catacurses::window &w, const point &top_left, const int width ) const +{ + int y = 0; + // Name & symbol + trim_and_print( w, top_left + point( 2, y ), width, c_white, string_format( "%s %s", colorize( sym, + color ), nname() ) ); + y++; + // Difficulty + std::string diff_str; + if( difficulty < 3 ) { + diff_str = _( "Minimal threat." ); + } else if( difficulty < 10 ) { + diff_str = _( "Mildly dangerous." ); + } else if( difficulty < 20 ) { + diff_str = _( "Dangerous." ); + } else if( difficulty < 30 ) { + diff_str = _( "Very dangerous." ); + } else if( difficulty < 50 ) { + diff_str = _( "Extremely dangerous." ); + } else { + diff_str = _( "Fatally dangerous!" ); + } + trim_and_print( w, top_left + point( 0, ++y ), width, c_light_gray, + string_format( "%s: %s", colorize( _( "Difficulty" ), c_white ), diff_str ) ); + // Origin + std::vector origin_list = + foldstring( string_format( "%s: %s", colorize( _( "Origin" ), c_white ), + enumerate_as_string( src.begin(), src.end(), + []( const std::pair &source ) { + return string_format( "'%s'", source.second->name() ); + }, enumeration_conjunction::arrow ) ), width ); + for( const std::string &org : origin_list ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_light_gray, org ); + } + // Size + const std::map size_map = { + {creature_size::tiny, translate_marker( "Tiny" )}, + {creature_size::small, translate_marker( "Small" )}, + {creature_size::medium, translate_marker( "Medium" )}, + {creature_size::large, translate_marker( "Large" )}, + {creature_size::huge, translate_marker( "Huge" )} + }; + auto size_iter = size_map.find( size ); + trim_and_print( w, top_left + point( 0, ++y ), width, c_light_gray, + string_format( "%s: %s", colorize( _( "Size" ), c_white ), + size_iter == size_map.end() ? _( "Unknown" ) : _( size_iter->second ) ) ); + // Species + std::vector species_list = + foldstring( string_format( "%s: %s", colorize( _( "Species" ), c_white ), + enumerate_as_string( species_descriptions(), []( const std::string & sp ) { + return colorize( sp, c_yellow ); + } ) ), width ); + for( const std::string &sp : species_list ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_light_gray, sp ); + } + // Senses + std::vector senses_str; + if( has_flag( MF_HEARS ) ) { + senses_str.emplace_back( colorize( _( "hearing" ), c_yellow ) ); + } + if( has_flag( MF_SEES ) ) { + senses_str.emplace_back( colorize( _( "sight" ), c_yellow ) ); + } + if( has_flag( MF_SMELLS ) ) { + senses_str.emplace_back( colorize( _( "smell" ), c_yellow ) ); + } + trim_and_print( w, top_left + point( 0, ++y ), width, c_light_gray, + string_format( "%s: %s", colorize( _( "Senses" ), c_white ), enumerate_as_string( senses_str ) ) ); + // Abilities + if( has_flag( MF_SWIMS ) ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_white, _( "It can swim." ) ); + } + if( has_flag( MF_FLIES ) ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_white, _( "It can fly." ) ); + } + if( has_flag( MF_DIGS ) ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_white, _( "It can burrow underground." ) ); + } + if( has_flag( MF_CLIMBS ) ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_white, _( "It can climb." ) ); + } + if( has_flag( MF_GRABS ) ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_white, _( "It can grab." ) ); + } + if( has_flag( MF_VENOM ) ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_white, _( "It can inflict poison." ) ); + } + if( has_flag( MF_PARALYZE ) ) { + trim_and_print( w, top_left + point( 0, ++y ), width, c_white, _( "It can inflict paralysis." ) ); + } + // Description + fold_and_print( w, top_left + point( 0, y + 2 ), width, c_light_gray, get_description() ); +} diff --git a/src/mtype.h b/src/mtype.h index 1296c514796fd..9e360f6eb59fa 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -484,6 +484,7 @@ struct mtype { void set_strategy(); void add_goal( const std::string &goal_id ); const behavior::node_t *get_goals() const; + void faction_display( catacurses::window &w, const point &top_left, const int width ) const; // Historically located in monstergenerator.cpp void load( const JsonObject &jo, const std::string &src );