From 857ff8b9711f97e7c5c778837f173c6e7bf0443d Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 1 Apr 2019 21:26:36 +0200 Subject: [PATCH] Faliure, xp gain, and rot reimplementations As well as: -Apply suggestions from code review -Fix crafting_interruption test -Cleanup --- data/json/items/generic.json | 5 +- data/json/player_activities.json | 8 -- src/activity_handlers.cpp | 38 ++++---- src/activity_handlers.h | 2 - src/crafting.cpp | 141 ++++++++++------------------- src/item.cpp | 148 ++++++++++++++++++++++--------- src/item.h | 6 +- src/iuse.cpp | 3 +- src/player.cpp | 1 - src/player.h | 1 - src/player_activity.cpp | 31 +------ src/player_activity.h | 7 -- src/savegame_legacy.cpp | 2 +- tests/crafting_test.cpp | 29 +++++- 14 files changed, 215 insertions(+), 207 deletions(-) diff --git a/data/json/items/generic.json b/data/json/items/generic.json index d6a364ba5451b..14a194f04fd62 100644 --- a/data/json/items/generic.json +++ b/data/json/items/generic.json @@ -2998,9 +2998,8 @@ "id": "craft", "symbol": "%", "color": "white", - "name": "in progress %s", - "name_plural": "none", - "description": "This is an in progress %s. It is %s percent complete.", + "name": "in progress craft", + "description": "This is an in progress craft.", "price": 0, "weight": 1, "volume": 1, diff --git a/data/json/player_activities.json b/data/json/player_activities.json index 4951e773e3d83..96dc4f27e46dd 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -34,14 +34,6 @@ "type": "activity_type", "stop_phrase": "Stop crafting?", "based_on": "neither", - "refuel_fires": true, - "no_resume": true - }, - { - "id": "ACT_LONGCRAFT", - "type": "activity_type", - "stop_phrase": "Stop crafting?", - "based_on": "neither", "refuel_fires": true }, { diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 2f49aa52cafdd..d717c83dd50d6 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -58,7 +58,6 @@ const std::map< activity_id, std::function activity_handlers::do_turn_functions = { { activity_id( "ACT_BURROW" ), burrow_do_turn }, { activity_id( "ACT_CRAFT" ), craft_do_turn }, - { activity_id( "ACT_LONGCRAFT" ), craft_do_turn }, { activity_id( "ACT_FILL_LIQUID" ), fill_liquid_do_turn }, { activity_id( "ACT_PICKAXE" ), pickaxe_do_turn }, { activity_id( "ACT_DROP" ), drop_do_turn }, @@ -137,8 +136,6 @@ activity_handlers::finish_functions = { { activity_id( "ACT_WAIT_NPC" ), wait_npc_finish }, { activity_id( "ACT_SOCIALIZE" ), socialize_finish }, { activity_id( "ACT_TRY_SLEEP" ), try_sleep_finish }, - // { activity_id( "ACT_CRAFT" ), craft_finish }, - // { activity_id( "ACT_LONGCRAFT" ), longcraft_finish }, { activity_id( "ACT_DISASSEMBLE" ), disassemble_finish }, { activity_id( "ACT_BUILD" ), build_finish }, { activity_id( "ACT_VIBE" ), vibe_finish }, @@ -2633,11 +2630,24 @@ void activity_handlers::try_sleep_finish( player_activity *act, player *p ) void activity_handlers::craft_do_turn( player_activity *act, player *p ) { - int pos = act->values.front(); - item &craft = p->i_at( pos ); + item *craft = act->targets.front().get_item(); - const recipe &rec = craft.get_making(); + if( !craft->is_craft() ) { + debugmsg( "ACT_CRAFT target '%s' is not a craft. Aborting ACT_CRAFT.", craft->tname() ); + act->set_to_null(); + return; + } + if( !p->has_item( *craft ) ) { + p->add_msg_if_player( "%s no longer has the target '%s.' Aborting ACT_CRAFT.", + p->disp_name(), craft->tname() ); + act->set_to_null(); + return; + } + + const recipe &rec = craft->get_making(); const float crafting_speed = p->crafting_speed_multiplier( rec, true ); + const bool is_long = act->values[0]; + act->set_to_null(); if( crafting_speed <= 0.0f ) { if( p->lighting_craft_speed_multiplier( rec ) <= 0.0f ) { @@ -2645,7 +2655,6 @@ void activity_handlers::craft_do_turn( player_activity *act, player *p ) } else { p->add_msg_if_player( m_bad, _( "You are too frustrated to continue and just give up." ) ); } - p->cancel_activity(); return; } if( calendar::once_every( 1_hours ) && crafting_speed < 0.75f ) { @@ -2653,22 +2662,21 @@ void activity_handlers::craft_do_turn( player_activity *act, player *p ) p->add_msg_if_player( m_bad, _( "You can't focus and are working slowly." ) ); } - craft.item_counter += crafting_speed * p->get_moves(); + craft->item_counter += crafting_speed * p->get_moves(); p->set_moves( 0 ); - act->set_to_null(); - - if( craft.item_counter >= rec.time ) { - item craft_copy = craft; - p->i_rem( pos ); + + if( craft->item_counter >= rec.time ) { + item craft_copy = p->i_rem( craft ); p->complete_craft( craft_copy ); - if( act->values[1] ) { // If this is a long craft + if( is_long ) { if( p->making_would_work( p->lastrecipe, craft_copy.charges ) ) { p->last_craft->execute(); } } } else { p->assign_activity( activity_id( "ACT_CRAFT" ) ); - p->activity.values.push_back( pos ); + p->activity.targets.push_back( item_location( *p, craft ) ); + p->activity.values.push_back( is_long ); } } diff --git a/src/activity_handlers.h b/src/activity_handlers.h index 9adfcd5524eef..c14cf4c9139dd 100644 --- a/src/activity_handlers.h +++ b/src/activity_handlers.h @@ -120,8 +120,6 @@ void wait_weather_finish( player_activity *act, player *p ); void wait_npc_finish( player_activity *act, player *p ); void socialize_finish( player_activity *act, player *p ); void try_sleep_finish( player_activity *act, player *p ); -// void craft_finish( player_activity *act, player *p ); -// void longcraft_finish( player_activity *act, player *p ); void disassemble_finish( player_activity *act, player *p ); void build_finish( player_activity *act, player *p ); void vibe_finish( player_activity *act, player *p ); diff --git a/src/crafting.cpp b/src/crafting.cpp index 4aaaecdcbb95e..7b7aaf1e302cb 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -383,7 +383,7 @@ void player::make_craft_with_command( const recipe_id &id_to_make, int batch_siz // @param offset is the index of the created item in the range [0, batch_size-1], // it makes sure that the used items are distributed equally among the new items. -void set_components( std::vector &components, const std::list &used, +void set_components( std::list &components, const std::list &used, const int batch_size, const size_t offset ) { if( batch_size <= 1 ) { @@ -432,29 +432,6 @@ std::list player::consume_components_for_craft( const recipe &making, int return used; } -std::list player::consume_some_components_for_craft( const recipe &making, int batch_size ) -{ - std::list used; - if( has_trait( trait_id( "DEBUG_HS" ) ) ) { - return used; - } - const auto &req = making.requirements(); - int cou = 0; - for( const auto &it : req.get_components() ) { - // Each component currently has 50% chance of not being consumed - // Skip first item so failed craft with one item recipe always loses component - if( cou == 0 || one_in( 2 ) ) { - std::list tmp = consume_items( it, batch_size ); - used.splice( used.end(), tmp ); - } - ++cou; - } - for( const auto &it : req.get_tools() ) { - consume_tools( it, batch_size ); - } - return used; -} - static void set_item_food( item &newit ) { // TODO: encapsulate this into some function @@ -469,20 +446,20 @@ static void finalize_crafted_item( item &newit ) } } -static item *set_item_inventory( item &newit ) +static item *set_item_inventory( player &p, item &newit ) { item *ret_val = &null_item_reference(); if( newit.made_of( LIQUID ) ) { g->handle_all_liquid( newit, PICKUP_RANGE ); } else { - g->u.inv.assign_empty_invlet( newit, g->u ); + p.inv.assign_empty_invlet( newit, p ); // We might not have space for the item - if( !g->u.can_pickVolume( newit ) ) { //Accounts for result_mult - put_into_vehicle_or_drop( g->u, item_drop_reason::too_large, { newit } ); - } else if( !g->u.can_pickWeight( newit, !get_option( "DANGEROUS_PICKUPS" ) ) ) { - put_into_vehicle_or_drop( g->u, item_drop_reason::too_heavy, { newit } ); + if( !p.can_pickVolume( newit ) ) { //Accounts for result_mult + put_into_vehicle_or_drop( p, item_drop_reason::too_large, { newit } ); + } else if( !p.can_pickWeight( newit, !get_option( "DANGEROUS_PICKUPS" ) ) ) { + put_into_vehicle_or_drop( p, item_drop_reason::too_heavy, { newit } ); } else { - ret_val = &g->u.i_add( newit ); + ret_val = &p.i_add( newit ); add_msg( m_info, "%c - %s", ret_val->invlet == 0 ? ' ' : ret_val->invlet, ret_val->tname().c_str() ); } @@ -490,19 +467,35 @@ static item *set_item_inventory( item &newit ) return ret_val; } -time_duration get_rot_since( const time_point &start, const time_point &end, - const tripoint &location ); // weather.cpp +static void return_all_components_for_craft( player &p, std::list &used ) +{ + // Force add here since the craft item is removed immediately after + for( item &it : used ) { + set_item_inventory( p, it ); + } +} + +static void return_some_components_for_craft( player &p, std::list &used ) +{ + for( std::list::iterator it = used.begin(); it != used.end(); ++it ) { + // Force add here since the craft item is removed in the same turn + // Each component has a 50% chance of being returned + // Never return the first component + if( it != used.begin() && one_in( 2 ) ) { + set_item_inventory( p, *it ); + } + } +} void player::start_craft( const recipe &making, int batch_size, bool is_long ) { if( making.ident().is_null() ) { debugmsg( "no recipe with id %s found", activity.name ); - // activity.set_to_null(); return; } // Use up the components and tools - std::list used = consume_components_for_craft( making, batch_size ); + const std::list used = consume_components_for_craft( making, batch_size ); if( last_craft->has_cached_selections() && used.empty() ) { // This signals failure, even though there seem to be several paths where it shouldn't... return; @@ -511,20 +504,19 @@ void player::start_craft( const recipe &making, int batch_size, bool is_long ) reset_encumbrance(); // in case we were wearing something just consumed } - item craft( &making, batch_size, std::vector( used.begin(), used.end() ) ); + item craft( &making, batch_size, used ); - int pos = get_item_position( set_item_inventory( craft ) ); - if( pos == INT_MIN ) { + item *craft_in_inventory = set_item_inventory( *this, craft ); + if( !has_item( *craft_in_inventory ) ) { add_msg_if_player( _( "Activate the %s to start crafting" ), craft.tname() ); } else { add_msg_player_or_npc( string_format( pgettext( "in progress craft", "You start working on the %s" ), craft.tname() ), string_format( pgettext( "in progress craft", " starts working on the %s" ), - craft.tname() - ) ); + craft.tname() ) ); assign_activity( activity_id( "ACT_CRAFT" ) ); - activity.values.push_back( pos ); + activity.targets.push_back( item_location( *this, craft_in_inventory ) ); activity.values.push_back( is_long ); } } @@ -532,10 +524,9 @@ void player::start_craft( const recipe &making, int batch_size, bool is_long ) void player::complete_craft( item &craft ) { const recipe &making = craft.get_making(); // Which recipe is it? - int batch_size = craft.charges; + const int batch_size = craft.charges; - /* to be modified and moved to ACT_CRAFT - --------------------------- + /* to be modified and moved to ACT_CRAFT */ int secondary_dice = 0; int secondary_difficulty = 0; for( const auto &pr : making.required_skills ) { @@ -550,9 +541,9 @@ void player::complete_craft( item &craft ) } else { skill_dice = get_skill_level( making.skill_used ) * 4; } - */ - auto helpers = g->u.get_crafting_helpers(); - /* + + auto helpers = get_crafting_helpers(); + for( const npc *np : helpers ) { if( np->get_skill_level( making.skill_used ) >= get_skill_level( making.skill_used ) ) { @@ -638,62 +629,28 @@ void player::complete_craft( item &craft ) } } + std::list &used = craft.components; + // Messed up badly; waste some components. if( making.difficulty != 0 && diff_roll > skill_roll * ( 1 + 0.1 * rng( 1, 5 ) ) ) { add_msg( m_bad, _( "You fail to make the %s, and waste some materials." ), making.result_name() ); - consume_some_components_for_craft( making, batch_size ); - activity.set_to_null(); + return_some_components_for_craft( *this, used ); return; // Messed up slightly; no components wasted. } else if( diff_roll > skill_roll ) { add_msg( m_neutral, _( "You fail to make the %s, but don't waste any materials." ), making.result_name() ); - //this method would only have been called from a place that nulls activity.type, - //so it appears that it's safe to NOT null that variable here. - //rationale: this allows certain contexts (e.g. ACT_LONGCRAFT) to distinguish major and minor failures + return_all_components_for_craft( *this, used ); return; } - */ - /* to be deleted - // If we're here, the craft was a success! - // Use up the components and tools - std::list used = consume_components_for_craft( making, batch_size ); - if( last_craft->has_cached_selections() && used.empty() ) { - // This signals failure, even though there seem to be several paths where it shouldn't... - return; - } - if( !used.empty() ) { - reset_encumbrance(); // in case we were wearing something just consumed - } - */ - std::list used( craft.components.begin(), craft.components.end() ); - - const time_point now = calendar::turn; - time_point start_turn = now; - tripoint craft_pos = pos(); - // TODO: figure out how to do rot - // if( activity.values.size() > 1 && !activity.coords.empty() ) { - // start_turn = activity.values.at( 1 ); - // craft_pos = activity.coords.at( 0 ); - // } else { - // // either something went wrong or player had an old binary and saved - // // the game right in the middle of crafting, and then updated their - // // binary, so we didn't grab these values before starting the craft - // debugmsg( "Missing activity start time and temperature, using current val" ); - // } - const time_duration rot_points = get_rot_since( start_turn, now, craft_pos ); - double max_relative_rot = 0; - // We need to cycle all the used ingredients and find the most rotten item, - // this will then set our relative rot for the crafted items. + // Take the relative rot of the most rotten component + double max_relative_rot = 0.0; for( const item &it : used ) { if( !it.goes_bad() ) { continue; } - // make a copy of the item so we can play with its rot values - item it_copy = it; - it_copy.mod_rot( -rot_points ); - max_relative_rot = std::max( max_relative_rot, it_copy.get_relative_rot() ); + max_relative_rot = std::max( max_relative_rot, it.get_relative_rot() ); } // Set up the new item, and assign an inventory letter if available @@ -826,7 +783,7 @@ void player::complete_craft( item &craft ) } finalize_crafted_item( newit ); - set_item_inventory( newit ); + set_item_inventory( *this, newit ); } if( making.has_byproducts() ) { @@ -844,7 +801,7 @@ void player::complete_craft( item &craft ) } } finalize_crafted_item( bp ); - set_item_inventory( bp ); + set_item_inventory( *this, bp ); } } @@ -1517,7 +1474,7 @@ void player::complete_disassemble( int item_pos, const tripoint &loc, // If the components aren't empty, we want items exactly identical to them // Even if the best-fit recipe does not involve those items - std::vector components = dis_item.components; + std::list components = dis_item.components; // If the components are empty, item is the default kind and made of default components if( components.empty() ) { const bool uncraft_liquids_contained = dis.has_flag( "UNCRAFT_LIQUIDS_CONTAINED" ); @@ -1586,7 +1543,7 @@ void player::complete_disassemble( int item_pos, const tripoint &loc, act_item.item_tags.insert( "FILTHY" ); } - for( item::t_item_vector::iterator a = dis_item.components.begin(); a != dis_item.components.end(); + for( std::list::iterator a = dis_item.components.begin(); a != dis_item.components.end(); ++a ) { if( a->type == newit.type ) { act_item = *a; diff --git a/src/item.cpp b/src/item.cpp index c5f87f41420e1..730eb6d3c0277 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -209,13 +209,28 @@ item::item( const itype *type, time_point turn, solitary_tag ) item::item( const itype_id &id, time_point turn, solitary_tag tag ) : item( find_type( id ), turn, tag ) {} -item::item( const recipe *rec, long qty, t_item_vector items ) +item::item( const recipe *rec, long qty, std::list items ) : item( "craft", calendar::turn, qty ) { making = rec; components = items; -} + // Process this item to apply rot to components + active = true; + + for( const item &it : components ) { + if( it.has_flag( "HIDDEN_POISON" ) ) { + set_flag( "HIDDEN_POISON" ); + } + if( it.has_flag( "HIDDEN_HALLU" ) ) { + set_flag( "HIDDEN_HALLU" ); + } + if( it.is_filthy() ) { + set_flag( "FILTHY" ); + } + } + +} item item::make_corpse( const mtype_id &mt, time_point turn, const std::string &name ) { @@ -2079,9 +2094,10 @@ std::string item::info( std::vector &info, const iteminfo_query *parts } else if( idescription != item_vars.end() ) { info.push_back( iteminfo( "DESCRIPTION", idescription->second ) ); } else { - if( is_craft() && making ) { - const int percent_progress = item_counter / making->time; - info.push_back( iteminfo( "DESCRIPTION", string_format( _( type->description.c_str() ), + if( is_craft() ) { + const std::string desc = _( "This is an in progress %s. It is %d percent complete." ); + const int percent_progress = 100 * item_counter / making->time; + info.push_back( iteminfo( "DESCRIPTION", string_format( desc, making->result_name(), percent_progress ) ) ); } else { @@ -2596,6 +2612,22 @@ const std::string &item::symbol() const return type->sym; } +// Used for craft entity +static const item *get_most_rotten_component( const item &craft ) +{ + const item *most_rotten = nullptr; + for( const item &it : craft.components ) { + if( it.goes_bad() ) { + if( most_rotten == nullptr ) { + most_rotten = ⁢ + } else if( it.get_relative_rot() > most_rotten->get_relative_rot() ) { + most_rotten = ⁢ + } + } + } + return most_rotten; +} + nc_color item::color_in_inventory() const { player &u = g->u; // TODO: make a const reference @@ -2611,7 +2643,9 @@ nc_color item::color_in_inventory() const ret = c_brown; } else if( has_flag( "LEAK_DAM" ) && has_flag( "RADIOACTIVE" ) && damage() > 0 ) { ret = c_light_green; - } else if( active && !is_food() && !is_food_container() ) { // Active items show up as yellow + + } else if( active && !is_food() && !is_food_container() && !is_craft() ) { + // Active items show up as yellow ret = c_yellow; } else if( is_food() || is_food_container() ) { const bool preserves = type->container && type->container->preserves; @@ -2653,6 +2687,11 @@ nc_color item::color_in_inventory() const case NO_TOOL: break; } + } else if( is_craft() ) { + const item *most_rotten = get_most_rotten_component( *this ); + if( most_rotten ) { + ret = most_rotten->color_in_inventory(); + } } else if( is_gun() ) { // Guns are green if you are carrying ammo for them // ltred if you have ammo but no mags @@ -2847,6 +2886,26 @@ void item::on_damage( int, damage_type ) } +static std::string get_dirt_rot_suffixes( const item &it ) +{ + std::stringstream ret; + ret.str( "" ); + + if( it.item_tags.count( "DIRTY" ) ) { + ret << _( " (dirty)" ); + } else if( it.rotten() ) { + ret << _( " (rotten)" ); + } else if( it.has_flag( "MUSHY" ) ) { + ret << _( " (mushy)" ); + } else if( it.is_going_bad() ) { + ret << _( " (old)" ); + } else if( it.is_fresh() ) { + ret << _( " (fresh)" ); + } + + return ret.str(); +} + std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int truncate ) const { std::stringstream ret; @@ -2938,18 +2997,13 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t maintext = ret.str(); } else if( is_craft() ) { ret.str( "" ); - if( making ) { - ret << string_format( label( quantity ), making->result_name() ); - } else { - ret << string_format( label( quantity ), item().tname() ); - } + const std::string name = _( "in progress %s" ); + ret << string_format( name, making->result_name() ); if( charges > 1 ) { ret << " (" << charges << ")"; } - if( making ) { - const int percent_progress = 100 * item_counter / making->time; - ret << " (" << percent_progress << "%)"; - } + const int percent_progress = 100 * item_counter / making->time; + ret << " (" << percent_progress << "%)"; maintext = ret.str(); } else if( contents.size() == 1 ) { const item &contents_item = contents.front(); @@ -2978,17 +3032,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t ret << _( " (hallucinogenic)" ); } - if( item_tags.count( "DIRTY" ) ) { - ret << _( " (dirty)" ); - } else if( rotten() ) { - ret << _( " (rotten)" ); - } else if( has_flag( "MUSHY" ) ) { - ret << _( " (mushy)" ); - } else if( is_going_bad() ) { - ret << _( " (old)" ); - } else if( is_fresh() ) { - ret << _( " (fresh)" ); - } + ret << get_dirt_rot_suffixes( *this ); if( has_flag( "HOT" ) ) { ret << _( " (hot)" ); @@ -3003,6 +3047,18 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } } + if( is_craft() ) { + if( has_flag( "HIDDEN_POISON" ) && g->u.get_skill_level( skill_survival ) >= 3 ) { + ret << _( " (poisonous)" ); + } else if( has_flag( "HIDDEN_HALLU" ) && g->u.get_skill_level( skill_survival ) >= 5 ) { + ret << _( " (hallucinogenic)" ); + } + const item *most_rotten = get_most_rotten_component( *this ); + if( most_rotten ) { + ret << get_dirt_rot_suffixes( *most_rotten ); + } + } + const bool small = g->u.has_trait( trait_id( "SMALL2" ) ) || g->u.has_trait( trait_id( "SMALL_OK" ) ); const bool fits = has_flag( "FIT" ); @@ -3051,7 +3107,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } if( active && ( has_flag( "WATER_EXTINGUISH" ) || has_flag( "LITCIG" ) ) ) { ret << _( " (lit)" ); - } else if( active && !is_food() && !is_corpse() && ( typeId().length() < 3 || + } else if( active && !is_food() && !is_corpse() && !is_craft() && ( typeId().length() < 3 || typeId().compare( typeId().length() - 3, 3, "_on" ) != 0 ) ) { // Usually the items whose ids end in "_on" have the "active" or "on" string already contained // in their name, also food is active while it rots. @@ -4805,7 +4861,7 @@ bool item::is_salvageable() const bool item::is_craft() const { - return typeId() == "craft"; + return making != nullptr; } bool item::is_funnel_container( units::volume &bigger_than ) const @@ -6769,12 +6825,12 @@ bool item::needs_processing() const { return active || has_flag( "RADIO_ACTIVATION" ) || ( is_container() && !contents.empty() && contents.front().needs_processing() ) || - is_artifact() || ( is_food() ); + is_artifact() || is_food() || is_craft(); } int item::processing_speed() const { - if( is_corpse() || is_food() || is_food_container() ) { + if( is_corpse() || is_food() || is_food_container() || is_craft() ) { return 100; } // Unless otherwise indicated, update every turn. @@ -7041,6 +7097,21 @@ bool item::process_food( const player *carrier, const tripoint &p, int temp, flo return false; } +bool item::process_craft( const player *carrier, const tripoint &p, int temp, float insulation ) +{ + if( carrier != nullptr && carrier->has_item( *this ) ) { + temp += 5; // body heat increases inventory temperature + insulation *= 1.5; // clothing provides inventory some level of insulation + } + + // Process all stored components + // Already applied body heat, so pass nullptr instead of carrier + for( item &it : components ) { + it.process( nullptr, p, false, temp, insulation ); + } + return false; +} + void item::process_artifact( player *carrier, const tripoint & /*pos*/ ) { if( !is_artifact() ) { @@ -7389,7 +7460,7 @@ bool item::process_tool( player *carrier, const tripoint &pos ) bool item::process( player *carrier, const tripoint &pos, bool activate ) { - if( is_food() || is_food_container() ) { + if( is_food() || is_food_container() || is_craft() ) { return process( carrier, pos, activate, g->get_temperature( pos ), 1 ); } else { return process( carrier, pos, activate, 0, 1 ); @@ -7429,13 +7500,7 @@ bool item::process( player *carrier, const tripoint &pos, bool activate, int tem return false; } - // item_counter is used differently for in progress crafts and should not be - // decremented here. - if( is_craft() ) { - return false; - } - - if( !is_food() && item_counter > 0 ) { + if( !is_food() && !is_craft() && item_counter > 0 ) { item_counter--; } @@ -7453,7 +7518,10 @@ bool item::process( player *carrier, const tripoint &pos, bool activate, int tem if( has_flag( "FAKE_SMOKE" ) && process_fake_smoke( carrier, pos ) ) { return true; } - if( is_food() && process_food( carrier, pos, temp, insulation ) ) { + if( is_food() && process_food( carrier, pos, temp, insulation ) ) { + return true; + } + if( is_craft() && process_craft( carrier, pos, temp, insulation ) ) { return true; } if( is_corpse() && process_corpse( carrier, pos ) ) { @@ -7830,7 +7898,7 @@ std::vector item::get_uncraft_components() const const recipe &item::get_making() const { if( !making ) { - debugmsg( "Craft '%s' has a null recipe", tname() ); + debugmsg( "'%s' is not a craft or has a null recipe", tname() ); return recipe().ident().obj(); } return *making; diff --git a/src/item.h b/src/item.h index d632ba72ebbdb..a411ee7ef13b8 100644 --- a/src/item.h +++ b/src/item.h @@ -188,7 +188,7 @@ class item : public visitable item( const itype *type, time_point turn, solitary_tag ); /** For constructing in-progress crafts */ - item( const recipe *rec, long qty, std::vector items ); + item( const recipe *rec, long qty, std::list items ); /** * Filter converting this instance to another type preserving all other aspects @@ -1841,6 +1841,7 @@ class item : public visitable // processing types, just to make the process function cleaner. // The interface is the same as for @ref process. bool process_food( const player *carrier, const tripoint &p, int temp, float insulation ); + bool process_craft( const player *carrier, const tripoint &p, int temp, float insulation ); bool process_corpse( player *carrier, const tripoint &pos ); bool process_wet( player *carrier, const tripoint &pos ); bool process_litcig( player *carrier, const tripoint &pos ); @@ -1852,11 +1853,10 @@ class item : public visitable public: static const long INFINITE_CHARGES; - typedef std::vector t_item_vector; const itype *type; std::list contents; - t_item_vector components; + std::list components; /** What faults (if any) currently apply to this item */ std::set faults; std::set item_tags; // generic item specific flags diff --git a/src/iuse.cpp b/src/iuse.cpp index 1eccebf217592..48830644a8974 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -8002,7 +8002,8 @@ int iuse::craft( player *p, item *it, bool, const tripoint & ) string_format( pgettext( "in progress craft", " starts working on the %s" ), it->tname() ) ); p->assign_activity( activity_id( "ACT_CRAFT" ) ); - p->activity.values.push_back( pos ); + p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.values.push_back( 0 ); // Not a long craft } return 0; } diff --git a/src/player.cpp b/src/player.cpp index 3ecd89b7a0717..2813fc6f0fd57 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -11278,7 +11278,6 @@ void player::assign_activity( const player_activity &act, bool allow_resume ) add_msg_if_player( _( "You resume your task." ) ); activity = backlog.front(); backlog.pop_front(); - activity.resume_with( act ); } else { if( activity ) { backlog.push_front( activity ); diff --git a/src/player.h b/src/player.h index 47c85dfe6809d..cfe39e6b7ba80 100644 --- a/src/player.h +++ b/src/player.h @@ -1379,7 +1379,6 @@ class player : public Character void make_all_craft( const recipe_id &id, int batch_size ); std::list consume_components_for_craft( const recipe &making, int batch_size, bool ignore_last = false ); - std::list consume_some_components_for_craft( const recipe &making, int batch_size ); /** consume components and create an active, in progress craft containing them */ void start_craft( const recipe &making, int batch_size, bool is_long ); void complete_craft( item &craft ); diff --git a/src/player_activity.cpp b/src/player_activity.cpp index a09b9b5eb1800..d7b005a860ff2 100644 --- a/src/player_activity.cpp +++ b/src/player_activity.cpp @@ -158,20 +158,7 @@ bool player_activity::can_resume_with( const player_activity &other, const Chara return false; } - if( id() == activity_id( "ACT_CRAFT" ) || id() == activity_id( "ACT_LONGCRAFT" ) ) { - // The last value is a time stamp, and the last coord is the player - // position. We want to allow either to have changed. - // (This would be much less hacky in the hypothetical future of - // activity_handler_actors). - if( !( values.size() == other.values.size() && - !values.empty() && - std::equal( values.begin(), values.end() - 1, other.values.begin() ) && - coords.size() == other.coords.size() && - !coords.empty() && - std::equal( coords.begin(), coords.end() - 1, other.coords.begin() ) ) ) { - return false; - } - } else if( id() == activity_id( "ACT_CLEAR_RUBBLE" ) ) { + if( id() == activity_id( "ACT_CLEAR_RUBBLE" ) ) { if( other.coords.empty() || other.coords[0] != coords[0] ) { return false; } @@ -213,22 +200,6 @@ bool player_activity::can_resume_with( const player_activity &other, const Chara position == other.position && name == other.name && targets == other.targets; } -void player_activity::resume_with( const player_activity &other ) -{ - if( id() == activity_id( "ACT_CRAFT" ) || id() == activity_id( "ACT_LONGCRAFT" ) ) { - // For crafting actions, we need to update the start turn and position - // to the resumption time values. These are stored in the last - // elements of values and coords respectively. - if( !( !values.empty() && values.size() == other.values.size() && - !coords.empty() && coords.size() == other.coords.size() ) ) { - debugmsg( "Activities incompatible; should not have resumed" ); - return; - } - values.back() = other.values.back(); - coords.back() = other.coords.back(); - } -} - bool player_activity::is_distraction_ignored( distraction_type type ) const { return ignored_distractions.find( type ) != ignored_distractions.end(); diff --git a/src/player_activity.h b/src/player_activity.h index f325b93c1115c..11cf30e1d968d 100644 --- a/src/player_activity.h +++ b/src/player_activity.h @@ -100,13 +100,6 @@ class player_activity * can be resumed instead of starting the other activity. */ bool can_resume_with( const player_activity &other, const Character &who ) const; - /** - * When an old activity A is resumed by a new activity B, normally B is - * discarded and the saved A is simply used in its place. However, - * this will be called on A, passing B as an argument, in case A needs - * to grab any values from B. - */ - void resume_with( const player_activity &other ); bool is_distraction_ignored( distraction_type type ) const; void ignore_distraction( distraction_type type ); diff --git a/src/savegame_legacy.cpp b/src/savegame_legacy.cpp index 50b013aeabc8e..3e08f4e3abda1 100644 --- a/src/savegame_legacy.cpp +++ b/src/savegame_legacy.cpp @@ -540,7 +540,7 @@ void player_activity::deserialize_legacy_type( int legacy_type, activity_id &des activity_id( "ACT_GAME" ), activity_id( "ACT_WAIT" ), activity_id( "ACT_CRAFT" ), - activity_id( "ACT_LONGCRAFT" ), + activity_id::NULL_ID(), // ACT_LONGCRAFT is deprecated activity_id( "ACT_DISASSEMBLE" ), activity_id( "ACT_BUTCHER" ), activity_id( "ACT_LONGSALVAGE" ), diff --git a/tests/crafting_test.cpp b/tests/crafting_test.cpp index 0a658fd1c785e..4539df20543b6 100644 --- a/tests/crafting_test.cpp +++ b/tests/crafting_test.cpp @@ -425,6 +425,29 @@ static int actually_test_craft( const recipe_id &rid, const std::vector to return turns; } +// Resume the first in progress craft found in the player's inventory +static int resume_craft() +{ + item *craft = g->u.items_with( []( const item & itm ) { + return itm.is_craft(); + } ).front(); + const recipe &rec = craft->get_making(); + set_time( midday ); // Ensure light for crafting + REQUIRE( g->u.morale_crafting_speed_multiplier( rec ) == 1.0 ); + REQUIRE( g->u.lighting_craft_speed_multiplier( rec ) == 1.0 ); + REQUIRE( !g->u.activity ); + g->u.use( g->u.get_item_position( craft ) ); + CHECK( g->u.activity ); + CHECK( g->u.activity.id() == activity_id( "ACT_CRAFT" ) ); + int turns = 0; + while( g->u.activity.id() == activity_id( "ACT_CRAFT" ) ) { + ++turns; + g->u.moves = 100; + g->u.activity.do_turn( g->u ); + } + return turns; +} + static void verify_inventory( const std::vector &has, const std::vector &hasnt ) { @@ -462,11 +485,11 @@ TEST_CASE( "crafting_interruption" ) SECTION( "interrupted_craft" ) { int turns_taken = actually_test_craft( test_recipe, tools, 2 ); CHECK( turns_taken == 3 ); - verify_inventory( { "scrap" }, { "crude_picklock" } ); + verify_inventory( { "craft" }, { "crude_picklock" } ); SECTION( "resumed_craft" ) { - turns_taken = actually_test_craft( test_recipe, tools, INT_MAX ); + turns_taken = resume_craft(); CHECK( turns_taken == expected_turns_taken - 2 ); - verify_inventory( { "crude_picklock" }, { "scrap" } ); + verify_inventory( { "crude_picklock" }, { "craft" } ); } } }