diff --git a/data/json/item_actions.json b/data/json/item_actions.json index fbfddf39ce881..2b2629db53e14 100644 --- a/data/json/item_actions.json +++ b/data/json/item_actions.json @@ -854,6 +854,11 @@ "id": "WATER_PURIFIER", "name": { "str": "Purify some water" } }, + { + "type": "item_action", + "id": "WATER_TABLETS", + "name": { "str": "Purify some water" } + }, { "type": "item_action", "id": "WEATHER_TOOL", diff --git a/data/json/itemgroups/Locations_MapExtras/airdrop.json b/data/json/itemgroups/Locations_MapExtras/airdrop.json index e86893040b89c..a3d63e42e2a18 100644 --- a/data/json/itemgroups/Locations_MapExtras/airdrop.json +++ b/data/json/itemgroups/Locations_MapExtras/airdrop.json @@ -43,7 +43,7 @@ "type": "item_group", "subtype": "collection", "container-item": "box_small", - "entries": [ { "count": 8, "group": "pur_tablets_bottle_plastic_small_15" } ] + "entries": [ { "count": 8, "group": "pur_tablets_bottle_plastic_small_50" } ] }, { "id": "twoliter_water_batch", diff --git a/data/json/itemgroups/Locations_MapExtras/locations.json b/data/json/itemgroups/Locations_MapExtras/locations.json index 9b75f18ab2af7..79c825170ab48 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations.json +++ b/data/json/itemgroups/Locations_MapExtras/locations.json @@ -2154,7 +2154,7 @@ { "item": "apron_plastic", "prob": 25 }, { "item": "apron_cotton", "prob": 8 }, { "item": "apron_leather", "prob": 2 }, - { "prob": 15, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 15, "group": "pur_tablets_bottle_plastic_small_1_50" }, { "item": "lye_powder", "prob": 10, "charges": [ 1, 200 ] }, { "item": "oxy_powder", "prob": 12, "charges": [ 1, 200 ] }, { "item": "chemistry_set", "prob": 8 }, @@ -2576,7 +2576,7 @@ [ "flaregun", 5 ], [ "signal_flare", 10 ], [ "survivormap", 1 ], - { "prob": 5, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 5, "group": "pur_tablets_bottle_plastic_small_1_50" }, [ "razor_blade", 5 ], [ "sheath", 5 ], [ "bootsheath", 5 ], diff --git a/data/json/itemgroups/Locations_MapExtras/locations_commercial.json b/data/json/itemgroups/Locations_MapExtras/locations_commercial.json index 033a09b0458f2..227046ee1cb93 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations_commercial.json +++ b/data/json/itemgroups/Locations_MapExtras/locations_commercial.json @@ -752,7 +752,7 @@ [ "jacket_army", 40 ], [ "helmet_army_outdated", 10 ], [ "helmet_soviet_tanker", 1 ], - { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_50" }, [ "knife_trench", 14 ], [ "flaregun", 20 ], [ "signal_flare", 25 ], diff --git a/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json b/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json index 2847b4445b7c4..c0fabfd4e23f2 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json +++ b/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json @@ -146,7 +146,7 @@ [ "popcan_stove", 10 ], [ "denat_alcohol", 6 ], [ "methed_alcohol", 4 ], - { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_50" }, [ "water_clean", 50 ], [ "granola", 60 ], [ "fruit_leather", 50 ], diff --git a/data/json/itemgroups/activities_hobbies.json b/data/json/itemgroups/activities_hobbies.json index 318d68dfe5f79..9366235271fc1 100644 --- a/data/json/itemgroups/activities_hobbies.json +++ b/data/json/itemgroups/activities_hobbies.json @@ -410,7 +410,7 @@ [ "pocketwatch", 5 ], [ "boots_hiking", 20 ], [ "runner_bag", 15 ], - { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_50" }, [ "e_tool", 10 ], [ "knife_trench", 7 ], [ "flaregun", 20 ], @@ -548,7 +548,7 @@ [ "pocketwatch", 5 ], [ "boots_hiking", 20 ], [ "runner_bag", 15 ], - { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_50" }, [ "e_tool", 10 ], [ "knife_trench", 7 ], [ "flaregun", 20 ], @@ -749,10 +749,10 @@ }, { "type": "item_group", - "id": "pur_tablets_bottle_plastic_small_1_15", + "id": "pur_tablets_bottle_plastic_small_1_50", "subtype": "collection", "//": "This group was created automatically and may contain errors.", "container-item": "bottle_plastic_small", - "entries": [ { "item": "pur_tablets", "count": [ 1, 15 ] } ] + "entries": [ { "item": "pur_tablets", "count": [ 1, 50 ] } ] } ] diff --git a/data/json/itemgroups/collections_domestic.json b/data/json/itemgroups/collections_domestic.json index 53914dcb7441d..5a6f29d5e14b1 100644 --- a/data/json/itemgroups/collections_domestic.json +++ b/data/json/itemgroups/collections_domestic.json @@ -220,7 +220,7 @@ { "item": "apron_plastic", "prob": 15 }, { "item": "apron_cotton", "prob": 3 }, { "item": "apron_leather", "prob": 3 }, - { "prob": 15, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 15, "group": "pur_tablets_bottle_plastic_small_1_50" }, { "item": "lye_powder", "prob": 20, "charges": [ 100, -1 ] }, { "item": "oxy_powder", "prob": 24, "charges": [ 100, -1 ] }, { "item": "chemistry_set", "prob": 10 }, diff --git a/data/json/itemgroups/military.json b/data/json/itemgroups/military.json index 2645114bab1b0..2151799f5d9bb 100644 --- a/data/json/itemgroups/military.json +++ b/data/json/itemgroups/military.json @@ -843,7 +843,7 @@ { "prob": 25, "group": "antibiotics_bottle_plastic_pill_prescription_1_15" }, { "item": "purifier", "prob": 12 }, { "item": "heatpack", "prob": 60 }, - { "prob": 20, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 20, "group": "pur_tablets_bottle_plastic_small_1_50" }, { "prob": 5, "group": "fungicide_bag_plastic_400" }, { "group": "insecticide_bag_plastic", "prob": 5 }, { "item": "rx12_injector", "prob": 8, "charges": [ 0, 2 ] }, diff --git a/data/json/itemgroups/science_and_tech.json b/data/json/itemgroups/science_and_tech.json index 070cd6c3a2d2f..b0b63c04766e6 100644 --- a/data/json/itemgroups/science_and_tech.json +++ b/data/json/itemgroups/science_and_tech.json @@ -93,7 +93,7 @@ { "item": "oxy_powder", "prob": 8, "charges": [ 100, 200 ] }, [ "fungicide", 10 ], { "group": "insecticide_bag_plastic", "prob": 10 }, - { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_50" }, [ "optical_cloak", 1 ], [ "holo_cloak", 1 ], [ "survnote", 1 ], @@ -182,7 +182,7 @@ [ "apron_plastic", 20 ], [ "apron_cotton", 2 ], [ "apron_leather", 2 ], - { "prob": 5, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 5, "group": "pur_tablets_bottle_plastic_small_1_50" }, { "item": "lye_powder", "prob": 10, "charges": [ 100, 200 ] }, { "item": "oxy_powder", "prob": 12, "charges": [ 100, 200 ] }, { "item": "magnesium", "prob": 12, "charges": [ 50, 100 ] }, diff --git a/data/json/itemgroups/tools.json b/data/json/itemgroups/tools.json index 1d3752168d57b..1e549cc938b42 100644 --- a/data/json/itemgroups/tools.json +++ b/data/json/itemgroups/tools.json @@ -232,7 +232,7 @@ { "item": "mess_kit", "prob": 20 }, [ "multitool", 10 ], [ "pockknife", 30 ], - { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_15" }, + { "prob": 10, "group": "pur_tablets_bottle_plastic_small_1_50" }, { "item": "ref_lighter", "prob": 20, "charges": [ 0, 50 ] }, [ "rope_30", 20 ], [ "flint_steel", 10 ], @@ -443,7 +443,7 @@ { "item": "whistle_multitool" }, { "item": "glowstick", "charges": 1400 }, { "item": "handflare", "count": 4, "charges": 300 }, - { "group": "pur_tablets_bottle_plastic_small_15" }, + { "group": "pur_tablets_bottle_plastic_small_50" }, { "item": "pocket_survival" } ] }, @@ -458,7 +458,7 @@ { "item": "whistle_multitool", "prob": 70 }, { "item": "glowstick", "charges": 1400, "prob": 60 }, { "item": "handflare", "count": [ 0, 4 ], "charges": 300 }, - { "group": "pur_tablets_bottle_plastic_small_0_15" }, + { "group": "pur_tablets_bottle_plastic_small_0_50" }, { "item": "pocket_survival", "prob": 40 } ] }, @@ -566,7 +566,7 @@ { "item": "bandages", "count": 9 }, { "item": "medical_gauze", "count": 3 }, { "item": "adhesive_bandages", "count": 3 }, - { "group": "pur_tablets_bottle_plastic_small_0_3" }, + { "group": "pur_tablets_bottle_plastic_small_0_9" }, { "item": "gloves_medical" }, { "item": "scissors_medical" } ] @@ -583,7 +583,7 @@ { "item": "bandages", "count": [ 0, 9 ] }, { "item": "medical_gauze", "count": [ 0, 3 ] }, { "item": "adhesive_bandages", "count": [ 0, 3 ] }, - { "group": "pur_tablets_bottle_plastic_small_0_3" }, + { "group": "pur_tablets_bottle_plastic_small_0_9" }, { "item": "gloves_medical", "prob": 50 }, { "item": "scissors_medical", "prob": 30 } ] @@ -967,19 +967,19 @@ }, { "type": "item_group", - "id": "pur_tablets_bottle_plastic_small_15", + "id": "pur_tablets_bottle_plastic_small_50", "subtype": "collection", "//": "This group was created automatically and may contain errors.", "container-item": "bottle_plastic_small", - "entries": [ { "item": "pur_tablets", "count": 15 } ] + "entries": [ { "item": "pur_tablets", "count": 50 } ] }, { "type": "item_group", - "id": "pur_tablets_bottle_plastic_small_0_15", + "id": "pur_tablets_bottle_plastic_small_0_50", "subtype": "collection", "//": "This group was created automatically and may contain errors.", "container-item": "bottle_plastic_small", - "entries": [ { "item": "pur_tablets", "count": [ 0, 15 ] } ] + "entries": [ { "item": "pur_tablets", "count": [ 0, 50 ] } ] }, { "type": "item_group", @@ -1015,11 +1015,11 @@ }, { "type": "item_group", - "id": "pur_tablets_bottle_plastic_small_0_3", + "id": "pur_tablets_bottle_plastic_small_0_9", "subtype": "collection", "//": "This group was created automatically and may contain errors.", "container-item": "bottle_plastic_small", - "entries": [ { "item": "pur_tablets", "count": [ 0, 3 ] } ] + "entries": [ { "item": "pur_tablets", "count": [ 0, 9 ] } ] }, { "type": "item_group", diff --git a/data/json/items/chemicals_and_resources.json b/data/json/items/chemicals_and_resources.json index 5857ac749abc0..24110d94ec1ac 100644 --- a/data/json/items/chemicals_and_resources.json +++ b/data/json/items/chemicals_and_resources.json @@ -1179,14 +1179,21 @@ "material": [ "powder" ], "weight": "1 g", "color": "white", - "use_action": [ "WATER_PURIFIER" ], + "use_action": [ "WATER_TABLETS" ], "comestible_type": "MED", "symbol": "!", - "description": "Intended for the clarification and disinfection of unsafe drinking water, this halazone-based purification tablet removes dangerous contaminants using powerful chemicals. The label says to use one tablet per unit of water (250 mL).", + "//": "Modeled after camping-scale aquatabs (https://cdn.shopify.com/s/files/1/0371/7506/6755/files/2020-49mg_insert.pdf?v=1592588294). Usage procedure is: clarify and decant water, add tablets, mix for 10 minutes, wait for 30.", + "//2": "The recommended usage is 1 tablet per L, going to 2 tablets if cloudy/stained/cold", + "//3": "The same company manufactures larger tablets for larger volumes, but the amount of active ingredient per litre of water is roughly equivalent and the procedure is similar", + "//4": "The infrastructure does not currently exist for modelling whether water is cloudy.", + "//5": "Based on this: usage is modeled in-game as: 1. Spend 5m/1L with significant batch savings to decant, 2. Add tablets 3. Assume mixing can be arranged, 4. wait 40 minutes for clean water.", + "description": "Intended for the clarification and disinfection of unsafe drinking water while camping or travelling, this sodium dichloroisocyanurate-based purification tablet removes dangerous contaminants using powerful chemicals. The label says to use one tablet per liter (4 units of water).", "price": "9 cent", "price_postapoc": "20 cent", "volume": "2 ml", - "flags": [ "NO_INGEST", "WATER_DISSOLVE" ] + "flags": [ "NO_INGEST", "WATER_DISSOLVE" ], + "//6": "The ratio of how much water can be purified per tablet (maximum). Should match the water_purifying recipe", + "variables": { "water_per_tablet": "4" } }, { "type": "GENERIC", diff --git a/data/json/items/comestibles/drink.json b/data/json/items/comestibles/drink.json index a88fbf9b8ec31..2a83d1d9f689f 100644 --- a/data/json/items/comestibles/drink.json +++ b/data/json/items/comestibles/drink.json @@ -1872,5 +1872,35 @@ "phase": "liquid", "flags": [ "EATEN_COLD" ], "fun": 3 + }, + { + "id": "water_purifying", + "type": "COMESTIBLE", + "comestible_type": "DRINK", + "category": "food", + "name": { "str_sp": "water (purifying)" }, + "description": "You've dissolved tablets of disinfecting chemicals in this water to purify it. Soon it'll be ready to drink.", + "material": [ "water" ], + "weight": "250 g", + "volume": "250 ml", + "charges": 1, + "price": 50, + "price_postapoc": 1, + "symbol": "~", + "color": "light_blue", + "phase": "liquid", + "container": "bottle_plastic", + "sealed": false, + "quench": 50, + "ammo_data": { "ammo_type": "water" }, + "flags": [ "EATEN_COLD" ], + "use_action": { + "target": "water_clean", + "msg": "After using the tablets and carefully decanting out the resulting clean water, it's ready to drink", + "moves": 100, + "type": "delayed_transform", + "transform_age": 2400, + "not_ready_msg": "The chlorine released by the tablets is still working. You still need to wait a bit before you can drink it." + } } ] diff --git a/data/json/mapgen/military/mil_base/mil_base_z0.json b/data/json/mapgen/military/mil_base/mil_base_z0.json index 86ba5b124f1fa..3be4f044530e5 100644 --- a/data/json/mapgen/military/mil_base/mil_base_z0.json +++ b/data/json/mapgen/military/mil_base/mil_base_z0.json @@ -528,7 +528,7 @@ { "item": "thermos", "x": 38, "y": 59, "chance": 75, "repeat": [ 50, 150 ] }, { "item": "mess_kit", "x": 37, "y": 59, "chance": 75, "repeat": [ 50, 100 ] }, { "item": "water_purifier", "x": 36, "y": 59, "chance": 75, "repeat": [ 50, 100 ] }, - { "group": "pur_tablets_bottle_plastic_small_15", "x": 36, "y": 59, "chance": 75, "repeat": [ 50, 100 ] }, + { "group": "pur_tablets_bottle_plastic_small_50", "x": 36, "y": 59, "chance": 75, "repeat": [ 50, 100 ] }, { "group": "MRE", "x": [ 49, 55 ], "y": [ 53, 54 ], "chance": 75, "repeat": 1500 }, { "group": "MRE", "x": [ 49, 55 ], "y": 48, "chance": 75, "repeat": 750 }, { "group": "groce_pasta", "x": [ 44, 46 ], "y": 54, "chance": 75, "repeat": [ 50, 150 ] }, diff --git a/data/json/npcs/godco/classes.json b/data/json/npcs/godco/classes.json index d1d12c8942dc3..cb439397927d9 100644 --- a/data/json/npcs/godco/classes.json +++ b/data/json/npcs/godco/classes.json @@ -1033,10 +1033,10 @@ { "item": "heavy_flashlight", "prob": 20, "charges": [ 100, 300 ] }, { "item": "glowstick", "prob": 50, "charges": 1400 }, { "item": "handflare", "prob": 50, "charges": 300 }, + { "item": "pur_tablets", "prob": 10, "count": [ 15, 50 ] }, { "item": "gasoline_lantern", "prob": 7, "charges": [ 100, -1 ] }, { "item": "oil_lamp", "prob": 7, "charges": [ 100, -1 ] }, { "item": "lamp_oil", "prob": 5, "charges": [ 100, -1 ] }, - { "item": "pur_tablets", "prob": 10, "count": [ 5, 15 ] }, { "item": "mess_kit", "prob": 33 }, { "item": "popcan_stove", "prob": 33, "charges": [ 100, 500 ] }, { diff --git a/data/json/npcs/items_generic.json b/data/json/npcs/items_generic.json index 43a777188a178..89d60a0b67710 100644 --- a/data/json/npcs/items_generic.json +++ b/data/json/npcs/items_generic.json @@ -926,7 +926,7 @@ [ "pudding", 2 ], [ "puller", 1 ], [ "punch_dagger", 2 ], - { "group": "pur_tablets_bottle_plastic_small_5" }, + { "group": "pur_tablets_bottle_plastic_small_50" }, [ "purple_drink", 3 ], [ "purse", 4 ], [ "quikclot", 5 ], @@ -1147,10 +1147,10 @@ }, { "type": "item_group", - "id": "pur_tablets_bottle_plastic_small_5", + "id": "pur_tablets_bottle_plastic_small_50", "subtype": "collection", "//": "This group was created automatically and may contain errors.", "container-item": "bottle_plastic_small", - "entries": [ { "item": "pur_tablets", "count": 5 } ] + "entries": [ { "item": "pur_tablets", "count": 50 } ] } ] diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index 32c896f1fb998..f04aefeeb0727 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -88,12 +88,15 @@ { "type": "recipe", "activity_level": "NO_EXERCISE", - "result": "water", + "result": "water_purifying", "category": "CC_FOOD", - "id_suffix": "using_water_purifier", + "id_suffix": "using_tablets", "subcategory": "CSC_FOOD_DRINKS", - "skill_used": "survival", - "time": "1 m", + "skill_used": "cooking", + "time": "5 m", + "batch_time_factors": [ 90, 2 ], + "charges": 4, + "//": "See pur_tablets item definition for documentation and discussion", "autolearn": true, "components": [ [ [ "water_murky", 1 ] ], [ [ "pur_tablets", 1 ] ] ] }, diff --git a/data/mods/TEST_DATA/EOC.json b/data/mods/TEST_DATA/EOC.json index 3261927fb2783..7e8711441fbef 100644 --- a/data/mods/TEST_DATA/EOC.json +++ b/data/mods/TEST_DATA/EOC.json @@ -300,15 +300,21 @@ }, { "type": "effect_on_condition", - "id": "EOC_math_addiction", + "id": "EOC_math_addiction_setup", "effect": [ - { "set_string_var": "caffeine", "target_var": { "global_val": "add_id" } }, { "u_spawn_item": "backpack", "force_equip": true }, { "u_spawn_item": "test_whiskey_caffenated", "container": "bottle_glass" }, { "u_run_inv_eocs": "all", "true_eocs": [ { "id": "EOC_math_addiction_nested", "effect": [ { "u_activate": "ALCOHOL" } ] } ] - }, + } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_math_addiction_check", + "effect": [ + { "set_string_var": "caffeine", "target_var": { "global_val": "add_id" } }, { "math": [ "key_add_intensity", "=", "u_addiction_intensity(add_id)" ] }, { "math": [ "key_add_turn", "=", "u_addiction_turns(add_id)" ] } ] diff --git a/src/character.cpp b/src/character.cpp index 7cb09f5b11340..85c36bf6bcd98 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7092,6 +7092,17 @@ bool Character::invoke_item( item *used, const std::string &method, const tripoi return false; } + if( actually_used->is_comestible() && + actually_used->type->use_methods.find( "delayed_transform" ) == + actually_used->type->use_methods.end() ) { + // Assume that when activating food that can be transformed, you're trying to transform it. Otherwise... + // Try to eat it. + add_msg_if_player( m_info, string_format( "Attempting to eat %s", actually_used->display_name() ) ); + assign_activity( consume_activity_actor( item_location( *this, actually_used ) ) ); + // If the character isn't eating, then invoking the item failed somewhere + return !activity.is_null(); + } + std::optional charges_used = actually_used->type->invoke( this, *actually_used, pt, method ); if( !charges_used.has_value() ) { @@ -7105,16 +7116,6 @@ bool Character::invoke_item( item *used, const std::string &method, const tripoi return false; } - if( actually_used->is_comestible() ) { - const bool ret = consume_effects( *used ); - const int consumed = used->activation_consume( charges_used.value(), pt, this ); - if( consumed == 0 ) { - // Nothing was consumed from within the item. "Eat" the item itself away. - i_rem( actually_used ); - } - return ret; - } - actually_used->activation_consume( charges_used.value(), pt, this ); if( actually_used->has_flag( flag_SINGLE_USE ) || actually_used->is_bionic() || diff --git a/src/handle_liquid.cpp b/src/handle_liquid.cpp index 0ce6825b9a9ff..0756e13ba21a7 100644 --- a/src/handle_liquid.cpp +++ b/src/handle_liquid.cpp @@ -37,6 +37,7 @@ #include "units.h" #include "veh_interact.h" #include "vehicle.h" +#include "vehicle_selector.h" #include "vpart_position.h" #include "vpart_range.h" @@ -348,7 +349,7 @@ static bool get_liquid_target( item &liquid, const item *const source, const int } bool perform_liquid_transfer( item &liquid, const tripoint *const source_pos, - const vehicle *const source_veh, const int part_num, + vehicle *const source_veh, const int part_num, const monster *const /*source_mon*/, liquid_dest_opt &target ) { if( !liquid.made_of_from_type( phase_id::LIQUID ) ) { @@ -374,10 +375,19 @@ bool perform_liquid_transfer( item &liquid, const tripoint *const source_pos, }; map &here = get_map(); + item_location liquid_loc; switch( target.dest_opt ) { case LD_CONSUME: - player_character.assign_activity( consume_activity_actor( liquid ) ); - liquid.charges--; + if( source_pos ) { + liquid_loc = item_location( map_cursor( tripoint_bub_ms( *source_pos ) ), &liquid ); + } else if( source_veh ) { + liquid_loc = item_location( vehicle_cursor( *source_veh, part_num ), &liquid ); + } else { + player_character.assign_activity( consume_activity_actor( liquid ) ); + return true; + } + + player_character.assign_activity( consume_activity_actor( liquid_loc ) ); return true; case LD_ITEM: { // Currently activities can only store item position in the players inventory, @@ -458,7 +468,7 @@ bool can_handle_liquid( const item &liquid ) bool handle_liquid( item &liquid, const item *const source, const int radius, const tripoint *const source_pos, - const vehicle *const source_veh, const int part_num, + vehicle *const source_veh, const int part_num, const monster *const source_mon ) { bool success = false; diff --git a/src/handle_liquid.h b/src/handle_liquid.h index 5051e441a8a73..294d1b2b18fb9 100644 --- a/src/handle_liquid.h +++ b/src/handle_liquid.h @@ -110,12 +110,12 @@ bool can_handle_liquid( const item &liquid ); */ bool handle_liquid( item &liquid, const item *source = nullptr, int radius = 0, const tripoint *source_pos = nullptr, - const vehicle *source_veh = nullptr, int part_num = -1, + vehicle *source_veh = nullptr, int part_num = -1, const monster *source_mon = nullptr ); /* Not to be used directly. Use liquid_handler::handle_liquid instead. */ bool perform_liquid_transfer( item &liquid, const tripoint *source_pos, - const vehicle *source_veh, int part_num, + vehicle *source_veh, int part_num, const monster * /*source_mon*/, liquid_dest_opt &target ); } // namespace liquid_handler diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 1b211a92cf9c6..44372f6afae68 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -1886,6 +1886,7 @@ void Item_factory::init() add_iuse( "HEAT_SOLID_ITEMS", &iuse::heat_solid_items ); add_iuse( "HEAT_ALL_ITEMS", &iuse::heat_all_items ); add_iuse( "WATER_PURIFIER", &iuse::water_purifier ); + add_iuse( "WATER_TABLETS", &iuse::water_tablets ); add_iuse( "WEAK_ANTIBIOTIC", &iuse::weak_antibiotic ); add_iuse( "WEATHER_TOOL", &iuse::weather_tool ); add_iuse( "SEXTANT", &iuse::sextant ); diff --git a/src/iuse.cpp b/src/iuse.cpp index 8ba9200aeb014..051f490184758 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -281,6 +281,7 @@ static const itype_id itype_multi_cooker( "multi_cooker" ); static const itype_id itype_multi_cooker_filled( "multi_cooker_filled" ); static const itype_id itype_nicotine_liquid( "nicotine_liquid" ); static const itype_id itype_paper( "paper" ); +static const itype_id itype_pur_tablets( "pur_tablets" ); static const itype_id itype_radio_car( "radio_car" ); static const itype_id itype_radio_car_on( "radio_car_on" ); static const itype_id itype_radio_on( "radio_on" ); @@ -297,6 +298,7 @@ static const itype_id itype_towel( "towel" ); static const itype_id itype_towel_wet( "towel_wet" ); static const itype_id itype_water( "water" ); static const itype_id itype_water_clean( "water_clean" ); +static const itype_id itype_water_purifying( "water_purifying" ); static const itype_id itype_wax( "wax" ); static const itype_id itype_weather_reader( "weather_reader" ); @@ -1117,6 +1119,11 @@ std::optional iuse::blech( Character *p, item *it, const tripoint & ) std::optional iuse::blech_because_unclean( Character *p, item *it, const tripoint & ) { if( !p->is_npc() ) { + if( test_mode ) { + p->add_msg_if_player( m_info, + _( "Automatically eating the gross food because a test told us to." ) ); + return 1; + } if( it->made_of( phase_id::LIQUID ) ) { if( !p->query_yn( _( "This looks unclean; are you sure you want to drink it?" ) ) ) { return std::nullopt; @@ -2465,6 +2472,81 @@ std::optional iuse::water_purifier( Character *p, item *it, const tripoint return charges_of_water; } + +// Part of iuse::water_tablets, but with the user interaction split out so it can be unit tested +std::optional iuse::purify_water( Character *p, item *purifier, item_location &water ) +{ + const double default_ratio = 4; // Existing pur_tablets will not have the var + const int max_water_per_tablet = static_cast( purifier->get_var( "water_per_tablet", + default_ratio ) ); + if( max_water_per_tablet < 1 ) { + debugmsg( "ERROR: %s set to purify only %i water each. Nothing was purified.", + purifier->typeId().str(), max_water_per_tablet ); + return std::nullopt; + } + + const std::vector liquids = water->items_with( []( const item & it ) { + return it.typeId() == itype_water; + } ); + int charges_of_water = 0; + for( const item *water : liquids ) { + charges_of_water += water->charges; + } + float to_consume_f = charges_of_water; + to_consume_f /= max_water_per_tablet; + + const int available = p->crafting_inventory().count_item( itype_pur_tablets ); + if( available * max_water_per_tablet >= charges_of_water ) { + int to_consume = std::ceil( to_consume_f ); + p->add_msg_if_player( m_info, _( "Purifying %i water using %i %s" ), charges_of_water, to_consume, + purifier->tname( to_consume ) );; + // Pull from surrounding map first because it will update to_consume + get_map().use_amount( p->pos(), PICKUP_RANGE, itype_pur_tablets, to_consume ); + // Then pull from inventory + if( to_consume > 0 ) { + p->use_amount( itype_pur_tablets, to_consume ); + } + } else { + p->add_msg_if_player( m_info, + _( "You need %i tablets to purify that. You only have %i" ), + charges_of_water / max_water_per_tablet, available ); + return std::nullopt; + } + + // Try to match crafting recipe at 5m for 1, 90% batch time savings: + int req_moves = to_moves( 5_minutes ); + req_moves = req_moves * 0.1 * to_consume_f; + p->mod_moves( -req_moves ); + + for( item *water : liquids ) { + water->convert( itype_water_purifying, p ).poison = 0; + water->set_birthday( calendar::turn ); + } + // We've already consumed the tablets, so don't try to consume them again + return std::nullopt; +} + +std::optional iuse::water_tablets( Character *p, item *it, const tripoint & ) +{ + if( p->cant_do_mounted() ) { + return std::nullopt; + } + + item_location obj = g->inv_map_splice( []( const item_location & e ) { + return ( !e->empty() && e->has_item_with( []( const item & it ) { + return it.typeId() == itype_water; + } ) ) || ( e->typeId() == itype_water && + get_map().has_flag_furn( ter_furn_flag::TFLAG_LIQUIDCONT, e.position() ) ); + }, _( "Purify what?" ), 1, _( "You don't have water to purify." ) ); + + if( !obj ) { + p->add_msg_if_player( m_info, _( "You don't have that item!" ) ); + return std::nullopt; + } + + return purify_water( p, it, obj ); +} + std::optional iuse::radio_off( Character *p, item *it, const tripoint & ) { if( !it->ammo_sufficient( p ) ) { diff --git a/src/iuse.h b/src/iuse.h index 010996399dead..df15797bcb200 100644 --- a/src/iuse.h +++ b/src/iuse.h @@ -197,6 +197,7 @@ std::optional wash_hard_items( Character *, item *, const tripoint & ); std::optional wash_items( Character *p, bool soft_items, bool hard_items ); std::optional wash_soft_items( Character *, item *, const tripoint & ); std::optional water_purifier( Character *, item *, const tripoint & ); +std::optional water_tablets( Character *, item *, const tripoint & ); std::optional weak_antibiotic( Character *, item *, const tripoint & ); std::optional weather_tool( Character *, item *, const tripoint & ); std::optional sextant( Character *, item *, const tripoint & ); @@ -224,6 +225,7 @@ std::optional disassemble( Character *, item *, const tripoint & ); // Helper functions for other iuse functions void cut_log_into_planks( Character & ); void play_music( Character *p, const tripoint &source, int volume, int max_morale ); +std::optional purify_water( Character *p, item *purifier, item_location &water ); int towel_common( Character *, item *, bool ); // Helper for validating a potential target of robot control diff --git a/tests/eoc_test.cpp b/tests/eoc_test.cpp index 100664fd4c007..8aa136fb41034 100644 --- a/tests/eoc_test.cpp +++ b/tests/eoc_test.cpp @@ -51,7 +51,9 @@ effect_on_condition_EOC_martial_art_test_1( "EOC_martial_art_test_1" ); static const effect_on_condition_id effect_on_condition_EOC_martial_art_test_2( "EOC_martial_art_test_2" ); static const effect_on_condition_id -effect_on_condition_EOC_math_addiction( "EOC_math_addiction" ); +effect_on_condition_EOC_math_addiction_check( "EOC_math_addiction_check" ); +static const effect_on_condition_id +effect_on_condition_EOC_math_addiction_setup( "EOC_math_addiction_setup" ); static const effect_on_condition_id effect_on_condition_EOC_math_armor( "EOC_math_armor" ); static const effect_on_condition_id @@ -455,7 +457,12 @@ TEST_CASE( "EOC_math_addiction", "[eoc][math_parser]" ) REQUIRE( globvars.get_global_value( "npctalk_var_key_add_intensity" ).empty() ); REQUIRE( globvars.get_global_value( "npctalk_var_key_add_turn" ).empty() ); - CHECK( effect_on_condition_EOC_math_addiction->activate( d ) ); + CHECK( effect_on_condition_EOC_math_addiction_setup->activate( d ) ); + // Finish drinking + complete_activity( get_avatar() ); + + CHECK( effect_on_condition_EOC_math_addiction_check->activate( d ) ); + CHECK( globvars.get_global_value( "npctalk_var_key_add_intensity" ) == "1" ); CHECK( globvars.get_global_value( "npctalk_var_key_add_turn" ) == "3600" ); } diff --git a/tests/iuse_test.cpp b/tests/iuse_test.cpp index 63b68c3decb59..943121ed8c92b 100644 --- a/tests/iuse_test.cpp +++ b/tests/iuse_test.cpp @@ -9,6 +9,8 @@ #include "flag.h" #include "item.h" #include "itype.h" +#include "map_helpers.h" +#include "morale_types.h" #include "player_helpers.h" #include "type_id.h" #include "value_ptr.h" @@ -22,6 +24,7 @@ static const efftype_id effect_brainworms( "brainworms" ); static const efftype_id effect_conjunctivitis( "conjunctivitis" ); static const efftype_id effect_cureall( "cureall" ); static const efftype_id effect_dermatik( "dermatik" ); +static const efftype_id effect_foodpoison( "foodpoison" ); static const efftype_id effect_fungus( "fungus" ); static const efftype_id effect_glowing( "glowing" ); static const efftype_id effect_hallu( "hallu" ); @@ -42,15 +45,25 @@ static const efftype_id effect_took_xanax_visible( "took_xanax_visible" ); static const efftype_id effect_valium( "valium" ); static const efftype_id effect_visuals( "visuals" ); +static const furn_str_id furn_f_toilet( "f_toilet" ); + +static const itype_id itype_55gal_drum( "55gal_drum" ); static const itype_id itype_albuterol( "albuterol" ); static const itype_id itype_antifungal( "antifungal" ); static const itype_id itype_antiparasitic( "antiparasitic" ); +static const itype_id itype_backpack( "backpack" ); static const itype_id itype_diazepam( "diazepam" ); +static const itype_id itype_pur_tablets( "pur_tablets" ); static const itype_id itype_thorazine( "thorazine" ); static const itype_id itype_towel_wet( "towel_wet" ); +static const itype_id itype_water( "water" ); +static const itype_id itype_water_clean( "water_clean" ); +static const itype_id itype_water_purifying( "water_purifying" ); static const morale_type morale_wet( "morale_wet" ); +static const ter_str_id ter_t_grass( "t_grass" ); + TEST_CASE( "eyedrops", "[iuse][eyedrops]" ) { avatar dummy; @@ -700,3 +713,344 @@ TEST_CASE( "xanax", "[iuse][xanax]" ) } } +static item_location give_tablets( avatar &dummy, int count, bool in_inventory ) +{ + const int existing_tablets = dummy.crafting_inventory().count_item( itype_pur_tablets ); + item_location tablets; + if( in_inventory ) { + for( int i = 0; i < count; ++i ) { + tablets = dummy.i_add( item( itype_pur_tablets, calendar::turn ) ); + } + } else { + // Tablets fail to spawn directly on the ground, so put them in a backpack, then spawn that + item container( itype_backpack ); + for( int i = 0; i < count; ++i ) { + container.put_in( item( itype_pur_tablets, calendar::turn ), pocket_type::CONTAINER ); + } + item_location container_loc = get_map().add_item_ret_loc( dummy.pos_bub(), container, true ); + REQUIRE( container_loc ); + std::list all_tablets = container_loc->all_items_top(); + REQUIRE( !all_tablets.empty() ); + tablets = item_location( container_loc, all_tablets.front() ); + } + REQUIRE( tablets ); + // Since we checked it earlier, the crafting inventory needs to be invalidated to force an update + dummy.invalidate_crafting_inventory(); + REQUIRE( dummy.crafting_inventory().count_item( itype_pur_tablets ) == count + existing_tablets ); + return tablets; +} + +static item_location give_water( avatar &dummy, int count, bool in_inventory ) +{ + item container( itype_55gal_drum ); + item_location container_loc; + item water( itype_water ); + water.charges = count; + container.put_in( water, pocket_type::CONTAINER ); + if( in_inventory ) { + container_loc = dummy.i_add( container ); + } else { + container_loc = get_map().add_item_ret_loc( dummy.pos_bub(), container, true ); + } + REQUIRE( container_loc ); + // Spawning a container of water next to the player does not update the crafting inventory, so force an update + dummy.invalidate_crafting_inventory(); + REQUIRE( dummy.crafting_inventory().charges_of( itype_water ) == count ); + return item_location( dummy, &container_loc->only_item() ); +} + +TEST_CASE( "water_purification_tablet_activation", "[iuse][pur_tablets]" ) +{ + avatar dummy; + dummy.normalize(); + build_test_map( ter_t_grass ); + const tripoint test_origin( 20, 20, 0 ); + dummy.setpos( test_origin ); + // Give the player a backpack to hold the tablets + dummy.worn.wear_item( dummy, item( itype_backpack ), false, false ); + + SECTION( "1 tablet in inventory will purify 4 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 4, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet in inventory will purify 1 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 1, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 1 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet in inventory will not purify 5 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 5, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 5 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 0 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 1 ); + } + + SECTION( "3 tablets in inventory will purify 12 water in inventory" ) { + item_location tablet = give_tablets( dummy, 3, true ); + item_location water_location = give_water( dummy, 12, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 12 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "2 tablets in inventory will purify 4 water in inventory" ) { + item_location tablet = give_tablets( dummy, 2, true ); + item_location water_location = give_water( dummy, 4, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 1 ); + } + + SECTION( "1 tablet nearby will purify 4 water nearby" ) { + item_location tablet = give_tablets( dummy, 1, false ); + item_location water_location = give_water( dummy, 4, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "2 tablets nearby will purify 5 water nearby" ) { + item_location tablet = give_tablets( dummy, 2, false ); + item_location water_location = give_water( dummy, 5, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 5 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "3 tablets nearby will not purify 13 water nearby" ) { + item_location tablet = give_tablets( dummy, 3, false ); + item_location water_location = give_water( dummy, 13, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 13 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 0 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 3 ); + } + + SECTION( "1 tablet in inventory will purify 2 water nearby" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 2, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 2 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet nearby will purify 1 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, false ); + item_location water_location = give_water( dummy, 1, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 1 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet nearby and 1 tablet in inventory will purify 3 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, false ); + tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 3, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 3 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 1 ); + } + + SECTION( "1 tablet nearby and 1 tablet in inventory will purify 8 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, false ); + tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 8, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 8 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet nearby and 1 tablet in inventory will purify 2 water nearby" ) { + item_location tablet = give_tablets( dummy, 1, false ); + tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 8, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 8 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet nearby and 1 tablet in inventory will not purify 9 water nearby" ) { + item_location tablet = give_tablets( dummy, 1, false ); + tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 9, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 9 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 0 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 2 ); + } + + SECTION( "6 tablets will purify a toilet tank" ) { + item_location tablet = give_tablets( dummy, 6, true ); + get_map().furn_set( dummy.pos_bub() + tripoint_north, furn_f_toilet ); + item water( itype_water ); + water.charges = 24; + get_map().add_item( dummy.pos_bub() + tripoint_north, water ); + item_location water_location( map_cursor( dummy.pos_bub() + tripoint_north ), &water ); + + REQUIRE( water_location ); + REQUIRE( water_location.get_item()->typeId() == itype_water ); + REQUIRE( water_location.get_item()->charges == 24 ); + REQUIRE( water_location.where_recursive() == item_location::type::map ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + // TODO: Figure out why the crafting inventory doesn't update to show water_purifying + /*dummy.invalidate_crafting_inventory(); + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 24 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 );*/ + // Until then, show that it works by checking the item directly: + CHECK( water_location.get_item()->typeId() == itype_water_purifying ); + CHECK( water_location.get_item()->charges == 24 ); + } +} + +TEST_CASE( "water_tablet_purification_test", "[iuse][pur_tablets]" ) +{ + avatar dummy; + dummy.normalize(); + build_test_map( ter_t_grass ); + const tripoint test_origin( 20, 20, 0 ); + dummy.setpos( test_origin ); + + SECTION( "Test purifying time" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 4, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + REQUIRE( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + REQUIRE( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + + SECTION( "Still purifying at ten minutes" ) { + calendar::turn += 10_minutes; + dummy.invoke_item( water_location.get_item() ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + } + + SECTION( "Still purifying at thirty minutes" ) { + calendar::turn += 30_minutes; + dummy.invoke_item( water_location.get_item() ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + } + + SECTION( "Clean water at forty minutes" ) { + calendar::turn += 40_minutes; + dummy.invoke_item( water_location.get_item() ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 4 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 0 ); + } + } + + SECTION( "Test purifying time with old water" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 4, false ); + + calendar::turn += 24_hours; + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + SECTION( "Still purifying ten minutes later" ) { + calendar::turn += 10_minutes; + dummy.invoke_item( water_location.get_item() ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + } + + SECTION( "Still purifying thirty minutes later" ) { + calendar::turn += 30_minutes; + dummy.invoke_item( water_location.get_item() ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + } + + SECTION( "Clean water forty minutes later" ) { + calendar::turn += 40_minutes; + dummy.invoke_item( water_location.get_item() ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 4 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 0 ); + } + } + + SECTION( "Test purifying known bad water" ) { + item_location water_location = give_water( dummy, 4, false ); + water_location.get_item()->poison = 100; + + SECTION( "The water will poison us before purification" ) { + dummy.consume( water_location, true ); + REQUIRE( water_location.get_item()->charges == 3 ); + REQUIRE( water_location.get_item()->poison > 0 ); + + CHECK( dummy.has_effect( effect_foodpoison ) ); + } + + SECTION( "The water is safe after purification" ) { + item_location tablet = give_tablets( dummy, 1, true ); + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + calendar::turn += 40_minutes; + dummy.invoke_item( water_location.get_item() ); + REQUIRE( dummy.crafting_inventory().charges_of( itype_water_clean ) == 4 ); + dummy.consume( water_location, true ); + REQUIRE( water_location.get_item()->charges == 3 ); + + CHECK_FALSE( dummy.has_effect( effect_foodpoison ) ); + } + + } +} diff --git a/tools/spell_checker/dictionary.txt b/tools/spell_checker/dictionary.txt index aae38df7bde31..fc970f0ea346b 100644 --- a/tools/spell_checker/dictionary.txt +++ b/tools/spell_checker/dictionary.txt @@ -1484,6 +1484,9 @@ dex Dhamma Dhammapada dharmachakra +diaspora +dichloroisocyanurate +didn Dharmasastra dhyâna di