diff --git a/data/json/items/tool/cooking.json b/data/json/items/tool/cooking.json index 01a694f8d2d88..33178118897c8 100644 --- a/data/json/items/tool/cooking.json +++ b/data/json/items/tool/cooking.json @@ -132,6 +132,7 @@ "max_charges": 150, "charges_per_use": 1, "use_action": [ "WATER_PURIFIER" ], + "purification_factor": 2, "flags": [ "ALLOWS_REMOTE_USE" ] }, { diff --git a/data/json/items/tool/misc.json b/data/json/items/tool/misc.json index bf807c931a152..c6990be06ed68 100644 --- a/data/json/items/tool/misc.json +++ b/data/json/items/tool/misc.json @@ -428,7 +428,8 @@ "initial_charges": 4000, "max_charges": 4000, "charges_per_use": 1, - "use_action": [ "WATER_PURIFIER" ] + "use_action": [ "WATER_PURIFIER" ], + "purification_factor": 4 }, { "id": "makeshift_halberd", diff --git a/data/json/player_activities.json b/data/json/player_activities.json index 082d3456474dc..3558aacb42045 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -860,6 +860,15 @@ "based_on": "neither", "no_resume": true }, + { + "id": "ACT_PURIFY_WATER", + "type": "activity_type", + "activity_level": "NO_EXERCISE", + "verb": "purifying the water", + "rooted": true, + "based_on": "time", + "auto_needs": true + }, { "id": "ACT_CONSUME", "type": "activity_type", diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 121f6a8935e23..be160cc92d04c 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -2462,6 +2462,7 @@ CBMs can be defined like this: "cooks_like": "meat_cooked", // (Optional) If the item is used in a recipe, replaces it with its cooks_like "parasites": 10, // (Optional) Probability of becoming parasitised when eating "contamination": [ { "disease": "bad_food", "probability": 5 } ], // (Optional) List of diseases carried by this comestible and their associated probability. Values must be in the [0, 100] range. +"purification_factor": 0, // (Optional) If item is used to purify water, additional multiplicative factor to the time taken. 0 results in a constant time. "vitamins": [ [ "calcium", 5 ], [ "iron", 12 ] ], // Vitamins provided by consuming a charge (portion) of this. An integer percentage of ideal daily value average. Vitamins array keys include the following: calcium, iron, vitA, vitB, vitC, mutant_toxin, bad_food, blood, and redcells. Note that vitB is B12. ``` @@ -2630,6 +2631,7 @@ Alternately, every item (book, tool, armor, even food) can be used as a gunmod i "ammo": [ "NULL" ], // Ammo types used for reloading "revert_to": "torch_done", // Transforms into item when charges are expended "use_action": [ "firestarter" ] // Action performed when tool is used, see special definition below +"purification_factor": 1 // (OPTIONAL) If item is used to purify water, additional multiplicative factor to the time taken. 0 results in a constant time. ``` ### Seed Data diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 23caf6c078959..91625b0347343 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -58,6 +58,7 @@ static const efftype_id effect_sleep( "sleep" ); static const itype_id itype_bone_human( "bone_human" ); static const itype_id itype_electrohack( "electrohack" ); static const itype_id itype_pseudo_bio_picklock( "pseudo_bio_picklock" ); +static const itype_id itype_water_clean( "water_clean" ); static const skill_id skill_computer( "computer" ); static const skill_id skill_lockpick( "lockpick" ); @@ -1350,6 +1351,46 @@ std::unique_ptr try_sleep_activity_actor::deserialize( JsonIn &j return actor.clone(); } +void purify_water_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = moves; + act.moves_left = moves; +} + +void purify_water_activity_actor::finish( player_activity &act, Character & ) +{ + if( liquid ) { + liquid->convert( itype_water_clean ).poison = 0; + } else { + debugmsg( "Lost target item of ACT_PURIFY_WATER" ); + } + + act.set_to_null(); +} + +void purify_water_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + + jsout.member( "moves", moves ); + jsout.member( "liquid", liquid ); + + jsout.end_object(); +} + +std::unique_ptr purify_water_activity_actor::deserialize( JsonIn &jsin ) +{ + purify_water_activity_actor actor( item_location(), 1 ); + + JsonObject data = jsin.get_object(); + + data.read( "moves", actor.moves ); + data.read( "liquid", actor.liquid ); + + return actor.clone(); +} + + void unload_mag_activity_actor::start( player_activity &act, Character & ) { act.moves_left = moves_total; @@ -1834,6 +1875,7 @@ deserialize_functions = { { activity_id( "ACT_MOVE_ITEMS" ), &move_items_activity_actor::deserialize }, { activity_id( "ACT_OPEN_GATE" ), &open_gate_activity_actor::deserialize }, { activity_id( "ACT_PICKUP" ), &pickup_activity_actor::deserialize }, + { activity_id( "ACT_PURIFY_WATER" ), &purify_water_activity_actor::deserialize }, { activity_id( "ACT_TRY_SLEEP" ), &try_sleep_activity_actor::deserialize }, { activity_id( "ACT_UNLOAD_MAG" ), &unload_mag_activity_actor::deserialize }, { activity_id( "ACT_WORKOUT_HARD" ), &workout_activity_actor::deserialize }, diff --git a/src/activity_actor.h b/src/activity_actor.h index 09d22769651e2..0ef543ca63ebb 100644 --- a/src/activity_actor.h +++ b/src/activity_actor.h @@ -576,6 +576,32 @@ class consume_activity_actor : public activity_actor static std::unique_ptr deserialize( JsonIn &jsin ); }; +class purify_water_activity_actor : public activity_actor +{ + private: + item_location liquid; + int moves; + + public: + purify_water_activity_actor( item_location liquid, int moves = 1 ) : liquid( liquid ), + moves( moves ) {} + + activity_id get_type() const override { + return activity_id( "ACT_PURIFY_WATER" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &, Character & ) override {}; + void finish( player_activity &, Character & ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr deserialize( JsonIn &jsin ); +}; + class try_sleep_activity_actor : public activity_actor { private: diff --git a/src/item_factory.cpp b/src/item_factory.cpp index bf9dd0d0ddb63..ed534dfda7f7e 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -1988,6 +1988,7 @@ void Item_factory::load( islot_tool &slot, const JsonObject &jo, const std::stri assign( jo, "revert_to", slot.revert_to, strict ); assign( jo, "revert_msg", slot.revert_msg, strict ); assign( jo, "sub", slot.subtype, strict ); + assign( jo, "purification_factor", slot.purification_factor, strict ); if( jo.has_array( "rand_charges" ) ) { if( jo.has_member( "initial_charges" ) ) { @@ -2152,6 +2153,7 @@ void Item_factory::load( islot_comestible &slot, const JsonObject &jo, const std assign( jo, "spoils_in", slot.spoils, strict, 1_hours ); assign( jo, "cooks_like", slot.cooks_like, strict ); assign( jo, "smoking_result", slot.smoking_result, strict ); + assign( jo, "purification_factor", slot.purification_factor, strict ); for( const JsonObject &jsobj : jo.get_array( "contamination" ) ) { slot.contamination.emplace( diseasetype_id( jsobj.get_string( "disease" ) ), diff --git a/src/itype.h b/src/itype.h index e78ad628b4ab1..ee0e42456d529 100644 --- a/src/itype.h +++ b/src/itype.h @@ -111,6 +111,7 @@ struct islot_tool { int charges_per_use = 0; int turns_per_charge = 0; int power_draw = 0; + int purification_factor = 1; std::vector rand_charges; }; @@ -196,6 +197,9 @@ struct islot_comestible { /** Chance the above monster group spawns*/ int rot_spawn_chance = 10; + /** Additional multiplicative time factor for purifying water. 0 means a constant time */ + int purification_factor = 0; + private: /** effect on morale when consuming */ int fun = 0; diff --git a/src/iuse.cpp b/src/iuse.cpp index c42eae0db2216..5c72461c5b73c 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -2227,7 +2227,26 @@ int iuse::water_purifier( player *p, item *it, bool, const tripoint & ) return 0; } - p->moves -= to_moves( 2_seconds ); + int factor; + if( it->type->tool ) { + factor = it->type->tool->purification_factor; + } else if( it->type->comestible ) { + factor = it->type->comestible->purification_factor; + } else { + debugmsg( "ERROR: Water purifier not a tool or comestible" ); + return 0; + } + + /** A factor of 0 represents a constant time to purify, such as with water purification tablets. */ + if( factor == 0 ) { + p->assign_activity( player_activity( purify_water_activity_actor( item_location( *p, + &obj->contents.front() ), + to_moves( 30_minutes ) ) ) ); + } else { + p->assign_activity( player_activity( purify_water_activity_actor( item_location( *p, + &obj->contents.front() ), + to_moves( 2_minutes * liquid.charges * factor ) ) ) ); + } for( item *water : liquids ) { water->convert( itype_water_clean ).poison = 0;