Skip to content

Commit 94ea8d5

Browse files
authored
Allow resuming hacksaw cutting metal with different tools (#85010)
1 parent 3c2d45c commit 94ea8d5

File tree

4 files changed

+103
-33
lines changed

4 files changed

+103
-33
lines changed

src/activity_actor.cpp

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,22 +1267,7 @@ void hacksaw_activity_actor::start( player_activity &act, Character &/*who*/ )
12671267
return;
12681268
}
12691269

1270-
int qual = 0;
1271-
if( type.has_value() ) {
1272-
item veh_tool = item( type.value(), calendar::turn );
1273-
for( const std::pair<const quality_id, int> &quality : type.value()->qualities ) {
1274-
if( quality.first == qual_SAW_M ) {
1275-
qual = quality.second;
1276-
}
1277-
}
1278-
for( const std::pair<const quality_id, int> &quality : type.value()->charged_qualities ) {
1279-
if( quality.first == qual_SAW_M ) {
1280-
qual = std::max( qual, quality.second );
1281-
}
1282-
}
1283-
} else {
1284-
qual = tool->get_quality( qual_SAW_M );
1285-
}
1270+
int qual = get_tool_quality();
12861271
if( qual < 2 ) {
12871272
if( !testing ) {
12881273
debugmsg( "Item %s with 'HACKSAW' use action requires SAW_M quality of at least 2.",
@@ -1298,6 +1283,7 @@ void hacksaw_activity_actor::start( player_activity &act, Character &/*who*/ )
12981283
act.moves_total = moves_before_quality / ( qual - 1 );
12991284
add_msg_debug( debugmode::DF_ACTIVITY, "%s moves_total: %d", act.id().str(), act.moves_total );
13001285
act.moves_left = act.moves_total;
1286+
moves_left = act.moves_left;
13011287
}
13021288

13031289
static void tool_out_of_charges( Character &who, const std::string &tool_name )
@@ -1311,8 +1297,9 @@ static void tool_out_of_charges( Character &who, const std::string &tool_name )
13111297
who.cancel_activity();
13121298
}
13131299

1314-
void hacksaw_activity_actor::do_turn( player_activity &/*act*/, Character &who )
1300+
void hacksaw_activity_actor::do_turn( player_activity &act, Character &who )
13151301
{
1302+
moves_left = act.moves_left;
13161303
map &here = get_map();
13171304

13181305
std::string method = "HACKSAW";
@@ -1428,18 +1415,58 @@ void hacksaw_activity_actor::finish( player_activity &act, Character &who )
14281415
act.set_to_null();
14291416
}
14301417

1431-
//TODO: Make hacksawing resumable with different tools with the same SAW_M quality.
1432-
//Potentially make it possible to resume with different SAW_M quality and recalculate time to completion partway through.
1433-
//This is really not a big deal, and will cost a few minutes of in game time and part of a medium battery charge at worst as someone accidentally cancels the activity_actor and has to start again
1434-
//If a few minutes are life and death, sawing metal may not be the wise choice in the first place.
1435-
bool hacksaw_activity_actor::can_resume_with_internal( const activity_actor &other,
1436-
const Character &/*who*/ ) const
1418+
float hacksaw_activity_actor::exertion_level() const
14371419
{
1438-
const hacksaw_activity_actor &actor = static_cast<const hacksaw_activity_actor &>
1439-
( other );
1440-
return actor.target == target && ( ( veh_pos.has_value() &&
1441-
veh_pos.value() == actor.veh_pos.value_or( tripoint_bub_ms::max ) ) ||
1442-
actor.tool.operator == ( tool ) );
1420+
if( tool->ammo_required() ) {
1421+
return LIGHT_EXERCISE;
1422+
} else {
1423+
return get_type()->exertion_level();
1424+
}
1425+
}
1426+
1427+
int hacksaw_activity_actor::get_tool_quality() const
1428+
{
1429+
int qual = 0;
1430+
if( type.has_value() ) {
1431+
item veh_tool = item( type.value(), calendar::turn );
1432+
for( const std::pair<const quality_id, int> &quality : type.value()->qualities ) {
1433+
if( quality.first == qual_SAW_M ) {
1434+
qual = quality.second;
1435+
}
1436+
}
1437+
for( const std::pair<const quality_id, int> &quality : type.value()->charged_qualities ) {
1438+
if( quality.first == qual_SAW_M ) {
1439+
qual = std::max( qual, quality.second );
1440+
}
1441+
}
1442+
} else {
1443+
qual = tool->get_quality( qual_SAW_M );
1444+
}
1445+
1446+
return qual;
1447+
}
1448+
1449+
void hacksaw_activity_actor::set_resume_values_internal( const activity_actor &other,
1450+
const Character &/*who*/ )
1451+
{
1452+
// This method recalculates moves_left based on tool quality comparison but it doesn't have
1453+
// access to update the moves_left on the corresponding player_activity. You must set the
1454+
// player_activity's moves_left separately after resuming the activity_actor.
1455+
1456+
const hacksaw_activity_actor &actor = static_cast<const hacksaw_activity_actor &>( other );
1457+
1458+
int actor_qual = actor.get_tool_quality();
1459+
int qual = get_tool_quality();
1460+
1461+
int new_moves_left = -1;
1462+
if( actor_qual > 1 ) {
1463+
new_moves_left = moves_left * ( qual - 1 ) / ( actor_qual - 1 );
1464+
}
1465+
add_msg_debug( debugmode::DF_ACTIVITY,
1466+
"Hacksaw resume. Actor quality: %d, quality: %d, moves_left: %d, new_moves_left: %d.",
1467+
actor_qual, qual, moves_left, new_moves_left );
1468+
moves_left = new_moves_left;
1469+
tool = actor.tool;
14431470
}
14441471

14451472
void hacksaw_activity_actor::serialize( JsonOut &jsout ) const
@@ -1449,6 +1476,7 @@ void hacksaw_activity_actor::serialize( JsonOut &jsout ) const
14491476
jsout.member( "tool", tool );
14501477
jsout.member( "type", type );
14511478
jsout.member( "veh_pos", veh_pos );
1479+
jsout.member( "moves_left", moves_left );
14521480
jsout.end_object();
14531481
}
14541482

@@ -1460,6 +1488,7 @@ std::unique_ptr<activity_actor> hacksaw_activity_actor::deserialize( JsonValue &
14601488
data.read( "tool", actor.tool );
14611489
data.read( "type", actor.type );
14621490
data.read( "veh_pos", actor.veh_pos );
1491+
data.read( "moves_left", actor.moves_left );
14631492
return actor.clone();
14641493
}
14651494

src/activity_actor_definitions.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,8 @@ class hacksaw_activity_actor : public activity_actor
566566
void start( player_activity &act, Character &who ) override;
567567
void do_turn( player_activity &/*act*/, Character &who ) override;
568568
void finish( player_activity &act, Character &who ) override;
569+
float exertion_level() const override;
570+
int get_tool_quality() const;
569571

570572
std::unique_ptr<activity_actor> clone() const override {
571573
return std::make_unique<hacksaw_activity_actor>( *this );
@@ -574,15 +576,24 @@ class hacksaw_activity_actor : public activity_actor
574576
void serialize( JsonOut &jsout ) const override;
575577
static std::unique_ptr<activity_actor> deserialize( JsonValue &jsin );
576578

579+
int moves_left;
577580
// debugmsg causes a backtrace when fired during cata_test
578581
bool testing = false; // NOLINT(cata-serialize)
579582
private:
580583
tripoint_bub_ms target;
581584
item_location tool;
582585
std::optional<itype_id> type;
583586
std::optional<tripoint_bub_ms> veh_pos;
587+
584588
bool can_resume_with_internal( const activity_actor &other,
585-
const Character &/*who*/ ) const override;
589+
const Character &/*who*/ ) const override {
590+
const hacksaw_activity_actor &actor = static_cast<const hacksaw_activity_actor &>
591+
( other );
592+
return actor.target == target;
593+
}
594+
595+
void set_resume_values_internal( const activity_actor &other,
596+
const Character &/*who*/ ) override;
586597
};
587598

588599
class hacking_activity_actor : public activity_actor

src/iuse.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4748,16 +4748,45 @@ std::optional<int> iuse::hacksaw( Character *p, item *it, const tripoint_bub_ms
47484748
p->assign_activity( hacksaw_activity_actor( pnt, it->typeId(), it_pnt ) );
47494749
}
47504750

4751-
std::string query = string_format( _( "Cut up metal using your %1$s?" ), it->tname() );
4751+
std::string query;
4752+
if( p->activity.moves_left == p->activity.moves_total ) {
4753+
query += string_format( _( "Cut up metal using your %1$s?" ), it->tname() );
4754+
} else {
4755+
query += string_format( _( "Resume cutting up metal using your %1$s?" ), it->tname() );
4756+
}
4757+
4758+
// HACK: Update the player_activity moves_left based on progress stored in the activity_actor.
4759+
int activity_actor_moves_left = static_cast<const hacksaw_activity_actor &>(
4760+
*p->activity.actor ).moves_left;
4761+
p->activity.moves_left = activity_actor_moves_left;
4762+
47524763
query += "\n";
47534764
query += _( "Time to complete: " );
4754-
int required_moves = p->activity.moves_total;
4755-
time_duration required_time = time_duration::from_turns( required_moves / p->get_speed() );
4765+
int required_moves = p->activity.moves_left;
4766+
add_msg_debug( debugmode::DF_ACTIVITY, "iuse hacksaw required_moves: %d.", required_moves );
4767+
4768+
const float weary_mult = p->exertion_adjusted_move_multiplier( p->activity.exertion_level() );
4769+
time_duration required_time = time_duration::from_turns( required_moves * weary_mult /
4770+
p->get_speed() );
47564771
const std::string time_string = colorize( to_string( required_time, true ), c_light_gray );
47574772
query += time_string;
47584773

4774+
if( it->ammo_required() ) {
4775+
const int charges_needed = it->ammo_required() * required_moves / 100;
4776+
query += "\n";
4777+
if( it->ammo_current()->nname( charges_needed ) == "battery" ) {
4778+
query += string_format( _( "This will require %d kJ." ), charges_needed );
4779+
} else {
4780+
query += string_format(
4781+
_( "This will require %d %s." ),
4782+
charges_needed, it->ammo_current()->nname( charges_needed )
4783+
);
4784+
}
4785+
}
4786+
47594787
if( !query_yn( query ) ) {
47604788
p->cancel_activity();
4789+
p->add_msg_if_player( m_info, _( "You decide not to cut up this metal." ) );
47614790
}
47624791

47634792
return std::nullopt;

src/player_activity.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ class player_activity
3434
{
3535
private:
3636
activity_id type;
37-
cata::clone_ptr<activity_actor> actor;
3837

3938
std::set<distraction_type> ignored_distractions; // NOLINT(cata-serialize)
4039

4140
bool ignoreQuery = false; // NOLINT(cata-serialize)
4241

4342
public:
43+
cata::clone_ptr<activity_actor> actor;
44+
4445
/** Total number of moves (1/100th of a second) required to complete the activity */
4546
int moves_total = 0;
4647
/** The number of moves (1/100th of a second) remaining in this activity before it is complete. */

0 commit comments

Comments
 (0)