1 #include "item_location.h"
2
3 #include <algorithm>
4 #include <cstddef>
5 #include <functional>
6 #include <iosfwd>
7 #include <iterator>
8 #include <list>
9 #include <string>
10 #include <vector>
11
12 #include "character.h"
13 #include "character_id.h"
14 #include "color.h"
15 #include "debug.h"
16 #include "game.h"
17 #include "game_constants.h"
18 #include "item.h"
19 #include "item_contents.h"
20 #include "item_pocket.h"
21 #include "json.h"
22 #include "line.h"
23 #include "map.h"
24 #include "map_selector.h"
25 #include "optional.h"
26 #include "point.h"
27 #include "ret_val.h"
28 #include "safe_reference.h"
29 #include "string_formatter.h"
30 #include "translations.h"
31 #include "units.h"
32 #include "vehicle.h"
33 #include "vehicle_selector.h"
34 #include "visitable.h"
35 #include "vpart_position.h"
36
37 template <typename T>
find_index(const T & sel,const item * obj)38 static int find_index( const T &sel, const item *obj )
39 {
40 int idx = -1;
41 sel.visit_items( [&idx, &obj]( const item * e, item * ) {
42 idx++;
43 if( e == obj ) {
44 return VisitResponse::ABORT;
45 }
46 return VisitResponse::NEXT;
47 } );
48 return idx;
49 }
50
51 template <typename T>
retrieve_index(const T & sel,int idx)52 static item *retrieve_index( const T &sel, int idx )
53 {
54 item *obj = nullptr;
55 sel.visit_items( [&idx, &obj]( const item * e, item * ) {
56 if( idx-- == 0 ) {
57 obj = const_cast<item *>( e );
58 return VisitResponse::ABORT;
59 }
60 return VisitResponse::NEXT;
61 } );
62 return obj;
63 }
64
65 class item_location::impl
66 {
67 public:
68 class item_in_container;
69 class item_on_map;
70 class item_on_person;
71 class item_on_vehicle;
72 class nowhere;
73
74 impl() = default;
impl(item * i)75 explicit impl( item *i ) : what( i->get_safe_reference() ) {}
impl(int idx)76 explicit impl( int idx ) : idx( idx ), needs_unpacking( true ) {}
77
78 virtual ~impl() = default;
79
80 virtual type where() const = 0;
where_recursive() const81 virtual type where_recursive() const {
82 return where();
83 }
parent_item() const84 virtual item_location parent_item() const {
85 return item_location();
86 }
87 virtual tripoint position() const = 0;
88 virtual std::string describe( const Character * ) const = 0;
89 virtual item_location obtain( Character &, int ) = 0;
90 virtual units::volume volume_capacity() const = 0;
91 virtual units::mass weight_capacity() const = 0;
92 virtual int obtain_cost( const Character &, int ) const = 0;
93 virtual void remove_item() = 0;
94 virtual void on_contents_changed() = 0;
95 virtual void serialize( JsonOut &js ) const = 0;
96 virtual item *unpack( int ) const = 0;
97
target() const98 item *target() const {
99 ensure_unpacked();
100 return what.get();
101 }
102
valid() const103 virtual bool valid() const {
104 ensure_unpacked();
105 return !!what;
106 }
107
108 private:
ensure_unpacked() const109 void ensure_unpacked() const {
110 if( needs_unpacking ) {
111 if( item *i = unpack( idx ) ) {
112 what = i->get_safe_reference();
113 } else {
114 debugmsg( "item_location lost its target item during a save/load cycle" );
115 }
116 needs_unpacking = false;
117 }
118 }
119 mutable safe_reference<item> what;
120 mutable int idx = -1;
121 mutable bool needs_unpacking = false;
122
123 public:
124 //Flag that controls whether functions like obtain() should stack the obtained item
125 //with similar existing items in the inventory or create a new stack for the item
126 bool should_stack = true;
127 };
128
129 class item_location::impl::nowhere : public item_location::impl
130 {
131 public:
where() const132 type where() const override {
133 return type::invalid;
134 }
135
position() const136 tripoint position() const override {
137 debugmsg( "invalid use of nowhere item_location" );
138 return tripoint_min;
139 }
140
describe(const Character *) const141 std::string describe( const Character * ) const override {
142 debugmsg( "invalid use of nowhere item_location" );
143 return "";
144 }
145
obtain(Character &,int)146 item_location obtain( Character &, int ) override {
147 debugmsg( "invalid use of nowhere item_location" );
148 return item_location();
149 }
150
obtain_cost(const Character &,int) const151 int obtain_cost( const Character &, int ) const override {
152 debugmsg( "invalid use of nowhere item_location" );
153 return 0;
154 }
155
remove_item()156 void remove_item() override {
157 debugmsg( "invalid use of nowhere item_location" );
158 }
159
on_contents_changed()160 void on_contents_changed() override {
161 debugmsg( "invalid use of nowhere item_location" );
162 }
163
unpack(int) const164 item *unpack( int ) const override {
165 return nullptr;
166 }
167
serialize(JsonOut & js) const168 void serialize( JsonOut &js ) const override {
169 js.start_object();
170 js.member( "type", "null" );
171 js.end_object();
172 }
173
volume_capacity() const174 units::volume volume_capacity() const override {
175 return units::volume();
176 }
177
weight_capacity() const178 units::mass weight_capacity() const override {
179 return units::mass();
180 }
181 };
182
183 class item_location::impl::item_on_map : public item_location::impl
184 {
185 private:
186 map_cursor cur;
187
188 public:
item_on_map(const map_cursor & cur,item * which)189 item_on_map( const map_cursor &cur, item *which ) : impl( which ), cur( cur ) {}
item_on_map(const map_cursor & cur,int idx)190 item_on_map( const map_cursor &cur, int idx ) : impl( idx ), cur( cur ) {}
191
serialize(JsonOut & js) const192 void serialize( JsonOut &js ) const override {
193 js.start_object();
194 js.member( "type", "map" );
195 js.member( "pos", position() );
196 js.member( "idx", find_index( cur, target() ) );
197 js.end_object();
198 }
199
unpack(int idx) const200 item *unpack( int idx ) const override {
201 return retrieve_index( cur, idx );
202 }
203
where() const204 type where() const override {
205 return type::map;
206 }
207
position() const208 tripoint position() const override {
209 return cur.pos();
210 }
211
describe(const Character * ch) const212 std::string describe( const Character *ch ) const override {
213 std::string res = get_map().name( cur.pos() );
214 if( ch ) {
215 res += std::string( " " ) += direction_suffix( ch->pos(), cur.pos() );
216 }
217 return res;
218 }
219
obtain(Character & ch,int qty)220 item_location obtain( Character &ch, int qty ) override {
221 ch.moves -= obtain_cost( ch, qty );
222
223 on_contents_changed();
224 item obj = target()->split( qty );
225 const auto get_local_location = []( Character & ch, item * it ) {
226 if( ch.has_item( *it ) ) {
227 return item_location( ch, it );
228 } else {
229 return item_location{};
230 }
231 };
232 if( !obj.is_null() ) {
233 return get_local_location( ch, &ch.i_add( obj, should_stack ) );
234 } else {
235 item *inv = &ch.i_add( *target(), should_stack );
236 remove_item();
237 return get_local_location( ch, inv );
238 }
239 }
240
obtain_cost(const Character & ch,int qty) const241 int obtain_cost( const Character &ch, int qty ) const override {
242 if( !target() ) {
243 return 0;
244 }
245
246 item obj = *target();
247 obj = obj.split( qty );
248 if( obj.is_null() ) {
249 obj = *target();
250 }
251
252 int mv = ch.item_handling_cost( obj, true, MAP_HANDLING_PENALTY );
253 mv += 100 * rl_dist( ch.pos(), cur.pos() );
254
255 // TODO: handle unpacking costs
256
257 return mv;
258 }
259
remove_item()260 void remove_item() override {
261 on_contents_changed();
262 cur.remove_item( *what );
263 }
264
on_contents_changed()265 void on_contents_changed() override {
266 target()->on_contents_changed();
267 }
268
volume_capacity() const269 units::volume volume_capacity() const override {
270 map_stack stack = get_map().i_at( cur.pos() );
271 return stack.free_volume();
272 }
273
weight_capacity() const274 units::mass weight_capacity() const override {
275 return units::mass_max;
276 }
277 };
278
279 class item_location::impl::item_on_person : public item_location::impl
280 {
281 private:
282 character_id who_id;
283 mutable Character *who;
284
ensure_who_unpacked() const285 bool ensure_who_unpacked() const {
286 if( !who ) {
287 who = g->critter_by_id<Character>( who_id );
288 if( !who ) {
289 // If we failed to find it throw a debug message cause we're probably going to crash soon
290 debugmsg( "Failed to find item_location owner with character_id %d", who_id.get_value() );
291 return false;
292 }
293 }
294 return true;
295 }
296
297 public:
item_on_person(Character & who,item * which)298 item_on_person( Character &who, item *which ) : impl( which ) {
299 who_id = who.getID();
300 this->who = &who;
301 }
302
item_on_person(character_id who_id,int idx)303 item_on_person( character_id who_id, int idx ) : impl( idx ), who_id( who_id ), who( nullptr ) {}
304
serialize(JsonOut & js) const305 void serialize( JsonOut &js ) const override {
306 if( !ensure_who_unpacked() ) {
307 // Write an invalid item_location to avoid invalid json
308 js.start_object();
309 js.member( "type", "null" );
310 js.end_object();
311 return;
312 }
313 js.start_object();
314 js.member( "type", "character" );
315 js.member( "character", who_id );
316 js.member( "idx", find_index( *who, target() ) );
317 js.end_object();
318 }
319
unpack(int idx) const320 item *unpack( int idx ) const override {
321 if( !ensure_who_unpacked() ) {
322 return nullptr;
323 }
324 return retrieve_index( *who, idx );
325 }
326
where() const327 type where() const override {
328 return type::character;
329 }
330
position() const331 tripoint position() const override {
332 if( !ensure_who_unpacked() ) {
333 return tripoint_zero;
334 }
335 return who->pos();
336 }
337
describe(const Character * ch) const338 std::string describe( const Character *ch ) const override {
339 if( !target() || !ensure_who_unpacked() ) {
340 return std::string();
341 }
342
343 if( ch == who ) {
344 auto parents = who->parents( *target() );
345 if( !parents.empty() && who->is_worn( *parents.back() ) ) {
346 return parents.back()->type_name();
347
348 } else if( who->is_worn( *target() ) ) {
349 return _( "worn" );
350
351 } else {
352 return _( "inventory" );
353 }
354
355 } else {
356 return who->name;
357 }
358 }
359
obtain(Character & ch,int qty)360 item_location obtain( Character &ch, int qty ) override {
361 ch.mod_moves( -obtain_cost( ch, qty ) );
362
363 on_contents_changed();
364 if( &ch.i_at( ch.get_item_position( target() ) ) == target() ) {
365 // item already in target characters inventory at base of stack
366 return item_location( ch, target() );
367 }
368
369 item obj = target()->split( qty );
370 if( !obj.is_null() ) {
371 return item_location( ch, &ch.i_add( obj, should_stack ) );
372 } else {
373 item *inv = &ch.i_add( *target(), should_stack );
374 remove_item(); // This also takes off the item from whoever wears it.
375 return item_location( ch, inv );
376 }
377 }
378
obtain_cost(const Character & ch,int qty) const379 int obtain_cost( const Character &ch, int qty ) const override {
380 if( !target() || !ensure_who_unpacked() ) {
381 return 0;
382 }
383
384 int mv = 0;
385
386 item obj = *target();
387 obj = obj.split( qty );
388 if( obj.is_null() ) {
389 obj = *target();
390 }
391
392 item &target_ref = *target();
393 if( who->is_wielding( target_ref ) ) {
394 mv = who->item_handling_cost( obj, false, 0 );
395 } else {
396 // then we are wearing it
397 mv = who->item_handling_cost( obj, true, INVENTORY_HANDLING_PENALTY / 2 );
398 }
399
400 if( &ch != who ) {
401 // TODO: implement movement cost for transferring item between characters
402 }
403
404 return mv;
405 }
406
remove_item()407 void remove_item() override {
408 if( !ensure_who_unpacked() ) {
409 return;
410 }
411 on_contents_changed();
412 who->remove_item( *what );
413 }
414
on_contents_changed()415 void on_contents_changed() override {
416 target()->on_contents_changed();
417 }
418
valid() const419 bool valid() const override {
420 ensure_who_unpacked();
421 ensure_unpacked();
422 return !!what && !!who;
423 }
424
volume_capacity() const425 units::volume volume_capacity() const override {
426 return units::volume_max;
427 }
428
weight_capacity() const429 units::mass weight_capacity() const override {
430 return units::mass_max;
431 }
432 };
433
434 class item_location::impl::item_on_vehicle : public item_location::impl
435 {
436 private:
437 vehicle_cursor cur;
438
439 public:
item_on_vehicle(const vehicle_cursor & cur,item * which)440 item_on_vehicle( const vehicle_cursor &cur, item *which ) : impl( which ), cur( cur ) {}
item_on_vehicle(const vehicle_cursor & cur,int idx)441 item_on_vehicle( const vehicle_cursor &cur, int idx ) : impl( idx ), cur( cur ) {}
442
serialize(JsonOut & js) const443 void serialize( JsonOut &js ) const override {
444 js.start_object();
445 js.member( "type", "vehicle" );
446 js.member( "pos", position() );
447 js.member( "part", cur.part );
448 if( target() != &cur.veh.part( cur.part ).base ) {
449 js.member( "idx", find_index( cur, target() ) );
450 }
451 js.end_object();
452 }
453
unpack(int idx) const454 item *unpack( int idx ) const override {
455 return idx >= 0 ? retrieve_index( cur, idx ) : &cur.veh.part( cur.part ).base;
456 }
457
where() const458 type where() const override {
459 return type::vehicle;
460 }
461
position() const462 tripoint position() const override {
463 return cur.veh.global_part_pos3( cur.part );
464 }
465
describe(const Character * ch) const466 std::string describe( const Character *ch ) const override {
467 vpart_position part_pos( cur.veh, cur.part );
468 std::string res;
469 if( auto label = part_pos.get_label() ) {
470 res = colorize( *label, c_light_blue ) + " ";
471 }
472 if( auto cargo_part = part_pos.part_with_feature( "CARGO", true ) ) {
473 res += cargo_part->part().name();
474 } else {
475 debugmsg( "item in vehicle part without cargo storage" );
476 }
477 if( ch ) {
478 res += " " + direction_suffix( ch->pos(), part_pos.pos() );
479 }
480 return res;
481 }
482
obtain(Character & ch,int qty)483 item_location obtain( Character &ch, int qty ) override {
484 ch.moves -= obtain_cost( ch, qty );
485
486 on_contents_changed();
487 item obj = target()->split( qty );
488 if( !obj.is_null() ) {
489 return item_location( ch, &ch.i_add( obj, should_stack ) );
490 } else {
491 item *inv = &ch.i_add( *target(), should_stack );
492 remove_item();
493 return item_location( ch, inv );
494 }
495 }
496
obtain_cost(const Character & ch,int qty) const497 int obtain_cost( const Character &ch, int qty ) const override {
498 if( !target() ) {
499 return 0;
500 }
501
502 item obj = *target();
503 obj = obj.split( qty );
504 if( obj.is_null() ) {
505 obj = *target();
506 }
507
508 int mv = ch.item_handling_cost( obj, true, VEHICLE_HANDLING_PENALTY );
509 mv += 100 * rl_dist( ch.pos(), cur.veh.global_part_pos3( cur.part ) );
510
511 // TODO: handle unpacking costs
512
513 return mv;
514 }
515
remove_item()516 void remove_item() override {
517 on_contents_changed();
518 item &base = cur.veh.part( cur.part ).base;
519 if( &base == target() ) {
520 cur.veh.remove_part( cur.part ); // vehicle_part::base
521 } else {
522 cur.remove_item( *target() ); // item within CARGO
523 }
524 }
525
on_contents_changed()526 void on_contents_changed() override {
527 target()->on_contents_changed();
528 cur.veh.invalidate_mass();
529 }
530
volume_capacity() const531 units::volume volume_capacity() const override {
532 return cur.veh.free_volume( cur.part );
533 }
534
weight_capacity() const535 units::mass weight_capacity() const override {
536 return units::mass_max;
537 }
538 };
539
540 class item_location::impl::item_in_container : public item_location::impl
541 {
542 private:
543 item_location container;
544
545 // figures out the index for the item, which is where it is in the total list of contents
546 // note: could be a better way of handling this?
calc_index() const547 int calc_index() const {
548 int idx = 0;
549 for( const item *it : container->contents.all_items_top() ) {
550 if( target() == it ) {
551 return idx;
552 }
553 idx++;
554 }
555 if( container->contents.empty() ) {
556 return -1;
557 }
558 return idx;
559 }
560 public:
parent_item() const561 item_location parent_item() const override {
562 return container;
563 }
564
item_in_container(const item_location & container,item * which)565 item_in_container( const item_location &container, item *which ) :
566 impl( which ), container( container ) {}
567
serialize(JsonOut & js) const568 void serialize( JsonOut &js ) const override {
569 js.start_object();
570 js.member( "idx", calc_index() );
571 js.member( "type", "in_container" );
572 js.member( "parent", container );
573 js.end_object();
574 }
575
unpack(int idx) const576 item *unpack( int idx ) const override {
577 if( idx < 0 || static_cast<size_t>( idx ) >= target()->contents.num_item_stacks() ) {
578 return nullptr;
579 }
580 std::list<const item *> all_items = container->contents.all_items_ptr();
581 auto iter = all_items.begin();
582 std::advance( iter, idx );
583 if( iter != all_items.end() ) {
584 return const_cast<item *>( *iter );
585 } else {
586 return nullptr;
587 }
588 }
589
describe(const Character *) const590 std::string describe( const Character * ) const override {
591 if( !target() ) {
592 return std::string();
593 }
594 return string_format( _( "inside %s" ), container->tname() );
595 }
596
where() const597 type where() const override {
598 return type::container;
599 }
600
where_recursive() const601 type where_recursive() const override {
602 return container.where_recursive();
603 }
604
position() const605 tripoint position() const override {
606 return container.position();
607 }
608
remove_item()609 void remove_item() override {
610 on_contents_changed();
611 container->remove_item( *target() );
612 }
613
on_contents_changed()614 void on_contents_changed() override {
615 target()->on_contents_changed();
616 container->on_contents_changed();
617 }
618
obtain(Character & ch,const int qty)619 item_location obtain( Character &ch, const int qty ) override {
620 ch.mod_moves( -obtain_cost( ch, qty ) );
621
622 on_contents_changed();
623 if( container.held_by( ch ) ) {
624 // we don't need to move it in this case, it's in a pocket
625 // we just charge the obtain cost and leave it in place. otherwise
626 // it's liable to end up back in the same pocket, where shenanigans ensue
627 return item_location( container, target() );
628 }
629 const item obj = target()->split( qty );
630 if( !obj.is_null() ) {
631 return item_location( ch, &ch.i_add( obj, should_stack,
632 /*avoid=*/nullptr,
633 /*allow_drop=*/false ) );
634 } else {
635 item *const inv = &ch.i_add( *target(), should_stack,
636 /*avoid=*/nullptr,
637 /*allow_drop=*/false );
638 if( inv->is_null() ) {
639 debugmsg( "failed to add item to character inventory while obtaining from container" );
640 }
641 remove_item();
642 return item_location( ch, inv );
643 }
644 }
645
obtain_cost(const Character & ch,int qty) const646 int obtain_cost( const Character &ch, int qty ) const override {
647 if( !target() ) {
648 return 0;
649 }
650
651 item obj = *target();
652 obj = obj.split( qty );
653 if( obj.is_null() ) {
654 obj = *target();
655 }
656
657 const int container_mv = container->contents.obtain_cost( *target() );
658 if( container_mv == 0 ) {
659 debugmsg( "ERROR: %s does not contain %s", container->tname(), target()->tname() );
660 return 0;
661 }
662
663 int primary_cost = ch.mutation_value( "obtain_cost_multiplier" ) * ch.item_handling_cost( *target(),
664 true, container_mv );
665 int parent_obtain_cost = container.obtain_cost( ch, qty );
666 if( container->get_use( "holster" ) ) {
667 if( ch.is_worn( *container ) ) {
668 primary_cost = ch.item_retrieve_cost( *target(), *container, false, container_mv );
669 } else {
670 primary_cost = ch.item_retrieve_cost( *target(), *container );
671 }
672 // for holsters, we should not include the cost of wielding the holster itself
673 parent_obtain_cost = 0;
674 } else if( container.where() != item_location::type::container ) {
675 // a little bonus for grabbing something from what you're wearing
676 parent_obtain_cost /= 2;
677 }
678 return primary_cost + parent_obtain_cost;
679 }
680
volume_capacity() const681 units::volume volume_capacity() const override {
682 return container->contained_where( *target() )->remaining_volume();
683 }
684
weight_capacity() const685 units::mass weight_capacity() const override {
686 return container->contained_where( *target() )->remaining_weight();
687 }
688 };
689
690 const item_location item_location::nowhere;
691
item_location()692 item_location::item_location()
693 : ptr( new impl::nowhere() ) {}
694
item_location(const map_cursor & mc,item * which)695 item_location::item_location( const map_cursor &mc, item *which )
696 : ptr( new impl::item_on_map( mc, which ) ) {}
697
item_location(Character & ch,item * which)698 item_location::item_location( Character &ch, item *which )
699 : ptr( new impl::item_on_person( ch, which ) ) {}
700
item_location(const vehicle_cursor & vc,item * which)701 item_location::item_location( const vehicle_cursor &vc, item *which )
702 : ptr( new impl::item_on_vehicle( vc, which ) ) {}
703
item_location(const item_location & container,item * which)704 item_location::item_location( const item_location &container, item *which )
705 : ptr( new impl::item_in_container( container, which ) ) {}
706
operator ==(const item_location & rhs) const707 bool item_location::operator==( const item_location &rhs ) const
708 {
709 return ptr->target() == rhs.ptr->target();
710 }
711
operator !=(const item_location & rhs) const712 bool item_location::operator!=( const item_location &rhs ) const
713 {
714 return ptr->target() != rhs.ptr->target();
715 }
716
operator bool() const717 item_location::operator bool() const
718 {
719 return ptr->valid();
720 }
721
operator *()722 item &item_location::operator*()
723 {
724 return *ptr->target();
725 }
726
operator *() const727 const item &item_location::operator*() const
728 {
729 return *ptr->target();
730 }
731
operator ->()732 item *item_location::operator->()
733 {
734 return ptr->target();
735 }
736
operator ->() const737 const item *item_location::operator->() const
738 {
739 return ptr->target();
740 }
741
serialize(JsonOut & js) const742 void item_location::serialize( JsonOut &js ) const
743 {
744 ptr->serialize( js );
745 }
746
deserialize(JsonIn & js)747 void item_location::deserialize( JsonIn &js )
748 {
749 JsonObject obj = js.get_object();
750 auto type = obj.get_string( "type" );
751
752 int idx = -1;
753 tripoint pos = tripoint_min;
754
755 obj.read( "idx", idx );
756 obj.read( "pos", pos );
757
758 if( type == "character" ) {
759 character_id who_id;
760 if( obj.has_member( "character" ) ) {
761 obj.read( "character", who_id );
762 } else {
763 // This is for migrating saves before npc item locations were supported and all
764 // character item locations were assumed to be on g->u
765 who_id = get_player_character().getID();
766 }
767 ptr.reset( new impl::item_on_person( who_id, idx ) );
768
769 } else if( type == "map" ) {
770 ptr.reset( new impl::item_on_map( map_cursor( pos ), idx ) );
771
772 } else if( type == "vehicle" ) {
773 vehicle *const veh = veh_pointer_or_null( get_map().veh_at( pos ) );
774 int part = obj.get_int( "part" );
775 if( veh && part >= 0 && part < veh->part_count() ) {
776 ptr.reset( new impl::item_on_vehicle( vehicle_cursor( *veh, part ), idx ) );
777 }
778 } else if( type == "in_container" ) {
779 item_location parent;
780 obj.read( "parent", parent );
781 if( !parent.ptr->valid() ) {
782 debugmsg( "parent location does not point to valid item" );
783 ptr.reset( new impl::item_on_map( map_cursor( pos ), idx ) ); // drop on ground
784 return;
785 }
786 const std::list<item *> parent_contents = parent->contents.all_items_top();
787 if( idx > -1 && idx < static_cast<int>( parent_contents.size() ) ) {
788 auto iter = parent_contents.begin();
789 std::advance( iter, idx );
790 ptr.reset( new impl::item_in_container( parent, *iter ) );
791 } else {
792 // probably pointing to the wrong item
793 debugmsg( "contents index greater than contents size" );
794 }
795 }
796 }
797
parent_item() const798 item_location item_location::parent_item() const
799 {
800 if( where() == type::container ) {
801 return ptr->parent_item();
802 }
803 debugmsg( "this item location type has no parent" );
804 return item_location::nowhere;
805 }
806
has_parent() const807 bool item_location::has_parent() const
808 {
809 if( where() == type::container ) {
810 return !!ptr->parent_item();
811 }
812 return false;
813 }
814
parents_can_contain_recursive(item * it) const815 bool item_location::parents_can_contain_recursive( item *it ) const
816 {
817 if( !has_parent() ) {
818 return true;
819 }
820
821 item_location parent = parent_item();
822 item_pocket *pocket = parent->contents.contained_where( *get_item() );
823
824 if( pocket->can_contain( *it ).success() ) {
825 return parent.parents_can_contain_recursive( it );
826 }
827
828 return false;
829 }
830
max_charges_by_parent_recursive(const item & it) const831 int item_location::max_charges_by_parent_recursive( const item &it ) const
832 {
833 if( !has_parent() ) {
834 return item::INFINITE_CHARGES;
835 }
836
837 item_location parent = parent_item();
838 item_pocket *pocket = parent->contents.contained_where( *get_item() );
839
840 return std::min( { it.charges_per_volume( pocket->remaining_volume() ),
841 it.charges_per_weight( pocket->remaining_weight() ),
842 pocket->rigid() ? item::INFINITE_CHARGES : parent.max_charges_by_parent_recursive( it )
843 } );
844 }
845
eventually_contains(item_location loc) const846 bool item_location::eventually_contains( item_location loc ) const
847 {
848 while( loc.has_parent() ) {
849 if( ( loc = loc.parent_item() ) == *this ) {
850 return true;
851 }
852 }
853 return false;
854 }
855
where() const856 item_location::type item_location::where() const
857 {
858 return ptr->where();
859 }
860
where_recursive() const861 item_location::type item_location::where_recursive() const
862 {
863 return ptr->where_recursive();
864 }
865
position() const866 tripoint item_location::position() const
867 {
868 return ptr->position();
869 }
870
describe(const Character * ch) const871 std::string item_location::describe( const Character *ch ) const
872 {
873 return ptr->describe( ch );
874 }
875
obtain(Character & ch,int qty)876 item_location item_location::obtain( Character &ch, int qty )
877 {
878 if( !ptr->valid() ) {
879 debugmsg( "item location does not point to valid item" );
880 return item_location();
881 }
882 return ptr->obtain( ch, qty );
883 }
884
obtain_cost(const Character & ch,int qty) const885 int item_location::obtain_cost( const Character &ch, int qty ) const
886 {
887 return ptr->obtain_cost( ch, qty );
888 }
889
remove_item()890 void item_location::remove_item()
891 {
892 if( !ptr->valid() ) {
893 debugmsg( "item location does not point to valid item" );
894 return;
895 }
896 ptr->remove_item();
897 ptr.reset( new impl::nowhere() );
898 }
899
on_contents_changed()900 void item_location::on_contents_changed()
901 {
902 if( !ptr->valid() ) {
903 debugmsg( "item location does not point to valid item" );
904 return;
905 }
906 ptr->on_contents_changed();
907 }
908
get_item()909 item *item_location::get_item()
910 {
911 return ptr->target();
912 }
913
get_item() const914 const item *item_location::get_item() const
915 {
916 return ptr->target();
917 }
918
set_should_stack(bool should_stack) const919 void item_location::set_should_stack( bool should_stack ) const
920 {
921 ptr->should_stack = should_stack;
922 }
923
held_by(Character & who) const924 bool item_location::held_by( Character &who ) const
925 {
926 if( where() == type::character && g->critter_at<Character>( position() ) == &who ) {
927 return true;
928 } else if( has_parent() ) {
929 return parent_item().held_by( who );
930 }
931 return false;
932 }
933
volume_capacity() const934 units::volume item_location::volume_capacity() const
935 {
936 return ptr->volume_capacity();
937 }
938
weight_capacity() const939 units::mass item_location::weight_capacity() const
940 {
941 return ptr->weight_capacity();
942 }
943