Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

water rework part 1 #72252

Merged
merged 14 commits into from
Jun 4, 2024
4 changes: 2 additions & 2 deletions data/json/furniture_and_terrain/terrain-liquids.json
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@
"symbol": "~",
"color": "light_blue",
"move_cost": 3,
"flags": [ "TRANSPARENT", "LIQUIDCONT", "SPAWN_WITH_LIQUID", "FRESH_WATER", "MURKY" ],
"flags": [ "TRANSPARENT", "LIQUIDCONT", "SPAWN_WITH_WATER", "MURKY" ],
"examine_action": "finite_water_source"
},
{
Expand All @@ -491,7 +491,7 @@
"color": "light_blue",
"move_cost": 3,
"connect_groups": "INDOORFLOOR",
"flags": [ "TRANSPARENT", "LIQUIDCONT", "SPAWN_WITH_LIQUID", "FRESH_WATER", "MURKY", "INDOORS" ],
"flags": [ "TRANSPARENT", "LIQUIDCONT", "SPAWN_WITH_WATER", "MURKY", "INDOORS" ],
"examine_action": "finite_water_source"
},
{
Expand Down
53 changes: 33 additions & 20 deletions data/json/items/comestibles/drink.json
Original file line number Diff line number Diff line change
Expand Up @@ -1485,49 +1485,62 @@
{
"id": "water",
"type": "COMESTIBLE",
"comestible_type": "DRINK",
"category": "food",
"copy-from": "water_clean",
"name": { "str_sp": "water" },
"description": "Water, the stuff of life, necessary for all kinds of crafting, cleaning, and avoiding an agonizing death by dehydration. You should boil or purify it before using it as drinking water.",
"color": "light_blue",
"container": "bottle_plastic",
"sealed": false,
"contamination": [ { "disease": "highly_contaminated_food", "probability": 0.1 }, { "disease": "bad_food", "probability": 1 } ]
},
{
"id": "water_murky",
"type": "COMESTIBLE",
"copy-from": "water",
"name": { "str_sp": "murky water" },
"description": "Water, the stuff of life. This water looks like it has quite a bit of life in it in fact, and not the good kind. You should filter and sterilize it if you want to drink it.",
"quench": 30,
"parasites": 5,
"contamination": [ { "disease": "highly_contaminated_food", "probability": 100 } ]
},
{
"id": "water_clean",
"type": "COMESTIBLE",
"comestible_type": "DRINK",
"category": "food",
"material": [ "water" ],
"weight": "250 g",
"volume": "250 ml",
"charges": 1,
"price": "50 cent",
"price_postapoc": "1 cent",
"symbol": "~",
"color": "light_blue",
"phase": "liquid",
"container": "bottle_plastic",
"sealed": false,
"quench": 50,
"ammo_data": { "ammo_type": "water" },
"flags": [ "EATEN_COLD" ],
"use_action": [ "BLECH_BECAUSE_UNCLEAN" ]
},
{
"id": "water_clean",
"copy-from": "water",
"type": "COMESTIBLE",
"flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE" ],
"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",
"extend": { "flags": [ "NUTRIENT_OVERRIDE" ] },
"use_action": [ ]
"color": "light_cyan"
},
{
"id": "water_spring",
"copy-from": "water_clean",
"type": "COMESTIBLE",
"name": { "str_sp": "spring water" },
"description": "Natural alpine spring water, bottled at the source.",
"relative": { "fun": 1 }
},
{
"id": "water_mineral",
"copy-from": "water",
"copy-from": "water_clean",
"type": "COMESTIBLE",
"name": { "str_sp": "mineral water" },
"description": "Fancy mineral water, so fancy it makes you feel fancy just holding it.",
"container": "bottle_plastic",
"sealed": true,
"proportional": { "quench": 1.2 },
"relative": { "fun": 1 },
"use_action": [ ]
"relative": { "fun": 1 }
},
{
"type": "COMESTIBLE",
Expand Down
16 changes: 16 additions & 0 deletions data/json/items/tool/cooking.json
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,22 @@
],
"melee_damage": { "bash": 2 }
},
{
"id": "water_filter_sand",
"type": "TOOL",
"name": { "str": "slow-sand water filter" },
"//": "Slow sand biofilter: https://wwwnc.cdc.gov/travel/yellowbook/2024/preparing/water-disinfection#:~:text=Figure%202%2D02.%20Emergency%20gravel%20and%20sand%20filter",
"description": "An improvised water filter made from a plastic bucket filled with a layer of sand and a layer of gravel. It can be used to remove particulates and some pathogens from water, but it is not 100% effective.",
"//2": "Density of sand is around 1.6g/cm^3. Gravel is similar. pi * 5in * 5in * (10 in + 4 in) * 1.6 g/cm^3 ~= 29 kg. Add 1 kg for the bucket itself.",
"weight": "14800 g",
nornagon marked this conversation as resolved.
Show resolved Hide resolved
"volume": "21000 ml",
"price": 1000,
"price_postapoc": 150,
"to_hit": -3,
"material": [ "plastic", "sand" ],
"symbol": ";",
"color": "light_blue"
},
{
"id": "propane_cooker",
"type": "TOOL",
Expand Down
28 changes: 27 additions & 1 deletion data/json/recipes/recipe_food.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"category": "CC_FOOD",
"id_suffix": "using_water_purifier",
"subcategory": "CSC_FOOD_DRINKS",
"skill_used": "cooking",
"skill_used": "survival",
"time": "9 s",
"//": "https://www.netsolwater.com/how-much-energy-does-an-ro-system-use.php?blog=484",
"//1": "Unclear if this is a RO filter but basing it off that. 0.03 KwH per 2 liters in a RO filter = 14kJ for 1u of water",
Expand All @@ -85,6 +85,32 @@
"tools": [ [ [ "water_purifier", 14 ], [ "pur_tablets", 1 ], [ "char_purifier", 12 ], [ "char_purifier_clay", 12 ] ] ],
"components": [ [ [ "water", 1 ] ] ]
},
{
"type": "recipe",
"activity_level": "NO_EXERCISE",
"result": "water",
"category": "CC_FOOD",
"id_suffix": "using_water_purifier",
"subcategory": "CSC_FOOD_DRINKS",
"skill_used": "survival",
"time": "1 m",
"autolearn": true,
"components": [ [ [ "water_murky", 1 ] ], [ [ "pur_tablets", 1 ] ] ]
},
{
"type": "recipe",
"activity_level": "NO_EXERCISE",
"result": "water",
"charges": 10,
"category": "CC_FOOD",
"id_suffix": "using_water_filter_sand",
"subcategory": "CSC_FOOD_DRINKS",
"skill_used": "survival",
"time": "15 m",
"autolearn": true,
"tools": [ [ "water_filter_sand" ] ],
nornagon marked this conversation as resolved.
Show resolved Hide resolved
"components": [ [ [ "water_murky", 10 ] ] ]
},
{
"type": "recipe",
"activity_level": "NO_EXERCISE",
Expand Down
19 changes: 19 additions & 0 deletions data/json/recipes/tools/tools_primitive.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,5 +496,24 @@
],
"using": [ [ "blacksmithing_standard", 13 ], [ "steel_standard", 1 ] ],
"components": [ [ [ "2x4", 2 ] ] ]
},
{
"type": "recipe",
"result": "water_filter_sand",
"activity_level": "MODERATE_EXERCISE",
"category": "CC_OTHER",
"subcategory": "CSC_OTHER_TOOLS",
"skill_used": "survival",
"skills_required": [ [ "survival", 2 ] ],
"difficulty": 2,
"time": "30 m",
"autolearn": true,
"reversible": true,
"components": [
[ [ "bucket_5gal", 1 ] ],
[ [ "material_sand", 1250 ] ],
[ [ "material_gravel", 500 ] ],
[ [ "cotton_patchwork", 4 ], [ "wire_mesh", 4 ] ]
]
}
]
4 changes: 2 additions & 2 deletions src/consumption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1208,8 +1208,8 @@ static bool eat( item &food, Character &you, bool force )
}
}

for( const std::pair<const diseasetype_id, int> &elem : food.get_comestible()->contamination ) {
if( rng( 1, 100 ) <= elem.second ) {
for( const std::pair<const diseasetype_id, float> &elem : food.get_comestible()->contamination ) {
if( rng_float( 0, 100 ) < elem.second ) {
you.expose_to_disease( elem.first );
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ void Item_factory::finalize_post( itype &obj )
}

if( obj.comestible ) {
for( const std::pair<const diseasetype_id, int> &elem : obj.comestible->contamination ) {
for( const std::pair<const diseasetype_id, float> &elem : obj.comestible->contamination ) {
const diseasetype_id dtype = elem.first;
if( !dtype.is_valid() ) {
debugmsg( "contamination in %s contains invalid diseasetype_id %s.",
Expand Down Expand Up @@ -3308,7 +3308,7 @@ void Item_factory::load( islot_comestible &slot, const JsonObject &jo, const std

for( const JsonObject jsobj : jo.get_array( "contamination" ) ) {
slot.contamination.emplace( diseasetype_id( jsobj.get_string( "disease" ) ),
jsobj.get_int( "probability" ) );
jsobj.get_float( "probability" ) );
}

if( jo.has_member( "primary_material" ) ) {
Expand Down
2 changes: 1 addition & 1 deletion src/itype.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ struct islot_comestible {
std::vector<effect_on_condition_id> consumption_eocs;

/**List of diseases carried by this comestible and their associated probability*/
std::map<diseasetype_id, int> contamination;
std::map<diseasetype_id, float> contamination;

// Materials to generate the below
std::map<material_id, int> materials;
Expand Down
78 changes: 15 additions & 63 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ static const ammotype ammo_battery( "battery" );

static const damage_type_id damage_bash( "bash" );

static const diseasetype_id disease_bad_food( "bad_food" );

static const efftype_id effect_boomered( "boomered" );
static const efftype_id effect_crushed( "crushed" );
static const efftype_id effect_fake_common_cold( "fake_common_cold" );
Expand All @@ -141,6 +139,8 @@ static const item_group_id Item_spawn_data_default_zombie_items( "default_zombie

static const itype_id itype_battery( "battery" );
static const itype_id itype_nail( "nail" );
static const itype_id itype_water( "water" );
static const itype_id itype_water_murky( "water_murky" );

static const material_id material_glass( "glass" );

Expand Down Expand Up @@ -2301,18 +2301,13 @@ bool map::ter_set( const tripoint &p, const ter_id &new_terrain, bool avoid_crea
set_seen_cache_dirty( p );
}

if( new_t.has_flag( "SPAWN_WITH_LIQUID" ) ) {
if( new_t.has_flag( "FRESH_WATER" ) ) {
item water( "water", calendar::start_of_cataclysm );
// TODO: Move all numeric values to json
water.charges = rng( 40, 240 );
if( new_t.has_flag( ter_furn_flag::TFLAG_MURKY ) ) {
water.poison = rng( 1, 6 );
water.get_comestible()->parasites = 5;
water.get_comestible()->contamination = { { disease_bad_food, 5 } };
}
add_item( p, water );
}
if( new_t.has_flag( "SPAWN_WITH_WATER" ) ) {
itype_id water_type = new_t.has_flag( ter_furn_flag::TFLAG_MURKY ) ? itype_water_murky :
itype_water;
item water( water_type, calendar::start_of_cataclysm );
// TODO: Move all numeric values to json
water.charges = rng( 40, 240 );
add_item( p, water );
}

invalidate_max_populated_zlev( p.z );
Expand Down Expand Up @@ -5492,64 +5487,21 @@ item map::water_from( const tripoint &p )
return ret;
}

if( has_flag( ter_furn_flag::TFLAG_MURKY, p ) ) {
for( item &ret : get_map().i_at( p ) ) {
if( ret.made_of( phase_id::LIQUID ) ) {
ret.set_item_temperature( std::max( weather.get_temperature( p ),
temperatures::cold ) );
ret.poison = rng( 1, 6 );
ret.get_comestible()->parasites = 5;
ret.get_comestible()->contamination = { { disease_bad_food, 5 } };
return ret;
}
}
}

if( has_flag( ter_furn_flag::TFLAG_TOILET_WATER, p ) ) {
for( item &ret : get_map().i_at( p ) ) {
if( ret.made_of( phase_id::LIQUID ) ) {
ret.set_item_temperature( std::max( weather.get_temperature( p ),
temperatures::cold ) );
ret.poison = one_in( 3 ) ? 0 : rng( 1, 3 );
return ret;
}
}
}

const ter_id terrain_id = ter( p );
if( terrain_id == ter_t_sewage ) {
item ret( "water_sewage", calendar::turn, item::INFINITE_CHARGES );
ret.set_item_temperature( std::max( weather.get_temperature( p ),
temperatures::cold ) );
ret.poison = rng( 1, 7 );
return ret;
}

item ret( "water", calendar::turn, item::INFINITE_CHARGES );
ret.set_item_temperature( std::max( weather.get_temperature( p ),
temperatures::cold ) );
// iexamine::water_source requires a valid liquid from this function.
if( terrain_id->has_examine( iexamine::water_source ) ) {
int poison_chance = 0;
if( terrain_id.obj().has_flag( ter_furn_flag::TFLAG_DEEP_WATER ) ) {
if( terrain_id.obj().has_flag( ter_furn_flag::TFLAG_CURRENT ) ) {
poison_chance = 20;
} else {
poison_chance = 4;
}
} else {
if( terrain_id.obj().has_flag( ter_furn_flag::TFLAG_CURRENT ) ) {
poison_chance = 10;
} else {
poison_chance = 3;
}
}
if( one_in( poison_chance ) ) {
ret.poison = rng( 1, 4 );
}
return ret;
}
if( furn( p )->has_examine( iexamine::water_source ) ) {
if( terrain_id->has_examine( iexamine::water_source ) ||
furn( p )->has_examine( iexamine::water_source ) ) {
itype_id liquid_id = has_flag( ter_furn_flag::TFLAG_MURKY, p ) ? itype_water_murky : itype_water;
item ret( liquid_id, calendar::turn, item::INFINITE_CHARGES );
ret.set_item_temperature( std::max( weather.get_temperature( p ),
temperatures::cold ) );
return ret;
}
return item();
Expand Down
7 changes: 1 addition & 6 deletions src/weather.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,15 +246,10 @@ void item::add_rain_to_container( int charges )
ret.charges = std::min( charges, capa );
if( contents.can_contain( ret ).success() ) {
// This is easy. Just add 1 charge of the rain liquid to the container.
// Funnels aren't always clean enough for water. // TODO: disinfectant squeegie->funnel
ret.poison = one_in( 10 ) ? 1 : 0;
put_in( ret, pocket_type::CONTAINER );
} else {
static const std::set<itype_id> allowed_liquid_types{
itype_water
};
item *found_liq = contents.get_item_with( [&]( const item & liquid ) {
return allowed_liquid_types.count( liquid.typeId() );
return liquid.typeId() == itype_water;
} );
if( found_liq == nullptr ) {
debugmsg( "Rainwater failed to add to container" );
Expand Down
Loading