Skip to content

Commit

Permalink
Merge pull request #72615 from RenechCDDA/dropping_safes_is_unsafe
Browse files Browse the repository at this point in the history
[CR] Reign in the damage from items dropped from height, with math
  • Loading branch information
Maleclypse committed Mar 28, 2024
2 parents 10c525d + c96be03 commit ac793dc
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 40 deletions.
4 changes: 0 additions & 4 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ static const quality_id qual_LIFT( "LIFT" );
static const skill_id skill_cooking( "cooking" );
static const skill_id skill_melee( "melee" );
static const skill_id skill_survival( "survival" );
static const skill_id skill_throw( "throw" );
static const skill_id skill_weapon( "weapon" );

static const species_id species_ROBOT( "ROBOT" );
Expand Down Expand Up @@ -14600,9 +14599,6 @@ bool item::on_drop( const tripoint &pos, map &m )

avatar &player_character = get_avatar();

// set variable storing information of character dropping item
dropped_char_stats.throwing = player_character.get_skill_level( skill_throw );

return type->drop_action && type->drop_action.call( &player_character, *this, pos );
}

Expand Down
6 changes: 0 additions & 6 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,6 @@ template<>
struct enum_traits<iteminfo::flags> {
static constexpr bool is_flag_enum = true;
};
// Currently used to store the only throwing stats of character dropping this item. Default is -1
struct dropped_by_character_stats {
float throwing;
};

iteminfo vol_to_info( const std::string &type, const std::string &left,
const units::volume &vol, int decimal_places = 2, bool lower_is_better = true );
Expand Down Expand Up @@ -236,8 +232,6 @@ class item : public visitable

~item() override;

struct dropped_by_character_stats dropped_char_stats = { -1.0f };

/** Return a pointer-like type that's automatically invalidated if this
* item is destroyed or assigned-to */
safe_reference<item> get_safe_reference();
Expand Down
92 changes: 62 additions & 30 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2864,10 +2864,10 @@ void map::drop_items( const tripoint &p )
// rather than disappearing if it would be overloaded

tripoint below( p );
int height_fallen = 0;
int max_height_fallen = 0;
while( !has_floor_or_water( below ) ) {
below.z--;
height_fallen++;
max_height_fallen++;
}

if( below == p ) {
Expand All @@ -2877,53 +2877,85 @@ void map::drop_items( const tripoint &p )
float damage_total = 0.0f;
for( item &i : items ) {
units::mass wt_dropped = i.weight();
float item_density = i.get_base_material().density();
float damage = 5 * to_kilogram( wt_dropped ) * height_fallen * item_density;

if( max_height_fallen <= 0 ) {
debugmsg( "Tried to calculate damage for falling item, but item somehow fell less than one z-level!" );
max_height_fallen = 1;
}
Creature *creature_below = get_creature_tracker().creature_at( below );
double height_fallen = max_height_fallen;
if( creature_below ) {
// Discount most of the first z-level's falling distance, depending on how big the creature is
// Characters (player/NPC) are normally medium, which is 0.5 or standing about 1.2m tall.
// This is shorter than the average adult, but it's an *okay* approximation.
height_fallen -= occupied_tile_fraction( creature_below->get_size() );
}
// in meters, assuming one z-level is ~2.5m.
const double distance_to_fall = height_fallen * 2.5;

// in meters per second (squared).
const double gravity_acceleration_constant = 9.8;

// in seconds.
double falling_time = sqrt( 2 * distance_to_fall / gravity_acceleration_constant );

// in meters per second.
double velocity_at_impact = gravity_acceleration_constant * falling_time;

// in joules.
double impact_energy = to_kilogram( wt_dropped ) * std::pow( velocity_at_impact, 2.0 ) / 2.0;

// in faces smashed (parts per hundred).
double damage = sqrt( impact_energy );
damage_total += damage;

add_item_or_charges( below, i );

// Bash creature standing below
Creature *creature_below = get_creature_tracker().creature_at( below );
if( creature_below ) {
// creature's dodge modifier
float dodge_mod = creature_below->dodge_roll();
// if item dropped by character their throwing skill modifier -1 if not dropped by a character
float throwing_mod = i.dropped_char_stats.throwing == -1.0f ? 0.0f : 5 *
i.dropped_char_stats.throwing;

// values calibrated so that %hit chance starts from 60% going up and down according to the two modifiers
float hit_mod = ( throwing_mod + 18 ) / ( dodge_mod + 15 );
// Most of the threat comes from the projectile going very fast before it enters a creature's vertical FOV
// or e.g. it fell from so high up that there was no indication of something coming down before it (possibly) hits
float hit_mod = velocity_at_impact / ( dodge_mod + 5 );

int creature_hit_chance = rng( 0, 100 );
creature_hit_chance /= hit_mod * occupied_tile_fraction( creature_below->get_size() );
double avoid_chance = hit_mod * occupied_tile_fraction( creature_below->get_size() );
// We use sqrt here because it trends back towards 1. Spectacularly low hit_mod will still have SOME chance to hit while
// hits of >1 avoid_chance are not always going to strike the head
creature_hit_chance /= sqrt( avoid_chance );
bodypart_id hit_part;
if( creature_hit_chance < 15 ) {
add_msg_if_player_sees( creature_below->pos(), _( "Falling %s hits %s in the head!" ), i.tname(),
creature_below->get_name() );
creature_below->deal_damage( nullptr, bodypart_id( "head" ), damage_instance( damage_bash,
damage ) );
hit_part = creature_below->get_random_body_part_of_type( body_part_type::type::head );
} else if( creature_hit_chance < 30 ) {
add_msg_if_player_sees( creature_below->pos(), _( "Falling %s hits %s in the torso!" ), i.tname(),
creature_below->get_name() );
creature_below->deal_damage( nullptr, bodypart_id( "torso" ), damage_instance( damage_bash,
damage ) );
} else if( creature_hit_chance < 65 ) {
add_msg_if_player_sees( creature_below->pos(), _( "Falling %s hits %s in the left arm!" ),
i.tname(), creature_below->get_name() );
creature_below->deal_damage( nullptr, bodypart_id( "arm_l" ), damage_instance( damage_bash,
damage ) );
hit_part = creature_below->get_random_body_part_of_type( body_part_type::type::torso );
} else if( creature_hit_chance < 90 ) {
hit_part = creature_below->get_random_body_part_of_type( body_part_type::type::arm );
} else if( creature_hit_chance < 100 ) {
add_msg_if_player_sees( creature_below->pos(), _( "Falling %s hits %s in the right arm!" ),
i.tname(), creature_below->get_name() );
creature_below->deal_damage( nullptr, bodypart_id( "arm_r" ), damage_instance( damage_bash,
damage ) );
hit_part = creature_below->get_random_body_part_of_type( body_part_type::type::leg );
} else {
add_msg_if_player_sees( creature_below->pos(), _( "Falling %s misses the %s!" ), i.tname(),
creature_below->get_name() );
add_msg_if_player_sees( creature_below->pos(), _( "Falling %1$s misses %2$s!" ), i.tname(),
creature_below->disp_name() );
}
// Did we hit at all? Then run the message.
if( creature_hit_chance < 100 ) {
if( hit_part.is_valid() && !creature_below->is_monster() ) {
//~First positional argument: Item name. Second: Name of a person (e.g. "Jane") or player (e.g. "you"). Third: Body part name, accusative.
add_msg_if_player_sees( creature_below->pos(), _(
"Falling %1$s hits %2$s on the %3$s for %4$i damage!" ),
i.tname(), creature_below->disp_name(), hit_part->accusative, static_cast<int>( damage ) );
} else {
add_msg_if_player_sees( creature_below->pos(), _( "Falling %1$s hits %2$s for %3$i damage!" ),
i.tname(), creature_below->disp_name(), static_cast<int>( damage ) );
}
// FIXME: Hardcoded damage type!
creature_below->deal_damage( nullptr, hit_part, damage_instance( damage_bash, damage ) );
}
}

// Bash items at bottom since currently bash_items only bash glass items
// FIXME: Hardcoded damage type!
int chance = static_cast<int>( 200 * i.resist( damage_bash, true ) / damage + 1 );
if( one_in( chance ) ) {
i.inc_damage();
Expand Down

0 comments on commit ac793dc

Please sign in to comment.