Skip to content

Commit

Permalink
feat(content,balance): Vehicle collision damage logic update, new veh…
Browse files Browse the repository at this point in the history
…icle part flags (#5926)

* Updated vehicle collision logic files

* feat(content,balance): Vehicle collision logic update, new vehicle part flags

* feat(content,balance): Vehicle collision logic update, new vehicle part flags

* feat(content,balance): Vehicle collision damage logic update, new vehicle part flags

Updated vehicle_move.cpp and map.cpp to include comments about impulse

* style(autofix.ci): automated formatting

* feat(content,balance): Vehicle collision damage logic update, new vehicle part flags

Worked in changes based on code review.

* feat(content,balance): Vehicle collision damage logic update, new vehicle part flags

Updated vehicle.cpp (again) to work in comment that I had missed

* Add files via upload

* feat(content,balance): Vehicle collision damage logic update, new vehicle part flags

* Delete vehicle_move.cpp

* Delete vehicle.cpp

* feat(content,balance): Vehicle collision damage logic update, new vehicle part flags

* Corrected map.cpp

* Corrected map.cpp, again

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
Retagin and autofix-ci[bot] authored Jan 15, 2025
1 parent af3b299 commit 305e501
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 96 deletions.
2 changes: 1 addition & 1 deletion data/json/vehicleparts/battery.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"using": [ [ "soldering_standard", 5 ], [ "vehicle_repair_electronics", 1 ], [ "plastics", 1 ] ]
}
},
"flags": [ "FOLDABLE" ]
"flags": [ "FOLDABLE", "SHOCK_RESISTANT" ]
},
{
"id": "battery_motorbike",
Expand Down
12 changes: 6 additions & 6 deletions data/json/vehicleparts/vehicle_parts.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"removal": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "vehicle_bolt", 1 ] ] },
"repair": { "skills": [ [ "mechanics", 2 ] ], "time": "30 m", "using": [ [ "welding_standard", 5 ], [ "steel_tiny", 1 ] ] }
},
"flags": [ "SEAT", "BOARDABLE", "CARGO", "BELTABLE" ],
"flags": [ "SEAT", "BOARDABLE", "CARGO", "BELTABLE", "SHOCK_RESISTANT" ],
"breaks_into": "ig_vp_seat",
"damage_reduction": { "all": 2 }
},
Expand Down Expand Up @@ -151,7 +151,7 @@
"removal": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "vehicle_bolt", 1 ] ] },
"repair": { "skills": [ [ "mechanics", 2 ] ], "time": "30 m", "using": [ [ "welding_standard", 5 ], [ "steel_tiny", 1 ] ] }
},
"flags": [ "BED", "SEAT", "BOARDABLE", "BELTABLE", "CARGO" ],
"flags": [ "BED", "SEAT", "BOARDABLE", "BELTABLE", "CARGO", "SHOCK_RESISTANT" ],
"breaks_into": "ig_vp_seat",
"damage_reduction": { "all": 3 }
},
Expand Down Expand Up @@ -680,7 +680,7 @@
"color": "light_gray",
"broken_symbol": "*",
"broken_color": "dark_gray",
"flags": [ "NO_REPAIR" ],
"flags": [ "NO_REPAIR", "SHOCK_IMMUNE" ],
"requirements": {
"install": { "skills": [ [ "mechanics", 2 ] ], "qualities": [ { "id": "SCREW", "level": 3 }, { "id": "WRENCH", "level": 3 } ] },
"removal": { "skills": [ [ "mechanics", 2 ] ], "using": [ [ "vehicle_screw", 1 ] ] }
Expand Down Expand Up @@ -1641,7 +1641,7 @@
"removal": { "skills": [ [ "mechanics", 1 ] ], "time": "150 s", "using": [ [ "vehicle_screw", 1 ] ] },
"repair": { "skills": [ [ "mechanics", 1 ] ], "time": "20 s", "using": [ [ "adhesive", 1 ], [ "fabric_standard", 1 ] ] }
},
"flags": [ "SEATBELT", "FOLDABLE" ],
"flags": [ "SEATBELT", "FOLDABLE", "SHOCK_IMMUNE" ],
"breaks_into": [ { "item": "nylon", "count": [ 0, 3 ] } ]
},
{
Expand Down Expand Up @@ -1695,7 +1695,7 @@
"removal": { "skills": [ [ "mechanics", 2 ] ], "time": "5 m", "using": [ [ "vehicle_screw", 1 ] ] },
"repair": { "skills": [ [ "mechanics", 2 ] ], "time": "20 s", "using": [ [ "adhesive", 1 ], [ "fabric_standard", 1 ] ] }
},
"flags": [ "SEATBELT", "FOLDABLE" ],
"flags": [ "SEATBELT", "FOLDABLE", "SHOCK_IMMUNE" ],
"breaks_into": [ { "item": "seatbelt", "count": [ 0, 3 ] } ]
},
{
Expand Down Expand Up @@ -2916,7 +2916,7 @@
"using": [ [ "adhesive", 1 ], [ "vehicle_repair_electronics", 1 ] ]
}
},
"flags": [ "WATCH", "ALARMCLOCK", "FOLDABLE" ],
"flags": [ "WATCH", "ALARMCLOCK", "FOLDABLE", "SHOCK_RESISTANT" ],
"breaks_into": [ { "item": "scrap", "prob": 50 } ]
},
{
Expand Down
10 changes: 10 additions & 0 deletions data/json/vehicleparts/vp_flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,15 @@
"context": [ "vehicle_part" ],
"type": "json_flag",
"requires_flag": "WHEEL_MOUNT_SKATEBOARD"
},
{
"id": "SHOCK_IMMUNE",
"context": [ "vehicle_part" ],
"type": "json_flag"
},
{
"id": "SHOCK_RESISTANT",
"context": [ "vehicle_part" ],
"type": "json_flag"
}
]
79 changes: 55 additions & 24 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ static const efftype_id effect_crushed( "crushed" );

static const ter_str_id t_rock_floor_no_roof( "t_rock_floor_no_roof" );

// Conversion constant for 100ths of miles per hour to meters per second
constexpr float velocity_constant = 0.0044704;

#define dbg(x) DebugLog((x),DC::Map)

static location_vector<item> nulitems( new
Expand Down Expand Up @@ -297,6 +300,7 @@ maptile map::maptile_at_internal( const tripoint &p )

// Vehicle functions


VehicleList map::get_vehicles()
{
if( last_full_vehicle_list_dirty ) {
Expand Down Expand Up @@ -682,7 +686,19 @@ vehicle *map::move_vehicle( vehicle &veh, const tripoint &dp, const tileray &fac
} else {
impulse += coll_dmg;
veh.damage( coll.part, coll_dmg, DT_BASH );
veh.damage_all( coll_dmg / 2, coll_dmg, DT_BASH, collision_point );
// Upper bound of shock damage
int shock_max = coll_dmg;
// Lower bound of shock damage
int shock_min = coll_dmg / 2;
float coll_part_bash_resist = veh.part_info( coll.part ).damage_reduction.type_resist(
DT_BASH );
// Reduce shock damage by collision part DR to prevent bushes from damaging car batteries
shock_min = std::max<int>( 0, shock_min - coll_part_bash_resist );
shock_max = std::max<int>( 0, shock_max - coll_part_bash_resist );
// Shock damage decays exponentially, we only want to track shock damage that would cause meaningful damage.
if( shock_min >= 20 ) {
veh.damage_all( shock_min, shock_max, DT_BASH, collision_point );
}
}
}

Expand Down Expand Up @@ -849,18 +865,23 @@ float map::vehicle_vehicle_collision( vehicle &veh, vehicle &veh2,
point epicenter1;
point epicenter2;

float dmg;
float veh1_impulse = 0;
float veh2_impulse = 0;
float delta_vel = 0;
// A constant to tune how many Ns of impulse are equivalent to 1 point of damage, look in vehicle_move.cpp for the impulse to damage function.
const float dmg_adjust = impulse_to_damage( 1 );
float dmg_veh1 = 0;
float dmg_veh2 = 0;
// Vertical collisions will be simpler for a while (1D)
if( !vertical ) {
// For reference, a cargo truck weighs ~25300, a bicycle 690,
// and 38mph is 3800 'velocity'
// Converting away from 100*mph, because mixing unit systems is bad.
// 1 mph = 0.44704m/s = 100 "velocity". For velocity to m/s, *0.0044704
rl_vec2d velo_veh1 = veh.velo_vec();
rl_vec2d velo_veh2 = veh2.velo_vec();
const float m1 = to_kilogram( veh.total_mass() );
const float m2 = to_kilogram( veh2.total_mass() );
//Energy of vehicle1 and vehicle2 before collision
float E = 0.5 * m1 * velo_veh1.magnitude() * velo_veh1.magnitude() +
0.5 * m2 * velo_veh2.magnitude() * velo_veh2.magnitude();

// Collision_axis
point cof1 = veh .rotated_center_of_mass();
Expand All @@ -878,27 +899,30 @@ float map::vehicle_vehicle_collision( vehicle &veh, vehicle &veh2,
// imp? & delta? & final? reworked:
// newvel1 =( vel1 * ( mass1 - mass2 ) + ( 2 * mass2 * vel2 ) ) / ( mass1 + mass2 )
// as per http://en.wikipedia.org/wiki/Elastic_collision
//velocity of veh1 before collision in the direction of collision_axis_y
float vel1_y = collision_axis_y.dot_product( velo_veh1 );
float vel1_x = collision_axis_x.dot_product( velo_veh1 );
//velocity of veh2 before collision in the direction of collision_axis_y
float vel2_y = collision_axis_y.dot_product( velo_veh2 );
float vel2_x = collision_axis_x.dot_product( velo_veh2 );
//velocity of veh1 before collision in the direction of collision_axis_y, converting to m/s
float vel1_y = velocity_constant * collision_axis_y.dot_product( velo_veh1 );
float vel1_x = velocity_constant * collision_axis_x.dot_product( velo_veh1 );
//velocity of veh2 before collision in the direction of collision_axis_y, converting to m/s
float vel2_y = velocity_constant * collision_axis_y.dot_product( velo_veh2 );
float vel2_x = velocity_constant * collision_axis_x.dot_product( velo_veh2 );
delta_vel = std::abs( vel1_y - vel2_y );
// Keep in mind get_collision_factor is looking for m/s, not m/h.
// e = 0 -> inelastic collision
// e = 1 -> elastic collision
float e = get_collision_factor( vel1_y / 100 - vel2_y / 100 );
float e = get_collision_factor( vel1_y - vel2_y );
add_msg( m_debug, "Requested collision factor, received %.2f", e );

// Velocity after collision
// vel1_x_a = vel1_x, because in x-direction we have no transmission of force
float vel1_x_a = vel1_x;
float vel2_x_a = vel2_x;
// Transmission of force only in direction of collision_axix_y
// Equation: partially elastic collision
float vel1_y_a = ( m2 * vel2_y * ( 1 + e ) + vel1_y * ( m1 - m2 * e ) ) / ( m1 + m2 );
float vel2_y_a = ( m1 * vel1_y * ( 1 + e ) + vel2_y * ( m2 - m1 * e ) ) / ( m1 + m2 );
float vel1_y_a = ( ( m2 * vel2_y * ( 1 + e ) + vel1_y * ( m1 - m2 * e ) ) / ( m1 + m2 ) );
float vel2_y_a = ( ( m1 * vel1_y * ( 1 + e ) + vel2_y * ( m2 - m1 * e ) ) / ( m1 + m2 ) );
// Add both components; Note: collision_axis is normalized
rl_vec2d final1 = collision_axis_y * vel1_y_a + collision_axis_x * vel1_x_a;
rl_vec2d final2 = collision_axis_y * vel2_y_a + collision_axis_x * vel2_x_a;
rl_vec2d final1 = ( collision_axis_y * vel1_y_a + collision_axis_x * vel1_x_a ) / velocity_constant;
rl_vec2d final2 = ( collision_axis_y * vel2_y_a + collision_axis_x * vel2_x_a ) / velocity_constant;

veh.move.init( final1.as_point() );
if( final1.dot_product( veh.face_vec() ) < 0 ) {
Expand All @@ -925,21 +949,28 @@ float map::vehicle_vehicle_collision( vehicle &veh, vehicle &veh2,
veh.of_turn = avg_of_turn * .9;
veh2.of_turn = avg_of_turn * 1.1;

//Energy after collision
float E_a = 0.5 * m1 * final1.magnitude() * final1.magnitude() +
0.5 * m2 * final2.magnitude() * final2.magnitude();
float d_E = E - E_a; //Lost energy at collision -> deformation energy
dmg = std::abs( d_E / 1000 / 2000 ); //adjust to balance damage
// Remember that the impulse on vehicle 1 is techncally negative, slowing it
veh1_impulse = std::abs( m1 * ( vel1_y_a - vel1_y ) );
veh2_impulse = std::abs( m2 * ( vel2_y_a - vel2_y ) );
} else {
const float m1 = to_kilogram( veh.total_mass() );
// Collision is perfectly inelastic for simplicity
// Assume veh2 is standing still
dmg = std::abs( veh.vertical_velocity / 100 ) * m1 / 10;
dmg_veh1 = ( std::abs( vmiph_to_mps( veh.vertical_velocity ) ) * ( m1 / 10 ) ) / 2;
dmg_veh2 = dmg_veh1;
veh.vertical_velocity = 0;
}

float dmg_veh1 = dmg * 0.5;
float dmg_veh2 = dmg * 0.5;
// To facilitate pushing vehicles, because the simulation pretends cars are ping pong balls that get all their velocity in zero starting distance to slam into eachother while touching.
// Stay under 6 m/s to push cars without damaging them
if( delta_vel >= 6.0f ) {
dmg_veh1 = veh1_impulse * dmg_adjust;
dmg_veh2 = veh2_impulse * dmg_adjust;
} else {
dmg_veh1 = 0;
dmg_veh2 = 0;
}


int coll_parts_cnt = 0; //quantity of colliding parts between veh1 and veh2
for( const auto &veh_veh_coll : collisions ) {
Expand Down
34 changes: 28 additions & 6 deletions src/vehicle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6644,24 +6644,46 @@ void vehicle::damage_all( int dmg1, int dmg2, damage_type type, point impact )
if( dmg2 < dmg1 ) {
std::swap( dmg1, dmg2 );
}

if( dmg1 < 1 ) {
return;
}

const float damage_min = std::abs( dmg1 );
const float damage_max = std::abs( dmg2 );
add_msg( m_debug, "Shock damage to vehicle of %.2f to %.2f", damage_min, damage_max );
for( const vpart_reference &vp : get_all_parts() ) {
const size_t p = vp.part_index();
const vpart_info &shockpart = part_info( p );
int distance = 1 + square_dist( vp.mount(), impact );
if( distance > 1 ) {
int net_dmg = rng( dmg1, dmg2 ) / ( distance * distance );
if( part_info( p ).location != part_location_structure ||
!part_info( p ).has_flag( "PROTRUSION" ) ) {
if( shockpart.location != part_location_structure ||
!shockpart.has_flag( "PROTRUSION" ) ) {
if( shockpart.has_flag( "SHOCK_IMMUNE" ) ) {
net_dmg = 0;
continue;
}
int shock_absorber = part_with_feature( p, "SHOCK_ABSORBER", true );
if( shock_absorber >= 0 ) {
net_dmg = std::max( 0, net_dmg - parts[ shock_absorber ].info().bonus );
net_dmg = std::max( 0.0f, net_dmg - ( parts[ shock_absorber ].info().bonus ) -
shockpart.damage_reduction.type_resist( type ) );
}
if( shockpart.has_flag( "SHOCK_RESISTANT" ) ) {
float damage_resist = 0;
for( const int elem : all_parts_at_location( shockpart.location ) ) {
//Original intent was to find the frame that the part was mounted on and grab that objects resistance, but instead we will go with half the largest damage resist in the stack.
damage_resist = std::max( damage_resist, part_info( elem ).damage_reduction.type_resist( type ) );
}
damage_resist = damage_resist / 2;

add_msg( m_debug, "%1s inherited %.1f damage resistance!", shockpart.name(), damage_resist );
net_dmg = std::max( 0.0f, net_dmg - damage_resist );
}
}
damage_direct( p, net_dmg, type );
if( net_dmg > part_info( p ).damage_reduction.type_resist( type ) ) {
damage_direct( p, net_dmg, type );
add_msg( m_debug, _( "%1s took %.1f damage from shock." ), part_info( p ).name(), 1.0f * net_dmg );
}

}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/vehicle.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct rider_data {
int prt = -1;
bool moved = false;
};
//collision factor for vehicle-vehicle collision; delta_v in mph
//collision factor for vehicle-vehicle collision; delta_v in m/s
float get_collision_factor( float delta_v );

//How far to scatter parts from a vehicle when the part is destroyed (+/-)
Expand Down Expand Up @@ -112,7 +112,7 @@ struct veh_collision {
//int veh?
int part = 0;
veh_coll_type type = veh_coll_nothing;
// impulse
// Impulse, in Ns. Call impulse_to_damage or damage_to_impulse from vehicle_move.cpp for conversion to damage.
int imp = 0;
//vehicle
void *target = nullptr;
Expand Down Expand Up @@ -183,6 +183,8 @@ int mps_to_vmiph( double mps );
double vmiph_to_mps( int vmiph );
int cmps_to_vmiph( int cmps );
int vmiph_to_cmps( int vmiph );
float impulse_to_damage( float impulse );
float damage_to_impulse( float damage );

class turret_data
{
Expand Down
Loading

0 comments on commit 305e501

Please sign in to comment.