From 946365f32e39cedad9c58132726b8af4ceff45dd Mon Sep 17 00:00:00 2001 From: EddiTheBambi Date: Fri, 16 Aug 2019 09:51:21 +0200 Subject: [PATCH] Allow player to pick up frozen liquids (#32763) * allow player to pick frozen liquids Players should be able to chip or crush ice or other frozen liquids and pick them up and keep them in their inventory as long as they're frozen --- src/advanced_inv.cpp | 2 +- src/character.cpp | 5 ++++- src/game.cpp | 6 ++++++ src/item.cpp | 10 +++++----- src/item.h | 4 ++++ src/pickup.cpp | 13 ++++++++----- src/player.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- src/player.h | 9 +++++++++ 8 files changed, 79 insertions(+), 13 deletions(-) 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;