Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
#include "mtype.h"
#include "npc.h"
#include "omdata.h"
#include "options.h"
#include "output.h"
#include "overmapbuffer.h"
#include "player.h"
Expand Down Expand Up @@ -3418,8 +3419,25 @@ void activity_handlers::try_sleep_do_turn( player_activity *act, player *p )
{
if( !p->has_effect( effect_sleep ) ) {
if( character_funcs::roll_can_sleep( *p ) ) {
constexpr std::string_view sleep_skip_disabled_key = "sleep_skip_time_disabled";
const bool sleep_skip_time = get_option<bool>( "SLEEP_SKIP_TIME" );
const bool hostile_in_reality_bubble = sleep_skip_time && p->is_avatar() &&
!g->get_creatures_if( [&]( const Creature & critter ) {
return &critter != p && p->attitude_to( critter ) == Attitude::A_HOSTILE;
} ).empty();
const bool disable_sleep_skip_for_this_sleep = hostile_in_reality_bubble;
if( hostile_in_reality_bubble ) {
if( !query_yn(
_( "you don't feel that safe. Go to sleep anyway? (do not skip time on sleeping)" ) ) ) {
act->set_to_null();
return;
}
}
act->set_to_null();
p->fall_asleep();
if( disable_sleep_skip_for_this_sleep ) {
p->set_value( sleep_skip_disabled_key.data(), "true" );
}
p->remove_value( "sleep_query" );
} else if( one_in( 1000 ) ) {
p->add_msg_if_player( _( "You toss and turn…" ) );
Expand Down
2 changes: 2 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8403,6 +8403,7 @@ void Character::cough( bool harmful, int loudness )

void Character::wake_up()
{
remove_value( "sleep_skip_time_disabled" );
remove_effect( effect_slept_through_alarm );
remove_effect( effect_lying_down );
remove_effect( effect_alarm_clock );
Expand Down Expand Up @@ -10264,6 +10265,7 @@ void Character::fall_asleep( const time_duration &duration )
cancel_activity();
}
}
remove_value( "sleep_skip_time_disabled" );
add_effect( effect_sleep, duration );
}

Expand Down
24 changes: 23 additions & 1 deletion src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1522,11 +1522,33 @@ bool game::do_turn()
get_option<bool>( "SLEEP_SKIP_VEH" );
const auto soundperf = asleep && get_option<bool>( "SLEEP_SKIP_SOUND" );
const auto monperf = asleep && get_option<bool>( "SLEEP_SKIP_MON" );
const bool sleep_skip_time = asleep && get_option<bool>( "SLEEP_SKIP_TIME" ) &&
u.get_value( "sleep_skip_time_disabled" ) != "true";
const bool hostile_in_reality_bubble = sleep_skip_time &&
!get_creatures_if( [&]( const Creature & critter ) {
return &critter != &u && u.attitude_to( critter ) == Attitude::A_HOSTILE;
} ).empty();
if( sleep_skip_time && !hostile_in_reality_bubble ) {
const auto sleep_duration = u.get_effect_dur( effect_sleep );
if( sleep_duration > 1_turns ) {
calendar::turn += sleep_duration - 1_turns;
m.process_items();
m.process_fields();
if( !vehperf ) {
for( auto &veh : m.get_vehicles() ) {
veh.v->update_time( calendar::turn );
}
m.vehmove();
}
}
}
// Actual stuff
if( new_game ) {
new_game = false;
} else {
gamemode->per_turn();
if( gamemode != nullptr ) {
gamemode->per_turn();
}
calendar::turn += 1_turns;
}

Expand Down
3 changes: 3 additions & 0 deletions src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2273,6 +2273,9 @@ void options_manager::add_options_debug()
add( "SLEEP_SKIP_MON", page_id, translate_marker( "Sleep Boost: Skip Monster Movement" ),
translate_marker( "Monsters do not move while sleeping" ),
false );
add( "SLEEP_SKIP_TIME", page_id, translate_marker( "Skip Time When Sleeping" ),
translate_marker( "when sleeping, completely skip time like that funny block game" ),
false );
#if defined(__ANDROID__)
add( "LOAD_FROM_EXTERNAL", page_id, translate_marker( "External Storage Saving" ),
translate_marker( "Save in data/catalcysm... instead of Documents/..." ),
Expand Down
109 changes: 109 additions & 0 deletions tests/sleep_skip_time_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "catch/catch.hpp"

#include "avatar.h"
#include "calendar.h"
#include "flag.h"
#include "game.h"
#include "item.h"
#include "map.h"
#include "map_helpers.h"
#include "monster.h"
#include "mtype.h"
#include "options_helpers.h"
#include "point.h"
#include "state_helpers.h"
#include "type_id.h"
#include "vehicle.h"
#include "vehicle_part.h"

static const efftype_id effect_sleep( "sleep" );
static const itype_id fuel_type_battery( "battery" );
static const mtype_id mon_zombie( "mon_zombie" );

namespace
{

auto run_sleep_turn( const time_duration &duration ) -> time_duration
{
avatar &you = g->u;
const time_point before_sleep = calendar::turn;
you.fall_asleep( duration );
REQUIRE( you.has_effect( effect_sleep ) );
CHECK_FALSE( g->do_turn() );
return calendar::turn - before_sleep;
}

}

TEST_CASE( "sleep skip time advances to wake up when safe", "[sleep][perf]" )
{
clear_all_state();
build_test_map( ter_id( "t_pavement" ) );
clear_creatures();
override_option sleep_skip_time( "SLEEP_SKIP_TIME", "true" );
g->u.remove_value( "sleep_skip_time_disabled" );
calendar::turn = calendar::turn_zero + 1_days;

const time_duration elapsed = run_sleep_turn( 2_hours );

CHECK( elapsed >= 2_hours - 1_turns );
CHECK_FALSE( g->u.has_effect( effect_sleep ) );
}

TEST_CASE( "sleep skip time falls back when hostiles are nearby", "[sleep][perf]" )
{
clear_all_state();
build_test_map( ter_id( "t_pavement" ) );
clear_creatures();
override_option sleep_skip_time( "SLEEP_SKIP_TIME", "true" );
g->u.remove_value( "sleep_skip_time_disabled" );
calendar::turn = calendar::turn_zero + 1_days;

avatar &you = g->u;
REQUIRE( g->place_critter_at( mon_zombie, you.pos() + tripoint_east ) != nullptr );

const time_duration elapsed = run_sleep_turn( 2_hours );

CHECK( elapsed < 2_hours );
CHECK( elapsed >= 1_turns );
}

TEST_CASE( "sleep skip time processes rotting and charging effects", "[sleep][perf]" )
{
clear_all_state();
build_test_map( ter_id( "t_pavement" ) );
clear_creatures();
override_option sleep_skip_time( "SLEEP_SKIP_TIME", "true" );
g->u.remove_value( "sleep_skip_time_disabled" );
calendar::turn = calendar::turn_zero + 2_days;

map &here = get_map();
const tripoint item_pos = g->u.pos();
detached_ptr<item> rotting_item = item::spawn( "meat_cooked" );
rotting_item->mod_rot( 7_days );
here.add_item_or_charges( item_pos, std::move( rotting_item ), false );

const tripoint vehicle_origin = tripoint( 10, 10, 0 );
vehicle *veh_ptr = here.add_vehicle( vproto_id( "recharge_test" ), vehicle_origin, 0_degrees, 100,
0 );
REQUIRE( veh_ptr != nullptr );
auto cargo_part_index = veh_ptr->part_with_feature( point_zero, "CARGO", true );
REQUIRE( cargo_part_index >= 0 );

auto chargers = veh_ptr->get_parts_at( vehicle_origin, "RECHARGE", part_status_flag::available );
REQUIRE( chargers.size() == 1 );
chargers.front()->enabled = true;

detached_ptr<item> battery_item = item::spawn( "light_battery_cell" );
battery_item->ammo_unset();
REQUIRE( battery_item->has_flag( flag_RECHARGE ) );
veh_ptr->add_item( veh_ptr->part( cargo_part_index ), std::move( battery_item ) );

run_sleep_turn( 1_hours );

CHECK( here.i_at( item_pos ).empty() );
auto cargo_items = veh_ptr->get_items( cargo_part_index );
REQUIRE( cargo_items.size() == 1 );
CHECK( cargo_items.only_item().ammo_remaining() > 0 );
CHECK( veh_ptr->fuel_left( fuel_type_battery ) > 0 );
}
Loading