From 9e517cd6fdd45fa534c0090a483430870fb44d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9A=AE=E7=9A=AE=E5=B1=B1=E4=B8=8A=E7=9A=84=E5=92=95?= =?UTF-8?q?=E5=92=95=E2=91=A8?= Date: Wed, 22 Jun 2022 04:28:22 +0800 Subject: [PATCH] Fix some problems of thievery (#1582) * Npc react to thievery immediately * Fix for moving count_by_charges items * Do not move loot that has other owner * Some improvements * Make use of the activity actor to avoid static variable use. Inline npc::witness_thievery as well. * Just found a better way to store value. --- src/activity_actor.cpp | 38 +++++++++++++++++++++++++--------- src/activity_item_handling.cpp | 8 +++++++ src/item.cpp | 20 +++++++++++------- src/npc.h | 3 --- src/npcmove.cpp | 20 +----------------- src/npctalk_funcs.cpp | 6 ------ 6 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 64a7f90a8c6b..29402464c8ad 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -59,6 +59,8 @@ static const mtype_id mon_zombie_crawler( "mon_zombie_crawler" ); static const std::string flag_RELOAD_AND_SHOOT( "RELOAD_AND_SHOOT" ); +static const std::string has_thievery_witness( "has_thievery_witness" ); + aim_activity_actor::aim_activity_actor() { initial_view_offset = get_avatar().view_offset; @@ -779,14 +781,6 @@ void move_items_activity_actor::do_turn( player_activity &act, Character &who ) // Make a copy to be put in the destination location item newit = leftovers; - // Handle charges, quantity == 0 means move all - if( quantity != 0 && newit.count_by_charges() ) { - newit.charges = std::min( newit.charges, quantity ); - leftovers.charges -= quantity; - } else { - leftovers.charges = 0; - } - // Check that we can pick it up. if( !newit.made_of( LIQUID ) ) { // This is for hauling across zlevels, remove when going up and down stairs @@ -796,6 +790,13 @@ void move_items_activity_actor::do_turn( player_activity &act, Character &who ) } else { continue; } + // Handle charges, quantity == 0 means move all + if( quantity != 0 && newit.count_by_charges() ) { + newit.charges = std::min( newit.charges, quantity ); + leftovers.charges -= quantity; + } else { + leftovers.charges = 0; + } const tripoint src = target.position(); const int distance = src.z == dest.z ? std::max( rl_dist( src, dest ), 1 ) : 1; who.mod_moves( -pickup::cost_to_move_item( who, newit ) * distance ); @@ -843,7 +844,7 @@ std::unique_ptr move_items_activity_actor::deserialize( JsonIn & return actor.clone(); } -void pickup_activity_actor::do_turn( player_activity &, Character &who ) +void pickup_activity_actor::do_turn( player_activity &act, Character &who ) { // If we don't have target items bail out if( target_items.empty() ) { @@ -865,9 +866,20 @@ void pickup_activity_actor::do_turn( player_activity &, Character &who ) // False indicates that the player canceled pickup when met with some prompt const bool keep_going = pickup::do_pickup( target_items, autopickup ); + // Check thievey witness + npc *witness = nullptr; + if( !act.str_values.empty() && act.str_values[0] == has_thievery_witness ) { + for( npc &guy : g->all_npcs() ) { + if( guy.get_attitude() == NPCATT_RECOVER_GOODS ) { + witness = &guy; + break; + } + } + } + // If there are items left we ran out of moves, so continue the activity // Otherwise, we are done. - if( !keep_going || target_items.empty() ) { + if( !keep_going || target_items.empty() || witness ) { who.cancel_activity(); if( who.get_value( "THIEF_MODE_KEEP" ) != "YES" ) { @@ -880,6 +892,12 @@ void pickup_activity_actor::do_turn( player_activity &, Character &who ) // TODO: Move this to advanced inventory instead of hacking it in here cancel_aim_processing(); } + + if( witness ) { + witness->talk_to_u(); + // Then remove "has_thievery_witness" from the activity + act.str_values.clear(); + } } } diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index 256e4a7fb423..32350dccfc2f 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -2261,6 +2261,10 @@ void activity_on_turn_move_loot( player_activity &act, player &p ) src_veh = &vp->vehicle(); src_part = vp->part_index(); for( auto &it : src_veh->get_items( src_part ) ) { + if( !it.is_owned_by( p, true ) ) { + continue; + } + it.set_owner( p ); items.push_back( std::make_pair( &it, true ) ); } } else { @@ -2268,6 +2272,10 @@ void activity_on_turn_move_loot( player_activity &act, player &p ) src_part = -1; } for( auto &it : g->m.i_at( src_loc ) ) { + if( !it.is_owned_by( p, true ) ) { + continue; + } + it.set_owner( p ); items.push_back( std::make_pair( &it, false ) ); } diff --git a/src/item.cpp b/src/item.cpp index a9ebcb5a46dc..1401d892a8fb 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -269,6 +269,9 @@ static const std::string flag_WATER_EXTINGUISH( "WATER_EXTINGUISH" ); static const std::string flag_WET( "WET" ); static const std::string flag_WIND_EXTINGUISH( "WIND_EXTINGUISH" ); +static const std::string has_thievery_witness( "has_thievery_witness" ); +static const activity_id ACT_PICKUP( "ACT_PICKUP" ); + static const matec_id rapid_strike( "RAPID" ); class npc_class; @@ -4303,16 +4306,17 @@ void item::handle_pickup_ownership( Character &c ) } if( !witnesses.empty() ) { set_old_owner( get_owner() ); - bool guard_chosen = false; - for( npc *elem : witnesses ) { - if( elem->myclass == npc_class_id( "NC_BOUNTY_HUNTER" ) ) { - guard_chosen = true; - elem->witness_thievery( &*this ); - break; + // Make sure there is only one witness + for( npc &guy : g->all_npcs() ) { + if( guy.get_attitude() == NPCATT_RECOVER_GOODS ) { + guy.set_attitude( NPCATT_NULL ); } } - if( !guard_chosen ) { - random_entry( witnesses )->witness_thievery( &*this ); + random_entry( witnesses )->set_attitude( NPCATT_RECOVER_GOODS ); + // Notify the activity that we got a witness + if( c.activity && !c.activity.is_null() && c.activity.id() == ACT_PICKUP ) { + c.activity.str_values.clear(); + c.activity.str_values.emplace_back( has_thievery_witness ); } } set_owner( c ); diff --git a/src/npc.h b/src/npc.h index 02a7d5e218e9..d2280e87e4a0 100644 --- a/src/npc.h +++ b/src/npc.h @@ -1049,8 +1049,6 @@ class npc : public player void handle_sound( sounds::sound_t priority, const std::string &description, int heard_volume, const tripoint &spos ); - void witness_thievery( item *it ); - /* shift() works much like monster::shift(), and is called when the player moves * from one submap to an adjacent submap. It updates our position (shifting by * 12 tiles), as well as our plans. @@ -1305,7 +1303,6 @@ class npc : public player tripoint_abs_omt goal; tripoint wander_pos = tripoint_min; int wander_time = 0; - item *known_stolen_item = nullptr; // the item that the NPC wants the player to drop or barter for. /** * Location and index of the corpse we'd like to pulp (if any). */ diff --git a/src/npcmove.cpp b/src/npcmove.cpp index c1158360e07c..f9da36cc21f5 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -656,14 +656,6 @@ void npc::regen_ai_cache() auto i = std::begin( ai_cache.sound_alerts ); while( i != std::end( ai_cache.sound_alerts ) ) { if( sees( here.getlocal( i->abs_pos ) ) ) { - // if they were responding to a call for guards because of thievery - npc *const sound_source = g->critter_at( here.getlocal( i->abs_pos ) ); - if( sound_source ) { - if( my_fac == sound_source->my_fac && sound_source->known_stolen_item ) { - sound_source->known_stolen_item = nullptr; - set_attitude( NPCATT_RECOVER_GOODS ); - } - } i = ai_cache.sound_alerts.erase( i ); if( ai_cache.sound_alerts.size() == 1 ) { path.clear(); @@ -1308,16 +1300,6 @@ void npc::execute_action( npc_action action ) } } -void npc::witness_thievery( item *it ) -{ - known_stolen_item = it; - // Shopkeep is behind glass - if( myclass == npc_class_id( "NC_EVAC_SHOPKEEP" ) ) { - return; - } - set_attitude( NPCATT_RECOVER_GOODS ); -} - npc_action npc::method_of_fleeing() { if( in_vehicle ) { @@ -1941,7 +1923,7 @@ npc_action npc::address_needs( float danger ) npc_action npc::address_player() { Character &player_character = get_player_character(); - if( ( attitude == NPCATT_TALK || attitude == NPCATT_RECOVER_GOODS ) && sees( player_character ) ) { + if( ( attitude == NPCATT_TALK ) && sees( player_character ) ) { if( player_character.in_sleep_state() ) { // Leave sleeping characters alone. return npc_undecided; diff --git a/src/npctalk_funcs.cpp b/src/npctalk_funcs.cpp index 340a4978c462..48ef25cca219 100644 --- a/src/npctalk_funcs.cpp +++ b/src/npctalk_funcs.cpp @@ -837,9 +837,6 @@ void talk_function::drop_stolen_item( npc &p ) g->m.add_item_or_charges( g->u.pos(), to_drop ); } } - if( p.known_stolen_item ) { - p.known_stolen_item = nullptr; - } if( g->u.is_hauling() ) { g->u.stop_hauling(); } @@ -848,9 +845,6 @@ void talk_function::drop_stolen_item( npc &p ) void talk_function::remove_stolen_status( npc &p ) { - if( p.known_stolen_item ) { - p.known_stolen_item = nullptr; - } p.set_attitude( NPCATT_NULL ); }