Skip to content

Commit

Permalink
Resume butchering
Browse files Browse the repository at this point in the history
Allows resuming previously started butchering activities.

This is done by storing a var on the corpse item with the progress. For
example, aborting a dissection after 90% has been completed will store
var `DISSECT_progress`=`0.9` on the corpse.

Displays percent of previous progress in the butchery ui, if it has
already started earlier.
  • Loading branch information
inogenous committed May 9, 2024
1 parent 581c99b commit c08fba4
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 27 deletions.
78 changes: 59 additions & 19 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,14 @@ activity_handlers::do_turn_functions = {
{ ACT_MULTIPLE_CRAFT, multiple_craft_do_turn },
{ ACT_MULTIPLE_DIS, multiple_dis_do_turn },
{ ACT_MULTIPLE_READ, multiple_read_do_turn },
{ ACT_BLEED, butcher_do_turn },
{ ACT_BUTCHER, butcher_do_turn },
{ ACT_BUTCHER_FULL, butcher_do_turn },
{ ACT_FIELD_DRESS, butcher_do_turn },
{ ACT_SKIN, butcher_do_turn },
{ ACT_QUARTER, butcher_do_turn },
{ ACT_DISMEMBER, butcher_do_turn },
{ ACT_DISSECT, butcher_do_turn },
};

const std::map< activity_id, std::function<void( player_activity *, Character * )> >
Expand Down Expand Up @@ -426,6 +434,53 @@ static bool butcher_dissect_item( item &what, const tripoint &pos,
return success;
}

static std::string butcher_progress_var( const butcher_type action )
{
return io::enum_to_string( action ) + "_progress";
}

// How much of `butcher_type` has already been completed, in range [0..1], 0=not started yet, 1=completed.
// used for resuming previously started butchery
double butcher_get_progress( const item &corpse_item, const butcher_type action )
{
return corpse_item.get_var( butcher_progress_var( action ), 0.0 );
}

static butcher_type get_butcher_type( player_activity *act )
{
butcher_type action = butcher_type::QUICK;
if( act->id() == ACT_BUTCHER ) {
action = butcher_type::QUICK;
} else if( act->id() == ACT_BUTCHER_FULL ) {
action = butcher_type::FULL;
} else if( act->id() == ACT_FIELD_DRESS ) {
action = butcher_type::FIELD_DRESS;
} else if( act->id() == ACT_QUARTER ) {
action = butcher_type::QUARTER;
} else if( act->id() == ACT_DISSECT ) {
action = butcher_type::DISSECT;
} else if( act->id() == ACT_BLEED ) {
action = butcher_type::BLEED;
} else if( act->id() == ACT_SKIN ) {
action = butcher_type::SKIN;
} else if( act->id() == ACT_DISMEMBER ) {
action = butcher_type::DISMEMBER;
}
return action;
}

void activity_handlers::butcher_do_turn( player_activity *act, Character * )
{
if( act->targets.empty() || act->moves_total <= 0 || act->moves_left <= 0 ) {
return;
}
const butcher_type action = get_butcher_type( act );
const double progress = static_cast<double>( act->moves_total - act->moves_left ) /
act->moves_total;
item &corpse_item = *act->targets.back();
corpse_item.set_var( butcher_progress_var( action ), progress );
}

static void set_up_butchery( player_activity &act, Character &you, butcher_type action )
{
const int factor = you.max_quality( action == butcher_type::DISSECT ? qual_CUT_FINE : qual_BUTCHER,
Expand Down Expand Up @@ -666,7 +721,9 @@ static void set_up_butchery( player_activity &act, Character &you, butcher_type
}
}
}
act.moves_left = butcher_time_to_cut( you, corpse_item, action ) * butchery_requirements.first;
const double progress = butcher_get_progress( corpse_item, action );
act.moves_total = butcher_time_to_cut( you, corpse_item, action ) * butchery_requirements.first;
act.moves_left = act.moves_total - static_cast<int>( act.moves_total * progress );

// We have a valid target, so preform the full finish function
// instead of just selecting the next valid target
Expand Down Expand Up @@ -1256,24 +1313,7 @@ void activity_handlers::butcher_finish( player_activity *act, Character *you )
return;
}

butcher_type action = butcher_type::QUICK;
if( act->id() == ACT_BUTCHER ) {
action = butcher_type::QUICK;
} else if( act->id() == ACT_BUTCHER_FULL ) {
action = butcher_type::FULL;
} else if( act->id() == ACT_FIELD_DRESS ) {
action = butcher_type::FIELD_DRESS;
} else if( act->id() == ACT_QUARTER ) {
action = butcher_type::QUARTER;
} else if( act->id() == ACT_DISSECT ) {
action = butcher_type::DISSECT;
} else if( act->id() == ACT_BLEED ) {
action = butcher_type::BLEED;
} else if( act->id() == ACT_SKIN ) {
action = butcher_type::SKIN;
} else if( act->id() == ACT_DISMEMBER ) {
action = butcher_type::DISMEMBER;
}
const butcher_type action = get_butcher_type( act );

// index is a bool that determines if we are ready to start the next target
if( act->index ) {
Expand Down
2 changes: 2 additions & 0 deletions src/activity_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ bool generic_multi_activity_handler( player_activity &act, Character &you,
void activity_on_turn_fetch( player_activity &, Character *you );
int get_auto_consume_moves( Character &you, bool food );
bool try_fuel_fire( player_activity &act, Character &you, bool starting_fire = false );
double butcher_get_progress( const item &corpse_item, const butcher_type action );

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (src)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (other)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (other)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

Check failure on line 169 in src/activity_handlers.h

View workflow job for this annotation

GitHub Actions / build (other)

parameter 'action' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls,-warnings-as-errors]

enum class item_drop_reason : int {
deliberate,
Expand Down Expand Up @@ -195,6 +196,7 @@ void adv_inventory_do_turn( player_activity *act, Character *you );
void armor_layers_do_turn( player_activity *act, Character *you );
void atm_do_turn( player_activity *act, Character *you );
void build_do_turn( player_activity *act, Character *you );
void butcher_do_turn( player_activity *act, Character *you );
void dismember_do_turn( player_activity *act, Character *you );
void chop_trees_do_turn( player_activity *act, Character *you );
void consume_drink_menu_do_turn( player_activity *act, Character *you );
Expand Down
41 changes: 33 additions & 8 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9467,6 +9467,23 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
}
return to_string_clipped( time_duration::from_moves( time_to_cut ) );
};
auto progress_str = [&]( butcher_type bt ) {
std::string result = "";

Check failure on line 9471 in src/game.cpp

View workflow job for this annotation

GitHub Actions / build (src)

redundant string initialization [readability-redundant-string-init,-warnings-as-errors]
if( index != -1 ) {
const double progress = butcher_get_progress( *corpses[index], bt );
if( progress > 0 ) {
result = string_format( _( "%d%% complete" ), static_cast<int>( 100 * progress ) );
}
} else {
for( const map_stack::iterator &it : corpses ) {
const double progress = butcher_get_progress( *it, bt );
if( progress > 0 ) {
result = _( "partially complete" );
}
}
}
return ( result == "" ) ? "" : ( " " + colorize( result, c_dark_gray ) );

Check failure on line 9485 in src/game.cpp

View workflow job for this annotation

GitHub Actions / build (src)

the 'empty' method should be used to check for emptiness instead of comparing to an empty object [readability-container-size-empty,-warnings-as-errors]
};
const bool enough_light = player_character.fine_detail_vision_mod() <= 4;

const int factor = player_character.max_quality( qual_BUTCHER, PICKUP_RANGE );
Expand Down Expand Up @@ -9529,7 +9546,8 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
const std::string cannot_see = colorize( _( "can't see!" ), c_red );

smenu.addentry_col( static_cast<int>( butcher_type::QUICK ), enough_light,
'B', _( "Quick butchery" ),
'B', _( "Quick butchery" )
+ progress_str( butcher_type::QUICK ),
enough_light ? cut_time( butcher_type::QUICK ) : cannot_see,
string_format( "%s %s",
_( "This technique is used when you are in a hurry, "
Expand All @@ -9539,7 +9557,8 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
"Prevents zombies from raising." ),
msgFactor ) );
smenu.addentry_col( static_cast<int>( butcher_type::FULL ), enough_light,
'b', _( "Full butchery" ),
'b', _( "Full butchery" )
+ progress_str( butcher_type::FULL ),
enough_light ? cut_time( butcher_type::FULL ) : cannot_see,
string_format( "%s %s",
_( "This technique is used to properly butcher a corpse, "
Expand All @@ -9549,7 +9568,8 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
"but it is time consuming." ),
msgFactor ) );
smenu.addentry_col( static_cast<int>( butcher_type::FIELD_DRESS ), enough_light && has_organs,
'f', _( "Field dress corpse" ),
'f', _( "Field dress corpse" )
+ progress_str( butcher_type::FIELD_DRESS ),
enough_light ? ( has_organs ? cut_time( butcher_type::FIELD_DRESS ) :
colorize( _( "has no organs" ), c_red ) ) : cannot_see,
string_format( "%s %s",
Expand All @@ -9560,7 +9580,8 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
"better effects." ),
msgFactor ) );
smenu.addentry_col( static_cast<int>( butcher_type::SKIN ), enough_light && has_skin,
's', _( "Skin corpse" ),
's', _( "Skin corpse" )
+ progress_str( butcher_type::SKIN ),
enough_light ? ( has_skin ? cut_time( butcher_type::SKIN ) : colorize( _( "has no skin" ),
c_red ) ) : cannot_see,
string_format( "%s %s",
Expand All @@ -9571,7 +9592,8 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
"scraps that can be used in other ways." ),
msgFactor ) );
smenu.addentry_col( static_cast<int>( butcher_type::BLEED ), enough_light && has_blood,
'l', _( "Bleed corpse" ),
'l', _( "Bleed corpse" )
+ progress_str( butcher_type::BLEED ),
enough_light ? ( has_blood ? cut_time( butcher_type::BLEED ) : colorize( _( "has no blood" ),
c_red ) ) : cannot_see,
string_format( "%s %s",
Expand All @@ -9581,7 +9603,8 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
"to do a good job." ),
msgFactor ) );
smenu.addentry_col( static_cast<int>( butcher_type::QUARTER ), enough_light,
'k', _( "Quarter corpse" ),
'k', _( "Quarter corpse" )
+ progress_str( butcher_type::QUARTER ),
enough_light ? cut_time( butcher_type::QUARTER ) : cannot_see,
string_format( "%s %s",
_( "By quartering a previously field dressed corpse you will "
Expand All @@ -9591,15 +9614,17 @@ static void butcher_submenu( const std::vector<map_stack::iterator> &corpses, in
"harvest them later." ),
msgFactor ) );
smenu.addentry_col( static_cast<int>( butcher_type::DISMEMBER ), true,
'm', _( "Dismember corpse" ),
'm', _( "Dismember corpse" )
+ progress_str( butcher_type::DISMEMBER ),
cut_time( butcher_type::DISMEMBER ),
string_format( "%s %s",
_( "If you're aiming to just destroy a body outright and don't "
"care about harvesting it, dismembering it will hack it apart "
"in a very short amount of time but yields little to no usable flesh." ),
msgFactor ) );
smenu.addentry_col( static_cast<int>( butcher_type::DISSECT ), enough_light,
'd', _( "Dissect corpse" ),
'd', _( "Dissect corpse" )
+ progress_str( butcher_type::DISSECT ),
enough_light ? cut_time( butcher_type::DISSECT ) : cannot_see,
string_format( "%s %s%s",
_( "By careful dissection of the corpse, you will examine it for "
Expand Down

0 comments on commit c08fba4

Please sign in to comment.