diff --git a/src/bionics.cpp b/src/bionics.cpp index baa5397877bc0..a785b50060820 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -1573,7 +1573,7 @@ void Character::bionics_uninstall_failure( int difficulty, int success, float ad case 1: if( !has_trait( trait_NOPAIN ) ) { add_msg_if_player( m_bad, _( "It really hurts!" ) ); - mod_pain( rng( failure_level * 3, failure_level * 6 ) ); + mod_pain( rng( 10, 30 ) ); } break; @@ -1581,12 +1581,12 @@ void Character::bionics_uninstall_failure( int difficulty, int success, float ad case 3: for( const bodypart_id &bp : get_all_body_parts() ) { const body_part enum_bp = bp->token; - if( has_effect( effect_under_op, enum_bp ) ) { + if( has_effect( effect_under_op, enum_bp ) && enum_bp != num_bp ) { if( bp_hurt.count( mutate_to_main_part( enum_bp ) ) > 0 ) { continue; } bp_hurt.emplace( mutate_to_main_part( enum_bp ) ); - apply_damage( this, bp, rng( failure_level, failure_level * 2 ), true ); + apply_damage( this, bp, rng( 2, 6 ), true ); add_msg_player_or_npc( m_bad, _( "Your %s is damaged." ), _( "'s %s is damaged." ), body_part_name_accusative( enum_bp ) ); } @@ -1930,6 +1930,9 @@ bool Character::uninstall_bionic( const bionic_id &b_id, player &installer, bool } else { activity.str_values.push_back( "false" ); } + for( const std::pair &elem : b_id->occupied_bodyparts ) { + add_effect( effect_under_op, difficulty * 20_minutes, elem.first, true, difficulty ); + } return true; } @@ -2196,6 +2199,9 @@ bool Character::install_bionics( const itype &type, player &installer, bool auto } else { activity.str_values.push_back( "false" ); } + for( const std::pair &elem : bioid->occupied_bodyparts ) { + add_effect( effect_under_op, difficulty * 20_minutes, elem.first, true, difficulty ); + } return true; } @@ -2275,7 +2281,7 @@ void Character::bionics_install_failure( const bionic_id &bid, const std::string case 1: if( !( has_trait( trait_NOPAIN ) ) ) { add_msg_if_player( m_bad, _( "It really hurts!" ) ); - mod_pain( rng( failure_level * 3, failure_level * 6 ) ); + mod_pain( rng( 10, 30 ) ); } drop_cbm = true; break; @@ -2284,7 +2290,7 @@ void Character::bionics_install_failure( const bionic_id &bid, const std::string case 3: for( const bodypart_id &bp : get_all_body_parts() ) { const body_part enum_bp = bp->token; - if( has_effect( effect_under_op, enum_bp ) ) { + if( has_effect( effect_under_op, enum_bp ) && enum_bp != num_bp ) { if( bp_hurt.count( mutate_to_main_part( enum_bp ) ) > 0 ) { continue; } diff --git a/src/character.cpp b/src/character.cpp index 8fc17e136dc4e..281c78cb51c1f 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -6360,7 +6360,7 @@ float Character::active_light() const lumination = static_cast( maxlum ); float mut_lum = 0.0f; - for( const std::pair &mut : my_mutations ) { + for( const std::pair &mut : my_mutations ) { if( mut.second.powered ) { float curr_lum = 0.0f; for( const auto elem : mut.first->lumination ) { @@ -7953,7 +7953,7 @@ void Character::recalculate_enchantment_cache() } // get from traits/ mutations - for( const std::pair &mut_map : my_mutations ) { + for( const std::pair &mut_map : my_mutations ) { const mutation_branch &mut = mut_map.first.obj(); // only add the passive or powered active mutations diff --git a/src/event.cpp b/src/event.cpp index c0daa07ac241d..2e42f49f433c5 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -138,7 +138,7 @@ DEFINE_EVENT_FIELDS( teleports_into_wall ) } // namespace event_detail template -static void get_fields_if_match( event_type type, std::map &out ) +static void get_fields_if_match( event_type type, event::fields_type &out ) { if( Type == type ) { out = { event_detail::event_spec::fields.begin(), @@ -148,10 +148,10 @@ static void get_fields_if_match( event_type type, std::map -static std::map +static event::fields_type get_fields_helper( event_type type, std::integer_sequence ) { - std::map result; + event::fields_type result; bool discard[] = { ( get_fields_if_match( I )>( type, result ), true )... }; @@ -159,7 +159,7 @@ get_fields_helper( event_type type, std::integer_sequence ) return result; } -std::map event::get_fields( event_type type ) +event::fields_type event::get_fields( event_type type ) { return get_fields_helper( type, std::make_integer_sequence( event_type::num_event_types )> {} ); diff --git a/src/event.h b/src/event.h index e5a42030cffe9..f618bb6c23f54 100644 --- a/src/event.h +++ b/src/event.h @@ -558,7 +558,8 @@ class event > ()( calendar::turn, std::forward( args )... ); } - static std::map get_fields( event_type ); + using fields_type = std::unordered_map; + static fields_type get_fields( event_type ); event_type type() const { return type_; @@ -577,6 +578,14 @@ class event return it->second; } + cata_variant get_variant_or_void( const std::string &key ) const { + auto it = data_.find( key ); + if( it == data_.end() ) { + return cata_variant(); + } + return it->second; + } + template auto get( const std::string &key ) const { return get_variant( key ).get(); diff --git a/src/event_field_transformations.cpp b/src/event_field_transformations.cpp index 719b57824879c..8239678e62bcf 100644 --- a/src/event_field_transformations.cpp +++ b/src/event_field_transformations.cpp @@ -17,6 +17,9 @@ static std::vector species_of_monster( const cata_variant &v ) return result; } -const std::unordered_map event_field_transformations = { - { "species_of_monster", species_of_monster }, +const std::unordered_map event_field_transformations = { + { + "species_of_monster", + { species_of_monster, cata_variant_type::species_id, { cata_variant_type::mtype_id } } + }, }; diff --git a/src/event_field_transformations.h b/src/event_field_transformations.h index 55b1f3d4a8cd9..e8c4362a88535 100644 --- a/src/event_field_transformations.h +++ b/src/event_field_transformations.h @@ -8,7 +8,14 @@ #include "cata_variant.h" -using EventFieldTransformation = std::vector( * )( const cata_variant & ); -extern const std::unordered_map event_field_transformations; +struct event_field_transformation { + using function_type = std::vector( * )( const cata_variant & ); + function_type function; + cata_variant_type return_type; + std::vector argument_types; +}; + +extern const std::unordered_map +event_field_transformations; #endif // CATA_SRC_EVENT_FIELD_TRANSFORMATIONS_H diff --git a/src/event_statistics.cpp b/src/event_statistics.cpp index 28e57c5aa25b9..0b9f0fc6784cf 100644 --- a/src/event_statistics.cpp +++ b/src/event_statistics.cpp @@ -145,6 +145,7 @@ class event_transformation::impl virtual event_multiset initialize( stats_tracker & ) const = 0; virtual std::unique_ptr watch( stats_tracker & ) const = 0; virtual void check( const std::string &/*name*/ ) const {} + virtual cata::event::fields_type fields() const = 0; virtual monotonically monotonicity() const = 0; virtual std::unique_ptr clone() const = 0; }; @@ -156,6 +157,7 @@ class event_statistic::impl virtual cata_variant value( stats_tracker & ) const = 0; virtual std::unique_ptr watch( stats_tracker & ) const = 0; virtual void check( const std::string &/*name*/ ) const {} + virtual cata_variant_type type() const = 0; virtual monotonically monotonicity() const = 0; virtual std::unique_ptr clone() const = 0; }; @@ -200,10 +202,31 @@ struct value_constraint { } } - void check( const std::string &name ) const { - if( equals_statistic_ && !equals_statistic_->is_valid() ) { - debugmsg( "constraint for event_transformation %s refers to invalid statistic %s", - name, equals_statistic_->str() ); + void check( const std::string &name, const cata_variant_type input_type ) const { + if( equals_statistic_ ) { + if( !equals_statistic_->is_valid() ) { + debugmsg( "constraint for event_transformation %s refers to invalid statistic %s", + name, equals_statistic_->str() ); + return; + } + + cata_variant_type stat_type = ( *equals_statistic_ )->type(); + if( stat_type != input_type ) { + debugmsg( "constraint for event_transformation %s matches statistic %s of " + "different type. Statistic has type %s but value compared with it has " + "type %s", + name, equals_statistic_->str(), io::enum_to_string( stat_type ), + io::enum_to_string( input_type ) ); + } + } + + if( equals_ ) { + if( input_type != equals_->type() ) { + debugmsg( "constraint for event_transformation %s matches constant of type %s but " + "value compared with it has type %s", + name, io::enum_to_string( equals_->type() ), + io::enum_to_string( input_type ) ); + } } } @@ -214,7 +237,7 @@ struct value_constraint { }; struct new_field { - EventFieldTransformation transformation; + event_field_transformation transformation; std::string input_field; void deserialize( JsonIn &jsin ) { @@ -235,20 +258,46 @@ struct new_field { } } + void check( const std::string &context_name, + const cata::event::fields_type &input_fields ) const { + auto it = input_fields.find( input_field ); + if( it == input_fields.end() ) { + debugmsg( "event_transformation %s specifies transformation on field %s but not such " + "field exists on the input", context_name, input_field ); + return; + } + if( transformation.argument_types.size() != 1 ) { + debugmsg( "event_field_transformations of arity not 1 not supported" ); + return; + } + if( it->second != transformation.argument_types[0] ) { + debugmsg( "event_transformation %s specifies transformation on field %s of incorrect " + "type. Transformation expects %s; field was %s.", context_name, input_field, + io::enum_to_string( transformation.argument_types[0] ), + io::enum_to_string( it->second ) ); + } + } + std::vector transform( const cata::event::data_type &data, const std::string &new_field_name ) const { std::vector result; auto it = data.find( input_field ); if( it == data.end() ) { - debugmsg( "Expected input field '%s' not present in event", input_field ); + // Field missing, probably due to stale data. Reporting an error + // would spam, so we rely on the fact that it should already have + // been reported at startup time. return result; } - for( cata_variant v : transformation( it->second ) ) { + for( cata_variant v : transformation.function( it->second ) ) { result.push_back( data ); result.back().emplace( new_field_name, v ); } return result; } + + cata_variant_type type( const cata::event::fields_type &/*input*/ ) const { + return transformation.return_type; + } }; // Helper struct to abstract the two possible sources of event_multisets: @@ -278,6 +327,22 @@ struct event_source { } } + std::string debug_description() const { + if( transformation.is_empty() ) { + return "event type " + io::enum_to_string( type ); + } else { + return "event_transformation " + transformation->id.str(); + } + } + + bool is_game_start() const { + if( transformation.is_empty() ) { + return type == event_type::game_start; + } else { + return false; + } + } + void add_watcher( stats_tracker &stats, event_multiset_watcher *watcher ) const { if( transformation.is_empty() ) { stats.add_watcher( type, watcher ); @@ -286,6 +351,14 @@ struct event_source { } } + cata::event::fields_type fields() const { + if( transformation.is_empty() ) { + return cata::event::get_fields( type ); + } else { + return transformation->fields(); + } + } + monotonically monotonicity() const { if( transformation.is_empty() ) { return monotonically::increasing; @@ -369,6 +442,35 @@ struct event_transformation_impl : public event_transformation::impl { return initialize( source_.get( stats ).counts(), stats ); } + void check( const std::string &name ) const override { + const cata::event::fields_type input_fields = source_.fields(); + + for( const std::pair &p : new_fields_ ) { + if( input_fields.count( p.first ) ) { + debugmsg( "event_transformation %s tries to add field with name %s but a field " + "with that name already exists", name, p.first ); + } + p.second.check( name, input_fields ); + } + + for( const std::pair &p : constraints_ ) { + auto it = input_fields.find( p.first ); + if( it == input_fields.end() ) { + debugmsg( "event_transformation %s applies constraint to field %s, but no field " + "with that name exists in the input", name, p.first ); + } else { + p.second.check( name, it->second ); + } + } + + for( const std::string drop_field_name : drop_fields_ ) { + if( input_fields.find( drop_field_name ) == input_fields.end() ) { + debugmsg( "event_transformation %s lists field %s to be dropped, but no field " + "with that name exists in the input", name, drop_field_name ); + } + } + } + struct state : stats_tracker_state, event_multiset_watcher, stat_watcher { state( const event_transformation_impl *trans, stats_tracker &stats ) : transformation_( trans ), @@ -408,10 +510,18 @@ struct event_transformation_impl : public event_transformation::impl { return std::make_unique( this, stats ); } - void check( const std::string &name ) const override { - for( const std::pair &p : constraints_ ) { - p.second.check( name ); + cata::event::fields_type fields() const override { + cata::event::fields_type result = source_.fields(); + + for( const std::pair &p : new_fields_ ) { + result.emplace( p.first, p.second.type( result ) ); + } + + for( const std::string drop_field_name : drop_fields_ ) { + result.erase( drop_field_name ); } + + return result; } monotonically monotonicity() const override { @@ -460,6 +570,11 @@ void event_transformation::check() const impl_->check( id.str() ); } +cata::event::fields_type event_transformation::fields() const +{ + return impl_->fields(); +} + monotonically event_transformation::monotonicity() const { return impl_->monotonicity(); @@ -504,6 +619,10 @@ struct event_statistic_count : event_statistic::impl { return std::make_unique( this, stats ); } + cata_variant_type type() const override { + return cata_variant_type::int_; + } + monotonically monotonicity() const override { return source.monotonicity(); } @@ -553,6 +672,10 @@ struct event_statistic_total : event_statistic::impl { return std::make_unique( this, stats ); } + cata_variant_type type() const override { + return cata_variant_type::int_; + } + monotonically monotonicity() const override { return source.monotonicity(); } @@ -563,17 +686,17 @@ struct event_statistic_total : event_statistic::impl { }; struct event_statistic_unique_value : event_statistic::impl { - event_statistic_unique_value( const string_id &id, event_type type, + event_statistic_unique_value( const string_id &id, const event_source &s, const std::string &field ) : - id_( id ), type_( type ), field_( field ) + id_( id ), source_( s ), field_( field ) {} string_id id_; - event_type type_; + event_source source_; std::string field_; cata_variant value( stats_tracker &stats ) const override { - const event_multiset::counts_type &counts = stats.get_events( type_ ).counts(); + const event_multiset::counts_type counts = source_.get( stats ).counts(); if( counts.size() != 1 ) { return cata_variant(); } @@ -590,18 +713,18 @@ struct event_statistic_unique_value : event_statistic::impl { state( const event_statistic_unique_value *s, stats_tracker &stats ) : stat( s ) { init( stats ); - stats.add_watcher( stat->type_, this ); + stat->source_.add_watcher( stats, this ); } void init( stats_tracker &stats ) { - count = stats.get_events( stat->type_ ).count(); + count = stat->source_.get( stats ).count(); value = stat->value( stats ); } void event_added( const cata::event &e, stats_tracker &stats ) override { ++count; if( count == 1 ) { - value = e.get_variant( stat->field_ ); + value = e.get_variant_or_void( stat->field_ ); } else if( count == 2 ) { value = cata_variant(); } else { @@ -625,16 +748,26 @@ struct event_statistic_unique_value : event_statistic::impl { } void check( const std::string &name ) const override { - std::map event_fields = cata::event::get_fields( type_ ); + cata::event::fields_type event_fields = source_.fields(); auto it = event_fields.find( field_ ); if( it == event_fields.end() ) { - debugmsg( "event_statistic %s refers to field %s in event_type %s, but that type has " - "no such field", name, field_, io::enum_to_string( type_ ) ); + debugmsg( "event_statistic %s refers to field %s in event source %s, but that source " + "has no such field", name, field_, source_.debug_description() ); + } + } + + cata_variant_type type() const override { + cata::event::fields_type source_fields = source_.fields(); + auto it = source_fields.find( field_ ); + if( it == source_fields.end() ) { + return cata_variant_type::void_; + } else { + return it->second; } } monotonically monotonicity() const override { - if( type_ == event_type::game_start ) { + if( source_.is_game_start() ) { return monotonically::constant; } else { return monotonically::unknown; @@ -686,6 +819,11 @@ void event_statistic::check() const impl_->check( id.str() ); } +cata_variant_type event_statistic::type() const +{ + return impl_->type(); +} + monotonically event_statistic::monotonicity() const { return impl_->monotonicity(); diff --git a/src/event_statistics.h b/src/event_statistics.h index bae47d0676f08..197e64e4e75aa 100644 --- a/src/event_statistics.h +++ b/src/event_statistics.h @@ -4,6 +4,7 @@ #include #include +#include #include #include "clone_ptr.h" @@ -11,6 +12,7 @@ #include "translations.h" class cata_variant; +enum class cata_variant_type : int; class event_multiset; enum class event_type : int; class JsonObject; @@ -18,6 +20,8 @@ enum class monotonically : int; class stats_tracker; class stats_tracker_state; +using event_fields_type = std::unordered_map; + // event_tansformations and event_statistics are both functions of events. // They are intended to be calculated via a stats_tracker object. // They can be defined in json, and are useful therein for the creation of @@ -48,6 +52,7 @@ class event_transformation string_id id; bool was_loaded = false; + event_fields_type fields() const; monotonically monotonicity() const; class impl; @@ -75,6 +80,7 @@ class event_statistic return description_; } + cata_variant_type type() const; monotonically monotonicity() const; class impl; diff --git a/src/game.cpp b/src/game.cpp index 9dff4a11fec29..39d0e1f68b81b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3243,6 +3243,8 @@ void game::invalidate_main_ui_adaptor() const } } +static void draw_trail( const tripoint &start, const tripoint &end, bool bDrawX ); + void game::draw() { if( test_mode ) { @@ -3288,6 +3290,10 @@ void game::draw() draw_zones( start, end, offset ); } + if( trail_start && trail_end ) { + draw_trail( trail_start.value(), trail_end.value(), trail_end_x ); + } + wrefresh( w_terrain ); draw_panels( true ); @@ -6899,38 +6905,41 @@ std::vector game::find_nearby_items( int iRadius ) return ret; } -void game::draw_trail_to_square( const tripoint &t, bool bDrawX ) +void draw_trail( const tripoint &start, const tripoint &end, const bool bDrawX ) { - //Reset terrain - draw_ter(); - std::vector pts; - tripoint center = u.pos() + u.view_offset; - if( t != tripoint_zero ) { + tripoint center = g->u.pos() + g->u.view_offset; + if( start != end ) { //Draw trail - pts = line_to( u.pos(), u.pos() + t, 0, 0 ); + pts = line_to( start, end, 0, 0 ); } else { //Draw point - pts.push_back( u.pos() ); + pts.emplace_back( start ); } - draw_line( u.pos() + t, center, pts ); + g->draw_line( end, center, pts ); if( bDrawX ) { char sym = 'X'; - if( t.z > 0 ) { + if( end.z > center.z ) { sym = '^'; - } else if( t.z < 0 ) { + } else if( end.z < center.z ) { sym = 'v'; } if( pts.empty() ) { - mvwputch( w_terrain, point( POSX, POSY ), c_white, sym ); + mvwputch( g->w_terrain, point( POSX, POSY ), c_white, sym ); } else { - mvwputch( w_terrain, pts[pts.size() - 1].xy() - u.view_offset.xy() + point( POSX - u.posx(), - POSY - u.posy() ), + mvwputch( g->w_terrain, pts.back().xy() - g->u.view_offset.xy() + + point( POSX - g->u.posx(), POSY - g->u.posy() ), c_white, sym ); } } +} +void game::draw_trail_to_square( const tripoint &t, bool bDrawX ) +{ + //Reset terrain + draw_ter(); + ::draw_trail( u.pos(), u.pos() + t, bDrawX ); wrefresh( w_terrain ); } @@ -7153,26 +7162,49 @@ void game::list_items_monsters() game::vmenu_ret game::list_items( const std::vector &item_list ) { std::vector ground_items = item_list; - int iInfoHeight = std::min( 25, TERMY / 2 ); - int width = 45; + int iInfoHeight = 0; + int iMaxRows = 0; + int width = 0; + int max_name_width = 0; //find max length of item name and resize window width for( const map_item_stack &cur_item : ground_items ) { const int item_len = utf8_width( remove_color_tags( cur_item.example->display_name() ) ) + 15; - if( item_len > width ) { - width = item_len; + if( item_len > max_name_width ) { + max_name_width = item_len; } } - width = clamp( width, 45, ( TERMX - VIEW_OFFSET_X ) / 3 ); - const int offsetX = TERMX - VIEW_OFFSET_X - width; + tripoint active_pos; + map_item_stack *activeItem = nullptr; + + catacurses::window w_items; + catacurses::window w_items_border; + catacurses::window w_item_info; + + ui_adaptor ui; + ui.on_screen_resize( [&]( ui_adaptor & ui ) { + iInfoHeight = std::min( 25, TERMY / 2 ); + iMaxRows = TERMY - iInfoHeight - 2 - VIEW_OFFSET_Y * 2; + + width = clamp( max_name_width, 45, ( TERMX - VIEW_OFFSET_X ) / 3 ); + + const int offsetX = TERMX - VIEW_OFFSET_X - width; - catacurses::window w_items = catacurses::newwin( TERMY - 2 - iInfoHeight - VIEW_OFFSET_Y * 2, - width - 2, point( offsetX + 1, VIEW_OFFSET_Y + 1 ) ); - catacurses::window w_items_border = catacurses::newwin( TERMY - iInfoHeight - VIEW_OFFSET_Y * 2, - width, point( offsetX, VIEW_OFFSET_Y ) ); - catacurses::window w_item_info = catacurses::newwin( iInfoHeight, width, - point( offsetX, TERMY - iInfoHeight - VIEW_OFFSET_Y ) ); + w_items = catacurses::newwin( TERMY - 2 - iInfoHeight - VIEW_OFFSET_Y * 2, + width - 2, point( offsetX + 1, VIEW_OFFSET_Y + 1 ) ); + w_items_border = catacurses::newwin( TERMY - iInfoHeight - VIEW_OFFSET_Y * 2, + width, point( offsetX, VIEW_OFFSET_Y ) ); + w_item_info = catacurses::newwin( iInfoHeight, width, + point( offsetX, TERMY - iInfoHeight - VIEW_OFFSET_Y ) ); + + if( activeItem ) { + centerlistview( active_pos, width ); + } + + ui.position( point( offsetX, VIEW_OFFSET_Y ), point( width, TERMY - VIEW_OFFSET_Y * 2 ) ); + } ); + ui.mark_resize(); // use previously selected sorting method bool sort_radius = uistate.list_item_sort != 2; @@ -7204,15 +7236,10 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) u.view_offset = tripoint_zero; int iActive = 0; // Item index that we're looking at - const int iMaxRows = TERMY - iInfoHeight - 2 - VIEW_OFFSET_Y * 2; - int iStartPos = 0; - tripoint active_pos; - cata::optional iLastActive; bool refilter = true; int page_num = 0; int iCatSortNum = 0; int iScrollPos = 0; - map_item_stack *activeItem = nullptr; std::map mSortCategory; std::string action; @@ -7236,15 +7263,135 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) ctxt.register_action( "SORT" ); ctxt.register_action( "TRAVEL_TO" ); - // FIXME: temporarily disable redrawing of lower UIs before this UI is migrated to `ui_adaptor` - ui_adaptor ui( ui_adaptor::disable_uis_below {} ); + cata::optional filter_type; + + ui.on_redraw( [&]( const ui_adaptor & ) { + reset_item_list_state( w_items_border, iInfoHeight, sort_radius ); + + if( ground_items.empty() ) { + wrefresh( w_items_border ); + mvwprintz( w_items, point( 2, 10 ), c_white, _( "You don't see any items around you!" ) ); + } else { + int iStartPos = 0; + werase( w_items ); + calcStartPos( iStartPos, iActive, iMaxRows, iItemNum ); + int iNum = 0; + bool high = false; + bool low = false; + int index = 0; + int iCatSortOffset = 0; + + for( int i = 0; i < iStartPos; i++ ) { + if( !mSortCategory[i].empty() ) { + iNum++; + } + } + for( auto iter = filtered_items.begin(); iter != filtered_items.end(); ++index ) { + if( highPEnd > 0 && index < highPEnd + iCatSortOffset ) { + high = true; + low = false; + } else if( index >= lowPStart + iCatSortOffset ) { + high = false; + low = true; + } else { + high = false; + low = false; + } + + if( iNum >= iStartPos && iNum < iStartPos + ( iMaxRows > iItemNum ? iItemNum : iMaxRows ) ) { + int iThisPage = 0; + if( !mSortCategory[iNum].empty() ) { + iCatSortOffset++; + mvwprintz( w_items, point( 1, iNum - iStartPos ), c_magenta, mSortCategory[iNum] ); + } else { + if( iNum == iActive ) { + iThisPage = page_num; + } + std::string sText; + if( iter->vIG.size() > 1 ) { + sText += string_format( "[%d/%d] (%d) ", iThisPage + 1, iter->vIG.size(), iter->totalcount ); + } + sText += iter->example->tname(); + if( iter->vIG[iThisPage].count > 1 ) { + sText += string_format( "[%d]", iter->vIG[iThisPage].count ); + } + + nc_color col = c_light_green; + if( iNum != iActive ) { + if( high ) { + col = c_yellow; + } else if( low ) { + col = c_red; + } else { + col = iter->example->color_in_inventory(); + } + } + trim_and_print( w_items, point( 1, iNum - iStartPos ), width - 9, col, sText ); + const int numw = iItemNum > 9 ? 2 : 1; + const int x = iter->vIG[iThisPage].pos.x; + const int y = iter->vIG[iThisPage].pos.y; + mvwprintz( w_items, point( width - 6 - numw, iNum - iStartPos ), + iNum == iActive ? c_light_green : c_light_gray, + "%*d %s", numw, rl_dist( point_zero, point( x, y ) ), + direction_name_short( direction_from( point_zero, point( x, y ) ) ) ); + ++iter; + } + } else { + ++iter; + } + iNum++; + } + iNum = 0; + for( int i = 0; i < iActive; i++ ) { + if( !mSortCategory[i].empty() ) { + iNum++; + } + } + mvwprintz( w_items_border, point( ( width - 9 ) / 2 + ( iItemNum > 9 ? 0 : 1 ), 0 ), + c_light_green, " %*d", iItemNum > 9 ? 2 : 1, iItemNum > 0 ? iActive - iNum + 1 : 0 ); + wprintz( w_items_border, c_white, " / %*d ", iItemNum > 9 ? 2 : 1, iItemNum - iCatSortNum ); + werase( w_item_info ); + + if( iItemNum > 0 && activeItem ) { + std::vector vThisItem; + std::vector vDummy; + activeItem->example->info( true, vThisItem ); + + item_info_data dummy( "", "", vThisItem, vDummy, iScrollPos ); + dummy.without_getch = true; + dummy.without_border = true; + + draw_item_info( w_item_info, dummy ); + } + draw_scrollbar( w_items_border, iActive, iMaxRows, iItemNum, point_south ); + wrefresh( w_items_border ); + } + + const bool bDrawLeft = ground_items.empty() || filtered_items.empty() || !activeItem || filter_type; + draw_custom_border( w_item_info, bDrawLeft, true, true, true, LINE_XXXO, LINE_XOXX, true, true ); + + if( iItemNum > 0 && activeItem ) { + // print info window title: < item name > + mvwprintw( w_item_info, point( 2, 0 ), "< " ); + trim_and_print( w_item_info, point( 4, 0 ), width - 8, activeItem->example->color_in_inventory(), + activeItem->example->display_name() ); + wprintw( w_item_info, " >" ); + } + + wrefresh( w_items ); + wrefresh( w_item_info ); + + if( filter_type ) { + draw_item_filter_rules( w_item_info, 0, iInfoHeight - 1, filter_type.value() ); + } + } ); do { - if( action == "COMPARE" ) { + if( action == "COMPARE" && activeItem ) { game_menus::inv::compare( u, active_pos ); - refresh_all(); } else if( action == "FILTER" ) { - draw_item_filter_rules( w_item_info, 0, iInfoHeight - 1, item_filter_type::FILTER ); + filter_type = item_filter_type::FILTER; + ui.invalidate_ui(); string_input_popup() .title( _( "Filter:" ) ) .width( 55 ) @@ -7255,10 +7402,10 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) refilter = true; addcategory = !sort_radius; uistate.list_item_filter_active = !sFilter.empty(); + filter_type = cata::nullopt; } else if( action == "RESET_FILTER" ) { sFilter.clear(); filtered_items = ground_items; - iLastActive.reset(); refilter = true; uistate.list_item_filter_active = false; addcategory = !sort_radius; @@ -7271,11 +7418,12 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) vDummy ); info_data.handle_scrolling = true; - draw_item_info( 0, width - 5, 0, TERMY - VIEW_OFFSET_Y * 2, info_data ); - // wait until the user presses a key to wipe the screen - iLastActive.reset(); + draw_item_info( [&]() -> catacurses::window { + return catacurses::newwin( TERMY - VIEW_OFFSET_Y * 2, width - 5, point_zero ); + }, info_data ); } else if( action == "PRIORITY_INCREASE" ) { - draw_item_filter_rules( w_item_info, 0, iInfoHeight - 1, item_filter_type::HIGH_PRIORITY ); + filter_type = item_filter_type::HIGH_PRIORITY; + ui.invalidate_ui(); list_item_upvote = string_input_popup() .title( _( "High Priority:" ) ) .width( 55 ) @@ -7287,8 +7435,10 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) refilter = true; addcategory = !sort_radius; uistate.list_item_priority_active = !list_item_upvote.empty(); + filter_type = cata::nullopt; } else if( action == "PRIORITY_DECREASE" ) { - draw_item_filter_rules( w_item_info, 0, iInfoHeight - 1, item_filter_type::LOW_PRIORITY ); + filter_type = item_filter_type::LOW_PRIORITY; + ui.invalidate_ui(); list_item_downvote = string_input_popup() .title( _( "Low Priority:" ) ) .width( 55 ) @@ -7300,6 +7450,7 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) refilter = true; addcategory = !sort_radius; uistate.list_item_downvote_active = !list_item_downvote.empty(); + filter_type = cata::nullopt; } else if( action == "SORT" ) { if( sort_radius ) { sort_radius = false; @@ -7315,7 +7466,7 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) mSortCategory.clear(); refilter = true; - } else if( action == "TRAVEL_TO" ) { + } else if( action == "TRAVEL_TO" && activeItem ) { if( !u.sees( u.pos() + active_pos ) ) { add_msg( _( "You can't see that destination." ) ); } @@ -7342,7 +7493,6 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) lowPStart = list_filter_low_priority( filtered_items, highPEnd, list_item_downvote ); iActive = 0; page_num = 0; - iLastActive.reset(); iItemNum = filtered_items.size(); } @@ -7372,12 +7522,7 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) iItemNum = static_cast( filtered_items.size() ) + iCatSortNum; } - reset_item_list_state( w_items_border, iInfoHeight, sort_radius ); - - if( action == "HELP_KEYBINDINGS" ) { - draw_ter(); - wrefresh( w_terrain ); - } else if( action == "UP" ) { + if( action == "UP" ) { do { iActive--; @@ -7411,133 +7556,46 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) iScrollPos++; } else if( action == "NEXT_TAB" || action == "PREV_TAB" ) { u.view_offset = stored_view_offset; + trail_start = trail_end = cata::nullopt; + invalidate_main_ui_adaptor(); return game::vmenu_ret::CHANGE_TAB; } - if( ground_items.empty() ) { - reset_item_list_state( w_items_border, iInfoHeight, sort_radius ); - wrefresh( w_items_border ); - mvwprintz( w_items, point( 2, 10 ), c_white, _( "You don't see any items around you!" ) ); - } else { - werase( w_items ); - calcStartPos( iStartPos, iActive, iMaxRows, iItemNum ); - int iNum = 0; - active_pos = tripoint_zero; - bool high = false; - bool low = false; - int index = 0; - int iCatSortOffset = 0; - - for( int i = 0; i < iStartPos; i++ ) { - if( !mSortCategory[i].empty() ) { - iNum++; - } - } - for( auto iter = filtered_items.begin(); iter != filtered_items.end(); ++index ) { - if( highPEnd > 0 && index < highPEnd + iCatSortOffset ) { - high = true; - low = false; - } else if( index >= lowPStart + iCatSortOffset ) { - high = false; - low = true; - } else { - high = false; - low = false; - } - - if( iNum >= iStartPos && iNum < iStartPos + ( iMaxRows > iItemNum ? iItemNum : iMaxRows ) ) { - int iThisPage = 0; - if( !mSortCategory[iNum].empty() ) { - iCatSortOffset++; - mvwprintz( w_items, point( 1, iNum - iStartPos ), c_magenta, mSortCategory[iNum] ); - } else { - if( iNum == iActive ) { - iThisPage = page_num; - active_pos = iter->vIG[iThisPage].pos; - activeItem = &( *iter ); - } - std::string sText; - if( iter->vIG.size() > 1 ) { - sText += string_format( "[%d/%d] (%d) ", iThisPage + 1, iter->vIG.size(), iter->totalcount ); - } - sText += iter->example->tname(); - if( iter->vIG[iThisPage].count > 1 ) { - sText += string_format( "[%d]", iter->vIG[iThisPage].count ); - } + active_pos = tripoint_zero; + activeItem = nullptr; - nc_color col = c_light_green; - if( iNum != iActive ) { - if( high ) { - col = c_yellow; - } else if( low ) { - col = c_red; - } else { - col = iter->example->color_in_inventory(); - } - } - trim_and_print( w_items, point( 1, iNum - iStartPos ), width - 9, col, sText ); - const int numw = iItemNum > 9 ? 2 : 1; - const int x = iter->vIG[iThisPage].pos.x; - const int y = iter->vIG[iThisPage].pos.y; - mvwprintz( w_items, point( width - 6 - numw, iNum - iStartPos ), - iNum == iActive ? c_light_green : c_light_gray, - "%*d %s", numw, rl_dist( point_zero, point( x, y ) ), - direction_name_short( direction_from( point_zero, point( x, y ) ) ) ); - ++iter; - } - } else { + if( mSortCategory[iActive].empty() ) { + auto iter = filtered_items.begin(); + for( int iNum = 0; iter != filtered_items.end() && iNum < iActive; iNum++ ) { + if( mSortCategory[iNum].empty() ) { ++iter; } - iNum++; } - iNum = 0; - for( int i = 0; i < iActive; i++ ) { - if( !mSortCategory[i].empty() ) { - iNum++; - } - } - mvwprintz( w_items_border, point( ( width - 9 ) / 2 + ( iItemNum > 9 ? 0 : 1 ), 0 ), - c_light_green, " %*d", iItemNum > 9 ? 2 : 1, iItemNum > 0 ? iActive - iNum + 1 : 0 ); - wprintz( w_items_border, c_white, " / %*d ", iItemNum > 9 ? 2 : 1, iItemNum - iCatSortNum ); - werase( w_item_info ); - - if( iItemNum > 0 ) { - std::vector vThisItem; - std::vector vDummy; - activeItem->example->info( true, vThisItem ); - - item_info_data dummy( "", "", vThisItem, vDummy, iScrollPos ); - dummy.without_getch = true; - dummy.without_border = true; - - draw_item_info( w_item_info, dummy ); - - iLastActive.emplace( active_pos ); - centerlistview( active_pos, width ); - draw_trail_to_square( active_pos, true ); + if( iter != filtered_items.end() ) { + active_pos = iter->vIG[page_num].pos; + activeItem = &( *iter ); } - draw_scrollbar( w_items_border, iActive, iMaxRows, iItemNum, point_south ); - wrefresh( w_items_border ); } - const bool bDrawLeft = ground_items.empty() || filtered_items.empty(); - draw_custom_border( w_item_info, bDrawLeft, true, true, true, LINE_XXXO, LINE_XOXX, true, true ); - - if( iItemNum > 0 ) { - // print info window title: < item name > - mvwprintw( w_item_info, point( 2, 0 ), "< " ); - trim_and_print( w_item_info, point( 4, 0 ), width - 8, activeItem->example->color_in_inventory(), - activeItem->example->display_name() ); - wprintw( w_item_info, " >" ); + if( activeItem ) { + centerlistview( active_pos, width ); + trail_start = u.pos(); + trail_end = u.pos() + active_pos; + trail_end_x = true; + } else { + u.view_offset = stored_view_offset; + trail_start = trail_end = cata::nullopt; } + invalidate_main_ui_adaptor(); + + ui_manager::redraw(); - wrefresh( w_items ); - wrefresh( w_item_info ); - catacurses::refresh(); action = ctxt.handle_input(); } while( action != "QUIT" ); u.view_offset = stored_view_offset; + trail_start = trail_end = cata::nullopt; + invalidate_main_ui_adaptor(); return game::vmenu_ret::QUIT; } @@ -7545,18 +7603,44 @@ game::vmenu_ret game::list_monsters( const std::vector &monster_list { const int iInfoHeight = 15; const int width = 45; - const int offsetX = TERMX - VIEW_OFFSET_X - width; //VIEW_OFFSET_X; - const int iMaxRows = TERMY - iInfoHeight - VIEW_OFFSET_Y * 2 - 1; + int offsetX = 0; + int iMaxRows = 0; - catacurses::window w_monsters = catacurses::newwin( iMaxRows, width - 2, point( offsetX + 1, - VIEW_OFFSET_Y + 1 ) ); - catacurses::window w_monsters_border = catacurses::newwin( iMaxRows + 1, width, point( offsetX, - VIEW_OFFSET_Y ) ); + catacurses::window w_monsters; + catacurses::window w_monsters_border; + catacurses::window w_monster_info; + catacurses::window w_monster_info_border; - catacurses::window w_monster_info = catacurses::newwin( iInfoHeight - 2, width - 2, - point( offsetX + 1, TERMY - iInfoHeight - VIEW_OFFSET_Y + 1 ) ); - catacurses::window w_monster_info_border = catacurses::newwin( iInfoHeight, width, point( offsetX, - TERMY - iInfoHeight - VIEW_OFFSET_Y ) ); + Creature *cCurMon = nullptr; + tripoint iActivePos; + + bool hide_ui = false; + + ui_adaptor ui; + ui.on_screen_resize( [&]( ui_adaptor & ui ) { + if( hide_ui ) { + ui.position( point_zero, point_zero ); + } else { + offsetX = TERMX - VIEW_OFFSET_X - width; + iMaxRows = TERMY - iInfoHeight - VIEW_OFFSET_Y * 2 - 1; + + w_monsters = catacurses::newwin( iMaxRows, width - 2, point( offsetX + 1, + VIEW_OFFSET_Y + 1 ) ); + w_monsters_border = catacurses::newwin( iMaxRows + 1, width, point( offsetX, + VIEW_OFFSET_Y ) ); + w_monster_info = catacurses::newwin( iInfoHeight - 2, width - 2, + point( offsetX + 1, TERMY - iInfoHeight - VIEW_OFFSET_Y + 1 ) ); + w_monster_info_border = catacurses::newwin( iInfoHeight, width, point( offsetX, + TERMY - iInfoHeight - VIEW_OFFSET_Y ) ); + + if( cCurMon ) { + centerlistview( iActivePos, width ); + } + + ui.position( point( offsetX, VIEW_OFFSET_Y ), point( width, TERMY - VIEW_OFFSET_Y * 2 ) ); + } + } ); + ui.mark_resize(); const int max_gun_range = u.weapon.gun_range( &u ); @@ -7564,16 +7648,6 @@ game::vmenu_ret game::list_monsters( const std::vector &monster_list u.view_offset = tripoint_zero; int iActive = 0; // monster index that we're looking at - int iStartPos = 0; - cata::optional iLastActivePos; - Creature *cCurMon = nullptr; - - draw_custom_border( w_monsters_border, true, true, true, true, true, true, LINE_XOXO, LINE_XOXO ); - draw_custom_border( w_monster_info_border, true, true, true, true, LINE_XXXO, LINE_XOXX, true, - true ); - - mvwprintz( w_monsters_border, point( 2, 0 ), c_light_green, " " ); - wprintz( w_monsters_border, c_white, _( "Monsters" ) ); std::string action; input_context ctxt( "LIST_MONSTERS" ); @@ -7601,17 +7675,179 @@ game::vmenu_ret game::list_monsters( const std::vector &monster_list } } - // FIXME: temporarily disable redrawing of lower UIs before this UI is migrated to `ui_adaptor` - ui_adaptor ui( ui_adaptor::disable_uis_below {} ); + ui.on_redraw( [&]( const ui_adaptor & ) { + if( !hide_ui ) { + draw_custom_border( w_monsters_border, true, true, true, true, true, true, LINE_XOXO, LINE_XOXO ); + draw_custom_border( w_monster_info_border, true, true, true, true, LINE_XXXO, LINE_XOXX, true, + true ); + + mvwprintz( w_monsters_border, point( 2, 0 ), c_light_green, " " ); + wprintz( w_monsters_border, c_white, _( "Monsters" ) ); + + if( monster_list.empty() ) { + werase( w_monsters ); + mvwprintz( w_monsters, point( 2, iMaxRows / 3 ), c_white, + _( "You don't see any monsters around you!" ) ); + } else { + werase( w_monsters ); + + const int iNumMonster = monster_list.size(); + const int iMenuSize = monster_list.size() + mSortCategory.size(); + + const int numw = iNumMonster > 999 ? 4 : + iNumMonster > 99 ? 3 : + iNumMonster > 9 ? 2 : 1; + + // given the currently selected monster iActive. get the selected row + int iSelPos = iActive; + for( auto &ia : mSortCategory ) { + int index = ia.first; + if( index <= iSelPos ) { + ++iSelPos; + } else { + break; + } + } + int iStartPos = 0; + // use selected row get the start row + calcStartPos( iStartPos, iSelPos, iMaxRows - 1, iMenuSize ); + + // get first visible monster and category + int iCurMon = iStartPos; + auto CatSortIter = mSortCategory.cbegin(); + while( CatSortIter != mSortCategory.cend() && CatSortIter->first < iStartPos ) { + ++CatSortIter; + --iCurMon; + } + + const auto endY = std::min( iMaxRows - 1, iMenuSize ); + for( int y = 0; y < endY; ++y ) { + if( CatSortIter != mSortCategory.cend() ) { + const int iCurPos = iStartPos + y; + const int iCatPos = CatSortIter->first; + if( iCurPos == iCatPos ) { + const std::string cat_name = Creature::get_attitude_ui_data( + CatSortIter->second ).first.translated(); + mvwprintz( w_monsters, point( 1, y ), c_magenta, cat_name ); + ++CatSortIter; + continue; + } + } + // select current monster + const auto critter = monster_list[iCurMon]; + const bool selected = iCurMon == iActive; + ++iCurMon; + if( critter->sees( g->u ) ) { + mvwprintz( w_monsters, point( 0, y ), c_yellow, "!" ); + } + bool is_npc = false; + const monster *m = dynamic_cast( critter ); + const npc *p = dynamic_cast( critter ); + nc_color name_color = critter->basic_symbol_color(); + + if( selected ) { + name_color = hilite( name_color ); + } + + if( m != nullptr ) { + trim_and_print( w_monsters, point( 1, y ), width - 26, name_color, m->name() ); + } else { + trim_and_print( w_monsters, point( 1, y ), width - 26, name_color, critter->disp_name() ); + is_npc = true; + } + + if( selected && !get_safemode().empty() ) { + const std::string monName = is_npc ? get_safemode().npc_type_name() : m->name(); + + std::string sSafemode; + if( get_safemode().has_rule( monName, Creature::A_ANY ) ) { + sSafemode = _( "emove from safemode Blacklist" ); + } else { + sSafemode = _( "dd to safemode Blacklist" ); + } + + shortcut_print( w_monsters, point( 2, getmaxy( w_monsters ) - 1 ), + c_white, c_light_green, sSafemode ); + } + + nc_color color = c_white; + std::string sText; + + if( m != nullptr ) { + m->get_HP_Bar( color, sText ); + } else { + std::tie( sText, color ) = + ::get_hp_bar( critter->get_hp(), critter->get_hp_max(), false ); + } + mvwprintz( w_monsters, point( width - 25, y ), color, sText ); + + if( m != nullptr ) { + const auto att = m->get_attitude(); + sText = att.first; + color = att.second; + } else if( p != nullptr ) { + sText = npc_attitude_name( p->get_attitude() ); + color = p->symbol_color(); + } + mvwprintz( w_monsters, point( width - 19, y ), color, sText ); + + const int mon_dist = rl_dist( u.pos(), critter->pos() ); + const int numd = mon_dist > 999 ? 4 : + mon_dist > 99 ? 3 : + mon_dist > 9 ? 2 : 1; + + trim_and_print( w_monsters, point( width - ( 8 + numd ), y ), 6 + numd, + selected ? c_light_green : c_light_gray, + "%*d %s", + numd, mon_dist, + direction_name_short( direction_from( u.pos(), critter->pos() ) ) ); + } + + mvwprintz( w_monsters_border, point( ( width / 2 ) - numw - 2, 0 ), c_light_green, " %*d", numw, + iActive + 1 ); + wprintz( w_monsters_border, c_white, " / %*d ", numw, static_cast( monster_list.size() ) ); + + werase( w_monster_info ); + if( cCurMon ) { + cCurMon->print_info( w_monster_info, 1, iInfoHeight - 3, 1 ); + } + + draw_custom_border( w_monster_info_border, true, true, true, true, LINE_XXXO, LINE_XOXX, true, + true ); + + if( bVMonsterLookFire ) { + mvwprintw( w_monster_info_border, point_east, "< " ); + wprintz( w_monster_info_border, c_light_green, ctxt.press_x( "look" ) ); + wprintz( w_monster_info_border, c_light_gray, " %s", _( "to look around" ) ); + + if( cCurMon && rl_dist( u.pos(), cCurMon->pos() ) <= max_gun_range ) { + wprintw( w_monster_info_border, " " ); + wprintz( w_monster_info_border, c_light_green, ctxt.press_x( "fire" ) ); + wprintz( w_monster_info_border, c_light_gray, " %s", _( "to shoot" ) ); + } + wprintw( w_monster_info_border, " >" ); + } + + draw_scrollbar( w_monsters_border, iActive, iMaxRows, static_cast( monster_list.size() ), + point_south ); + } + + wrefresh( w_monsters_border ); + wrefresh( w_monster_info_border ); + wrefresh( w_monsters ); + wrefresh( w_monster_info ); + } + } ); do { - if( action == "HELP_KEYBINDINGS" ) { - draw_ter(); - wrefresh( w_terrain ); - } else if( action == "UP" ) { + if( action == "UP" ) { iActive--; if( iActive < 0 ) { - iActive = static_cast( monster_list.size() ) - 1; + if( monster_list.empty() ) { + iActive = 0; + } else { + iActive = static_cast( monster_list.size() ) - 1; + } } } else if( action == "DOWN" ) { iActive++; @@ -7620,6 +7856,8 @@ game::vmenu_ret game::list_monsters( const std::vector &monster_list } } else if( action == "NEXT_TAB" || action == "PREV_TAB" ) { u.view_offset = stored_view_offset; + trail_start = trail_end = cata::nullopt; + invalidate_main_ui_adaptor(); return game::vmenu_ret::CHANGE_TAB; } else if( action == "SAFEMODE_BLACKLIST_REMOVE" ) { const auto m = dynamic_cast( cCurMon ); @@ -7637,177 +7875,44 @@ game::vmenu_ret game::list_monsters( const std::vector &monster_list RULE_BLACKLISTED ); } } else if( action == "look" ) { - iLastActivePos = look_around(); + hide_ui = true; + ui.mark_resize(); + look_around(); + hide_ui = false; + ui.mark_resize(); } else if( action == "fire" ) { if( cCurMon != nullptr && rl_dist( u.pos(), cCurMon->pos() ) <= max_gun_range ) { u.last_target = shared_from( *cCurMon ); u.view_offset = stored_view_offset; + trail_start = trail_end = cata::nullopt; + invalidate_main_ui_adaptor(); return game::vmenu_ret::FIRE; } } - if( monster_list.empty() ) { - mvwprintz( w_monsters, point( 2, iMaxRows / 3 ), c_white, - _( "You don't see any monsters around you!" ) ); - } else { - werase( w_monsters ); - - const int iNumMonster = monster_list.size(); - const int iMenuSize = monster_list.size() + mSortCategory.size(); - - const int numw = iNumMonster > 999 ? 4 : - iNumMonster > 99 ? 3 : - iNumMonster > 9 ? 2 : 1; - - // given the currently selected monster iActive. get the selected row - int iSelPos = iActive; - for( auto &ia : mSortCategory ) { - int index = ia.first; - if( index <= iSelPos ) { - ++iSelPos; - } else { - break; - } - } - // use selected row get the start row - calcStartPos( iStartPos, iSelPos, iMaxRows - 1, iMenuSize ); - - // get first visible monster and category - int iCurMon = iStartPos; - auto CatSortIter = mSortCategory.cbegin(); - while( CatSortIter != mSortCategory.cend() && CatSortIter->first < iStartPos ) { - ++CatSortIter; - --iCurMon; - } - - const auto endY = std::min( iMaxRows - 1, iMenuSize ); - for( int y = 0; y < endY; ++y ) { - if( CatSortIter != mSortCategory.cend() ) { - const int iCurPos = iStartPos + y; - const int iCatPos = CatSortIter->first; - if( iCurPos == iCatPos ) { - const std::string cat_name = Creature::get_attitude_ui_data( - CatSortIter->second ).first.translated(); - mvwprintz( w_monsters, point( 1, y ), c_magenta, cat_name ); - ++CatSortIter; - continue; - } - } - // select current monster - const auto critter = monster_list[iCurMon]; - const bool selected = iCurMon == iActive; - ++iCurMon; - if( critter->sees( g->u ) ) { - mvwprintz( w_monsters, point( 0, y ), c_yellow, "!" ); - } - bool is_npc = false; - const monster *m = dynamic_cast( critter ); - const npc *p = dynamic_cast( critter ); - nc_color name_color = critter->basic_symbol_color(); - - if( selected ) { - name_color = hilite( name_color ); - } - - if( m != nullptr ) { - trim_and_print( w_monsters, point( 1, y ), width - 26, name_color, m->name() ); - } else { - trim_and_print( w_monsters, point( 1, y ), width - 26, name_color, critter->disp_name() ); - is_npc = true; - } - - if( selected && !get_safemode().empty() ) { - const std::string monName = is_npc ? get_safemode().npc_type_name() : m->name(); - - std::string sSafemode; - if( get_safemode().has_rule( monName, Creature::A_ANY ) ) { - sSafemode = _( "emove from safemode Blacklist" ); - } else { - sSafemode = _( "dd to safemode Blacklist" ); - } - - shortcut_print( w_monsters, point( 2, getmaxy( w_monsters ) - 1 ), - c_white, c_light_green, sSafemode ); - } - - nc_color color = c_white; - std::string sText; - - if( m != nullptr ) { - m->get_HP_Bar( color, sText ); - } else { - std::tie( sText, color ) = - ::get_hp_bar( critter->get_hp(), critter->get_hp_max(), false ); - } - mvwprintz( w_monsters, point( width - 25, y ), color, sText ); - - if( m != nullptr ) { - const auto att = m->get_attitude(); - sText = att.first; - color = att.second; - } else if( p != nullptr ) { - sText = npc_attitude_name( p->get_attitude() ); - color = p->symbol_color(); - } - mvwprintz( w_monsters, point( width - 19, y ), color, sText ); - - const int mon_dist = rl_dist( u.pos(), critter->pos() ); - const int numd = mon_dist > 999 ? 4 : - mon_dist > 99 ? 3 : - mon_dist > 9 ? 2 : 1; - - trim_and_print( w_monsters, point( width - ( 8 + numd ), y ), 6 + numd, - selected ? c_light_green : c_light_gray, - "%*d %s", - numd, mon_dist, - direction_name_short( direction_from( u.pos(), critter->pos() ) ) ); - } - - mvwprintz( w_monsters_border, point( ( width / 2 ) - numw - 2, 0 ), c_light_green, " %*d", numw, - iActive + 1 ); - wprintz( w_monsters_border, c_white, " / %*d ", numw, static_cast( monster_list.size() ) ); - + if( iActive >= 0 && static_cast( iActive ) < monster_list.size() ) { cCurMon = monster_list[iActive]; - - werase( w_monster_info ); - cCurMon->print_info( w_monster_info, 1, iInfoHeight - 3, 1 ); - - draw_custom_border( w_monster_info_border, true, true, true, true, LINE_XXXO, LINE_XOXX, true, - true ); - - if( bVMonsterLookFire ) { - mvwprintw( w_monster_info_border, point_east, "< " ); - wprintz( w_monster_info_border, c_light_green, ctxt.press_x( "look" ) ); - wprintz( w_monster_info_border, c_light_gray, " %s", _( "to look around" ) ); - - if( rl_dist( u.pos(), cCurMon->pos() ) <= max_gun_range ) { - wprintw( w_monster_info_border, " " ); - wprintz( w_monster_info_border, c_light_green, ctxt.press_x( "fire" ) ); - wprintz( w_monster_info_border, c_light_gray, " %s", _( "to shoot" ) ); - } - wprintw( w_monster_info_border, " >" ); - } - - // Only redraw trail/terrain if x/y position changed or if keybinding menu erased it - tripoint iActivePos = cCurMon->pos() - u.pos(); - iLastActivePos.emplace( iActivePos ); + iActivePos = cCurMon->pos() - u.pos(); centerlistview( iActivePos, width ); - draw_trail_to_square( iActivePos, false ); - - draw_scrollbar( w_monsters_border, iActive, iMaxRows, static_cast( monster_list.size() ), - point_south ); + trail_start = u.pos(); + trail_end = cCurMon->pos(); + trail_end_x = false; + } else { + cCurMon = nullptr; + iActivePos = tripoint_zero; + u.view_offset = stored_view_offset; + trail_start = trail_end = cata::nullopt; } + invalidate_main_ui_adaptor(); - wrefresh( w_monsters_border ); - wrefresh( w_monster_info_border ); - wrefresh( w_monsters ); - wrefresh( w_monster_info ); - catacurses::refresh(); + ui_manager::redraw(); action = ctxt.handle_input(); } while( action != "QUIT" ); u.view_offset = stored_view_offset; + trail_start = trail_end = cata::nullopt; + invalidate_main_ui_adaptor(); return game::vmenu_ret::QUIT; } diff --git a/src/game.h b/src/game.h index 105e638d3b615..23e221fddae9a 100644 --- a/src/game.h +++ b/src/game.h @@ -235,6 +235,9 @@ class game bool zone_blink = false; bool zone_cursor = false; bool is_looking = false; + cata::optional trail_start; + cata::optional trail_end; + bool trail_end_x = false; public: // when force_redraw is true, redraw all panel instead of just animated panels diff --git a/src/iexamine.cpp b/src/iexamine.cpp index b1ced8b7ee9ee..a5b59cf751613 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -2725,7 +2725,7 @@ void iexamine::fireplace( player &p, const tripoint &examp ) return; } case 2: { - if( g->m.add_field( examp, fd_fire, 1 ) ) { + if( !g->m.get_field( examp, fd_fire ) && g->m.add_field( examp, fd_fire, 1 ) ) { p.mod_power_level( -bio_lighter->power_activate ); p.mod_moves( -to_moves( 1_seconds ) ); } else { diff --git a/src/item.cpp b/src/item.cpp index 14271213ac0b0..6450fecf416ec 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -8708,8 +8708,7 @@ void item::process_temperature_rot( float insulation, const tripoint &pos, const weather_generator &wgen = g->weather.get_cur_weather_gen(); const unsigned int seed = g->get_seed(); - const tripoint &local = g->m.getlocal( pos ); - int local_mod = g->new_game ? 0 : g->m.get_temperature( local ); + int local_mod = g->new_game ? 0 : g->m.get_temperature( pos ); int enviroment_mod; // Toilets and vending machines will try to get the heat radiation and convection during mapgen and segfault. diff --git a/src/json.h b/src/json.h index fdedbb6750fae..61844e9593b4f 100644 --- a/src/json.h +++ b/src/json.h @@ -1412,7 +1412,7 @@ void deserialize( cata::optional &obj, JsonIn &jsin ) obj.reset(); } else { obj.emplace(); - jsin.read( *obj ); + jsin.read( *obj, true ); } } diff --git a/src/map.cpp b/src/map.cpp index 3fc1796975df3..68933a1516135 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -6735,9 +6735,8 @@ void map::loadn( const tripoint &grid, const bool update_vehicles ) template void map::remove_rotten_items( Container &items, const tripoint &pnt ) { - const tripoint abs_pnt = getabs( pnt ); for( auto it = items.begin(); it != items.end(); ) { - if( it->has_rotten_away( abs_pnt ) ) { + if( it->has_rotten_away( pnt ) ) { if( it->is_comestible() ) { rotten_item_spawn( *it, pnt ); } diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 59728e06b2a3f..10d3942450bd8 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -3707,10 +3707,9 @@ void rescale_tileset( int size ) game_ui::init_ui(); } -window_dimensions get_window_dimensions( const catacurses::window &win ) +static window_dimensions get_window_dimensions( const catacurses::window &win, + const point &pos, const point &size ) { - cata_cursesport::WINDOW *const pwin = win.get(); - window_dimensions dim; if( use_tiles && g && win == g->w_terrain ) { // tiles might have different dimensions than standard font @@ -3731,9 +3730,15 @@ window_dimensions get_window_dimensions( const catacurses::window &win ) // multiplied by the user's specified scaling factor regardless of whether tiles are in use dim.scaled_font_size *= get_scaling_factor(); - dim.window_pos_cell = pwin->pos; - dim.window_size_cell.x = pwin->width; - dim.window_size_cell.y = pwin->height; + if( win ) { + cata_cursesport::WINDOW *const pwin = win.get(); + dim.window_pos_cell = pwin->pos; + dim.window_size_cell.x = pwin->width; + dim.window_size_cell.y = pwin->height; + } else { + dim.window_pos_cell = pos; + dim.window_size_cell = size; + } // the window position is *always* in standard font dimensions! dim.window_pos_pixel = point( dim.window_pos_cell.x * fontwidth, @@ -3745,6 +3750,16 @@ window_dimensions get_window_dimensions( const catacurses::window &win ) return dim; } +window_dimensions get_window_dimensions( const catacurses::window &win ) +{ + return get_window_dimensions( win, point_zero, point_zero ); +} + +window_dimensions get_window_dimensions( const point &pos, const point &size ) +{ + return get_window_dimensions( {}, pos, size ); +} + cata::optional input_context::get_coordinates( const catacurses::window &capture_win_ ) { if( !coordinate_input_received ) { diff --git a/src/sdltiles.h b/src/sdltiles.h index 34a3e457d78c9..f67b543096379 100644 --- a/src/sdltiles.h +++ b/src/sdltiles.h @@ -39,6 +39,9 @@ struct window_dimensions { point window_size_pixel; }; window_dimensions get_window_dimensions( const catacurses::window &win ); +// Get dimensional info of an imaginary normal catacurses::window with the given +// position and size. Unlike real catacurses::window, size can be zero. +window_dimensions get_window_dimensions( const point &pos, const point &size ); #endif // TILES diff --git a/src/ui_manager.cpp b/src/ui_manager.cpp index 352c2b633d9bc..4f21019807b33 100644 --- a/src/ui_manager.cpp +++ b/src/ui_manager.cpp @@ -54,7 +54,16 @@ void ui_adaptor::position_from_window( const catacurses::window &win ) void ui_adaptor::position( const point &topleft, const point &size ) { - position_from_window( catacurses::newwin( size.y, size.x, topleft ) ); + const rectangle old_dimensions = dimensions; + // ensure position is updated before calling invalidate +#ifdef TILES + const window_dimensions dim = get_window_dimensions( topleft, size ); + dimensions = rectangle( dim.window_pos_pixel, dim.window_pos_pixel + dim.window_size_pixel ); +#else + dimensions = rectangle( topleft, topleft + size ); +#endif + invalidated = true; + ui_manager::invalidate( old_dimensions ); } void ui_adaptor::on_redraw( const redraw_callback_t &fun ) diff --git a/src/ui_manager.h b/src/ui_manager.h index 2ab2a41c24f7a..7e6c081524d6f 100644 --- a/src/ui_manager.h +++ b/src/ui_manager.h @@ -33,8 +33,8 @@ class ui_adaptor ui_adaptor &operator=( ui_adaptor &&rhs ) = delete; void position_from_window( const catacurses::window &win ); - // In effect the same as - // position_from_window( catacurses::newwin( size.y, size.x, topleft ) ); + // Set the position and size of the ui to that of an imaginary normal + // catacurses::window, except that size can be zero. // Note that `topleft` and `size` are in console cells on both tiles // and curses build. void position( const point &topleft, const point &size ); diff --git a/src/units.h b/src/units.h index c457606ceccca..9b76e51c45dce 100644 --- a/src/units.h +++ b/src/units.h @@ -700,7 +700,7 @@ T read_from_json_string( JsonIn &jsin, const std::vector