Skip to content

Commit

Permalink
clean water goes bad if left in open container (#74353)
Browse files Browse the repository at this point in the history
* clean water goes bad if left in open container

* add string description to DECAYS_IN_AIR flag

* use item_counter instead for correct stack merging

* combine item_counter in item::combine

* increase decay rate when outdoors

* astyle

* update call sites too

* Update data/json/flags.json

Co-authored-by: Brambor <[email protected]>

---------

Co-authored-by: Maleclypse <[email protected]>
Co-authored-by: Brambor <[email protected]>
  • Loading branch information
3 people authored Jun 14, 2024
1 parent 767b21a commit fd7656a
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 26 deletions.
5 changes: 5 additions & 0 deletions data/json/flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -2479,5 +2479,10 @@
"id": "PREFIX_XS",
"type": "json_flag",
"item_prefix": "XS "
},
{
"id": "DECAYS_IN_AIR",
"type": "json_flag",
"info": "This will eventually <bad>go bad</bad> if left in the open air too long."
}
]
8 changes: 6 additions & 2 deletions data/json/items/comestibles/drink.json
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,7 @@
"color": "light_blue",
"container": "bottle_plastic",
"sealed": false,
"delete": { "flags": [ "DECAYS_IN_AIR" ] },
"contamination": [ { "disease": "highly_contaminated_food", "probability": 0.1 }, { "disease": "bad_food", "probability": 1 } ]
},
{
Expand Down Expand Up @@ -1518,12 +1519,15 @@
"phase": "liquid",
"quench": 50,
"ammo_data": { "ammo_type": "water" },
"flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE" ],
"flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE", "DECAYS_IN_AIR" ],
"name": { "str_sp": "clean water" },
"description": "Fresh, clean water. Truly the best thing to quench your thirst.",
"container": "bottle_plastic",
"sealed": true,
"color": "light_cyan"
"color": "light_cyan",
"//": "Clean water becomes regular water in about 4 weeks if exposed to air (1 week if outdoors).",
"properties": { "max_air_exposure_hours": "720" },
"revert_to": "water"
},
{
"id": "water_spring",
Expand Down
9 changes: 9 additions & 0 deletions src/active_item_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ float item_reference::spoil_multiplier()
} );
}

bool item_reference::has_watertight_container()
{
return std::any_of(
pocket_chain.begin(), pocket_chain.end(),
[]( item_pocket const * pk ) {
return pk->can_contain_liquid( false );
} );
}

bool active_item_cache::add( item &it, point location, item *parent,
std::vector<item_pocket const *> const &pocket_chain )
{
Expand Down
1 change: 1 addition & 0 deletions src/active_item_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct item_reference {
std::vector<item_pocket const *> pocket_chain;

float spoil_multiplier();
bool has_watertight_container();
};

enum class special_item_type : int {
Expand Down
1 change: 1 addition & 0 deletions src/flag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const flag_id flag_CUT_HARVEST( "CUT_HARVEST" );
const flag_id flag_CUT_IMMUNE( "CUT_IMMUNE" );
const flag_id flag_DANGEROUS( "DANGEROUS" );
const flag_id flag_DEAF( "DEAF" );
const flag_id flag_DECAYS_IN_AIR( "DECAYS_IN_AIR" );
const flag_id flag_DIAMOND( "DIAMOND" );
const flag_id flag_DIG_TOOL( "DIG_TOOL" );
const flag_id flag_DIMENSIONAL_ANCHOR( "DIMENSIONAL_ANCHOR" );
Expand Down
1 change: 1 addition & 0 deletions src/flag.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ extern const flag_id flag_CUT_HARVEST;
extern const flag_id flag_CUT_IMMUNE;
extern const flag_id flag_DANGEROUS;
extern const flag_id flag_DEAF;
extern const flag_id flag_DECAYS_IN_AIR;
extern const flag_id flag_DIAMOND;
extern const flag_id flag_DIG_TOOL;
extern const flag_id flag_DIMENSIONAL_ANCHOR;
Expand Down
53 changes: 47 additions & 6 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ item &item::convert( const itype_id &new_type, Character *carrier )
carrier->on_item_acquire( *this );
}

item_counter = 0;
update_prefix_suffix_flags();
return *this;
}
Expand Down Expand Up @@ -1413,6 +1414,10 @@ bool item::combine( const item &rhs )
set_item_specific_energy( units::from_joule_per_gram( combined_specific_energy ) );
}

if( item_counter > 0 || rhs.item_counter > 0 ) {
item_counter = ( static_cast<double>( item_counter ) * charges + static_cast<double>
( rhs.item_counter ) * rhs.charges ) / ( charges + rhs.charges );
}
}
charges += rhs.charges;
if( !rhs.has_flag( flag_NO_PARASITES ) ) {
Expand Down Expand Up @@ -2539,6 +2544,8 @@ void item::debug_info( std::vector<iteminfo> &info, const iteminfo_query *parts,
active );
info.emplace_back( "BASE", _( "burn: " ), "", iteminfo::lower_is_better,
burnt );
info.emplace_back( "BASE", _( "counter: " ), "", iteminfo::lower_is_better,
item_counter );
if( countdown_point != calendar::turn_max ) {
info.emplace_back( "BASE", _( "countdown: " ), "", iteminfo::lower_is_better,
to_seconds<int>( countdown_point - calendar::turn ) );
Expand Down Expand Up @@ -8016,6 +8023,23 @@ void item::calc_rot_while_processing( time_duration processing_duration )
last_temp_check += processing_duration;
}

bool item::process_decay_in_air( map &here, Character *carrier, const tripoint &pos,
int max_air_exposure_hours,
time_duration time_delta )
{
if( !has_own_flag( flag_FROZEN ) ) {
double environment_multiplier = here.is_outside( pos ) ? 2.0 : 1.0;
time_duration new_air_exposure = time_duration::from_seconds( item_counter ) + time_delta *
rng_normal( 0.9, 1.1 ) * environment_multiplier;
if( new_air_exposure >= time_duration::from_hours( max_air_exposure_hours ) ) {
convert( *type->revert_to, carrier );
return true;
}
item_counter = to_seconds<int>( new_air_exposure );
}
return false;
}

int item::get_env_resist( int override_base_resist ) const
{
const islot_armor *t = find_armor_data();
Expand Down Expand Up @@ -12689,7 +12713,7 @@ void item::apply_freezerburn()
}

bool item::process_temperature_rot( float insulation, const tripoint &pos, map &here,
Character *carrier, const temperature_flag flag, float spoil_modifier )
Character *carrier, const temperature_flag flag, float spoil_modifier, bool watertight_container )
{
const time_point now = calendar::turn;

Expand Down Expand Up @@ -12739,6 +12763,10 @@ bool item::process_temperature_rot( float insulation, const tripoint &pos, map &
time_point time = last_temp_check;
item_internal::scoped_goes_bad_cache _cache( this );
const bool process_rot = goes_bad() && spoil_modifier != 0;
const bool decays_in_air = !watertight_container && has_flag( flag_DECAYS_IN_AIR ) &&
type->revert_to;
int64_t max_air_exposure_hours = decays_in_air ? get_property_int64_t( "max_air_exposure_hours" ) :
0;

if( now - time > 1_hours ) {
// This code is for items that were left out of reality bubble for long time
Expand Down Expand Up @@ -12803,6 +12831,11 @@ bool item::process_temperature_rot( float insulation, const tripoint &pos, map &
}
last_temp_check = time;

if( decays_in_air &&
process_decay_in_air( here, carrier, pos, max_air_exposure_hours, time_delta ) ) {
return false;
}

// Calculate item rot
if( process_rot ) {
calc_rot( env_temperature, spoil_modifier, time_delta );
Expand All @@ -12820,6 +12853,12 @@ bool item::process_temperature_rot( float insulation, const tripoint &pos, map &
if( now - time > smallest_interval ) {
calc_temp( temp, insulation, now - time );
last_temp_check = now;

if( decays_in_air &&
process_decay_in_air( here, carrier, pos, max_air_exposure_hours, now - time ) ) {
return false;
}

if( process_rot ) {
calc_rot( temp, spoil_modifier, now - time );
return has_rotten_away() && carrier == nullptr;
Expand Down Expand Up @@ -14075,14 +14114,15 @@ bool item::process_gun_cooling( Character *carrier )
}

bool item::process( map &here, Character *carrier, const tripoint &pos, float insulation,
temperature_flag flag, float spoil_multiplier_parent, bool recursive )
temperature_flag flag, float spoil_multiplier_parent, bool watertight_container, bool recursive )
{
process_relic( carrier, pos );
if( recursive ) {
contents.process( here, carrier, pos, type->insulation_factor * insulation, flag,
spoil_multiplier_parent );
spoil_multiplier_parent, watertight_container );
}
return process_internal( here, carrier, pos, insulation, flag, spoil_multiplier_parent );
return process_internal( here, carrier, pos, insulation, flag, spoil_multiplier_parent,
watertight_container );
}

bool item::leak( map &here, Character *carrier, const tripoint &pos, item_pocket *pocke )
Expand All @@ -14109,7 +14149,7 @@ void item::set_last_temp_check( const time_point &pt )
}

bool item::process_internal( map &here, Character *carrier, const tripoint &pos,
float insulation, const temperature_flag flag, float spoil_modifier )
float insulation, const temperature_flag flag, float spoil_modifier, bool watertight_container )
{
if( ethereal ) {
if( !has_var( "ethereal" ) ) {
Expand Down Expand Up @@ -14228,7 +14268,8 @@ bool item::process_internal( map &here, Character *carrier, const tripoint &pos,
}
// All foods that go bad have temperature
if( has_temperature() &&
process_temperature_rot( insulation, pos, here, carrier, flag, spoil_modifier ) ) {
process_temperature_rot( insulation, pos, here, carrier, flag, spoil_modifier,
watertight_container ) ) {
if( is_comestible() ) {
here.rotten_item_spawn( *this, pos );
}
Expand Down
13 changes: 9 additions & 4 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,8 @@ class item : public visitable
* @return true if the item is fully rotten and is ready to be removed
*/
bool process_temperature_rot( float insulation, const tripoint &pos, map &here, Character *carrier,
temperature_flag flag = temperature_flag::NORMAL, float spoil_modifier = 1.0f );
temperature_flag flag = temperature_flag::NORMAL, float spoil_modifier = 1.0f,
bool watertight_container = false );

/** Set the item to HOT and resets last_temp_check */
void heat_up();
Expand Down Expand Up @@ -1448,7 +1449,7 @@ class item : public visitable
*/
bool process( map &here, Character *carrier, const tripoint &pos, float insulation = 1,
temperature_flag flag = temperature_flag::NORMAL, float spoil_multiplier_parent = 1.0f,
bool recursive = true );
bool watertight_container = false, bool recursive = true );

bool leak( map &here, Character *carrier, const tripoint &pos, item_pocket *pocke = nullptr );

Expand Down Expand Up @@ -3006,8 +3007,8 @@ class item : public visitable
const use_function *get_use_internal( const std::string &use_name ) const;
template<typename Item>
static Item *get_usable_item_helper( Item &self, const std::string &use_name );
bool process_internal( map &here, Character *carrier, const tripoint &pos, float insulation = 1,
temperature_flag flag = temperature_flag::NORMAL, float spoil_modifier = 1.0f );
bool process_internal( map &here, Character *carrier, const tripoint &pos, float insulation,
temperature_flag flag, float spoil_modifier, bool watertight_container );
void iterate_covered_body_parts_internal( side s,
const std::function<void( const bodypart_str_id & )> &cb ) const;
void iterate_covered_sub_body_parts_internal( side s,
Expand Down Expand Up @@ -3073,6 +3074,10 @@ class item : public visitable
bool process_blackpowder_fouling( Character *carrier );
bool process_gun_cooling( Character *carrier );
bool process_tool( Character *carrier, const tripoint &pos );
bool process_decay_in_air( map &here, Character *carrier, const tripoint &pos,
int max_air_exposure_hours,
time_duration time_delta );


public:
static const int INFINITE_CHARGES;
Expand Down
5 changes: 3 additions & 2 deletions src/item_contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2494,11 +2494,12 @@ void item_contents::remove_internal( const std::function<bool( item & )> &filter
}

void item_contents::process( map &here, Character *carrier, const tripoint &pos, float insulation,
temperature_flag flag, float spoil_multiplier_parent )
temperature_flag flag, float spoil_multiplier_parent, bool watertight_container )
{
for( item_pocket &pocket : contents ) {
if( pocket.is_type( pocket_type::CONTAINER ) ) {
pocket.process( here, carrier, pos, insulation, flag, spoil_multiplier_parent );
pocket.process( here, carrier, pos, insulation, flag, spoil_multiplier_parent,
watertight_container );
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/item_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ class item_contents
* NOTE: this destroys the items that get processed
*/
void process( map &here, Character *carrier, const tripoint &pos, float insulation = 1,
temperature_flag flag = temperature_flag::NORMAL, float spoil_multiplier_parent = 1.0f );
temperature_flag flag = temperature_flag::NORMAL, float spoil_multiplier_parent = 1.0f,
bool watertight_container = false );

void leak( map &here, Character *carrier, const tripoint &pos, item_pocket *pocke = nullptr );

Expand Down
9 changes: 5 additions & 4 deletions src/item_pocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ bool item_pocket::detonate( const tripoint &pos, std::vector<item> &drops )
}

bool item_pocket::process( const itype &type, map &here, Character *carrier, const tripoint &pos,
float insulation, const temperature_flag flag )
float insulation, temperature_flag flag, bool watertight_container )
{
bool processed = false;
float spoil_multiplier = 1.0f;
Expand All @@ -908,7 +908,7 @@ bool item_pocket::process( const itype &type, map &here, Character *carrier, con
spoil_multiplier = 0.0f;
}
if( it->process( here, carrier, pos, type.insulation_factor * insulation, flag,
spoil_multiplier ) ) {
spoil_multiplier, watertight_container ) ) {
it->spill_contents( pos );
it = contents.erase( it );
processed = true;
Expand Down Expand Up @@ -1983,12 +1983,13 @@ void item_pocket::remove_items_if( const std::function<bool( item & )> &filter )
}

void item_pocket::process( map &here, Character *carrier, const tripoint &pos, float insulation,
temperature_flag flag, float spoil_multiplier_parent )
temperature_flag flag, float spoil_multiplier_parent, bool watertight_container )
{
for( auto iter = contents.begin(); iter != contents.end(); ) {
if( iter->process( here, carrier, pos, insulation, flag,
// spoil multipliers on pockets are not additive or multiplicative, they choose the best
std::min( spoil_multiplier_parent, spoil_multiplier() ) ) ) {
std::min( spoil_multiplier_parent, spoil_multiplier() ),
watertight_container || can_contain_liquid( false ) ) ) {
iter->spill_contents( pos );
iter = contents.erase( iter );
} else {
Expand Down
5 changes: 3 additions & 2 deletions src/item_pocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ class item_pocket
std::string translated_sealed_prefix() const;
bool detonate( const tripoint &p, std::vector<item> &drops );
bool process( const itype &type, map &here, Character *carrier, const tripoint &pos,
float insulation, temperature_flag flag );
float insulation, temperature_flag flag, bool watertight_container );
void remove_all_ammo( Character &guy );
void remove_all_mods( Character &guy );

Expand All @@ -304,7 +304,8 @@ class item_pocket
* NOTE: this destroys the items that get processed
*/
void process( map &here, Character *carrier, const tripoint &pos, float insulation = 1,
temperature_flag flag = temperature_flag::NORMAL, float spoil_multiplier_parent = 1.0f );
temperature_flag flag = temperature_flag::NORMAL, float spoil_multiplier_parent = 1.0f,
bool watertight_container = false );

void leak( map &here, Character *carrier, const tripoint &pos, item_pocket *pocke = nullptr );

Expand Down
12 changes: 7 additions & 5 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5561,10 +5561,11 @@ void map::update_lum( item_location &loc, bool add )
}

static bool process_map_items( map &here, item_stack &items, safe_reference<item> &item_ref,
item *parent, const tripoint &location, const float insulation,
const temperature_flag flag, const float spoil_multiplier )
item *parent, const tripoint &location, float insulation,
temperature_flag flag, float spoil_multiplier, bool watertight_container )
{
if( item_ref->process( here, nullptr, location, insulation, flag, spoil_multiplier, false ) ) {
if( item_ref->process( here, nullptr, location, insulation, flag, spoil_multiplier,
watertight_container, false ) ) {
// Item is to be destroyed so erase it from the map stack
// unless it was already destroyed by processing.
if( item_ref ) {
Expand Down Expand Up @@ -5760,7 +5761,8 @@ void map::process_items_in_submap( submap &current_submap, const tripoint &gridp

process_map_items( *this, items, active_item_ref.item_ref, active_item_ref.parent,

Check failure on line 5762 in src/map.cpp

View workflow job for this annotation

GitHub Actions / build (src)

Unsequenced calls to member functions of 'active_item_ref' (of type 'item_reference'), at least one of which is non-const. [cata-unsequenced-calls,-warnings-as-errors]
map_location, 1, flag,
spoil_multiplier * active_item_ref.spoil_multiplier() );
spoil_multiplier * active_item_ref.spoil_multiplier(),
active_item_ref.has_watertight_container() );
}
}

Expand Down Expand Up @@ -5841,7 +5843,7 @@ void map::process_items_in_vehicle( vehicle &cur_veh, submap &current_submap )
}
if( !process_map_items( *this, items, active_item_ref.item_ref, active_item_ref.parent,

Check failure on line 5844 in src/map.cpp

View workflow job for this annotation

GitHub Actions / build (src)

Unsequenced calls to member functions of 'active_item_ref' (of type 'item_reference'), at least one of which is non-const. [cata-unsequenced-calls,-warnings-as-errors]
item_loc, it_insulation, flag,
active_item_ref.spoil_multiplier() ) ) {
active_item_ref.spoil_multiplier(), active_item_ref.has_watertight_container() ) ) {
// If the item was NOT destroyed, we can skip the remainder,
// which handles fallout from the vehicle being damaged.
continue;
Expand Down

0 comments on commit fd7656a

Please sign in to comment.