// Get use methods of this item and its contents bool item_has_uses_recursive( const item &it ) { if( !it.type->use_methods.empty() ) { return true; } for( const auto &elem : it.contents ) { if( item_has_uses_recursive( elem ) ) { return true; } } return false; }
item_action_map item_action_generator::map_actions_to_items( player &p, const std::vector<item *> &pseudos ) const { std::set< item_action_id > unmapped_actions; for( auto &ia_ptr : item_actions ) { // Get ids of wanted actions unmapped_actions.insert( ia_ptr.first ); } item_action_map candidates; std::vector< item * > items = p.inv_dump(); items.reserve( items.size() + pseudos.size() ); items.insert( items.end(), pseudos.begin(), pseudos.end() ); std::unordered_set< item_action_id > to_remove; for( item *i : items ) { if( !item_has_uses_recursive( *i ) ) { continue; } for( const item_action_id &use : unmapped_actions ) { // Actually used item can be different from the "outside item" // For example, sheathed knife item *actual_item = i->get_usable_item( use ); if( actual_item == nullptr ) { continue; } const use_function *func = actual_item->get_use( use ); if( !( func && func->get_actor_ptr() && func->get_actor_ptr()->can_use( p, *actual_item, false, p.pos() ) ) ) { continue; } if( !actual_item->ammo_sufficient() ) { continue; } // Add to usable items if it needs less charges per use or has less charges auto found = candidates.find( use ); bool better = false; if( found == candidates.end() ) { better = true; } else { if( actual_item->ammo_required() > found->second->ammo_required() ) { continue; // Other item consumes less charges } if( found->second->ammo_remaining() > actual_item->ammo_remaining() ) { better = true; // Items with less charges preferred } } if( better ) { candidates[use] = i; if( actual_item->ammo_required() == 0 ) { to_remove.insert( use ); } } } for( const item_action_id &r : to_remove ) { unmapped_actions.erase( r ); } } return candidates; }
item_action_map item_action_generator::map_actions_to_items( player &p, const std::vector<item *> &pseudos ) const { std::set< item_action_id > unmapped_actions; for( auto &ia_ptr : item_actions ) { // Get ids of wanted actions unmapped_actions.insert( ia_ptr.first ); } item_action_map candidates; std::vector< item * > items = p.inv_dump(); items.reserve( items.size() + pseudos.size() ); items.insert( items.end(), pseudos.begin(), pseudos.end() ); std::unordered_set< item_action_id > to_remove; for( item *i : items ) { if( !item_has_uses_recursive( *i ) ) { continue; } for( const item_action_id &use : unmapped_actions ) { // Actually used item can be different from the "outside item" // For example, sheathed knife item *actual_item = i->get_usable_item( use ); if( actual_item == nullptr ) { continue; } const auto tool = dynamic_cast<const it_tool *>( actual_item->type ); const use_function *ufunc = actual_item->get_use( use ); // Can't just test for charges_per_use > charges, because charges can be -1 if( ufunc == nullptr || ( ufunc->get_actor_ptr() != nullptr && !ufunc->get_actor_ptr()->can_use( &p, actual_item, false, p.pos() ) ) || ( tool != nullptr && tool->charges_per_use > 0 && tool->charges_per_use > actual_item->charges ) ) { continue; } // Add to usable items if it needs less charges per use or has less charges auto found = candidates.find( use ); int would_use_charges = tool == nullptr ? 0 : tool->charges_per_use; bool better = false; if( found == candidates.end() ) { better = true; } else { const auto other = dynamic_cast<const it_tool *>( found->second->type ); if( other == nullptr || would_use_charges > other->charges_per_use ) { continue; // Other item consumes less charges } if( found->second->charges > actual_item->charges ) { better = true; // Items with less charges preferred } } if( better ) { candidates[use] = i; if( would_use_charges == 0 ) { to_remove.insert( use ); } } } for( const item_action_id &r : to_remove ) { unmapped_actions.erase( r ); } } return candidates; }