From 175eae431eedb9422590111004ff126231851a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Karlsson?= Date: Sat, 15 Jun 2024 05:56:08 +0200 Subject: [PATCH] Fix character volume calculation (#74348) * Provide body volume calculation based on body density * Simplify calculations * Adjust test cases to the volume calculation * Adjust volumes for baseline tests too * Make checks for volume pass within reasaonable ranges Relying on volumes down to the ml allows us to make changes to volume calculations, and keep the tests withing reasaonable boundaries. references #74263 * Make checks for volume less dependent on exact volumes Make tests more flexible * Make tests exact, set character to exact calorie count This strategy makes the test repeatable * Remove unused header units.h --- src/character.cpp | 26 +++++++++++++++++--------- tests/char_volume_test.cpp | 18 +++++++++--------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index cce5aa112901b..dd124a251e751 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -3108,15 +3108,23 @@ units::volume Character::get_total_volume() const units::volume Character::get_base_volume() const { - const int your_height = height(); // avg 175cm - // Arbitrary number picked relative to aisle (100L), not necessarily accurate - const units::volume avg_human_volume = 70_liter; - - // Very scientific video game size to metric human volume calculation. Avg height == avg_human_volume; - units::volume your_base_volume = units::from_liter( static_cast( your_height ) / 2.5 ); - double volume_proport = units::to_liter( your_base_volume ) / units::to_liter( avg_human_volume ); - - return std::pow( volume_proport, 3.0 ) * avg_human_volume; + // The formula used here to calculate base volume for a human body is + // BV = W / BD + // Where: + // * BV is the body volume in liters + // * W is the weight in kilograms + // * BD is the body density (kg/L), estimated using the Brozek formula: + // BD = 1.097 – 0.00046971 * W + 0.00000056 * W^2 – 0.00012828 * H + // See + // https://en.wikipedia.org/wiki/Body_fat_percentage + // https://calculator.academy/body-volume-calculator/ + const int your_height = height(); + const double your_weight = units::to_kilogram( bodyweight() ); + const double your_density = 1.097 - 0.00046971 * your_weight + + 0.00000056 * std::pow( your_weight, 2 ) + - 0.00012828 * your_height; + units::volume your_base_volume = units::from_liter( your_weight / your_density ); + return your_base_volume; } units::mass Character::weight_carried() const diff --git a/tests/char_volume_test.cpp b/tests/char_volume_test.cpp index 3c90e01a0c214..82cca754a9494 100644 --- a/tests/char_volume_test.cpp +++ b/tests/char_volume_test.cpp @@ -42,21 +42,23 @@ TEST_CASE( "character_baseline_volumes", "[volume]" ) { clear_avatar(); Character &you = get_player_character(); + you.set_stored_kcal( you.get_healthy_kcal() ); REQUIRE( you.get_mutations().empty() ); REQUIRE( you.height() == 175 ); - CHECK( you.get_base_volume() == 70_liter ); + REQUIRE( you.bodyweight() == 76562390_milligram ); + CHECK( you.get_base_volume() == 73485_ml ); REQUIRE( your_height_with_trait( trait_SMALL2 ) == 70 ); - CHECK( your_volume_with_trait( trait_SMALL2 ) == 4480_ml ); + CHECK( your_volume_with_trait( trait_SMALL2 ) == 23326_ml ); REQUIRE( your_height_with_trait( trait_SMALL ) == 122 ); - CHECK( your_volume_with_trait( trait_SMALL ) == 23717_ml ); + CHECK( your_volume_with_trait( trait_SMALL ) == 42476_ml ); REQUIRE( your_height_with_trait( trait_LARGE ) == 227 ); - CHECK( your_volume_with_trait( trait_LARGE ) == 152778_ml ); + CHECK( your_volume_with_trait( trait_LARGE ) == 116034_ml ); REQUIRE( your_height_with_trait( trait_HUGE ) == 280 ); - CHECK( your_volume_with_trait( trait_HUGE ) == 286720_ml ); + CHECK( your_volume_with_trait( trait_HUGE ) == 156228_ml ); } TEST_CASE( "character_at_volume_can_or_cannot_enter_vehicle", "[volume]" ) @@ -66,8 +68,6 @@ TEST_CASE( "character_at_volume_can_or_cannot_enter_vehicle", "[volume]" ) map &here = get_map(); Character &you = get_player_character(); REQUIRE( you.get_mutations().empty() ); - REQUIRE( you.get_base_volume() == 70_liter ); - REQUIRE( you.get_total_volume() == 70_liter ); tripoint test_pos = tripoint{10, 10, 0 }; @@ -107,7 +107,7 @@ TEST_CASE( "character_at_volume_can_or_cannot_enter_vehicle", "[volume]" ) cramped = false; // Try the cramped aisle with a rock again, but now we are tiny, so it is easy. - CHECK( your_volume_with_trait( trait_SMALL2 ) == 4480_ml ); + CHECK( your_volume_with_trait( trait_SMALL2 ) == 23326_ml ); you.setpos( test_pos ); // set our position again, clear_avatar() moved us dest_loc = dest_loc + tripoint_north; CHECK( you.can_move_to_vehicle_tile( dest_loc, cramped ) ); @@ -115,7 +115,7 @@ TEST_CASE( "character_at_volume_can_or_cannot_enter_vehicle", "[volume]" ) dest_loc = you.get_location(); //reset // Same aisle, but now we have HUGE GUTS. We will never fit. - CHECK( your_volume_with_trait( trait_HUGE ) == 286720_ml ); + CHECK( your_volume_with_trait( trait_HUGE ) == 156228_ml ); you.setpos( test_pos ); // set our position again, clear_avatar() moved us dest_loc = dest_loc + tripoint_north; CHECK( !you.can_move_to_vehicle_tile( dest_loc ) );