Example #1
0
void place_construction( const std::string &desc )
{
    g->refresh_all();
    const inventory &total_inv = g->u.crafting_inventory();

    std::vector<construction *> cons = constructions_by_desc( desc );
    std::map<tripoint, const construction *> valid;
    for( const tripoint &p : g->m.points_in_radius( g->u.pos(), 1 ) ) {
        for( const auto *con : cons ) {
            if( p != g->u.pos() && can_construct( *con, p ) && player_can_build( g->u, total_inv, *con ) ) {
                valid[ p ] = con;
            }
        }
    }

    for( auto &elem : valid ) {
        g->m.drawsq( g->w_terrain, g->u, elem.first, true, false,
                     g->u.pos() + g->u.view_offset );
    }
    wrefresh( g->w_terrain );

    tripoint dirp;
    if( !choose_adjacent( _( "Construct where?" ), dirp ) ) {
        return;
    }

    if( valid.find( dirp ) == valid.end() ) {
        add_msg( m_info, _( "You cannot build there!" ) );
        return;
    }

    const construction &con = *valid.find( dirp )->second;
    g->u.assign_activity( activity_id( "ACT_BUILD" ), con.adjusted_time(), con.id );
    g->u.activity.placement = dirp;
}
Example #2
0
nc_color construction_color( std::string &con_name, bool highlight )
{
    nc_color col = c_dkgray;
    if( g->u.has_trait( "DEBUG_HS" ) ) {
        col = c_white;
    } else if( can_construct( con_name ) ) {
        construction *con_first = nullptr;
        std::vector<construction *> cons = constructions_by_desc( con_name );
        const inventory &total_inv = g->u.crafting_inventory();
        for( auto &con : cons ) {
            if( con->requirements->can_make_with_inventory( total_inv ) ) {
                con_first = con;
                break;
            }
        }
        if( con_first != nullptr ) {
            int pskill = g->u.get_skill_level( con_first->skill );
            int diff = con_first->difficulty;
            if( pskill < diff ) {
                col = c_red;
            } else if( pskill == diff ) {
                col = c_ltblue;
            } else {
                col = c_white;
            }
        }
    }
    return highlight ? hilite( col ) : col;
}
bool player_can_build(player &p, inventory pinv, const std::string &desc)
{
    // check all with the same desc to see if player can build any
    std::vector<construction *> cons = constructions_by_desc(desc);
    for (unsigned i = 0; i < cons.size(); ++i) {
        if (player_can_build(p, pinv, cons[i])) {
            return true;
        }
    }
    return false;
}
Example #4
0
bool can_construct( const std::string &desc )
{
    // check all with the same desc to see if player can build any
    std::vector<construction *> cons = constructions_by_desc( desc );
    for( auto &con : cons ) {
        if( can_construct( *con ) ) {
            return true;
        }
    }
    return false;
}
Example #5
0
bool player_can_build( player &p, const inventory &pinv, const std::string &desc )
{
    // check all with the same desc to see if player can build any
    std::vector<construction *> cons = constructions_by_desc( desc );
    for( auto &con : cons ) {
        if( player_can_build( p, pinv, *con ) ) {
            return true;
        }
    }
    return false;
}
Example #6
0
static bool can_construct( const std::string &desc )
{
    // check all with the same desc to see if player can build any
    std::vector<construction *> cons = constructions_by_desc(desc);
    for( unsigned i = 0; i < cons.size(); ++i ) {
        if( can_construct(cons[i]) ) {
            return true;
        }
    }
    return false;
}
void place_construction(const std::string &desc)
{
    g->refresh_all();
    inventory total_inv = g->crafting_inventory(&(g->u));

    std::vector<construction *> cons = constructions_by_desc(desc);
    std::map<point, construction *> valid;
    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 (x == g->u.posx && y == g->u.posy) {
                y++;
            }
            for (unsigned i = 0; i < cons.size(); ++i) {
                if (can_construct(cons[i], x, y)
                    && player_can_build(g->u, total_inv, cons[i])) {
                    valid[point(x, y)] = cons[i];
                }
            }
        }
    }

    for (std::map<point, construction *>::iterator it = valid.begin();
         it != valid.end(); ++it) {
        int x = it->first.x, y = it->first.y;
        g->m.drawsq(g->w_terrain, g->u, x, y, true, false);
    }
    wrefresh(g->w_terrain);

    int dirx, diry;
    if (!g->choose_adjacent(_("Contruct where?"), dirx, diry)) {
        return;
    }

    point choice(dirx, diry);
    if (valid.find(choice) == valid.end()) {
        g->add_msg(_("You cannot build there!"));
        return;
    }

    construction *con = valid[choice];
    g->u.assign_activity(ACT_BUILD, con->time * 1000, con->id);
    g->u.activity.placement = choice;
}
void construction_menu()
{
    static bool hide_unconstructable = false;
    // only display constructions the player can theoretically perform
    std::vector<std::string> available;
    load_available_constructions( available, hide_unconstructable );

    if(available.empty()) {
        popup(_("You can not construct anything here."));
        return;
    }

    int iMaxY = TERMY;
    if (available.size() + 2 < iMaxY) {
        iMaxY = available.size() + 2;
    }
    if (iMaxY < FULL_SCREEN_HEIGHT) {
        iMaxY = FULL_SCREEN_HEIGHT;
    }

    WINDOW *w_con = newwin( iMaxY, FULL_SCREEN_WIDTH, (TERMY > iMaxY) ? (TERMY - iMaxY) / 2 : 0,
                            (TERMX > FULL_SCREEN_WIDTH) ? (TERMX - FULL_SCREEN_WIDTH) / 2 : 0 );
    draw_border(w_con);
    mvwprintz(w_con, 0, 8, c_ltred, _(" Construction "));

    mvwputch(w_con,  0, 30, c_ltgray, LINE_OXXX);
    mvwputch(w_con, iMaxY - 1, 30, c_ltgray, LINE_XXOX);
    for( int i = 1; i < iMaxY - 1; ++i ) {
        mvwputch(w_con, i, 30, c_ltgray, LINE_XOXO);
    }

    wrefresh(w_con);

    bool update_info = true;
    int select = 0;
    int chosen = 0;
    long ch;
    bool exit = false;

    inventory total_inv = g->crafting_inventory(&(g->u));

    do {
        // Erase existing list of constructions
        for( int i = 1; i < iMaxY - 1; i++ ) {
            for( int j = 1; j < 30; j++ ) {
                mvwputch(w_con, i, j, c_black, ' ');
            }
        }
        //Draw Scrollbar
        draw_scrollbar(w_con, select, iMaxY - 2, available.size(), 1);
        // Determine where in the master list to start printing
        //int offset = select - 11;
        int offset = 0;
        if (select >= iMaxY - 2) {
            offset = select - iMaxY + 3;
        }
        // Print the constructions between offset and max (or how many will fit)
        for (int i = 0; i < iMaxY - 2 && (i + offset) < available.size(); i++) {
            int current = i + offset;
            nc_color col = (player_can_build(g->u, total_inv, available[current]) ?
                            c_white : c_dkgray);
            // Map menu items to hotkey letters, skipping j, k, l, and q.
            unsigned char hotkey = 97 + current;
            if (hotkey > 122) {
                hotkey = hotkey - 58;
            }

            if (current == select) {
                col = hilite(col);
            }
            // print construction name with limited length.
            // limit(28) = 30(column len) - 2(letter + ' ').
            mvwprintz(w_con, 1 + i, 1, col, "%c %s", hotkey,
                      utf8_substr(available[current].c_str(), 0, 27).c_str());
        }

        if (update_info) {
            update_info = false;
            std::string current_desc = available[select];
            // Clear out lines for tools & materials
            for (int i = 1; i < iMaxY - 1; i++) {
                for (int j = 31; j < 79; j++) {
                    mvwputch(w_con, i, j, c_black, ' ');
                }
            }

            // Print instructions for toggling recipe hiding.
            mvwprintz(w_con, 1, 31, c_white, "%s", _("Press ';' to toggle unavailable constructions."));

            // Print consruction name
            mvwprintz(w_con, 2, 31, c_white, "%s", current_desc.c_str());

            // Print stages and their requirement
            int posx = 33, posy = 2;
            std::vector<construction *> options = constructions_by_desc(current_desc);
            for( unsigned i = 0; i < options.size(); ++i) {
                construction *current_con = options[i];
                if( hide_unconstructable && !can_construct(current_con) ) {
                    continue;
                }
                nc_color color_stage = c_white;

                // display required skill and difficulty
                int pskill = g->u.skillLevel(current_con->skill);
                int diff = (current_con->difficulty > 0) ? current_con->difficulty : 0;
                posy++;
                mvwprintz(w_con, posy, 31, c_white,
                          _("Skill: %s"), Skill::skill(current_con->skill)->name().c_str());
                posy++;
                mvwprintz(w_con, posy, 31, (pskill >= diff ? c_white : c_red),
                          _("Difficulty: %d"), diff);
                // display required terrain
                if (current_con->pre_terrain != "") {
                    posy++;
                    if (current_con->pre_is_furniture) {
                        mvwprintz(w_con, posy, 31, color_stage, _("Replaces: %s"),
                                  furnmap[current_con->pre_terrain].name.c_str());
                    } else {
                        mvwprintz(w_con, posy, 31, color_stage, _("Replaces: %s"),
                                  termap[current_con->pre_terrain].name.c_str());
                    }
                }
                // display result
                if (current_con->post_terrain != "") {
                    posy++;
                    if (current_con->post_is_furniture) {
                        mvwprintz(w_con, posy, 31, color_stage, _("Result: %s"),
                                  furnmap[current_con->post_terrain].name.c_str());
                    } else {
                        mvwprintz(w_con, posy, 31, color_stage, _("Result: %s"),
                                  termap[current_con->post_terrain].name.c_str());
                    }
                }
                // display time needed
                posy++;
                mvwprintz(w_con, posy, 31, color_stage, _("Time: %1d minutes"), current_con->time);
                // Print tools
                std::vector<bool> has_tool;
                posy++;
                posx = 33;
                for (int i = 0; i < current_con->tools.size(); i++) {
                    has_tool.push_back(false);
                    mvwprintz(w_con, posy, posx - 2, c_white, ">");
                    for (unsigned j = 0; j < current_con->tools[i].size(); j++) {
                        itype_id tool = current_con->tools[i][j].type;
                        nc_color col = c_red;
                        if (total_inv.has_amount(tool, 1)) {
                            has_tool[i] = true;
                            col = c_green;
                        }
                        int length = utf8_width(item_controller->find_template(tool)->name.c_str());
                        if( posx + length > FULL_SCREEN_WIDTH - 1 ) {
                            posy++;
                            posx = 33;
                        }
                        mvwprintz(w_con, posy, posx, col,
                                  item_controller->find_template(tool)->name.c_str());
                        posx += length + 1; // + 1 for an empty space
                        if (j < current_con->tools[i].size() - 1) { // "OR" if there's more
                            if (posx > FULL_SCREEN_WIDTH - 3) {
                                posy++;
                                posx = 33;
                            }
                            mvwprintz(w_con, posy, posx, c_white, _("OR"));
                            posx += utf8_width(_("OR")) + 1;
                        }
                    }
                    posy ++;
                    posx = 33;
                }
                // Print components
                posx = 33;
                std::vector<bool> has_component;
                for( int i = 0; i < current_con->components.size(); i++ ) {
                    has_component.push_back(false);
                    mvwprintz(w_con, posy, posx - 2, c_white, ">");
                    for( unsigned j = 0; j < current_con->components[i].size(); j++ ) {
                        nc_color col = c_red;
                        component comp = current_con->components[i][j];
                        if( ( item_controller->find_template(comp.type)->is_ammo() &&
                              total_inv.has_charges(comp.type, comp.count)) ||
                            (!item_controller->find_template(comp.type)->is_ammo() &&
                             total_inv.has_amount(comp.type, comp.count)) ) {
                            has_component[i] = true;
                            col = c_green;
                        }
                        int length = utf8_width(item_controller->find_template(comp.type)->name.c_str());
                        if (posx + length > FULL_SCREEN_WIDTH - 1) {
                            posy++;
                            posx = 33;
                        }
                        mvwprintz(w_con, posy, posx, col, "%s x%d",
                                  item_controller->find_template(comp.type)->name.c_str(), comp.count);
                        posx += length + 3; // + 2 for " x", + 1 for an empty space
                        // Add more space for the length of the count
                        if (comp.count < 10) {
                            posx++;
                        } else if (comp.count < 100) {
                            posx += 2;
                        } else {
                            posx += 3;
                        }

                        if (j < current_con->components[i].size() - 1) { // "OR" if there's more
                            if (posx > FULL_SCREEN_WIDTH - 3) {
                                posy++;
                                posx = 33;
                            }
                            mvwprintz(w_con, posy, posx, c_white, _("OR"));
                            posx += utf8_width(_("OR")) + 1;
                        }
                    }
                    posy ++;
                    posx = 33;
                }
            }
            wrefresh(w_con);
        } // Finished updating

        ch = getch();
        switch (ch) {
        case KEY_DOWN:
            update_info = true;
            if (select < available.size() - 1) {
                select++;
            } else {
                select = 0;
            }
            break;
        case KEY_UP:
            update_info = true;
            if (select > 0) {
                select--;
            } else {
                select = available.size() - 1;
            }
            break;
        case ' ':
        case KEY_ESCAPE:
        case 'q':
        case 'Q':
            exit = true;
            break;
        case ';':
            update_info = true;
            hide_unconstructable = !hide_unconstructable;
            load_available_constructions( available, hide_unconstructable );
            break;
        case '\n':
        default:
            if (ch > 64 && ch < 91) { //A-Z
                chosen = ch - 65 + 26;
            } else if (ch > 96 && ch < 123) { //a-z
                chosen = ch - 97;
            } else if (ch == '\n') {
                chosen = select;
            }
            if (chosen < available.size()) {
                if (player_can_build(g->u, total_inv, available[chosen])) {
                    place_construction(available[chosen]);
                    exit = true;
                } else {
                    popup(_("You can't build that!"));
                    select = chosen;
                    for (int i = 1; i < iMaxY - 1; i++) {
                        mvwputch(w_con, i, 30, c_ltgray, LINE_XOXO);
                    }
                    update_info = true;
                }
            }
            break;
        }
    } while (!exit);

    for (int i = iMaxY - FULL_SCREEN_HEIGHT; i <= iMaxY; ++i) {
        for (int j = TERRAIN_WINDOW_WIDTH; j <= FULL_SCREEN_WIDTH; ++j) {
            mvwputch(w_con, i, j, c_black, ' ');
        }
    }

    wrefresh(w_con);
    g->refresh_all();
}
Example #9
0
void construction_menu()
{
    static bool hide_unconstructable = false;
    // only display constructions the player can theoretically perform
    std::vector<std::string> available;
    std::map<std::string, std::vector<std::string>> cat_available;
    load_available_constructions( available, cat_available, hide_unconstructable );

    if( available.empty() ) {
        popup( _( "You can not construct anything here." ) );
        return;
    }

    int w_height = TERMY;
    if( ( int )available.size() + 2 < w_height ) {
        w_height = available.size() + 2;
    }
    if( w_height < FULL_SCREEN_HEIGHT ) {
        w_height = FULL_SCREEN_HEIGHT;
    }

    const int w_width = std::max( FULL_SCREEN_WIDTH, TERMX * 2 / 3);
    const int w_y0 = ( TERMY > w_height ) ? ( TERMY - w_height ) / 2 : 0;
    const int w_x0 = ( TERMX > w_width ) ? ( TERMX - w_width ) / 2 : 0;
    WINDOW_PTR w_con_ptr {newwin( w_height, w_width, w_y0, w_x0 )};
    WINDOW *const w_con = w_con_ptr.get();

    const int w_list_width = int( .375 * w_width );
    const int w_list_height = w_height - 4;
    const int w_list_x0 = 1;
    WINDOW_PTR w_list_ptr {newwin( w_list_height, w_list_width, w_y0 + 3, w_x0 + w_list_x0 )};
    WINDOW *const w_list = w_list_ptr.get();

    draw_grid( w_con, w_list_width + w_list_x0 );

    //tabcount needs to be increased to add more categories
    int tabcount = 10;
    std::string construct_cat[] = {_( "All" ), _( "Constructions" ), _( "Furniture" ),
                                   _( "Digging and Mining" ), _( "Repairing" ),
                                   _( "Reinforcing" ), _( "Decorative" ),
                                   _( "Farming and Woodcutting" ), _( "Others" ),
                                   _( "Filter" )
                                  };

    bool update_info = true;
    bool update_cat = true;
    bool isnew = true;
    int tabindex = 0;
    int select = 0;
    int offset = 0;
    bool exit = false;
    std::string category_name = "";
    std::vector<std::string> constructs;
    //storage for the color text so it can be scrolled
    std::vector< std::vector < std::string > > construct_buffers;
    std::vector<std::string> full_construct_buffer;
    std::vector<int> construct_buffer_breakpoints;
    int total_project_breakpoints = 0;
    int current_construct_breakpoint = 0;
    bool previous_hide_unconstructable = false;
    //track the cursor to determine when to refresh the list of construction recipes
    int previous_tabindex = -1;
    int previous_select = -1;

    const inventory &total_inv = g->u.crafting_inventory();

    input_context ctxt( "CONSTRUCTION" );
    ctxt.register_action( "UP", _( "Move cursor up" ) );
    ctxt.register_action( "DOWN", _( "Move cursor down" ) );
    ctxt.register_action( "RIGHT", _( "Move tab right" ) );
    ctxt.register_action( "LEFT", _( "Move tab left" ) );
    ctxt.register_action( "PAGE_UP" );
    ctxt.register_action( "PAGE_DOWN" );
    ctxt.register_action( "CONFIRM" );
    ctxt.register_action( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" );
    ctxt.register_action( "QUIT" );
    ctxt.register_action( "HELP_KEYBINDINGS" );
    ctxt.register_action( "FILTER" );

    std::string filter;
    int previous_index = 0;
    do {
        if( update_cat ) {
            update_cat = false;
            switch( tabindex ) {
                case 0:
                    category_name = "ALL";
                    break;
                case 1:
                    category_name = "CONSTRUCT";
                    break;
                case 2:
                    category_name = "FURN";
                    break;
                case 3:
                    category_name = "DIG";
                    break;
                case 4:
                    category_name = "REPAIR";
                    break;
                case 5:
                    category_name = "REINFORCE";
                    break;
                case 6:
                    category_name = "DECORATE";
                    break;
                case 7:
                    category_name = "FARM_WOOD";
                    break;
                case 8:
                    category_name = "OTHER";
                    break;
                case 9:
                    category_name = "FILTER";
                    break;
            }

            if( category_name == "ALL" ) {
                constructs = available;
                previous_index = tabindex;
            } else if( category_name == "FILTER" ) {
                constructs.clear();
                std::copy_if( available.begin(), available.end(),
                    std::back_inserter( constructs ),
                    [&](const std::string &a){
                        return lcmatch(a, filter);
                    } );
            } else {
                constructs = cat_available[category_name];
                previous_index = tabindex;
            }
            if( isnew ){
                if( !uistate.last_construction.empty() ){
                    select = std::distance(constructs.begin(),
                                            std::find( constructs.begin(),
                                                        constructs.end(),
                                                        uistate.last_construction ));
                }
                filter = uistate.construction_filter;
            }
        }
        // Erase existing tab selection & list of constructions
        mvwhline( w_con, 1, 1, ' ', w_list_width );
        werase( w_list );
        // Print new tab listing
        mvwprintz( w_con, 1, 1, c_yellow, "<< %s >>", construct_cat[tabindex].c_str() );
        // Determine where in the master list to start printing
        calcStartPos( offset, select, w_list_height, constructs.size() );
        // Print the constructions between offset and max (or how many will fit)
        for( size_t i = 0; ( int )i < w_list_height && ( i + offset ) < constructs.size(); i++ ) {
            int current = i + offset;
            std::string con_name = constructs[current];
            bool highlight = ( current == select );

            trim_and_print( w_list, i, 0, w_list_width,
                            construction_color( con_name, highlight ), "%s",
                            con_name.c_str() );
        }

        if( update_info ) {
            update_info = false;
            // Clear out lines for tools & materials
            const int pos_x = w_list_width + w_list_x0 + 2;
            const int available_window_width = w_width - pos_x - 1;
            for( int i = 1; i < w_height - 1; i++ ) {
                mvwhline( w_con, i, pos_x, ' ', available_window_width );
            }

            nc_color color_stage = c_white;
            std::vector<std::string> notes;
            notes.push_back( string_format( _( "Press %s or %s to tab." ),
                             ctxt.get_desc( "LEFT" ).c_str(), ctxt.get_desc( "RIGHT" ).c_str() ) );
            notes.push_back( string_format( _( "Press %s to search." ),
                             ctxt.get_desc( "FILTER" ).c_str() ) );
            notes.push_back( string_format( _( "Press %s to toggle unavailable constructions." ),
                            ctxt.get_desc( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ).c_str() ) );
            notes.push_back( string_format( _( "Press %s to view and edit key-bindings." ),
                            ctxt.get_desc( "HELP_KEYBINDINGS" ).c_str() ) );

            //leave room for top and bottom UI text
            const int available_buffer_height = w_height - 3 - 3 - (int)notes.size();

            // print the hotkeys regardless of if there are constructions
            for( size_t i = 0; i < notes.size(); ++i ) {
                trim_and_print( w_con, w_height - 1 - (int)notes.size() + (int)i, pos_x,
                                available_window_width, c_white, "%s", notes[i].c_str() );
            }

            if( !constructs.empty() ) {
                if( select >= (int) constructs.size() ){
                    select = 0;
                }
                std::string current_desc = constructs[select];
                // Print construction name
                trim_and_print( w_con, 1, pos_x, available_window_width, c_white,
                                "%s", current_desc.c_str() );

                //only reconstruct the project list when moving away from the current item, or when changing the display mode
                if( previous_select != select || previous_tabindex != tabindex ||
                    previous_hide_unconstructable != hide_unconstructable ) {
                    previous_select = select;
                    previous_tabindex = tabindex;
                    previous_hide_unconstructable = hide_unconstructable;

                    //construct the project list buffer

                    // Print stages and their requirement.
                    std::vector<construction *> options = constructions_by_desc( current_desc );

                    construct_buffers.clear();
                    total_project_breakpoints = 0;
                    current_construct_breakpoint = 0;
                    construct_buffer_breakpoints.clear();
                    full_construct_buffer.clear();
                    int stage_counter = 0;
                    for( std::vector<construction *>::iterator it = options.begin();
                         it != options.end(); ++it ) {
                        stage_counter++;
                        construction *current_con = *it;
                        if( hide_unconstructable && !can_construct( *current_con ) ) {
                            continue;
                        }
                        // Update the cached availability of components and tools in the requirement object
                        current_con->requirements->can_make_with_inventory( total_inv );

                        std::vector<std::string> current_buffer;
                        std::ostringstream current_line;

                        // display result only if more than one step.
                        // Assume single stage constructions should be clear
                        // in their description what their result is.
                        if( current_con->post_terrain != "" && options.size() > 1 ) {
                            //also print out stage number when multiple stages are available
                            current_line << _( "Stage #" ) << stage_counter;
                            current_buffer.push_back( current_line.str() );
                            current_line.str( "" );

                            std::string result_string;
                            if( current_con->post_is_furniture ) {
                                result_string = furn_str_id( current_con->post_terrain ).obj().name;
                            } else {
                                result_string = ter_str_id( current_con->post_terrain ).obj().name;
                            }
                            current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format(
                                             _( "Result: %s" ), result_string.c_str() ) << "</color>";
                            std::vector<std::string> folded_result_string = foldstring( current_line.str(),
                                    available_window_width );
                            current_buffer.insert( current_buffer.end(), folded_result_string.begin(),
                                                   folded_result_string.end() );
                        }

                        current_line.str( "" );
                        // display required skill and difficulty
                        int pskill = g->u.get_skill_level( current_con->skill );
                        int diff = ( current_con->difficulty > 0 ) ? current_con->difficulty : 0;

                        current_line << "<color_" << string_from_color( ( pskill >= diff ? c_white : c_red ) ) << ">" <<
                                     string_format( _( "Skill Req: %d (%s)" ), diff,
                                                    current_con->skill.obj().name().c_str() ) << "</color>";
                        current_buffer.push_back( current_line.str() );
                        // TODO: Textify pre_flags to provide a bit more information.
                        // Example: First step of dig pit could say something about
                        // requiring diggable ground.
                        current_line.str( "" );
                        if( current_con->pre_terrain != "" ) {
                            std::string require_string;
                            if( current_con->pre_is_furniture ) {
                                require_string = furn_str_id( current_con->pre_terrain ).obj().name;
                            } else {
                                require_string = ter_str_id( current_con->pre_terrain ).obj().name;
                            }
                            current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format(
                                             _( "Requires: %s" ), require_string.c_str() ) << "</color>";
                            std::vector<std::string> folded_result_string = foldstring( current_line.str(),
                                    available_window_width );
                            current_buffer.insert( current_buffer.end(), folded_result_string.begin(),
                                                   folded_result_string.end() );
                        }
                        // get pre-folded versions of the rest of the construction project to be displayed later

                        // get time needed
                        std::vector<std::string> folded_time = current_con->get_folded_time_string(
                                available_window_width );
                        current_buffer.insert( current_buffer.end(), folded_time.begin(), folded_time.end() );

                        std::vector<std::string> folded_tools = current_con->requirements->get_folded_tools_list(
                                available_window_width, color_stage, total_inv );
                        current_buffer.insert( current_buffer.end(), folded_tools.begin(), folded_tools.end() );

                        std::vector<std::string> folded_components = current_con->requirements->get_folded_components_list(
                                    available_window_width, color_stage, total_inv );
                        current_buffer.insert( current_buffer.end(), folded_components.begin(), folded_components.end() );

                        construct_buffers.push_back( current_buffer );
                    }

                    //determine where the printing starts for each project, so it can be scrolled to those points
                    size_t current_buffer_location = 0;
                    for( size_t i = 0; i < construct_buffers.size(); i++ ) {
                        construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location ) );
                        full_construct_buffer.insert( full_construct_buffer.end(), construct_buffers[i].begin(),
                                                      construct_buffers[i].end() );

                        //handle text too large for one screen
                        if( construct_buffers[i].size() > static_cast<size_t>( available_buffer_height ) ) {
                            construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location +
                                                                    static_cast<size_t>( available_buffer_height ) ) );
                        }
                        current_buffer_location += construct_buffers[i].size();
                        if( i < construct_buffers.size() - 1 ) {
                            full_construct_buffer.push_back( std::string( "" ) );
                            current_buffer_location++;
                        }
                    }
                    total_project_breakpoints = static_cast<int>( construct_buffer_breakpoints.size() );
                }
                if( current_construct_breakpoint > 0 ) {
                    // Print previous stage indicator if breakpoint is past the beginning
                    trim_and_print( w_con, 2, pos_x, available_window_width, c_white,
                                    _( "Press %s to show previous stage(s)." ),
                                    ctxt.get_desc( "PAGE_UP" ).c_str() );
                }
                if( static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] +
                                         available_buffer_height ) < full_construct_buffer.size() ) {
                    // Print next stage indicator if more breakpoints are remaining after screen height
                    trim_and_print( w_con, w_height - 2 - (int)notes.size(), pos_x, available_window_width,
                                    c_white, _( "Press %s to show next stage(s)." ),
                                    ctxt.get_desc( "PAGE_DOWN" ).c_str() );
                }
                // Leave room for above/below indicators
                int ypos = 3;
                nc_color stored_color = color_stage;
                for( size_t i = static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] );
                     i < full_construct_buffer.size(); i++ ) {
                    //the value of 3 is from leaving room at the top of window
                    if( ypos > available_buffer_height + 3 ) {
                        break;
                    }
                    print_colored_text( w_con, ypos++, ( w_list_width + w_list_x0 + 2 ), stored_color, color_stage, full_construct_buffer[i] );
                }
            }
        } // Finished updating

        draw_scrollbar( w_con, select, w_list_height, constructs.size(), 3 );
        wrefresh( w_con );
        wrefresh( w_list );

        const std::string action = ctxt.handle_input();
        if( action == "FILTER" ){
            filter = string_input_popup( _( "Search" ), 50, filter, "", _( "Filter" ), 100, false );
            if( !filter.empty() ){
                update_info = true;
                update_cat = true;
                tabindex = 9;
                select = 0;
            }else if( previous_index !=9 ){
                tabindex = previous_index;
                update_info = true;
                update_cat = true;
                select = 0;
            }
            uistate.construction_filter = filter;
        } else if( action == "DOWN" ) {
            update_info = true;
            if( select < ( int )constructs.size() - 1 ) {
                select++;
            } else {
                select = 0;
            }
        } else if( action == "UP" ) {
            update_info = true;
            if( select > 0 ) {
                select--;
            } else {
                select = constructs.size() - 1;
            }
        } else if( action == "LEFT" ) {
            update_info = true;
            update_cat = true;
            select = 0;
            tabindex--;
            if( tabindex < 0 ) {
                tabindex = tabcount - 1;
            }
        } else if( action == "RIGHT" ) {
            update_info = true;
            update_cat = true;
            select = 0;
            tabindex = ( tabindex + 1 ) % tabcount;
        } else if( action == "PAGE_UP" ) {
            update_info = true;
            if( current_construct_breakpoint > 0 ) {
                current_construct_breakpoint--;
            }
            if( current_construct_breakpoint < 0 ) {
                current_construct_breakpoint = 0;
            }
        } else if( action == "PAGE_DOWN" ) {
            update_info = true;
            if( current_construct_breakpoint < total_project_breakpoints - 1 ) {
                current_construct_breakpoint++;
            }
            if( current_construct_breakpoint >= total_project_breakpoints ) {
                current_construct_breakpoint = total_project_breakpoints - 1;
            }
        } else if( action == "QUIT" ) {
            exit = true;
        } else if( action == "HELP_KEYBINDINGS" ) {
            draw_grid( w_con, w_list_width + w_list_x0 );
        } else if( action == "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ) {
            update_info = true;
            update_cat = true;
            hide_unconstructable = !hide_unconstructable;
            select = 0;
            offset = 0;
            load_available_constructions( available, cat_available, hide_unconstructable );
        } else if( action == "CONFIRM" ) {
            if( constructs.empty() || select >= (int) constructs.size() ){
                continue;// Nothing to be done here
            }
            if( player_can_build( g->u, total_inv, constructs[select] ) ) {
                place_construction( constructs[select] );
                uistate.last_construction = constructs[select];
                exit = true;
            } else {
                popup( _( "You can't build that!" ) );
                draw_grid( w_con, w_list_width + w_list_x0 );
                update_info = true;
            }
        }
    } while( !exit );

    w_list_ptr.reset();
    w_con_ptr.reset();
    g->refresh_all();
}