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 == ⁢
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