From ce213a0bd67400ae6c7d00e6ce006da212a41fc3 Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Sun, 25 Feb 2024 10:25:17 -0500 Subject: [PATCH 01/12] Ensure pur_tablets purify 1 water each via crafting/activation --- src/iuse.cpp | 66 ++++++++++---- src/iuse.h | 1 + tests/iuse_test.cpp | 209 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 18 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index 7e24e88dec26f..aa6dd5237821b 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" ); @@ -2485,40 +2486,69 @@ std::optional iuse::pack_item( Character *p, item *it, const tripoint & ) return 0; } -std::optional iuse::water_purifier( Character *p, item *it, const tripoint & ) +// Part of iuse::water_purifier, 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 ) { - if( p->cant_do_mounted() ) { - return std::nullopt; - } - item_location obj = g->inv_map_splice( []( const item & e ) { - return !e.empty() && e.has_item_with( []( const item & it ) { - return it.typeId() == itype_water; - } ); - }, _( "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; - } - - const std::vector liquids = obj->items_with( []( const item & it ) { + 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; } - if( !it->ammo_sufficient( p, charges_of_water ) ) { + if( !purifier->ammo_sufficient( p, charges_of_water ) ) { p->add_msg_if_player( m_info, _( "That volume of water is too large to purify." ) ); return std::nullopt; } + if( purifier->typeId() == itype_pur_tablets ) { + const int available = p->crafting_inventory().count_item( itype_pur_tablets ); + if( available >= charges_of_water ) { + p->add_msg_if_player( m_info, _( "Purifying water using %s" ), purifier->tname() ); + // Pull from surrounding map first because it will update to_consume + int to_consume = charges_of_water; + 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 %1i tablets to purify that. You only have %2i" ), charges_of_water, available ); + return std::nullopt; + } + } + p->moves -= to_moves( 2_seconds ); for( item *water : liquids ) { water->convert( itype_water_clean, p ).poison = 0; } - return charges_of_water; + if( purifier->typeId() == itype_pur_tablets ) { + // We've already consumed the tablets, so don't try to consume them again + return std::nullopt; + } else { + return charges_of_water; + } +} + +std::optional iuse::water_purifier( Character *p, item *it, const tripoint & ) +{ + if( p->cant_do_mounted() ) { + return std::nullopt; + } + item_location obj = g->inv_map_splice( []( const item & e ) { + return !e.empty() && e.has_item_with( []( const item & it ) { + return it.typeId() == itype_water; + } ); + }, _( "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 & ) diff --git a/src/iuse.h b/src/iuse.h index 168249fda7502..d75039bde153a 100644 --- a/src/iuse.h +++ b/src/iuse.h @@ -223,6 +223,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/iuse_test.cpp b/tests/iuse_test.cpp index 606278a35a2ec..ada5ef6a2e0f5 100644 --- a/tests/iuse_test.cpp +++ b/tests/iuse_test.cpp @@ -9,6 +9,7 @@ #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" @@ -41,12 +42,17 @@ 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 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" ); TEST_CASE( "eyedrops", "[iuse][eyedrops]" ) { @@ -666,3 +672,206 @@ 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(), 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(), 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_tablets", "[iuse][pur_tablets]" ) +{ + avatar dummy; + dummy.normalize(); + build_test_map( 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 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_clean ) == 1 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet in inventory will not purify 2 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 2, true ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 2 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 1 ); + } + + SECTION( "3 tablets in inventory will purify 3 water in inventory" ) { + item_location tablet = give_tablets( dummy, 3, 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_clean ) == 3 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "4 tablets in inventory will purify 3 water in inventory" ) { + item_location tablet = give_tablets( dummy, 4, 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_clean ) == 3 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 1 ); + } + + SECTION( "1 tablet nearby will purify 1 water nearby" ) { + item_location tablet = give_tablets( dummy, 1, false ); + item_location water_location = give_water( dummy, 1, 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_clean ) == 1 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "3 tablets nearby will purify 3 water nearby" ) { + item_location tablet = give_tablets( dummy, 3, false ); + item_location water_location = give_water( dummy, 3, 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_clean ) == 3 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "3 tablets nearby will not purify 4 water nearby" ) { + item_location tablet = give_tablets( dummy, 3, 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 ) == 4 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 3 ); + } + + SECTION( "1 tablet in inventory will purify 1 water nearby" ) { + item_location tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 1, 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_clean ) == 1 ); + 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_clean ) == 1 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet nearby and 1 tablet in inventory will purify 1 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, false ); + 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_clean ) == 1 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 1 ); + } + + SECTION( "1 tablet nearby and 1 tablet in inventory will purify 2 water in inventory" ) { + item_location tablet = give_tablets( dummy, 1, false ); + tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 2, 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_clean ) == 2 ); + 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, 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_clean ) == 2 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); + } + + SECTION( "1 tablet nearby and 1 tablet in inventory will not purify 3 water nearby" ) { + item_location tablet = give_tablets( dummy, 1, false ); + tablet = give_tablets( dummy, 1, true ); + item_location water_location = give_water( dummy, 3, false ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + CHECK( dummy.crafting_inventory().charges_of( itype_water ) == 3 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 2 ); + } +} From 30b22988545deabf2d3b0295ca0d4877f09abd2b Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:10:16 -0500 Subject: [PATCH 02/12] Move purification tablets to making a delayed-transform item --- data/json/item_actions.json | 5 + data/json/items/chemicals_and_resources.json | 9 +- data/json/items/comestibles/drink.json | 30 ++++ data/json/recipes/recipe_food.json | 15 ++ src/item_factory.cpp | 1 + src/iuse.cpp | 100 +++++++---- src/iuse.h | 1 + tests/iuse_test.cpp | 170 ++++++++++++++----- 8 files changed, 260 insertions(+), 71 deletions(-) diff --git a/data/json/item_actions.json b/data/json/item_actions.json index f9a19c50afca4..961fc39d5804c 100644 --- a/data/json/item_actions.json +++ b/data/json/item_actions.json @@ -850,6 +850,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/items/chemicals_and_resources.json b/data/json/items/chemicals_and_resources.json index 666566c527222..593f4a1b4283e 100644 --- a/data/json/items/chemicals_and_resources.json +++ b/data/json/items/chemicals_and_resources.json @@ -1140,10 +1140,15 @@ "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 litre (4 units of water).", "price": "9 cent", "price_postapoc": "20 cent", "volume": "2 ml", diff --git a/data/json/items/comestibles/drink.json b/data/json/items/comestibles/drink.json index 7f385e922da91..2f47e46254b77 100644 --- a/data/json/items/comestibles/drink.json +++ b/data/json/items/comestibles/drink.json @@ -1842,5 +1842,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. This isn't ready to drink yet." + } } ] diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index b9d2078f40f08..62d4ffacb32f8 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -85,6 +85,21 @@ "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_purifying", + "category": "CC_FOOD", + "id_suffix": "using_tablets", + "subcategory": "CSC_FOOD_DRINKS", + "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", 4 ] ], [ [ "pur_tablets", 1 ] ] ] + }, { "type": "recipe", "activity_level": "NO_EXERCISE", diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 2175178f375fb..aa8eb0386a6fb 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -1859,6 +1859,7 @@ void Item_factory::init() add_iuse( "WASH_HARD_ITEMS", &iuse::wash_hard_items ); add_iuse( "WASH_ALL_ITEMS", &iuse::wash_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 aa6dd5237821b..99e4f93ec5cda 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -298,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" ); @@ -2486,61 +2487,100 @@ std::optional iuse::pack_item( Character *p, item *it, const tripoint & ) return 0; } -// Part of iuse::water_purifier, 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 ) +std::optional iuse::water_purifier( Character *p, item *it, const tripoint & ) { - const std::vector liquids = water->items_with( []( const item & it ) { + if( p->cant_do_mounted() ) { + return std::nullopt; + } + item_location obj = g->inv_map_splice( []( const item & e ) { + return !e.empty() && e.has_item_with( []( const item & it ) { + return it.typeId() == itype_water; + } ); + }, _( "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; + } + + const std::vector liquids = obj->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; } - if( !purifier->ammo_sufficient( p, charges_of_water ) ) { + if( !it->ammo_sufficient( p, charges_of_water ) ) { p->add_msg_if_player( m_info, _( "That volume of water is too large to purify." ) ); return std::nullopt; } - if( purifier->typeId() == itype_pur_tablets ) { - const int available = p->crafting_inventory().count_item( itype_pur_tablets ); - if( available >= charges_of_water ) { - p->add_msg_if_player( m_info, _( "Purifying water using %s" ), purifier->tname() ); - // Pull from surrounding map first because it will update to_consume - int to_consume = charges_of_water; - 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 %1i tablets to purify that. You only have %2i" ), charges_of_water, available ); - return std::nullopt; - } - } - p->moves -= to_moves( 2_seconds ); for( item *water : liquids ) { water->convert( itype_water_clean, p ).poison = 0; } - if( purifier->typeId() == itype_pur_tablets ) { - // We've already consumed the tablets, so don't try to consume them again - return std::nullopt; + 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 ) +{ + // TODO: Find a way to move this to json: + const int max_water_per_tablet = 4; + + 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 %1i water using %2i %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 { - return charges_of_water; + p->add_msg_if_player( m_info, + _( "You need %1i tablets to purify that. You only have %2i" ), + 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->moves -= req_moves; + + for( item *water : liquids ) { + water->convert( itype_water_purifying, p ).poison = 0; } + // We've already consumed the tablets, so don't try to consume them again + return std::nullopt; } -std::optional iuse::water_purifier( Character *p, item *it, const tripoint & ) +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 & e ) { - return !e.empty() && e.has_item_with( []( const item & it ) { + + 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 ) { diff --git a/src/iuse.h b/src/iuse.h index d75039bde153a..2b61b0499f2c0 100644 --- a/src/iuse.h +++ b/src/iuse.h @@ -196,6 +196,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 & ); diff --git a/tests/iuse_test.cpp b/tests/iuse_test.cpp index ada5ef6a2e0f5..5b978ef2a0a2e 100644 --- a/tests/iuse_test.cpp +++ b/tests/iuse_test.cpp @@ -53,6 +53,7 @@ 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" ); TEST_CASE( "eyedrops", "[iuse][eyedrops]" ) { @@ -728,6 +729,17 @@ TEST_CASE( "water_purification_tablets", "[iuse][pur_tablets]" ) // 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 ); @@ -735,84 +747,84 @@ TEST_CASE( "water_purification_tablets", "[iuse][pur_tablets]" ) 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_clean ) == 1 ); + 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 2 water in inventory" ) { + 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, 2, 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 ) == 2 ); - CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + 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 3 water in inventory" ) { + 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, 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_clean ) == 3 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 12 ); CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); } - SECTION( "4 tablets in inventory will purify 3 water in inventory" ) { - item_location tablet = give_tablets( dummy, 4, true ); - item_location water_location = give_water( dummy, 3, true ); + 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_clean ) == 3 ); + 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 1 water nearby" ) { + SECTION( "1 tablet nearby will purify 4 water nearby" ) { item_location tablet = give_tablets( dummy, 1, false ); - item_location water_location = give_water( 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_clean ) == 1 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); } - SECTION( "3 tablets nearby will purify 3 water nearby" ) { - item_location tablet = give_tablets( dummy, 3, false ); - item_location water_location = give_water( dummy, 3, false ); + 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_clean ) == 3 ); + 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 4 water nearby" ) { + 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, 4, 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 ) == 4 ); - CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + 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 1 water nearby" ) { + 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, 1, false ); + 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_clean ) == 1 ); + CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 2 ); CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 0 ); } @@ -823,55 +835,135 @@ TEST_CASE( "water_purification_tablets", "[iuse][pur_tablets]" ) 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_clean ) == 1 ); + 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 1 water in inventory" ) { + 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, 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_clean ) == 1 ); + 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 2 water in inventory" ) { + 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, 2, 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_clean ) == 2 ); + 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, 2, false ); + 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_clean ) == 2 ); + 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 3 water nearby" ) { + 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, 3, false ); + 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 ) == 3 ); - CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); + 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 ); } + + /* + Currently fails, and has odd behaviour in-game where it's possible to + convert purifying water in a toilet to clean water instantly rather than waiting. + SECTION( "6 tablets will purify a toilet tank" ) { + item_location tablet = give_tablets( dummy, 6, true ); + get_map().furn_set( dummy.pos() + tripoint_north, f_toilet ); + item water( itype_water ); + water.charges = 24; + get_map().add_item( dummy.pos() + tripoint_north, water ); + item_location water_location( map_cursor( dummy.pos() + tripoint_north ), &water ); + dummy.invalidate_crafting_inventory(); + + REQUIRE( dummy.crafting_inventory().charges_of( itype_water ) == 24 ); + REQUIRE( water_location ); + + iuse::purify_water( &dummy, tablet.get_item(), water_location ); + + 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 ); + }*/ +} + +TEST_CASE( "water_purifying", "[iuse][pur_tablets]" ) +{ + avatar dummy; + dummy.normalize(); + build_test_map( t_grass ); + const tripoint test_origin( 20, 20, 0 ); + dummy.setpos( test_origin ); + + SECTION( "Still purifying at ten minutes" ) { + 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 ); + + 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" ) { + 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 ); + + 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" ) { + 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 ); + + 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 ); + } } From 29165476bc4a30b6bfe31f4566b86b2bc6de4e79 Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:41:27 -0500 Subject: [PATCH 03/12] Fix water_purifying recipe JSON formatting --- data/json/recipes/recipe_food.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index 62d4ffacb32f8..21dd6fa9cfe30 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -94,8 +94,8 @@ "subcategory": "CSC_FOOD_DRINKS", "skill_used": "cooking", "time": "5 m", - "batch_time_factors": [ 90, 2 ], - "charges": 4, + "batch_time_factors": [ 90, 2 ], + "charges": 4, "//": "See pur_tablets item definition for documentation and discussion", "autolearn": true, "components": [ [ [ "water", 4 ] ], [ [ "pur_tablets", 1 ] ] ] From 836073023989c12970197e967ad55a6b2024768a Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:15:00 -0500 Subject: [PATCH 04/12] Fix water purification message formatting --- src/iuse.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index 99e4f93ec5cda..a6c547b847ac5 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -2543,7 +2543,7 @@ std::optional iuse::purify_water( Character *p, item *purifier, item_locati 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 %1i water using %2i %s" ), charges_of_water, to_consume, + 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 ); @@ -2553,7 +2553,7 @@ std::optional iuse::purify_water( Character *p, item *purifier, item_locati } } else { p->add_msg_if_player( m_info, - _( "You need %1i tablets to purify that. You only have %2i" ), + _( "You need %i tablets to purify that. You only have %i" ), charges_of_water / max_water_per_tablet, available ); return std::nullopt; } From bdfa309b477d2676e4971b712ffd717381211148 Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:19:36 -0500 Subject: [PATCH 05/12] Expand purification testing and ensure birthday is reset --- src/iuse.cpp | 6 +++ tests/iuse_test.cpp | 114 +++++++++++++++++++++++++++++++------------- 2 files changed, 87 insertions(+), 33 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index a6c547b847ac5..1719ab4398ab1 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -1112,6 +1112,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; @@ -2565,6 +2570,7 @@ std::optional iuse::purify_water( Character *p, item *purifier, item_locati 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; diff --git a/tests/iuse_test.cpp b/tests/iuse_test.cpp index 5b978ef2a0a2e..c5a4a8d043538 100644 --- a/tests/iuse_test.cpp +++ b/tests/iuse_test.cpp @@ -22,6 +22,7 @@ static const efftype_id effect_boomered( "boomered" ); static const efftype_id effect_brainworms( "brainworms" ); 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" ); @@ -719,7 +720,7 @@ static item_location give_water( avatar &dummy, int count, bool in_inventory ) return item_location( dummy, &container_loc->only_item() ); } -TEST_CASE( "water_purification_tablets", "[iuse][pur_tablets]" ) +TEST_CASE( "water_purification_tablet_activation", "[iuse][pur_tablets]" ) { avatar dummy; dummy.normalize(); @@ -887,9 +888,6 @@ TEST_CASE( "water_purification_tablets", "[iuse][pur_tablets]" ) CHECK( dummy.crafting_inventory().count_item( itype_pur_tablets ) == 2 ); } - /* - Currently fails, and has odd behaviour in-game where it's possible to - convert purifying water in a toilet to clean water instantly rather than waiting. SECTION( "6 tablets will purify a toilet tank" ) { item_location tablet = give_tablets( dummy, 6, true ); get_map().furn_set( dummy.pos() + tripoint_north, f_toilet ); @@ -897,21 +895,26 @@ TEST_CASE( "water_purification_tablets", "[iuse][pur_tablets]" ) water.charges = 24; get_map().add_item( dummy.pos() + tripoint_north, water ); item_location water_location( map_cursor( dummy.pos() + tripoint_north ), &water ); - dummy.invalidate_crafting_inventory(); - REQUIRE( dummy.crafting_inventory().charges_of( itype_water ) == 24 ); 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 ); - 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 ); - }*/ + // 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_purifying", "[iuse][pur_tablets]" ) +TEST_CASE( "water_tablet_purification_test", "[iuse][pur_tablets]" ) { avatar dummy; dummy.normalize(); @@ -919,7 +922,7 @@ TEST_CASE( "water_purifying", "[iuse][pur_tablets]" ) const tripoint test_origin( 20, 20, 0 ); dummy.setpos( test_origin ); - SECTION( "Still purifying at ten minutes" ) { + SECTION( "Test purifying time" ) { item_location tablet = give_tablets( dummy, 1, true ); item_location water_location = give_water( dummy, 4, false ); @@ -928,42 +931,87 @@ TEST_CASE( "water_purifying", "[iuse][pur_tablets]" ) REQUIRE( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); REQUIRE( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); - calendar::turn += 10_minutes; - dummy.invoke_item( water_location.get_item() ); + 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 ); + 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( "Still purifying at thirty minutes" ) { + 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 ); - REQUIRE( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); - REQUIRE( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + SECTION( "Still purifying ten minutes later" ) { + calendar::turn += 10_minutes; + dummy.invoke_item( water_location.get_item() ); - 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 ); + } - 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( "Clean water at forty minutes" ) { - item_location tablet = give_tablets( dummy, 1, true ); + SECTION( "Test purifying known bad water" ) { item_location water_location = give_water( dummy, 4, false ); + water_location.get_item()->poison = 100; - iuse::purify_water( &dummy, tablet.get_item(), water_location ); + 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 ); - REQUIRE( dummy.crafting_inventory().charges_of( itype_water_clean ) == 0 ); - REQUIRE( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 4 ); + CHECK( dummy.has_effect( effect_foodpoison ) ); + } - calendar::turn += 40_minutes; - dummy.invoke_item( water_location.get_item() ); + 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 ) ); + } - CHECK( dummy.crafting_inventory().charges_of( itype_water_clean ) == 4 ); - CHECK( dummy.crafting_inventory().charges_of( itype_water_purifying ) == 0 ); } } From 6769b434d6f2f14cbb1365428a61b2a000f7f458 Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:27:35 -0500 Subject: [PATCH 06/12] Update dictionary and tweak wording --- data/json/items/chemicals_and_resources.json | 2 +- data/json/items/comestibles/drink.json | 2 +- tools/spell_checker/dictionary.txt | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/json/items/chemicals_and_resources.json b/data/json/items/chemicals_and_resources.json index 593f4a1b4283e..ff0ccf5e7c4f0 100644 --- a/data/json/items/chemicals_and_resources.json +++ b/data/json/items/chemicals_and_resources.json @@ -1148,7 +1148,7 @@ "//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 litre (4 units of 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", diff --git a/data/json/items/comestibles/drink.json b/data/json/items/comestibles/drink.json index 2f47e46254b77..f67534dd26ad3 100644 --- a/data/json/items/comestibles/drink.json +++ b/data/json/items/comestibles/drink.json @@ -1870,7 +1870,7 @@ "moves": 100, "type": "delayed_transform", "transform_age": 2400, - "not_ready_msg": "The chlorine released by the tablets is still working. This isn't ready to drink yet." + "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/tools/spell_checker/dictionary.txt b/tools/spell_checker/dictionary.txt index 1061d9fe701df..5118464e9aea9 100644 --- a/tools/spell_checker/dictionary.txt +++ b/tools/spell_checker/dictionary.txt @@ -803,6 +803,7 @@ dewlap dextrous dharmachakra diaspora +dichloroisocyanurate didn dieselpunk diffract From 717630e2cfc00dd58d1ef3dd3de40e4f63cf2618 Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:05:35 -0500 Subject: [PATCH 07/12] Fix handle_liquid consuming charges when you back out --- src/handle_liquid.cpp | 18 ++++++++++++++---- src/handle_liquid.h | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/handle_liquid.cpp b/src/handle_liquid.cpp index 0ce6825b9a9ff..8295f81fcb53d 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( *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 From 5f216d4a68ce4a38271a1ebcf194639622a0e04a Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:22:50 -0500 Subject: [PATCH 08/12] Fix EOC test that expected alcohol to be consumed instantly --- data/mods/TEST_DATA/EOC.json | 12 +++++++++--- tests/eoc_test.cpp | 11 +++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/data/mods/TEST_DATA/EOC.json b/data/mods/TEST_DATA/EOC.json index b91527e9f607b..aa206984c8f06 100644 --- a/data/mods/TEST_DATA/EOC.json +++ b/data/mods/TEST_DATA/EOC.json @@ -284,15 +284,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/tests/eoc_test.cpp b/tests/eoc_test.cpp index 866d0c85d1fcb..c5bfe8d5e2149 100644 --- a/tests/eoc_test.cpp +++ b/tests/eoc_test.cpp @@ -45,7 +45,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_setup( "EOC_math_addiction_setup" ); +static const effect_on_condition_id +effect_on_condition_EOC_math_addiction_check( "EOC_math_addiction_check" ); static const effect_on_condition_id effect_on_condition_EOC_math_armor( "EOC_math_armor" ); static const effect_on_condition_id @@ -444,7 +446,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" ); } From e0650e55d3ec5e195738e83d71ba0b02b5303578 Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:31:39 -0500 Subject: [PATCH 09/12] Rewqrk how comestibles are activated --- src/character.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 33cb0b9855949..2590d13b8d2ba 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7328,6 +7328,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() ) { @@ -7341,16 +7352,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() || From a4ec2ad79722da13b485ff3de5454ad906e7107d Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Fri, 1 Mar 2024 15:35:09 -0500 Subject: [PATCH 10/12] Move the ratio of tablets:water to json --- data/json/items/chemicals_and_resources.json | 4 +++- src/iuse.cpp | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/data/json/items/chemicals_and_resources.json b/data/json/items/chemicals_and_resources.json index ff0ccf5e7c4f0..1300be74bd4a5 100644 --- a/data/json/items/chemicals_and_resources.json +++ b/data/json/items/chemicals_and_resources.json @@ -1152,7 +1152,9 @@ "price": "9 cent", "price_postapoc": "20 cent", "volume": "2 ml", - "flags": [ "NO_INGEST" ] + "flags": [ "NO_INGEST" ], + "//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/src/iuse.cpp b/src/iuse.cpp index 1719ab4398ab1..7dd43847091e8 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -2532,8 +2532,14 @@ std::optional iuse::water_purifier( Character *p, item *it, const tripoint // 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 ) { - // TODO: Find a way to move this to json: - const int max_water_per_tablet = 4; + 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; From a0fc3ddaae1ca6183279e00d03c5fb9626f1949a Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Sat, 2 Mar 2024 14:21:22 -0500 Subject: [PATCH 11/12] Increase spawning for water purification tablets to ~50/pack --- .../Locations_MapExtras/airdrop.json | 2 +- .../Locations_MapExtras/locations.json | 4 ++-- .../locations_commercial.json | 2 +- .../locations_mapextras.json | 2 +- data/json/itemgroups/activities_hobbies.json | 8 +++---- .../json/itemgroups/collections_domestic.json | 2 +- data/json/itemgroups/military.json | 2 +- data/json/itemgroups/science_and_tech.json | 4 ++-- data/json/itemgroups/tools.json | 22 +++++++++---------- .../mapgen/military/mil_base/mil_base_z0.json | 2 +- data/json/npcs/godco/classes.json | 2 +- data/json/npcs/items_generic.json | 6 ++--- 12 files changed, 29 insertions(+), 29 deletions(-) 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 f253709b0e5c2..8e7cde3d84658 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 }, @@ -2571,7 +2571,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 2726a5bf1e7c3..7fe7a865882c1 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations_commercial.json +++ b/data/json/itemgroups/Locations_MapExtras/locations_commercial.json @@ -758,7 +758,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 f55390f57d59c..2ecc1b779b6b8 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json +++ b/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json @@ -180,7 +180,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 cfe5e4ce7c2f1..7aaf795fe1056 100644 --- a/data/json/itemgroups/activities_hobbies.json +++ b/data/json/itemgroups/activities_hobbies.json @@ -411,7 +411,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 ], @@ -547,7 +547,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 ], @@ -746,10 +746,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 99e1583ca7b24..e1c513761a57d 100644 --- a/data/json/itemgroups/collections_domestic.json +++ b/data/json/itemgroups/collections_domestic.json @@ -218,7 +218,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-min": 100 }, { "item": "oxy_powder", "prob": 24, "charges-min": 100 }, { "item": "chemistry_set", "prob": 10 }, diff --git a/data/json/itemgroups/military.json b/data/json/itemgroups/military.json index 758b0338b9939..431c3725d9eca 100644 --- a/data/json/itemgroups/military.json +++ b/data/json/itemgroups/military.json @@ -852,7 +852,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 ec3e9d2848631..8132398a70dbd 100644 --- a/data/json/itemgroups/science_and_tech.json +++ b/data/json/itemgroups/science_and_tech.json @@ -101,7 +101,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 ], { "prob": 2, "group": "antiparasitic_bottle_plastic_pill_prescription_1_10" }, @@ -198,7 +198,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 82a7410237afe..9e23d37e0c81d 100644 --- a/data/json/itemgroups/tools.json +++ b/data/json/itemgroups/tools.json @@ -252,7 +252,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 ], @@ -460,7 +460,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" } ] }, @@ -475,7 +475,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 } ] }, @@ -583,7 +583,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" } ] @@ -600,7 +600,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 } ] @@ -965,19 +965,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", @@ -1013,11 +1013,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/mapgen/military/mil_base/mil_base_z0.json b/data/json/mapgen/military/mil_base/mil_base_z0.json index 1c9afcde9f8a2..0030e0be2cb75 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 e7a7e09c11b1e..a0b6e0e89b75d 100644 --- a/data/json/npcs/godco/classes.json +++ b/data/json/npcs/godco/classes.json @@ -1038,7 +1038,7 @@ { "item": "gasoline_lantern", "prob": 7, "charges-min": 100 }, { "item": "oil_lamp", "prob": 7, "charges-min": 100 }, { "item": "lamp_oil", "prob": 5, "charges-min": 100 }, - { "item": "pur_tablets", "prob": 10, "count": [ 5, 15 ] }, + { "item": "pur_tablets", "prob": 10, "count": [ 15, 50 ] }, { "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 2484dbb5acbf5..edc7c4db5da9c 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 ], @@ -1148,10 +1148,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 } ] } ] From efeb1e497b382d2a24cc603c79b7f50ac2492dbd Mon Sep 17 00:00:00 2001 From: ZeroInternalReflection <89038572+ZeroInternalReflection@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:10:43 -0500 Subject: [PATCH 12/12] Fix clang-tidy errors --- src/character.cpp | 2 +- tests/eoc_test.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 2590d13b8d2ba..68fc2bc8a1ab9 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7336,7 +7336,7 @@ bool Character::invoke_item( item *used, const std::string &method, const tripoi 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() ); + return !activity.is_null(); } std::optional charges_used = actually_used->type->invoke( this, *actually_used, diff --git a/tests/eoc_test.cpp b/tests/eoc_test.cpp index c5bfe8d5e2149..d4be1fa4b3ead 100644 --- a/tests/eoc_test.cpp +++ b/tests/eoc_test.cpp @@ -45,10 +45,10 @@ 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_setup( "EOC_math_addiction_setup" ); -static const effect_on_condition_id 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 effect_on_condition_EOC_math_diag_assign( "EOC_math_diag_assign" );