1 #include "visitable.h"
2 
3 #include <algorithm>
4 #include <climits>
5 #include <iosfwd>
6 #include <limits>
7 #include <map>
8 #include <memory>
9 #include <string>
10 #include <unordered_map>
11 #include <utility>
12 
13 #include "active_item_cache.h"
14 #include "bionics.h"
15 #include "character.h"
16 #include "colony.h"
17 #include "debug.h"
18 #include "inventory.h"
19 #include "item.h"
20 #include "item_contents.h"
21 #include "item_pocket.h"
22 #include "make_static.h"
23 #include "map.h"
24 #include "map_selector.h"
25 #include "memory_fast.h"
26 #include "monster.h"
27 #include "mtype.h"
28 #include "mutation.h"
29 #include "pimpl.h"
30 #include "player.h"
31 #include "point.h"
32 #include "submap.h"
33 #include "temp_crafting_inventory.h"
34 #include "units.h"
35 #include "value_ptr.h"
36 #include "veh_type.h"
37 #include "vehicle.h"
38 #include "vehicle_selector.h"
39 
40 static const itype_id itype_apparatus( "apparatus" );
41 static const itype_id itype_adv_UPS_off( "adv_UPS_off" );
42 static const itype_id itype_toolset( "toolset" );
43 static const itype_id itype_UPS( "UPS" );
44 static const itype_id itype_UPS_off( "UPS_off" );
45 
46 static const quality_id qual_BUTCHER( "BUTCHER" );
47 
48 static const bionic_id bio_tools( "bio_tools" );
49 static const bionic_id bio_ups( "bio_ups" );
50 
51 static const json_character_flag json_flag_BIONIC_TOGGLED( "BIONIC_TOGGLED" );
52 
53 /** @relates visitable */
find_parent(const item & it) const54 item *read_only_visitable::find_parent( const item &it ) const
55 {
56     item *res = nullptr;
57     if( visit_items( [&]( item * node, item * parent ) {
58     if( node == &it ) {
59             res = parent;
60             return VisitResponse::ABORT;
61         }
62         return VisitResponse::NEXT;
63     } ) != VisitResponse::ABORT ) {
64         debugmsg( "Tried to find item parent using an object that doesn't contain it" );
65     }
66     return res;
67 }
68 
69 /** @relates visitable */
parents(const item & it) const70 std::vector<item *> read_only_visitable::parents( const item &it ) const
71 {
72     std::vector<item *> res;
73     for( item *obj = find_parent( it ); obj; obj = find_parent( *obj ) ) {
74         res.push_back( obj );
75     }
76     return res;
77 }
78 
79 /** @relates visitable */
has_item(const item & it) const80 bool read_only_visitable::has_item( const item &it ) const
81 {
82     return visit_items( [&it]( const item * node, item * ) {
83         return node == &it ? VisitResponse::ABORT : VisitResponse::NEXT;
84     } ) == VisitResponse::ABORT;
85 }
86 
87 /** @relates visitable */
has_item_with(const std::function<bool (const item &)> & filter) const88 bool read_only_visitable::has_item_with( const std::function<bool( const item & )> &filter ) const
89 {
90     return visit_items( [&filter]( const item * node, item * ) {
91         return filter( *node ) ? VisitResponse::ABORT : VisitResponse::NEXT;
92     } ) == VisitResponse::ABORT;
93 }
94 
95 /** Sums the two terms, being careful to not trigger overflow.
96  * Doesn't handle underflow.
97  *
98  * @param a The first addend.
99  * @param b The second addend.
100  * @return the sum of the addends, but truncated to std::numeric_limits<int>::max().
101  */
102 template <typename T>
sum_no_wrap(T a,T b)103 static T sum_no_wrap( T a, T b )
104 {
105     if( a > std::numeric_limits<T>::max() - b ||
106         b > std::numeric_limits<T>::max() - a ) {
107         return std::numeric_limits<T>::max();
108     }
109     return a + b;
110 }
111 
112 template <typename T>
has_quality_internal(const T & self,const quality_id & qual,int level,int limit)113 static int has_quality_internal( const T &self, const quality_id &qual, int level, int limit )
114 {
115     int qty = 0;
116 
117     self.visit_items( [&qual, level, &limit, &qty]( item * e, item * ) {
118         if( e->get_quality( qual ) >= level ) {
119             qty = sum_no_wrap( qty, static_cast<int>( e->count() ) );
120             if( qty >= limit ) {
121                 // found sufficient items
122                 return VisitResponse::ABORT;
123             }
124         }
125         return VisitResponse::NEXT;
126     } );
127     return std::min( qty, limit );
128 }
129 
has_quality_from_vpart(const vehicle & veh,int part,const quality_id & qual,int level,int limit)130 static int has_quality_from_vpart( const vehicle &veh, int part, const quality_id &qual, int level,
131                                    int limit )
132 {
133     int qty = 0;
134 
135     point pos = veh.cpart( part ).mount;
136     for( const auto &n : veh.parts_at_relative( pos, true ) ) {
137 
138         // only unbroken parts can provide tool qualities
139         if( !veh.cpart( n ).is_broken() ) {
140             auto tq = veh.part_info( n ).qualities;
141             auto iter = tq.find( qual );
142 
143             // does the part provide this quality?
144             if( iter != tq.end() && iter->second >= level ) {
145                 qty = sum_no_wrap( qty, 1 );
146             }
147         }
148     }
149     return std::min( qty, limit );
150 }
151 
has_quality(const quality_id & qual,int level,int qty) const152 bool read_only_visitable::has_quality( const quality_id &qual, int level, int qty ) const
153 {
154     return has_quality_internal( *this, qual, level, qty ) == qty;
155 }
156 
157 /** @relates visitable */
has_quality(const quality_id & qual,int level,int qty) const158 bool inventory::has_quality( const quality_id &qual, int level, int qty ) const
159 {
160     int res = 0;
161     for( const auto &stack : this->items ) {
162         res += stack.size() * has_quality_internal( stack.front(), qual, level, qty );
163         if( res >= qty ) {
164             return true;
165         }
166     }
167     return false;
168 }
169 
170 /** @relates visitable */
has_quality(const quality_id & qual,int level,int qty) const171 bool vehicle_selector::has_quality( const quality_id &qual, int level, int qty ) const
172 {
173     for( const auto &cursor : *this ) {
174         if( cursor.ignore_vpart ) {
175             continue;
176         }
177 
178         qty -= has_quality_from_vpart( cursor.veh, cursor.part, qual, level, qty );
179         if( qty <= 0 ) {
180             return true;
181         }
182     }
183     return has_quality_internal( *this, qual, level, qty ) == qty;
184 }
185 
186 /** @relates visitable */
has_quality(const quality_id & qual,int level,int qty) const187 bool vehicle_cursor::has_quality( const quality_id &qual, int level, int qty ) const
188 {
189     if( !ignore_vpart ) {
190         qty -= has_quality_from_vpart( veh, part, qual, level, qty );
191     }
192     return qty <= 0 ? true : has_quality_internal( *this, qual, level, qty ) == qty;
193 }
194 
195 /** @relates visitable */
has_quality(const quality_id & qual,int level,int qty) const196 bool Character::has_quality( const quality_id &qual, int level, int qty ) const
197 {
198     for( const auto &bio : *this->my_bionics ) {
199         if( bio.get_quality( qual ) >= level ) {
200             if( qty <= 1 ) {
201                 return true;
202             }
203 
204             qty--;
205         }
206     }
207 
208     return qty <= 0 ? true : has_quality_internal( *this, qual, level, qty ) == qty;
209 }
210 
has_tools(const itype_id & it,int quantity,const std::function<bool (const item &)> & filter) const211 bool read_only_visitable::has_tools( const itype_id &it, int quantity,
212                                      const std::function<bool( const item & )> &filter ) const
213 {
214     return has_amount( it, quantity, true, filter );
215 }
216 
has_components(const itype_id & it,int quantity,const std::function<bool (const item &)> & filter) const217 bool read_only_visitable::has_components( const itype_id &it, int quantity,
218         const std::function<bool( const item & )> &filter ) const
219 {
220     return has_amount( it, quantity, false, filter );
221 }
222 
has_charges(const itype_id & it,int quantity,const std::function<bool (const item &)> & filter) const223 bool read_only_visitable::has_charges( const itype_id &it, int quantity,
224                                        const std::function<bool( const item & )> &filter ) const
225 {
226     return ( charges_of( it, INT_MAX, filter ) >= quantity );
227 }
228 
229 template <typename T>
max_quality_internal(const T & self,const quality_id & qual)230 static int max_quality_internal( const T &self, const quality_id &qual )
231 {
232     int res = INT_MIN;
233     self.visit_items( [&res, &qual]( item * e, item * ) {
234         res = std::max( res, e->get_quality( qual ) );
235         return VisitResponse::NEXT;
236     } );
237     return res;
238 }
239 
max_quality_from_vpart(const vehicle & veh,int part,const quality_id & qual)240 static int max_quality_from_vpart( const vehicle &veh, int part, const quality_id &qual )
241 {
242     int res = INT_MIN;
243 
244     point pos = veh.cpart( part ).mount;
245     for( const auto &n : veh.parts_at_relative( pos, true ) ) {
246 
247         // only unbroken parts can provide tool qualities
248         if( !veh.cpart( n ).is_broken() ) {
249             auto tq = veh.part_info( n ).qualities;
250             auto iter = tq.find( qual );
251 
252             // does the part provide this quality?
253             if( iter != tq.end() ) {
254                 res = std::max( res, iter->second );
255             }
256         }
257     }
258     return res;
259 }
260 
max_quality(const quality_id & qual) const261 int read_only_visitable::max_quality( const quality_id &qual ) const
262 {
263     return max_quality_internal( *this, qual );
264 }
265 
266 /** @relates visitable */
max_quality(const quality_id & qual) const267 int Character::max_quality( const quality_id &qual ) const
268 {
269     int res = INT_MIN;
270 
271     for( const bionic &bio : *my_bionics ) {
272         res = std::max( res, bio.get_quality( qual ) );
273     }
274 
275     if( qual == qual_BUTCHER ) {
276         for( const trait_id &mut : get_mutations() ) {
277             res = std::max( res, mut->butchering_quality );
278         }
279     }
280 
281     return std::max( res, max_quality_internal( *this, qual ) );
282 }
283 
284 /** @relates visitable */
max_quality(const quality_id & qual) const285 int vehicle_cursor::max_quality( const quality_id &qual ) const
286 {
287     int vpart = ignore_vpart ? 0 : max_quality_from_vpart( veh, part, qual );
288     return std::max( vpart, max_quality_internal( *this, qual ) );
289 }
290 
291 /** @relates visitable */
max_quality(const quality_id & qual) const292 int vehicle_selector::max_quality( const quality_id &qual ) const
293 {
294     int res = INT_MIN;
295     for( const auto &e : *this ) {
296         res = std::max( res, e.max_quality( qual ) );
297     }
298     return res;
299 }
300 
301 template<typename T, typename V>
items_with_internal(V & self,const std::function<bool (const item &)> & filter)302 static inline std::vector<T> items_with_internal( V &self, const std::function<bool( const item & )>
303         &filter )
304 {
305     std::vector<T> res;
306     self.visit_items( [&res, &filter]( const item * node, item * ) {
307         if( filter( *node ) ) {
308             res.push_back( const_cast<T>( node ) );
309         }
310         return VisitResponse::NEXT;
311     } );
312     return res;
313 }
314 
315 /** @relates visitable */
items_with(const std::function<bool (const item &)> & filter) const316 std::vector<const item *> read_only_visitable::items_with(
317     const std::function<bool( const item & )> &filter ) const
318 {
319     return items_with_internal<const item *>( *this, filter );
320 }
321 /** @relates visitable */
items_with(const std::function<bool (const item &)> & filter)322 std::vector<item *> read_only_visitable::items_with(
323     const std::function<bool( const item & )> &filter )
324 {
325     return items_with_internal<item *>( *this, filter );
326 }
327 
visit_internal(const std::function<VisitResponse (item *,item *)> & func,const item * node,item * parent=nullptr)328 static VisitResponse visit_internal( const std::function<VisitResponse( item *, item * )> &func,
329                                      const item *node, item *parent = nullptr )
330 {
331     // hack to avoid repetition
332     item *m_node = const_cast<item *>( node );
333 
334     switch( func( m_node, parent ) ) {
335         case VisitResponse::ABORT:
336             return VisitResponse::ABORT;
337 
338         case VisitResponse::NEXT:
339             if( m_node->contents.visit_contents( func, m_node ) == VisitResponse::ABORT ) {
340                 return VisitResponse::ABORT;
341             }
342         /* intentional fallthrough */
343 
344         case VisitResponse::SKIP:
345             return VisitResponse::NEXT;
346     }
347 
348     /* never reached but suppresses GCC warning */
349     return VisitResponse::ABORT;
350 }
351 
visit_contents(const std::function<VisitResponse (item *,item *)> & func,item * parent)352 VisitResponse item_contents::visit_contents( const std::function<VisitResponse( item *, item * )>
353         &func, item *parent )
354 {
355     for( item_pocket &pocket : contents ) {
356         if( !pocket.is_type( item_pocket::pocket_type::CONTAINER ) ) {
357             // anything that is not CONTAINER is accessible only via its specific accessor
358             return VisitResponse::NEXT;
359         }
360         switch( pocket.visit_contents( func, parent ) ) {
361             case VisitResponse::ABORT:
362                 return VisitResponse::ABORT;
363             default:
364                 break;
365         }
366     }
367     return VisitResponse::NEXT;
368 }
369 
visit_contents(const std::function<VisitResponse (item *,item *)> & func,item * parent)370 VisitResponse item_pocket::visit_contents( const std::function<VisitResponse( item *, item * )>
371         &func, item *parent )
372 {
373     for( item &e : contents ) {
374         switch( visit_internal( func, &e, parent ) ) {
375             case VisitResponse::ABORT:
376                 return VisitResponse::ABORT;
377             default:
378                 break;
379         }
380     }
381     return VisitResponse::NEXT;
382 }
383 
384 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const385 VisitResponse item::visit_items(
386     const std::function<VisitResponse( item *, item * )> &func ) const
387 {
388     return visit_internal( func, this );
389 }
390 
391 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const392 VisitResponse inventory::visit_items(
393     const std::function<VisitResponse( item *, item * )> &func ) const
394 {
395     for( const auto &stack : items ) {
396         for( const auto &it : stack ) {
397             if( visit_internal( func, &it ) == VisitResponse::ABORT ) {
398                 return VisitResponse::ABORT;
399             }
400         }
401     }
402     return VisitResponse::NEXT;
403 }
404 
405 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const406 VisitResponse temp_crafting_inventory::visit_items(
407     const std::function<VisitResponse( item *, item * )> &func ) const
408 {
409     for( item *it : items ) {
410         if( visit_internal( func, it ) == VisitResponse::ABORT ) {
411             return VisitResponse::ABORT;
412         }
413     }
414     return VisitResponse::NEXT;
415 }
416 
417 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const418 VisitResponse Character::visit_items( const std::function<VisitResponse( item *, item * )> &func )
419 const
420 {
421     if( !weapon.is_null() &&
422         visit_internal( func, &weapon ) == VisitResponse::ABORT ) {
423         return VisitResponse::ABORT;
424     }
425 
426     for( const auto &e : worn ) {
427         if( visit_internal( func, &e ) == VisitResponse::ABORT ) {
428             return VisitResponse::ABORT;
429         }
430     }
431 
432     return inv->visit_items( func );
433 }
434 
435 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const436 VisitResponse map_cursor::visit_items(
437     const std::function<VisitResponse( item *, item * )> &func ) const
438 {
439     map &here = get_map();
440     // skip inaccessible items
441     if( here.has_flag( "SEALED", pos() ) && !here.has_flag( "LIQUIDCONT", pos() ) ) {
442         return VisitResponse::NEXT;
443     }
444 
445     for( item &e : here.i_at( pos() ) ) {
446         if( visit_internal( func, &e ) == VisitResponse::ABORT ) {
447             return VisitResponse::ABORT;
448         }
449     }
450     return VisitResponse::NEXT;
451 }
452 
453 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const454 VisitResponse map_selector::visit_items(
455     const std::function<VisitResponse( item *, item * )> &func ) const
456 {
457     for( auto &cursor : * ( const_cast<map_selector *>( this ) ) ) {
458         if( cursor.visit_items( func ) == VisitResponse::ABORT ) {
459             return VisitResponse::ABORT;
460         }
461     }
462     return VisitResponse::NEXT;
463 }
464 
465 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const466 VisitResponse vehicle_cursor::visit_items(
467     const std::function<VisitResponse( item *, item * )> &func ) const
468 {
469     int idx = veh.part_with_feature( part, "CARGO", true );
470     if( idx >= 0 ) {
471         for( auto &e : veh.get_items( idx ) ) {
472             if( visit_internal( func, &e ) == VisitResponse::ABORT ) {
473                 return VisitResponse::ABORT;
474             }
475         }
476     }
477     return VisitResponse::NEXT;
478 }
479 
480 /** @relates visitable */
visit_items(const std::function<VisitResponse (item *,item *)> & func) const481 VisitResponse vehicle_selector::visit_items(
482     const std::function<VisitResponse( item *, item * )> &func ) const
483 {
484     for( const auto &cursor :  *this ) {
485         if( cursor.visit_items( func ) == VisitResponse::ABORT ) {
486             return VisitResponse::ABORT;
487         }
488     }
489     return VisitResponse::NEXT;
490 }
491 
492 /** @relates visitable */
remove_item(item & it)493 item visitable::remove_item( item &it )
494 {
495     auto obj = remove_items_with( [&it]( const item & e ) {
496         return &e == &it;
497     }, 1 );
498     if( !obj.empty() ) {
499         return obj.front();
500 
501     } else {
502         debugmsg( "Tried removing item from object which did not contain it" );
503         return item();
504     }
505 }
506 
507 /** @relates visitable */
remove_items_with(const std::function<bool (const item & e)> & filter,int count)508 std::list<item> item::remove_items_with( const std::function<bool( const item &e )>
509         &filter, int count )
510 {
511     std::list<item> res;
512 
513     if( count <= 0 ) {
514         // nothing to do
515         return res;
516     }
517 
518     contents.remove_internal( filter, count, res );
519     return res;
520 }
521 
522 /** @relates visitable */
remove_items_with(const std::function<bool (const item & e)> & filter,int count)523 std::list<item> inventory::remove_items_with( const
524         std::function<bool( const item &e )> &filter, int count )
525 {
526     std::list<item> res;
527 
528     if( count <= 0 ) {
529         // nothing to do
530         return res;
531     }
532 
533     for( auto stack = items.begin(); stack != items.end() && count > 0; ) {
534         std::list<item> &istack = *stack;
535         const char original_invlet = istack.front().invlet;
536 
537         for( auto istack_iter = istack.begin(); istack_iter != istack.end() && count > 0; ) {
538             if( filter( *istack_iter ) ) {
539                 count--;
540                 res.splice( res.end(), istack, istack_iter++ );
541                 // The non-first items of a stack may have different invlets, the code
542                 // in inventory only ever checks the invlet of the first item. This
543                 // ensures that the first item of a stack always has the same invlet, even
544                 // after the original first item was removed.
545                 if( istack_iter == istack.begin() && istack_iter != istack.end() ) {
546                     istack_iter->invlet = original_invlet;
547                 }
548 
549             } else {
550                 istack_iter->contents.remove_internal( filter, count, res );
551                 ++istack_iter;
552             }
553         }
554 
555         if( istack.empty() ) {
556             stack = items.erase( stack );
557         } else {
558             ++stack;
559         }
560     }
561 
562     // Invalidate binning cache
563     binned = false;
564 
565     return res;
566 }
567 
568 /** @relates visitable */
remove_items_with(const std::function<bool (const item & e)> & filter,int count)569 std::list<item> Character::remove_items_with( const
570         std::function<bool( const item &e )> &filter, int count )
571 {
572     std::list<item> res;
573 
574     if( count <= 0 ) {
575         // nothing to do
576         return res;
577     }
578 
579     // first try and remove items from the inventory
580     res = inv->remove_items_with( filter, count );
581     count -= res.size();
582     if( count == 0 ) {
583         return res;
584     }
585 
586     // then try any worn items
587     for( auto iter = worn.begin(); iter != worn.end(); ) {
588         if( filter( *iter ) ) {
589             iter->on_takeoff( *this );
590             res.splice( res.end(), worn, iter++ );
591             if( --count == 0 ) {
592                 return res;
593             }
594         } else {
595             iter->contents.remove_internal( filter, count, res );
596             if( count == 0 ) {
597                 return res;
598             }
599             ++iter;
600         }
601     }
602 
603     // finally try the currently wielded item (if any)
604     if( filter( weapon ) ) {
605         res.push_back( remove_weapon() );
606         count--;
607     } else {
608         weapon.contents.remove_internal( filter, count, res );
609     }
610 
611     return res;
612 }
613 
614 /** @relates visitable */
remove_items_with(const std::function<bool (const item & e)> & filter,int count)615 std::list<item> map_cursor::remove_items_with( const
616         std::function<bool( const item &e )> &filter, int count )
617 {
618     std::list<item> res;
619 
620     if( count <= 0 ) {
621         // nothing to do
622         return res;
623     }
624 
625     map &here = get_map();
626     if( !here.inbounds( pos() ) ) {
627         debugmsg( "cannot remove items from map: cursor out-of-bounds" );
628         return res;
629     }
630 
631     // fetch the appropriate item stack
632     point offset;
633     submap *sub = here.get_submap_at( pos(), offset );
634     cata::colony<item> &stack = sub->get_items( offset );
635 
636     for( auto iter = stack.begin(); iter != stack.end(); ) {
637         if( filter( *iter ) ) {
638             // remove from the active items cache (if it isn't there does nothing)
639             sub->active_items.remove( &*iter );
640 
641             // if necessary remove item from the luminosity map
642             sub->update_lum_rem( offset, *iter );
643 
644             // finally remove the item
645             res.push_back( *iter );
646             iter = stack.erase( iter );
647 
648             if( --count == 0 ) {
649                 return res;
650             }
651         } else {
652             iter->contents.remove_internal( filter, count, res );
653             if( count == 0 ) {
654                 return res;
655             }
656             ++iter;
657         }
658     }
659     here.update_submap_active_item_status( pos() );
660     return res;
661 }
662 
663 /** @relates visitable */
remove_items_with(const std::function<bool (const item & e)> & filter,int count)664 std::list<item> map_selector::remove_items_with( const
665         std::function<bool( const item &e )> &filter, int count )
666 {
667     std::list<item> res;
668 
669     for( auto &cursor : *this ) {
670         std::list<item> out = cursor.remove_items_with( filter, count );
671         count -= out.size();
672         res.splice( res.end(), out );
673     }
674 
675     return res;
676 }
677 
678 /** @relates visitable */
remove_items_with(const std::function<bool (const item & e)> & filter,int count)679 std::list<item> vehicle_cursor::remove_items_with( const
680         std::function<bool( const item &e )> &filter, int count )
681 {
682     std::list<item> res;
683 
684     if( count <= 0 ) {
685         // nothing to do
686         return res;
687     }
688 
689     int idx = veh.part_with_feature( part, "CARGO", false );
690     if( idx < 0 ) {
691         return res;
692     }
693 
694     vehicle_part &p = veh.part( idx );
695     for( auto iter = p.items.begin(); iter != p.items.end(); ) {
696         if( filter( *iter ) ) {
697             // remove from the active items cache (if it isn't there does nothing)
698             veh.active_items.remove( &*iter );
699 
700             res.push_back( *iter );
701             iter = p.items.erase( iter );
702 
703             if( --count == 0 ) {
704                 return res;
705             }
706         } else {
707             iter->contents.remove_internal( filter, count, res );
708             if( count == 0 ) {
709                 return res;
710             }
711             ++iter;
712         }
713     }
714 
715     if( !res.empty() ) {
716         // if we removed any items then invalidate the cached mass
717         veh.invalidate_mass();
718     }
719 
720     return res;
721 }
722 
723 /** @relates visitable */
remove_items_with(const std::function<bool (const item & e)> & filter,int count)724 std::list<item> vehicle_selector::remove_items_with( const
725         std::function<bool( const item &e )> &filter, int count )
726 {
727     std::list<item> res;
728 
729     for( auto &cursor : *this ) {
730         std::list<item> out = cursor.remove_items_with( filter, count );
731         count -= out.size();
732         res.splice( res.end(), out );
733     }
734 
735     return res;
736 }
737 
738 template <typename T, typename M>
charges_of_internal(const T & self,const M & main,const itype_id & id,int limit,const std::function<bool (const item &)> & filter,const std::function<void (int)> & visitor)739 static int charges_of_internal( const T &self, const M &main, const itype_id &id, int limit,
740                                 const std::function<bool( const item & )> &filter,
741                                 const std::function<void( int )> &visitor )
742 {
743     int qty = 0;
744 
745     bool found_tool_with_UPS = false;
746     self.visit_items( [&]( const item * e, item * ) {
747         if( filter( *e ) ) {
748             if( e->is_tool() ) {
749                 if( e->typeId() == id ) {
750                     // includes charges from any included magazine.
751                     qty = sum_no_wrap( qty, e->ammo_remaining() );
752                     if( e->has_flag( STATIC( flag_id( "USE_UPS" ) ) ) ) {
753                         found_tool_with_UPS = true;
754                     }
755                 }
756                 if( !e->has_pockets() ) {
757                     return qty < limit ? VisitResponse::SKIP : VisitResponse::ABORT;
758                 }
759 
760             } else if( e->count_by_charges() ) {
761                 if( e->typeId() == id ) {
762                     qty = sum_no_wrap( qty, e->charges );
763                 }
764                 // items counted by charges are not themselves expected to be containers
765                 return qty < limit ? VisitResponse::SKIP : VisitResponse::ABORT;
766             }
767         }
768         // recurse through any nested containers
769         return qty < limit ? VisitResponse::NEXT : VisitResponse::ABORT;
770     } );
771 
772     if( qty < limit && found_tool_with_UPS ) {
773         qty += main.charges_of( itype_UPS, limit - qty );
774         if( visitor ) {
775             visitor( qty );
776         }
777     }
778 
779     return std::min( qty, limit );
780 }
781 
782 /** @relates visitable */
charges_of(const itype_id & what,int limit,const std::function<bool (const item &)> & filter,const std::function<void (int)> & visitor) const783 int read_only_visitable::charges_of( const itype_id &what, int limit,
784                                      const std::function<bool( const item & )> &filter,
785                                      const std::function<void( int )> &visitor ) const
786 {
787     if( what == itype_UPS ) {
788         int qty = 0;
789         qty = sum_no_wrap( qty, charges_of( itype_UPS_off ) );
790         qty = sum_no_wrap( qty, static_cast<int>( charges_of( itype_adv_UPS_off ) / 0.6 ) );
791         return std::min( qty, limit );
792     }
793 
794     return charges_of_internal( *this, *this, what, limit, filter, visitor );
795 }
796 
797 /** @relates visitable */
charges_of(const itype_id & what,int limit,const std::function<bool (const item &)> & filter,const std::function<void (int)> & visitor) const798 int inventory::charges_of( const itype_id &what, int limit,
799                            const std::function<bool( const item & )> &filter,
800                            const std::function<void( int )> &visitor ) const
801 {
802     if( what == itype_UPS ) {
803         int qty = 0;
804         qty = sum_no_wrap( qty, charges_of( itype_UPS_off ) );
805         qty = sum_no_wrap( qty, static_cast<int>( charges_of( itype_adv_UPS_off ) / 0.6 ) );
806         return std::min( qty, limit );
807     }
808     const auto &binned = get_binned_items();
809     const auto iter = binned.find( what );
810     if( iter == binned.end() ) {
811         return 0;
812     }
813 
814     int res = 0;
815     for( const item *it : iter->second ) {
816         res = sum_no_wrap( res, charges_of_internal( *it, *this, what, limit, filter, visitor ) );
817         if( res >= limit ) {
818             break;
819         }
820     }
821     return std::min( limit, res );
822 }
823 
824 /** @relates visitable */
charges_of(const itype_id & what,int limit,const std::function<bool (const item &)> & filter,const std::function<void (int)> & visitor) const825 int Character::charges_of( const itype_id &what, int limit,
826                            const std::function<bool( const item & )> &filter,
827                            const std::function<void( int )> &visitor ) const
828 {
829     const player *p = dynamic_cast<const player *>( this );
830 
831     if( what == itype_toolset ) {
832         if( p && p->has_active_bionic( bio_tools ) ) {
833             return std::min( units::to_kilojoule( p->get_power_level() ), limit );
834         } else {
835             return 0;
836         }
837     }
838 
839     if( what == itype_UPS ) {
840         int qty = 0;
841         qty = sum_no_wrap( qty, charges_of( itype_UPS_off ) );
842         qty = sum_no_wrap( qty, static_cast<int>( charges_of( itype_adv_UPS_off ) / 0.6 ) );
843         if( p && p->has_active_bionic( bio_ups ) ) {
844             qty = sum_no_wrap( qty, units::to_kilojoule( p->get_power_level() ) );
845         }
846         if( p && p->is_mounted() ) {
847             const auto *mons = p->mounted_creature.get();
848             if( mons->has_flag( MF_RIDEABLE_MECH ) && mons->battery_item ) {
849                 qty = sum_no_wrap( qty, mons->battery_item->ammo_remaining() );
850             }
851         }
852         return std::min( qty, limit );
853     }
854 
855     return charges_of_internal( *this, *this, what, limit, filter, visitor );
856 }
857 
858 template <typename T>
amount_of_internal(const T & self,const itype_id & id,bool pseudo,int limit,const std::function<bool (const item &)> & filter)859 static int amount_of_internal( const T &self, const itype_id &id, bool pseudo, int limit,
860                                const std::function<bool( const item & )> &filter )
861 {
862     int qty = 0;
863     self.visit_items( [&qty, &id, &pseudo, &limit, &filter]( const item * e, item * ) {
864         if( ( id == STATIC( itype_id( "any" ) ) || e->typeId() == id ) && filter( *e ) &&
865             ( pseudo || !e->has_flag( STATIC( flag_id( "PSEUDO" ) ) ) ) ) {
866             qty = sum_no_wrap( qty, 1 );
867         }
868         return qty != limit ? VisitResponse::NEXT : VisitResponse::ABORT;
869     } );
870     return qty;
871 }
872 
873 /** @relates visitable */
amount_of(const itype_id & what,bool pseudo,int limit,const std::function<bool (const item &)> & filter) const874 int read_only_visitable::amount_of( const itype_id &what, bool pseudo, int limit,
875                                     const std::function<bool( const item & )> &filter ) const
876 {
877     return amount_of_internal( *this, what, pseudo, limit, filter );
878 }
879 
880 /** @relates visitable */
amount_of(const itype_id & what,bool pseudo,int limit,const std::function<bool (const item &)> & filter) const881 int inventory::amount_of( const itype_id &what, bool pseudo, int limit,
882                           const std::function<bool( const item & )> &filter ) const
883 {
884     const auto &binned = get_binned_items();
885     const auto iter = binned.find( what );
886     if( iter == binned.end() && what != STATIC( itype_id( "any" ) ) ) {
887         return 0;
888     }
889 
890     int res = 0;
891     if( what.str() == "any" ) {
892         for( const auto &kv : binned ) {
893             for( const item *it : kv.second ) {
894                 res = sum_no_wrap( res, it->amount_of( what, pseudo, limit, filter ) );
895             }
896         }
897     } else {
898         for( const item *it : iter->second ) {
899             res = sum_no_wrap( res, it->amount_of( what, pseudo, limit, filter ) );
900         }
901     }
902 
903     return std::min( limit, res );
904 }
905 
906 /** @relates visitable */
amount_of(const itype_id & what,bool pseudo,int limit,const std::function<bool (const item &)> & filter) const907 int Character::amount_of( const itype_id &what, bool pseudo, int limit,
908                           const std::function<bool( const item & )> &filter ) const
909 {
910     if( pseudo ) {
911         for( const auto &bio : *this->my_bionics ) {
912             const bionic_data &bid = bio.info();
913             if( bid.fake_item == what && ( !bid.activated || bio.powered ) ) {
914                 return 1;
915             }
916         }
917     }
918 
919     if( what == itype_apparatus && pseudo ) {
920         int qty = 0;
921         visit_items( [&qty, &limit, &filter]( const item * e, item * ) {
922             if( e->get_quality( quality_id( "SMOKE_PIPE" ) ) >= 1 && filter( *e ) ) {
923                 qty = sum_no_wrap( qty, 1 );
924             }
925             return qty < limit ? VisitResponse::SKIP : VisitResponse::ABORT;
926         } );
927         return std::min( qty, limit );
928     }
929 
930     return amount_of_internal( *this, what, pseudo, limit, filter );
931 }
932 
933 /** @relates visitable */
has_amount(const itype_id & what,int qty,bool pseudo,const std::function<bool (const item &)> & filter) const934 bool read_only_visitable::has_amount( const itype_id &what, int qty, bool pseudo,
935                                       const std::function<bool( const item & )> &filter ) const
936 {
937     return amount_of( what, pseudo, qty, filter ) == qty;
938 }
939