@@ -263,8 +263,6 @@ void Item_factory::finalize_pre( itype &obj )
npc_implied_flags( obj );

if( obj.comestible ) {
obj.comestible->spoils *= HOURS( 1 ); // JSON specifies hours so convert to turns

if( get_option<bool>( "NO_VITAMINS" ) ) {
obj.comestible->vitamins.clear();
} else if( obj.comestible->vitamins.empty() && obj.comestible->healthy >= 0 ) {
@@ -1431,7 +1429,7 @@ void Item_factory::load( islot_comestible &slot, JsonObject &jo, const std::stri
assign( jo, "stim", slot.stim, strict );
assign( jo, "healthy", slot.healthy, strict );
assign( jo, "parasites", slot.parasites, strict, 0 );
assign( jo, "spoils_in", slot.spoils, strict, 0 );
assign( jo, "spoils_in", slot.spoils, strict, 1_hours );

if( jo.has_string( "addiction_type" ) ) {
slot.add = addiction_type( jo.get_string( "addiction_type" ) );
@@ -110,8 +110,8 @@ struct islot_comestible
/** effect on character nutrition (may be negative) */
int nutr = 0;

/** turns until becomes rotten, or zero if never spoils */
int spoils = 0;
/** Time until becomes rotten at standard temperature, or zero if never spoils */
time_duration spoils = 0;

/** addiction potential */
int addict = 0;
@@ -423,24 +423,24 @@ int iuse::smoking(player *p, item *it, bool, const tripoint&)

item cig;
if (it->typeId() == "cig") {
cig = item("cig_lit", int(calendar::turn));
cig = item( "cig_lit", calendar::turn );
cig.item_counter = 40;
p->mod_hunger(-3);
p->mod_thirst(2);
} else if (it->typeId() == "handrolled_cig") {
// This transforms the hand-rolled into a normal cig, which isn't exactly
// what I want, but leaving it for now.
cig = item("cig_lit", int(calendar::turn));
cig = item( "cig_lit", calendar::turn );
cig.item_counter = 40;
p->mod_thirst(2);
p->mod_hunger(-3);
} else if (it->typeId() == "cigar") {
cig = item("cigar_lit", int(calendar::turn));
cig = item( "cigar_lit", calendar::turn );
cig.item_counter = 120;
p->mod_thirst(3);
p->mod_hunger(-4);
} else if (it->typeId() == "joint") {
cig = item("joint_lit", int(calendar::turn));
cig = item( "joint_lit", calendar::turn );
cig.item_counter = 40;
p->mod_hunger(4);
p->mod_thirst(6);
@@ -4078,8 +4078,8 @@ void iuse::cut_log_into_planks( player &p )
{
p.moves -= 300;
p.add_msg_if_player(_("You cut the log into planks."));
item plank("2x4", int(calendar::turn));
item scrap("splinter", int(calendar::turn));
item plank( "2x4", calendar::turn );
item scrap( "splinter", calendar::turn );
const int max_planks = 10;
/** @EFFECT_FABRICATION increases number of planks cut from a log */
int planks = normal_roll( 2 + p.get_skill_level( skill_fabrication ), 1 );
@@ -1272,7 +1272,7 @@ int salvage_actor::cut_up( player &p, item &it, item &cut ) const
for( auto salvaged : materials_salvaged ) {
std::string mat_name = salvaged.first;
int amount = salvaged.second;
item result( mat_name, int(calendar::turn) );
item result( mat_name, calendar::turn );
if (amount > 0) {
add_msg( m_good, ngettext("Salvaged %1$i %2$s.", "Salvaged %1$i %2$s.", amount),
amount, result.display_name( amount ).c_str() );
@@ -1840,7 +1840,9 @@ void musical_instrument_actor::load( JsonObject &obj )
volume = obj.get_int( "volume" );
fun = obj.get_int( "fun" );
fun_bonus = obj.get_int( "fun_bonus", 0 );
description_frequency = obj.get_int( "description_frequency" );
if( !obj.read( "description_frequency", description_frequency ) ) {
obj.throw_error( "missing member \"description_frequency\"" );
}
player_descriptions = obj.get_string_array( "player_descriptions" );
npc_descriptions = obj.get_string_array( "npc_descriptions" );
}
@@ -1913,14 +1915,13 @@ long musical_instrument_actor::use( player &p, item &it, bool t, const tripoint&
std::string desc = "";
/** @EFFECT_PER increases morale bonus when playing an instrument */
const int morale_effect = fun + fun_bonus * p.per_cur;
//@todo: change description_frequency to time_duration
if( morale_effect >= 0 && calendar::once_every( time_duration::from_turns( description_frequency ) ) ) {
if( morale_effect >= 0 && calendar::once_every( description_frequency ) ) {
if( !player_descriptions.empty() && p.is_player() ) {
desc = _( random_entry( player_descriptions ).c_str() );
} else if (!npc_descriptions.empty() && p.is_npc() ) {
desc = string_format(_("%1$s %2$s"), p.disp_name(false).c_str(), random_entry( npc_descriptions ).c_str() );
}
} else if( morale_effect < 0 && ( int(calendar::turn) % 10 ) == 0 ) {
} else if( morale_effect < 0 && calendar::once_every( 10_turns ) ) {
// No musical skills = possible morale penalty
if ( p.is_player() ) {
desc = _("You produce an annoying sound");
@@ -10,6 +10,8 @@
#include "int_id.h"
#include "explosion.h"
#include "units.h"
#include "calendar.h"

#include <limits.h>
#include <set>
#include <map>
@@ -634,9 +636,9 @@ class musical_instrument_actor : public iuse_actor
*/
std::vector< std::string > npc_descriptions;
/**
* Display description once per this many turns
* Display description once per this duration (@ref calendar::once_every).
*/
int description_frequency;
time_duration description_frequency = 0;

musical_instrument_actor( const std::string &type = "musical_instrument" ) : iuse_actor( type ) {}

@@ -339,8 +339,9 @@ void map::generate_lightmap( const int zlev )
}

} else if( vp.has_flag( VPFLAG_CIRCLE_LIGHT ) ) {
if( ( calendar::turn % 2 && vp.has_flag( VPFLAG_ODDTURN ) ) ||
( !( calendar::turn % 2 ) && vp.has_flag( VPFLAG_EVENTURN ) ) ||
const bool odd_turn = calendar::once_every( 2_turns );
if( ( odd_turn && vp.has_flag( VPFLAG_ODDTURN ) ) ||
( !odd_turn && vp.has_flag( VPFLAG_EVENTURN ) ) ||
( !( vp.has_flag( VPFLAG_EVENTURN ) || vp.has_flag( VPFLAG_ODDTURN ) ) ) ) {

add_light_source( src, vp.bonus );
@@ -4591,8 +4591,8 @@ void map::make_active( item_location &loc )
static void apply_in_fridge(item &it)
{
if (it.is_food()) {
if (it.fridge == 0) {
it.fridge = (int) calendar::turn;
if( it.fridge == calendar::before_time_starts ) {
it.fridge = calendar::turn;
}
// cool down of the HOT flag, is unsigned, don't go below 1
if ((it.has_flag("HOT")) && (it.item_counter > 10)) {
@@ -6590,7 +6590,7 @@ void map::saven( const int gridx, const int gridy, const int gridz )

dbg( D_INFO ) << "map::saven abs_x: " << abs_x << " abs_y: " << abs_y << " abs_z: " << abs_z
<< " gridn: " << gridn;
submap_to_save->turn_last_touched = int(calendar::turn);
submap_to_save->last_touched = calendar::turn;
MAPBUFFER.add_submap( abs_x, abs_y, abs_z, submap_to_save );
}

@@ -6630,7 +6630,7 @@ static void generate_uniform( const int x, const int y, const int z, const ter_i
submap *sm = new submap();
sm->is_uniform = true;
std::uninitialized_fill_n( &sm->ter[0][0], block_size, terrain_type );
sm->turn_last_touched = int(calendar::turn);
sm->last_touched = calendar::turn;
MAPBUFFER.add_submap( x + xd, y + yd, z, sm );
}
}
@@ -6779,7 +6779,7 @@ void map::remove_rotten_items( Container &items, const tripoint &pnt )
}
}

void map::fill_funnels( const tripoint &p, int since_turn )
void map::fill_funnels( const tripoint &p, const time_point &since )
{
const auto &tr = tr_at( p );
if( !tr.is_funnel() ) {
@@ -6798,7 +6798,7 @@ void map::fill_funnels( const tripoint &p, int since_turn )
}
}
if( biggest_container != items.end() ) {
retroactively_fill_from_funnel( *biggest_container, tr, since_turn, calendar::turn, getabs( p ) );
retroactively_fill_from_funnel( *biggest_container, tr, since, calendar::turn, getabs( p ) );
}
}

@@ -6838,21 +6838,21 @@ void map::grow_plant( const tripoint &p )
}
}

void map::restock_fruits( const tripoint &p, int time_since_last_actualize )
void map::restock_fruits( const tripoint &p, const time_duration &time_since_last_actualize )
{
const auto &ter = this->ter( p ).obj();
if( !ter.has_flag( TFLAG_HARVESTED ) ) {
return; // Already harvestable. Do nothing.
}
// Make it harvestable again if the last actualization was during a different season or year.
const calendar last_touched = calendar::turn - time_since_last_actualize;
const time_point last_touched = calendar::turn - time_since_last_actualize;
if( season_of_year( calendar::turn ) != season_of_year( last_touched ) ||
time_since_last_actualize >= to_turns<int>( calendar::season_length() ) ) {
time_since_last_actualize >= calendar::season_length() ) {
ter_set( p, ter.transforms_into );
}
}

void map::produce_sap( const tripoint &p, int time_since_last_actualize )
void map::produce_sap( const tripoint &p, const time_duration &time_since_last_actualize )
{
if( time_since_last_actualize <= 0 ) {
return;
@@ -6866,34 +6866,33 @@ void map::produce_sap( const tripoint &p, int time_since_last_actualize )
static const int maple_sap_per_season = 56;

// How many turns to produce 1 charge (250 ml) of sap?
const int turns_season = to_turns<int>( calendar::season_length() );
const int producing_length = int( 0.75f * turns_season );
const time_duration producing_length = 0.75 * calendar::season_length();

const int turns_to_produce = producing_length / ( maple_sap_per_season * 4 );
const time_duration turns_to_produce = producing_length / ( maple_sap_per_season * 4 );

// How long of this time_since_last_actualize have we been in the producing period (late winter, early spring)?
int time_producing = 0;
time_duration time_producing = 0;

if( time_since_last_actualize >= to_turns<int>( calendar::year_length() ) ) {
if( time_since_last_actualize >= calendar::year_length() ) {
time_producing = producing_length;
} else {
// We are only producing sap on the intersection with the sap producing season.
int early_spring_end = int( 0.5f * turns_season );
int late_winter_start = int( 3.75f * turns_season );
const time_duration early_spring_end = 0.5f * calendar::season_length();
const time_duration late_winter_start = 3.75f * calendar::season_length();

calendar last_actualize = calendar::turn - time_since_last_actualize;
int last_actualize_tof = last_actualize.turn_of_year();
const time_point last_actualize = calendar::turn - time_since_last_actualize;
const time_duration last_actualize_tof = time_past_new_year( last_actualize );
bool last_producing = (
last_actualize_tof >= late_winter_start ||
last_actualize_tof < early_spring_end
);
int current_tof = calendar::turn.turn_of_year();
const time_duration current_tof = time_past_new_year( calendar::turn );
bool current_producing = (
current_tof >= late_winter_start ||
current_tof < early_spring_end
);

int non_producing_length = int( 3.25f * turns_season );
const time_duration non_producing_length = 3.25 * calendar::season_length();

if( last_producing && current_producing ) {
if( time_since_last_actualize < non_producing_length ) {
@@ -6910,19 +6909,19 @@ void map::produce_sap( const tripoint &p, int time_since_last_actualize )
if( last_actualize_tof < early_spring_end ) {
time_producing = early_spring_end - last_actualize_tof;
} else {
time_producing = to_turns<int>( calendar::year_length() ) - last_actualize_tof + early_spring_end;
time_producing = calendar::year_length() - last_actualize_tof + early_spring_end;
}
} else if ( !last_producing && current_producing ) {
// We hit the start of late winter
if( current_tof >= late_winter_start ) {
time_producing = current_tof - late_winter_start;
} else {
time_producing = int( 0.25f * turns_season ) + current_tof;
time_producing = 0.25f * calendar::season_length() + current_tof;
}
}
}

long new_charges = roll_remainder( (double)time_producing / turns_to_produce );
long new_charges = roll_remainder( time_producing / turns_to_produce );
// Not enough time to produce 1 charge of sap
if( new_charges <= 0 ) {
return;
@@ -6950,15 +6949,15 @@ void map::produce_sap( const tripoint &p, int time_since_last_actualize )
}
}

void map::rad_scorch( const tripoint &p, int time_since_last_actualize )
void map::rad_scorch( const tripoint &p, const time_duration &time_since_last_actualize )
{
const int rads = get_radiation( p );
if( rads == 0 ) {
return;
}

// TODO: More interesting rad scorch chance - base on season length?
if( !x_in_y( 1.0 * rads * rads * time_since_last_actualize, DAYS(91) ) ) {
if( !x_in_y( 1.0 * rads * rads * time_since_last_actualize, 91_days ) ) {
return;
}

@@ -6996,16 +6995,16 @@ void map::rad_scorch( const tripoint &p, int time_since_last_actualize )
}
}

void map::decay_cosmetic_fields( const tripoint &p, int time_since_last_actualize )
void map::decay_cosmetic_fields( const tripoint &p, const time_duration &time_since_last_actualize )
{
for( auto &pr : field_at( p ) ) {
auto &fd = pr.second;
if( !fd.decays_on_actualize() ) {
continue;
}

const int added_age = 2 * time_since_last_actualize / rng( 2, 4 );
fd.mod_age( added_age );
const time_duration added_age = 2 * time_since_last_actualize / rng( 2, 4 );
fd.mod_age( to_turns<int>( added_age ) );
const int hl = fieldlist[ fd.getFieldType() ].halflife;
const int density_drop = fd.getFieldAge() / hl;
if( density_drop > 0 ) {
@@ -7023,7 +7022,7 @@ void map::actualize( const int gridx, const int gridy, const int gridz )
return;
}

const auto time_since_last_actualize = calendar::turn - tmpsub->turn_last_touched;
const time_duration time_since_last_actualize = calendar::turn - tmpsub->last_touched;
const bool do_funnels = ( gridz >= 0 );

// check spoiled stuff, and fill up funnels while we're at it
@@ -7047,7 +7046,7 @@ void map::actualize( const int gridx, const int gridy, const int gridz )
}

if( do_funnels ) {
fill_funnels( pnt, tmpsub->turn_last_touched );
fill_funnels( pnt, tmpsub->last_touched );
}

grow_plant( pnt );
@@ -7064,13 +7063,13 @@ void map::actualize( const int gridx, const int gridy, const int gridz )

//Check for Merchants to restock
for( npc &guy : g->all_npcs() ) {
if( guy.restock > 0 && calendar::turn > guy.restock ) {
if( guy.restock != calendar::before_time_starts && calendar::turn > guy.restock ) {
guy.shop_restock();
}
}

// the last time we touched the submap, is right now.
tmpsub->turn_last_touched = calendar::turn;
tmpsub->last_touched = calendar::turn;
}

void map::add_roofs( const int gridx, const int gridy, const int gridz )
@@ -1292,34 +1292,33 @@ class map
template <typename Container>
void remove_rotten_items( Container &items, const tripoint &p );
/**
* Try to fill funnel based items here. Simulates rain from `since_turn` till now.
* Try to fill funnel based items here. Simulates rain from @p since till now.
* @param p The location in this map where to fill funnels.
* @param since_turn First turn of simulated filling.
*/
void fill_funnels( const tripoint &p, int since_turn );
void fill_funnels( const tripoint &p, const time_point &since );
/**
* Try to grow a harvestable plant to the next stage(s).
*/
void grow_plant( const tripoint &p );
/**
* Try to grow fruits on static plants (not planted by the player)
* @param p Place to restock
* @param time_since_last_actualize Time (in turns) since this function has been
* @param time_since_last_actualize Time since this function has been
* called the last time.
*/
void restock_fruits( const tripoint &p, int time_since_last_actualize );
void restock_fruits( const tripoint &p, const time_duration &time_since_last_actualize );
/**
* Produce sap on tapped maple trees
* @param p Location of tapped tree
* @param time_since_last_actualize Time (in turns) since this function has been
* @param time_since_last_actualize Time since this function has been
* called the last time.
*/
void produce_sap( const tripoint &p, int time_since_last_actualize );
void produce_sap( const tripoint &p, const time_duration &time_since_last_actualize );
/**
* Radiation-related plant (and fungus?) death.
*/
void rad_scorch( const tripoint &p, int time_since_last_actualize );
void decay_cosmetic_fields( const tripoint &p, int time_since_last_actualize );
void rad_scorch( const tripoint &p, const time_duration &time_since_last_actualize );
void decay_cosmetic_fields( const tripoint &p, const time_duration &time_since_last_actualize );

void player_in_field( player &u );
void monster_in_field( monster &z );
@@ -223,7 +223,7 @@ void mapbuffer::save_quad( const std::string &dirname, const std::string &filena
jsout.write( submap_addr.z );
jsout.end_array();

jsout.member( "turn_last_touched", sm->turn_last_touched );
jsout.member( "turn_last_touched", sm->last_touched );
jsout.member( "temperature", sm->temperature );

jsout.member( "terrain" );
@@ -439,7 +439,7 @@ void mapbuffer::deserialize( JsonIn &jsin )
jsin.end_array();
submap_coordinates = tripoint( locx, locy, locz );
} else if( submap_member_name == "turn_last_touched" ) {
sm->turn_last_touched = jsin.get_int();
sm->last_touched = jsin.get_int();
} else if( submap_member_name == "temperature" ) {
sm->temperature = jsin.get_int();
} else if( submap_member_name == "terrain" ) {
@@ -26,7 +26,7 @@ Messages player_messages;

struct game_message : public JsonDeserializer, public JsonSerializer {
std::string message;
calendar timestamp_in_turns = 0;
time_point timestamp_in_turns = 0;
int timestamp_in_user_actions = 0;
int count = 1;
game_message_type type = m_neutral;
@@ -39,8 +39,8 @@ struct game_message : public JsonDeserializer, public JsonSerializer {
type( t ) {
}

int turn() const {
return timestamp_in_turns.get_turn();
const time_point &turn() const {
return timestamp_in_turns;
}

std::string get_with_count() const {
@@ -51,15 +51,15 @@ struct game_message : public JsonDeserializer, public JsonSerializer {
return string_format( _( "%s x %d" ), message.c_str(), count );
}

bool is_new( int const current ) const {
bool is_new( const time_point &current ) const {
return turn() >= current;
}

bool is_recent( int const current ) const {
return turn() + 5 >= current;
bool is_recent( const time_point &current ) const {
return turn() + 5_turns >= current;
}

nc_color get_color( int const current ) const {
nc_color get_color( const time_point &current ) const {
if( is_new( current ) ) {
// color for new messages
return msgtype_to_color( type, false );
@@ -75,15 +75,15 @@ struct game_message : public JsonDeserializer, public JsonSerializer {

void deserialize( JsonIn &jsin ) override {
JsonObject obj = jsin.get_object();
timestamp_in_turns = obj.get_int( "turn" );
obj.read( "turn", timestamp_in_turns );
message = obj.get_string( "message" );
count = obj.get_int( "count" );
type = static_cast<game_message_type>( obj.get_int( "type" ) );
}

void serialize( JsonOut &jsout ) const override {
jsout.start_object();
jsout.member( "turn", static_cast<int>( timestamp_in_turns ) );
jsout.member( "turn", timestamp_in_turns );
jsout.member( "message", message );
jsout.member( "count", count );
jsout.member( "type", static_cast<int>( type ) );
@@ -103,7 +103,7 @@ class Messages::impl_t
{
public:
std::deque<game_message> messages; // Messages to be printed
int curmes = 0; // The last-seen message.
time_point curmes = 0; // The last-seen message.

bool has_undisplayed_messages() const {
return !messages.empty() && messages.back().turn() > curmes;
@@ -120,7 +120,7 @@ class Messages::impl_t
}

auto &last_msg = messages.back();
if( last_msg.turn() + 3 < calendar::turn.get_turn() ) {
if( last_msg.turn() + 3_turns < calendar::turn ) {
return false;
}

@@ -174,7 +174,7 @@ class Messages::impl_t

std::transform( begin( messages ) + offset, end( messages ), back_inserter( result ),
[]( game_message const & msg ) {
return std::make_pair( msg.timestamp_in_turns.print_time(),
return std::make_pair( to_string_time_of_day( msg.timestamp_in_turns ),
msg.count ? msg.message + to_string( msg.count ) : msg.message );
}
);
@@ -313,7 +313,7 @@ void Messages::display_messages()
draw_scrollbar( w, offset, bottom, msg_count, 1, 0, c_white, true );

int line = 1;
int lasttime = -1;
time_duration lasttime = -1_turns;
for( int i = offset; i < msg_count; ++i ) {
const int retrieve_history = abs( i - flip );
if( line > bottom ) {
@@ -324,19 +324,19 @@ void Messages::display_messages()
}

const game_message &m = player_messages.impl_->history( retrieve_history );
const calendar timepassed = calendar::turn - m.timestamp_in_turns;
std::string long_ago = to_string_clipped( time_duration::from_turns( timepassed ) );
const time_duration timepassed = calendar::turn - m.timestamp_in_turns;
std::string long_ago = to_string_clipped( timepassed );
nc_color col = msgtype_to_color( m.type, false );

// Here we separate the unit and amount from one another so that they can be properly padded when they're drawn on the screen.
// Note that the very first character of 'unit' is often a space (except for languages where the time unit directly follows the number.)
const auto amount_len = long_ago.find_first_not_of( "0123456789" );
std::string amount = long_ago.substr( 0, amount_len );
std::string unit = long_ago.substr( amount_len );
if( timepassed.get_turn() != lasttime ) {
if( timepassed != lasttime ) {
right_print( w, line, 2, c_light_blue, string_format( _( "%-3s%-10s" ), amount.c_str(),
unit.c_str() ) );
lasttime = timepassed.get_turn();
lasttime = timepassed;
}

nc_color col_out = col;
@@ -382,7 +382,7 @@ void Messages::display_messages()
}
}

player_messages.impl_->curmes = calendar::turn.get_turn();
player_messages.impl_->curmes = calendar::turn;
}

void Messages::display_messages( const catacurses::window &ipk_target, int const left,
@@ -453,7 +453,7 @@ void Messages::display_messages( const catacurses::window &ipk_target, int const
}
}

player_messages.impl_->curmes = calendar::turn.get_turn();
player_messages.impl_->curmes = calendar::turn;
}

void add_msg( std::string msg )
@@ -202,7 +202,7 @@ bool talk_function::outpost_missions( npc &p, std::string id, std::string title
if (npc_list.size()>0){
entry = _("Profit: $25-$500\nDanger: Low\nTime: 10 hour missions\n \nPatrol Roster:\n");
for( auto &elem : npc_list ) {
entry = entry + " " + elem->name + " ["+ to_string((calendar::turn.get_turn()-elem->companion_mission_time)/600) +" hours] \n";
entry = entry + " " + elem->name + " ["+ to_string( to_hours<int>( calendar::turn - elem->companion_mission_time ) ) +" hours] \n";
}
entry = entry + _("\n \nDo you wish to bring your allies back into your party?");
col_missions["Retrieve Scavenging Patrol"] = entry;
@@ -220,7 +220,7 @@ bool talk_function::outpost_missions( npc &p, std::string id, std::string title
if (npc_list.size()>0){
entry = _("Profit: $200-$1000\nDanger: Medium\nTime: 10 hour missions\n \nRaid Roster:\n");
for( auto &elem : npc_list ) {
entry = entry + " " + elem->name + " ["+ to_string((calendar::turn.get_turn()-elem->companion_mission_time)/600) +" hours] \n";
entry = entry + " " + elem->name + " ["+ to_string( to_hours<int>( calendar::turn - elem->companion_mission_time ) ) +" hours] \n";
}
entry = entry + _("\n \nDo you wish to bring your allies back into your party?");
col_missions["Retrieve Scavenging Raid"] = entry;
@@ -237,7 +237,7 @@ bool talk_function::outpost_missions( npc &p, std::string id, std::string title
if (npc_list.size()>0){
entry = _("Profit: $8/hour\nDanger: Minimal\nTime: 1 hour minimum\n \nLabor Roster:\n");
for( auto &elem : npc_list ) {
entry = entry + " " + elem->name + " ["+ to_string((calendar::turn.get_turn()-elem->companion_mission_time)/600) +" hours] \n";
entry = entry + " " + elem->name + " ["+ to_string( to_hours<int>( calendar::turn - elem->companion_mission_time ) ) +" hours] \n";
}
entry = entry + _("\n \nDo you wish to bring your allies back into your party?");
col_missions["Recover Ally from Menial Labor"] = entry;
@@ -254,7 +254,7 @@ bool talk_function::outpost_missions( npc &p, std::string id, std::string title
if (npc_list.size()>0){
entry = _("Profit: $12/hour\nDanger: Minimal\nTime: 1 hour minimum\n \nLabor Roster:\n");
for( auto &elem : npc_list ) {
entry = entry + " " + elem->name + " ["+ to_string((calendar::turn.get_turn()-elem->companion_mission_time)/600) +" hours] \n";
entry = entry + " " + elem->name + " ["+ to_string( to_hours<int>( calendar::turn - elem->companion_mission_time ) ) +" hours] \n";
}
entry = entry + _("\n \nDo you wish to bring your allies back into your party?");
col_missions["Recover Ally from Carpentry Work"] = entry;
@@ -310,7 +310,7 @@ bool talk_function::outpost_missions( npc &p, std::string id, std::string title
if (npc_list.size()>0){
entry = _("Profit: $10/hour\nDanger: Low\nTime: 4 hour minimum\n \nLabor Roster:\n");
for( auto &elem : npc_list ) {
entry = entry + " " + elem->name + " ["+ to_string((calendar::turn.get_turn()-elem->companion_mission_time)/600) +" hours] \n";
entry = entry + " " + elem->name + " ["+ to_string( to_hours<int>( calendar::turn - elem->companion_mission_time ) ) +" hours] \n";
}
entry = entry + _("\n \nDo you wish to bring your allies back into your party?");
col_missions["Recover Ally from Foraging"] = entry;
@@ -332,20 +332,20 @@ bool talk_function::outpost_missions( npc &p, std::string id, std::string title
entry = _("Profit: $18/hour\nDanger: High\nTime: UNKNOWN\n \n"
" \nRoster:\n");
for( auto &elem : npc_list ) {
if (elem->companion_mission_time == -1){
if( elem->companion_mission_time == calendar::before_time_starts ) {
entry = entry + " " + elem->name + _(" [READY] \n");
npc_list_aux.push_back(elem);
} else if (calendar::turn.get_turn() >= elem->companion_mission_time) {
} else if( calendar::turn >= elem->companion_mission_time ) {
entry = entry + " " + elem->name + _(" [COMPLETE] \n");
} else {
entry = entry + " " + elem->name + " ["+ to_string(abs(calendar::turn.get_turn()-elem->companion_mission_time)/600) +" Hours] \n";
entry = entry + " " + elem->name + " ["+ to_string( abs( to_hours<int>( calendar::turn - elem->companion_mission_time ) ) ) +" Hours] \n";
}
}
if (npc_list_aux.size()>0){
entry_aux = _("Profit: $18/hour\nDanger: High\nTime: UNKNOWN\n \n"
" \nRoster:\n");
for( auto &elem : npc_list_aux ) {
if (elem->companion_mission_time == -1){
if( elem->companion_mission_time == calendar::before_time_starts ) {
entry_aux = entry_aux + " " + elem->name + " [READY] \n";
}
}
@@ -491,7 +491,7 @@ void talk_function::individual_mission( npc &p, std::string desc, std::string id
popup("%s %s", comp->name.c_str(), desc.c_str());
comp->set_companion_mission( p, id );
if (group){
comp->companion_mission_time = -1;
comp->companion_mission_time = calendar::before_time_starts;
} else {
comp->companion_mission_time = calendar::turn.get_turn();
}
@@ -507,10 +507,9 @@ void talk_function::caravan_depart( npc &p, std::string dest, std::string id )
popup(_("The caravan departs with an estimated total travel time of %d hours..."), int(time/600));

for( auto &elem : npc_list ) {
if (elem->companion_mission_time == -1){
if( elem->companion_mission_time == calendar::before_time_starts ) {
//Adds a 10% error in estimated travel time
elem->companion_mission_time = time + int(time * rng_float(-.1,.1)) +
calendar::turn.get_turn();
elem->companion_mission_time = calendar::turn + time_duration::from_turns( time + time * rng_float( -.1, .1 ) );
}
}

@@ -530,7 +529,7 @@ void talk_function::caravan_return( npc &p, std::string dest, std::string id )
if (comp == NULL){
return;
}
if (comp->companion_mission_time == -1){
if( comp->companion_mission_time == calendar::before_time_starts ) {
popup(_("%s returns to your party."), comp->name.c_str());
companion_return( *comp );
return;
@@ -1141,7 +1140,8 @@ bool talk_function::labor_return( npc &p )
return false;
}

float turns = (calendar::turn.get_turn()-comp->companion_mission_time)/600;
//@todo actually it's hours, not turns
float turns = to_hours<float>( calendar::turn - comp->companion_mission_time );
int money = 8*turns;
g->u.cash += money*100;

@@ -1208,7 +1208,8 @@ bool talk_function::carpenter_return( npc &p )
}
}

float turns = (calendar::turn.get_turn()-comp->companion_mission_time)/600;
//@todo actually it's hours, not turns
float turns = to_hours<float>( calendar::turn - comp->companion_mission_time );
int money = 12*turns;
g->u.cash += money*100;

@@ -1295,7 +1296,8 @@ bool talk_function::forage_return( npc &p )
}
}

float turns = (calendar::turn.get_turn()-comp->companion_mission_time)/600;
//@todo actually it's hours, not turns
float turns = to_hours<float>( calendar::turn - comp->companion_mission_time );
int money = 10*turns;
g->u.cash += money*100;

@@ -1418,7 +1420,7 @@ void talk_function::force_on_force( std::vector<std::shared_ptr<npc>> defender,
void talk_function::companion_return( npc &comp ){
assert( !comp.is_active() );
comp.reset_companion_mission();
comp.companion_mission_time = 0;
comp.companion_mission_time = calendar::before_time_starts;
// npc *may* be active, or not if outside the reality bubble
g->reload_npcs();
}
@@ -1461,7 +1463,7 @@ npc *talk_function::companion_choose(){
npc *talk_function::companion_choose_return(std::string id, int deadline){
std::vector<npc *> available;
for( const auto &guy : overmap_buffer.get_companion_mission_npcs() ) {
if( guy->get_companion_mission() == id && guy->companion_mission_time <= deadline) {
if( guy->get_companion_mission() == id && guy->companion_mission_time <= time_point::from_turn( deadline ) ) {
available.push_back( guy.get() );
}
}
@@ -114,18 +114,13 @@ void game::list_missions()
mvwprintz( w_missions, y++, 31, c_white, miss->get_description() );
}
if( miss->has_deadline() ) {
const calendar deadline( miss->get_deadline() );
//@todo: make a function to print the time (don't we already have one?)
//~ 1 season name, 2 day of season, 3 time of day
std::string dl = string_format( _( "%1$s, day %2$d %3$s" ),
calendar::name_season( season_of_year( deadline ) ),
day_of_season<int>( deadline ) + 1, deadline.print_time() );
mvwprintz( w_missions, y++, 31, c_white, _( "Deadline: %s" ), dl.c_str() );
const time_point deadline = miss->get_deadline();
mvwprintz( w_missions, y++, 31, c_white, _( "Deadline: %s" ), to_string( deadline ) );

if( tab != tab_mode::TAB_COMPLETED ) {
// There's no point in displaying this for a completed mission.
// @TODO: But displaying when you completed it would be useful.
const time_duration remaining = time_duration::from_turns( deadline.get_turn() - calendar::turn );
const time_duration remaining = deadline - calendar::turn;
std::string remaining_time;

if( remaining <= 0_turns ) {
@@ -88,9 +88,9 @@ MonsterGroupResult MonsterGroupManager::GetResultFromGroup(
mt.in_category( "WILDLIFE" ) );
}
//Insure that the time is not before the spawn first appears or after it stops appearing
valid_entry = valid_entry && ( HOURS( it->starts ) < calendar::turn.get_turn() );
valid_entry = valid_entry && ( calendar::time_of_cataclysm + it->starts < calendar::turn );
valid_entry = valid_entry && ( it->lasts_forever() ||
HOURS( it->ends ) > calendar::turn.get_turn() );
calendar::time_of_cataclysm + it->ends > calendar::turn );

std::vector<std::pair<int, int> > valid_times_of_day;
bool season_limited = false;
@@ -332,22 +332,23 @@ void MonsterGroupManager::LoadMonsterGroup( JsonObject &jo )
pack_min = packarr.next_int();
pack_max = packarr.next_int();
}
int starts = 0;
int ends = 0;
static const time_duration tdfactor = 1_hours;
time_duration starts = 0;
time_duration ends = 0;
if( mon.has_member( "starts" ) ) {
if( get_option<float>( "MONSTER_UPGRADE_FACTOR" ) > 0 ) {
starts = mon.get_int( "starts" ) * get_option<float>( "MONSTER_UPGRADE_FACTOR" );
starts = tdfactor * mon.get_int( "starts" ) * get_option<float>( "MONSTER_UPGRADE_FACTOR" );
} else {
// Default value if the monster upgrade factor is set to 0.0 - off
starts = mon.get_int( "starts" );
starts = tdfactor * mon.get_int( "starts" );
}
}
if( mon.has_member( "ends" ) ) {
if( get_option<float>( "MONSTER_UPGRADE_FACTOR" ) > 0 ) {
ends = mon.get_int( "ends" ) * get_option<float>( "MONSTER_UPGRADE_FACTOR" );
ends = tdfactor * mon.get_int( "ends" ) * get_option<float>( "MONSTER_UPGRADE_FACTOR" );
} else {
// Default value if the monster upgrade factor is set to 0.0 - off
ends = mon.get_int( "ends" );
ends = tdfactor * mon.get_int( "ends" );
}
}
MonsterGroupEntry new_mon_group = MonsterGroupEntry( name, freq, cost, pack_min, pack_max, starts,
@@ -8,6 +8,7 @@
#include <string>
#include "enums.h"
#include "string_id.h"
#include "calendar.h"
#include "monster.h"

// from overmap.h
@@ -37,15 +38,14 @@ struct MonsterGroupEntry {
int pack_minimum;
int pack_maximum;
std::vector<std::string> conditions;
int starts;
int ends;
time_duration starts;
time_duration ends;
bool lasts_forever() const {
return ( ends <= 0 );
}

MonsterGroupEntry( const mtype_id &id, int new_freq, int new_cost,
int new_pack_min, int new_pack_max, int new_starts,
int new_ends )
int new_pack_min, int new_pack_max, const time_duration &new_starts, const time_duration &new_ends )
: name( id )
, frequency( new_freq )
, cost_multiplier( new_cost )
@@ -276,7 +276,7 @@ void monster::try_upgrade(bool pin_time) {
return;
}

const int current_day = calendar::turn.get_turn() / DAYS(1);
const int current_day = to_days<int>( calendar::time_of_cataclysm - calendar::turn );

if (upgrade_time < 0) {
upgrade_time = next_upgrade_time();
@@ -288,7 +288,7 @@ void monster::try_upgrade(bool pin_time) {
upgrade_time += current_day;
} else {
// offset by starting season
upgrade_time += calendar::start / DAYS(1);
upgrade_time += to_days<int>( calendar::time_of_cataclysm - calendar::start );
}
}

@@ -74,6 +74,10 @@ void starting_clothes( npc &who, const npc_class_id &type, bool male );
void starting_inv( npc &who, const npc_class_id &type );

npc::npc()
: player()
, restock( calendar::before_time_starts )
, companion_mission_time( calendar::before_time_starts )
, last_updated( calendar::turn )
{
submap_coords = point( 0, 0 );
position.x = -1;
@@ -100,9 +104,6 @@ npc::npc()
mission = NPC_MISSION_NULL;
myclass = npc_class_id::NULL_ID();
patience = 0;
restock = -1;
companion_mission_time = 0;
last_updated = calendar::turn;
attitude = NPCATT_NULL;

*path_settings = pathfinding_settings( 0, 1000, 1000, 10, true, true, true );
@@ -305,7 +306,7 @@ void npc::randomize( const npc_class_id &type )
per_max = the_class.roll_perception();

if( myclass->get_shopkeeper_items() != "EMPTY_GROUP" ) {
restock = DAYS( 3 );
restock = calendar::turn + 3_days;
cash += 100000;
}

@@ -1409,7 +1410,7 @@ bool npc::wants_to_buy( const item &it, int at_price, int market_price ) const

void npc::shop_restock()
{
restock = calendar::turn + DAYS( 3 );
restock = calendar::turn + 3_days;
if( is_friend() ) {
return;
}
@@ -2083,23 +2084,21 @@ void npc::on_unload()

void npc::on_load()
{
const int now = calendar::turn;
// Cap at some reasonable number, say 2 days
const time_duration dt = std::min( calendar::turn - last_updated, 2_days );
// TODO: Sleeping, healing etc.
int dt = now - last_updated;
last_updated = calendar::turn;
// Cap at some reasonable number, say 2 days (2 * 48 * 30 minutes)
dt = std::min( dt, 2 * 48 * MINUTES( 30 ) );
int cur = now - dt;
add_msg( m_debug, "on_load() by %s, %d turns", name.c_str(), dt );
time_point cur = calendar::turn - dt;
add_msg( m_debug, "on_load() by %s, %d turns", name, to_turns<int>( dt ) );
// First update with 30 minute granularity, then 5 minutes, then turns
for( ; cur < now - MINUTES( 30 ); cur += MINUTES( 30 ) + 1 ) {
update_body( cur, cur + MINUTES( 30 ) );
for( ; cur < calendar::turn - 30_minutes; cur += 30_minutes + 1_turns ) {
update_body( to_turn<int>( cur ), to_turn<int>( cur + 30_minutes ) );
}
for( ; cur < now - MINUTES( 5 ); cur += MINUTES( 5 ) + 1 ) {
update_body( cur, cur + MINUTES( 5 ) );
for( ; cur < calendar::turn - 5_minutes; cur += 5_minutes + 1_turns ) {
update_body( to_turn<int>( cur ), to_turn<int>( cur + 5_minutes ) );
}
for( ; cur < now; cur++ ) {
update_body( cur, cur + 1 );
for( ; cur < calendar::turn; cur += 1_turns ) {
update_body( to_turn<int>( cur ), to_turn<int>( cur + 1_turns ) );
}

if( dt > 0 ) {
@@ -5,6 +5,7 @@
#include "player.h"
#include "faction.h"
#include "pimpl.h"
#include "calendar.h"

#include <vector>
#include <string>
@@ -767,7 +768,7 @@ class npc : public player
*/
point submap_coords;
// Type of complaint->last time we complained about this type
std::map<std::string, int> complaints;
std::map<std::string, time_point> complaints;

npc_short_term_cache ai_cache;
public:
@@ -807,7 +808,7 @@ class npc : public player
*/
tripoint pulp_location;

int restock;
time_point restock;
bool fetching_item;
bool has_new_items; // If true, we have something new and should re-equip
int worst_item_value; // The value of our least-wanted item
@@ -817,7 +818,7 @@ class npc : public player
// Personality & other defining characteristics
string_id<faction> fac_id; // A temp variable used to inform the game which faction to link
faction *my_fac;
int companion_mission_time;
time_point companion_mission_time;
npc_mission mission;
npc_personality personality;
npc_opinion op_of_u;
@@ -830,7 +831,7 @@ class npc : public player
// Dummy point that indicates that the goal is invalid.
static const tripoint no_goal_point;

int last_updated;
time_point last_updated;
/**
* Do some cleanup and caching as npc is being unloaded from map.
*/
@@ -3177,15 +3177,30 @@ bool npc::complain()
// Don't wake player up with non-serious complaints
const bool do_complain = rules.allow_complain && !g->u.in_sleep_state();

// Don't have a default constructor for time_point, so accessing it in the
// complaints map is a bit difficult, those lambdas should cover it.
const auto complain_since = [this]( const std::string & key, const time_duration & d ) {
const auto iter = complaints.find( key );
return iter == complaints.end() || iter->second < calendar::turn - d;
};
const auto set_complain_since = [this]( const std::string & key ) {
const auto iter = complaints.find( key );
if( iter == complaints.end() ) {
complaints.emplace( key, calendar::turn );
} else {
iter->second = calendar::turn;
}
};

// When infected, complain every (4-intensity) hours
// At intensity 3, ignore player wanting us to shut up
if( has_effect( effect_infected ) ) {
body_part bp = bp_affected( *this, effect_infected );
const auto &eff = get_effect( effect_infected, bp );
if( complaints[infected_string] < calendar::turn - HOURS( 4 - eff.get_intensity() ) &&
if( complain_since( infected_string, time_duration::from_hours( 4 - eff.get_intensity() ) ) &&
( do_complain || eff.get_intensity() >= 3 ) ) {
say( _( "My %s wound is infected..." ), body_part_name( bp ).c_str() );
complaints[infected_string] = calendar::turn;
set_complain_since( infected_string );
// Only one complaint per turn
return true;
}
@@ -3194,61 +3209,55 @@ bool npc::complain()
// When bitten, complain every hour, but respect restrictions
if( has_effect( effect_bite ) ) {
body_part bp = bp_affected( *this, effect_bite );
if( do_complain &&
complaints[bite_string] < calendar::turn - HOURS( 1 ) ) {
if( do_complain && complain_since( bite_string, 1_hours ) ) {
say( _( "The bite wound on my %s looks bad." ), body_part_name( bp ).c_str() );
complaints[bite_string] = calendar::turn;
set_complain_since( bite_string );
return true;
}
}

// When tired, complain every 30 minutes
// If massively tired, ignore restrictions
if( get_fatigue() > TIRED &&
complaints[fatigue_string] < calendar::turn - MINUTES( 30 ) &&
if( get_fatigue() > TIRED && complain_since( fatigue_string, 30_minutes ) &&
( do_complain || get_fatigue() > MASSIVE_FATIGUE - 100 ) ) {
say( "<yawn>" );
complaints[fatigue_string] = calendar::turn;
set_complain_since( fatigue_string );
return true;
}

// Radiation every 10 minutes
if( radiation > 90 &&
complaints[radiation_string] < calendar::turn - MINUTES( 10 ) &&
if( radiation > 90 && complain_since( radiation_string, 10_minutes ) &&
( do_complain || radiation > 150 ) ) {
say( _( "I'm suffering from radiation sickness..." ) );
complaints[radiation_string] = calendar::turn;
set_complain_since( radiation_string );
return true;
}

// Hunger every 3-6 hours
// Since NPCs can't starve to death, respect the rules
if( get_hunger() > 160 &&
complaints[hunger_string] < calendar::turn - std::max( HOURS( 3 ),
MINUTES( 60 * 8 - get_hunger() ) ) &&
if( get_hunger() > 160 && complain_since( hunger_string, std::max( 3_hours,
time_duration::from_minutes( 60 * 8 - get_hunger() ) ) ) &&
do_complain ) {
say( _( "<hungry>" ) );
complaints[hunger_string] = calendar::turn;
set_complain_since( hunger_string );
return true;
}

// Thirst every 2 hours
// Since NPCs can't dry to death, respect the rules
if( get_thirst() > 80
&& complaints[thirst_string] < calendar::turn - HOURS( 2 ) &&
if( get_thirst() > 80 && complain_since( thirst_string, 2_hours ) &&
do_complain ) {
say( _( "<thirsty>" ) );
complaints[thirst_string] = calendar::turn;
set_complain_since( thirst_string );
return true;
}

//Bleeding every 5 minutes
if( has_effect( effect_bleed ) ) {
body_part bp = bp_affected( *this, effect_bleed );
if( do_complain &&
complaints[bleed_string] < calendar::turn - MINUTES( 5 ) ) {
if( do_complain && complain_since( bleed_string, 5_minutes ) ) {
say( _( "My %s is bleeding!" ), body_part_name( bp ).c_str() );
complaints[bleed_string] = calendar::turn;
set_complain_since( bleed_string );
return true;
}
}
@@ -2180,10 +2180,7 @@ void player::memorial( std::ostream &memorial_file, std::string epitaph )
//~ First parameter: Pronoun, second parameter: a profession name (with article)
memorial_file << string_format( _( "%1$s was %2$s when the apocalypse began." ),
pronoun.c_str(), profession_name.c_str() ) << eol;
memorial_file << string_format( _( "%1$s died on %2$s of year %3$d, day %4$d, at %5$s." ),
pronoun, calendar::name_season( season_of_year( calendar::turn ) ),
( calendar::turn.years() + 1 ),
day_of_season<int>( calendar::turn ) + 1, calendar::turn.print_time() ) << eol;
memorial_file << string_format( _( "%1$s died on %2$s." ), pronoun, to_string( time_point( calendar::turn ) ) ) << eol;
memorial_file << kill_place << eol;
memorial_file << eol;

@@ -2376,18 +2373,11 @@ void player::add_memorial_log( const std::string &male_msg, const std::string &f
return;
}

std::stringstream timestamp;
//~ A timestamp. Parameters from left to right: Year, season, day, time
timestamp << string_format( _( "Year %1$d, %2$s %3$d, %4$s" ), calendar::turn.years() + 1,
calendar::name_season( season_of_year( calendar::turn ) ),
day_of_season<int>( calendar::turn ) + 1, calendar::turn.print_time()
);

const oter_id &cur_ter = overmap_buffer.ter( global_omt_location() );
const std::string &location = cur_ter->get_name();

std::stringstream log_message;
log_message << "| " << timestamp.str() << " | " << location.c_str() << " | " << msg;
log_message << "| " << to_string( time_point( calendar::turn ) ) << " | " << location << " | " << msg;

memorial_log.push_back( log_message.str() );

@@ -5468,7 +5458,7 @@ void player::suffer()
}

const bool radiogenic = has_trait( trait_RADIOGENIC );
if( radiogenic && int(calendar::turn) % MINUTES(30) == 0 && radiation > 0 ) {
if( radiogenic && calendar::once_every( 30_minutes ) && radiation > 0 ) {
// At 200 irradiation, twice as fast as REGEN
if( x_in_y( radiation, 200 ) ) {
healall( 1 );
@@ -5488,7 +5478,7 @@ void player::suffer()
}
}

if( radiation > 200 && ( int(calendar::turn) % MINUTES(10) == 0 ) && x_in_y( radiation, 1000 ) ) {
if( radiation > 200 && calendar::once_every( 10_minutes ) && x_in_y( radiation, 1000 ) ) {
hurtall( 1, nullptr );
radiation -= 5;
}
@@ -1040,7 +1040,7 @@ void player::hardcoded_effects( effect &it )
// Get a dream if category strength is high enough.
if( strength != 0 ) {
//Once every 6 / 3 / 2 hours, with a bit of randomness
if( ( int( calendar::turn ) % ( 3600 / strength ) == 0 ) && one_in( 3 ) ) {
if( calendar::once_every( 6_hours / strength ) && one_in( 3 ) ) {
// Select a dream
std::string dream = get_category_dream( highcat, strength );
if( !dream.empty() ) {
@@ -1029,8 +1029,6 @@ void npc::load(JsonObject &data)
int misstmp = 0;
int classtmp = 0;
int atttmp = 0;
int comp_miss_t = 0;
int stock = 0;;
std::string facID;
std::string comp_miss;
std::string classid;
@@ -1121,12 +1119,12 @@ void npc::load(JsonObject &data)
companion_mission = comp_miss;
}

if ( data.read( "companion_mission_time", comp_miss_t) ) {
companion_mission_time = comp_miss_t;
if( !data.read( "companion_mission_time", companion_mission_time ) ) {
companion_mission_time = calendar::before_time_starts;
}

if ( data.read( "restock", stock) ) {
restock = stock;
if( !data.read( "restock", restock ) ) {
restock = calendar::before_time_starts;
}

data.read("op_of_u", op_of_u);
@@ -1136,9 +1134,18 @@ void npc::load(JsonObject &data)
data.read("combat_rules", rules);
}

last_updated = data.get_int( "last_updated", calendar::turn );
if( data.has_object( "complaints" ) ) {
data.read( "complaints", complaints );
if( !data.read( "last_updated", last_updated ) ) {
last_updated = calendar::turn;
}
//@todo time_point does not have a default constructor, need to read in the map manually
{
complaints.clear();
JsonObject jo = data.get_object( "complaints" );
for( const std::string &key : jo.get_member_names() ) {
time_point p = 0;
jo.read( key, p );
complaints.emplace( key, p );
}
}
}

@@ -1457,6 +1464,16 @@ void time_point::deserialize( JsonIn &jsin )
turn_ = jsin.get_int();
}

void time_duration::serialize( JsonOut &jsout ) const
{
jsout.write( turns_ );
}

void time_duration::deserialize( JsonIn &jsin )
{
turns_ = jsin.get_int();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
///// item.h

@@ -1504,9 +1521,9 @@ void item::io( Archive& archive )
archive.io( "damage", damage_, 0.0 );
archive.io( "active", active, false );
archive.io( "item_counter", item_counter, static_cast<decltype(item_counter)>( 0 ) );
archive.io( "fridge", fridge, 0 );
archive.io( "rot", rot, 0 );
archive.io( "last_rot_check", last_rot_check, 0 );
archive.io( "fridge", fridge, calendar::before_time_starts );
archive.io( "rot", rot, 0_turns );
archive.io( "last_rot_check", last_rot_check, calendar::time_of_cataclysm );
archive.io( "techniques", techniques, io::empty_default_tag() );
archive.io( "faults", faults, io::empty_default_tag() );
archive.io( "item_tags", item_tags, io::empty_default_tag() );
@@ -9,6 +9,7 @@
#include "int_id.h"
#include "string_id.h"
#include "active_item_cache.h"
#include "calendar.h"

#include <vector>
#include <list>
@@ -167,7 +168,7 @@ struct submap {
active_item_cache active_items;

int field_count = 0;
int turn_last_touched = 0;
time_point last_touched = 0;
int temperature = 0;
std::vector<spawn_point> spawns;
/**
@@ -1313,7 +1313,7 @@ void vehicle::beeper_sound()
return;
}

const bool odd_turn = (calendar::turn % 2 == 0);
const bool odd_turn = calendar::once_every( 2_turns );
for( size_t p = 0; p < parts.size(); ++p ) {
if( !part_flag( p, "BEEPER" ) ) {
continue;
@@ -3,6 +3,7 @@
#include "coordinate_conversions.h"
#include "options.h"
#include "output.h"
#include "calendar.h"
#include "game.h"
#include "map.h"
#include "messages.h"
@@ -70,7 +71,7 @@ void weather_effect::glare()

int get_hourly_rotpoints_at_temp( int temp );

int get_rot_since( const int startturn, const int endturn, const tripoint &location )
time_duration get_rot_since( const time_point &start, const time_point &end, const tripoint &location )
{
// Ensure food doesn't rot in ice labs, where the
// temperature is much less than the weather specifies.
@@ -81,11 +82,11 @@ int get_rot_since( const int startturn, const int endturn, const tripoint &locat
return 0;
}
// TODO: maybe have different rotting speed when underground?
int ret = 0;
time_duration ret = 0;
const auto &wgen = g->get_cur_weather_gen();
for (calendar i(startturn); i.get_turn() < endturn; i += 600) {
for( time_point i = start; i < end; i += 1_hours ) {
w_point w = wgen.get_weather( location, i, g->get_seed() );
ret += std::min(600, endturn - i.get_turn()) * get_hourly_rotpoints_at_temp(w.temperature) / 600;
ret += std::min( 1_hours, end - i ) / 1_hours * get_hourly_rotpoints_at_temp( w.temperature ) * 1_turns;
}
return ret;
}
@@ -145,15 +146,15 @@ weather_sum sum_conditions( const time_point &start, const time_point &end,
/**
* Determine what a funnel has filled out of game, using funnelcontainer.bday as a starting point.
*/
void retroactively_fill_from_funnel( item &it, const trap &tr, int startturn, int endturn,
const tripoint &location )
void retroactively_fill_from_funnel( item &it, const trap &tr, const time_point &start,
const time_point &end, const tripoint &location )
{
if( startturn > endturn || !tr.is_funnel() ) {
if( start > end || !tr.is_funnel() ) {
return;
}

it.set_birthday( endturn ); // bday == last fill check
auto data = sum_conditions( startturn, endturn, location );
it.set_birthday( end ); // bday == last fill check
auto data = sum_conditions( start, end, location );

// Technically 0.0 division is OK, but it will be cleaner without it
if( data.rain_amount > 0 ) {
@@ -496,6 +497,16 @@ void weather_effect::acid()
generic_very_wet(true);
}

static std::string print_time_just_hour( const time_point &p )
{
const int hour = to_hours<int>( time_past_midnight( p ) );
int hour_param = hour % 12;
if( hour_param == 0 ) {
hour_param = 12;
}
return string_format( hour < 12 ? _( "%d AM" ) : _( "%d PM" ), hour_param );
}

// Script from Wikipedia:
// Current time
// The current time is hour/minute Eastern Standard Time
@@ -532,7 +543,7 @@ std::string weather_forecast( point const &abs_sm_pos )
// Current time
weather_report << string_format(
_("The current time is %s Eastern Standard Time. At %s in %s, it was %s. The temperature was %s. "),
calendar::turn.print_time().c_str(), calendar::turn.print_time(true).c_str(),
to_string_time_of_day( calendar::turn ), print_time_just_hour( calendar::turn ),
city_name.c_str(),
weather_data(g->weather).name.c_str(), print_temperature(g->temperature).c_str()
);
@@ -22,12 +22,12 @@
#define BODYTEMP_SCORCHING 9500 //!< Level 3 hotness.
///@}

#include "calendar.h"

#include <string>
#include <vector>
#include <utility>

class time_duration;
class time_point;
class item;
struct point;
struct tripoint;
@@ -148,11 +148,9 @@ weather_sum sum_conditions( const time_point &start,
* @param pos The absolute position of the funnel (in the map square system, the one used
* by the @ref map, but absolute).
* @param tr The funnel (trap which acts as a funnel).
* @param startturn First turn of the retroactive filling.
* @param endturn Last turn of the retroactive filling.
*/
void retroactively_fill_from_funnel( item &it, const trap &tr, int startturn, int endturn,
const tripoint &pos );
void retroactively_fill_from_funnel( item &it, const trap &tr, const time_point &start,
const time_point &end, const tripoint &pos );

double funnel_charges_per_turn( double surface_area_mm2, double rain_depth_mm_per_hour );

@@ -161,9 +159,9 @@ double funnel_charges_per_turn( double surface_area_mm2, double rain_depth_mm_pe
* locations.
* The location is in absolute maps squares (the system which the @ref map uses),
* but absolute (@ref map::getabs).
* The returned value is in turns (at standard conditions it is endturn-startturn).
* The returned value is in time at standard conditions it is `end - start`.
*/
int get_rot_since( int startturn, int endturn, const tripoint &pos );
time_duration get_rot_since( const time_point &start, const time_point &end, const tripoint &pos );

/**
* Is it warm enough to plant seeds?
@@ -18,14 +18,14 @@ constexpr double tau = 2 * PI;

weather_generator::weather_generator() = default;

w_point weather_generator::get_weather( const tripoint &location, const calendar &t,
w_point weather_generator::get_weather( const tripoint &location, const time_point &t,
unsigned seed ) const
{
const double x( location.x /
2000.0 ); // Integer x position / widening factor of the Perlin function.
const double y( location.y /
2000.0 ); // Integer y position / widening factor of the Perlin function.
const double z( double( t.get_turn() + to_turns<int>( calendar::season_length() ) ) /
const double z( to_turn<int>( t + calendar::season_length() ) /
2000.0 ); // Integer turn / widening factor of the Perlin function.

const double dayFraction = time_past_midnight( t ) / 1_days;
@@ -42,8 +42,7 @@ w_point weather_generator::get_weather( const tripoint &location, const calendar
double A( raw_noise_4d( x, y, z, modSEED ) * 8.0 );
double W;

const double now( double( t.turn_of_year() + to_turns<int>( calendar::season_length() ) / 2 ) /
to_turns<int>( calendar::year_length() ) ); // [0,1)
const double now( time_past_new_year( t ) / calendar::year_length() ); // [0,1)

This comment has been minimized.

Copy link
@ZhilkinSerg

ZhilkinSerg Apr 11, 2018

Contributor

@BevapDin: People say weather became somewhat colder (https://discourse.cataclysmdda.org/t/15260) and this line is likely candidate.

Shouldn't it be const double now( ( time_past_new_year( t ) + calendar::season_length() / 2 ) / calendar::year_length() ), actually?

const double ctn( cos( tau * now ) );

// Temperature variation
@@ -86,12 +85,12 @@ w_point weather_generator::get_weather( const tripoint &location, const calendar
}

weather_type weather_generator::get_weather_conditions( const tripoint &location,
const calendar &t, unsigned seed ) const
const time_point &t, unsigned seed ) const
{
w_point w( get_weather( location, t, seed ) );
weather_type wt = get_weather_conditions( w );
// Make sure we don't say it's sunny at night! =P
if( wt == WEATHER_SUNNY && t.is_night() ) {
if( wt == WEATHER_SUNNY && calendar( to_turn<int>( t ) ).is_night() ) {
return WEATHER_CLEAR;
}
return wt;
@@ -4,7 +4,7 @@

struct point;
struct tripoint;
class calendar;
class time_point;
class JsonObject;
enum weather_type : int;

@@ -31,8 +31,8 @@ class weather_generator
* by the @ref map). You can use @ref map::getabs to get an absolute position from a
* relative position (relative to the map you called getabs on).
*/
w_point get_weather( const tripoint &, const calendar &, unsigned ) const;
weather_type get_weather_conditions( const tripoint &, const calendar &, unsigned seed ) const;
w_point get_weather( const tripoint &, const time_point &, unsigned ) const;
weather_type get_weather_conditions( const tripoint &, const time_point &, unsigned seed ) const;
weather_type get_weather_conditions( const w_point & ) const;
int get_water_temperature() const;
void test_weather() const;