From 1cfe8ce1b2841f9856f5a53d71711e48629df711 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Sun, 8 Mar 2020 14:48:32 -0400 Subject: [PATCH] create new item_location::item_in_container --- src/game.cpp | 5 ++ src/handle_liquid.cpp | 1 + src/item_contents.cpp | 22 +++++++ src/item_contents.h | 5 ++ src/item_location.cpp | 114 +++++++++++++++++++++++++++++++++++ src/item_location.h | 7 ++- tests/item_location_test.cpp | 26 ++++++++ 7 files changed, 179 insertions(+), 1 deletion(-) diff --git a/src/game.cpp b/src/game.cpp index f1fc5b89b5366..33d165b536ea6 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -8636,6 +8636,11 @@ void game::wield( item_location &loc ) loc.remove_item(); if( !u.wield( to_wield ) ) { switch( location_type ) { + case item_location::type::container: + // this will not cause things to spill, as it is inside another item + loc = loc.obtain( g->u ); + wield( loc ); + break; case item_location::type::character: if( worn_index != INT_MIN ) { auto it = u.worn.begin(); diff --git a/src/handle_liquid.cpp b/src/handle_liquid.cpp index b80bbd4ee9351..1854b45ddff7a 100644 --- a/src/handle_liquid.cpp +++ b/src/handle_liquid.cpp @@ -352,6 +352,7 @@ static bool perform_liquid_transfer( item &liquid, const tripoint *const source_ case item_location::type::vehicle: g->m.veh_at( target.item_loc.position() )->vehicle().make_active( target.item_loc ); break; + case item_location::type::container: case item_location::type::character: case item_location::type::invalid: break; diff --git a/src/item_contents.cpp b/src/item_contents.cpp index ebaa61f6c8dbb..29d18fc93722d 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -144,6 +144,28 @@ std::list item_contents::all_items_top() const return ret; } +std::list item_contents::all_items_ptr() +{ + std::list ret; + for( item &it : items ) { + ret.push_back( &it ); + std::list inside = it.contents.all_items_ptr(); + ret.insert( ret.end(), inside.begin(), inside.end() ); + } + return ret; +} + +std::list item_contents::all_items_ptr() const +{ + std::list ret; + for( const item &it : items ) { + ret.push_back( &it ); + std::list inside = it.contents.all_items_ptr(); + ret.insert( ret.end(), inside.begin(), inside.end() ); + } + return ret; +} + std::vector item_contents::gunmods() { std::vector res; diff --git a/src/item_contents.h b/src/item_contents.h index c1a3a944b7259..06dcd6601e734 100644 --- a/src/item_contents.h +++ b/src/item_contents.h @@ -36,6 +36,11 @@ class item_contents /** returns a list of pointers to all top-level items */ std::list all_items_top() const; + // returns a list of pointers to all items inside recursively + std::list all_items_ptr(); + // returns a list of pointers to all items inside recursively + std::list all_items_ptr() const; + /** gets all gunmods in the item */ std::vector gunmods(); /** gets all gunmods in the item */ diff --git a/src/item_location.cpp b/src/item_location.cpp index 29c3862f7e15c..b706ba30b4e8c 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -62,6 +62,7 @@ class item_location::impl class item_on_map; class item_on_person; class item_on_vehicle; + class item_in_container; class nowhere; impl() = default; @@ -71,6 +72,9 @@ class item_location::impl virtual ~impl() = default; virtual type where() const = 0; + virtual item_location parent_item() const { + return item_location(); + } virtual tripoint position() const = 0; virtual std::string describe( const Character * ) const = 0; virtual item_location obtain( Character &, int ) = 0; @@ -477,6 +481,97 @@ class item_location::impl::item_on_vehicle : public item_location::impl } }; +class item_location::impl::item_in_container : public item_location::impl +{ + private: + item_location container; + + // figures out the index for the item, which is where it is in the total list of contents + // note: could be a better way of handling this? + int calc_index() const { + int idx = 0; + for( const item *it : container->contents.all_items_top() ) { + if( target() == it ) { + return idx; + } + idx++; + } + if( container->contents.empty() ) { + return -1; + } + return idx; + } + public: + item_location parent_item() const override { + return container; + } + + item_in_container( const item_location &container, item *which ) : + impl( which ), container( container ) {} + + void serialize( JsonOut &js ) const override { + js.start_object(); + js.member( "idx", calc_index() ); + js.member( "type", "in_container" ); + js.member( "parent", container ); + js.end_object(); + } + + item *unpack( int idx ) const override { + if( idx < 0 || static_cast( idx ) >= target()->contents.num_item_stacks() ) { + return nullptr; + } + std::list all_items = container->contents.all_items_ptr(); + auto iter = all_items.begin(); + std::advance( iter, idx ); + if( iter != all_items.end() ) { + return const_cast( *iter ); + } else { + return nullptr; + } + } + + std::string describe( const Character * ) const override { + if( !target() ) { + return std::string(); + } + return string_format( _( "inside %s" ), container->tname() ); + } + + type where() const override { + return type::container; + } + + tripoint position() const override { + return container.position(); + } + + void remove_item() override { + container->remove_item( *target() ); + } + + item_location obtain( Character &ch, int qty ) override { + ch.mod_moves( -obtain_cost( ch, qty ) ); + + item obj = target()->split( qty ); + if( !obj.is_null() ) { + return item_location( ch, &ch.i_add( obj, should_stack ) ); + } else { + item *inv = &ch.i_add( *target(), should_stack ); + remove_item(); + return item_location( ch, inv ); + } + } + + int obtain_cost( const Character &ch, int qty ) const override { + if( !target() ) { + return 0; + } + // a temporary measure before pockets + return INVENTORY_HANDLING_PENALTY + container.obtain_cost( ch, qty ); + } +}; + const item_location item_location::nowhere; item_location::item_location() @@ -491,6 +586,9 @@ item_location::item_location( Character &ch, item *which ) item_location::item_location( const vehicle_cursor &vc, item *which ) : ptr( new impl::item_on_vehicle( vc, which ) ) {} +item_location::item_location( const item_location &container, item *which ) + : ptr( new impl::item_in_container( container, which ) ) {} + bool item_location::operator==( const item_location &rhs ) const { return ptr->target() == rhs.ptr->target(); @@ -562,7 +660,23 @@ void item_location::deserialize( JsonIn &js ) if( veh && part >= 0 && part < static_cast( veh->parts.size() ) ) { ptr.reset( new impl::item_on_vehicle( vehicle_cursor( *veh, part ), idx ) ); } + } else if( type == "in_container" ) { + item_location parent; + obj.read( "parent", parent ); + const std::list parent_contents = parent->contents.all_items_top(); + auto iter = parent_contents.begin(); + std::advance( iter, idx ); + ptr.reset( new impl::item_in_container( parent, *iter ) ); + } +} + +item_location item_location::parent_item() const +{ + if( where() == type::container ) { + return ptr->parent_item(); } + debugmsg( "this item location type has no parent" ); + return item_location::nowhere; } item_location::type item_location::where() const diff --git a/src/item_location.h b/src/item_location.h index 9e36e12a8f7fc..d1660339222dd 100644 --- a/src/item_location.h +++ b/src/item_location.h @@ -27,7 +27,8 @@ class item_location invalid = 0, character = 1, map = 2, - vehicle = 3 + vehicle = 3, + container = 4 }; item_location(); @@ -37,6 +38,7 @@ class item_location item_location( Character &ch, item *which ); item_location( const map_cursor &mc, item *which ); item_location( const vehicle_cursor &vc, item *which ); + item_location( const item_location &container, item *which ); void serialize( JsonOut &js ) const; void deserialize( JsonIn &js ); @@ -85,6 +87,9 @@ class item_location void set_should_stack( bool should_stack ) const; + /** returns the parent item, or an invalid location if it has no parent */ + item_location parent_item() const; + private: class impl; diff --git a/tests/item_location_test.cpp b/tests/item_location_test.cpp index d302f3492c1a3..8ea770a3c9074 100644 --- a/tests/item_location_test.cpp +++ b/tests/item_location_test.cpp @@ -2,6 +2,7 @@ #include #include +#include "avatar.h" #include "catch/catch.hpp" #include "game.h" #include "item.h" @@ -64,3 +65,28 @@ TEST_CASE( "item_location_doesnt_return_stale_map_item", "[item][item_location]" m.add_item( pos, item( "jeans" ) ); CHECK( !item_loc ); } + +TEST_CASE( "item_in_container", "[item][item_location]" ) +{ + avatar &dummy = g->u; + item &backpack = dummy.i_add( item( "backpack" ) ); + item jeans( "jeans" ); + + REQUIRE( dummy.has_item( backpack ) ); + + backpack.put_in( jeans ); + + item_location backpack_loc( dummy, & **dummy.wear( backpack ) ); + + REQUIRE( dummy.has_item( *backpack_loc ) ); + + item_location jeans_loc( backpack_loc, &jeans ); + + REQUIRE( backpack_loc.where() == item_location::type::character ); + REQUIRE( jeans_loc.where() == item_location::type::container ); + + CHECK( backpack_loc.obtain_cost( dummy ) + INVENTORY_HANDLING_PENALTY == jeans_loc.obtain_cost( + dummy ) ); + + CHECK( jeans_loc.parent_item() == backpack_loc ); +}