Skip to content

Commit ba6b42a

Browse files
perf: add sleep time skip option with safe fallback
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> Assisted-by: openai/gpt-5.3-codex on opencode
1 parent 0695eb2 commit ba6b42a

File tree

5 files changed

+155
-1
lines changed

5 files changed

+155
-1
lines changed

src/activity_handlers.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include "mtype.h"
7272
#include "npc.h"
7373
#include "omdata.h"
74+
#include "options.h"
7475
#include "output.h"
7576
#include "overmapbuffer.h"
7677
#include "player.h"
@@ -3418,8 +3419,25 @@ void activity_handlers::try_sleep_do_turn( player_activity *act, player *p )
34183419
{
34193420
if( !p->has_effect( effect_sleep ) ) {
34203421
if( character_funcs::roll_can_sleep( *p ) ) {
3422+
constexpr std::string_view sleep_skip_disabled_key = "sleep_skip_time_disabled";
3423+
const bool sleep_skip_time = get_option<bool>( "SLEEP_SKIP_TIME" );
3424+
const bool hostile_in_reality_bubble = sleep_skip_time && p->is_avatar() &&
3425+
!g->get_creatures_if( [&]( const Creature & critter ) {
3426+
return &critter != p && p->attitude_to( critter ) == Attitude::A_HOSTILE;
3427+
} ).empty();
3428+
const bool disable_sleep_skip_for_this_sleep = hostile_in_reality_bubble;
3429+
if( hostile_in_reality_bubble ) {
3430+
if( !query_yn(
3431+
_( "you don't feel that safe. Go to sleep anyway? (do not skip time on sleeping)" ) ) ) {
3432+
act->set_to_null();
3433+
return;
3434+
}
3435+
}
34213436
act->set_to_null();
34223437
p->fall_asleep();
3438+
if( disable_sleep_skip_for_this_sleep ) {
3439+
p->set_value( sleep_skip_disabled_key.data(), "true" );
3440+
}
34233441
p->remove_value( "sleep_query" );
34243442
} else if( one_in( 1000 ) ) {
34253443
p->add_msg_if_player( _( "You toss and turn…" ) );

src/character.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8403,6 +8403,7 @@ void Character::cough( bool harmful, int loudness )
84038403

84048404
void Character::wake_up()
84058405
{
8406+
remove_value( "sleep_skip_time_disabled" );
84068407
remove_effect( effect_slept_through_alarm );
84078408
remove_effect( effect_lying_down );
84088409
remove_effect( effect_alarm_clock );
@@ -10264,6 +10265,7 @@ void Character::fall_asleep( const time_duration &duration )
1026410265
cancel_activity();
1026510266
}
1026610267
}
10268+
remove_value( "sleep_skip_time_disabled" );
1026710269
add_effect( effect_sleep, duration );
1026810270
}
1026910271

src/game.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1522,11 +1522,33 @@ bool game::do_turn()
15221522
get_option<bool>( "SLEEP_SKIP_VEH" );
15231523
const auto soundperf = asleep && get_option<bool>( "SLEEP_SKIP_SOUND" );
15241524
const auto monperf = asleep && get_option<bool>( "SLEEP_SKIP_MON" );
1525+
const bool sleep_skip_time = asleep && get_option<bool>( "SLEEP_SKIP_TIME" ) &&
1526+
u.get_value( "sleep_skip_time_disabled" ) != "true";
1527+
const bool hostile_in_reality_bubble = sleep_skip_time &&
1528+
!get_creatures_if( [&]( const Creature & critter ) {
1529+
return &critter != &u && u.attitude_to( critter ) == Attitude::A_HOSTILE;
1530+
} ).empty();
1531+
if( sleep_skip_time && !hostile_in_reality_bubble ) {
1532+
const time_duration sleep_duration = u.get_effect_dur( effect_sleep );
1533+
if( sleep_duration > 1_turns ) {
1534+
const int turns_to_skip = to_turns<int>( sleep_duration - 1_turns );
1535+
for( int i = 0; i < turns_to_skip && u.has_effect( effect_sleep ); i++ ) {
1536+
calendar::turn += 1_turns;
1537+
m.process_items();
1538+
m.process_fields();
1539+
if( !vehperf ) {
1540+
m.vehmove();
1541+
}
1542+
}
1543+
}
1544+
}
15251545
// Actual stuff
15261546
if( new_game ) {
15271547
new_game = false;
15281548
} else {
1529-
gamemode->per_turn();
1549+
if( gamemode != nullptr ) {
1550+
gamemode->per_turn();
1551+
}
15301552
calendar::turn += 1_turns;
15311553
}
15321554

src/options.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,6 +2273,9 @@ void options_manager::add_options_debug()
22732273
add( "SLEEP_SKIP_MON", page_id, translate_marker( "Sleep Boost: Skip Monster Movement" ),
22742274
translate_marker( "Monsters do not move while sleeping" ),
22752275
false );
2276+
add( "SLEEP_SKIP_TIME", page_id, translate_marker( "Skip Time When Sleeping" ),
2277+
translate_marker( "when sleeping, completely skip time like that funny block game" ),
2278+
false );
22762279
#if defined(__ANDROID__)
22772280
add( "LOAD_FROM_EXTERNAL", page_id, translate_marker( "External Storage Saving" ),
22782281
translate_marker( "Save in data/catalcysm... instead of Documents/..." ),

tests/sleep_skip_time_test.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#include "catch/catch.hpp"
2+
3+
#include "avatar.h"
4+
#include "calendar.h"
5+
#include "flag.h"
6+
#include "game.h"
7+
#include "item.h"
8+
#include "map.h"
9+
#include "map_helpers.h"
10+
#include "monster.h"
11+
#include "mtype.h"
12+
#include "options_helpers.h"
13+
#include "point.h"
14+
#include "state_helpers.h"
15+
#include "type_id.h"
16+
#include "vehicle.h"
17+
#include "vehicle_part.h"
18+
19+
static const efftype_id effect_sleep( "sleep" );
20+
static const itype_id fuel_type_battery( "battery" );
21+
static const mtype_id mon_zombie( "mon_zombie" );
22+
23+
namespace
24+
{
25+
26+
auto run_sleep_turn( const time_duration &duration ) -> time_duration
27+
{
28+
avatar &you = g->u;
29+
const time_point before_sleep = calendar::turn;
30+
you.fall_asleep( duration );
31+
REQUIRE( you.has_effect( effect_sleep ) );
32+
CHECK_FALSE( g->do_turn() );
33+
return calendar::turn - before_sleep;
34+
}
35+
36+
}
37+
38+
TEST_CASE( "sleep skip time advances to wake up when safe", "[sleep][perf]" )
39+
{
40+
clear_all_state();
41+
build_test_map( ter_id( "t_pavement" ) );
42+
clear_creatures();
43+
override_option sleep_skip_time( "SLEEP_SKIP_TIME", "true" );
44+
g->u.remove_value( "sleep_skip_time_disabled" );
45+
calendar::turn = calendar::turn_zero + 1_days;
46+
47+
const time_duration elapsed = run_sleep_turn( 2_hours );
48+
49+
CHECK( elapsed >= 2_hours - 1_turns );
50+
CHECK_FALSE( g->u.has_effect( effect_sleep ) );
51+
}
52+
53+
TEST_CASE( "sleep skip time falls back when hostiles are nearby", "[sleep][perf]" )
54+
{
55+
clear_all_state();
56+
build_test_map( ter_id( "t_pavement" ) );
57+
clear_creatures();
58+
override_option sleep_skip_time( "SLEEP_SKIP_TIME", "true" );
59+
g->u.remove_value( "sleep_skip_time_disabled" );
60+
calendar::turn = calendar::turn_zero + 1_days;
61+
62+
avatar &you = g->u;
63+
REQUIRE( g->place_critter_at( mon_zombie, you.pos() + tripoint_east ) != nullptr );
64+
65+
const time_duration elapsed = run_sleep_turn( 2_hours );
66+
67+
CHECK( elapsed < 2_hours );
68+
CHECK( elapsed >= 1_turns );
69+
}
70+
71+
TEST_CASE( "sleep skip time processes rotting and charging effects", "[sleep][perf]" )
72+
{
73+
clear_all_state();
74+
build_test_map( ter_id( "t_pavement" ) );
75+
clear_creatures();
76+
override_option sleep_skip_time( "SLEEP_SKIP_TIME", "true" );
77+
g->u.remove_value( "sleep_skip_time_disabled" );
78+
calendar::turn = calendar::turn_zero + 2_days;
79+
80+
map &here = get_map();
81+
const tripoint item_pos = g->u.pos();
82+
detached_ptr<item> rotting_item = item::spawn( "meat_cooked" );
83+
rotting_item->mod_rot( 7_days );
84+
here.add_item_or_charges( item_pos, std::move( rotting_item ), false );
85+
86+
const tripoint vehicle_origin = tripoint( 10, 10, 0 );
87+
vehicle *veh_ptr = here.add_vehicle( vproto_id( "recharge_test" ), vehicle_origin, 0_degrees, 100,
88+
0 );
89+
REQUIRE( veh_ptr != nullptr );
90+
auto cargo_part_index = veh_ptr->part_with_feature( point_zero, "CARGO", true );
91+
REQUIRE( cargo_part_index >= 0 );
92+
93+
auto chargers = veh_ptr->get_parts_at( vehicle_origin, "RECHARGE", part_status_flag::available );
94+
REQUIRE( chargers.size() == 1 );
95+
chargers.front()->enabled = true;
96+
97+
detached_ptr<item> battery_item = item::spawn( "light_battery_cell" );
98+
battery_item->ammo_unset();
99+
REQUIRE( battery_item->has_flag( flag_RECHARGE ) );
100+
veh_ptr->add_item( veh_ptr->part( cargo_part_index ), std::move( battery_item ) );
101+
102+
run_sleep_turn( 1_hours );
103+
104+
CHECK( here.i_at( item_pos ).empty() );
105+
auto cargo_items = veh_ptr->get_items( cargo_part_index );
106+
REQUIRE( cargo_items.size() == 1 );
107+
CHECK( cargo_items.only_item().ammo_remaining() > 0 );
108+
CHECK( veh_ptr->fuel_left( fuel_type_battery ) > 0 );
109+
}

0 commit comments

Comments
 (0)