Skip to content

Commit

Permalink
Properly move pulping to activity actor, tweak it (CleverRaven#79483)
Browse files Browse the repository at this point in the history
* Refactor pulping

* please clang tidy

* Make pulping work again

* please iwyu and clang

* Update src/character.cpp

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* please clang more

* Update src/activity_actor_definitions.h

* Apply suggestions from code review

Co-authored-by: moxian <[email protected]>

* Adress suggestions

* Update data/json/monsters/monster_flags.json

Co-authored-by: Maleclypse <[email protected]>

* please clang

* please clang and iwyu

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: moxian <[email protected]>
Co-authored-by: Maleclypse <[email protected]>
  • Loading branch information
4 people authored Feb 21, 2025
1 parent 8523a27 commit da181ea
Show file tree
Hide file tree
Showing 12 changed files with 480 additions and 131 deletions.
6 changes: 6 additions & 0 deletions data/json/monsters/monster_flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -643,5 +643,11 @@
"id": "TEEP_IMMUNE",
"type": "monster_flag",
"//": "Immune to telepathic damage/detection"
},
{
"id": "PULP_PRYING",
"type": "monster_flag",
"//": "This monster has significant armor, and it is much harder to pulp it without a proper tool",
"//2": "Rule of thumb: if monster has something like a spine character wants to break, and it is covered by any armor, use this flag"
}
]
2 changes: 1 addition & 1 deletion data/json/player_activities.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@
"type": "activity_type",
"activity_level": "EXTRA_EXERCISE",
"verb": "smashing",
"based_on": "neither",
"based_on": "speed",
"can_resume": false
},
{
Expand Down
302 changes: 207 additions & 95 deletions src/activity_actor.cpp

Large diffs are not rendered by default.

47 changes: 33 additions & 14 deletions src/activity_actor_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "character.h"
#include "clone_ptr.h"
#include "contents_change_handler.h"
#include "game.h"
#include "handle_liquid.h"
#include "item.h"
#include "itype.h"
Expand All @@ -40,6 +41,7 @@ class SkillLevel;
class player_activity;

struct islot_book;
struct pulp_data;

class aim_activity_actor : public activity_actor
{
Expand Down Expand Up @@ -2412,36 +2414,53 @@ class pulp_activity_actor : public activity_actor
{
public:
pulp_activity_actor() = default;
explicit pulp_activity_actor( const tripoint_abs_ms placement,
const bool pulp_acid = false ) : placement( { placement } ),
num_corpses( 0 ), pulp_acid( pulp_acid ) {}
explicit pulp_activity_actor( const std::set<tripoint_abs_ms> &placement,
const bool pulp_acid = false ) : placement( placement ), num_corpses( 0 ), pulp_acid( pulp_acid ) {}
explicit pulp_activity_actor( const tripoint_abs_ms placement ) : placement( { placement } ),
num_corpses( 0 ) {}
explicit pulp_activity_actor( const std::set<tripoint_abs_ms> &placement ) : placement( placement ),
num_corpses( 0 ) {}
const activity_id &get_type() const override {
static const activity_id ACT_PULP( "ACT_PULP" );
return ACT_PULP;
}

void start( player_activity &act, Character &who ) override;
void start( player_activity &act, Character &you ) override;
void do_turn( player_activity &act, Character &you ) override;
bool punch_corpse_once( item &corpse, Character &you, tripoint_bub_ms pos, map &here );
void finish( player_activity &, Character & ) override;

void send_final_message( Character &you ) const;

std::unique_ptr<activity_actor> clone() const override {
return std::make_unique<pulp_activity_actor>( *this );
auto plp = std::make_unique<pulp_activity_actor>( *this );
plp.get()->current_pos_iter = plp.get()->placement.begin();
return plp;
}

void serialize( JsonOut &jsout ) const override;
static std::unique_ptr<activity_actor> deserialize( JsonValue &jsin );

private:
bool can_resume_with_internal( const activity_actor &other,
const Character &/*who*/ ) const override {
const pulp_activity_actor &actor = static_cast<const pulp_activity_actor &>( other );
return actor.pulp_acid == pulp_acid;
}
// tripoints with corpses we need to pulp;
// either single tripoint shoved into set, or 3x3 zone around u/npc
std::set<tripoint_abs_ms> placement;
int num_corpses;
bool pulp_acid;

float float_corpse_damage_accum = 0.0f; // NOLINT(cata-serialize)

int unpulped_corpses_qty = 0;

// query player if they want to pulp corpses that cost more than 10 minutes to pulp
bool too_long_to_pulp = false;
bool too_long_to_pulp_interrupted = false;
// if corpse cost more than hour to pulp, drop it
bool way_too_long_to_pulp = false;

// how many corpses we pulped
int num_corpses = 0;

pulp_data pd;

// what `placement` we are currently at
std::set<tripoint_abs_ms>::const_iterator current_pos_iter; // NOLINT(cata-serialize)
};

class wait_stamina_activity_actor : public activity_actor
Expand Down
37 changes: 37 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9699,6 +9699,43 @@ bool Character::use_charges_if_avail( const itype_id &it, int quantity )
return false;
}

std::pair<float, item> Character::get_best_weapon_by_damage_type( const damage_type_id dmg_type )
const
{
std::pair<float, item> best_weapon = std::make_pair( 0, item() );

cache_visit_items_with( "best_damage_" + dmg_type.str(), &item::is_melee, [&best_weapon, this,
dmg_type]( const item & it ) {
damage_instance di;
roll_damage( dmg_type, false, di, true, it, attack_vector_id::NULL_ID(),
sub_bodypart_str_id::NULL_ID(), 1.f );
for( damage_unit &du : di.damage_units ) {
if( du.type == dmg_type && best_weapon.first < du.amount ) {
best_weapon = std::make_pair( du.amount, it );
}
}
} );

return best_weapon;
}

std::pair<int, const item *> Character::get_best_tool( const quality_id quality ) const
{
// todo: check edge case of you having bionic tool/mutation,
// something that max_quality() is aware but max_quality() is not?

const int max_qual = max_quality( quality );

std::vector<const item *> nit = cache_get_items_with( "best_quality_" + quality.str(), {},
[quality, max_qual]( const item & i ) {
return i.get_quality( quality ) == max_qual;
} );

const item *it = random_entry( nit );
const std::pair<int, const item *> best_tool = std::make_pair( max_qual, it );
return best_tool;
}

units::energy Character::available_ups() const
{
units::energy available_charges = 0_kJ;
Expand Down
4 changes: 4 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -2964,6 +2964,10 @@ class Character : public Creature, public visitable
// Uses up charges
bool use_charges_if_avail( const itype_id &it, int quantity );

std::pair<float, item> get_best_weapon_by_damage_type( damage_type_id dmg_type ) const;

std::pair<int, const item *> get_best_tool( quality_id quality ) const;

/**
* Available ups from all sources
* Sum of mech, bionic UPS and UPS
Expand Down
150 changes: 150 additions & 0 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,13 @@ static const proficiency_id proficiency_prof_wound_care_expert( "prof_wound_care

static const quality_id qual_BUTCHER( "BUTCHER" );
static const quality_id qual_CUT_FINE( "CUT_FINE" );
static const quality_id qual_PRY( "PRY" );

static const skill_id skill_dodge( "dodge" );
static const skill_id skill_firstaid( "firstaid" );
static const skill_id skill_gun( "gun" );
static const skill_id skill_survival( "survival" );
static const skill_id skill_swimming( "swimming" );

static const species_id species_PLANT( "PLANT" );

Expand Down Expand Up @@ -14289,6 +14291,154 @@ void game::climb_down_using( const tripoint_bub_ms &examp, climbing_aid_id aid_i
}
}

pulp_data game::calculate_character_ability_to_pulp( const Character &you )
{
pulp_data pd;

double pulp_power_bash = 1;

const std::pair<float, item> pair_bash = you.get_best_weapon_by_damage_type( damage_bash );
pulp_power_bash = pair_bash.first;
pd.bash_tool = pair_bash.second.display_name();

if( pair_bash.second.has_flag( flag_MESSY ) || pair_bash.first > 40 ) {
pd.mess_radius = 2;
}

const double weight_factor = units::to_kilogram( you.get_weight() ) / 10;
const double athletic_factor = std::min( 9.0f, you.get_skill_level( skill_swimming ) + 3 );
const int strength_factor = you.get_str() / 2;
// stomp deliver about 5000-13000 N of force, beginner boxer deal 2500 N of force in it's punch
// ballpark of 13 damage for untrained human vs 30+ for highly trained person of average weight
const double pulp_power_stomps = athletic_factor + weight_factor + strength_factor;

// if no tool is bashy enough, use only stomps
// if stomps are too weak, use only bash
// if roughly same, use both
float bash_factor = 0.0;
if( pulp_power_bash < pulp_power_stomps * 0.5 ) {
bash_factor = pulp_power_stomps;
pd.stomps_only = true;
} else if( pulp_power_stomps < pulp_power_bash * 0.5 ) {
bash_factor = pulp_power_bash;
pd.weapon_only = true;
} else {
bash_factor = std::max( pulp_power_stomps, pulp_power_bash );
}

add_msg_debug( debugmode::DF_ACTIVITY,
"you: %s, bash weapon: %s, bash damage: %s, pulp_power_bash: %s, pulp_power_stomps: %s",
you.name.c_str(), pair_bash.second.display_name().c_str(), pair_bash.first, pulp_power_bash,
pulp_power_stomps, bash_factor );

bash_factor = std::pow( bash_factor, 1.8f );
std::pair<int, const item *> pair_cut = you.get_best_tool( qual_BUTCHER );
pd.cut_quality = pair_cut.first;
if( pd.cut_quality > 5 ) {
pd.can_severe_cutting = true;
pd.cut_tool = item::nname( pair_cut.second->typeId() );
}

std::pair<int, const item *> pair_pry;
if( you.max_quality( qual_PRY ) > 0 ) {
pd.can_pry_armor = true;
pair_pry = you.get_best_tool( qual_PRY );
pd.pry_tool = item::nname( pair_pry.second->typeId() );
}

add_msg_debug( debugmode::DF_ACTIVITY,
"final bash factor: %s, butcher tool name: %s, butcher tool quality: %s, prying tool name (if used): %s",
bash_factor, pd.cut_tool, pd.cut_quality, pd.pry_tool );

pd.pulp_power = bash_factor
* std::sqrt( you.get_skill_level( skill_survival ) + 2 )
* ( pd.can_severe_cutting ? 1 : 0.85 );

add_msg_debug( debugmode::DF_ACTIVITY, "final pulp_power: %s", pd.pulp_power );

// since we are trying to depict pulping as more involved than you Isaac Clarke the corpse 100% of time,
// we would assume there is some time between attacks, where you try to reach some sweet spot,
// cut the part with a knife, or plain rest because you are not athletic enough
// REGEN_RATE + 1 purely to prevent you regening all of your stamina in the process + apply muscle strain when it be implemented
pd.pulp_effort = get_option<float>( "PLAYER_BASE_STAMINA_REGEN_RATE" ) + 1;

return pd;
}

pulp_data game::calculate_pulpability( const Character &you, const mtype &corpse_mtype )
{
pulp_data pd = calculate_character_ability_to_pulp( you );

return calculate_pulpability( you, corpse_mtype, pd );
}

pulp_data game::calculate_pulpability( const Character &you, const mtype &corpse_mtype,
pulp_data pd )
{
double pow_factor;
if( corpse_mtype.size == creature_size::huge ) {
pow_factor = 1.2;
} else if( corpse_mtype.size == creature_size::large ) {
pow_factor = 1.1;
} else {
pow_factor = 1;
}

// in seconds
int time_to_pulp =
( std::pow( units::to_liter( corpse_mtype.volume ), pow_factor ) * 1000 ) / pd.pulp_power;

// +25% to pulp time if char knows no weakpoints of monster
// -25% if knows all of them
if( !corpse_mtype.families.families.empty() ) {
float wp_known = 0;
for( const weakpoint_family &wf : corpse_mtype.families.families ) {
if( you.has_proficiency( wf.proficiency ) ) {
++wp_known;
} else if( !pd.unknown_prof.has_value() ) {
pd.unknown_prof = wf.proficiency;
}
}
time_to_pulp *= 1.25 - 0.5 * wp_known / corpse_mtype.families.families.size();
}

const bool acid_immune = you.is_immune_damage( damage_acid ) ||
you.is_immune_field( fd_acid );
// this corpse is acid, and you are not immune to it
pd.acid_corpse = corpse_mtype.bloodType().obj().has_acid && !acid_immune;

// if acid, you prefer to mainly cut corpse instead of bashing it, to not spray acid in your eyes
if( pd.acid_corpse ) {
time_to_pulp *= ( 300 - pd.cut_quality * 2 ) / 100;
}

// you have a hard time pulling armor to reach important parts of this monster
if( corpse_mtype.has_flag( mon_flag_PULP_PRYING ) ) {
if( pd.can_pry_armor ) {
time_to_pulp *= 1.25;
} else {
time_to_pulp *= 1.7;
}
}

pd.time_to_pulp = time_to_pulp;

return pd;
}

bool game::can_pulp_corpse( const Character &you, const mtype &corpse_mtype )
{
pulp_data pd = calculate_pulpability( you, corpse_mtype );

return can_pulp_corpse( pd );
}

bool game::can_pulp_corpse( const pulp_data &pd )
{
// if pulping is longer than an hour, this is a hard no
return pd.time_to_pulp < 3600;
}

namespace cata_event_dispatch
{
void avatar_moves( const tripoint_abs_ms &old_abs_pos, const avatar &u, const map &m )
Expand Down
33 changes: 33 additions & 0 deletions src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class vehicle;
class viewer;
enum action_id : int;
struct special_game;
struct mtype;
struct visibility_variables;
template <typename Tripoint> class tripoint_range;

Expand Down Expand Up @@ -129,6 +130,32 @@ struct w_map {
catacurses::window win;
};

struct pulp_data {
// how far the splatter goes
int mess_radius = 1;
int cut_quality;
// how much damage you deal to corpse every second, average of multiple values
float pulp_power;
// how much stamina is consumed after each punch
float pulp_effort;
int time_to_pulp;
// potential prof we can learn by pulping
std::optional<proficiency_id> unknown_prof;
// for monsters with PULP_PRYING flag
bool can_pry_armor = false;
// if acid corpse, we start to cut really slow
bool acid_corpse = false;
// all used in ending messages
bool can_severe_cutting = false;
bool stomps_only = false;
bool weapon_only = false;
bool used_pry = false;
bool couldnt_use_pry = false;
std::string bash_tool;
std::string cut_tool;
std::string pry_tool;
};

bool is_valid_in_w_terrain( const point_rel_ms &p );
namespace turn_handler
{
Expand Down Expand Up @@ -1305,6 +1332,12 @@ class game
const tripoint_bub_ms &examp,
climbing_aid_id aid,
bool deploy_affordance = false );

pulp_data calculate_character_ability_to_pulp( const Character &you );
pulp_data calculate_pulpability( const Character &you, const mtype &corpse_mtype );
pulp_data calculate_pulpability( const Character &you, const mtype &corpse_mtype, pulp_data pd );
bool can_pulp_corpse( const Character &you, const mtype &corpse_mtype );
bool can_pulp_corpse( const pulp_data &pd );
};

// Returns temperature modifier from direct heat radiation of nearby sources
Expand Down
Loading

0 comments on commit da181ea

Please sign in to comment.