diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index df986249f4b1..d339915cb3c4 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -3585,1080 +3585,1039 @@ void activity_handlers::plant_seed_finish( player_activity *act, player *p ) } else if( seed_id->seed->required_terrain_flag == flag_PLANTABLE ) { here.set( examp, t_dirt, f_plant_seed ); } else { - here.furn_set( examp, f_plant_seed ); - p->add_msg_player_or_npc( _( "You plant some %s." ), _( " plants some %s." ), - item::nname( seed_id ) ); + here.furn_set( examp, f_plant_seed ); + p->add_msg_player_or_npc( _( "You plant some %s." ), _( " plants some %s." ), + item::nname( seed_id ) ); + } + // Go back to what we were doing before + // could be player zone activity, or could be NPC multi-farming + act->set_to_null(); + resume_for_multi_activities( *p ); } - // Go back to what we were doing before - // could be player zone activity, or could be NPC multi-farming - act->set_to_null(); - resume_for_multi_activities( *p ); -} -void activity_handlers::build_do_turn( player_activity *act, player *p ) -{ - map &here = get_map(); - partial_con *pc = here.partial_con_at( here.getlocal( act->placement ) ); - // Maybe the player and the NPC are working on the same construction at the same time - if( !pc ) { - if( p->is_npc() ) { - // if player completes the work while NPC still in activity loop - p->activity->set_to_null();// = player_activity(); - p->set_moves( 0 ); - } else { - p->cancel_activity(); + void activity_handlers::build_do_turn( player_activity * act, player * p ) { + map &here = get_map(); + partial_con *pc = here.partial_con_at( here.getlocal( act->placement ) ); + // Maybe the player and the NPC are working on the same construction at the same time + if( !pc ) { + if( p->is_npc() ) { + // if player completes the work while NPC still in activity loop + p->activity->set_to_null();// = player_activity(); + p->set_moves( 0 ); + } else { + p->cancel_activity(); + } + add_msg( m_info, _( "%s did not find an unfinished construction at the activity spot." ), + p->disp_name() ); + return; } - add_msg( m_info, _( "%s did not find an unfinished construction at the activity spot." ), - p->disp_name() ); - return; - } - // if you ( or NPC ) are finishing someone else's started construction... - const construction &built = pc->id.obj(); - if( !p->has_trait( trait_DEBUG_HS ) && !p->meets_skill_requirements( built ) ) { - add_msg( m_info, _( "%s can't work on this construction anymore." ), p->disp_name() ); - p->cancel_activity(); - if( p->is_npc() ) { - p->activity->set_to_null();// = player_activity(); - p->set_moves( 0 ); + // if you ( or NPC ) are finishing someone else's started construction... + const construction &built = pc->id.obj(); + if( !p->has_trait( trait_DEBUG_HS ) && !p->meets_skill_requirements( built ) ) { + add_msg( m_info, _( "%s can't work on this construction anymore." ), p->disp_name() ); + p->cancel_activity(); + if( p->is_npc() ) { + p->activity->set_to_null();// = player_activity(); + p->set_moves( 0 ); + } + return; } - return; - } - // item_counter represents the percent progress relative to the base batch time - // stored precise to 5 decimal places ( e.g. 67.32 percent would be stored as 6732000 ) - const int old_counter = pc->counter; - - // Base moves for construction with no speed modifier or assistants - // Must ensure >= 1 so we don't divide by 0; - const double base_total_moves = std::max( 1, to_moves( built.time ) ); - // Current expected total moves, includes construction speed modifiers and assistants - const double cur_total_moves = std::max( 1, built.adjusted_time() ); - // Delta progress in moves adjusted for current crafting speed - const double delta_progress = p->get_moves() * base_total_moves / cur_total_moves; - // Current progress in moves - const double current_progress = old_counter * base_total_moves / 10000000.0 + - delta_progress; - // Current progress as a percent of base_total_moves to 2 decimal places - pc->counter = std::round( current_progress / base_total_moves * 10000000.0 ); - - p->set_moves( 0 ); - - pc->counter = std::min( pc->counter, 10000000 ); - // If construction_progress has reached 100% or more - if( pc->counter >= 10000000 ) { - // Activity is canceled in complete_construction() - complete_construction( *p ); - } -} - -void activity_handlers::tidy_up_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + // item_counter represents the percent progress relative to the base batch time + // stored precise to 5 decimal places ( e.g. 67.32 percent would be stored as 6732000 ) + const int old_counter = pc->counter; -void activity_handlers::multiple_fish_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + // Base moves for construction with no speed modifier or assistants + // Must ensure >= 1 so we don't divide by 0; + const double base_total_moves = std::max( 1, to_moves( built.time ) ); + // Current expected total moves, includes construction speed modifiers and assistants + const double cur_total_moves = std::max( 1, built.adjusted_time() ); + // Delta progress in moves adjusted for current crafting speed + const double delta_progress = p->get_moves() * base_total_moves / cur_total_moves; + // Current progress in moves + const double current_progress = old_counter * base_total_moves / 10000000.0 + + delta_progress; + // Current progress as a percent of base_total_moves to 2 decimal places + pc->counter = std::round( current_progress / base_total_moves * 10000000.0 ); -void activity_handlers::multiple_construction_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + p->set_moves( 0 ); -void activity_handlers::multiple_mine_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} - -void activity_handlers::multiple_chop_planks_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + pc->counter = std::min( pc->counter, 10000000 ); + // If construction_progress has reached 100% or more + if( pc->counter >= 10000000 ) { + // Activity is canceled in complete_construction() + complete_construction( *p ); + } + } -void activity_handlers::multiple_butcher_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + void activity_handlers::tidy_up_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); + } -void activity_handlers::vehicle_deconstruction_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + void activity_handlers::multiple_fish_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); + } -void activity_handlers::vehicle_repair_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + void activity_handlers::multiple_construction_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); + } -void activity_handlers::chop_trees_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + void activity_handlers::multiple_mine_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); + } -void activity_handlers::multiple_farm_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + void activity_handlers::multiple_chop_planks_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); + } -void activity_handlers::fetch_do_turn( player_activity *act, player *p ) -{ - generic_multi_activity_handler( *act, *p ); -} + void activity_handlers::multiple_butcher_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); + } -void activity_handlers::craft_do_turn( player_activity *act, player *p ) -{ - item *craft = &*act->targets.front(); - - // item_location::get_item() will return nullptr if the item is lost - if( !craft ) { - p->add_msg_player_or_npc( - _( "You no longer have the in progress craft in your possession. " - "You stop crafting. " - "Reactivate the in progress craft to continue crafting." ), - _( " no longer has the in progress craft in their possession. " - " stops crafting." ) ); - p->cancel_activity(); - return; + void activity_handlers::vehicle_deconstruction_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); } - if( !craft->is_craft() ) { - debugmsg( "ACT_CRAFT target '%s' is not a craft. Aborting ACT_CRAFT.", craft->tname() ); - p->cancel_activity(); - return; + void activity_handlers::vehicle_repair_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); } - if( !p->can_continue_craft( *craft ) ) { - p->cancel_activity(); - return; + void activity_handlers::chop_trees_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); } - const recipe &rec = craft->get_making(); - const tripoint bench_pos = act->coords.front(); - // Ugly - bench_type bench_t = bench_type( act->values[1] ); - const float crafting_speed = crafting_speed_multiplier( *p, *craft, bench_location{bench_t, bench_pos} ); - const int assistants = p->available_assistant_count( craft->get_making() ); - const bool is_long = act->values[0]; + void activity_handlers::multiple_farm_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); + } - if( crafting_speed <= 0.0f ) { - p->cancel_activity(); - return; + void activity_handlers::fetch_do_turn( player_activity * act, player * p ) { + generic_multi_activity_handler( *act, *p ); } - // item_counter represents the percent progress relative to the base batch time - // stored precise to 5 decimal places ( e.g. 67.32 percent would be stored as 6'732'000 ) - const int old_counter = craft->item_counter; + void activity_handlers::craft_do_turn( player_activity * act, player * p ) { + item *craft = &*act->targets.front(); - // Base moves for batch size with no speed modifier or assistants - // Must ensure >= 1 so we don't divide by 0; - const double base_total_moves = std::max( 1, rec.batch_time( craft->charges, 1.0f, 0 ) ); - // Current expected total moves, includes crafting speed modifiers and assistants - const double cur_total_moves = std::max( 1, rec.batch_time( craft->charges, crafting_speed, - assistants ) ); - // Delta progress in moves adjusted for current crafting speed - const double delta_progress = p->get_moves() * base_total_moves / cur_total_moves; - // Current progress in moves - const double current_progress = craft->item_counter * base_total_moves / 10'000'000.0 + - delta_progress; - // Current progress as a percent of base_total_moves to 2 decimal places - craft->item_counter = std::round( current_progress / base_total_moves * 10'000'000.0 ); - p->set_moves( 0 ); + // item_location::get_item() will return nullptr if the item is lost + if( !craft ) { + p->add_msg_player_or_npc( + _( "You no longer have the in progress craft in your possession. " + "You stop crafting. " + "Reactivate the in progress craft to continue crafting." ), + _( " no longer has the in progress craft in their possession. " + " stops crafting." ) ); + p->cancel_activity(); + return; + } - // This is to ensure we don't over count skill steps - craft->item_counter = std::min( craft->item_counter, 10'000'000 ); + if( !craft->is_craft() ) { + debugmsg( "ACT_CRAFT target '%s' is not a craft. Aborting ACT_CRAFT.", craft->tname() ); + p->cancel_activity(); + return; + } - // Skill and tools are gained/consumed after every 5% progress - int five_percent_steps = craft->item_counter / 500'000 - old_counter / 500'000; - if( five_percent_steps > 0 ) { - p->craft_skill_gain( *craft, five_percent_steps ); - } + if( !p->can_continue_craft( *craft ) ) { + p->cancel_activity(); + return; + } - // Unlike skill, tools are consumed once at the start and should not be consumed at the end - if( craft->item_counter >= 10'000'000 ) { - --five_percent_steps; - } + const recipe &rec = craft->get_making(); + const tripoint bench_pos = act->coords.front(); + // Ugly + bench_type bench_t = bench_type( act->values[1] ); + const float crafting_speed = crafting_speed_multiplier( *p, *craft, bench_location{bench_t, bench_pos} ); + const int assistants = p->available_assistant_count( craft->get_making() ); + const bool is_long = act->values[0]; - if( five_percent_steps > 0 ) { - if( !p->craft_consume_tools( *craft, five_percent_steps, false ) ) { - // So we don't skip over any tool comsuption - craft->item_counter -= craft->item_counter % 500000 + 1; + if( crafting_speed <= 0.0f ) { p->cancel_activity(); return; } - } - // if item_counter has reached 100% or more - if( craft->item_counter >= 10'000'000 ) { - //TODO!: CHEEKY check - item *craft_copy = craft; - p->cancel_activity(); - complete_craft( *p, *craft_copy, bench_location{bench_t, bench_pos} ); - act->targets.front()->detach(); - if( is_long ) { - if( p->making_would_work( p->lastrecipe, craft_copy->charges ) ) { - p->last_craft->execute( bench_pos ); + // item_counter represents the percent progress relative to the base batch time + // stored precise to 5 decimal places ( e.g. 67.32 percent would be stored as 6'732'000 ) + const int old_counter = craft->item_counter; + + // Base moves for batch size with no speed modifier or assistants + // Must ensure >= 1 so we don't divide by 0; + const double base_total_moves = std::max( 1, rec.batch_time( craft->charges, 1.0f, 0 ) ); + // Current expected total moves, includes crafting speed modifiers and assistants + const double cur_total_moves = std::max( 1, rec.batch_time( craft->charges, crafting_speed, + assistants ) ); + // Delta progress in moves adjusted for current crafting speed + const double delta_progress = p->get_moves() * base_total_moves / cur_total_moves; + // Current progress in moves + const double current_progress = craft->item_counter * base_total_moves / 10'000'000.0 + + delta_progress; + // Current progress as a percent of base_total_moves to 2 decimal places + craft->item_counter = std::round( current_progress / base_total_moves * 10'000'000.0 ); + p->set_moves( 0 ); + + // This is to ensure we don't over count skill steps + craft->item_counter = std::min( craft->item_counter, 10'000'000 ); + + // Skill and tools are gained/consumed after every 5% progress + int five_percent_steps = craft->item_counter / 500'000 - old_counter / 500'000; + if( five_percent_steps > 0 ) { + p->craft_skill_gain( *craft, five_percent_steps ); + } + + // Unlike skill, tools are consumed once at the start and should not be consumed at the end + if( craft->item_counter >= 10'000'000 ) { + --five_percent_steps; + } + + if( five_percent_steps > 0 ) { + if( !p->craft_consume_tools( *craft, five_percent_steps, false ) ) { + // So we don't skip over any tool comsuption + craft->item_counter -= craft->item_counter % 500000 + 1; + p->cancel_activity(); + return; } } - } else if( craft->item_counter >= craft->get_next_failure_point() ) { - bool destroy = craft->handle_craft_failure( *p ); - // If the craft needs to be destroyed, do it and stop crafting. - if( destroy ) { - p->add_msg_player_or_npc( _( "There is nothing left of the %s to craft from." ), - _( "There is nothing left of the %s was crafting." ), craft->tname() ); - act->targets.front()->detach(); + + // if item_counter has reached 100% or more + if( craft->item_counter >= 10'000'000 ) { + //TODO!: CHEEKY check + item *craft_copy = craft; p->cancel_activity(); + complete_craft( *p, *craft_copy, bench_location{bench_t, bench_pos} ); + act->targets.front()->detach(); + if( is_long ) { + if( p->making_would_work( p->lastrecipe, craft_copy->charges ) ) { + p->last_craft->execute( bench_pos ); + } + } + } else if( craft->item_counter >= craft->get_next_failure_point() ) { + bool destroy = craft->handle_craft_failure( *p ); + // If the craft needs to be destroyed, do it and stop crafting. + if( destroy ) { + p->add_msg_player_or_npc( _( "There is nothing left of the %s to craft from." ), + _( "There is nothing left of the %s was crafting." ), craft->tname() ); + act->targets.front()->detach(); + p->cancel_activity(); + } } } -} - -void activity_handlers::vibe_finish( player_activity *act, player *p ) -{ - p->add_msg_if_player( m_good, _( "You feel much better." ) ); - p->add_morale( MORALE_FEELING_GOOD, 10, 40 ); - act->set_to_null(); -} -void activity_handlers::atm_finish( player_activity *act, player * ) -{ - // ATM sets index to 0 to indicate it's finished. - if( !act->index ) { + void activity_handlers::vibe_finish( player_activity * act, player * p ) { + p->add_msg_if_player( m_good, _( "You feel much better." ) ); + p->add_morale( MORALE_FEELING_GOOD, 10, 40 ); act->set_to_null(); } -} -void activity_handlers::eat_menu_finish( player_activity *, player * ) -{ - // Only exists to keep the eat activity alive between turns - return; -} + void activity_handlers::atm_finish( player_activity * act, player * ) { + // ATM sets index to 0 to indicate it's finished. + if( !act->index ) { + act->set_to_null(); + } + } -void activity_handlers::pry_nails_do_turn( player_activity *act, player * ) -{ - sfx::play_activity_sound( "tool", "hammer", sfx::get_heard_volume( act->placement ) ); -} + void activity_handlers::eat_menu_finish( player_activity *, player * ) { + // Only exists to keep the eat activity alive between turns + return; + } -void activity_handlers::pry_nails_finish( player_activity *act, player *p ) -{ - const tripoint &pnt = act->placement; - map &here = get_map(); - const ter_id type = here.ter( pnt ); - - int nails = 0; - int boards = 0; - ter_id newter; - if( type == t_fence ) { - nails = 6; - boards = 3; - newter = t_fence_post; - p->add_msg_if_player( _( "You pry out the fence post." ) ); - } else if( type == t_window_reinforced_noglass ) { - nails = 16; - boards = 8; - newter = t_window_boarded_noglass; - p->add_msg_if_player( _( "You pry the boards from the window." ) ); - } else if( type == t_window_reinforced ) { - nails = 16; - boards = 8; - newter = t_window_boarded; - p->add_msg_if_player( _( "You pry the boards from the window." ) ); - } else if( type == t_window_boarded ) { - nails = 8; - boards = 4; - newter = t_window_frame; - p->add_msg_if_player( _( "You pry the boards from the window." ) ); - } else if( type == t_window_boarded_noglass ) { - nails = 8; - boards = 4; - newter = t_window_empty; - p->add_msg_if_player( _( "You pry the boards from the window frame." ) ); - } else if( type == t_door_boarded || type == t_door_boarded_damaged || - type == t_rdoor_boarded || type == t_rdoor_boarded_damaged || - type == t_door_boarded_peep || type == t_door_boarded_damaged_peep ) { - nails = 8; - boards = 4; - if( type == t_door_boarded ) { - newter = t_door_c; - } else if( type == t_door_boarded_damaged ) { - newter = t_door_b; - } else if( type == t_door_boarded_peep ) { - newter = t_door_c_peep; - } else if( type == t_door_boarded_damaged_peep ) { - newter = t_door_b_peep; - } else if( type == t_rdoor_boarded ) { - newter = t_rdoor_c; - } else { // if (type == t_rdoor_boarded_damaged) - newter = t_rdoor_b; - } - p->add_msg_if_player( _( "You pry the boards from the door." ) ); - } - p->practice( skill_fabrication, 1, 1 ); - here.spawn_item( p->pos(), itype_nail, 0, nails ); - here.spawn_item( p->pos(), itype_2x4, boards ); - here.ter_set( pnt, newter ); - act->set_to_null(); -} + void activity_handlers::pry_nails_do_turn( player_activity * act, player * ) { + sfx::play_activity_sound( "tool", "hammer", sfx::get_heard_volume( act->placement ) ); + } -void activity_handlers::chop_tree_do_turn( player_activity *act, player * ) -{ - map &here = get_map(); - sfx::play_activity_sound( "tool", "axe", sfx::get_heard_volume( here.getlocal( act->placement ) ) ); - if( calendar::once_every( 1_minutes ) ) { - //~ Sound of a wood chopping tool at work! - sounds::sound( here.getlocal( act->placement ), 15, sounds::sound_t::activity, _( "CHK!" ) ); + void activity_handlers::pry_nails_finish( player_activity * act, player * p ) { + const tripoint &pnt = act->placement; + map &here = get_map(); + const ter_id type = here.ter( pnt ); + + int nails = 0; + int boards = 0; + ter_id newter; + if( type == t_fence ) { + nails = 6; + boards = 3; + newter = t_fence_post; + p->add_msg_if_player( _( "You pry out the fence post." ) ); + } else if( type == t_window_reinforced_noglass ) { + nails = 16; + boards = 8; + newter = t_window_boarded_noglass; + p->add_msg_if_player( _( "You pry the boards from the window." ) ); + } else if( type == t_window_reinforced ) { + nails = 16; + boards = 8; + newter = t_window_boarded; + p->add_msg_if_player( _( "You pry the boards from the window." ) ); + } else if( type == t_window_boarded ) { + nails = 8; + boards = 4; + newter = t_window_frame; + p->add_msg_if_player( _( "You pry the boards from the window." ) ); + } else if( type == t_window_boarded_noglass ) { + nails = 8; + boards = 4; + newter = t_window_empty; + p->add_msg_if_player( _( "You pry the boards from the window frame." ) ); + } else if( type == t_door_boarded || type == t_door_boarded_damaged || + type == t_rdoor_boarded || type == t_rdoor_boarded_damaged || + type == t_door_boarded_peep || type == t_door_boarded_damaged_peep ) { + nails = 8; + boards = 4; + if( type == t_door_boarded ) { + newter = t_door_c; + } else if( type == t_door_boarded_damaged ) { + newter = t_door_b; + } else if( type == t_door_boarded_peep ) { + newter = t_door_c_peep; + } else if( type == t_door_boarded_damaged_peep ) { + newter = t_door_b_peep; + } else if( type == t_rdoor_boarded ) { + newter = t_rdoor_c; + } else { // if (type == t_rdoor_boarded_damaged) + newter = t_rdoor_b; + } + p->add_msg_if_player( _( "You pry the boards from the door." ) ); + } + p->practice( skill_fabrication, 1, 1 ); + here.spawn_item( p->pos(), itype_nail, 0, nails ); + here.spawn_item( p->pos(), itype_2x4, boards ); + here.ter_set( pnt, newter ); + act->set_to_null(); } -} -void activity_handlers::chop_tree_finish( player_activity *act, player *p ) -{ - map &here = get_map(); - const tripoint &pos = here.getlocal( act->placement ); + void activity_handlers::chop_tree_do_turn( player_activity * act, player * ) { + map &here = get_map(); + sfx::play_activity_sound( "tool", "axe", sfx::get_heard_volume( here.getlocal( act->placement ) ) ); + if( calendar::once_every( 1_minutes ) ) { + //~ Sound of a wood chopping tool at work! + sounds::sound( here.getlocal( act->placement ), 15, sounds::sound_t::activity, _( "CHK!" ) ); + } + } - tripoint direction; - if( !p->is_npc() ) { - if( p->backlog.empty() || p->backlog.front()->id() != ACT_MULTIPLE_CHOP_TREES ) { - while( true ) { - if( const std::optional dir = choose_direction( - _( "Select a direction for the tree to fall in." ) ) ) { - direction = *dir; - break; + void activity_handlers::chop_tree_finish( player_activity * act, player * p ) { + map &here = get_map(); + const tripoint &pos = here.getlocal( act->placement ); + + tripoint direction; + if( !p->is_npc() ) { + if( p->backlog.empty() || p->backlog.front()->id() != ACT_MULTIPLE_CHOP_TREES ) { + while( true ) { + if( const std::optional dir = choose_direction( + _( "Select a direction for the tree to fall in." ) ) ) { + direction = *dir; + break; + } + // try again } - // try again } - } - } else { - // Try to safely fell tree - std::vector valid_directions; - - for( const tripoint &elem : here.points_in_radius( pos, 1 ) ) { - bool cantuse = false; - tripoint direc = elem - pos; - tripoint proposed_to = pos + point( 3 * direction.x, 3 * direction.y ); - std::vector rough_tree_line = line_to( pos, proposed_to ); - for( const tripoint &elem : rough_tree_line ) { - // Try not to drop onto a critter - if( g->critter_at( elem ) ) { - cantuse = true; - break; - } + } else { + // Try to safely fell tree + std::vector valid_directions; + + for( const tripoint &elem : here.points_in_radius( pos, 1 ) ) { + bool cantuse = false; + tripoint direc = elem - pos; + tripoint proposed_to = pos + point( 3 * direction.x, 3 * direction.y ); + std::vector rough_tree_line = line_to( pos, proposed_to ); + for( const tripoint &elem : rough_tree_line ) { + // Try not to drop onto a critter + if( g->critter_at( elem ) ) { + cantuse = true; + break; + } - ter_t ter = here.ter( elem ).obj(); - furn_t furn = here.furn( elem ).obj(); - // Furniture / Terrain test - if( elem != pos && ( ter.bash.str_max != -1 || ( furn.id && furn.bash.str_max != -1 ) ) ) { - cantuse = true; - break; + ter_t ter = here.ter( elem ).obj(); + furn_t furn = here.furn( elem ).obj(); + // Furniture / Terrain test + if( elem != pos && ( ter.bash.str_max != -1 || ( furn.id && furn.bash.str_max != -1 ) ) ) { + cantuse = true; + break; + } + // Vehicle check + if( veh_pointer_or_null( here.veh_at( elem ) ) ) { + cantuse = true; + break; + } } - // Vehicle check - if( veh_pointer_or_null( here.veh_at( elem ) ) ) { - cantuse = true; - break; + if( !cantuse ) { + // Passed all tests for safe direction, add to the possible routes + valid_directions.push_back( direc ); } } - if( !cantuse ) { - // Passed all tests for safe direction, add to the possible routes - valid_directions.push_back( direc ); - } + // Select a random valid direction, or none if empty + direction = random_entry( valid_directions, direction ); } - // Select a random valid direction, or none if empty - direction = random_entry( valid_directions, direction ); - } - - const tripoint to = pos + 3 * direction.xy() + point( rng( -1, 1 ), rng( -1, 1 ) ); - std::vector tree = line_to( pos, to, rng( 1, 8 ) ); - for( const tripoint &elem : tree ) { - here.batter( elem, 300, 5 ); - here.ter_set( elem, t_trunk ); - } - here.ter_set( pos, t_stump ); - p->add_msg_if_player( m_good, _( "You finish chopping down a tree." ) ); - // sound of falling tree - sfx::play_variant_sound( "misc", "timber", - sfx::get_heard_volume( here.getlocal( act->placement ) ) ); - act->set_to_null(); + const tripoint to = pos + 3 * direction.xy() + point( rng( -1, 1 ), rng( -1, 1 ) ); + std::vector tree = line_to( pos, to, rng( 1, 8 ) ); + for( const tripoint &elem : tree ) { + here.batter( elem, 300, 5 ); + here.ter_set( elem, t_trunk ); + } - // Quality of tool used and assistants can together both reduce intensity of work. - if( act->targets.empty() ) { - debugmsg( "woodcutting item location not set" ); - resume_for_multi_activities( *p ); - return; - } + here.ter_set( pos, t_stump ); + p->add_msg_if_player( m_good, _( "You finish chopping down a tree." ) ); + // sound of falling tree + sfx::play_variant_sound( "misc", "timber", + sfx::get_heard_volume( here.getlocal( act->placement ) ) ); + act->set_to_null(); - safe_reference &loc = act->targets[ 0 ]; - if( !loc ) { - debugmsg( "woodcutting item location lost" ); - resume_for_multi_activities( *p ); - return; - } + // Quality of tool used and assistants can together both reduce intensity of work. + if( act->targets.empty() ) { + debugmsg( "woodcutting item location not set" ); + resume_for_multi_activities( *p ); + return; + } - item *it = &*loc; + safe_reference &loc = act->targets[ 0 ]; + if( !loc ) { + debugmsg( "woodcutting item location lost" ); + resume_for_multi_activities( *p ); + return; + } - int act_exertion = iuse::chop_moves( *p, *it ); - p->add_msg_if_player( m_good, _( "You finish chopping down a tree." ) ); - const std::vector helpers = character_funcs::get_crafting_helpers( *p, 3 ); - act_exertion = act_exertion * ( 10 - helpers.size() ) / 10; + item *it = &*loc; - p->mod_stored_kcal( std::min( -1, -act_exertion / to_moves( 80_seconds ) ) ); - p->mod_thirst( std::max( 1, act_exertion / to_moves( 12_minutes ) ) ); - p->mod_fatigue( std::max( 1, act_exertion / to_moves( 6_minutes ) ) ); + int act_exertion = iuse::chop_moves( *p, *it ); + p->add_msg_if_player( m_good, _( "You finish chopping down a tree." ) ); + const std::vector helpers = character_funcs::get_crafting_helpers( *p, 3 ); + act_exertion = act_exertion * ( 10 - helpers.size() ) / 10; - resume_for_multi_activities( *p ); -} + p->mod_stored_kcal( std::min( -1, -act_exertion / to_moves( 80_seconds ) ) ); + p->mod_thirst( std::max( 1, act_exertion / to_moves( 12_minutes ) ) ); + p->mod_fatigue( std::max( 1, act_exertion / to_moves( 6_minutes ) ) ); -void activity_handlers::chop_logs_finish( player_activity *act, player *p ) -{ - map &here = get_map(); - const tripoint &pos = here.getlocal( act->placement ); - int log_quan; - int stick_quan; - int splint_quan; - if( here.ter( pos ) == t_trunk ) { - log_quan = rng( 2, 3 ); - stick_quan = rng( 0, 1 ); - splint_quan = 0; - } else if( here.ter( pos ) == t_stump ) { - log_quan = rng( 0, 2 ); - stick_quan = 0; - splint_quan = rng( 5, 15 ); - } else { - log_quan = 0; - stick_quan = 0; - splint_quan = 0; - } - for( int i = 0; i != log_quan; ++i ) { - detached_ptr obj = item::spawn( itype_log, calendar::turn ); - obj->set_var( "activity_var", p->name ); - here.add_item_or_charges( pos, std::move( obj ) ); - } - for( int i = 0; i != stick_quan; ++i ) { - detached_ptr obj = item::spawn( itype_stick_long, calendar::turn ); - obj->set_var( "activity_var", p->name ); - here.add_item_or_charges( pos, std::move( obj ) ); - } - for( int i = 0; i != splint_quan; ++i ) { - detached_ptr obj = item::spawn( itype_splinter, calendar::turn ); - obj->set_var( "activity_var", p->name ); - here.add_item_or_charges( pos, std::move( obj ) ); + resume_for_multi_activities( *p ); } - here.ter_set( pos, t_dirt ); - p->add_msg_if_player( m_good, _( "You finish chopping wood." ) ); - - act->set_to_null(); - // Quality of tool used and assistants can together both reduce intensity of work. + void activity_handlers::chop_logs_finish( player_activity * act, player * p ) { + map &here = get_map(); + const tripoint &pos = here.getlocal( act->placement ); + int log_quan; + int stick_quan; + int splint_quan; + if( here.ter( pos ) == t_trunk ) { + log_quan = rng( 2, 3 ); + stick_quan = rng( 0, 1 ); + splint_quan = 0; + } else if( here.ter( pos ) == t_stump ) { + log_quan = rng( 0, 2 ); + stick_quan = 0; + splint_quan = rng( 5, 15 ); + } else { + log_quan = 0; + stick_quan = 0; + splint_quan = 0; + } + for( int i = 0; i != log_quan; ++i ) { + detached_ptr obj = item::spawn( itype_log, calendar::turn ); + obj->set_var( "activity_var", p->name ); + here.add_item_or_charges( pos, std::move( obj ) ); + } + for( int i = 0; i != stick_quan; ++i ) { + detached_ptr obj = item::spawn( itype_stick_long, calendar::turn ); + obj->set_var( "activity_var", p->name ); + here.add_item_or_charges( pos, std::move( obj ) ); + } + for( int i = 0; i != splint_quan; ++i ) { + detached_ptr obj = item::spawn( itype_splinter, calendar::turn ); + obj->set_var( "activity_var", p->name ); + here.add_item_or_charges( pos, std::move( obj ) ); + } + here.ter_set( pos, t_dirt ); + p->add_msg_if_player( m_good, _( "You finish chopping wood." ) ); - safe_reference &loc = act->targets[ 0 ]; - if( !loc ) { - debugmsg( "woodcutting item location lost" ); - return; - } + act->set_to_null(); - item *it = &*loc; - int act_exertion = iuse::chop_moves( *p, *it ); - const std::vector helpers = character_funcs::get_crafting_helpers( *p, 3 ); - act_exertion = act_exertion * ( 10 - helpers.size() ) / 10; + // Quality of tool used and assistants can together both reduce intensity of work. - p->mod_stored_kcal( std::min( -1, -act_exertion / to_moves( 80_seconds ) ) ); - p->mod_thirst( std::max( 1, act_exertion / to_moves( 12_minutes ) ) ); - p->mod_fatigue( std::max( 1, act_exertion / to_moves( 6_minutes ) ) ); + safe_reference &loc = act->targets[ 0 ]; + if( !loc ) { + debugmsg( "woodcutting item location lost" ); + return; + } - resume_for_multi_activities( *p ); -} + item *it = &*loc; + int act_exertion = iuse::chop_moves( *p, *it ); + const std::vector helpers = character_funcs::get_crafting_helpers( *p, 3 ); + act_exertion = act_exertion * ( 10 - helpers.size() ) / 10; -void activity_handlers::chop_planks_finish( player_activity *act, player *p ) -{ - const int max_planks = 10; - /** @EFFECT_FABRICATION increases number of planks cut from a log */ - int planks = normal_roll( 2 + p->get_skill_level( skill_id( "fabrication" ) ), 1 ); - int wasted_planks = max_planks - planks; - int scraps = rng( wasted_planks, wasted_planks * 3 ); - planks = std::min( planks, max_planks ); + p->mod_stored_kcal( std::min( -1, -act_exertion / to_moves( 80_seconds ) ) ); + p->mod_thirst( std::max( 1, act_exertion / to_moves( 12_minutes ) ) ); + p->mod_fatigue( std::max( 1, act_exertion / to_moves( 6_minutes ) ) ); - map &here = get_map(); - if( planks > 0 ) { - here.spawn_item( here.getlocal( act->placement ), itype_2x4, planks, 0, calendar::turn ); - p->add_msg_if_player( m_good, _( "You produce %d planks." ), planks ); - } - if( scraps > 0 ) { - here.spawn_item( here.getlocal( act->placement ), itype_splinter, scraps, 0, calendar::turn ); - p->add_msg_if_player( m_good, _( "You produce %d splinters." ), scraps ); + resume_for_multi_activities( *p ); } - if( planks < max_planks / 2 ) { - p->add_msg_if_player( m_bad, _( "You waste a lot of the wood." ) ); + + void activity_handlers::chop_planks_finish( player_activity * act, player * p ) { + const int max_planks = 10; + /** @EFFECT_FABRICATION increases number of planks cut from a log */ + int planks = normal_roll( 2 + p->get_skill_level( skill_id( "fabrication" ) ), 1 ); + int wasted_planks = max_planks - planks; + int scraps = rng( wasted_planks, wasted_planks * 3 ); + planks = std::min( planks, max_planks ); + + map &here = get_map(); + if( planks > 0 ) { + here.spawn_item( here.getlocal( act->placement ), itype_2x4, planks, 0, calendar::turn ); + p->add_msg_if_player( m_good, _( "You produce %d planks." ), planks ); + } + if( scraps > 0 ) { + here.spawn_item( here.getlocal( act->placement ), itype_splinter, scraps, 0, calendar::turn ); + p->add_msg_if_player( m_good, _( "You produce %d splinters." ), scraps ); + } + if( planks < max_planks / 2 ) { + p->add_msg_if_player( m_bad, _( "You waste a lot of the wood." ) ); + } + act->set_to_null(); + resume_for_multi_activities( *p ); } - act->set_to_null(); - resume_for_multi_activities( *p ); -} -void activity_handlers::jackhammer_do_turn( player_activity *act, player * ) -{ - map &here = get_map(); - sfx::play_activity_sound( "tool", "jackhammer", - sfx::get_heard_volume( here.getlocal( act->placement ) ) ); - if( calendar::once_every( 1_minutes ) ) { - sounds::sound( here.getlocal( act->placement ), 15, sounds::sound_t::destructive_activity, - //~ Sound of a jackhammer at work! - _( "TATATATATATATAT!" ) ); + void activity_handlers::jackhammer_do_turn( player_activity * act, player * ) { + map &here = get_map(); + sfx::play_activity_sound( "tool", "jackhammer", + sfx::get_heard_volume( here.getlocal( act->placement ) ) ); + if( calendar::once_every( 1_minutes ) ) { + sounds::sound( here.getlocal( act->placement ), 15, sounds::sound_t::destructive_activity, + //~ Sound of a jackhammer at work! + _( "TATATATATATATAT!" ) ); + } } -} -void activity_handlers::jackhammer_finish( player_activity *act, player *p ) -{ - map &here = get_map(); - const tripoint &pos = here.getlocal( act->placement ); + void activity_handlers::jackhammer_finish( player_activity * act, player * p ) { + map &here = get_map(); + const tripoint &pos = here.getlocal( act->placement ); - here.destroy( pos, true ); + here.destroy( pos, true ); - if( p->is_avatar() ) { - const int helpersize = character_funcs::get_crafting_helpers( *p, 3 ).size(); - p->mod_stored_nutr( 5 - helpersize ); - p->mod_thirst( 5 - helpersize ); - p->mod_fatigue( 10 - ( helpersize * 2 ) ); - } - p->add_msg_player_or_npc( m_good, - _( "You finish drilling." ), - _( " finishes drilling." ) ); - act->set_to_null(); - if( !act->targets.empty() ) { - item &it = *act->targets.front(); - p->consume_charges( it, it.ammo_required() ); - } else { - debugmsg( "jackhammer activity targets empty" ); - } - if( resume_for_multi_activities( *p ) ) { - for( item *&elem : here.i_at( pos ) ) { - elem->set_var( "activity_var", p->name ); + if( p->is_avatar() ) { + const int helpersize = character_funcs::get_crafting_helpers( *p, 3 ).size(); + p->mod_stored_nutr( 5 - helpersize ); + p->mod_thirst( 5 - helpersize ); + p->mod_fatigue( 10 - ( helpersize * 2 ) ); + } + p->add_msg_player_or_npc( m_good, + _( "You finish drilling." ), + _( " finishes drilling." ) ); + act->set_to_null(); + if( !act->targets.empty() ) { + item &it = *act->targets.front(); + p->consume_charges( it, it.ammo_required() ); + } else { + debugmsg( "jackhammer activity targets empty" ); + } + if( resume_for_multi_activities( *p ) ) { + for( item *&elem : here.i_at( pos ) ) { + elem->set_var( "activity_var", p->name ); + } } } -} -void activity_handlers::fill_pit_do_turn( player_activity *act, player * ) -{ - sfx::play_activity_sound( "tool", "shovel", 100 ); - if( calendar::once_every( 1_minutes ) ) { - //~ Sound of a shovel filling a pit or mound at work! - sounds::sound( act->placement, 10, sounds::sound_t::activity, _( "hsh!" ) ); + void activity_handlers::fill_pit_do_turn( player_activity * act, player * ) { + sfx::play_activity_sound( "tool", "shovel", 100 ); + if( calendar::once_every( 1_minutes ) ) { + //~ Sound of a shovel filling a pit or mound at work! + sounds::sound( act->placement, 10, sounds::sound_t::activity, _( "hsh!" ) ); + } } -} -void activity_handlers::fill_pit_finish( player_activity *act, player *p ) -{ - const tripoint &pos = act->placement; - map &here = get_map(); - const ter_id ter = here.ter( pos ); - const ter_id old_ter = ter; + void activity_handlers::fill_pit_finish( player_activity * act, player * p ) { + const tripoint &pos = act->placement; + map &here = get_map(); + const ter_id ter = here.ter( pos ); + const ter_id old_ter = ter; - if( ter == t_pit || ter == t_pit_spiked || ter == t_pit_glass || - ter == t_pit_corpsed ) { - here.ter_set( pos, t_pit_shallow ); - } else { - here.ter_set( pos, t_dirt ); + if( ter == t_pit || ter == t_pit_spiked || ter == t_pit_glass || + ter == t_pit_corpsed ) { + here.ter_set( pos, t_pit_shallow ); + } else { + here.ter_set( pos, t_dirt ); + } + int act_exertion = to_moves( time_duration::from_minutes( 15 ) ); + if( old_ter == t_pit_shallow ) { + act_exertion = to_moves( time_duration::from_minutes( 10 ) ); + } else if( old_ter == t_dirtmound ) { + act_exertion = to_moves( time_duration::from_minutes( 5 ) ); + } + const int helpersize = character_funcs::get_crafting_helpers( *p, 3 ).size(); + act_exertion = act_exertion * ( 10 - helpersize ) / 10; + p->mod_stored_kcal( std::min( -1, -act_exertion / to_moves( 20_seconds ) ) ); + p->mod_thirst( std::max( 1, act_exertion / to_moves( 3_minutes ) ) ); + p->mod_fatigue( std::max( 1, act_exertion / to_moves( 90_seconds ) ) ); + p->add_msg_if_player( m_good, _( "You finish filling up %s." ), old_ter.obj().name() ); + act->set_to_null(); } - int act_exertion = to_moves( time_duration::from_minutes( 15 ) ); - if( old_ter == t_pit_shallow ) { - act_exertion = to_moves( time_duration::from_minutes( 10 ) ); - } else if( old_ter == t_dirtmound ) { - act_exertion = to_moves( time_duration::from_minutes( 5 ) ); - } - const int helpersize = character_funcs::get_crafting_helpers( *p, 3 ).size(); - act_exertion = act_exertion * ( 10 - helpersize ) / 10; - p->mod_stored_kcal( std::min( -1, -act_exertion / to_moves( 20_seconds ) ) ); - p->mod_thirst( std::max( 1, act_exertion / to_moves( 3_minutes ) ) ); - p->mod_fatigue( std::max( 1, act_exertion / to_moves( 90_seconds ) ) ); - p->add_msg_if_player( m_good, _( "You finish filling up %s." ), old_ter.obj().name() ); - act->set_to_null(); -} -void activity_handlers::play_with_pet_finish( player_activity *act, player *p ) -{ - p->add_morale( MORALE_PLAY_WITH_PET, rng( 3, 10 ), 10, 5_hours, 25_minutes ); - p->add_msg_if_player( m_good, _( "Playing with your %s has lifted your spirits a bit." ), - act->str_values[0] ); - act->set_to_null(); -} + void activity_handlers::play_with_pet_finish( player_activity * act, player * p ) { + p->add_morale( MORALE_PLAY_WITH_PET, rng( 3, 10 ), 10, 5_hours, 25_minutes ); + p->add_msg_if_player( m_good, _( "Playing with your %s has lifted your spirits a bit." ), + act->str_values[0] ); + act->set_to_null(); + } -void activity_handlers::shaving_finish( player_activity *act, player *p ) -{ - p->add_msg_if_player( _( "You open up your kit and shave." ) ); - p->add_morale( MORALE_SHAVE, 8, 8, 240_minutes, 3_minutes ); - act->set_to_null(); -} + void activity_handlers::shaving_finish( player_activity * act, player * p ) { + p->add_msg_if_player( _( "You open up your kit and shave." ) ); + p->add_morale( MORALE_SHAVE, 8, 8, 240_minutes, 3_minutes ); + act->set_to_null(); + } -void activity_handlers::haircut_finish( player_activity *act, player *p ) -{ - p->add_msg_if_player( _( "You give your hair a trim." ) ); - p->add_morale( MORALE_HAIRCUT, 3, 3, 480_minutes, 3_minutes ); - act->set_to_null(); -} + void activity_handlers::haircut_finish( player_activity * act, player * p ) { + p->add_msg_if_player( _( "You give your hair a trim." ) ); + p->add_morale( MORALE_HAIRCUT, 3, 3, 480_minutes, 3_minutes ); + act->set_to_null(); + } -std::vector get_sorted_tiles_by_distance( const tripoint &abspos, - const std::unordered_set &tiles ) -{ - const auto cmp = [abspos]( tripoint a, tripoint b ) { - const int da = rl_dist( abspos, a ); - const int db = rl_dist( abspos, b ); + std::vector get_sorted_tiles_by_distance( const tripoint & abspos, + const std::unordered_set &tiles ) { + const auto cmp = [abspos]( tripoint a, tripoint b ) { + const int da = rl_dist( abspos, a ); + const int db = rl_dist( abspos, b ); - return da < db; - }; + return da < db; + }; - std::vector sorted( tiles.begin(), tiles.end() ); - std::sort( sorted.begin(), sorted.end(), cmp ); + std::vector sorted( tiles.begin(), tiles.end() ); + std::sort( sorted.begin(), sorted.end(), cmp ); - return sorted; -} + return sorted; + } -template -static void cleanup_tiles( std::unordered_set &tiles, fn &cleanup ) -{ - auto it = tiles.begin(); - map &here = get_map(); - while( it != tiles.end() ) { - auto current = it++; + template + static void cleanup_tiles( std::unordered_set &tiles, fn & cleanup ) { + auto it = tiles.begin(); + map &here = get_map(); + while( it != tiles.end() ) { + auto current = it++; - const tripoint &tile_loc = here.getlocal( *current ); + const tripoint &tile_loc = here.getlocal( *current ); - if( cleanup( tile_loc ) ) { - tiles.erase( current ); + if( cleanup( tile_loc ) ) { + tiles.erase( current ); + } } } -} -static void perform_zone_activity_turn( player *p, - const zone_type_id &ztype, - const std::function &tile_filter, - const std::function &tile_action, - const std::string &finished_msg ) -{ - const zone_manager &mgr = zone_manager::get_manager(); - map &here = get_map(); - const tripoint abspos = here.getabs( p->pos() ); - std::unordered_set unsorted_tiles = mgr.get_near( ztype, abspos ); + static void perform_zone_activity_turn( player * p, + const zone_type_id & ztype, + const std::function &tile_filter, + const std::function &tile_action, + const std::string & finished_msg ) { + const zone_manager &mgr = zone_manager::get_manager(); + map &here = get_map(); + const tripoint abspos = here.getabs( p->pos() ); + std::unordered_set unsorted_tiles = mgr.get_near( ztype, abspos ); - cleanup_tiles( unsorted_tiles, tile_filter ); + cleanup_tiles( unsorted_tiles, tile_filter ); - // sort remaining tiles by distance - const std::vector &tiles = get_sorted_tiles_by_distance( abspos, unsorted_tiles ); + // sort remaining tiles by distance + const std::vector &tiles = get_sorted_tiles_by_distance( abspos, unsorted_tiles ); - for( const tripoint &tile : tiles ) { - const tripoint &tile_loc = here.getlocal( tile ); + for( const tripoint &tile : tiles ) { + const tripoint &tile_loc = here.getlocal( tile ); - std::vector route = here.route( p->pos(), tile_loc, p->get_pathfinding_settings(), - p->get_path_avoid() ); - if( route.size() > 1 ) { - route.pop_back(); + std::vector route = here.route( p->pos(), tile_loc, p->get_pathfinding_settings(), + p->get_path_avoid() ); + if( route.size() > 1 ) { + route.pop_back(); - p->set_destination( route, p->remove_activity() ); - p->activity = std::make_unique( ); - return; - } else { - // we are at destination already - /* Perform action */ - tile_action( *p, tile_loc ); - if( p->moves <= 0 ) { + p->set_destination( route, p->remove_activity() ); + p->activity = std::make_unique( ); return; + } else { + // we are at destination already + /* Perform action */ + tile_action( *p, tile_loc ); + if( p->moves <= 0 ) { + return; + } } } + add_msg( m_info, finished_msg ); + p->activity->set_to_null(); } - add_msg( m_info, finished_msg ); - p->activity->set_to_null(); -} -void activity_handlers::fertilize_plot_do_turn( player_activity *act, player *p ) -{ - itype_id fertilizer; - auto check_fertilizer = [&]( bool ask_user = true ) -> void { - if( act->str_values.empty() ) - { - act->str_values.emplace_back( "" ); - } - fertilizer = itype_id( act->str_values[0] ); + void activity_handlers::fertilize_plot_do_turn( player_activity * act, player * p ) { + itype_id fertilizer; + auto check_fertilizer = [&]( bool ask_user = true ) -> void { + if( act->str_values.empty() ) + { + act->str_values.emplace_back( "" ); + } + fertilizer = itype_id( act->str_values[0] ); + + /* If unspecified, or if we're out of what we used before, ask */ + if( ask_user && ( fertilizer.is_empty() || !p->has_charges( fertilizer, 1 ) ) ) + { + fertilizer = iexamine::choose_fertilizer( *p, "plant", + false /* Don't confirm action with player */ ); + act->str_values[0] = fertilizer.str(); + } + }; - /* If unspecified, or if we're out of what we used before, ask */ - if( ask_user && ( fertilizer.is_empty() || !p->has_charges( fertilizer, 1 ) ) ) - { - fertilizer = iexamine::choose_fertilizer( *p, "plant", - false /* Don't confirm action with player */ ); - act->str_values[0] = fertilizer.str(); - } - }; + auto have_fertilizer = [&]() { + return !fertilizer.is_empty() && p->has_charges( fertilizer, 1 ); + }; - auto have_fertilizer = [&]() { - return !fertilizer.is_empty() && p->has_charges( fertilizer, 1 ); - }; + const auto reject_tile = [&]( const tripoint & tile ) { + check_fertilizer(); + ret_val can_fert = iexamine::can_fertilize( *p, tile, fertilizer ); + return !can_fert.success(); + }; - const auto reject_tile = [&]( const tripoint & tile ) { - check_fertilizer(); - ret_val can_fert = iexamine::can_fertilize( *p, tile, fertilizer ); - return !can_fert.success(); - }; + const auto fertilize = [&]( player & p, const tripoint & tile ) { + check_fertilizer(); + if( have_fertilizer() ) { + iexamine::fertilize_plant( p, tile, fertilizer ); + if( !have_fertilizer() ) { + add_msg( m_info, _( "You have run out of %s." ), item::nname( fertilizer ) ); + } + } + }; - const auto fertilize = [&]( player & p, const tripoint & tile ) { check_fertilizer(); - if( have_fertilizer() ) { - iexamine::fertilize_plant( p, tile, fertilizer ); - if( !have_fertilizer() ) { - add_msg( m_info, _( "You have run out of %s." ), item::nname( fertilizer ) ); - } + if( !have_fertilizer() ) { + act->set_to_null(); + return; } - }; - check_fertilizer(); - if( !have_fertilizer() ) { - act->set_to_null(); - return; + perform_zone_activity_turn( p, + zone_type_FARM_PLOT, + reject_tile, + fertilize, + _( "You fertilized every plot you could." ) ); } - perform_zone_activity_turn( p, - zone_type_FARM_PLOT, - reject_tile, - fertilize, - _( "You fertilized every plot you could." ) ); -} + void activity_handlers::robot_control_do_turn( player_activity * act, player * p ) { + if( act->monsters.empty() ) { + debugmsg( "No monster assigned in ACT_ROBOT_CONTROL" ); + act->set_to_null(); + return; + } + const shared_ptr_fast z = act->monsters[0].lock(); -void activity_handlers::robot_control_do_turn( player_activity *act, player *p ) -{ - if( act->monsters.empty() ) { - debugmsg( "No monster assigned in ACT_ROBOT_CONTROL" ); - act->set_to_null(); - return; - } - const shared_ptr_fast z = act->monsters[0].lock(); + if( !z || !iuse::robotcontrol_can_target( p, *z ) ) { + p->add_msg_if_player( _( "Target lost. IFF override failed." ) ); + act->set_to_null(); + return; + } - if( !z || !iuse::robotcontrol_can_target( p, *z ) ) { - p->add_msg_if_player( _( "Target lost. IFF override failed." ) ); - act->set_to_null(); - return; + // TODO: Add some kind of chance of getting the target's attention } - // TODO: Add some kind of chance of getting the target's attention -} - -void activity_handlers::robot_control_finish( player_activity *act, player *p ) -{ - act->set_to_null(); + void activity_handlers::robot_control_finish( player_activity * act, player * p ) { + act->set_to_null(); - if( act->monsters.empty() ) { - debugmsg( "No monster assigned in ACT_ROBOT_CONTROL" ); - return; - } + if( act->monsters.empty() ) { + debugmsg( "No monster assigned in ACT_ROBOT_CONTROL" ); + return; + } - shared_ptr_fast z = act->monsters[0].lock(); - act->monsters.clear(); + shared_ptr_fast z = act->monsters[0].lock(); + act->monsters.clear(); - if( !z || !iuse::robotcontrol_can_target( p, *z ) ) { - p->add_msg_if_player( _( "Target lost. IFF override failed." ) ); - return; - } + if( !z || !iuse::robotcontrol_can_target( p, *z ) ) { + p->add_msg_if_player( _( "Target lost. IFF override failed." ) ); + return; + } - p->add_msg_if_player( _( "You unleash your override attack on the %s." ), z->name() ); + p->add_msg_if_player( _( "You unleash your override attack on the %s." ), z->name() ); - /** @EFFECT_INT increases chance of successful robot reprogramming, vs difficulty */ - /** @EFFECT_COMPUTER increases chance of successful robot reprogramming, vs difficulty */ - const int computer_skill = p->get_skill_level( skill_id( "computer" ) ); - const float randomized_skill = rng( 2, p->int_cur ) + computer_skill; - float success = computer_skill - 3 * z->type->difficulty / randomized_skill; - if( z->has_flag( MF_RIDEABLE_MECH ) ) { - success = randomized_skill - rng( 1, 11 ); - } - // rideable mechs are not hostile, they have no AI, they do not resist control as much. - if( success >= 0 ) { - p->add_msg_if_player( _( "You successfully override the %s's IFF protocols!" ), - z->name() ); - z->friendly = -1; + /** @EFFECT_INT increases chance of successful robot reprogramming, vs difficulty */ + /** @EFFECT_COMPUTER increases chance of successful robot reprogramming, vs difficulty */ + const int computer_skill = p->get_skill_level( skill_id( "computer" ) ); + const float randomized_skill = rng( 2, p->int_cur ) + computer_skill; + float success = computer_skill - 3 * z->type->difficulty / randomized_skill; if( z->has_flag( MF_RIDEABLE_MECH ) ) { - z->add_effect( effect_pet, 1_turns, num_bp ); - } - } else if( success >= -2 ) { - //A near success - p->add_msg_if_player( _( "The %s short circuits as you attempt to reprogram it!" ), z->name() ); - //damage it a little - z->apply_damage( p, bodypart_id( "torso" ), rng( 1, 10 ) ); - if( z->is_dead() ) { - p->practice( skill_id( "computer" ), 10 ); - // Do not do the other effects if the robot died - return; - } - if( one_in( 3 ) ) { - p->add_msg_if_player( _( "…and turns friendly!" ) ); - //did the robot became friendly permanently? + success = randomized_skill - rng( 1, 11 ); + } + // rideable mechs are not hostile, they have no AI, they do not resist control as much. + if( success >= 0 ) { + p->add_msg_if_player( _( "You successfully override the %s's IFF protocols!" ), + z->name() ); + z->friendly = -1; + if( z->has_flag( MF_RIDEABLE_MECH ) ) { + z->add_effect( effect_pet, 1_turns, num_bp ); + } + } else if( success >= -2 ) { + //A near success + p->add_msg_if_player( _( "The %s short circuits as you attempt to reprogram it!" ), z->name() ); + //damage it a little + z->apply_damage( p, bodypart_id( "torso" ), rng( 1, 10 ) ); + if( z->is_dead() ) { + p->practice( skill_id( "computer" ), 10 ); + // Do not do the other effects if the robot died + return; + } if( one_in( 3 ) ) { - //it did - z->friendly = -1; - } else { - // it didn't - z->friendly = rng( 5, 40 ); + p->add_msg_if_player( _( "…and turns friendly!" ) ); + //did the robot became friendly permanently? + if( one_in( 3 ) ) { + //it did + z->friendly = -1; + } else { + // it didn't + z->friendly = rng( 5, 40 ); + } } + } else { + p->add_msg_if_player( _( "…but the robot refuses to acknowledge you as an ally!" ) ); } - } else { - p->add_msg_if_player( _( "…but the robot refuses to acknowledge you as an ally!" ) ); + p->practice( skill_computer, 10 ); } - p->practice( skill_computer, 10 ); -} -void activity_handlers::tree_communion_do_turn( player_activity *act, player *p ) -{ - // There's an initial rooting process. - if( act->values.front() > 0 ) { - act->values.front() -= 1; - if( act->values.front() == 0 ) { - if( p->has_trait( trait_id( trait_SPIRITUAL ) ) ) { - p->add_msg_if_player( m_good, _( "The ancient tree spirits answer your call." ) ); - } else { - p->add_msg_if_player( m_good, _( "Your communion with the trees has begun." ) ); + void activity_handlers::tree_communion_do_turn( player_activity * act, player * p ) { + // There's an initial rooting process. + if( act->values.front() > 0 ) { + act->values.front() -= 1; + if( act->values.front() == 0 ) { + if( p->has_trait( trait_id( trait_SPIRITUAL ) ) ) { + p->add_msg_if_player( m_good, _( "The ancient tree spirits answer your call." ) ); + } else { + p->add_msg_if_player( m_good, _( "Your communion with the trees has begun." ) ); + } } + return; } - return; - } - // Information is received every minute. - if( !calendar::once_every( 1_minutes ) ) { - return; - } - // Breadth-first search forest tiles until one reveals new overmap tiles. - std::queue q; - std::unordered_set seen; - tripoint_abs_omt loc = p->global_omt_location(); - q.push( loc ); - seen.insert( loc ); - const std::function filter = []( const oter_id & ter ) { - return ter.obj().is_wooded() || ter.obj().get_name() == "field"; - }; - while( !q.empty() ) { - tripoint_abs_omt tpt = q.front(); - if( overmap_buffer.reveal( tpt, 3, filter ) ) { - if( p->has_trait( trait_SPIRITUAL ) ) { - p->add_morale( MORALE_TREE_COMMUNION, 2, 30, 8_hours, 6_hours ); - } else { - p->add_morale( MORALE_TREE_COMMUNION, 1, 15, 2_hours, 1_hours ); - } - if( one_in( 128 ) ) { - p->add_msg_if_player( "%s", SNIPPET.random_from_category( "tree_communion" ).value_or( - translation() ) ); - } + // Information is received every minute. + if( !calendar::once_every( 1_minutes ) ) { return; } - for( const tripoint_abs_omt &neighbor : points_in_radius( tpt, 1 ) ) { - if( seen.find( neighbor ) != seen.end() ) { - continue; + // Breadth-first search forest tiles until one reveals new overmap tiles. + std::queue q; + std::unordered_set seen; + tripoint_abs_omt loc = p->global_omt_location(); + q.push( loc ); + seen.insert( loc ); + const std::function filter = []( const oter_id & ter ) { + return ter.obj().is_wooded() || ter.obj().get_name() == "field"; + }; + while( !q.empty() ) { + tripoint_abs_omt tpt = q.front(); + if( overmap_buffer.reveal( tpt, 3, filter ) ) { + if( p->has_trait( trait_SPIRITUAL ) ) { + p->add_morale( MORALE_TREE_COMMUNION, 2, 30, 8_hours, 6_hours ); + } else { + p->add_morale( MORALE_TREE_COMMUNION, 1, 15, 2_hours, 1_hours ); + } + if( one_in( 128 ) ) { + p->add_msg_if_player( "%s", SNIPPET.random_from_category( "tree_communion" ).value_or( + translation() ) ); + } + return; } - seen.insert( neighbor ); - if( !overmap_buffer.ter( neighbor ).obj().is_wooded() ) { - continue; + for( const tripoint_abs_omt &neighbor : points_in_radius( tpt, 1 ) ) { + if( seen.find( neighbor ) != seen.end() ) { + continue; + } + seen.insert( neighbor ); + if( !overmap_buffer.ter( neighbor ).obj().is_wooded() ) { + continue; + } + q.push( neighbor ); } - q.push( neighbor ); + q.pop(); } - q.pop(); + p->add_msg_if_player( m_info, _( "The trees have shown you what they will." ) ); + act->set_to_null(); } - p->add_msg_if_player( m_info, _( "The trees have shown you what they will." ) ); - act->set_to_null(); -} -static void blood_magic( player *p, int cost ) -{ - std::vector uile; - std::vector parts; - int i = 0; - for( const bodypart_id &bp : p->get_all_body_parts( true ) ) { - const int hp_cur = p->get_part_hp_cur( bp ); - uilist_entry entry( i, hp_cur > cost, i + 49, body_part_hp_bar_ui_text( bp ) ); - - const std::pair &hp = get_hp_bar( hp_cur, p->get_part_hp_max( bp ) ); - entry.ctxt = colorize( hp.first, hp.second ); - uile.emplace_back( entry ); - parts.push_back( bp ); - i++; - } - int action = -1; - while( action < 0 ) { - action = uilist( _( "Choose part\nto draw blood from." ), uile ); - } - p->mod_part_hp_cur( parts[action], - cost ); - p->mod_pain( std::max( 1, cost / 3 ) ); -} + static void blood_magic( player * p, int cost ) { + std::vector uile; + std::vector parts; + int i = 0; + for( const bodypart_id &bp : p->get_all_body_parts( true ) ) { + const int hp_cur = p->get_part_hp_cur( bp ); + uilist_entry entry( i, hp_cur > cost, i + 49, body_part_hp_bar_ui_text( bp ) ); -void activity_handlers::spellcasting_finish( player_activity *act, player *p ) -{ - act->set_to_null(); - const int level_override = act->get_value( 0 ); - spell_id sp( act->name ); + const std::pair &hp = get_hp_bar( hp_cur, p->get_part_hp_max( bp ) ); + entry.ctxt = colorize( hp.first, hp.second ); + uile.emplace_back( entry ); + parts.push_back( bp ); + i++; + } + int action = -1; + while( action < 0 ) { + action = uilist( _( "Choose part\nto draw blood from." ), uile ); + } + p->mod_part_hp_cur( parts[action], - cost ); + p->mod_pain( std::max( 1, cost / 3 ) ); + } + + void activity_handlers::spellcasting_finish( player_activity * act, player * p ) { + act->set_to_null(); + const int level_override = act->get_value( 0 ); + spell_id sp( act->name ); - // if level is -1 then we know it's a player spell, otherwise we build it from the ground up - spell temp_spell( sp ); - spell &spell_being_cast = ( level_override == -1 ) ? p->magic->get_spell( sp ) : temp_spell; + // if level is -1 then we know it's a player spell, otherwise we build it from the ground up + spell temp_spell( sp ); + spell &spell_being_cast = ( level_override == -1 ) ? p->magic->get_spell( sp ) : temp_spell; - // if level != 1 then we need to set the spell's level - if( level_override != -1 ) { - while( spell_being_cast.get_level() < level_override && !spell_being_cast.is_max_level() ) { - spell_being_cast.gain_level(); + // if level != 1 then we need to set the spell's level + if( level_override != -1 ) { + while( spell_being_cast.get_level() < level_override && !spell_being_cast.is_max_level() ) { + spell_being_cast.gain_level(); + } } - } - const bool no_fail = act->get_value( 1 ) == 1; - const bool no_mana = act->get_value( 2 ) == 0; + const bool no_fail = act->get_value( 1 ) == 1; + const bool no_mana = act->get_value( 2 ) == 0; - // choose target for spell (if the spell has a range > 0) + // choose target for spell (if the spell has a range > 0) - tripoint target = p->pos(); - bool target_is_valid = false; - if( spell_being_cast.range() > 0 && !spell_being_cast.is_valid_target( target_none ) && - !spell_being_cast.has_flag( RANDOM_TARGET ) ) { - do { - avatar &you = *p->as_avatar(); - std::vector trajectory = target_handler::mode_spell( you, spell_being_cast, no_fail, - no_mana ); - - if( !trajectory.empty() ) { - target = trajectory.back(); - target_is_valid = spell_being_cast.is_valid_target( *p, target ); - if( !( spell_being_cast.is_valid_target( target_ground ) || p->sees( target ) ) ) { + tripoint target = p->pos(); + bool target_is_valid = false; + if( spell_being_cast.range() > 0 && !spell_being_cast.is_valid_target( target_none ) && + !spell_being_cast.has_flag( RANDOM_TARGET ) ) { + do { + avatar &you = *p->as_avatar(); + std::vector trajectory = target_handler::mode_spell( you, spell_being_cast, no_fail, + no_mana ); + + if( !trajectory.empty() ) { + target = trajectory.back(); + target_is_valid = spell_being_cast.is_valid_target( *p, target ); + if( !( spell_being_cast.is_valid_target( target_ground ) || p->sees( target ) ) ) { + target_is_valid = false; + } + } else { target_is_valid = false; } - } else { - target_is_valid = false; - } - if( !target_is_valid ) { - if( query_yn( _( "Stop casting spell? Time spent will be lost." ) ) ) { - return; + if( !target_is_valid ) { + if( query_yn( _( "Stop casting spell? Time spent will be lost." ) ) ) { + return; + } } + } while( !target_is_valid ); + } else if( spell_being_cast.has_flag( RANDOM_TARGET ) ) { + const std::optional target_ = spell_being_cast.random_valid_target( *p, p->pos() ); + if( !target_ ) { + p->add_msg_if_player( game_message_params{ m_bad, gmf_bypass_cooldown }, + _( "Your spell can't find a suitable target." ) ); + return; } - } while( !target_is_valid ); - } else if( spell_being_cast.has_flag( RANDOM_TARGET ) ) { - const std::optional target_ = spell_being_cast.random_valid_target( *p, p->pos() ); - if( !target_ ) { + target = *target_; + } + + // no turning back now. it's all said and done. + bool success = no_fail || rng_float( 0.0f, 1.0f ) >= spell_being_cast.spell_fail( *p ); + int exp_gained = spell_being_cast.casting_exp( *p ); + if( !success ) { p->add_msg_if_player( game_message_params{ m_bad, gmf_bypass_cooldown }, - _( "Your spell can't find a suitable target." ) ); + _( "You lose your concentration!" ) ); + if( !spell_being_cast.is_max_level() && level_override == -1 ) { + // still get some experience for trying + spell_being_cast.gain_exp( exp_gained / 5 ); + p->add_msg_if_player( m_good, _( "You gain %i experience. New total %i." ), exp_gained / 5, + spell_being_cast.xp() ); + } return; } - target = *target_; - } - // no turning back now. it's all said and done. - bool success = no_fail || rng_float( 0.0f, 1.0f ) >= spell_being_cast.spell_fail( *p ); - int exp_gained = spell_being_cast.casting_exp( *p ); - if( !success ) { - p->add_msg_if_player( game_message_params{ m_bad, gmf_bypass_cooldown }, - _( "You lose your concentration!" ) ); - if( !spell_being_cast.is_max_level() && level_override == -1 ) { - // still get some experience for trying - spell_being_cast.gain_exp( exp_gained / 5 ); - p->add_msg_if_player( m_good, _( "You gain %i experience. New total %i." ), exp_gained / 5, - spell_being_cast.xp() ); + if( spell_being_cast.has_flag( spell_flag::VERBAL ) ) { + sounds::sound( p->pos(), p->get_shout_volume() / 2, sounds::sound_t::speech, _( "cast a spell" ), + false ); } - return; - } - - if( spell_being_cast.has_flag( spell_flag::VERBAL ) ) { - sounds::sound( p->pos(), p->get_shout_volume() / 2, sounds::sound_t::speech, _( "cast a spell" ), - false ); - } - p->add_msg_if_player( spell_being_cast.message(), spell_being_cast.name() ); + p->add_msg_if_player( spell_being_cast.message(), spell_being_cast.name() ); - spell_being_cast.cast_all_effects( *p, target ); + spell_being_cast.cast_all_effects( *p, target ); - if( !no_mana ) { - // pay the cost - int cost = spell_being_cast.energy_cost( *p ); - switch( spell_being_cast.energy_source() ) { - case mana_energy: - p->magic->mod_mana( *p, -cost ); - break; - case stamina_energy: - p->mod_stamina( -cost ); - break; - case bionic_energy: - p->mod_power_level( -units::from_kilojoule( cost ) ); - break; - case hp_energy: - blood_magic( p, cost ); - break; - case fatigue_energy: - p->mod_fatigue( cost ); - break; - case none_energy: - default: - break; - } - spell_being_cast.use_components( *p ); - } - if( level_override == -1 ) { - if( !spell_being_cast.is_max_level() ) { - // reap the reward - int old_level = spell_being_cast.get_level(); - if( old_level == 0 ) { - spell_being_cast.gain_level(); - p->add_msg_if_player( m_good, - _( "Something about how this spell works just clicked! You gained a level!" ) ); - } else { - spell_being_cast.gain_exp( exp_gained ); - p->add_msg_if_player( m_good, _( "You gain %i experience. New total %i." ), exp_gained, - spell_being_cast.xp() ); + if( !no_mana ) { + // pay the cost + int cost = spell_being_cast.energy_cost( *p ); + switch( spell_being_cast.energy_source() ) { + case mana_energy: + p->magic->mod_mana( *p, -cost ); + break; + case stamina_energy: + p->mod_stamina( -cost ); + break; + case bionic_energy: + p->mod_power_level( -units::from_kilojoule( cost ) ); + break; + case hp_energy: + blood_magic( p, cost ); + break; + case fatigue_energy: + p->mod_fatigue( cost ); + break; + case none_energy: + default: + break; } - if( spell_being_cast.get_level() != old_level ) { - g->events().send( spell_being_cast.id(), - spell_being_cast.get_level() ); + spell_being_cast.use_components( *p ); + } + if( level_override == -1 ) { + if( !spell_being_cast.is_max_level() ) { + // reap the reward + int old_level = spell_being_cast.get_level(); + if( old_level == 0 ) { + spell_being_cast.gain_level(); + p->add_msg_if_player( m_good, + _( "Something about how this spell works just clicked! You gained a level!" ) ); + } else { + spell_being_cast.gain_exp( exp_gained ); + p->add_msg_if_player( m_good, _( "You gain %i experience. New total %i." ), exp_gained, + spell_being_cast.xp() ); + } + if( spell_being_cast.get_level() != old_level ) { + g->events().send( spell_being_cast.id(), + spell_being_cast.get_level() ); + } } } - } - if( !act->targets.empty() && act->targets.front() ) { - item &it = *act->targets.front(); - if( !it.has_flag( flag_USE_PLAYER_ENERGY ) ) { - p->consume_charges( it, it.type->charges_to_use() ); + if( !act->targets.empty() && act->targets.front() ) { + item &it = *act->targets.front(); + if( !it.has_flag( flag_USE_PLAYER_ENERGY ) ) { + p->consume_charges( it, it.type->charges_to_use() ); + } } } -} -void activity_handlers::study_spell_do_turn( player_activity *act, player *p ) -{ - if( !character_funcs::can_see_fine_details( *p ) ) { - act->values[2] = -1; - act->moves_left = 0; - return; - } - if( act->get_str_value( 1 ) == "study" ) { - spell &studying = p->magic->get_spell( spell_id( act->name ) ); - if( act->get_str_value( 0 ) == "gain_level" ) { - if( studying.get_level() < act->get_value( 1 ) ) { - act->moves_left = 1000000; - } else { - act->moves_left = 0; + void activity_handlers::study_spell_do_turn( player_activity * act, player * p ) { + if( !character_funcs::can_see_fine_details( *p ) ) { + act->values[2] = -1; + act->moves_left = 0; + return; + } + if( act->get_str_value( 1 ) == "study" ) { + spell &studying = p->magic->get_spell( spell_id( act->name ) ); + if( act->get_str_value( 0 ) == "gain_level" ) { + if( studying.get_level() < act->get_value( 1 ) ) { + act->moves_left = 1000000; + } else { + act->moves_left = 0; + } } + const int xp = roll_remainder( studying.exp_modifier( *p ) / to_turns( 6_seconds ) ); + act->values[0] += xp; + studying.gain_exp( xp ); } - const int xp = roll_remainder( studying.exp_modifier( *p ) / to_turns( 6_seconds ) ); - act->values[0] += xp; - studying.gain_exp( xp ); } -} - -void activity_handlers::study_spell_finish( player_activity *act, player *p ) -{ - act->set_to_null(); - const int total_exp_gained = act->get_value( 0 ); - if( act->get_str_value( 1 ) == "study" ) { - p->add_msg_if_player( m_good, _( "You gained %i experience from your study session." ), - total_exp_gained ); - const spell &sp = p->magic->get_spell( spell_id( act->name ) ); - p->practice( sp.skill(), total_exp_gained, sp.get_difficulty() ); - } else if( act->get_str_value( 1 ) == "learn" && act->values[2] == 0 ) { - p->magic->learn_spell( act->name, *p ); - } - if( act->values[2] == -1 ) { - p->add_msg_if_player( m_bad, _( "It's too dark to read." ) ); + void activity_handlers::study_spell_finish( player_activity * act, player * p ) { + act->set_to_null(); + const int total_exp_gained = act->get_value( 0 ); + + if( act->get_str_value( 1 ) == "study" ) { + p->add_msg_if_player( m_good, _( "You gained %i experience from your study session." ), + total_exp_gained ); + const spell &sp = p->magic->get_spell( spell_id( act->name ) ); + p->practice( sp.skill(), total_exp_gained, sp.get_difficulty() ); + } else if( act->get_str_value( 1 ) == "learn" && act->values[2] == 0 ) { + p->magic->learn_spell( act->name, *p ); + } + if( act->values[2] == -1 ) { + p->add_msg_if_player( m_bad, _( "It's too dark to read." ) ); + } } -} -//This is just used for robofac_intercom_mission_2 -void activity_handlers::mind_splicer_finish( player_activity *act, player *p ) -{ - act->set_to_null(); + //This is just used for robofac_intercom_mission_2 + void activity_handlers::mind_splicer_finish( player_activity * act, player * p ) { + act->set_to_null(); - if( act->targets.size() != 1 || !act->targets[0] ) { - debugmsg( "Incompatible arguments to: activity_handlers::mind_splicer_finish" ); - return; + if( act->targets.size() != 1 || !act->targets[0] ) { + debugmsg( "Incompatible arguments to: activity_handlers::mind_splicer_finish" ); + return; + } + item &data_card = *act->targets[0]; + p->add_msg_if_player( m_info, _( "…you finally find the memory banks." ) ); + p->add_msg_if_player( m_info, _( "The kit makes a copy of the data inside the bionic." ) ); + data_card.contents.clear_items(); + data_card.put_in( item::spawn( itype_mind_scan_robofac ) ); } - item &data_card = *act->targets[0]; - p->add_msg_if_player( m_info, _( "…you finally find the memory banks." ) ); - p->add_msg_if_player( m_info, _( "The kit makes a copy of the data inside the bionic." ) ); - data_card.contents.clear_items(); - data_card.put_in( item::spawn( itype_mind_scan_robofac ) ); -}