diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp index e325cd8689a7b..5227387b1c831 100644 --- a/src/advanced_inv.cpp +++ b/src/advanced_inv.cpp @@ -2162,7 +2162,7 @@ bool advanced_inventory::query_charges( aim_location destarea, const advanced_in amount = input_amount; // Includes moving from/to inventory and around on the map. - if( it.made_of_from_type( LIQUID ) ) { + if( it.made_of_from_type( LIQUID ) && !it.is_frozen_liquid() ) { popup( _( "You can't pick up a liquid." ) ); redraw = true; return false; diff --git a/src/character.cpp b/src/character.cpp index 91cd625dcb56d..acf0871d0dba2 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -1115,9 +1115,12 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes return VisitResponse::SKIP; } if( !node->made_of( SOLID ) ) { - // some liquids are ammo but we can't reload with them unless within a container + // some liquids are ammo but we can't reload with them unless within a container or frozen return VisitResponse::SKIP; } + if( node->is_frozen_liquid() ) { + out = item_location( src, node ); + } if( node->is_ammo_container() && !node->contents.empty() && !node->contents_made_of( SOLID ) ) { for( const ammotype &at : ammo ) { diff --git a/src/game.cpp b/src/game.cpp index 1fead92b11581..c3e55921452e5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -8538,6 +8538,12 @@ void game::reload( item_location &loc, bool prompt, bool empty ) item::reload_option( &u, it, it, u.ammo_location ) : u.select_ammo( *it, prompt, empty ); + if( opt.ammo.get_item() != nullptr && opt.ammo.get_item()->is_frozen_liquid() ) { + if( !u.crush_frozen_liquid( opt.ammo ) ) { + return; + } + } + if( opt ) { u.assign_activity( activity_id( "ACT_RELOAD" ), opt.moves(), opt.qty() ); if( use_loc ) { diff --git a/src/item.cpp b/src/item.cpp index 0409a7a3c824c..424e661cfed2e 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -545,6 +545,11 @@ bool item::is_unarmed_weapon() const return has_flag( "UNARMED_WEAPON" ) || is_null(); } +bool item::is_frozen_liquid() const +{ + return made_of( SOLID ) && made_of_from_type( LIQUID ); +} + bool item::covers( const body_part bp ) const { return get_covered_body_parts().test( bp ); @@ -6631,11 +6636,6 @@ bool item::reload( player &u, item_location loc, int qty ) debugmsg( "Tried to reload liquid container with non-liquid." ); return false; } - if( !ammo->made_of( LIQUID ) ) { - u.add_msg_if_player( m_bad, _( "The %s froze solid before you could finish." ), - ammo->tname() ); - return false; - } if( container ) { container->on_contents_changed(); } diff --git a/src/item.h b/src/item.h index 6e8d1461fde01..10969c2dc2f20 100644 --- a/src/item.h +++ b/src/item.h @@ -1084,6 +1084,10 @@ class item : public visitable bool is_unarmed_weapon() const; //Returns true if the item should be considered unarmed bool has_temperature() const; + + /** Returns true if the item is A: is SOLID and if it B: is of type LIQUID */ + bool is_frozen_liquid() const; + float get_specific_heat_liquid() const; float get_specific_heat_solid() const; float get_latent_heat() const; diff --git a/src/pickup.cpp b/src/pickup.cpp index f517d5bc9f349..3a4cb4ba3f758 100644 --- a/src/pickup.cpp +++ b/src/pickup.cpp @@ -181,7 +181,8 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer // We already checked in do_pickup if this was a nullptr // Make copies so the original remains untouched if we bail out - item newit = *loc.get_item(); + item_location newloc = loc; + item newit = *newloc.get_item(); item leftovers = newit; const auto wield_check = u.can_wield( newit ); @@ -213,8 +214,10 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer if( newit.is_ammo() && newit.charges == 0 ) { picked_up = true; option = NUM_ANSWERS; //Skip the options part - } else if( newit.made_of_from_type( LIQUID ) ) { - got_water = true; + } else if( newit.is_frozen_liquid() ) { + if( !( got_water = !( u.crush_frozen_liquid( newloc ) ) ) ) { + option = STASH; + } } else if( !u.can_pickWeight( newit, false ) ) { if( !autopickup ) { const std::string &explain = string_format( _( "The %s is too heavy!" ), @@ -388,11 +391,11 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) bool isEmpty = ( g->m.i_at( p ).empty() ); // Hide the pickup window if this is a toilet and there's nothing here - // but water. + // but non-frozen water. if( ( !isEmpty ) && g->m.furn( p ) == f_toilet ) { isEmpty = true; for( const item &maybe_water : g->m.i_at( p ) ) { - if( maybe_water.typeId() != "water" ) { + if( maybe_water.typeId() != "water" || maybe_water.is_frozen_liquid() ) { isEmpty = false; break; } diff --git a/src/player.cpp b/src/player.cpp index 3d09c6ef2425b..bd3149cf7eb6f 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -8108,7 +8108,7 @@ void player::mend_item( item_location &&obj, bool interactive ) int player::item_reload_cost( const item &it, const item &ammo, int qty ) const { - if( ammo.is_ammo() ) { + if( ammo.is_ammo() || ammo.is_frozen_liquid() ) { qty = std::max( std::min( ammo.charges, qty ), 1 ); } else if( ammo.is_ammo_container() || ammo.is_container() ) { qty = std::max( std::min( ammo.contents.front().charges, qty ), 1 ); @@ -11647,6 +11647,47 @@ std::vector player::all_items_with_flag( const std::string &flag ) } ); } +item &player::item_with_best_of_quality( const quality_id &qid ) +{ + int maxq = max_quality( qid ); + auto items_with_quality = items_with( [qid]( const item & it ) { + return it.has_quality( qid ); + } ); + for( item *it : items_with_quality ) { + if( it->get_quality( qid ) == maxq ) { + return *it; + } + } + return null_item_reference(); +} + +bool player::crush_frozen_liquid( item_location loc ) +{ + + player &u = g->u; + + if( u.has_quality( quality_id( "HAMMER" ) ) ) { + item hammering_item = u.item_with_best_of_quality( quality_id( "HAMMER" ) ); + if( query_yn( _( "Do you want to crush up %s with your %s?\n%s" ), loc.get_item()->display_name(), + hammering_item.tname(), + colorize( _( "Be wary of fragile items nearby!" ), c_red ) ) ) { + + //Risk smashing tile with hammering tool, risk is lower with higher dex, damage lower with lower strength + if( one_in( 1 + u.dex_cur / 4 ) ) { + add_msg_if_player( colorize( _( "You swing your %s wildly!" ), c_red ), + hammering_item.tname() ); + int smashskill = u.str_cur + hammering_item.damage_melee( DT_BASH ); + g->m.bash( loc.position(), smashskill ); + } + add_msg_if_player( _( "You crush up and gather %s" ), loc.get_item()->display_name() ); + return true; + } + } else { + popup( _( "You need a hammering tool to crush up frozen liquids!" ) ); + } + return false; +} + bool player::has_item_with_flag( const std::string &flag, bool need_charges ) const { return has_item_with( [&flag, &need_charges]( const item & it ) { diff --git a/src/player.h b/src/player.h index 1bf85193d9e56..54eed67516480 100644 --- a/src/player.h +++ b/src/player.h @@ -1339,6 +1339,15 @@ class player : public Character // Carried items may leak radiation or chemicals int leak_level( const std::string &flag ) const; + /** Returns the item in the player's inventory with the highest of the specified quality*/ + item &item_with_best_of_quality( const quality_id &qid ); + + /** + * Prompts user about crushing item at item_location loc, for harvesting of frozen liquids + * @param loc Location for item to crush + */ + bool crush_frozen_liquid( item_location loc ); + // Has a weapon, inventory item or worn item with flag bool has_item_with_flag( const std::string &flag, bool need_charges = false ) const;