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; }
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; }
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; }
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(g, ACT_BUILD, con->time * 1000, con->id); g->u.moves = 0; g->u.activity.placement = choice; }
void game::place_construction(constructable *con) { refresh_all(); inventory total_inv; total_inv.form_from_map(this, point(u.posx, u.posy), PICKUP_RANGE); total_inv.add_stack(u.inv_dump()); std::vector<point> valid; for (int x = u.posx - 1; x <= u.posx + 1; x++) { for (int y = u.posy - 1; y <= u.posy + 1; y++) { if (x == u.posx && y == u.posy) y++; construct test; bool place_okay = (test.*(con->able))(this, point(x, y)); for (int i = 0; i < con->stages.size() && !place_okay; i++) { if (m.ter(x, y) == con->stages[i].terrain) place_okay = true; } if (place_okay) { // Make sure we're not trying to continue a construction that we can't finish int starting_stage = 0, max_stage = 0; for (int i = 0; i < con->stages.size(); i++) { if (m.ter(x, y) == con->stages[i].terrain) starting_stage = i + 1; if (player_can_build(u, total_inv, con, i, true)) max_stage = i; } if (max_stage >= starting_stage) { valid.push_back(point(x, y)); m.drawsq(w_terrain, u, x, y, true, false); wrefresh(w_terrain); } } } } mvprintz(0, 0, c_red, _("Pick a direction in which to construct:")); int dirx, diry; get_direction(this, dirx, diry, input()); if (dirx == -2) { add_msg(_("Invalid direction.")); return; } dirx += u.posx; diry += u.posy; bool point_is_okay = false; for (int i = 0; i < valid.size() && !point_is_okay; i++) { if (valid[i].x == dirx && valid[i].y == diry) point_is_okay = true; } if (!point_is_okay) { add_msg(_("You cannot build there!")); return; } // Figure out what stage to start at, and what stage is the maximum int starting_stage = 0, max_stage = 0; for (int i = 0; i < con->stages.size(); i++) { if (m.ter(dirx, diry) == con->stages[i].terrain) starting_stage = i + 1; if (player_can_build(u, total_inv, con, i, true)) max_stage = i; } u.activity = player_activity(ACT_BUILD, con->stages[starting_stage].time*1000, con->id); u.moves = 0; std::vector<int> stages; for (int i = starting_stage; i <= max_stage; i++) stages.push_back(i); u.activity.values = stages; u.activity.placement = point(dirx, diry); }
void game::construction_menu() { WINDOW *w_con = newwin(25, 80, 0, 0); wborder(w_con, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); mvwprintz(w_con, 0, 1, c_red, _("Construction")); mvwputch(w_con, 0, 30, c_white, LINE_OXXX); mvwputch(w_con, 24, 30, c_white, LINE_XXOX); for (int i = 1; i < 24; i++) mvwputch(w_con, i, 30, c_white, LINE_XOXO); mvwprintz(w_con, 1, 31, c_white, _("Difficulty:")); wrefresh(w_con); bool update_info = true; int select = 0; char ch; inventory total_inv; total_inv.form_from_map(this, point(u.posx, u.posy), PICKUP_RANGE); total_inv.add_stack(u.inv_dump()); if (u.has_bionic(bio_tools)) { item tools(itypes[itm_toolset], turn); tools.charges = u.power_level; total_inv += tools; } do { // Determine where in the master list to start printing int offset = select - 11; if (offset > constructions.size() - 22) offset = constructions.size() - 22; if (offset < 0) offset = 0; // Print the constructions between offset and max (or how many will fit) for (int i = 0; i < 22 && i + offset < constructions.size(); i++) { int current = i + offset; nc_color col = (player_can_build(u, total_inv, constructions[current], 0) ? c_white : c_dkgray); if (current == select) col = hilite(col); mvwprintz(w_con, 1 + i, 1, col, constructions[current]->name.c_str()); } if (update_info) { update_info = false; constructable* current_con = constructions[select]; // Print difficulty int pskill = u.sklevel[sk_carpentry], diff = current_con->difficulty; mvwprintz(w_con, 1, 43, (pskill >= diff ? c_white : c_red), "%d ", diff); // Clear out lines for tools & materials for (int i = 2; i < 24; i++) { for (int j = 31; j < 79; j++) mvwputch(w_con, i, j, c_black, 'x'); } // Print stages and their requirements int posx = 33, posy = 2; for (int n = 0; n < current_con->stages.size(); n++) { nc_color color_stage = (player_can_build(u, total_inv, current_con, n) ? c_white : c_dkgray); mvwprintz(w_con, posy, 31, color_stage, _("Stage %d: %s"), n + 1, terlist[current_con->stages[n].terrain].name.c_str()); posy++; // Print tools construction_stage stage = current_con->stages[n]; bool has_tool[3] = {stage.tools[0].empty(), stage.tools[1].empty(), stage.tools[2].empty()}; for (int i = 0; i < 3 && !has_tool[i]; i++) { posy++; posx = 33; for (int j = 0; j < stage.tools[i].size(); j++) { itype_id tool = stage.tools[i][j]; nc_color col = c_red; if (total_inv.has_amount(tool, 1)) { has_tool[i] = true; col = c_green; } int length = itypes[tool]->name.length(); if (posx + length > 79) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, col, itypes[tool]->name.c_str()); posx += length + 1; // + 1 for an empty space if (j < stage.tools[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, _("OR")); posx += 3; } } } // Print components posy++; posx = 33; bool has_component[3] = {stage.components[0].empty(), stage.components[1].empty(), stage.components[2].empty()}; for (int i = 0; i < 3; i++) { posx = 33; while (has_component[i]) i++; for (int j = 0; j < stage.components[i].size() && i < 3; j++) { nc_color col = c_red; component comp = stage.components[i][j]; if (( itypes[comp.type]->is_ammo() && total_inv.has_charges(comp.type, comp.count)) || (!itypes[comp.type]->is_ammo() && total_inv.has_amount(comp.type, comp.count))) { has_component[i] = true; col = c_green; } int length = itypes[comp.type]->name.length(); if (posx + length > 79) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, col, "%s x%d", itypes[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 < stage.components[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, _("OR")); posx += 3; } } posy++; } } wrefresh(w_con); } // Finished updating ch = input(); switch (ch) { case 'j': update_info = true; if (select < constructions.size() - 1) select++; else select = 0; break; case 'k': update_info = true; if (select > 0) select--; else select = constructions.size() - 1; break; case '\n': case 'l': if (player_can_build(u, total_inv, constructions[select], 0)) { place_construction(constructions[select]); ch = 'q'; } else { popup(_("You can't build that!")); for (int i = 1; i < 24; i++) mvwputch(w_con, i, 30, c_white, LINE_XOXO); update_info = true; } break; } } while (ch != 'q' && ch != 'Q' && ch != KEY_ESCAPE); refresh_all(); }
void game::construction_menu() { int iMaxY = TERMY; if (constructions.size()+2 < iMaxY) iMaxY = constructions.size()+2; if (iMaxY < 25) iMaxY = 25; WINDOW *w_con = newwin(iMaxY, 80, (TERMY > iMaxY) ? (TERMY-iMaxY)/2 : 0, (TERMX > 80) ? (TERMX-80)/2 : 0); wborder(w_con, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); 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); mvwprintz(w_con, 1, 31, c_white, "Difficulty:"); wrefresh(w_con); bool update_info = true; int select = 0; int chosen = 0; long ch; bool exit = false; inventory total_inv = crafting_inventory(); 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, ' '); } // 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) < constructions.size(); i++) { int current = i + offset; nc_color col = (player_can_build(u, total_inv, constructions[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); mvwprintz(w_con, 1 + i, 1, col, "%c %s", hotkey, constructions[current]->name.c_str()); } if (update_info) { update_info = false; constructable* current_con = constructions[select]; // Print difficulty int pskill = u.skillLevel("carpentry"); int diff = current_con->difficulty > 0 ? current_con->difficulty : 0; mvwprintz(w_con, 1, 43, (pskill >= diff ? c_white : c_red), "%d ", diff); // Clear out lines for tools & materials for (int i = 2; i < iMaxY-1; i++) { for (int j = 31; j < 79; j++) mvwputch(w_con, i, j, c_black, ' '); } // Print stages and their requirements int posx = 33, posy = 2; for (int n = 0; n < current_con->stages.size(); n++) { nc_color color_stage = (player_can_build(u, total_inv, current_con, n, false, true) ? c_white : c_dkgray); mvwprintz(w_con, posy, 31, color_stage, "Stage %d: %s", n + 1, current_con->stages[n].terrain == t_null? "" : terlist[current_con->stages[n].terrain].name.c_str()); posy++; // Print tools construction_stage stage = current_con->stages[n]; bool has_tool[10] = {stage.tools[0].empty(), stage.tools[1].empty(), stage.tools[2].empty(), stage.tools[3].empty(), stage.tools[4].empty(), stage.tools[5].empty(), stage.tools[6].empty(), stage.tools[7].empty(), stage.tools[8].empty(), stage.tools[9].empty()}; posy++; posx = 33; for (int i = 0; i < 9 && !has_tool[i]; i++) { mvwprintz(w_con, posy, posx-2, c_white, ">"); for (int j = 0; j < stage.tools[i].size(); j++) { itype_id tool = stage.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 = item_controller->find_template(tool)->name.length(); if (posx + length > 79) { 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 < stage.tools[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, "OR"); posx += 3; } } posy += 2; posx = 33; } // Print components posx = 33; bool has_component[10] = {stage.components[0].empty(), stage.components[1].empty(), stage.components[2].empty(), stage.components[3].empty(), stage.components[4].empty(), stage.components[5].empty(), stage.components[6].empty(), stage.components[7].empty(), stage.components[8].empty(), stage.components[9].empty()}; for (int i = 0; i < 10; i++) { if (has_component[i]) continue; mvwprintz(w_con, posy, posx-2, c_white, ">"); for (int j = 0; j < stage.components[i].size() && i < 10; j++) { nc_color col = c_red; component comp = stage.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 = item_controller->find_template(comp.type)->name.length(); if (posx + length > 79) { 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 < stage.components[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, "OR"); posx += 3; } } posx = 33; posy += 2; } } wrefresh(w_con); } // Finished updating ch = getch(); switch (ch) { case KEY_DOWN: update_info = true; if (select < constructions.size() - 1) select++; else select = 0; break; case KEY_UP: update_info = true; if (select > 0) select--; else select = constructions.size() - 1; break; case ' ': case KEY_ESCAPE: case 'q': case 'Q': exit = true; 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 < constructions.size()) { if (player_can_build(u, total_inv, constructions[chosen])) { place_construction(constructions[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-25; i < iMaxY+1; i++) { for (int j = TERRAIN_WINDOW_WIDTH; j < 81; j++) mvwputch(w_con, i, j, c_black, ' '); } wrefresh(w_con); refresh_all(); }
void game::place_construction(constructable *con) { refresh_all(); inventory total_inv = crafting_inventory(&u); std::vector<point> valid; for (int x = u.posx - 1; x <= u.posx + 1; x++) { for (int y = u.posy - 1; y <= u.posy + 1; y++) { if (x == u.posx && y == u.posy) y++; construct test; bool place_okay = (test.*(con->able))(this, point(x, y)); for (int i = 0; i < con->stages.size() && !place_okay; i++) { ter_id t = con->stages[i].terrain; furn_id f = con->stages[i].furniture; if ((t != t_null || f != f_null) && (m.ter(x, y) == t || t == t_null) && (m.furn(x, y) == f || f == f_null)) place_okay = true; } if (place_okay) { // Make sure we're not trying to continue a construction that we can't finish int starting_stage = 0, max_stage = -1; for (int i = 0; i < con->stages.size(); i++) { ter_id t = con->stages[i].terrain; furn_id f = con->stages[i].furniture; if ((t != t_null || f != f_null) && (m.ter(x, y) == t || t == t_null) && (m.furn(x, y) == f || f == f_null)) starting_stage = i + 1; } if (starting_stage == con->stages.size() && con->loopstages) starting_stage = 0; // Looping stages for(int i = starting_stage; i < con->stages.size(); i++) { if (player_can_build(u, total_inv, con, i, true, true)) max_stage = i; else break; } if (max_stage >= starting_stage) { valid.push_back(point(x, y)); m.drawsq(w_terrain, u, x, y, true, false); wrefresh(w_terrain); } } } } // snip snip if (con->name == _("Move Furniture") ) { grab(); return; } // int dirx, diry; if (!choose_adjacent(_("Contruct where?"), dirx, diry)) return; bool point_is_okay = false; for (int i = 0; i < valid.size() && !point_is_okay; i++) { if (valid[i].x == dirx && valid[i].y == diry) point_is_okay = true; } if (!point_is_okay) { add_msg(_("You cannot build there!")); return; } // Figure out what stage to start at, and what stage is the maximum int starting_stage = 0, max_stage = 0; for (int i = 0; i < con->stages.size(); i++) { ter_id t = con->stages[i].terrain; furn_id f = con->stages[i].furniture; if ((t != t_null || f != f_null) && (m.ter(dirx, diry) == t || t == t_null) && (m.furn(dirx, diry) == f || f == f_null)) starting_stage = i + 1; if (player_can_build(u, total_inv, con, i, true)) max_stage = i; } if (starting_stage == con->stages.size() && con->loopstages) starting_stage = 0; // Looping stages u.assign_activity(this, ACT_BUILD, con->stages[starting_stage].time * 1000, con->id); u.moves = 0; std::vector<int> stages; for (int i = starting_stage; i <= max_stage; i++) stages.push_back(i); u.activity.values = stages; u.activity.placement = point(dirx, diry); }
void game::construction_menu() { WINDOW *w_con = newwin(25, 80, 0, 0); wborder(w_con, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); mvwprintz(w_con, 0, 1, c_red, "Construction"); mvwputch(w_con, 0, 30, c_white, LINE_OXXX); mvwputch(w_con, 24, 30, c_white, LINE_XXOX); for (int i = 1; i < 24; i++) mvwputch(w_con, i, 30, c_white, LINE_XOXO); mvwprintz(w_con, 1, 31, c_white, "Difficulty:"); wrefresh(w_con); bool update_info = true; unsigned int select = 0; char ch; inventory total_inv = crafting_inventory(); do { // Erase existing list of constructions for (int i = 1; i < 24; i++) { for (int j = 1; j < 29; j++) mvwputch(w_con, i, j, c_black, 'x'); } // Determine where in the master list to start printing //int offset = select - 11; int offset = 0; if (select >= 22) offset = select - 22; // Print the constructions between offset and max (or how many will fit) for (int i = 0; i <= 22 && (i + offset) < constructions.size(); i++) { int current = i + offset; nc_color col = (player_can_build(u, total_inv, constructions[current]) ? c_white : c_dkgray); // Map menu items to hotkey letters, skipping j, k, l, and q. char hotkey = current + ((current < 9) ? 97 : ((current < 13) ? 100 : 101)); if (current == select) col = hilite(col); mvwprintz(w_con, 1 + i, 1, col, "%c %s", hotkey, constructions[current]->name.c_str()); } if (update_info) { update_info = false; constructable* current_con = constructions[select]; // Print difficulty int pskill = u.skillLevel("carpentry").level(); int diff = current_con->difficulty > 0 ? current_con->difficulty : 0; mvwprintz(w_con, 1, 43, (pskill >= diff ? c_white : c_red), "%d ", diff); // Clear out lines for tools & materials for (int i = 2; i < 24; i++) { for (int j = 31; j < 79; j++) mvwputch(w_con, i, j, c_black, 'x'); } // Print stages and their requirements int posx = 33, posy = 2; for (int n = 0; n < current_con->stages.size(); n++) { nc_color color_stage = (player_can_build(u, total_inv, current_con, n, false, true) ? c_white : c_dkgray); mvwprintz(w_con, posy, 31, color_stage, "Stage %d: %s", n + 1, current_con->stages[n].terrain == t_null? "" : terlist[current_con->stages[n].terrain].name.c_str()); posy++; // Print tools construction_stage stage = current_con->stages[n]; bool has_tool[3] = {stage.tools[0].empty(), stage.tools[1].empty(), stage.tools[2].empty()}; for (int i = 0; i < 3 && !has_tool[i]; i++) { posy++; posx = 33; for (int j = 0; j < stage.tools[i].size(); j++) { itype_id tool = stage.tools[i][j]; nc_color col = c_red; if (total_inv.has_amount(tool, 1)) { has_tool[i] = true; col = c_green; } int length = itypes[tool]->name.length(); if (posx + length > 79) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, col, itypes[tool]->name.c_str()); posx += length + 1; // + 1 for an empty space if (j < stage.tools[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, "OR"); posx += 3; } } } // Print components posy++; posx = 33; bool has_component[3] = {stage.components[0].empty(), stage.components[1].empty(), stage.components[2].empty()}; for (int i = 0; i < 3; i++) { posx = 33; while (has_component[i]) i++; for (int j = 0; j < stage.components[i].size() && i < 3; j++) { nc_color col = c_red; component comp = stage.components[i][j]; if (( itypes[comp.type]->is_ammo() && total_inv.has_charges(comp.type, comp.count)) || (!itypes[comp.type]->is_ammo() && total_inv.has_amount(comp.type, comp.count))) { has_component[i] = true; col = c_green; } int length = itypes[comp.type]->name.length(); if (posx + length > 79) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, col, "%s x%d", itypes[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 < stage.components[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, "OR"); posx += 3; } } posy++; } } wrefresh(w_con); } // Finished updating ch = input(); switch (ch) { case 'j': update_info = true; if (select < constructions.size() - 1) select++; else select = 0; break; case 'k': update_info = true; if (select > 0) select--; else select = constructions.size() - 1; break; case '\n': case 'l': if (player_can_build(u, total_inv, constructions[select])) { place_construction(constructions[select]); ch = 'q'; } else { popup("You can't build that!"); for (int i = 1; i < 24; i++) mvwputch(w_con, i, 30, c_white, LINE_XOXO); update_info = true; } break; case 'q': case 'Q': case KEY_ESCAPE: break; default: if (ch < 96 || unsigned(ch) > constructions.size() + 101) break; // Map menu items to hotkey letters, skipping j, k, l, and q. char hotkey = ch - ((ch < 106) ? 97 : ((ch < 112) ? 100 : 101)); if (player_can_build(u, total_inv, constructions[hotkey])) { place_construction(constructions[hotkey]); ch = 'q'; } else { popup("You can't build that!"); for (int i = 1; i < 24; i++) mvwputch(w_con, i, 30, c_white, LINE_XOXO); update_info = true; } break; } } while (ch != 'q' && ch != 'Q' && ch != KEY_ESCAPE); refresh_all(); }
void construction_menu() { // only display constructions the player can theoretically perform std::vector<std::string> available; for (unsigned i = 0; i < constructions.size(); ++i) { construction *c = constructions[i]; if (can_construct(c)) { bool already_have_it = false; for (unsigned j = 0; j < available.size(); ++j) { if (available[j] == c->description) { already_have_it = true; break; } } if (!already_have_it) { available.push_back(c->description); } } } 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); wborder(w_con, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); 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); } mvwprintz(w_con, 1, 31, c_white, _("Difficulty:")); 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); mvwprintz(w_con, 1 + i, 1, col, "%c %s", hotkey, available[current].c_str()); } if (update_info) { update_info = false; std::string current_desc = available[select]; // Clear out lines for tools & materials for (int i = 2; i < iMaxY-1; i++) { for (int j = 31; j < 79; j++) mvwputch(w_con, i, j, c_black, ' '); } // Print stages and their requirement int posx = 33, posy = 0; std::vector<construction*> options = constructions_by_desc[current_desc]; for (unsigned i = 0; i < options.size(); ++i) { construction *current_con = options[i]; if (!can_construct(current_con)) { continue; } nc_color color_stage = c_white; // display difficulty int pskill = g->u.skillLevel("carpentry"); int diff = current_con->difficulty > 0 ? current_con->difficulty : 0; 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 += 3; } } 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 += 3; } } 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 '\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(); }
void game::place_construction(constructable *con) { refresh_all(); inventory total_inv; total_inv.form_from_map(this, point(u.posx, u.posy), PICKUP_RANGE); total_inv.add_stack(u.inv_dump()); std::vector<point> valid; for (int x = u.posx - 1; x <= u.posx + 1; x++) { for (int y = u.posy - 1; y <= u.posy + 1; y++) { if (x == u.posx && y == u.posy) y++; construct test; // initial function check int stage_num = ((test.*(con->able))(this, point(x, y)) ? 0 : -1); // looking for result tiles from completed previous stages for (int i = 0; i < con->stages.size() && stage_num < 0; i++) if (m.ter(x, y) == con->stages[i].terrain && i + 1 < con->stages.size()) stage_num = i + 1; if (player_can_build(u, total_inv, con, stage_num, true)) { valid.push_back(point(x, y)); m.drawsq(this, w_terrain, x, y, true, false); wrefresh(w_terrain); } } } if (valid.empty()) { add_msg("There is no proper place here!"); return; } mvprintz(0, 0, c_red, "Pick a direction in which to construct:"); int dirx, diry; get_direction(this, dirx, diry, input()); if (dirx == -2) { add_msg("Invalid direction."); return; } dirx += u.posx; diry += u.posy; bool point_is_okay = false; for (int i = 0; i < valid.size() && !point_is_okay; i++) { if (valid[i].x == dirx && valid[i].y == diry) point_is_okay = true; } if (!point_is_okay) { add_msg("You cannot build there!"); return; } // Figure out what stage to start at, and what stage is the maximum int starting_stage = 0, max_stage = 0; for (int i = 0; i < con->stages.size(); i++) { if (m.ter(dirx, diry) == con->stages[i].terrain) starting_stage = i + 1; if (player_can_build(u, total_inv, con, i, true)) max_stage = i; } u.assign_activity(ACT_BUILD, con->stages[starting_stage].time * 1000, con->id); u.moves = 0; std::vector<int> stages; for (int i = starting_stage; i <= max_stage; i++) stages.push_back(i); u.activity.values = stages; u.activity.placement = point(dirx, diry); }
void game::construction_menu() { if (u.morale_level() < MIN_MORALE_CRAFT) { // See morale.h add_msg("Your morale is too low to construct..."); return; } WINDOW *w_con = newwin(25, 80, 0, 0); wborder(w_con, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); mvwprintz(w_con, 0, 1, c_yellow, "Construction"); mvwprintz(w_con, 0, 31, c_yellow, "Your carpentry skill: %d.%s%d", u.sklevel[sk_carpentry], ((u.skexercise[sk_carpentry] < 10 || u.skexercise[sk_carpentry] == 0) ? "0" : ""), (u.skexercise[sk_carpentry] < 0 ? 0 : u.skexercise[sk_carpentry])); mvwputch(w_con, 0, 30, c_white, LINE_OXXX); mvwputch(w_con, 24, 30, c_white, LINE_XXOX); for (int i = 1; i < 24; i++) mvwputch(w_con, i, 30, c_white, LINE_XOXO); wrefresh(w_con); bool update_info = true; int select = 0; char ch; inventory total_inv; total_inv.form_from_map(this, point(u.posx, u.posy), PICKUP_RANGE); total_inv.add_stack(u.inv_dump()); if (u.has_bionic(bio_tools)) { item tools(itypes[itm_toolset], turn); tools.charges = u.power_level; total_inv += tools; } do { // Erase existing list of constructions for (int i = 1; i < 24; i++) { for (int j = 1; j < 29; j++) mvwputch(w_con, i, j, c_black, 'x'); } // Determine where in the master list to start printing int offset = select - 11; // if (offset > constructions.size() - 22) // acts weird with this // offset = constructions.size() - 22; if (offset <= 0) { offset = 0; mvwputch(w_con, 1, 0, c_dkgray, LINE_XOXO); } else mvwputch(w_con, 1, 0, h_white, '^'); if (constructions.size() - offset > 23) mvwputch(w_con, 23, 0, h_white, 'v'); else mvwputch(w_con, 23, 0, c_dkgray, LINE_XOXO); // Print the constructions between offset and max (or how many will fit) for (int i = 0; i < 23 && i + offset < constructions.size(); i++) { int current = i + offset; nc_color col = (player_can_build(u, total_inv, constructions[current]) ? c_white : c_dkgray); if (current == select) col = hilite(col); mvwprintz(w_con, 1 + i, 1, col, constructions[current]->name.c_str()); } if (update_info) { update_info = false; constructable* current_con = constructions[select]; // Clear out lines for tools & materials int posx = 31, posy = 0; for (int i = posy + 1; i < 24; i++) { for (int j = posx; j < 79; j++) mvwputch(w_con, i, j, c_black, 'x'); } // Print stages and their requirements for (int n = 0; n < current_con->stages.size(); n++) { posx = 31; posy++; nc_color color_stage = (player_can_build(u, total_inv, current_con, n, true) ? c_white : c_dkgray); // Print stage number and resulting terrain type posy++; if (current_con->stages[n].terrain == t_null) mvwprintz(w_con, posy, posx, color_stage, "Stage %d", n + 1); else mvwprintz(w_con, posy, posx, color_stage, "Stage %d: %s", n + 1, terlist[current_con->stages[n].terrain].name.c_str()); // Print difficulty if (current_con->stages[n].difficulty > 0){ posy++; int pskill = u.sklevel[sk_carpentry], diff = current_con->stages[n].difficulty; mvwprintz(w_con, posy, posx, color_stage, "Difficulty:"); mvwprintz(w_con, posy, posx + 12, (pskill >= diff ? c_green : c_red), "%d", diff); } // Print time (in minutes) posy++; mvwprintz(w_con, posy, posx, color_stage, "Time to complete: %d minutes", current_con->stages[n].time); // Print tools construction_stage stage = current_con->stages[n]; bool has_tool[3] = {stage.tools[0].empty(), stage.tools[1].empty(), stage.tools[2].empty()}; for (int i = 0; i < 3 && !has_tool[i]; i++) { posy++; posx = 33; mvwputch(w_con, posy, posx - 2, color_stage, '>'); for (int j = 0; j < stage.tools[i].size(); j++) { itype_id tool = stage.tools[i][j]; nc_color col = c_red; if (total_inv.has_amount(tool, 1)) { has_tool[i] = true; col = c_green; } int length = itypes[tool]->name.length(); if (posx + length > 79) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, col, itypes[tool]->name.c_str()); posx += length + 1; // + 1 for an empty space if (j < stage.tools[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, "OR"); posx += 3; } } } // Print components bool has_component[3] = {stage.components[0].empty(), stage.components[1].empty(), stage.components[2].empty()}; for (int i = 0; i < 3; i++) { posx = 33; while (has_component[i]) i++; for (int j = 0; j < stage.components[i].size() && i < 3; j++) { nc_color col = c_red; component comp = stage.components[i][j]; if (( itypes[comp.type]->is_ammo() && total_inv.has_charges(comp.type, comp.count)) || (!itypes[comp.type]->is_ammo() && total_inv.has_amount(comp.type, comp.count))) { has_component[i] = true; col = c_green; } int length = itypes[comp.type]->name.length(); if (posx + length > 79) { posy++; posx = 33; } posy++; mvwputch(w_con, posy, posx - 2, color_stage, '>'); mvwprintz(w_con, posy, posx, col, "%dx %s", comp.count, itypes[comp.type]->name.c_str()); 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 < stage.components[i].size() - 1) { // "OR" if there's more if (posx > 77) { posy++; posx = 33; } mvwprintz(w_con, posy, posx, c_white, "OR"); posx += 3; } } } } wrefresh(w_con); } // Finished updating ch = input(); switch (ch) { case 'j': update_info = true; select++; if (select >= constructions.size()) select = 0; break; case 'k': update_info = true; select--; if (select < 0) select = constructions.size() - 1; break; case '\n': case 'l': if (player_can_build(u, total_inv, constructions[select])) { place_construction(constructions[select]); ch = 'q'; } else { popup("You can't build that!"); for (int i = 1; i < 24; i++) mvwputch(w_con, i, 30, c_white, LINE_XOXO); update_info = true; } break; } } while (ch != 'q' && ch != 'Q' && ch != KEY_ESCAPE); refresh_all(); }
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(); }
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, ' '); } } // 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]) && can_construct( available[current] ) ) ? c_white : c_dkgray; if (current == select) { col = hilite(col); } // print construction name with limited length. // limit(28) = 30(column len) - 2(letter + ' '). // If we run out of hotkeys, just stop assigning them. mvwprintz(w_con, 1 + i, 1, col, "%c %s", (current < hotkeys.size()) ? hotkeys[current] : ' ', 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_tools(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( size_t 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_components(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; } } } // Finished updating //Draw Scrollbar. //Doing it here lets us refresh the entire window all at once. draw_scrollbar(w_con, select, iMaxY - 2, available.size(), 1); 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 == '\n') { chosen = select; } else { // Get the index corresponding to the key pressed. chosen = hotkeys.find_first_of( ch ); if( chosen == std::string::npos ) { break; } } 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(); }