forked from cataclysmbnteam/Cataclysm-BN
-
Notifications
You must be signed in to change notification settings - Fork 0
/
calendar.h
622 lines (561 loc) · 22.8 KB
/
calendar.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
#pragma once
#ifndef CATA_SRC_CALENDAR_H
#define CATA_SRC_CALENDAR_H
#include <string>
#include <utility>
#include <vector>
class calendar_config;
class JsonIn;
class JsonOut;
class time_duration;
class time_point;
template<typename T> struct enum_traits;
/** Real world seasons */
enum season_type {
SPRING = 0,
SUMMER = 1,
AUTUMN = 2,
WINTER = 3,
NUM_SEASONS
};
template<>
struct enum_traits<season_type> {
static constexpr season_type last = season_type::NUM_SEASONS;
};
/** Phases of the moon */
enum moon_phase {
/** New (completely dark) moon */
MOON_NEW = 0,
/** One quarter of moon lit, amount lit is increasing every day */
MOON_WAXING_CRESCENT,
/** One half of moon lit, amount lit is increasing every day */
MOON_HALF_MOON_WAXING,
/** Three quarters of moon lit, amount lit is increasing every day */
MOON_WAXING_GIBBOUS,
/** Full moon */
MOON_FULL,
/** Three quarters of moon lit, amount lit decreases every day */
MOON_WANING_GIBBOUS,
/** Half of moon is lit, amount lit decreases every day */
MOON_HALF_MOON_WANING,
/** One quarter of moon lit, amount lit decreases every day */
MOON_WANING_CRESCENT,
/** Not a valid moon phase, but can be used for iterating through enum */
MOON_PHASE_MAX
};
/**
* Time keeping namespace that holds global time.
*
* Encapsulates the current time of day, date, and year. Also tracks seasonal variation in day
* length, the passing of the seasons themselves, and the phases of the moon.
*/
namespace calendar
{
/**
* Predicate to handle rate-limiting. Returns `true` after every @p event_frequency duration.
*/
bool once_every( const time_duration &event_frequency );
/**
* A number that represents the longest possible action.
*
* This number should be regarded as a number of turns, and can safely be converted to a
* number of seconds or moves (movement points) without integer overflow. If used to
* represent a larger unit (hours/days/years), then this will result in integer overflow.
*/
extern const int INDEFINITELY_LONG;
/**
* The expected duration of the cataclysm
*
* Large duration that can be used to approximate infinite amounts of time.
*
* This number can't be safely converted to a number of moves without causing
* an integer overflow.
*/
extern const time_duration INDEFINITELY_LONG_DURATION;
/// @returns Whether the eternal season is enabled.
bool eternal_season();
void set_eternal_season( bool is_eternal_season );
/** @returns Time in a year, (configured in current world settings) */
time_duration year_length();
/** @returns Time of a season (configured in current world settings) */
time_duration season_length();
void set_season_length( int dur );
/**
* @returns ratio of actual season length (a world option) to default season length. This
* should be used to convert JSON values (that assume the default for the season length
* option) to actual in-game length.
*/
float season_ratio();
/** Returns the translated name of the season (with first letter being uppercase). */
std::string name_season( season_type s );
const extern time_point &start_of_cataclysm;
const extern time_point &start_of_game;
const extern season_type &initial_season;
extern time_point turn;
extern calendar_config config;
void set_calendar_config( const calendar_config & );
/**
* A time point that is always before the current turn, even when the game has
* just started. This implies `before_time_starts < calendar::turn` is always
* true. It can be used to initialize `time_point` values that denote that last
* time a cache was update.
*/
extern const time_point before_time_starts;
/**
* Represents time point 0.
* TODO: flesh out the documentation
*/
extern const time_point turn_zero;
} // namespace calendar
template<typename T>
constexpr T to_turns( const time_duration &duration );
template<typename T>
constexpr T to_seconds( const time_duration &duration );
template<typename T>
constexpr T to_minutes( const time_duration &duration );
template<typename T>
constexpr T to_hours( const time_duration &duration );
template<typename T>
constexpr T to_days( const time_duration &duration );
template<typename T>
constexpr T to_weeks( const time_duration &duration );
template<typename T>
constexpr T to_moves( const time_duration &duration );
template<typename T>
constexpr T to_turn( const time_point &point );
template<typename T>
constexpr time_duration operator/( const time_duration &lhs, T rhs );
template<typename T>
inline time_duration &operator/=( time_duration &lhs, T rhs );
template<typename T>
constexpr time_duration operator*( const time_duration &lhs, T rhs );
template<typename T>
constexpr time_duration operator*( T lhs, const time_duration &rhs );
template<typename T>
inline time_duration &operator*=( time_duration &lhs, T rhs );
/**
* A duration defined as a number of specific time units.
* Currently it stores the number (as integer) of turns.
* Note: currently variable season length is ignored by this class (N turns are
* always N turns) and there is no way to create an instance from units larger
* than days (e.g. seasons or years) and no way to convert a duration into those
* units directly. (You can still use @ref calendar to do this.)
*
* Operators for time points and time duration are defined as one may expect them:
* -duration ==> duration (inverse)
* point - point ==> duration (duration between to points in time)
* point + duration ==> point (the revers of above)
* point - duration ==> point (same as: point + -duration)
* duration + duration ==> duration
* duration - duration ==> duration (same as: duration + -duration)
* duration * scalar ==> duration (simple scaling yields a double with precise value)
* scalar * duration ==> duration (same as above)
* duration / duration ==> scalar (revers of above)
* duration / scalar ==> duration (same as: duration * 1/scalar)
* duration % duration ==> duration ("remainder" of duration / some integer)
* Also shortcuts: += and -= and *= and /=
*/
class time_duration
{
private:
friend class time_point;
int turns_;
explicit constexpr time_duration( const int t ) : turns_( t ) { }
public:
time_duration() : turns_( 0 ) {}
void serialize( JsonOut &jsout ) const;
void deserialize( JsonIn &jsin );
/**
* Named constructors to get a duration representing a multiple of the named time
* units. Note that a duration is stored as integer number of turns, so
* `from_minutes( 0.0001 )` will be stored as "0 turns".
* The template type is used for the conversion from given time unit to turns, so
* `from_hours( 0.5 )` will yield "1800 turns".
* Conversion of units greater than days (seasons) is not supported because they
* depend on option settings ("season length").
*/
/**@{*/
template<typename T>
static constexpr time_duration from_turns( const T t ) {
return time_duration( t );
}
template<typename T>
static constexpr time_duration from_seconds( const T t ) {
return time_duration( t );
}
template<typename T>
static constexpr time_duration from_minutes( const T m ) {
return from_turns( m * 60 );
}
template<typename T>
static constexpr time_duration from_hours( const T h ) {
return from_minutes( h * 60 );
}
template<typename T>
static constexpr time_duration from_days( const T d ) {
return from_hours( d * 24 );
}
template<typename T>
static constexpr time_duration from_weeks( const T d ) {
return from_days( d * 7 );
}
/**@}*/
/**
* Converts the duration to an amount of the given units. The conversions is
* done with values of the given template type. That means using an integer
* type (e.g. `int`) will return a truncated value (amount of *full* minutes
* that make up the duration, discarding the remainder).
* Calling `to_minutes<double>` will return a precise number.
* Example:
* `to_hours<int>( from_minutes( 90 ) ) == 1`
* `to_hours<double>( from_minutes( 90 ) ) == 1.5`
*/
/**@{*/
template<typename T>
friend constexpr T to_turns( const time_duration &duration ) {
return duration.turns_;
}
template<typename T>
friend constexpr T to_seconds( const time_duration &duration ) {
return duration.turns_;
}
template<typename T>
friend constexpr T to_minutes( const time_duration &duration ) {
return static_cast<T>( duration.turns_ ) / static_cast<T>( 60 );
}
template<typename T>
friend constexpr T to_hours( const time_duration &duration ) {
return static_cast<T>( duration.turns_ ) / static_cast<T>( 60 * 60 );
}
template<typename T>
friend constexpr T to_days( const time_duration &duration ) {
return static_cast<T>( duration.turns_ ) / static_cast<T>( 60 * 60 * 24 );
}
template<typename T>
friend constexpr T to_weeks( const time_duration &duration ) {
return static_cast<T>( duration.turns_ ) / static_cast<T>( 60 * 60 * 24 * 7 );
}
template<typename T>
friend constexpr T to_moves( const time_duration &duration ) {
return to_turns<int>( duration ) * 100;
}
/**@{*/
constexpr bool operator<( const time_duration &rhs ) const {
return turns_ < rhs.turns_;
}
constexpr bool operator<=( const time_duration &rhs ) const {
return turns_ <= rhs.turns_;
}
constexpr bool operator>( const time_duration &rhs ) const {
return turns_ > rhs.turns_;
}
constexpr bool operator>=( const time_duration &rhs ) const {
return turns_ >= rhs.turns_;
}
constexpr bool operator==( const time_duration &rhs ) const {
return turns_ == rhs.turns_;
}
constexpr bool operator!=( const time_duration &rhs ) const {
return turns_ != rhs.turns_;
}
friend constexpr time_duration operator-( const time_duration &duration ) {
return time_duration( -duration.turns_ );
}
friend constexpr time_duration operator+( const time_duration &lhs, const time_duration &rhs ) {
return time_duration( lhs.turns_ + rhs.turns_ );
}
friend time_duration &operator+=( time_duration &lhs, const time_duration &rhs ) {
return lhs = time_duration( lhs.turns_ + rhs.turns_ );
}
friend constexpr time_duration operator-( const time_duration &lhs, const time_duration &rhs ) {
return time_duration( lhs.turns_ - rhs.turns_ );
}
friend time_duration &operator-=( time_duration &lhs, const time_duration &rhs ) {
return lhs = time_duration( lhs.turns_ - rhs.turns_ );
}
// Using double here because it has the highest precision. Callers can cast it to whatever they want.
friend double operator/( const time_duration &lhs, const time_duration &rhs ) {
return static_cast<double>( lhs.turns_ ) / static_cast<double>( rhs.turns_ );
}
template<typename T>
friend constexpr time_duration operator/( const time_duration &lhs, const T rhs ) {
return time_duration( lhs.turns_ / rhs );
}
template<typename T>
friend time_duration &operator/=( time_duration &lhs, const T rhs ) {
return lhs = time_duration( lhs.turns_ / rhs );
}
template<typename T>
friend constexpr time_duration operator*( const time_duration &lhs, const T rhs ) {
return time_duration( lhs.turns_ * rhs );
}
template<typename T>
friend constexpr time_duration operator*( const T lhs, const time_duration &rhs ) {
return time_duration( lhs * rhs.turns_ );
}
template<typename T>
friend time_duration &operator*=( time_duration &lhs, const T rhs ) {
return lhs = time_duration( lhs.turns_ * rhs );
}
friend time_duration operator%( const time_duration &lhs, const time_duration &rhs ) {
return time_duration( lhs.turns_ % rhs.turns_ );
}
/// Returns a random duration in the range [low, hi].
friend time_duration rng( time_duration lo, time_duration hi );
static const std::vector<std::pair<std::string, time_duration>> units;
};
/// @see x_in_y(int,int)
bool x_in_y( const time_duration &a, const time_duration &b );
/**
* Convert the given number into an duration by calling the matching
* `time_duration::from_*` function.
*/
/**@{*/
constexpr time_duration operator"" _turns( const unsigned long long int v )
{
return time_duration::from_turns( v );
}
constexpr time_duration operator"" _seconds( const unsigned long long int v )
{
return time_duration::from_seconds( v );
}
constexpr time_duration operator"" _minutes( const unsigned long long int v )
{
return time_duration::from_minutes( v );
}
constexpr time_duration operator"" _hours( const unsigned long long int v )
{
return time_duration::from_hours( v );
}
constexpr time_duration operator"" _days( const unsigned long long int v )
{
return time_duration::from_days( v );
}
constexpr time_duration operator"" _weeks( const unsigned long long int v )
{
return time_duration::from_weeks( v );
}
/**@}*/
/**
* Returns a string showing a duration. The string contains at most two numbers
* along with their units. E.g. 3661 seconds will return "1 hour and 1 minute"
* (the 1 additional second is clipped). An input of 3601 will return "1 hour"
* (the second is clipped again and the number of additional minutes would be
* 0 so it's skipped).
*/
std::string to_string( const time_duration &d );
enum class clipped_align {
none,
right,
};
enum class clipped_unit {
forever,
second,
minute,
hour,
day,
week,
season,
year,
};
/**
* Returns a value representing the passed in duration truncated to an appropriate unit
* along with the unit in question.
* "10 days" or "1 minute".
* The chosen unit will be the smallest unit, that is at least as much as the
* given duration. E.g. an input of 60 minutes will return "1 hour", an input of
* 59 minutes will return "59 minutes".
*/
std::pair<int, clipped_unit> clipped_time( const time_duration &d );
/**
* Returns a string showing a duration as whole number of appropriate units, e.g.
* "10 days" or "1 minute".
* The chosen unit will be the smallest unit, that is at least as much as the
* given duration. E.g. an input of 60 minutes will return "1 hour", an input of
* 59 minutes will return "59 minutes".
*/
std::string to_string_clipped( const time_duration &d, clipped_align align = clipped_align::none );
/**
* Returns approximate duration.
* @param verbose If true, 'less than' and 'more than' will be printed instead of '<' and '>' respectively.
*/
std::string to_string_approx( const time_duration &dur, bool verbose = true );
/**
* A point in the game time. Use `calendar::turn` to get the current point.
* Modify it by adding/subtracting @ref time_duration.
* This can be compared with the usual comparison operators.
* It can be (de)serialized via JSON.
*
* Note that is does not handle variable sized season length. Changing the
* season length has no effect on it.
*/
class time_point
{
private:
friend class time_duration;
int turn_;
explicit constexpr time_point( const int t ) : turn_( t ) { }
public:
time_point();
// TODO: remove this, nobody should need it, one should use a constant `time_point`
// (representing turn 0) and a `time_duration` instead.
static constexpr time_point from_turn( const int t ) {
return time_point( t );
}
void serialize( JsonOut &jsout ) const;
void deserialize( JsonIn &jsin );
// TODO: try to get rid of this
template<typename T>
friend constexpr T to_turn( const time_point &point ) {
return point.turn_;
}
friend constexpr inline bool operator<( const time_point &lhs, const time_point &rhs ) {
return to_turn<int>( lhs ) < to_turn<int>( rhs );
}
friend constexpr inline bool operator<=( const time_point &lhs, const time_point &rhs ) {
return to_turn<int>( lhs ) <= to_turn<int>( rhs );
}
friend constexpr inline bool operator>( const time_point &lhs, const time_point &rhs ) {
return to_turn<int>( lhs ) > to_turn<int>( rhs );
}
friend constexpr inline bool operator>=( const time_point &lhs, const time_point &rhs ) {
return to_turn<int>( lhs ) >= to_turn<int>( rhs );
}
friend constexpr inline bool operator==( const time_point &lhs, const time_point &rhs ) {
return to_turn<int>( lhs ) == to_turn<int>( rhs );
}
friend constexpr inline bool operator!=( const time_point &lhs, const time_point &rhs ) {
return to_turn<int>( lhs ) != to_turn<int>( rhs );
}
friend constexpr inline time_duration operator-(
const time_point &lhs, const time_point &rhs ) {
return time_duration::from_turns( to_turn<int>( lhs ) - to_turn<int>( rhs ) );
}
friend constexpr inline time_point operator+(
const time_point &lhs, const time_duration &rhs ) {
return time_point::from_turn( to_turn<int>( lhs ) + to_turns<int>( rhs ) );
}
friend time_point inline &operator+=( time_point &lhs, const time_duration &rhs ) {
return lhs = time_point::from_turn( to_turn<int>( lhs ) + to_turns<int>( rhs ) );
}
friend constexpr inline time_point operator-(
const time_point &lhs, const time_duration &rhs ) {
return time_point::from_turn( to_turn<int>( lhs ) - to_turns<int>( rhs ) );
}
friend time_point inline &operator-=( time_point &lhs, const time_duration &rhs ) {
return lhs = time_point::from_turn( to_turn<int>( lhs ) - to_turns<int>( rhs ) );
}
// TODO: implement minutes_of_hour and so on and use it.
};
inline time_duration time_past_midnight( const time_point &p )
{
return ( p - calendar::turn_zero ) % 1_days;
}
inline time_duration time_past_new_year( const time_point &p )
{
return ( p - calendar::turn_zero ) % calendar::year_length();
}
template<typename T>
inline T minute_of_hour( const time_point &p )
{
return to_minutes<T>( ( p - calendar::turn_zero ) % 1_hours );
}
template<typename T>
inline T hour_of_day( const time_point &p )
{
return to_hours<T>( ( p - calendar::turn_zero ) % 1_days );
}
/// This uses the current season length.
template<typename T>
inline T day_of_season( const time_point &p )
{
return to_days<T>( ( p - calendar::turn_zero ) % calendar::season_length() );
}
/**
* A class that keeps time data other than current time. Similar to @ref calendar namespace, but not static and so more testable.
*/
class calendar_config
{
private:
time_point _start_of_cataclysm;
time_point _start_of_game;
season_type _initial_season;
bool _eternal_season;
// TODO: Remove and make the class immutable, have set_calendar in game or something
friend class game;
friend void calendar::set_calendar_config( const calendar_config & );
calendar_config &operator=( const calendar_config & ) = default;
public:
calendar_config( time_point start_of_cataclysm,
time_point start_of_game,
season_type initial_season,
bool eternal_season )
: _start_of_cataclysm( start_of_cataclysm )
, _start_of_game( start_of_game )
, _initial_season( initial_season )
, _eternal_season( eternal_season )
{}
calendar_config( const calendar_config & ) = default;
// TODO: Remove reference bit after testing!
const time_point &start_of_cataclysm() const {
return _start_of_cataclysm;
}
const time_point &start_of_game() const {
return _start_of_game;
}
const season_type &initial_season() const {
return _initial_season;
}
bool eternal_season() const {
return _eternal_season;
}
time_duration season_length() const {
// TODO: Store it in calendar config once the rest of the game looks at config properly
return calendar::season_length();
}
};
/// @returns The season of the of the given time point. Returns the same season for
/// any input if the calendar::eternal_season yields true.
season_type season_of_year( const time_point &p );
/// @returns The time point formatted to be shown to the player. Contains year, season, day and time of day.
std::string to_string( const time_point &p );
/// @returns The time point formatted to be shown to the player. Contains only the time of day, not the year, day or season.
std::string to_string_time_of_day( const time_point &p );
/** Returns the current light level of the moon. */
moon_phase get_moon_phase( const time_point &p );
/** Returns the current sunrise time based on the time of year. */
time_point sunrise( const time_point &p );
/** Returns the current sunset time based on the time of year. */
time_point sunset( const time_point &p );
/** Returns the time it gets light based on sunrise */
time_point daylight_time( const time_point &p );
/** Returns the time it gets dark based on sunset */
time_point night_time( const time_point &p );
/** Returns true if it's currently night time - after dusk and before dawn. */
bool is_night( const time_point &p );
/** Returns true if it's currently day time - after dawn and before dusk. */
bool is_day( const time_point &p );
/** Returns true if it's currently dusk - between sunset and and twilight_duration after sunset. */
bool is_dusk( const time_point &p );
/** Returns true if it's currently dawn - between sunrise and twilight_duration after sunrise. */
bool is_dawn( const time_point &p );
/** Returns the current seasonally-adjusted maximum daylight level */
double current_daylight_level( const time_point &p );
/** How much light is provided in full daylight */
double default_daylight_level();
/** Returns the current sunlight or moonlight level through the preceding functions.
* By default, returns sunlight level for vision, with moonlight providing a measurable amount
* of light. with vision == false, returns sunlight for solar panel purposes, and moonlight
* provides 0 light */
float sunlight( const time_point &p, bool vision = true );
enum class weekdays : int {
SUNDAY = 0,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
};
weekdays day_of_week( const time_point &p );
#endif // CATA_SRC_CALENDAR_H