Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CR] Catch weariness fluctuations #47273

Closed
39 changes: 30 additions & 9 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5069,6 +5069,26 @@ std::string Character::debug_weary_info() const
get_healthy_kcal(), fatigue, morale, weight, bmi );
}

int Character::weary_tracker() const
{
return weary.tracker;
}

int Character::weary_intake() const
{
return weary.intake;
}

int Character::weary_low_activity_ticks() const
{
return weary.low_activity_ticks;
}

int Character::weary_tick_counter() const
{
return weary.tick_counter;
}

void weariness_tracker::clear()
{
tracker = 0;
Expand Down Expand Up @@ -5680,31 +5700,32 @@ void Character::try_reduce_weariness( const float exertion )
weary.tick_counter++;
if( exertion == NO_EXERCISE ) {
weary.low_activity_ticks++;
// Recover twice as fast at rest
// Recover twice as fast if asleep/similar
if( in_sleep_state() ) {
weary.low_activity_ticks++;
}
}

const float recovery_mult = get_option<float>( "WEARY_RECOVERY_MULT" );

if( weary.low_activity_ticks >= 6 ) {
if( weary.low_activity_ticks >= 1 ) {
int reduction = weary.tracker;
const int bmr = base_bmr();
// 1/20 of whichever's bigger
// 1/120 of whichever's bigger
if( bmr > reduction ) {
reduction = bmr * recovery_mult;
reduction = std::floor( bmr * recovery_mult * weary.low_activity_ticks / 6 );
} else {
reduction *= recovery_mult;
reduction = std::ceil( reduction * recovery_mult * weary.low_activity_ticks / 6 );
}
weary.low_activity_ticks = 0;

weary.tracker -= reduction;
weary.tracker -= std::max( reduction, 1 );
}

if( weary.tick_counter >= 12 ) {
weary.intake *= 1 - recovery_mult;
weary.tick_counter = 0;
// If happens to be no reduction, character is not (as) hypoglycemic
if( weary.tick_counter >= 3 ) {
weary.intake *= std::pow( 1 - recovery_mult, 0.25f );
weary.tick_counter -= 3;
}

// Normalize values, make sure we stay above 0
Expand Down
4 changes: 4 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -2730,6 +2730,10 @@ class Character : public Creature, public visitable
int weariness_level() const;
int weary_threshold() const;
int weariness() const;
int weary_tracker() const;
int weary_intake() const;
int weary_low_activity_ticks() const;
int weary_tick_counter() const;
float activity_level() const;
float exertion_adjusted_move_multiplier( float level = -1.0f ) const;
void try_reduce_weariness( float exertion );
Expand Down
45 changes: 40 additions & 5 deletions tests/activity_scheduling_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,17 @@ weariness_events do_activity( tasklist tasks )
int new_weariness = guy.weariness_level();
spent += task.interval;
tasks.advance( task.interval );
// If we're more weary than we were when we started, report it
// If we're more or less weary than we were when we started, report it
if( new_weariness != weariness_lvl ) {
int new_weary = guy.weariness();
int new_thresh = guy.weary_threshold();
int new_tracker = guy.weary_tracker();
int new_intake = guy.weary_intake();
int new_low_activity_ticks = guy.weary_low_activity_ticks();
int new_tick_counter = guy.weary_tick_counter();
activity_log.log( weariness_lvl, new_weariness, spent,
new_weary, new_thresh );
new_weary, new_thresh, new_tracker, new_intake,
new_low_activity_ticks, new_tick_counter );
weariness_lvl = new_weariness;
}
}
Expand Down Expand Up @@ -171,14 +176,20 @@ time_duration tasklist::duration()
}

void weariness_events::log( const int old_level, const int new_level, const time_duration &when,
const int new_weariness, const int new_threshold )
const int new_weariness, const int new_threshold,
const int new_tracker, const int new_intake,
const int new_low_activity_ticks, const int new_tick_counter )
{
weary_transition added;
added.from = old_level;
added.to = new_level;
added.minutes = to_minutes<int>( when );
added.new_weariness = new_weariness;
added.new_threshold = new_threshold;
added.new_tracker = new_tracker;
added.new_intake = new_intake;
added.new_low_activity_ticks = new_low_activity_ticks;
added.new_tick_counter = new_tick_counter;

transitions.insert( transitions.end(), added );
}
Expand All @@ -198,13 +209,37 @@ int weariness_events::transition_minutes( const int from, const int to,
return ret.first;
}

bool weariness_events::have_weary_increase() const
{
for( const weary_transition &change : transitions ) {
if( change.from < change.to ) {
return true;
}
}

return false;
}

bool weariness_events::have_weary_decrease() const
{
for( const weary_transition &change : transitions ) {
if( change.from > change.to ) {
return true;
}
}

return false;
}

std::string weariness_events::summarize() const
{
std::string buffer;
for( const weary_transition &change : transitions ) {
buffer += string_format( "Transition: Weariness lvl from %d to %d at %d min (W %d Th %d)\n",
buffer += string_format( "Chng: Weary lv %d to %d at %d min (W %d Th %d Tr %d In %d Lt %d Tk %d)\n",
change.from, change.to, change.minutes,
change.new_weariness, change.new_threshold );
change.new_weariness, change.new_threshold,
change.new_tracker, change.new_intake,
change.new_low_activity_ticks, change.new_tick_counter );
}
return buffer;
}
Expand Down
10 changes: 9 additions & 1 deletion tests/activity_scheduling_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,26 @@ struct weariness_events {
int to = 0;
int new_weariness = 0;
int new_threshold = 0;
int new_tracker = 0;
int new_intake = 0;
int new_low_activity_ticks = 0;
int new_tick_counter = 0;
};

std::vector<weary_transition> transitions;

public:
void log( int old_level, int new_level, const time_duration &when,
int new_weariness, int new_threshold );
int new_weariness, int new_threshold, int new_tracker, int new_intake,
int new_low_activity_ticks, int new_tick_counter );

// Return the first time a transition between `from` and `to` occurs, in minutes
// if around = 0_seconds or equivalent, otherwise return the time closest to around
int transition_minutes( int from, int to, const time_duration &around ) const;

bool have_weary_increase() const;
bool have_weary_decrease() const;

std::string summarize() const;

bool empty() const;
Expand Down
37 changes: 26 additions & 11 deletions tests/weary_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static const meal_schedule milk( itype_id( "milk" ) );

static const sleep_schedule sched_sleep{};

TEST_CASE( "weary_assorted_tasks", "[weary][activities]" )
TEST_CASE( "weary_assorted_tasks", "[weary][activities][!mayfail]" )
{
const avatar &guy = get_avatar();

Expand Down Expand Up @@ -51,17 +51,19 @@ TEST_CASE( "weary_assorted_tasks", "[weary][activities]" )

SECTION( "Moderate tasks" ) {
INFO( "\nPlanting 8 hours:" );
clear_avatar();
INFO( guy.debug_weary_info() );
weariness_events info = do_activity( moderate_8h );
INFO( info.summarize() );
INFO( guy.debug_weary_info() );
REQUIRE( !info.empty() );
CHECK( info.transition_minutes( 0, 1, 370_minutes ) == Approx( 370 ).margin( 5 ) );
CHECK( !info.have_weary_decrease() );
CHECK( guy.weariness_level() == 1 );
}

SECTION( "Heavy tasks" ) {
INFO( "\nDigging Pits 8 hours:" );
SECTION( "Heavy tasks - Digging Pits 8 hours" ) {
clear_avatar();
INFO( guy.debug_weary_info() );
weariness_events info = do_activity( soldier_8h );
INFO( info.summarize() );
Expand All @@ -70,11 +72,15 @@ TEST_CASE( "weary_assorted_tasks", "[weary][activities]" )
CHECK( info.transition_minutes( 0, 1, 120_minutes ) == Approx( 120 ).margin( 5 ) );
CHECK( info.transition_minutes( 1, 2, 255_minutes ) == Approx( 255 ).margin( 5 ) );
CHECK( info.transition_minutes( 2, 3, 360_minutes ) == Approx( 360 ).margin( 5 ) );
CHECK( info.transition_minutes( 3, 4, 470_minutes ) == Approx( 470 ).margin( 5 ) );
CHECK( info.transition_minutes( 3, 4, 465_minutes ) == Approx( 465 ).margin( 5 ) );
CHECK( !info.have_weary_decrease() );
CHECK( guy.weariness_level() == 4 );
}

INFO( "\nDigging Pits 12 hours:" );
info = do_activity( soldier_12h );
SECTION( "Heavy tasks - Digging Pits 12 hours" ) {
clear_avatar();
INFO( guy.debug_weary_info() );
weariness_events info = do_activity( soldier_12h );
INFO( info.summarize() );
INFO( guy.debug_weary_info() );
REQUIRE( !info.empty() );
Expand All @@ -83,6 +89,7 @@ TEST_CASE( "weary_assorted_tasks", "[weary][activities]" )
CHECK( info.transition_minutes( 2, 3, 355_minutes ) == Approx( 355 ).margin( 5 ) );
CHECK( info.transition_minutes( 3, 4, 465_minutes ) == Approx( 465 ).margin( 5 ) );
CHECK( info.transition_minutes( 4, 5, 595_minutes ) == Approx( 595 ).margin( 5 ) );
CHECK( !info.have_weary_decrease() );
CHECK( guy.weariness_level() == 5 );
}
}
Expand Down Expand Up @@ -125,13 +132,19 @@ TEST_CASE( "weary_recovery", "[weary][activities]" )
INFO( info.summarize() );
INFO( guy.debug_weary_info() );
REQUIRE( !info.empty() );
CHECK( info.transition_minutes( 4, 3, 520_minutes ) == Approx( 520 ).margin( 5 ) );
CHECK( info.transition_minutes( 3, 2, 670_minutes ) == Approx( 670 ).margin( 5 ) );
CHECK( info.transition_minutes( 4, 3, 505_minutes ) == Approx( 505 ).margin( 5 ) );
CHECK( info.transition_minutes( 3, 2, 630_minutes ) == Approx( 630 ).margin( 5 ) );
CHECK( info.transition_minutes( 1, 0, 0_minutes ) > ( 8 * 60 ) ); // should be INT_MAX
CHECK( info.transition_minutes( 2, 1, 0_minutes ) > ( 8 * 60 ) );
CHECK( info.transition_minutes( 1, 2, 16_hours ) <= ( 8 * 60 ) );
CHECK( info.transition_minutes( 2, 3, 16_hours ) <= ( 8 * 60 ) );
CHECK( info.transition_minutes( 3, 4, 16_hours ) <= ( 8 * 60 ) );
CHECK( guy.weariness_level() == 1 );
}

SECTION( "1 day vehicle work" ) {
INFO( "\n3 meals, 10h vehicle work, 4h reading, 10h sleep, 16h waiting" );
clear_avatar();
INFO( guy.debug_weary_info() );
weariness_events info = do_activity( mechanic_day );
INFO( info.summarize() );
Expand All @@ -140,7 +153,7 @@ TEST_CASE( "weary_recovery", "[weary][activities]" )
CHECK( info.transition_minutes( 0, 1, 325_minutes ) == Approx( 325 ).margin( 5 ) );
CHECK( info.transition_minutes( 1, 2, 625_minutes ) == Approx( 625 ).margin( 5 ) );
CHECK( info.transition_minutes( 2, 1, 740_minutes ) == Approx( 740 ).margin( 5 ) );
CHECK( info.transition_minutes( 1, 0, 995_minutes ) == Approx( 995 ).margin( 5 ) );
CHECK( info.transition_minutes( 1, 0, 985_minutes ) == Approx( 985 ).margin( 5 ) );
}
}

Expand All @@ -165,6 +178,7 @@ TEST_CASE( "weary_24h_tasks", "[weary][activities]" )
}

SECTION( "Digging 24 hours" ) {
clear_avatar();
INFO( guy.debug_weary_info() );
weariness_events info = do_activity( digging_24h );
INFO( info.summarize() );
Expand All @@ -175,9 +189,10 @@ TEST_CASE( "weary_24h_tasks", "[weary][activities]" )
CHECK( info.transition_minutes( 2, 3, 360_minutes ) == Approx( 360 ).margin( 5 ) );
CHECK( info.transition_minutes( 3, 4, 470_minutes ) == Approx( 470 ).margin( 5 ) );
CHECK( info.transition_minutes( 4, 5, 595_minutes ) == Approx( 595 ).margin( 5 ) );
CHECK( info.transition_minutes( 5, 6, 730_minutes ) == Approx( 730 ).margin( 5 ) );
CHECK( info.transition_minutes( 6, 7, 835_minutes ) == Approx( 835 ).margin( 5 ) );
CHECK( info.transition_minutes( 5, 6, 740_minutes ) == Approx( 740 ).margin( 5 ) );
CHECK( info.transition_minutes( 6, 7, 845_minutes ) == Approx( 845 ).margin( 5 ) );
CHECK( info.transition_minutes( 7, 8, 915_minutes ) == Approx( 915 ).margin( 10 ) );
CHECK( !info.have_weary_decrease() );
// TODO: You should collapse from this - currently we
// just get really high levels of weariness
CHECK( guy.weariness_level() > 8 );
Expand Down