void pick_up_from_feet( player &p, int pos ) {
    auto size_before = g->m.i_at( p.pos() ).size();
    REQUIRE( size_before > pos );
    p.moves = 100;
    p.assign_activity( activity_id( "ACT_PICKUP" ) );
    p.activity.placement = tripoint(0, 0, 0);
    p.activity.values.push_back( false );   // not from vehicle
    p.activity.values.push_back( pos );     // index of item to pick up
    p.activity.values.push_back( 0 );
    p.activity.do_turn( p );
    REQUIRE( g->m.i_at( p.pos() ).size() == size_before - 1 );
}
Exemple #2
0
void gates::open_gate( const tripoint &pos, player &p )
{
    const gate_id gid = get_gate_id( pos );

    if( !gates_data.is_valid( gid ) ) {
        p.add_msg_if_player( _( "Nothing happens." ) );
        return;
    }

    const gate_data &gate = gates_data.obj( gid );

    p.add_msg_if_player( gate.pull_message.c_str() );
    p.assign_activity( ACT_OPEN_GATE, gate.moves );
    p.activity.placement = pos;
}
std::list<item> obtain_activity_items( player_activity &act, player &p )
{
    std::list<item> res;

    auto items = reorder_for_dropping( p, convert_to_indexes( act ) );

    debug_drop_list( items );

    while( !items.empty() && ( p.is_npc() || p.moves > 0 || items.front().consumed_moves == 0 ) ) {
        const auto &ait = items.front();

        p.mod_moves( -ait.consumed_moves );

        if( p.is_worn( *ait.it ) ) {
            p.takeoff( *ait.it, &res );
        } else if( ait.it->count_by_charges() ) {
            res.push_back( p.reduce_charges( const_cast<item *>( ait.it ), ait.count ) );
        } else {
            res.push_back( p.i_rem( ait.it ) );
        }

        items.pop_front();
    }
    // Avoid tumbling to the ground. Unload cleanly.
    const units::volume excessive_volume = p.volume_carried() - p.volume_capacity();
    if( excessive_volume > 0 ) {
        const auto excess = p.inv.remove_randomly_by_volume( excessive_volume );
        res.insert( res.begin(), excess.begin(), excess.end() );
    }
    // Load anything that remains (if any) into the activity
    act.values.clear();
    if( !items.empty() ) {
        for( const auto &drop : convert_to_indexes( p, items ) ) {
            act.values.push_back( drop.first );
            act.values.push_back( drop.second );
        }
    }
    // And either cancel if it's empty, or restart if it's not.
    if( act.values.empty() ) {
        p.cancel_activity();
    } else {
        p.assign_activity( act );
    }

    return res;
}
void activity_on_turn_move_loot( player_activity &, player &p )
{
    const activity_id act_move_loot = activity_id( "ACT_MOVE_LOOT" );
    auto &mgr = zone_manager::get_manager();
    if( g->m.check_vehicle_zones( g->get_levz() ) ) {
        mgr.cache_vzones();
    }
    const auto abspos = g->m.getabs( p.pos() );
    const auto &src_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos );
    vehicle *src_veh, *dest_veh;
    int src_part, dest_part;

    // Nuke the current activity, leaving the backlog alone.
    p.activity = player_activity();

    // sort source tiles by distance
    const auto &src_sorted = get_sorted_tiles_by_distance( abspos, src_set );

    if( !mgr.is_sorting() ) {
        mgr.start_sort( src_sorted );
    }

    for( auto &src : src_sorted ) {
        const auto &src_loc = g->m.getlocal( src );
        if( !g->m.inbounds( src_loc ) ) {
            if( !g->m.inbounds( p.pos() ) ) {
                // p is implicitly an NPC that has been moved off the map, so reset the activity
                // and unload them
                p.assign_activity( act_move_loot );
                p.set_moves( 0 );
                g->reload_npcs();
                mgr.end_sort();
                return;
            }
            std::vector<tripoint> route;
            route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(),
                                p.get_path_avoid() );
            if( route.empty() ) {
                // can't get there, can't do anything, skip it
                continue;
            }
            p.set_destination( route, player_activity( act_move_loot ) );
            mgr.end_sort();
            return;
        }

        bool is_adjacent_or_closer = square_dist( p.pos(), src_loc ) <= 1;

        // skip tiles in IGNORE zone and tiles on fire
        // (to prevent taking out wood off the lit brazier)
        // and inaccessible furniture, like filled charcoal kiln
        if( mgr.has( zone_type_id( "LOOT_IGNORE" ), src ) ||
            g->m.get_field( src_loc, fd_fire ) != nullptr ||
            !g->m.can_put_items_ter_furn( src_loc ) ) {
            continue;
        }

        // the boolean in this pair being true indicates the item is from a vehicle storage space
        auto items = std::vector<std::pair<item *, bool>>();

        //Check source for cargo part
        //map_stack and vehicle_stack are different types but inherit from item_stack
        // TODO: use one for loop
        if( const cata::optional<vpart_reference> vp = g->m.veh_at( src_loc ).part_with_feature( "CARGO",
                false ) ) {
            src_veh = &vp->vehicle();
            src_part = vp->part_index();
            for( auto &it : src_veh->get_items( src_part ) ) {
                items.push_back( std::make_pair( &it, true ) );
            }
        } else {
            src_veh = nullptr;
            src_part = -1;
        }
        for( auto &it : g->m.i_at( src_loc ) ) {
            items.push_back( std::make_pair( &it, false ) );
        }
        //Skip items that have already been processed
        for( auto it = items.begin() + mgr.get_num_processed( src ); it < items.end(); it++ ) {

            mgr.increment_num_processed( src );

            const auto thisitem = it->first;

            if( thisitem->made_of_from_type( LIQUID ) ) { // skip unpickable liquid
                continue;
            }

            // Only if it's from a vehicle do we use the vehicle source location information.
            vehicle *this_veh = it->second ? src_veh : nullptr;
            const int this_part = it->second ? src_part : -1;

            const auto id = mgr.get_near_zone_type_for_item( *thisitem, abspos );

            // checks whether the item is already on correct loot zone or not
            // if it is, we can skip such item, if not we move the item to correct pile
            // think empty bag on food pile, after you ate the content
            if( !mgr.has( id, src ) ) {
                const auto &dest_set = mgr.get_near( id, abspos );

                for( auto &dest : dest_set ) {
                    const auto &dest_loc = g->m.getlocal( dest );

                    //Check destination for cargo part
                    if( const cata::optional<vpart_reference> vp = g->m.veh_at( dest_loc ).part_with_feature( "CARGO",
                            false ) ) {
                        dest_veh = &vp->vehicle();
                        dest_part = vp->part_index();
                    } else {
                        dest_veh = nullptr;
                        dest_part = -1;
                    }

                    // skip tiles with inaccessible furniture, like filled charcoal kiln
                    if( !g->m.can_put_items_ter_furn( dest_loc ) ) {
                        continue;
                    }

                    units::volume free_space;
                    // if there's a vehicle with space do not check the tile beneath
                    if( dest_veh ) {
                        free_space = dest_veh->free_volume( dest_part );
                    } else {
                        free_space = g->m.free_volume( dest_loc );
                    }
                    // check free space at destination
                    if( free_space >= thisitem->volume() ) {
                        // before we move any item, check if player is at or
                        // adjacent to the loot source tile
                        if( !is_adjacent_or_closer ) {
                            std::vector<tripoint> route;
                            bool adjacent = false;

                            // get either direct route or route to nearest adjacent tile if
                            // source tile is impassable
                            if( g->m.passable( src_loc ) ) {
                                route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(),
                                                    p.get_path_avoid() );
                            } else {
                                // immpassable source tile (locker etc.),
                                // get route to nerest adjacent tile instead
                                route = route_adjacent( p, src_loc );
                                adjacent = true;
                            }

                            // check if we found path to source / adjacent tile
                            if( route.empty() ) {
                                add_msg( m_info, _( "%s can't reach the source tile. Try to sort out loot without a cart." ),
                                         p.disp_name() );
                                mgr.end_sort();
                                return;
                            }

                            // shorten the route to adjacent tile, if necessary
                            if( !adjacent ) {
                                route.pop_back();
                            }

                            // set the destination and restart activity after player arrives there
                            // we don't need to check for safe mode,
                            // activity will be restarted only if
                            // player arrives on destination tile
                            p.set_destination( route, player_activity( act_move_loot ) );
                            mgr.end_sort();
                            return;
                        }
                        move_item( p, *thisitem, thisitem->count(), src_loc, dest_loc, this_veh, this_part );

                        // moved item away from source so decrement
                        mgr.decrement_num_processed( src );

                        break;
                    }
                }
                if( p.moves <= 0 ) {
                    // Restart activity and break from cycle.
                    p.assign_activity( act_move_loot );
                    mgr.end_sort();
                    return;
                }
            }
        }
    }

    // If we got here without restarting the activity, it means we're done
    add_msg( m_info, string_format( _( "%s sorted out every item possible." ), p.disp_name() ) );
    mgr.end_sort();
}