Example #1
0
// Pick up items at (pos).
void Pickup::pick_up( const tripoint &pos, int min )
{
    int veh_root_part = 0;
    int cargo_part = -1;

    vehicle *veh = g->m.veh_at (pos, veh_root_part);
    bool from_vehicle = false;

    if( min != -1 ) {
        cargo_part = interact_with_vehicle( veh, pos, veh_root_part );
        from_vehicle = cargo_part >= 0;
        if( cargo_part == -2 ) {
            // -2 indicates that we already interacted with the vehicle.
            return;
        }
    }

    if (g->m.has_flag("SEALED", pos)) {
        return;
    }

    //min == -1 is Autopickup
    if (!g->u.can_pickup(min != -1)) { // no message on autopickup (-1)
        return;
    }

    if( !from_vehicle ) {
        bool isEmpty = (g->m.i_at(pos).empty());

        // Hide the pickup window if this is a toilet and there's nothing here
        // but water.
        if ((!isEmpty) && g->m.furn(pos) == f_toilet) {
            isEmpty = true;
            for( auto maybe_water : g->m.i_at(pos) ) {
                if( maybe_water.typeId() != "water") {
                    isEmpty = false;
                    break;
                }
            }
        }

        if (isEmpty && (min != -1 || !OPTIONS["AUTO_PICKUP_ADJACENT"] )) {
            return;
        }
    }

    // which items are we grabbing?
    std::vector<item> here;
    if( from_vehicle ) {
        auto vehitems = veh->get_items(cargo_part);
        here.resize( vehitems.size() );
        std::copy( vehitems.begin(), vehitems.end(), here.begin() );
    } else {
        auto mapitems = g->m.i_at(pos);
        here.resize( mapitems.size() );
        std::copy( mapitems.begin(), mapitems.end(), here.begin() );
    }

    if (min == -1) {
        if( g->check_zone( "NO_AUTO_PICKUP", pos ) ) {
            here.clear();
        }

        // Recursively pick up adjacent items if that option is on.
        if( OPTIONS["AUTO_PICKUP_ADJACENT"] && g->u.pos() == pos ) {
            //Autopickup adjacent
            direction adjacentDir[8] = {NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST};
            for( auto &elem : adjacentDir ) {

                tripoint apos = tripoint( direction_XY( elem ), 0 );
                apos += pos;

                if( g->m.has_flag( "SEALED", apos ) ) {
                    continue;
                }
                if( g->check_zone( "NO_AUTO_PICKUP", apos ) ) {
                    continue;
                }
                pick_up( apos, min );
            }
        }
    }

    // Not many items, just grab them
    if ((int)here.size() <= min && min != -1) {
        g->u.assign_activity( ACT_PICKUP, 0 );
        g->u.activity.placement = pos - g->u.pos();
        g->u.activity.values.push_back( from_vehicle );
        // Only one item means index is 0.
        g->u.activity.values.push_back( 0 );
        // auto-pickup means pick up all.
        g->u.activity.values.push_back( 0 );
        return;
    }

    if(min != -1) { // don't bother if we're just autopickup-ing
        g->temp_exit_fullscreen();
    }
    bool sideStyle = use_narrow_sidebar();

    // Otherwise, we have Autopickup, 2 or more items and should list them, etc.
    int maxmaxitems = sideStyle ? TERMY : getmaxy(g->w_messages) - 3;

    int itemsH = std::min(25, TERMY / 2);
    int pickupBorderRows = 3;

    // The pickup list may consume the entire terminal, minus space needed for its
    // header/footer and the item info window.
    int minleftover = itemsH + pickupBorderRows;
    if(maxmaxitems > TERMY - minleftover) {
        maxmaxitems = TERMY - minleftover;
    }

    const int minmaxitems = sideStyle ? 6 : 9;

    std::vector<bool> getitem;
    getitem.resize(here.size(), false);

    int maxitems = here.size();
    maxitems = (maxitems < minmaxitems ? minmaxitems : (maxitems > maxmaxitems ? maxmaxitems :
                maxitems ));

    int itemcount = 0;
    std::map<int, unsigned int> pickup_count; // Count of how many we'll pick up from each stack

    if (min == -1) { //Auto Pickup, select matching items
        if( !select_autopickup_items( here, getitem) ) {
            // If we didn't find anything, bail out now.
            return;
        }
    } else {
        int pickupH = maxitems + pickupBorderRows;
        int pickupW = getmaxx(g->w_messages);
        int pickupY = VIEW_OFFSET_Y;
        int pickupX = getbegx(g->w_messages);

        int itemsW = pickupW;
        int itemsY = sideStyle ? pickupY + pickupH : TERMY - itemsH;
        int itemsX = pickupX;

        WINDOW *w_pickup    = newwin(pickupH, pickupW, pickupY, pickupX);
        WINDOW *w_item_info = newwin(itemsH,  itemsW,  itemsY,  itemsX);
        WINDOW_PTR w_pickupptr( w_pickup );
        WINDOW_PTR w_item_infoptr( w_item_info );

        int ch = ' ';
        int start = 0, cur_it;
        int new_weight = g->u.weight_carried(), new_volume = g->u.volume_carried();
        bool update = true;
        mvwprintw(w_pickup, 0, 0, _("PICK UP"));
        int selected = 0;
        int last_selected = -1;

        if(g->was_fullscreen) {
            g->draw_ter();
        }
        // Now print the two lists; those on the ground and about to be added to inv
        // Continue until we hit return or space
        do {
            static const std::string pickup_chars =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;";
            int idx = -1;
            for (int i = 1; i < pickupH; i++) {
                mvwprintw(w_pickup, i, 0,
                          "                                                ");
            }
            if (ch >= '0' && ch <= '9') {
                ch = (char)ch - '0';
                itemcount *= 10;
                itemcount += ch;
                if( itemcount < 0 ) {
                    itemcount = 0;
                }
            } else if ( ch == '<' || ch == KEY_PPAGE ) {
                if ( start > 0 ) {
                    start -= maxitems;
                } else {
                    start = (int)( (here.size()-1) / maxitems ) * maxitems;
                }
                selected = start;
                mvwprintw(w_pickup, maxitems + 2, 0, "         ");
            } else if ( ch == '>' || ch == KEY_NPAGE ) {
                if ( start + maxitems < (int)here.size() ) {
                    start += maxitems;
                } else {
                    start = 0;
                }
                selected = start;
                mvwprintw(w_pickup, maxitems + 2, pickupH, "            ");
            } else if ( ch == KEY_UP ) {
                selected--;
                if ( selected < 0 ) {
                    selected = here.size() - 1;
                    start = (int)( here.size() / maxitems ) * maxitems;
                    if (start >= (int)here.size()) {
                        start -= maxitems;
                    }
                } else if ( selected < start ) {
                    start -= maxitems;
                }
            } else if ( ch == KEY_DOWN ) {
                selected++;
                if ( selected >= (int)here.size() ) {
                    selected = 0;
                    start = 0;
                } else if ( selected >= start + maxitems ) {
                    start += maxitems;
                }
            } else if ( selected >= 0 && (
                            ( ch == KEY_RIGHT && !getitem[selected]) ||
                            ( ch == KEY_LEFT && getitem[selected] )
                        ) ) {
                idx = selected;
            } else if ( ch == '`' ) {
                std::string ext = string_input_popup(
                                      _("Enter 2 letters (case sensitive):"), 3, "", "", "", 2);
                if(ext.size() == 2) {
                    int p1 = pickup_chars.find(ext.at(0));
                    int p2 = pickup_chars.find(ext.at(1));
                    if ( p1 != -1 && p2 != -1 ) {
                        idx = pickup_chars.size() + ( p1 * pickup_chars.size() ) + p2;
                    }
                }
            } else {
                idx = ( ch <= 127 ) ? pickup_chars.find(ch) : -1;
            }

            if( idx >= 0 && idx < (int)here.size()) {
                if( getitem[idx] ) {
                    if( pickup_count[idx] != 0 && (int)pickup_count[idx] < here[idx].charges ) {
                        item temp = here[idx];
                        temp.charges = pickup_count[idx];
                        new_weight -= temp.weight();
                        new_volume -= temp.volume();
                    } else {
                        new_weight -= here[idx].weight();
                        new_volume -= here[idx].volume();
                    }
                }
                if (itemcount != 0 || pickup_count[idx] == 0) {
                    if (itemcount >= here[idx].charges || !here[idx].count_by_charges()) {
                        // Ignore the count if we pickup the whole stack anyway
                        // or something that is not counted by charges (tools)
                        itemcount = 0;
                    }
                    pickup_count[idx] = itemcount;
                    itemcount = 0;
                }

                // Note: this might not change the value of getitem[idx] at all!
                getitem[idx] = ( ch == KEY_RIGHT ? true : ( ch == KEY_LEFT ? false : !getitem[idx] ) );
                if ( ch != KEY_RIGHT && ch != KEY_LEFT) {
                    selected = idx;
                    start = (int)( idx / maxitems ) * maxitems;
                }

                if (getitem[idx]) {
                    if (pickup_count[idx] != 0 &&
                        (int)pickup_count[idx] < here[idx].charges) {
                        item temp = here[idx];
                        temp.charges = pickup_count[idx];
                        new_weight += temp.weight();
                        new_volume += temp.volume();
                    } else {
                        new_weight += here[idx].weight();
                        new_volume += here[idx].volume();
                    }
                } else {
                    pickup_count[idx] = 0;
                }
                update = true;
            }

            if ( selected != last_selected ) {
                last_selected = selected;
                werase(w_item_info);
                if ( selected >= 0 && selected <= (int)here.size() - 1 ) {
                    std::vector<iteminfo> vThisItem, vDummy;
                    here[selected].info(true, vThisItem);

                    draw_item_info(w_item_info, "", vThisItem, vDummy, 0, true, true);
                }
                draw_border(w_item_info);
                mvwprintw(w_item_info, 0, 2, "< ");
                trim_and_print(w_item_info, 0, 4, itemsW - 8, c_white, "%s >", here[selected].display_name().c_str());
                wrefresh(w_item_info);
            }

            if (ch == ',') {
                int count = 0;
                for (size_t i = 0; i < here.size(); i++) {
                    if (getitem[i]) {
                        count++;
                    } else {
                        new_weight += here[i].weight();
                        new_volume += here[i].volume();
                    }
                    getitem[i] = true;
                }
                if (count == (int)here.size()) {
                    for (size_t i = 0; i < here.size(); i++) {
                        getitem[i] = false;
                    }
                    new_weight = g->u.weight_carried();
                    new_volume = g->u.volume_carried();
                }
                update = true;
            }

            for (cur_it = start; cur_it < start + maxitems; cur_it++) {
                mvwprintw(w_pickup, 1 + (cur_it % maxitems), 0,
                          "                                        ");
                if (cur_it < (int)here.size()) {
                    nc_color icolor = here[cur_it].color_in_inventory();
                    if (cur_it == selected) {
                        icolor = hilite(icolor);
                    }

                    if (cur_it < (int)pickup_chars.size() ) {
                        mvwputch(w_pickup, 1 + (cur_it % maxitems), 0, icolor,
                                 char(pickup_chars[cur_it]));
                    } else {
                        int p = cur_it - pickup_chars.size();
                        int p1 = p / pickup_chars.size();
                        int p2 = p % pickup_chars.size();
                        mvwprintz(w_pickup, 1 + (cur_it % maxitems), 0, icolor, "`%c%c",
                                  char(pickup_chars[p1]), char(pickup_chars[p2]));
                    }
                    if (getitem[cur_it]) {
                        if (pickup_count[cur_it] == 0) {
                            wprintz(w_pickup, c_ltblue, " + ");
                        } else {
                            wprintz(w_pickup, c_ltblue, " # ");
                        }
                    } else {
                        wprintw(w_pickup, " - ");
                    }
                    std::string item_name = here[cur_it].display_name();
                    if (OPTIONS["ITEM_SYMBOLS"]) {
                        item_name = string_format("%c %s", here[cur_it].symbol(), item_name.c_str());
                    }
                    trim_and_print(w_pickup, 1 + (cur_it % maxitems), 6, pickupW - 4, icolor,
                                   "%s", item_name.c_str());
                }
            }

            int pw = pickupW;
            const char *unmark = _("[left] Unmark");
            const char *scroll = _("[up/dn] Scroll");
            const char *mark   = _("[right] Mark");
            mvwprintw(w_pickup, maxitems + 1, 0,                         unmark);
            mvwprintw(w_pickup, maxitems + 1, (pw - std::strlen(scroll)) / 2, scroll);
            mvwprintw(w_pickup, maxitems + 1,  pw - std::strlen(mark),        mark);
            const char *prev = _("[pgup] Prev");
            const char *all = _("[,] All");
            const char *next   = _("[pgdn] Next");
            mvwprintw(w_pickup, maxitems + 2, 0, prev);
            mvwprintw(w_pickup, maxitems + 2, (pw - std::strlen(all)) / 2, all);
            mvwprintw(w_pickup, maxitems + 2, pw - std::strlen(next), next);

            if (update) { // Update weight & volume information
                update = false;
                for (int i = 9; i < pickupW; ++i) {
                    mvwaddch(w_pickup, 0, i, ' ');
                }
                mvwprintz(w_pickup, 0,  9,
                          (new_weight > g->u.weight_capacity() ? c_red : c_white),
                          _("Wgt %.1f"), g->u.convert_weight(new_weight) + 0.05); // +0.05 to round up
                wprintz(w_pickup, c_white, "/%.1f", g->u.convert_weight(g->u.weight_capacity()));
                mvwprintz(w_pickup, 0, 24,
                          (new_volume > g->u.volume_capacity() ? c_red : c_white),
                          _("Vol %d"), new_volume);
                wprintz(w_pickup, c_white, "/%d", g->u.volume_capacity());
            }
            wrefresh(w_pickup);

            ch = (int)getch();

        } while (ch != ' ' && ch != '\n' && ch != KEY_ENTER && ch != KEY_ESCAPE);

        bool item_selected = false;
        // Check if we have selected an item.
        for( auto selection : getitem ) {
            if( selection ) {
                item_selected = true;
            }
        }
        if( (ch != '\n' && ch != KEY_ENTER) || !item_selected ) {
            w_pickupptr.reset();
            w_item_infoptr.reset();
            add_msg(_("Never mind."));
            g->reenter_fullscreen();
            g->refresh_all();
            return;
        }
    }

    // At this point we've selected our items, register an activity to pick them up.
    g->u.assign_activity( ACT_PICKUP, 0 );
    g->u.activity.placement = pos - g->u.pos();
    g->u.activity.values.push_back( from_vehicle );
    if( min == -1 ) {
        // Auto pickup will need to auto resume since there can be several of them on the stack.
        g->u.activity.auto_resume = true;
    }
    for (size_t i = 0; i < here.size(); i++) {
        if( getitem[i] ) {
            g->u.activity.values.push_back( i );
            g->u.activity.values.push_back( pickup_count[i] );
        }
    }

    g->reenter_fullscreen();
}
std::vector<overmap *> overmapbuffer::get_overmaps_near( const point &p, const int radius )
{
    return get_overmaps_near( tripoint( p.x, p.y, 0 ), radius );
}
tripoint overmapbuffer::om_to_sm_copy(const tripoint& p) {
    return tripoint(p.x * 2 * OMAPX, p.y * 2 * OMAPX, p.z);
}
tripoint overmapbuffer::omt_to_seg_copy(const tripoint& p) {
    return tripoint(divide(p.x, 32), divide(p.y, 32), p.z);
}
Example #5
0
std::vector<tripoint> map::route( const tripoint &f, const tripoint &t,
                                  const int bash, const int maxdist,
                                  const std::set<tripoint> &pre_closed ) const
{
    /* TODO: If the origin or destination is out of bound, figure out the closest
     * in-bounds point and go to that, then to the real origin/destination.
     */
    std::vector<tripoint> ret;

    if( !inbounds( f ) ) {
        return ret;
    }

    if( !inbounds( t ) ) {
        tripoint clipped = t;
        clip_to_bounds( clipped );
        return route( f, clipped, bash, maxdist );
    }
    // First, check for a simple straight line on flat ground
    // Except when the line contains a pre-closed tile - we need to do regular pathing then
    if( f.z == t.z && clear_path( f, t, -1, 2, 2 ) ) {
        const auto line_path = line_to( f, t );
        const std::set<tripoint> sorted_line( line_path.begin(), line_path.end() );

        if( is_disjoint( sorted_line, pre_closed ) ) {
            return line_path;
        }
    }

    const int pad = 16;  // Should be much bigger - low value makes pathfinders dumb!
    int minx = std::min( f.x, t.x ) - pad;
    int miny = std::min( f.y, t.y ) - pad;
    int minz = std::min( f.z, t.z ); // TODO: Make this way bigger
    int maxx = std::max( f.x, t.x ) + pad;
    int maxy = std::max( f.y, t.y ) + pad;
    int maxz = std::max( f.z, t.z ); // Same TODO as above
    clip_to_bounds( minx, miny, minz );
    clip_to_bounds( maxx, maxy, maxz );

    pathfinder pf( minx, miny, maxx, maxy );
    // Make NPCs not want to path through player
    // But don't make player pathing stop working
    for( const auto &p : pre_closed ) {
        if( p.x >= minx && p.x < maxx && p.y >= miny && p.y < maxy ) {
            pf.close_point( p );
        }
    }

    // Start and end must not be closed
    pf.unclose_point( f );
    pf.unclose_point( t );
    pf.add_point( 0, 0, f, f );

    bool done = false;

    do {
        auto cur = pf.get_next();

        const int parent_index = flat_index( cur.x, cur.y );
        auto &layer = pf.get_layer( cur.z );
        auto &cur_state = layer.state[parent_index];
        if( cur_state == ASL_CLOSED ) {
            continue;
        }

        if( layer.gscore[parent_index] > maxdist ) {
            // Shortest path would be too long, return empty vector
            return std::vector<tripoint>();
        }

        if( cur == t ) {
            done = true;
            break;
        }

        cur_state = ASL_CLOSED;

        const auto &pf_cache = get_pathfinding_cache_ref( cur.z );
        const auto cur_special = pf_cache.special[cur.x][cur.y];

        // 7 3 5
        // 1 . 2
        // 6 4 8
        constexpr std::array<int, 8> x_offset{{ -1,  1,  0,  0,  1, -1, -1, 1 }};
        constexpr std::array<int, 8> y_offset{{  0,  0, -1,  1, -1,  1, -1, 1 }};
        for( size_t i = 0; i < 8; i++ ) {
            const tripoint p( cur.x + x_offset[i], cur.y + y_offset[i], cur.z );
            const int index = flat_index( p.x, p.y );

            // @todo Remove this and instead have sentinels at the edges
            if( p.x < minx || p.x >= maxx || p.y < miny || p.y >= maxy ) {
                continue;
            }

            if( layer.state[index] == ASL_CLOSED ) {
                continue;
            }

            // Penalize for diagonals or the path will look "unnatural"
            int newg = layer.gscore[parent_index] + ( ( cur.x != p.x && cur.y != p.y ) ? 1 : 0 );

            const auto p_special = pf_cache.special[p.x][p.y];

            constexpr auto non_normal = PF_SLOW | PF_WALL | PF_VEHICLE | PF_TRAP;
            // @todo De-uglify, de-huge-n
            if( !( p_special & non_normal ) ) {
                // Boring flat dirt - the most common case above the ground
                newg += 2;
            } else {
                int part = -1;
                const maptile &tile = maptile_at_internal( p );
                const auto &terrain = tile.get_ter_t();
                const auto &furniture = tile.get_furn_t();
                const vehicle *veh = veh_at_internal( p, part );

                const int cost = move_cost_internal( furniture, terrain, veh, part );
                // Don't calculate bash rating unless we intend to actually use it
                const int rating = ( bash == 0 || cost != 0 ) ? -1 :
                                   bash_rating_internal( bash, furniture, terrain, false, veh, part );

                if( cost == 0 && rating <= 0 && !terrain.open && veh == nullptr ) {
                    layer.state[index] = ASL_CLOSED; // Close it so that next time we won't try to calc costs
                    continue;
                }

                newg += cost;
                if( cost == 0 ) {
                    // Handle all kinds of doors
                    // Only try to open INSIDE doors from the inside
                    if( terrain.open &&
                        ( !terrain.has_flag( "OPENCLOSE_INSIDE" ) || !is_outside( cur ) ) ) {
                        newg += 4; // To open and then move onto the tile
                    } else if( veh != nullptr ) {
                        part = veh->obstacle_at_part( part );
                        int dummy = -1;
                        if( veh->part_flag( part, VPFLAG_OPENABLE ) &&
                            ( !veh->part_flag( part, "OPENCLOSE_INSIDE" ) ||
                              veh_at_internal( cur, dummy ) == veh ) ) {
                            // Handle car doors, but don't try to path through curtains
                            newg += 10; // One turn to open, 4 to move there
                        } else if( part != -1 && bash > 0 ) {
                            // Car obstacle that isn't a door
                            // Or there is no car obstacle, but the car is wedged into an obstacle,
                            //  in which case part == -1
                            newg += 2 * veh->parts[part].hp / bash + 8 + 4;
                        } else {
                            if( !veh->part_flag( part, VPFLAG_OPENABLE ) ) {
                                // Won't be openable, don't try from other sides
                                layer.state[index] = ASL_CLOSED;
                            }

                            continue;
                        }
                    } else if( rating > 1 ) {
                        // Expected number of turns to bash it down, 1 turn to move there
                        // and 5 turns of penalty not to trash everything just because we can
                        newg += ( 20 / rating ) + 2 + 10;
                    } else if( rating == 1 ) {
                        // Desperate measures, avoid whenever possible
                        newg += 500;
                    } else {
                        continue; // Unbashable and unopenable from here
                    }
                }

                if( p_special & PF_TRAP ) {
                    const auto &ter_trp = terrain.trap.obj();
                    const auto &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp;
                    if( !trp.is_benign() ) {
                        // For now make them detect all traps
                        if( has_zlevels() && terrain.has_flag( TFLAG_NO_FLOOR ) ) {
                            // Special case - ledge in z-levels
                            // Warning: really expensive, needs a cache
                            if( valid_move( p, tripoint( p.x, p.y, p.z - 1 ), false, true ) ) {
                                tripoint below( p.x, p.y, p.z - 1 );
                                if( !has_flag( TFLAG_NO_FLOOR, below ) ) {
                                    // Otherwise this would have been a huge fall
                                    auto &layer = pf.get_layer( p.z - 1 );
                                    // From cur, not p, because we won't be walking on air
                                    pf.add_point( layer.gscore[parent_index] + 10,
                                                  layer.score[parent_index] + 10 + 2 * rl_dist( below, t ),
                                                  cur, below );
                                }

                                // Close p, because we won't be walking on it
                                layer.state[index] = ASL_CLOSED;
                                continue;
                            }
                        } else {
                            // Otherwise it's walkable
                            newg += 500;
                        }
                    }
                }
            }

            // If not visited, add as open
            // If visited, add it only if we can do so with better score
            if( layer.state[index] == ASL_NONE || newg < layer.gscore[index] ) {
                pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p );
            }
        }

        if( !has_zlevels() || !( cur_special & PF_UPDOWN ) ) {
            // The part below is only for z-level pathing
            continue;
        }

        const maptile &parent_tile = maptile_at_internal( cur );
        const auto &parent_terrain = parent_tile.get_ter_t();
        if( cur.z > minz && parent_terrain.has_flag( TFLAG_GOES_DOWN ) ) {
            tripoint dest( cur.x, cur.y, cur.z - 1 );
            dest = vertical_move_destination<TFLAG_GOES_UP>( *this, dest );
            if( inbounds( dest ) ) {
                auto &layer = pf.get_layer( dest.z );
                pf.add_point( layer.gscore[parent_index] + 2,
                              layer.score[parent_index] + 2 * rl_dist( dest, t ),
                              cur, dest );
            }
        }
        if( cur.z < maxz && parent_terrain.has_flag( TFLAG_GOES_UP ) ) {
            tripoint dest( cur.x, cur.y, cur.z + 1 );
            dest = vertical_move_destination<TFLAG_GOES_DOWN>( *this, dest );
            if( inbounds( dest ) ) {
                auto &layer = pf.get_layer( dest.z );
                pf.add_point( layer.gscore[parent_index] + 2,
                              layer.score[parent_index] + 2 * rl_dist( dest, t ),
                              cur, dest );
            }
        }
        if( cur.z < maxz && parent_terrain.has_flag( TFLAG_RAMP ) &&
            valid_move( cur, tripoint( cur.x, cur.y, cur.z + 1 ), false, true ) ) {
            auto &layer = pf.get_layer( cur.z + 1 );
            for( size_t it = 0; it < 8; it++ ) {
                const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 );
                pf.add_point( layer.gscore[parent_index] + 4,
                              layer.score[parent_index] + 4 + 2 * rl_dist( above, t ),
                              cur, above );
            }
        }
    } while( !done && !pf.empty() );

    ret.reserve( rl_dist( f, t ) * 2 );
    if( done ) {
        tripoint cur = t;
        // Just to limit max distance, in case something weird happens
        for( int fdist = maxdist; fdist != 0; fdist-- ) {
            const int cur_index = flat_index( cur.x, cur.y );
            const auto &layer = pf.get_layer( cur.z );
            const tripoint &par = layer.parent[cur_index];
            if( cur == f ) {
                break;
            }

            ret.push_back( cur );
            // Jumps are acceptable on 1 z-level changes
            // This is because stairs teleport the player too
            if( rl_dist( cur, par ) > 1 && abs( cur.z - par.z ) != 1 ) {
                debugmsg( "Jump in our route! %d:%d:%d->%d:%d:%d",
                          cur.x, cur.y, cur.z, par.x, par.y, par.z );
                return ret;
            }

            cur = par;
        }

        std::reverse( ret.begin(), ret.end() );
    }

    return ret;
}
Example #6
0
/*
 * Drawing-related functions
 */
void Creature::draw( const catacurses::window &w, int player_x, int player_y, bool inverted ) const
{
    draw( w, tripoint( player_x, player_y, posz() ), inverted );
}
void board_up( map &m, const tripoint &start, const tripoint &end )
{
    std::vector<tripoint> furnitures1;
    std::vector<tripoint> furnitures2;
    std::vector<tripoint> boardables;
    tripoint p;
    p.z = m.get_abs_sub().z;
    int &x = p.x;
    int &y = p.y;
    int &z = p.z;
    for( x = start.x; x < end.x; x++ ) {
        for( y = start.y; y < end.y; y++ ) {
            bool must_board_around = false;
            const ter_id t = m.ter( x, y );
            if( t == t_window_domestic || t == t_window || t == t_window_no_curtains ) {
                // Windows are always to the outside and must be boarded
                must_board_around = true;
                m.ter_set( p, t_window_boarded );
            } else if( t == t_door_c || t == t_door_locked || t == t_door_c_peep ) {
                // Only board up doors that lead to the outside
                if( m.is_outside( tripoint( x + 1, y, z ) ) ||
                    m.is_outside( tripoint( x - 1, y, z ) ) ||
                    m.is_outside( tripoint( x, y + 1, z ) ) ||
                    m.is_outside( tripoint( x, y - 1, z ) ) ) {
                    m.ter_set( p, t_door_boarded );
                    must_board_around = true;
                } else {
                    // internal doors are opened instead
                    m.ter_set( p, t_door_o );
                }
            }
            if( must_board_around ) {
                // Board up the surroundings of the door/window
                add_boardable( m, tripoint( x + 1, y, z ), boardables );
                add_boardable( m, tripoint( x - 1, y, z ), boardables );
                add_boardable( m, tripoint( x, y + 1, z ), boardables );
                add_boardable( m, tripoint( x, y - 1, z ), boardables );
                add_boardable( m, tripoint( x + 1, y + 1, z ), boardables );
                add_boardable( m, tripoint( x - 1, y + 1, z ), boardables );
                add_boardable( m, tripoint( x + 1, y - 1, z ), boardables );
                add_boardable( m, tripoint( x - 1, y - 1, z ), boardables );
            }
        }
    }
    // Find all furniture that can be used to board up some place
    for( x = start.x; x < end.x; x++ ) {
        for( y = start.y; y < end.y; y++ ) {
            if( std::find( boardables.begin(), boardables.end(), p ) != boardables.end() ) {
                continue;
            }
            if( !m.has_furn( p ) ) {
                continue;
            }
            // If the furniture is movable and the character can move it, use it to barricade
            // g->u is workable here as NPCs by definition are not starting the game.  (Let's hope.)
            ///\EFFECT_STR determines what furniture might be used as a starting area barricade
            if( m.furn_at( p ).move_str_req > 0 && m.furn_at( p ).move_str_req < g->u.get_str() ) {
                if( m.furn_at( p ).movecost == 0 ) {
                    // Obstacles are better, prefer them
                    furnitures1.push_back( p );
                } else {
                    furnitures2.push_back( p );
                }
            }
        }
    }
    while( ( !furnitures1.empty() || !furnitures2.empty() ) && !boardables.empty() ) {
        const tripoint fp = random_entry_removed( furnitures1.empty() ? furnitures2 : furnitures1 );
        const tripoint bp = random_entry_removed( boardables );
        m.furn_set( bp, m.furn( fp ) );
        m.furn_set( fp, f_null );
        auto destination_items = m.i_at( bp );
        for( auto moved_item : m.i_at( fp ) ) {
            destination_items.push_back( moved_item );
        }
        m.i_clear( fp );
    }
}
Example #8
0
// We're reading in way too many entities here to mess around with creating sub-objects and
// seeking around in them, so we're using the json streaming API.
submap *mapbuffer::unserialize_submaps( const tripoint &p )
{
    // Map the tripoint to the submap quad that stores it.
    const tripoint om_addr = overmapbuffer::sm_to_omt_copy( p );
    const tripoint segment_addr = overmapbuffer::omt_to_seg_copy( om_addr );
    std::stringstream quad_path;
    quad_path << world_generator->active_world->world_path << "/maps/" <<
              segment_addr.x << "." << segment_addr.y << "." << segment_addr.z << "/" <<
              om_addr.x << "." << om_addr.y << "." << om_addr.z << ".map";

    std::ifstream fin( quad_path.str().c_str() );
    if( !fin.is_open() ) {
        // If it doesn't exist, trigger generating it.
        return NULL;
    }

    JsonIn jsin( fin );
    jsin.start_array();
    while( !jsin.end_array() ) {
        std::unique_ptr<submap> sm(new submap());
        tripoint submap_coordinates;
        jsin.start_object();
        bool rubpow_update = false;
        while( !jsin.end_object() ) {
            std::string submap_member_name = jsin.get_member_name();
            if( submap_member_name == "version" ) {
                if (jsin.get_int() < 22) {
                    rubpow_update = true;
                }
            } else if( submap_member_name == "coordinates" ) {
                jsin.start_array();
                int locx = jsin.get_int();
                int locy = jsin.get_int();
                int locz = jsin.get_int();
                jsin.end_array();
                submap_coordinates = tripoint( locx, locy, locz );
            } else if( submap_member_name == "turn_last_touched" ) {
                sm->turn_last_touched = jsin.get_int();
            } else if( submap_member_name == "temperature" ) {
                sm->temperature = jsin.get_int();
            } else if( submap_member_name == "terrain" ) {
                // TODO: try block around this to error out if we come up short?
                jsin.start_array();
                // Small duplication here so that the update check is only performed once
                if (rubpow_update) {
                    std::string ter_string;
                    item rock = item("rock", 0);
                    item chunk = item("steel_chunk", 0);
                    for( int j = 0; j < SEEY; j++ ) {
                        for( int i = 0; i < SEEX; i++ ) {
                            ter_string = jsin.get_string();
                            if (ter_string == "t_rubble") {
                                sm->ter[i][j] = termap[ "t_dirt" ].loadid;
                                sm->frn[i][j] = furnmap[ "f_rubble" ].loadid;
                                sm->itm[i][j].push_back( rock );
                                sm->itm[i][j].push_back( rock );
                            } else if (ter_string == "t_wreckage"){
                                sm->ter[i][j] = termap[ "t_dirt" ].loadid;
                                sm->frn[i][j] = furnmap[ "f_wreckage" ].loadid;
                                sm->itm[i][j].push_back( chunk );
                                sm->itm[i][j].push_back( chunk );
                            } else if (ter_string == "t_ash"){
                                sm->ter[i][j] = termap[ "t_dirt" ].loadid;
                                sm->frn[i][j] = furnmap[ "f_ash" ].loadid;
                            } else if (ter_string == "t_pwr_sb_support_l"){
                                sm->ter[i][j] = termap[ "t_support_l" ].loadid;
                            } else if (ter_string == "t_pwr_sb_switchgear_l"){
                                sm->ter[i][j] = termap[ "t_switchgear_l" ].loadid;
                            } else if (ter_string == "t_pwr_sb_switchgear_s"){
                                sm->ter[i][j] = termap[ "t_switchgear_s" ].loadid;
                            } else {
                                sm->ter[i][j] = terfind( ter_string );
                            }
                        }
                    }
                } else {
                    for( int j = 0; j < SEEY; j++ ) {
                        for( int i = 0; i < SEEX; i++ ) {
                            sm->ter[i][j] = terfind( jsin.get_string() );
                        }
                    }
                }
                jsin.end_array();
            } else if( submap_member_name == "radiation" ) {
                int rad_cell = 0;
                jsin.start_array();
                while( !jsin.end_array() ) {
                    int rad_strength = jsin.get_int();
                    int rad_num = jsin.get_int();
                    for( int i = 0; i < rad_num; ++i ) {
                        // A little array trick here, assign to it as a 1D array.
                        // If it's not in bounds we're kinda hosed anyway.
                        sm->set_radiation(0, rad_cell, rad_strength);
                        rad_cell++;
                    }
                }
            } else if( submap_member_name == "furniture" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    sm->frn[i][j] = furnmap[ jsin.get_string() ].loadid;
                    jsin.end_array();
                }
            } else if( submap_member_name == "items" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.start_array();
                    while( !jsin.end_array() ) {
                        item tmp;
                        jsin.read( tmp );

                        if( tmp.is_emissive() ) {
                            sm->update_lum_add(tmp, i, j);
                        }

                        tmp.visit_items( [ &sm, i, j ]( item *it ) {
                            for( auto& e: it->magazine_convert() ) {
                                sm->itm[i][j].push_back( e );
                            }
                            return VisitResponse::NEXT;
                        } );

                        sm->itm[i][j].push_back( tmp );
                        if( tmp.needs_processing() ) {
                            sm->active_items.add( std::prev(sm->itm[i][j].end()), point( i, j ) );
                        }
                    }
                }
            } else if( submap_member_name == "traps" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    // TODO: jsin should support returning an id like jsin.get_id<trap>()
                    sm->trp[i][j] = trap_str_id( jsin.get_string() );
                    jsin.end_array();
                }
            } else if( submap_member_name == "fields" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    // Coordinates loop
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.start_array();
                    while( !jsin.end_array() ) {
                        int type = jsin.get_int();
                        int density = jsin.get_int();
                        int age = jsin.get_int();
                        if (sm->fld[i][j].findField(field_id(type)) == NULL) {
                            sm->field_count++;
                        }
                        sm->fld[i][j].addField(field_id(type), density, age);
                    }
                }
            } else if( submap_member_name == "graffiti" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    sm->set_graffiti( i, j, jsin.get_string() );
                    jsin.end_array();
                }
            } else if(submap_member_name == "cosmetics") {
                jsin.start_array();
                while (!jsin.end_array()) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.read(sm->cosmetics[i][j]);
                    jsin.end_array();
                }
            } else if( submap_member_name == "spawns" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    const mtype_id type = mtype_id( jsin.get_string() ); // TODO: json should know how to read an string_id
                    int count = jsin.get_int();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    int faction_id = jsin.get_int();
                    int mission_id = jsin.get_int();
                    bool friendly = jsin.get_bool();
                    std::string name = jsin.get_string();
                    jsin.end_array();
                    spawn_point tmp( type, count, i, j, faction_id, mission_id, friendly, name );
                    sm->spawns.push_back( tmp );
                }
            } else if( submap_member_name == "vehicles" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    vehicle *tmp = new vehicle();
                    jsin.read( *tmp );
                    sm->vehicles.push_back( tmp );
                }
            } else if( submap_member_name == "computers" ) {
                std::string computer_data = jsin.get_string();
                sm->comp.load_data( computer_data );
            } else if( submap_member_name == "camp" ) {
                std::string camp_data = jsin.get_string();
                sm->camp.load_data( camp_data );
            } else {
                jsin.skip_value();
            }
        }
        if( !add_submap( submap_coordinates, sm ) ) {
            debugmsg( "submap %d,%d,%d was already loaded", submap_coordinates.x, submap_coordinates.y,
                      submap_coordinates.z );
        }
    }
    if( submaps.count( p ) == 0 ) {
        debugmsg("file %s did not contain the expected submap %d,%d,%d", quad_path.str().c_str(), p.x, p.y,
                 p.z);
        return NULL;
    }
    return submaps[ p ];
}
Example #9
0
bool mapbuffer::add_submap( int x, int y, int z, submap *sm )
{
    return add_submap( tripoint( x, y, z ), sm );
}
Example #10
0
// We're reading in way too many entities here to mess around with creating sub-objects and
// seeking around in them, so we're using the json streaming API.
submap *mapbuffer::unserialize_submaps( const tripoint &p )
{
    // Map the tripoint to the submap quad that stores it.
    const tripoint om_addr = overmapbuffer::sm_to_omt_copy( p );
    const tripoint segment_addr = overmapbuffer::omt_to_seg_copy( om_addr );
    std::stringstream quad_path;
    quad_path << world_generator->active_world->world_path << "/maps/" <<
        segment_addr.x << "." << segment_addr.y << "." << segment_addr.z << "/" <<
        om_addr.x << "." << om_addr.y << "." << om_addr.z << ".map";

    std::ifstream fin( quad_path.str().c_str() );
    if( !fin.is_open() ) {
        // If it doesn't exist, trigger generating it.
        return NULL;
    }

    JsonIn jsin( fin );
    jsin.start_array();
    while( !jsin.end_array() ) {
        std::unique_ptr<submap> sm(new submap());
        tripoint submap_coordinates;
        jsin.start_object();
        while( !jsin.end_object() ) {
            std::string submap_member_name = jsin.get_member_name();
            if( submap_member_name == "version" ) {
                // We aren't using the version number for anything at the moment.
                jsin.skip_value();
            } else if( submap_member_name == "coordinates" ) {
                jsin.start_array();
                int locx = jsin.get_int();
                int locy = jsin.get_int();
                int locz = jsin.get_int();
                jsin.end_array();
                submap_coordinates = tripoint( locx, locy, locz );
            } else if( submap_member_name == "turn_last_touched" ) {
                sm->turn_last_touched = jsin.get_int();
            } else if( submap_member_name == "temperature" ) {
                sm->temperature = jsin.get_int();
            } else if( submap_member_name == "terrain" ) {
                // TODO: try block around this to error out if we come up short?
                jsin.start_array();
                for( int j = 0; j < SEEY; j++ ) {
                    for( int i = 0; i < SEEX; i++ ) {
                        sm->ter[i][j] = termap[ jsin.get_string() ].loadid;
                    }
                }
                jsin.end_array();
            } else if( submap_member_name == "radiation" ) {
                int rad_cell = 0;
                jsin.start_array();
                while( !jsin.end_array() ) {
                    int rad_strength = jsin.get_int();
                    int rad_num = jsin.get_int();
                    for( int i = 0; i < rad_num; ++i ) {
                        // A little array trick here, assign to it as a 1D array.
                        // If it's not in bounds we're kinda hosed anyway.
                        sm->set_radiation(0, rad_cell, rad_strength);
                        rad_cell++;
                    }
                }
            } else if( submap_member_name == "furniture" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    sm->frn[i][j] = furnmap[ jsin.get_string() ].loadid;
                    jsin.end_array();
                }
            } else if( submap_member_name == "items" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.start_array();
                    while( !jsin.end_array() ) {
                        item tmp;
                        jsin.read( tmp );
                        sm->itm[i][j].push_back( tmp );
                    }
                }
            } else if( submap_member_name == "traps" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    sm->trp[i][j] = trapmap[ jsin.get_string() ];
                    jsin.end_array();
                }
            } else if( submap_member_name == "fields" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    // Coordinates loop
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.start_array();
                    while( !jsin.end_array() ) {
                        int type = jsin.get_int();
                        int density = jsin.get_int();
                        int age = jsin.get_int();
                        if (sm->fld[i][j].findField(field_id(type)) == NULL) {
                            sm->field_count++;
                        }
                        sm->fld[i][j].addField(field_id(type), density, age);
                    }
                }
            } else if( submap_member_name == "griffiti" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    sm->set_graffiti(i, j, graffiti( jsin.get_string() ));
                    jsin.end_array();
                }
            } else if(submap_member_name == "cosmetics") {
                jsin.start_array();
                while (!jsin.end_array()) {
                    jsin.start_array();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    jsin.read(sm->cosmetics[i][j]);
                    jsin.end_array();
                }
            } else if( submap_member_name == "spawns" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    jsin.start_array();
                    std::string type = jsin.get_string();
                    int count = jsin.get_int();
                    int i = jsin.get_int();
                    int j = jsin.get_int();
                    int faction_id = jsin.get_int();
                    int mission_id = jsin.get_int();
                    bool friendly = jsin.get_bool();
                    std::string name = jsin.get_string();
                    jsin.end_array();
                    spawn_point tmp( type, count, i, j, faction_id, mission_id, friendly, name );
                    sm->spawns.push_back( tmp );
                }
            } else if( submap_member_name == "vehicles" ) {
                jsin.start_array();
                while( !jsin.end_array() ) {
                    vehicle *tmp = new vehicle();
                    jsin.read( *tmp );
                    sm->vehicles.push_back( tmp );
                }
            } else if( submap_member_name == "computers" ) {
                std::string computer_data = jsin.get_string();
                sm->comp.load_data( computer_data );
            } else if( submap_member_name == "camp" ) {
                std::string camp_data = jsin.get_string();
                sm->camp.load_data( camp_data );
            } else {
                jsin.skip_value();
            }
        }
        if( !add_submap( submap_coordinates, sm ) ) {
            debugmsg( "submap %d,%d,%d was alread loaded", submap_coordinates.x, submap_coordinates.y, submap_coordinates.z );
        }
    }
    if( submaps.count( p ) == 0 ) {
        debugmsg("file %s did not contain the expected submap %d,%d,%d", quad_path.str().c_str(), p.x, p.y, p.z);
        return NULL;
    }
    return submaps[ p ];
}
Example #11
0
void map::generate_lightmap( const int zlev )
{
    auto &map_cache = get_cache( zlev );
    auto &lm = map_cache.lm;
    auto &sm = map_cache.sm;
    auto &outside_cache = map_cache.outside_cache;
    std::memset(lm, 0, sizeof(lm));
    std::memset(sm, 0, sizeof(sm));

    /* Bulk light sources wastefully cast rays into neighbors; a burning hospital can produce
         significant slowdown, so for stuff like fire and lava:
     * Step 1: Store the position and luminance in buffer via add_light_source, for efficient
         checking of neighbors.
     * Step 2: After everything else, iterate buffer and apply_light_source only in non-redundant
         directions
     * Step 3: ????
     * Step 4: Profit!
     */
    auto &light_source_buffer = map_cache.light_source_buffer;
    std::memset(light_source_buffer, 0, sizeof(light_source_buffer));

    constexpr int dir_x[] = {  0, -1 , 1, 0 };   //    [0]
    constexpr int dir_y[] = { -1,  0 , 0, 1 };   // [1][X][2]
    constexpr int dir_d[] = { 90, 0, 180, 270 }; //    [3]

    const float natural_light  = g->natural_light_level( zlev );
    const float inside_light = (natural_light > LIGHT_SOURCE_BRIGHT) ?
        LIGHT_AMBIENT_LOW + 1.0 : LIGHT_AMBIENT_MINIMAL;
    // Apply sunlight, first light source so just assign
    for( int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx ) {
        for( int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy ) {
            // In bright light indoor light exists to some degree
            if( !outside_cache[sx][sy] ) {
                lm[sx][sy] = inside_light;
            } else {
                lm[sx][sy] = natural_light;
            }
        }
    }

    apply_character_light( g->u );
    for( auto &n : g->active_npc ) {
        apply_character_light( *n );
    }

    // Traverse the submaps in order
    for (int smx = 0; smx < my_MAPSIZE; ++smx) {
        for (int smy = 0; smy < my_MAPSIZE; ++smy) {
            auto const cur_submap = get_submap_at_grid( smx, smy, zlev );

            for (int sx = 0; sx < SEEX; ++sx) {
                for (int sy = 0; sy < SEEY; ++sy) {
                    const int x = sx + smx * SEEX;
                    const int y = sy + smy * SEEY;
                    const tripoint p( x, y, zlev );
                    // Project light into any openings into buildings.
                    if (natural_light > LIGHT_SOURCE_BRIGHT && !outside_cache[p.x][p.y]) {
                        // Apply light sources for external/internal divide
                        for(int i = 0; i < 4; ++i) {
                            if (INBOUNDS(p.x + dir_x[i], p.y + dir_y[i]) &&
                                outside_cache[p.x + dir_x[i]][p.y + dir_y[i]]) {
                                lm[p.x][p.y] = natural_light;

                                if (light_transparency( p ) > LIGHT_TRANSPARENCY_SOLID) {
                                    apply_directional_light( p, dir_d[i], natural_light );
                                }
                            }
                        }
                    }

                    if( cur_submap->lum[sx][sy] && has_items( p ) ) {
                        auto items = i_at( p );
                        add_light_from_items( p, items.begin(), items.end() );
                    }

                    const ter_id terrain = cur_submap->ter[sx][sy];
                    if (terrain == t_lava) {
                        add_light_source( p, 50 );
                    } else if (terrain == t_console) {
                        add_light_source( p, 10 );
                    } else if (terrain == t_utility_light) {
                        add_light_source( p, 240 );
                    }

                    for( auto &fld : cur_submap->fld[sx][sy] ) {
                        const field_entry *cur = &fld.second;
                        // TODO: [lightmap] Attach light brightness to fields
                        switch(cur->getFieldType()) {
                        case fd_fire:
                            if (3 == cur->getFieldDensity()) {
                                add_light_source( p, 160 );
                            } else if (2 == cur->getFieldDensity()) {
                                add_light_source( p, 60 );
                            } else {
                                add_light_source( p, 20 );
                            }
                            break;
                        case fd_fire_vent:
                        case fd_flame_burst:
                            add_light_source( p, 20 );
                            break;
                        case fd_electricity:
                        case fd_plasma:
                            if (3 == cur->getFieldDensity()) {
                                add_light_source( p, 20 );
                            } else if (2 == cur->getFieldDensity()) {
                                add_light_source( p, 4 );
                            } else {
                                // Kinda a hack as the square will still get marked.
                                apply_light_source( p, LIGHT_SOURCE_LOCAL );
                            }
                            break;
                        case fd_incendiary:
                            if (3 == cur->getFieldDensity()) {
                                add_light_source( p, 160 );
                            } else if (2 == cur->getFieldDensity()) {
                                add_light_source( p, 60 );
                            } else {
                                add_light_source( p, 20 );
                            }
                            break;
                        case fd_laser:
                            apply_light_source( p, 4 );
                            break;
                        case fd_spotlight:
                            add_light_source( p, 80 );
                            break;
                        case fd_dazzling:
                            add_light_source( p, 5 );
                            break;
                        default:
                            //Suppress warnings
                            break;
                        }
                    }
                }
            }
        }
    }

    for (size_t i = 0; i < g->num_zombies(); ++i) {
        auto &critter = g->zombie(i);
        if(critter.is_hallucination()) {
            continue;
        }
        const tripoint &mp = critter.pos();
        if( inbounds( mp ) ) {
            if (critter.has_effect( effect_onfire)) {
                apply_light_source( mp, 8 );
            }
            // TODO: [lightmap] Attach natural light brightness to creatures
            // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot)
            // TODO: [lightmap] Allow creatures to have facing and arc lights
            if (critter.type->luminance > 0) {
                apply_light_source( mp, critter.type->luminance );
            }
        }
    }

    // Apply any vehicle light sources
    VehicleList vehs = get_vehicles();
    for( auto &vv : vehs ) {
        vehicle *v = vv.v;
        if(v->lights_on) {
            int dir = v->face.dir();
            float veh_luminance = 0.0;
            float iteration = 1.0;
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_CONE_LIGHT);
            for( auto &light_indice : light_indices ) {
                veh_luminance += ( v->part_info( light_indice ).bonus / iteration );
                iteration = iteration * 1.1;
            }
            if (veh_luminance > LL_LIT) {
                for( auto &light_indice : light_indices ) {
                    tripoint pp = tripoint( vv.x, vv.y, vv.z ) +
                                  v->parts[light_indice].precalc[0];
                    if( inbounds( pp ) ) {
                        add_light_source( pp, SQRT_2 ); // Add a little surrounding light
                        apply_light_arc( pp, dir + v->parts[light_indice].direction,
                                         veh_luminance, 45 );
                    }
                }
            }
        }
        if(v->overhead_lights_on) {
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_CIRCLE_LIGHT);
            for( auto &light_indice : light_indices ) {
                if( ( calendar::turn % 2 &&
                      v->part_info( light_indice ).has_flag( VPFLAG_ODDTURN ) ) ||
                    ( !( calendar::turn % 2 ) &&
                      v->part_info( light_indice ).has_flag( VPFLAG_EVENTURN ) ) ||
                    ( !v->part_info( light_indice ).has_flag( VPFLAG_EVENTURN ) &&
                      !v->part_info( light_indice ).has_flag( VPFLAG_ODDTURN ) ) ) {
                    tripoint pp = tripoint( vv.x, vv.y, vv.z ) +
                                  v->parts[light_indice].precalc[0];
                    if(inbounds( pp )) {
                        add_light_source( pp, v->part_info( light_indice ).bonus );
                    }
                }
            }
        }
        // why reinvent the [lightmap] wheel
        if(v->dome_lights_on) {
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_DOME_LIGHT);
            for( auto &light_indice : light_indices ) {
                tripoint pp = tripoint( vv.x, vv.y, vv.z ) +
                              v->parts[light_indice].precalc[0];
                if( inbounds( pp )) {
                    add_light_source( pp, v->part_info( light_indice ).bonus );
                }
            }
        }
        if(v->aisle_lights_on) {
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_AISLE_LIGHT);
            for( auto &light_indice : light_indices ) {
                tripoint pp = tripoint( vv.x, vv.y, vv.z ) +
                              v->parts[light_indice].precalc[0];
                if( inbounds( pp )) {
                    add_light_source( pp, v->part_info( light_indice ).bonus );
                }
            }
        }
        if(v->has_atomic_lights) {
            // atomic light is always on
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_ATOMIC_LIGHT);
            for( auto &light_indice : light_indices ) {
                tripoint pp = tripoint( vv.x, vv.y, vv.z ) +
                              v->parts[light_indice].precalc[0];
                if(inbounds( pp )) {
                    add_light_source( pp, v->part_info( light_indice ).bonus );
                }
            }
        }
        for( size_t p = 0; p < v->parts.size(); ++p ) {
            tripoint pp = tripoint( vv.x, vv.y, vv.z ) +
                          v->parts[p].precalc[0];
            if( !inbounds( pp ) ) {
                continue;
            }
            if( v->part_flag( p, VPFLAG_CARGO ) && !v->part_flag( p, "COVERED" ) ) {
                add_light_from_items( pp, v->get_items(p).begin(), v->get_items(p).end() );
            }
        }
    }

    /* Now that we have position and intensity of all bulk light sources, apply_ them
      This may seem like extra work, but take a 12x12 raging inferno:
        unbuffered: (12^2)*(160*4) = apply_light_ray x 92160
        buffered:   (12*4)*(160)   = apply_light_ray x 7680
    */
    const tripoint cache_start( 0, 0, zlev );
    const tripoint cache_end( LIGHTMAP_CACHE_X, LIGHTMAP_CACHE_Y, zlev );
    for( const tripoint &p : points_in_rectangle( cache_start, cache_end ) ) {
        if( light_source_buffer[p.x][p.y] > 0.0 ) {
            apply_light_source( p, light_source_buffer[p.x][p.y] );
        }
    }


    if (g->u.has_active_bionic("bio_night") ) {
        for( const tripoint &p : points_in_rectangle( cache_start, cache_end ) ) {
            if( rl_dist( p, g->u.pos() ) < 15 ) {
                lm[p.x][p.y] = LIGHT_AMBIENT_MINIMAL;
            }
        }
    }
}
Example #12
0
void mutation_branch::load( JsonObject &jsobj )
{
    const std::string id = jsobj.get_string( "id" );
    mutation_branch &new_mut = mutation_data[id];

    JsonArray jsarr;
    new_mut.name = _(jsobj.get_string("name").c_str());
    new_mut.description = _(jsobj.get_string("description").c_str());
    new_mut.points = jsobj.get_int("points");
    new_mut.visibility = jsobj.get_int("visibility", 0);
    new_mut.ugliness = jsobj.get_int("ugliness", 0);
    new_mut.startingtrait = jsobj.get_bool("starting_trait", false);
    new_mut.mixed_effect = jsobj.get_bool("mixed_effect", false);
    new_mut.activated = jsobj.get_bool("active", false);
    new_mut.starts_active = jsobj.get_bool("starts_active", false);
    new_mut.destroys_gear = jsobj.get_bool("destroys_gear", false);
    new_mut.allow_soft_gear = jsobj.get_bool("allow_soft_gear", false);
    new_mut.cost = jsobj.get_int("cost", 0);
    new_mut.cooldown = jsobj.get_int("time",0);
    new_mut.hunger = jsobj.get_bool("hunger",false);
    new_mut.thirst = jsobj.get_bool("thirst",false);
    new_mut.fatigue = jsobj.get_bool("fatigue",false);
    new_mut.valid = jsobj.get_bool("valid", true);
    new_mut.purifiable = jsobj.get_bool("purifiable", true);
    for( auto & s : jsobj.get_string_array( "initial_ma_styles" ) ) {
        new_mut.initial_ma_styles.push_back( matype_id( s ) );
    }

    JsonArray bodytemp_array = jsobj.get_array( "bodytemp_modifiers" );
    if( bodytemp_array.has_more() ) {
        new_mut.bodytemp_min = bodytemp_array.get_int( 0 );
        new_mut.bodytemp_max = bodytemp_array.get_int( 1 );
    }
    new_mut.bodytemp_sleep = jsobj.get_int( "bodytemp_sleep", 0 );
    new_mut.threshold = jsobj.get_bool("threshold", false);
    new_mut.profession = jsobj.get_bool("profession", false);

    auto vr = jsobj.get_array( "vitamin_rates" );
    while( vr.has_more() ) {
        auto pair = vr.next_array();
        new_mut.vitamin_rates[ vitamin_id( pair.get_string( 0 ) ) ] = pair.get_int( 1 );
    }

    load_mutation_mods(jsobj, "passive_mods", new_mut.mods);
    /* Not currently supported due to inability to save active mutation state
    load_mutation_mods(jsobj, "active_mods", new_mut.mods); */

    new_mut.prereqs = jsobj.get_string_array( "prereqs" );
    // Helps to be able to have a trait require more than one other trait
    // (Individual prereq-lists are "OR", not "AND".)
    // Traits shoud NOT appear in both lists for a given mutation, unless
    // you want that trait to satisfy both requirements.
    // These are additional to the first list.
    new_mut.prereqs2 = jsobj.get_string_array( "prereqs2" );
    // Dedicated-purpose prereq slot for Threshold mutations
    // Stuff like Huge might fit in more than one mutcat post-threshold, so yeah
    new_mut.threshreq = jsobj.get_string_array( "threshreq" );
    new_mut.cancels = jsobj.get_string_array( "cancels" );
    new_mut.replacements = jsobj.get_string_array( "changes_to" );
    new_mut.additions = jsobj.get_string_array( "leads_to" );
    new_mut.flags = jsobj.get_tags( "flags" );
    jsarr = jsobj.get_array("category");
    while (jsarr.has_more()) {
        std::string s = jsarr.next_string();
        new_mut.category.push_back(s);
        mutations_category[s].push_back(id);
    }
    jsarr = jsobj.get_array("wet_protection");
    while (jsarr.has_more()) {
        JsonObject jo = jsarr.next_object();
        std::string part_id = jo.get_string("part");
        int ignored = jo.get_int("ignored", 0);
        int neutral = jo.get_int("neutral", 0);
        int good = jo.get_int("good", 0);
        tripoint protect = tripoint(ignored, neutral, good);
        new_mut.protection[get_body_part_token( part_id )] = protect;
    }

    jsarr = jsobj.get_array("encumbrance_always");
    while (jsarr.has_more()) {
        JsonArray jo = jsarr.next_array();
        std::string part_id = jo.next_string();
        int enc = jo.next_int();
        new_mut.encumbrance_always[get_body_part_token( part_id )] = enc;
    }

    jsarr = jsobj.get_array("encumbrance_covered");
    while (jsarr.has_more()) {
        JsonArray jo = jsarr.next_array();
        std::string part_id = jo.next_string();
        int enc = jo.next_int();
        new_mut.encumbrance_covered[get_body_part_token( part_id )] = enc;
    }

    jsarr = jsobj.get_array("restricts_gear");
    while( jsarr.has_more() ) {
        new_mut.restricts_gear.insert( get_body_part_token( jsarr.next_string() ) );
    }

    jsarr = jsobj.get_array( "armor" );
    while( jsarr.has_more() ) {
        JsonObject jo = jsarr.next_object();
        auto parts = jo.get_tags( "parts" );
        std::set<body_part> bps;
        for( const std::string &part_string : parts ) {
            if( part_string == "ALL" ) {
                // Shorthand, since many muts protect whole body
                for( size_t i = 0; i < num_bp; i++ ) {
                    bps.insert( static_cast<body_part>( i ) );
                }
            } else {
                bps.insert( get_body_part_token( part_string ) );
            }
        }

        resistances res = load_resistances_instance( jo );

        for( body_part bp : bps ) {
            new_mut.armor[ bp ] = res;
        }
    }
}
Example #13
0
void tutorial_game::post_action(action_id act)
{
    switch (act) {
    case ACTION_RELOAD:
        if (g->u.weapon.is_gun() && !tutorials_seen[LESSON_GUN_FIRE]) {
            g->summon_mon("mon_zombie", tripoint(g->u.posx(), g->u.posy() - 6, g->u.posz()));
            g->summon_mon("mon_zombie", tripoint(g->u.posx() + 2, g->u.posy() - 5, g->u.posz()));
            g->summon_mon("mon_zombie", tripoint(g->u.posx() - 2, g->u.posy() - 5, g->u.posz()));
            add_message(LESSON_GUN_FIRE);
        }
        break;

    case ACTION_OPEN:
        add_message(LESSON_CLOSE);
        break;

    case ACTION_CLOSE:
        add_message(LESSON_SMASH);
        break;

    case ACTION_USE:
        if (g->u.has_amount("grenade_act", 1))
            add_message(LESSON_ACT_GRENADE);
        for (int x = g->u.posx() - 1; x <= g->u.posx() + 1; x++) {
            for (int y = g->u.posy() - 1; y <= g->u.posy() + 1; y++) {
                if (g->m.tr_at({x, y, g->u.posz()}).id == trap_str_id( "tr_bubblewrap" ))
                    add_message(LESSON_ACT_BUBBLEWRAP);
            }
        }
        break;

    case ACTION_EAT:
        if (g->u.last_item == "codeine")
            add_message(LESSON_TOOK_PAINKILLER);
        else if (g->u.last_item == "cig")
            add_message(LESSON_TOOK_CIG);
        else if (g->u.last_item == "water")
            add_message(LESSON_DRANK_WATER);
        break;

    case ACTION_WEAR: {
        item it( g->u.last_item, 0 );
        if (it.is_armor()) {
            if (it.get_coverage() >= 2 || it.get_thickness() >= 2)
                add_message(LESSON_WORE_ARMOR);
            if (it.get_storage() >= 20)
                add_message(LESSON_WORE_STORAGE);
            if (it.get_env_resist() >= 2)
                add_message(LESSON_WORE_MASK);
        }
    }
    break;

    case ACTION_WIELD:
        if (g->u.weapon.is_gun())
            add_message(LESSON_GUN_LOAD);
        break;

    case ACTION_EXAMINE:
        add_message(LESSON_INTERACT);
// Fall through to...
    case ACTION_PICKUP: {
        item it( g->u.last_item, 0 );
        if (it.is_armor())
            add_message(LESSON_GOT_ARMOR);
        else if (it.is_gun())
            add_message(LESSON_GOT_GUN);
        else if (it.is_ammo())
            add_message(LESSON_GOT_AMMO);
        else if (it.is_tool())
            add_message(LESSON_GOT_TOOL);
        else if (it.is_food())
            add_message(LESSON_GOT_FOOD);
        else if (it.is_weap())
            add_message(LESSON_GOT_WEAPON);

    }
    break;

    default: //TODO: add more actions here
        break;

    }
}
Example #14
0
void game::draw_custom_explosion( const tripoint &, const std::map<tripoint, nc_color> &all_area )
{
    if( test_mode ) {
        return; // avoid segfault from null tilecontext in tests
    }

    constexpr explosion_neighbors all_neighbors = N_NORTH | N_SOUTH | N_WEST | N_EAST;
    // We will "shell" the explosion area
    // Each phase will strip a single layer of points
    // A layer contains all points that have less than 4 neighbors in cardinal directions
    // Layers will first be generated, then drawn in inverse order

    // Start by getting rid of everything except current z-level
    std::map<tripoint, explosion_tile> neighbors;
#if defined(TILES)
    if( !use_tiles ) {
        for( const auto &pr : all_area ) {
            const tripoint relative_point = relative_view_pos( u, pr.first );
            if( relative_point.z == 0 ) {
                neighbors[pr.first] = explosion_tile{ N_NO_NEIGHBORS, pr.second };
            }
        }
    } else {
        // In tiles mode, the coordinates have to be absolute
        const tripoint view_center = relative_view_pos( u, u.pos() );
        for( const auto &pr : all_area ) {
            // Relative point is only used for z level check
            const tripoint relative_point = relative_view_pos( u, pr.first );
            if( relative_point.z == view_center.z ) {
                neighbors[pr.first] = explosion_tile{ N_NO_NEIGHBORS, pr.second };
            }
        }
    }
#else
    for( const auto &pr : all_area ) {
        const tripoint relative_point = relative_view_pos( u, pr.first );
        if( relative_point.z == 0 ) {
            neighbors[pr.first] = explosion_tile{ N_NO_NEIGHBORS, pr.second };
        }
    }
#endif

    // Searches for a neighbor, sets the neighborhood flag on current point and on the neighbor
    const auto set_neighbors = [&]( const tripoint & pos,
                                    explosion_neighbors & ngh,
                                    explosion_neighbors here,
    explosion_neighbors there ) {
        if( ( ngh & here ) == N_NO_NEIGHBORS ) {
            auto other = neighbors.find( pos );
            if( other != neighbors.end() ) {
                ngh = ngh | here;
                other->second.neighborhood = other->second.neighborhood | there;
            }
        }
    };

    // If the point we are about to remove has a neighbor in a given direction
    // unset that neighbor's flag that our current point is its neighbor
    const auto unset_neighbor = [&]( const tripoint & pos,
                                     const explosion_neighbors ngh,
                                     explosion_neighbors here,
    explosion_neighbors there ) {
        if( ( ngh & here ) != N_NO_NEIGHBORS ) {
            auto other = neighbors.find( pos );
            if( other != neighbors.end() ) {
                other->second.neighborhood = ( other->second.neighborhood | there ) ^ there;
            }
        }
    };

    // Find all neighborhoods
    for( auto &pr : neighbors ) {
        const tripoint &pt = pr.first;
        explosion_neighbors &ngh = pr.second.neighborhood;

        set_neighbors( tripoint( pt.x - 1, pt.y, pt.z ), ngh, N_WEST, N_EAST );
        set_neighbors( tripoint( pt.x + 1, pt.y, pt.z ), ngh, N_EAST, N_WEST );
        set_neighbors( tripoint( pt.x, pt.y - 1, pt.z ), ngh, N_NORTH, N_SOUTH );
        set_neighbors( tripoint( pt.x, pt.y + 1, pt.z ), ngh, N_SOUTH, N_NORTH );
    }

    // We need to save the layers because we will draw them in reverse order
    std::list< std::map<tripoint, explosion_tile> > layers;
    while( !neighbors.empty() ) {
        std::map<tripoint, explosion_tile> layer;
        bool changed = false;
        // Find a layer that can be drawn
        for( const auto &pr : neighbors ) {
            if( pr.second.neighborhood != all_neighbors ) {
                changed = true;
                layer.insert( pr );
            }
        }
        if( !changed ) {
            // An error, but a minor one - let it slide
            return;
        }
        // Remove the layer from the area to process
        for( const auto &pr : layer ) {
            const tripoint &pt = pr.first;
            const explosion_neighbors ngh = pr.second.neighborhood;

            unset_neighbor( tripoint( pt.x - 1, pt.y, pt.z ), ngh, N_WEST, N_EAST );
            unset_neighbor( tripoint( pt.x + 1, pt.y, pt.z ), ngh, N_EAST, N_WEST );
            unset_neighbor( tripoint( pt.x, pt.y - 1, pt.z ), ngh, N_NORTH, N_SOUTH );
            unset_neighbor( tripoint( pt.x, pt.y + 1, pt.z ), ngh, N_SOUTH, N_NORTH );
            neighbors.erase( pr.first );
        }

        layers.push_front( std::move( layer ) );
    }

#if defined(TILES)
    if( !use_tiles ) {
        draw_custom_explosion_curses( *this, layers );
        return;
    }

    explosion_animation anim;
    // We need to draw all explosions up to now
    std::map<tripoint, explosion_tile> combined_layer;
    for( const auto &layer : layers ) {
        combined_layer.insert( layer.begin(), layer.end() );
        tilecontext->init_custom_explosion_layer( combined_layer );
        if( is_layer_visible( layer ) ) {
            anim.progress();
        }
    }

    tilecontext->void_custom_explosion();
#else
    draw_custom_explosion_curses( *this, layers );
#endif
}
Example #15
0
bool Creature::sees( const int tx, const int ty ) const
{
    return sees( tripoint( tx, ty, posz() ) );
}
Example #16
0
bool mapbuffer::add_submap( int x, int y, int z, std::unique_ptr<submap> &sm )
{
    return add_submap( tripoint( x, y, z ), sm );
}
Example #17
0
bool Creature::sees( const point t ) const
{
    return sees( tripoint( t, posz() ) );
}
Example #18
0
submap *mapbuffer::lookup_submap(int x, int y, int z)
{
    return lookup_submap( tripoint( x, y, z ) );
}
Example #19
0
void monster::plan( const mfactions &factions )
{
    // Bots are more intelligent than most living stuff
    bool smart_planning = has_flag( MF_PRIORITIZE_TARGETS );
    Creature *target = nullptr;
    // 8.6f is rating for tank drone 60 tiles away, moose 16 or boomer 33
    float dist = !smart_planning ? 1000 : 8.6f;
    bool fleeing = false;
    bool docile = friendly != 0 && has_effect( effect_docile );
    bool angers_hostile_weak = type->anger.find( MTRIG_HOSTILE_WEAK ) != type->anger.end();
    int angers_hostile_near =
        ( type->anger.find( MTRIG_HOSTILE_CLOSE ) != type->anger.end() ) ? 5 : 0;
    int fears_hostile_near = ( type->fear.find( MTRIG_HOSTILE_CLOSE ) != type->fear.end() ) ? 5 : 0;
    bool group_morale = has_flag( MF_GROUP_MORALE ) && morale < type->morale;
    bool swarms = has_flag( MF_SWARMS );
    auto mood = attitude();

    // If we can see the player, move toward them or flee.
    if( friendly == 0 && sees( g->u ) ) {
        dist = rate_target( g->u, dist, smart_planning );
        fleeing = fleeing || is_fleeing( g->u );
        target = &g->u;
        if( dist <= 5 ) {
            anger += angers_hostile_near;
            morale -= fears_hostile_near;
        }
    } else if( friendly != 0 && !docile ) {
        // Target unfriendly monsters, only if we aren't interacting with the player.
        for( int i = 0, numz = g->num_zombies(); i < numz; i++ ) {
            monster &tmp = g->zombie( i );
            if( tmp.friendly == 0 ) {
                float rating = rate_target( tmp, dist, smart_planning );
                if( rating < dist ) {
                    target = &tmp;
                    dist = rating;
                }
            }
        }
    }

    if( docile ) {
        if( friendly != 0 && target != nullptr ) {
            set_dest( target->pos() );
        }

        return;
    }

    for( size_t i = 0; i < g->active_npc.size(); i++ ) {
        npc &who = *g->active_npc[i];
        auto faction_att = faction.obj().attitude( who.get_monster_faction() );
        if( faction_att == MFA_NEUTRAL || faction_att == MFA_FRIENDLY ) {
            continue;
        }

        float rating = rate_target( who, dist, smart_planning );
        bool fleeing_from = is_fleeing( who );
        // Switch targets if closer and hostile or scarier than current target
        if( ( rating < dist && fleeing ) ||
            ( rating < dist && attitude( &who ) == MATT_ATTACK ) ||
            ( !fleeing && fleeing_from ) ) {
            target = &who;
            dist = rating;
        }
        fleeing = fleeing || fleeing_from;
        if( rating <= 5 ) {
            anger += angers_hostile_near;
            morale -= fears_hostile_near;
        }
    }

    fleeing = fleeing || ( mood == MATT_FLEE );
    if( friendly == 0 ) {
        for( const auto &fac : factions ) {
            auto faction_att = faction.obj().attitude( fac.first );
            if( faction_att == MFA_NEUTRAL || faction_att == MFA_FRIENDLY ) {
                continue;
            }

            for( int i : fac.second ) { // mon indices
                monster &mon = g->zombie( i );
                float rating = rate_target( mon, dist, smart_planning );
                if( rating < dist ) {
                    target = &mon;
                    dist = rating;
                }
                if( rating <= 5 ) {
                    anger += angers_hostile_near;
                    morale -= fears_hostile_near;
                }
            }
        }
    }

    // Friendly monsters here
    // Avoid for hordes of same-faction stuff or it could get expensive
    const auto actual_faction = friendly == 0 ? faction : mfaction_str_id( "player" );
    auto const &myfaction_iter = factions.find( actual_faction );
    if( myfaction_iter == factions.end() ) {
        DebugLog( D_ERROR, D_GAME ) << disp_name() << " tried to find faction "
                                    << actual_faction.id().str()
                                    << " which wasn't loaded in game::monmove";
        swarms = false;
        group_morale = false;
    }
    swarms = swarms && target == nullptr; // Only swarm if we have no target
    if( group_morale || swarms ) {
        for( const int i : myfaction_iter->second ) {
            monster &mon = g->zombie( i );
            float rating = rate_target( mon, dist, smart_planning );
            if( group_morale && rating <= 10 ) {
                morale += 10 - rating;
            }
            if( swarms ) {
                if( rating < 5 ) { // Too crowded here
                    wander_pos.x = posx() * rng( 1, 3 ) - mon.posx();
                    wander_pos.y = posy() * rng( 1, 3 ) - mon.posy();
                    wandf = 2;
                    target = nullptr;
                    // Swarm to the furthest ally you can see
                } else if( rating < INT_MAX && rating > dist && wandf <= 0 ) {
                    target = &mon;
                    dist = rating;
                }
            }
        }
    }

    if( target != nullptr ) {

        tripoint dest = target->pos();
        auto att_to_target = attitude_to( *target );
        if( att_to_target == Attitude::A_HOSTILE && !fleeing ) {
            set_dest( dest );
        } else if( fleeing ) {
            set_dest( tripoint( posx() * 2 - dest.x, posy() * 2 - dest.y, posz() ) );
        }
        if( angers_hostile_weak && att_to_target != Attitude::A_FRIENDLY ) {
            int hp_per = target->hp_percentage();
            if( hp_per <= 70 ) {
                anger += 10 - int( hp_per / 10 );
            }
        }
    } else if( friendly > 0 && one_in( 3 ) ) {
        // Grow restless with no targets
        friendly--;
    } else if( friendly < 0 && sees( g->u ) ) {
        if( rl_dist( pos(), g->u.pos() ) > 2 ) {
            set_dest( g->u.pos() );
        } else {
            unset_dest();
        }
    }
}
Example #20
0
void tutorial_game::post_action( action_id act )
{
    switch( act ) {
        case ACTION_RELOAD:
            if( g->u.weapon.is_gun() && !tutorials_seen[LESSON_GUN_FIRE] ) {
                g->summon_mon( mon_zombie, tripoint( g->u.posx(), g->u.posy() - 6, g->u.posz() ) );
                g->summon_mon( mon_zombie, tripoint( g->u.posx() + 2, g->u.posy() - 5, g->u.posz() ) );
                g->summon_mon( mon_zombie, tripoint( g->u.posx() - 2, g->u.posy() - 5, g->u.posz() ) );
                add_message( LESSON_GUN_FIRE );
            }
            break;

        case ACTION_OPEN:
            add_message( LESSON_CLOSE );
            break;

        case ACTION_CLOSE:
            add_message( LESSON_SMASH );
            break;

        case ACTION_USE:
            if( g->u.has_amount( "grenade_act", 1 ) ) {
                add_message( LESSON_ACT_GRENADE );
            }
            for( const tripoint &dest : g->m.points_in_radius( g->u.pos(), 1 ) ) {
                if( g->m.tr_at( dest ).id == trap_str_id( "tr_bubblewrap" ) ) {
                    add_message( LESSON_ACT_BUBBLEWRAP );
                }
            }
            break;

        case ACTION_EAT:
            if( g->u.last_item == "codeine" ) {
                add_message( LESSON_TOOK_PAINKILLER );
            } else if( g->u.last_item == "cig" ) {
                add_message( LESSON_TOOK_CIG );
            } else if( g->u.last_item == "water" ) {
                add_message( LESSON_DRANK_WATER );
            }
            break;

        case ACTION_WEAR: {
            item it( g->u.last_item, 0 );
            if( it.is_armor() ) {
                if( it.get_coverage() >= 2 || it.get_thickness() >= 2 ) {
                    add_message( LESSON_WORE_ARMOR );
                }
                if( it.get_storage() >= units::from_liter( 5 ) ) {
                    add_message( LESSON_WORE_STORAGE );
                }
                if( it.get_env_resist() >= 2 ) {
                    add_message( LESSON_WORE_MASK );
                }
            }
        }
        break;

        case ACTION_WIELD:
            if( g->u.weapon.is_gun() ) {
                add_message( LESSON_GUN_LOAD );
            }
            break;

        case ACTION_EXAMINE:
            add_message( LESSON_INTERACT );
        /* fallthrough */
        case ACTION_PICKUP: {
            item it( g->u.last_item, 0 );
            if( it.is_armor() ) {
                add_message( LESSON_GOT_ARMOR );
            } else if( it.is_gun() ) {
                add_message( LESSON_GOT_GUN );
            } else if( it.is_ammo() ) {
                add_message( LESSON_GOT_AMMO );
            } else if( it.is_tool() ) {
                add_message( LESSON_GOT_TOOL );
            } else if( it.is_food() ) {
                add_message( LESSON_GOT_FOOD );
            } else if( it.is_melee() ) {
                add_message( LESSON_GOT_WEAPON );
            }

        }
        break;

        default: //TODO: add more actions here
            break;

    }
}
Example #21
0
tripoint overmapbuffer::sm_to_ms_copy(const tripoint& p) {
    return tripoint(p.x * SEEX, p.y * SEEY, p.z);
}
Example #22
0
#include "catch/catch.hpp"

#include "game.h"
#include "map.h"
#include "vehicle.h"
#include "veh_type.h"
#include "player.h"

TEST_CASE( "destroy_grabbed_vehicle_section" )
{
    GIVEN( "A vehicle grabbed by the player" ) {
        tripoint test_origin( 60, 60, 0 );
        g->u.setpos( test_origin );
        tripoint vehicle_origin = test_origin + tripoint( 1, 1, 0 );
        vehicle *veh_ptr = g->m.add_vehicle( vproto_id( "bicycle" ), vehicle_origin, -90 );
        REQUIRE( veh_ptr != nullptr );
        tripoint grab_point = test_origin + tripoint( 1, 0, 0 );
        g->u.grab_type = OBJECT_VEHICLE;
        g->u.grab_point = grab_point;
        WHEN( "The vehicle section grabbed by the player is destroyed" ) {
            g->m.destroy( grab_point );
            THEN( "The player's grab is released" ) {
                CHECK( g->u.grab_type == OBJECT_NONE );
                CHECK( g->u.grab_point == tripoint_zero );
            }
        }
    }
}
Example #23
0
bool fungal_effects::spread_fungus( const tripoint &p )
{
    int growth = 1;
    for( int i = p.x - 1; i <= p.x + 1; i++ ) {
        for( int j = p.y - 1; j <= p.y + 1; j++ ) {
            if( i == p.x && j == p.y ) {
                continue;
            }
            if( m.has_flag( "FUNGUS", tripoint( i, j, p.z ) ) ) {
                growth += 1;
            }
        }
    }

    bool converted = false;
    if( !m.has_flag_ter( "FUNGUS", p ) ) {
        // Terrain conversion
        if( m.has_flag_ter( "DIGGABLE", p ) ) {
            if( x_in_y( growth * 10, 100 ) ) {
                m.ter_set( p, t_fungus );
                converted = true;
            }
        } else if( m.has_flag( "FLAT", p ) ) {
            if( m.has_flag( TFLAG_INDOORS, p ) ) {
                if( x_in_y( growth * 10, 500 ) ) {
                    m.ter_set( p, t_fungus_floor_in );
                    converted = true;
                }
            } else if( m.has_flag( TFLAG_SUPPORTS_ROOF, p ) ) {
                if( x_in_y( growth * 10, 1000 ) ) {
                    m.ter_set( p, t_fungus_floor_sup );
                    converted = true;
                }
            } else {
                if( x_in_y( growth * 10, 2500 ) ) {
                    m.ter_set( p, t_fungus_floor_out );
                    converted = true;
                }
            }
        } else if( m.has_flag( "SHRUB", p ) ) {
            if( x_in_y( growth * 10, 200 ) ) {
                m.ter_set( p, t_shrub_fungal );
                converted = true;
            } else if( x_in_y( growth, 1000 ) ) {
                m.ter_set( p, t_marloss );
                converted = true;
            }
        } else if( m.has_flag( "THIN_OBSTACLE", p ) ) {
            if( x_in_y( growth * 10, 150 ) ) {
                m.ter_set( p, t_fungus_mound );
                converted = true;
            }
        } else if( m.has_flag( "YOUNG", p ) ) {
            if( x_in_y( growth * 10, 500 ) ) {
                m.ter_set( p, t_tree_fungal_young );
                converted = true;
            }
        } else if( m.has_flag( "WALL", p ) ) {
            if( x_in_y( growth * 10, 5000 ) ) {
                converted = true;
                m.ter_set( p, t_fungus_wall );
            }
        }
        // Furniture conversion
        if( converted ) {
            if( m.has_flag( "FLOWER", p ) ) {
                m.furn_set( p, f_flower_fungal );
            } else if( m.has_flag( "ORGANIC", p ) ) {
                if( m.furn( p ).obj().movecost == -10 ) {
                    m.furn_set( p, f_fungal_mass );
                } else {
                    m.furn_set( p, f_fungal_clump );
                }
            } else if( m.has_flag( "PLANT", p ) ) {
                // Replace the (already existing) seed
                m.i_at( p )[0] = item( "fungal_seeds", calendar::turn );
            }
        }
        return true;
    } else {
        // Everything is already fungus
        if( growth == 9 ) {
            return false;
        }
        for( int i = p.x - 1; i <= p.x + 1; i++ ) {
            for( int j = p.y - 1; j <= p.y + 1; j++ ) {
                tripoint dest( i, j, p.z );
                // One spread on average
                if( !m.has_flag( "FUNGUS", dest ) && one_in( 9 - growth ) ) {
                    //growth chance is 100 in X simplified
                    if( m.has_flag( "DIGGABLE", dest ) ) {
                        m.ter_set( dest, t_fungus );
                        converted = true;
                    } else if( m.has_flag( "FLAT", dest ) ) {
                        if( m.has_flag( TFLAG_INDOORS, dest ) ) {
                            if( one_in( 5 ) ) {
                                m.ter_set( dest, t_fungus_floor_in );
                                converted = true;
                            }
                        } else if( m.has_flag( TFLAG_SUPPORTS_ROOF, dest ) ) {
                            if( one_in( 10 ) ) {
                                m.ter_set( dest, t_fungus_floor_sup );
                                converted = true;
                            }
                        } else {
                            if( one_in( 25 ) ) {
                                m.ter_set( dest, t_fungus_floor_out );
                                converted = true;
                            }
                        }
                    } else if( m.has_flag( "SHRUB", dest ) ) {
                        if( one_in( 2 ) ) {
                            m.ter_set( dest, t_shrub_fungal );
                            converted = true;
                        } else if( one_in( 25 ) ) {
                            m.ter_set( dest, t_marloss );
                            converted = true;
                        }
                    } else if( m.has_flag( "THIN_OBSTACLE", dest ) ) {
                        if( x_in_y( 10, 15 ) ) {
                            m.ter_set( dest, t_fungus_mound );
                            converted = true;
                        }
                    } else if( m.has_flag( "YOUNG", dest ) ) {
                        if( one_in( 5 ) ) {
                            if( m.get_field_strength( p, fd_fungal_haze ) != 0 ) {
                                if( one_in( 8 ) ) { // young trees are vulnerable
                                    m.ter_set( dest, t_fungus );
                                    gm.summon_mon( mon_fungal_blossom, p );
                                    if( gm.u.sees( p ) ) {
                                        add_msg( m_warning, _( "The young tree blooms forth into a fungal blossom!" ) );
                                    }
                                } else if( one_in( 4 ) ) {
                                    m.ter_set( dest, t_marloss_tree );
                                }
                            } else {
                                m.ter_set( dest, t_tree_fungal_young );
                            }
                            converted = true;
                        }
                    } else if( m.has_flag( "TREE", dest ) ) {
                        if( one_in( 10 ) ) {
                            if( m.get_field_strength( p, fd_fungal_haze ) != 0 ) {
                                if( one_in( 10 ) ) {
                                    m.ter_set( dest, t_fungus );
                                    gm.summon_mon( mon_fungal_blossom, p );
                                    if( gm.u.sees( p ) ) {
                                        add_msg( m_warning, _( "The tree blooms forth into a fungal blossom!" ) );
                                    }
                                } else if( one_in( 6 ) ) {
                                    m.ter_set( dest, t_marloss_tree );
                                }
                            } else {
                                m.ter_set( dest, t_tree_fungal );
                            }
                            converted = true;
                        }
                    } else if( m.has_flag( "WALL", dest ) ) {
                        if( one_in( 50 ) ) {
                            converted = true;
                            m.ter_set( dest, t_fungus_wall );
                        }
                    }

                    if( converted ) {
                        if( m.has_flag( "FLOWER", dest ) ) {
                            m.furn_set( dest, f_flower_fungal );
                        } else if( m.has_flag( "ORGANIC", dest ) ) {
                            if( m.furn( dest ).obj().movecost == -10 ) {
                                m.furn_set( dest, f_fungal_mass );
                            } else {
                                m.furn_set( dest, f_fungal_clump );
                            }
                        } else if( m.has_flag( "PLANT", dest ) ) {
                            // Replace the (already existing) seed
                            m.i_at( p )[0] = item( "fungal_seeds", calendar::turn );
                        }
                    }
                }
            }
        }
        return false;
    }
}
Example #24
0
/**
 * Generate textual weather forecast for the specified radio tower.
 */
std::string weather_forecast( point const &abs_sm_pos )
{
    std::ostringstream weather_report;
    // Local conditions
    const auto cref = overmap_buffer.closest_city( tripoint( abs_sm_pos, 0 ) );
    const std::string city_name = cref ? cref.city->name : std::string( _( "middle of nowhere" ) );
    // Current time
    weather_report << string_format(
                       _("The current time is %s Eastern Standard Time.  At %s in %s, it was %s. The temperature was %s. "),
                       calendar::turn.print_time().c_str(), calendar::turn.print_time(true).c_str(),
                       city_name.c_str(),
                       weather_data(g->weather).name.c_str(), print_temperature(g->temperature).c_str()
                   );

    //weather_report << ", the dewpoint ???, and the relative humidity ???.  ";
    //weather_report << "The wind was <direction> at ? mi/km an hour.  ";
    //weather_report << "The pressure was ??? in/mm and steady/rising/falling.";

    // Regional conditions (simulated by choosing a random range containing the current conditions).
    // Adjusted for weather volatility based on how many weather changes are coming up.
    //weather_report << "Across <region>, skies ranged from <cloudiest> to <clearest>.  ";
    // TODO: Add fake reports for nearby cities

    // TODO: weather forecast
    // forecasting periods are divided into 12-hour periods, day (6-18) and night (19-5)
    // Accumulate percentages for each period of various weather statistics, and report that
    // (with fuzz) as the weather chances.
    // int weather_proportions[NUM_WEATHER_TYPES] = {0};
    double high = -100.0;
    double low = 100.0;
    point const abs_ms_pos = sm_to_ms_copy( abs_sm_pos );
    // TODO wind direction and speed
    int last_hour = calendar::turn - ( calendar::turn % HOURS(1) );
    for(int d = 0; d < 6; d++) {
        weather_type forecast = WEATHER_NULL;
        for(calendar i(last_hour + 7200 * d); i < last_hour + 7200 * (d + 1); i += 600) {
            w_point w = g->weather_gen->get_weather( abs_ms_pos, i );
            forecast = std::max(forecast, g->weather_gen->get_weather_conditions(w));
            high = std::max(high, w.temperature);
            low = std::min(low, w.temperature);
        }
        std::string day;
        bool started_at_night;
        calendar c(last_hour + 7200 * d);
        if(d == 0 && c.is_night()) {
            day = _("Tonight");
            started_at_night = true;
        } else {
            day = _("Today");
            started_at_night = false;
        }
        if(d > 0 && ((started_at_night && !(d % 2)) || (!started_at_night && d % 2))) {
            day = rmp_format(_("<Mon Night>%s Night"), c.day_of_week().c_str());
        } else {
            day = c.day_of_week();
        }
        weather_report << string_format(
                           _("%s... %s. Highs of %s. Lows of %s. "),
                           day.c_str(), weather_data(forecast).name.c_str(),
                           print_temperature(high).c_str(), print_temperature(low).c_str()
                       );
    }
    return weather_report.str();
}
bool overmapbuffer::reveal(const point &center, int radius, int z)
{
    return reveal( tripoint( center, z ), radius );
}
Example #26
0
void player::activate_mutation( const std::string &mut )
{
    const auto &mdata = mutation_branch::get( mut );
    auto &tdata = my_mutations[mut];
    int cost = mdata.cost;
    // You can take yourself halfway to Near Death levels of hunger/thirst.
    // Fatigue can go to Exhausted.
    if ((mdata.hunger && hunger >= 700) || (mdata.thirst && thirst >= 260) ||
      (mdata.fatigue && fatigue >= 575)) {
      // Insufficient Foo to *maintain* operation is handled in player::suffer
        add_msg(m_warning, _("You feel like using your %s would kill you!"), mdata.name.c_str());
        return;
    }
    if (tdata.powered && tdata.charge > 0) {
        // Already-on units just lose a bit of charge
        tdata.charge--;
    } else {
        // Not-on units, or those with zero charge, have to pay the power cost
        if (mdata.cooldown > 0) {
            tdata.charge = mdata.cooldown - 1;
        }
        if (mdata.hunger){
            hunger += cost;
        }
        if (mdata.thirst){
            thirst += cost;
        }
        if (mdata.fatigue){
            fatigue += cost;
        }
        tdata.powered = true;

        // Handle stat changes from activation
        apply_mods(mut, true);
        recalc_sight_limits();
    }

    if( mut == "WEB_WEAVER" ) {
        g->m.add_field(pos(), fd_web, 1, 0);
        add_msg(_("You start spinning web with your spinnerets!"));
    } else if (mut == "BURROW"){
        if (g->u.is_underwater()) {
            add_msg_if_player(m_info, _("You can't do that while underwater."));
            tdata.powered = false;
            return;
        }
        int dirx, diry;
        if (!choose_adjacent(_("Burrow where?"), dirx, diry)) {
            tdata.powered = false;
            return;
        }

        if (dirx == g->u.posx() && diry == g->u.posy()) {
            add_msg_if_player(_("You've got places to go and critters to beat."));
            add_msg_if_player(_("Let the lesser folks eat their hearts out."));
            tdata.powered = false;
            return;
        }
        int turns;
        if (g->m.is_bashable(dirx, diry) && g->m.has_flag("SUPPORTS_ROOF", dirx, diry) &&
            g->m.ter(dirx, diry) != t_tree) {
            // Takes about 100 minutes (not quite two hours) base time.
            // Being better-adapted to the task means that skillful Survivors can do it almost twice as fast.
            turns = (100000 - 5000 * g->u.skillLevel("carpentry"));
        } else if (g->m.move_cost(dirx, diry) == 2 && g->get_levz() == 0 &&
                   g->m.ter(dirx, diry) != t_dirt && g->m.ter(dirx, diry) != t_grass) {
            turns = 18000;
        } else {
            add_msg_if_player(m_info, _("You can't burrow there."));
            tdata.powered = false;
            return;
        }
        g->u.assign_activity(ACT_BURROW, turns, -1, 0);
        g->u.activity.placement = tripoint(dirx, diry,0);
        add_msg_if_player(_("You tear into the %s with your teeth and claws."),
                          g->m.tername(dirx, diry).c_str());
        tdata.powered = false;
        return; // handled when the activity finishes
    } else if (mut == "SLIMESPAWNER") {
        std::vector<tripoint> valid;
        for (int x = posx() - 1; x <= posx() + 1; x++) {
            for (int y = posy() - 1; y <= posy() + 1; y++) {
                tripoint dest(x, y, posz());
                if (g->is_empty(dest)) {
                    valid.push_back( dest );
                }
            }
        }
        // Oops, no room to divide!
        if (valid.size() == 0) {
            add_msg(m_bad, _("You focus, but are too hemmed in to birth a new slimespring!"));
            tdata.powered = false;
            return;
        }
        add_msg(m_good, _("You focus, and with a pleasant splitting feeling, birth a new slimespring!"));
        int numslime = 1;
        for (int i = 0; i < numslime && !valid.empty(); i++) {
            const tripoint target = random_entry_removed( valid );
            if (g->summon_mon("mon_player_blob", target)) {
                monster *slime = g->monster_at( target );
                slime->friendly = -1;
            }
        }
        //~ Usual enthusiastic slimespring small voices! :D
        if (one_in(3)) {
            add_msg(m_good, _("wow! you look just like me! we should look out for each other!"));
        } else if (one_in(2)) {
            add_msg(m_good, _("come on, big me, let's go!"));
        } else {
            add_msg(m_good, _("we're a team, we've got this!"));
        }
        tdata.powered = false;
        return;
    } else if (mut == "SHOUT1") {
        sounds::sound(pos(), 10 + 2 * str_cur, _("You shout loudly!"));
        tdata.powered = false;
        return;
    } else if (mut == "SHOUT2"){
        sounds::sound(pos(), 15 + 3 * str_cur, _("You scream loudly!"));
        tdata.powered = false;
        return;
    } else if (mut == "SHOUT3"){
        sounds::sound(pos(), 20 + 4 * str_cur, _("You let out a piercing howl!"));
        tdata.powered = false;
        return;
    } else if ((mut == "NAUSEA") || (mut == "VOMITOUS") ){
        vomit();
        tdata.powered = false;
        return;
    } else if (mut == "M_FERTILE"){
        spores();
        tdata.powered = false;
        return;
    } else if (mut == "M_BLOOM"){
        blossoms();
        tdata.powered = false;
        return;
    } else if (mut == "VINES3"){
        item newit("vine_30", calendar::turn, false);
        if (!can_pickVolume(newit.volume())) { //Accounts for result_mult
            add_msg(_("You detach a vine but don't have room to carry it, so you drop it."));
            g->m.add_item_or_charges(posx(), posy(), newit);
        } else if (!can_pickWeight(newit.weight(), !OPTIONS["DANGEROUS_PICKUPS"])) {
            add_msg(_("Your freshly-detached vine is too heavy to carry, so you drop it."));
            g->m.add_item_or_charges(posx(), posy(), newit);
        } else {
            inv.assign_empty_invlet(newit);
            newit = i_add(newit);
            add_msg(m_info, "%c - %s", newit.invlet == 0 ? ' ' : newit.invlet, newit.tname().c_str());
        }
        tdata.powered = false;
        return;
    }
}
tripoint overmapbuffer::sm_to_om_copy(const tripoint& p) {
    return tripoint(divide(p.x, 2 * OMAPX), divide(p.y, 2 * OMAPY), p.z);
}
Example #28
0
/*
 * Drawing-related functions
 */
void Creature::draw(WINDOW *w, int player_x, int player_y, bool inverted) const
{
    draw( w, tripoint( player_x, player_y, posz() ), inverted );
}
tripoint overmapbuffer::ms_to_sm_copy(const tripoint& p) {
    return tripoint(divide(p.x, SEEX), divide(p.y, SEEY), p.z);
}
#include "catch/catch.hpp"
#include "game.h"
#include "map.h"
#include "player.h"
#include "vehicle.h"
#include "enums.h"
#include "type_id.h"

TEST_CASE( "vehicle_split_section" )
{
    for( int dir = 0; dir < 360; dir += 15 ) {
        CHECK( !g->u.in_vehicle );
        const tripoint test_origin( 15, 15, 0 );
        g->u.setpos( test_origin );
        tripoint vehicle_origin = tripoint( 10, 10, 0 );
        VehicleList vehs = g->m.get_vehicles();
        vehicle *veh_ptr;
        for( auto &vehs_v : vehs ) {
            veh_ptr = vehs_v.v;
            g->m.destroy_vehicle( veh_ptr );
        }
        g->refresh_all();
        REQUIRE( g->m.get_vehicles().empty() );
        veh_ptr = g->m.add_vehicle( vproto_id( "cross_split_test" ), vehicle_origin, dir, 0, 0 );
        REQUIRE( veh_ptr != nullptr );
        std::set<tripoint> original_points = veh_ptr->get_points( true );

        g->m.destroy( vehicle_origin );
        veh_ptr->part_removal_cleanup();
        REQUIRE( veh_ptr->get_parts_at( vehicle_origin, "", part_status_flag::available ).empty() );