void veh_interact::do_rename(int reason) { std::string name = string_input_popup("Enter new vehicle name", 20); (veh->name = name); werase(w_stats); werase(w_grid); display_stats (); display_veh (); }
/** * Handles renaming a vehicle. * @param reason Unused. */ void veh_interact::do_rename(int reason) { std::string name = string_input_popup(_("Enter new vehicle name:"), 20); if(name.length() > 0) { (veh->name = name); } werase(w_stats); werase(w_grid); display_stats (); display_veh (); }
void zone_manager::zone_data::set_name() { const std::string new_name = string_input_popup() .title( _( "Zone name:" ) ) .width( 55 ) .text( name ) .max_length( 15 ) .query_string(); name = ( new_name.empty() ) ? _( "<no name>" ) : new_name; }
int query_int(const char *mes, ...) { va_list ap; va_start(ap, mes); const std::string text = vstring_format(mes, ap); va_end(ap); std::string raw_input = string_input_popup(text); //Note that atoi returns 0 for anything it doesn't like. return atoi(raw_input.c_str()); }
void construct::done_vehicle(game *g, point p) { std::string name = string_input_popup("Enter new vehicle name", 20); vehicle *veh = g->m.add_vehicle (g, veh_custom, p.x, p.y, 270, 0, 0); if (!veh) { debugmsg ("error constructing vehicle"); return; } veh->name = name; veh->install_part (0, 0, vp_frame_v2); }
void game::wishitem( player *p, int x, int y) { if ( p == NULL && x <= 0 ) { debugmsg("game::wishitem(): invalid parameters"); return; } int amount = 1; uimenu wmenu; wmenu.w_x = 0; wmenu.w_width = TERMX; wmenu.pad_right = ( TERMX / 2 > 40 ? TERMX - 40 : TERMX / 2 ); wmenu.return_invalid = true; wmenu.selected = uistate.wishitem_selected; wish_item_callback *cb = new wish_item_callback(); wmenu.callback = cb; for (int i = 0; i < standard_itype_ids.size(); i++) { itype *ity = item_controller->find_template(standard_itype_ids[i]); wmenu.addentry( i, true, 0, string_format(_("%s"), ity->name.c_str()) ); wmenu.entries[i].extratxt.txt = string_format("%c", ity->sym); wmenu.entries[i].extratxt.color = ity->color; wmenu.entries[i].extratxt.left = 1; } do { wmenu.query(); if ( wmenu.ret >= 0 ) { item granted = item_controller->create(standard_itype_ids[wmenu.ret], turn); if (p != NULL) { amount = helper::to_int( string_input_popup(_("How many?"), 20, helper::to_string_int( amount ), granted.tname())); } if (dynamic_cast<wish_item_callback *>(wmenu.callback)->incontainer) { granted = granted.in_its_container(&itypes); } if ( p != NULL ) { for (int i = 0; i < amount; i++) { p->i_add(granted); } } else if ( x >= 0 && y >= 0 ) { m.add_item_or_charges(x, y, granted); wmenu.keypress = 'q'; } dynamic_cast<wish_item_callback *>(wmenu.callback)->msg = _("Item granted, choose another or 'q' to quit."); uistate.wishitem_selected = wmenu.ret; } } while ( wmenu.keypress != 'q' && wmenu.keypress != KEY_ESCAPE && wmenu.keypress != ' ' ); delete wmenu.callback; wmenu.callback = NULL; return; }
int query_int(const char *mes, ...) { va_list ap; va_start(ap, mes); char buff[1024]; vsprintf(buff, mes, ap); va_end(ap); std::string raw_input = string_input_popup(std::string(buff)); //Note that atoi returns 0 for anything it doesn't like. return atoi(raw_input.c_str()); }
/** * Handles renaming a vehicle. * @param reason Unused. */ void veh_interact::do_rename(task_reason reason) { std::string name = string_input_popup(_("Enter new vehicle name:"), 20); if(name.length() > 0) { (veh->name = name); if (veh->tracking_on) { g->cur_om->vehicles[veh->om_id].name = name; } } werase(w_stats); werase(w_grid); display_stats (); display_veh (); }
bool key( const input_context &, const input_event &event, int /*entnum*/, uimenu * /*menu*/ ) override { if( event.get_first_input() == 'f' ) { incontainer = !incontainer; return true; } if( event.get_first_input() == 'F' ) { flag = string_input_popup() .title( _( "Add which flag? Use UPPERCASE letters without quotes" ) ) .query_string(); if( flag.length() > 0 ) { has_flag = true; } return true; } return false; }
void construct::done_vehicle(game *g, point p) { std::string name = string_input_popup(_("Enter new vehicle name:"), 20); if(name.empty()) { name = _("Car"); } vehicle *veh = g->m.add_vehicle (g, "custom", p.x, p.y, 270, 0, 0); if (!veh) { debugmsg ("error constructing vehicle"); return; } veh->name = name; //Update the vehicle cache immediately, or the vehicle will be invisible for the first couple of turns. g->m.update_vehicle_cache(veh, true); }
void construct::done_vehicle( const tripoint &p ) { std::string name = string_input_popup( _( "Enter new vehicle name:" ), 20 ); if( name.empty() ) { name = _( "Car" ); } vehicle *veh = g->m.add_vehicle( vproto_id( "none" ), p, 270, 0, 0 ); if( !veh ) { debugmsg( "error constructing vehicle" ); return; } veh->name = name; veh->install_part( 0, 0, vpart_from_item( g->u.lastconsumed ) ); // Update the vehicle cache immediately, // or the vehicle will be invisible for the first couple of turns. g->m.add_vehicle_to_cache( veh ); }
void show_auto_pickup() { save_reset_changes(false); const int iHeaderHeight = 4; const int iContentHeight = FULL_SCREEN_HEIGHT-2-iHeaderHeight; const int iOffsetX = (TERMX > FULL_SCREEN_WIDTH) ? (TERMX-FULL_SCREEN_WIDTH)/2 : 0; const int iOffsetY = (TERMY > FULL_SCREEN_HEIGHT) ? (TERMY-FULL_SCREEN_HEIGHT)/2 : 0; std::map<int, bool> mapLines; mapLines[3] = true; mapLines[50] = true; mapLines[54] = true; const int iTotalCols = mapLines.size()-1; WINDOW* w_auto_pickup_options = newwin(FULL_SCREEN_HEIGHT/2, FULL_SCREEN_WIDTH/2, iOffsetY + (FULL_SCREEN_HEIGHT/2)/2, iOffsetX + (FULL_SCREEN_WIDTH/2)/2); WINDOW* w_auto_pickup_help = newwin((FULL_SCREEN_HEIGHT/2)-2, FULL_SCREEN_WIDTH * 3/4, 8 + iOffsetY + (FULL_SCREEN_HEIGHT/2)/2, iOffsetX + 19/2); WINDOW* w_auto_pickup_border = newwin(FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, iOffsetY, iOffsetX); WINDOW* w_auto_pickup_header = newwin(iHeaderHeight, FULL_SCREEN_WIDTH - 2, 1 + iOffsetY, 1 + iOffsetX); WINDOW* w_auto_pickup = newwin(iContentHeight, FULL_SCREEN_WIDTH - 2, iHeaderHeight + 1 + iOffsetY, 1 + iOffsetX); wborder(w_auto_pickup_border, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX); mvwputch(w_auto_pickup_border, 3, 0, c_ltgray, LINE_XXXO); // |- mvwputch(w_auto_pickup_border, 3, 79, c_ltgray, LINE_XOXX); // -| for (std::map<int, bool>::iterator iter = mapLines.begin(); iter != mapLines.end(); ++iter) { mvwputch(w_auto_pickup_border, FULL_SCREEN_HEIGHT-1, iter->first + 1, c_ltgray, LINE_XXOX); // _|_ } mvwprintz(w_auto_pickup_border, 0, 29, c_ltred, _(" AUTO PICKUP MANAGER ")); wrefresh(w_auto_pickup_border); int tmpx = 0; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<A>dd"))+2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<R>emove"))+2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<C>opy"))+2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<M>ove"))+2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<E>nable"))+2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<D>isable"))+2; shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<T>est")); tmpx = 0; tmpx += shortcut_print(w_auto_pickup_header, 1, tmpx, c_white, c_ltgreen, _("<+-> Move up/down"))+2; tmpx += shortcut_print(w_auto_pickup_header, 1, tmpx, c_white, c_ltgreen, _("<Enter>-Edit"))+2; shortcut_print(w_auto_pickup_header, 1, tmpx, c_white, c_ltgreen, _("<Tab>-Switch Page")); for (int i = 0; i < 78; i++) { if (mapLines[i]) { mvwputch(w_auto_pickup_header, 2, i, c_ltgray, LINE_OXXX); mvwputch(w_auto_pickup_header, 3, i, c_ltgray, LINE_XOXO); } else { mvwputch(w_auto_pickup_header, 2, i, c_ltgray, LINE_OXOX); // Draw line under header } } mvwprintz(w_auto_pickup_header, 3, 0, c_white, "#"); mvwprintz(w_auto_pickup_header, 3, 7, c_white, _("Rules")); mvwprintz(w_auto_pickup_header, 3, 51, c_white, _("I/E")); wrefresh(w_auto_pickup_header); int iCurrentPage = 1; int iCurrentLine = 0; int iCurrentCol = 1; int iStartPos = 0; bool bStuffChanged = false; char ch = ' '; std::stringstream sTemp; do { int locx = 17; locx += shortcut_print(w_auto_pickup_header, 2, locx, c_white, (iCurrentPage == 1) ? hilite(c_white) : c_white, _("[<Global>]"))+1; shortcut_print(w_auto_pickup_header, 2, locx, c_white, (iCurrentPage == 2) ? hilite(c_white) : c_white, _("[<Character>]")); wrefresh(w_auto_pickup_header); // Clear the lines for (int i = 0; i < iContentHeight; i++) { for (int j = 0; j < 79; j++) { if (mapLines[j]) { mvwputch(w_auto_pickup, i, j, c_ltgray, LINE_XOXO); } else { mvwputch(w_auto_pickup, i, j, c_black, ' '); } } } if (iCurrentPage == 1 || iCurrentPage == 2) { if (iCurrentPage == 2 && g->u.name == "") { vAutoPickupRules[2].clear(); mvwprintz(w_auto_pickup, 8, 15, c_white, _("Please load a character first to use this page!")); } //Draw Scrollbar draw_scrollbar(w_auto_pickup_border, iCurrentLine, iContentHeight, vAutoPickupRules[iCurrentPage].size(), 5); calcStartPos(iStartPos, iCurrentLine, iContentHeight, vAutoPickupRules[iCurrentPage].size()); // display auto pickup for (int i = iStartPos; i < vAutoPickupRules[iCurrentPage].size(); i++) { if (i >= iStartPos && i < iStartPos + ((iContentHeight > vAutoPickupRules[iCurrentPage].size()) ? vAutoPickupRules[iCurrentPage].size() : iContentHeight)) { nc_color cLineColor = (vAutoPickupRules[iCurrentPage][i].bActive) ? c_white : c_ltgray; sTemp.str(""); sTemp << i + 1; mvwprintz(w_auto_pickup, i - iStartPos, 0, cLineColor, sTemp.str().c_str()); mvwprintz(w_auto_pickup, i - iStartPos, 4, cLineColor, ""); if (iCurrentLine == i) { wprintz(w_auto_pickup, c_yellow, ">> "); } else { wprintz(w_auto_pickup, c_yellow, " "); } wprintz(w_auto_pickup, (iCurrentLine == i && iCurrentCol == 1) ? hilite(cLineColor) : cLineColor, "%s", ((vAutoPickupRules[iCurrentPage][i].sRule == "") ? _("<empty rule>") : vAutoPickupRules[iCurrentPage][i].sRule).c_str()); mvwprintz(w_auto_pickup, i - iStartPos, 52, (iCurrentLine == i && iCurrentCol == 2) ? hilite(cLineColor) : cLineColor, "%s", ((vAutoPickupRules[iCurrentPage][i].bExclude) ? rm_prefix(_("<Exclude>E")).c_str() : rm_prefix(_("<Include>I")).c_str())); } } wrefresh(w_auto_pickup); } else if (iCurrentPage == 3) { wborder(w_auto_pickup_options, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX); mvwprintz(w_auto_pickup_options, 5, 10, c_white, _("Under construction!")); wrefresh(w_auto_pickup); wrefresh(w_auto_pickup_options); } ch = (char)input(); if (iCurrentPage == 3) { switch(ch) { case '\t': //Switch to next Page iCurrentPage++; if (iCurrentPage > 3) { iCurrentPage = 1; iCurrentLine = 0; } break; } } else if (iCurrentPage == 1 || iCurrentPage == 2) { if (iCurrentPage == 2 && g->u.name == "" && ch != '\t') { //Only allow loaded games to use the char sheet } else if (vAutoPickupRules[iCurrentPage].size() > 0 || ch == 'a' || ch == '\t') { switch(ch) { case 'j': //move down iCurrentLine++; iCurrentCol = 1; if (iCurrentLine >= vAutoPickupRules[iCurrentPage].size()) { iCurrentLine = 0; } break; case 'k': //move up iCurrentLine--; iCurrentCol = 1; if (iCurrentLine < 0) { iCurrentLine = vAutoPickupRules[iCurrentPage].size()-1; } break; case 'a': //add new rule case 'A': bStuffChanged = true; vAutoPickupRules[iCurrentPage].push_back(cPickupRules("", true, false)); iCurrentLine = vAutoPickupRules[iCurrentPage].size()-1; break; case 'r': //remove rule case 'R': bStuffChanged = true; vAutoPickupRules[iCurrentPage].erase(vAutoPickupRules[iCurrentPage].begin() + iCurrentLine); if (iCurrentLine > vAutoPickupRules[iCurrentPage].size()-1) { iCurrentLine--; } break; case 'c': //copy rule case 'C': bStuffChanged = true; vAutoPickupRules[iCurrentPage].push_back(cPickupRules(vAutoPickupRules[iCurrentPage][iCurrentLine].sRule, vAutoPickupRules[iCurrentPage][iCurrentLine].bActive, vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude)); iCurrentLine = vAutoPickupRules[iCurrentPage].size()-1; break; case 'm': //move rule global <-> character case 'M': if ((iCurrentPage == 1 && g->u.name != "") || iCurrentPage == 2) { bStuffChanged = true; //copy over vAutoPickupRules[(iCurrentPage == 1) ? 2 : 1].push_back(cPickupRules(vAutoPickupRules[iCurrentPage][iCurrentLine].sRule, vAutoPickupRules[iCurrentPage][iCurrentLine].bActive, vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude)); //remove old vAutoPickupRules[iCurrentPage].erase(vAutoPickupRules[iCurrentPage].begin() + iCurrentLine); iCurrentLine = vAutoPickupRules[(iCurrentPage == 1) ? 2 : 1].size()-1; iCurrentPage = (iCurrentPage == 1) ? 2 : 1; } break; case '\t': //Switch to next Page iCurrentPage++; if (iCurrentPage > 2) { iCurrentPage = 1; iCurrentLine = 0; } break; case '\n': //Edit Col in current line bStuffChanged = true; if (iCurrentCol == 1) { fold_and_print(w_auto_pickup_help, 1, 1, 999, c_white, _( "* is used as a Wildcard. A few Examples:\n" "\n" "wood arrow matches the itemname exactly\n" "wood ar* matches items beginning with wood ar\n" "*rrow matches items ending with rrow\n" "*avy fle*fi*arrow multible * are allowed\n" "heAVY*woOD*arrOW case insesitive search\n" "") ); wborder(w_auto_pickup_help, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX); wrefresh(w_auto_pickup_help); vAutoPickupRules[iCurrentPage][iCurrentLine].sRule = trim_rule(string_input_popup(_("Pickup Rule:"), 30, vAutoPickupRules[iCurrentPage][iCurrentLine].sRule)); } else if (iCurrentCol == 2) { vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude = !vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude; } break; case 'e': //enable rule case 'E': bStuffChanged = true; vAutoPickupRules[iCurrentPage][iCurrentLine].bActive = true; break; case 'd': //disable rule case 'D': bStuffChanged = true; vAutoPickupRules[iCurrentPage][iCurrentLine].bActive = false; break; case 'h': //move left iCurrentCol--; if (iCurrentCol < 1) { iCurrentCol = iTotalCols; } break; case 'l': //move right iCurrentCol++; if (iCurrentCol > iTotalCols) { iCurrentCol = 1; } break; case '+': //move rule up bStuffChanged = true; if (iCurrentLine < vAutoPickupRules[iCurrentPage].size()-1) { std::swap(vAutoPickupRules[iCurrentPage][iCurrentLine], vAutoPickupRules[iCurrentPage][iCurrentLine+1]); iCurrentLine++; iCurrentCol = 1; } break; case '-': //move rule down bStuffChanged = true; if (iCurrentLine > 0) { std::swap(vAutoPickupRules[iCurrentPage][iCurrentLine], vAutoPickupRules[iCurrentPage][iCurrentLine-1]); iCurrentLine--; iCurrentCol = 1; } break; case 't': //test rule case 'T': test_pattern(iCurrentPage, iCurrentLine); break; } } } } while(ch != 'q' && ch != 'Q' && ch != KEY_ESCAPE); if (bStuffChanged) { if(query_yn(_("Save changes?"))) { save_auto_pickup(false); if (g->u.name != "") { save_auto_pickup(true); } } else { save_reset_changes(true); } } werase(w_auto_pickup); werase(w_auto_pickup_border); werase(w_auto_pickup_header); werase(w_auto_pickup_options); werase(w_auto_pickup_help); }
int main() { init_environment(); init_data(); init_display(); cuss::interface i_editor; Window w_editor(0, 0, 80, 24); if (!i_editor.load_from_file("cuss/i_terrain.cuss")) { debugmsg("Couldn't load cuss/i_terrain.cuss!"); end_display(); return 1; } std::vector<std::string> type_names; std::vector<std::string> flags; std::vector<std::string> transformations; i_editor.ref_data("list_types", &type_names); i_editor.ref_data("list_flags", &flags); i_editor.ref_data("list_transformations", &transformations); i_editor.select("list_types"); for (int i = 0; i < TER_MAX; i++) { if (i >= TERRAIN_POOL.size() || TERRAIN_POOL[i] == NULL) { TERRAIN_POOL.push_back( new terrain_type ); TERRAIN_POOL[i]->name = default_terrain_name( terrain_id(i) ); } } // Main loop bool quit = false; do { type_names = get_names(); cuss::element* selected = i_editor.selected(); int ter_num = i_editor.get_int("list_types"); terrain_type* current_ter = NULL; if (ter_num < TERRAIN_POOL.size()) { current_ter = TERRAIN_POOL[ i_editor.get_int("list_types") ]; flags = get_flag_names(current_ter); transformations = get_transformation_names(current_ter); i_editor.ref_data("num_move", &(current_ter->move_cost)); i_editor.ref_data("num_sight", &(current_ter->sight_cost)); i_editor.ref_data("text_name", &(current_ter->name)); } i_editor.draw(&w_editor); long ch = getch(); if (selected->name == "text_name" && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ' ')) { current_ter->name += ch; } else if (selected->name == "text_name" && is_backspace(ch) && !current_ter->name.empty()) { current_ter->name = current_ter->name.substr(0, current_ter->name.length() - 1); } else if (ch == 's' || ch == 'S') { quit = true; } else if (ch == 'c' || ch == 'C') { set_symbol(current_ter); } else if (ch == 'a' || ch == 'A') { if (selected->name == "list_types") { terrain_type *tmp = new terrain_type; tmp->move_cost = 10; tmp->name = string_input_popup("Name:"); set_symbol(tmp); TERRAIN_POOL.push_back(tmp); i_editor.set_data("list_types", 999); } else if (selected->name == "list_flags") { add_flag(current_ter); i_editor.set_data("list_flags", 0); } else if (selected->name == "list_transformations") { add_transformation(current_ter); i_editor.set_data("list_transformations", 0); } } else if (ch == 'd' || ch == 'D') { if (selected->name == "list_flags") { int index = i_editor.get_int("list_flags"); if (index < flags.size()) { remove_flag(current_ter, index); i_editor.set_data("list_flags", 0); } } else if (selected->name == "list_transformations") { int index = i_editor.get_int("list_transformations"); if (index < transformations.size()) { remove_transformation(current_ter, index); i_editor.set_data("list_transformations", 0); } } } else if (ch == '?') { debugmsg("%d of %d", i_editor.get_int("list_transformations"), transformations.size()); debugmsg("%d (\\n = %d", TERRAIN_POOL[1]->name[0], '\n'); } else { i_editor.handle_action(ch); } } while (!quit); save_data(); end_display(); return 0; }
void debug_menu::wishitem( player *p, int x, int y, int z ) { if( p == NULL && x <= 0 ) { debugmsg( "game::wishitem(): invalid parameters" ); return; } const auto opts = item_controller->all(); int prev_amount, amount = 1; uimenu wmenu; wmenu.w_x = 0; wmenu.w_width = TERMX; wmenu.pad_right = ( TERMX / 2 > 40 ? TERMX - 40 : TERMX / 2 ); wmenu.return_invalid = true; wmenu.selected = uistate.wishitem_selected; wish_item_callback cb( opts ); wmenu.callback = &cb; for( size_t i = 0; i < opts.size(); i++ ) { item ity( opts[i], 0 ); wmenu.addentry( i, true, 0, string_format( _( "%.*s" ), wmenu.pad_right - 5, ity.tname( 1, false ).c_str() ) ); wmenu.entries[i].extratxt.txt = ity.symbol(); wmenu.entries[i].extratxt.color = ity.color(); wmenu.entries[i].extratxt.left = 1; } do { wmenu.query(); if( wmenu.ret >= 0 ) { item granted( opts[wmenu.ret] ); prev_amount = amount; if( p != NULL ) { string_input_popup() .title( _( "How many?" ) ) .width( 20 ) .description( granted.tname() ) .edit( amount ); } if( dynamic_cast<wish_item_callback *>( wmenu.callback )->incontainer ) { granted = granted.in_its_container(); } if( p != NULL ) { for( int i = 0; i < amount; i++ ) { p->i_add( granted ); } p->invalidate_crafting_inventory(); } else if( x >= 0 && y >= 0 ) { g->m.add_item_or_charges( tripoint( x, y, z ), granted ); wmenu.keypress = 'q'; } if( amount > 0 ) { dynamic_cast<wish_item_callback *>( wmenu.callback )->msg = _( "Wish granted. Wish for more or hit 'q' to quit." ); } uistate.wishitem_selected = wmenu.ret; if( !amount ) { amount = prev_amount; } } } while( wmenu.keypress != 'q' && wmenu.keypress != KEY_ESCAPE && wmenu.keypress != ' ' ); }
// Pick up items at (pos). void Pickup::pick_up( const tripoint &pos, int min ) { int veh_root_part = 0; int cargo_part = -1; vehicle *veh = g->m.veh_at( pos, veh_root_part ); bool from_vehicle = false; if( min != -1 ) { switch( interact_with_vehicle( veh, pos, veh_root_part ) ) { case DONE: return; case ITEMS_FROM_CARGO: cargo_part = veh->part_with_feature( veh_root_part, "CARGO", false ); from_vehicle = cargo_part >= 0; break; case ITEMS_FROM_GROUND: // Nothing to change, default is to pick from ground anyway. if( g->m.has_flag( "SEALED", pos ) ) { return; } break; } } if( !from_vehicle ) { bool isEmpty = ( g->m.i_at( pos ).empty() ); // Hide the pickup window if this is a toilet and there's nothing here // but water. if( ( !isEmpty ) && g->m.furn( pos ) == f_toilet ) { isEmpty = true; for( auto maybe_water : g->m.i_at( pos ) ) { if( maybe_water.typeId() != "water" ) { isEmpty = false; break; } } } if( isEmpty && ( min != -1 || !OPTIONS["AUTO_PICKUP_ADJACENT"] ) ) { return; } } // which items are we grabbing? std::vector<item> here; if( from_vehicle ) { auto vehitems = veh->get_items( cargo_part ); here.resize( vehitems.size() ); std::copy( vehitems.rbegin(), vehitems.rend(), here.begin() ); } else { auto mapitems = g->m.i_at( pos ); here.resize( mapitems.size() ); std::copy( mapitems.rbegin(), mapitems.rend(), here.begin() ); } if( min == -1 ) { if( g->check_zone( "NO_AUTO_PICKUP", pos ) ) { here.clear(); } // Recursively pick up adjacent items if that option is on. if( OPTIONS["AUTO_PICKUP_ADJACENT"] && g->u.pos() == pos ) { //Autopickup adjacent direction adjacentDir[8] = {NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST}; for( auto &elem : adjacentDir ) { tripoint apos = tripoint( direction_XY( elem ), 0 ); apos += pos; if( g->m.has_flag( "SEALED", apos ) ) { continue; } if( g->check_zone( "NO_AUTO_PICKUP", apos ) ) { continue; } pick_up( apos, min ); } } } // Not many items, just grab them if( ( int )here.size() <= min && min != -1 ) { g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); // Only one item means index is 0. g->u.activity.values.push_back( 0 ); // auto-pickup means pick up all. g->u.activity.values.push_back( 0 ); return; } if( min != -1 ) { // don't bother if we're just autopickup-ing g->temp_exit_fullscreen(); } bool sideStyle = use_narrow_sidebar(); // Otherwise, we have Autopickup, 2 or more items and should list them, etc. int maxmaxitems = sideStyle ? TERMY : getmaxy( g->w_messages ) - 3; int itemsH = std::min( 25, TERMY / 2 ); int pickupBorderRows = 3; // The pickup list may consume the entire terminal, minus space needed for its // header/footer and the item info window. int minleftover = itemsH + pickupBorderRows; if( maxmaxitems > TERMY - minleftover ) { maxmaxitems = TERMY - minleftover; } const int minmaxitems = sideStyle ? 6 : 9; std::vector<pickup_count> getitem( here.size() ); int maxitems = here.size(); maxitems = ( maxitems < minmaxitems ? minmaxitems : ( maxitems > maxmaxitems ? maxmaxitems : maxitems ) ); int itemcount = 0; if( min == -1 ) { //Auto Pickup, select matching items if( !select_autopickup_items( here, getitem ) ) { // If we didn't find anything, bail out now. return; } } else { int pickupH = maxitems + pickupBorderRows; int pickupW = getmaxx( g->w_messages ); int pickupY = VIEW_OFFSET_Y; int pickupX = getbegx( g->w_messages ); int itemsW = pickupW; int itemsY = sideStyle ? pickupY + pickupH : TERMY - itemsH; int itemsX = pickupX; WINDOW *w_pickup = newwin( pickupH, pickupW, pickupY, pickupX ); WINDOW *w_item_info = newwin( itemsH, itemsW, itemsY, itemsX ); WINDOW_PTR w_pickupptr( w_pickup ); WINDOW_PTR w_item_infoptr( w_item_info ); std::string action; long raw_input_char = ' '; input_context ctxt( "PICKUP" ); ctxt.register_action( "UP" ); ctxt.register_action( "DOWN" ); ctxt.register_action( "RIGHT" ); ctxt.register_action( "LEFT" ); ctxt.register_action( "NEXT_TAB", _( "Next page" ) ); ctxt.register_action( "PREV_TAB", _( "Previous page" ) ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "SELECT_ALL" ); ctxt.register_action( "QUIT", _( "Cancel" ) ); ctxt.register_action( "ANY_INPUT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); int start = 0, cur_it; player pl_copy = g->u; pl_copy.set_fake( true ); bool update = true; mvwprintw( w_pickup, 0, 0, _( "PICK UP" ) ); int selected = 0; int iScrollPos = 0; if( g->was_fullscreen ) { g->draw_ter(); } // Now print the two lists; those on the ground and about to be added to inv // Continue until we hit return or space do { const std::string pickup_chars = ctxt.get_available_single_char_hotkeys( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;" ); int idx = -1; for( int i = 1; i < pickupH; i++ ) { mvwprintw( w_pickup, i, 0, " " ); } if( action == "ANY_INPUT" && raw_input_char >= '0' && raw_input_char <= '9' ) { int raw_input_char_value = ( char )raw_input_char - '0'; itemcount *= 10; itemcount += raw_input_char_value; if( itemcount < 0 ) { itemcount = 0; } } else if( action == "SCROLL_UP" ) { iScrollPos--; } else if( action == "SCROLL_DOWN" ) { iScrollPos++; } else if( action == "PREV_TAB" ) { if( start > 0 ) { start -= maxitems; } else { start = ( int )( ( here.size() - 1 ) / maxitems ) * maxitems; } selected = start; mvwprintw( w_pickup, maxitems + 2, 0, " " ); } else if( action == "NEXT_TAB" ) { if( start + maxitems < ( int )here.size() ) { start += maxitems; } else { start = 0; } iScrollPos = 0; selected = start; mvwprintw( w_pickup, maxitems + 2, pickupH, " " ); } else if( action == "UP" ) { selected--; iScrollPos = 0; if( selected < 0 ) { selected = here.size() - 1; start = ( int )( here.size() / maxitems ) * maxitems; if( start >= ( int )here.size() ) { start -= maxitems; } } else if( selected < start ) { start -= maxitems; } } else if( action == "DOWN" ) { selected++; iScrollPos = 0; if( selected >= ( int )here.size() ) { selected = 0; start = 0; } else if( selected >= start + maxitems ) { start += maxitems; } } else if( selected >= 0 && ( ( action == "RIGHT" && !getitem[selected] ) || ( action == "LEFT" && getitem[selected] ) ) ) { idx = selected; } else if( action == "ANY_INPUT" && raw_input_char == '`' ) { std::string ext = string_input_popup( _( "Enter 2 letters (case sensitive):" ), 3, "", "", "", 2 ); if( ext.size() == 2 ) { int p1 = pickup_chars.find( ext.at( 0 ) ); int p2 = pickup_chars.find( ext.at( 1 ) ); if( p1 != -1 && p2 != -1 ) { idx = pickup_chars.size() + ( p1 * pickup_chars.size() ) + p2; } } } else if( action == "ANY_INPUT" ) { idx = ( raw_input_char <= 127 ) ? pickup_chars.find( raw_input_char ) : -1; iScrollPos = 0; } if( idx >= 0 && idx < ( int )here.size() ) { if( getitem[idx] ) { if( here[idx].count_by_charges() ) { if( getitem[idx].count == 0 ) { pl_copy.inv.find_item( getitem[idx].position ).charges -= here[idx].charges; } else { pl_copy.inv.find_item( getitem[idx].position ).charges -= getitem[idx].count; } } else { unsigned stack_size = pl_copy.inv.const_stack( getitem[idx].position ).size(); pl_copy.i_rem( getitem[idx].position ); //if the stack_was emptied, removing the item invalidated later positions- fix them if( stack_size == 1 ) { for( unsigned i = 0; i < here.size(); i++ ) { if( getitem[i] && getitem[i].position > getitem[idx].position ) { getitem[i].position--; } } } } } //end if getitem[idx] if( itemcount != 0 || getitem[idx].count == 0 ) { if( itemcount >= here[idx].charges || !here[idx].count_by_charges() ) { // Ignore the count if we pickup the whole stack anyway // or something that is not counted by charges (tools) itemcount = 0; } getitem[idx].count = itemcount; itemcount = 0; } // Note: this might not change the value of getitem[idx] at all! getitem[idx].pick = ( action == "RIGHT" ? true : ( action == "LEFT" ? false : !getitem[idx] ) ); if( action != "RIGHT" && action != "LEFT" ) { selected = idx; start = ( int )( idx / maxitems ) * maxitems; } if( getitem[idx] ) { item temp = here[idx]; if( getitem[idx].count != 0 && getitem[idx].count < here[idx].charges ) { temp.charges = getitem[idx].count; } item *added = &( pl_copy.i_add( temp ) ); getitem[idx].position = pl_copy.inv.position_by_item( added ); } else { getitem[idx].count = 0; } update = true; } werase( w_item_info ); if( selected >= 0 && selected <= ( int )here.size() - 1 ) { std::vector<iteminfo> vThisItem, vDummy; here[selected].info( true, vThisItem ); draw_item_info( w_item_info, "", "", vThisItem, vDummy, iScrollPos, true, true ); } draw_custom_border( w_item_info, false ); mvwprintw( w_item_info, 0, 2, "< " ); trim_and_print( w_item_info, 0, 4, itemsW - 8, c_white, "%s >", here[selected].display_name().c_str() ); wrefresh( w_item_info ); if( action == "SELECT_ALL" ) { int count = 0; for( size_t i = 0; i < here.size(); i++ ) { if( getitem[i] ) { count++; } else { item *added = &( pl_copy.i_add( here[i] ) ); getitem[i].position = pl_copy.inv.position_by_item( added ); } getitem[i].pick = true; } if( count == ( int )here.size() ) { for( size_t i = 0; i < here.size(); i++ ) { getitem[i].pick = false; } pl_copy = g->u; pl_copy.set_fake( true ); } update = true; } for( cur_it = start; cur_it < start + maxitems; cur_it++ ) { mvwprintw( w_pickup, 1 + ( cur_it % maxitems ), 0, " " ); if( cur_it < ( int )here.size() ) { nc_color icolor = here[cur_it].color_in_inventory(); if( cur_it == selected ) { icolor = hilite( icolor ); } if( cur_it < ( int )pickup_chars.size() ) { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, char( pickup_chars[cur_it] ) ); } else if( cur_it < ( int )pickup_chars.size() + ( int )pickup_chars.size() * ( int )pickup_chars.size() ) { int p = cur_it - pickup_chars.size(); int p1 = p / pickup_chars.size(); int p2 = p % pickup_chars.size(); mvwprintz( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, "`%c%c", char( pickup_chars[p1] ), char( pickup_chars[p2] ) ); } else { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, ' ' ); } if( getitem[cur_it] ) { if( getitem[cur_it].count == 0 ) { wprintz( w_pickup, c_ltblue, " + " ); } else { wprintz( w_pickup, c_ltblue, " # " ); } } else { wprintw( w_pickup, " - " ); } std::string item_name = here[cur_it].display_name(); if( OPTIONS["ITEM_SYMBOLS"] ) { item_name = string_format( "%s %s", here[cur_it].symbol().c_str(), item_name.c_str() ); } trim_and_print( w_pickup, 1 + ( cur_it % maxitems ), 6, pickupW - 4, icolor, "%s", item_name.c_str() ); } } mvwprintw( w_pickup, maxitems + 1, 0, _( "[%s] Unmark" ), ctxt.get_desc( "LEFT", 1 ).c_str() ); center_print( w_pickup, maxitems + 1, c_ltgray, _( "[%s] Help" ), ctxt.get_desc( "HELP_KEYBINDINGS", 1 ).c_str() ); right_print( w_pickup, maxitems + 1, 0, c_ltgray, _( "[%s] Mark" ), ctxt.get_desc( "RIGHT", 1 ).c_str() ); mvwprintw( w_pickup, maxitems + 2, 0, _( "[%s] Prev" ), ctxt.get_desc( "PREV_TAB", 1 ).c_str() ); center_print( w_pickup, maxitems + 2, c_ltgray, _( "[%s] All" ), ctxt.get_desc( "SELECT_ALL", 1 ).c_str() ); right_print( w_pickup, maxitems + 2, 0, c_ltgray, _( "[%s] Next" ), ctxt.get_desc( "NEXT_TAB", 1 ).c_str() ); if( update ) { // Update weight & volume information update = false; for( int i = 9; i < pickupW; ++i ) { mvwaddch( w_pickup, 0, i, ' ' ); } mvwprintz( w_pickup, 0, 9, ( pl_copy.weight_carried() > g->u.weight_capacity() ? c_red : c_white ), _( "Wgt %.1f" ), convert_weight( pl_copy.weight_carried() ) + 0.05 ); // +0.05 to round up wprintz( w_pickup, c_white, "/%.1f", convert_weight( g->u.weight_capacity() ) ); mvwprintz( w_pickup, 0, 24, ( pl_copy.volume_carried() > g->u.volume_capacity() ? c_red : c_white ), _( "Vol %d" ), pl_copy.volume_carried() ); wprintz( w_pickup, c_white, "/%d", g->u.volume_capacity() ); } wrefresh( w_pickup ); action = ctxt.handle_input(); raw_input_char = ctxt.get_raw_input().get_first_input(); } while( action != "QUIT" && action != "CONFIRM" ); bool item_selected = false; // Check if we have selected an item. for( auto selection : getitem ) { if( selection ) { item_selected = true; } } if( action != "CONFIRM" || !item_selected ) { w_pickupptr.reset(); w_item_infoptr.reset(); add_msg( _( "Never mind." ) ); g->reenter_fullscreen(); g->refresh_all(); return; } } // At this point we've selected our items, register an activity to pick them up. g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); if( min == -1 ) { // Auto pickup will need to auto resume since there can be several of them on the stack. g->u.activity.auto_resume = true; } std::reverse( getitem.begin(), getitem.end() ); for( size_t i = 0; i < here.size(); i++ ) { if( getitem[i] ) { g->u.activity.values.push_back( i ); g->u.activity.values.push_back( getitem[i].count ); } } g->reenter_fullscreen(); }
// Pick up items at (pos). void Pickup::pick_up( const tripoint &pos, int min ) { int veh_root_part = 0; int cargo_part = -1; vehicle *veh = g->m.veh_at (pos, veh_root_part); bool from_vehicle = false; if( min != -1 ) { switch( interact_with_vehicle( veh, pos, veh_root_part ) ) { case DONE: return; case ITEMS_FROM_CARGO: cargo_part = veh->part_with_feature( veh_root_part, "CARGO", false ); from_vehicle = cargo_part >= 0; break; case ITEMS_FROM_GROUND: // Nothing to change, default is to pick from ground anyway. break; } } if (g->m.has_flag("SEALED", pos)) { return; } //min == -1 is Autopickup if (!g->u.can_pickup(min != -1)) { // no message on autopickup (-1) return; } if( !from_vehicle ) { bool isEmpty = (g->m.i_at(pos).empty()); // Hide the pickup window if this is a toilet and there's nothing here // but water. if ((!isEmpty) && g->m.furn(pos) == f_toilet) { isEmpty = true; for( auto maybe_water : g->m.i_at(pos) ) { if( maybe_water.typeId() != "water") { isEmpty = false; break; } } } if (isEmpty && (min != -1 || !OPTIONS["AUTO_PICKUP_ADJACENT"] )) { return; } } // which items are we grabbing? std::vector<item> here; if( from_vehicle ) { auto vehitems = veh->get_items(cargo_part); here.resize( vehitems.size() ); std::copy( vehitems.begin(), vehitems.end(), here.begin() ); } else { auto mapitems = g->m.i_at(pos); here.resize( mapitems.size() ); std::copy( mapitems.begin(), mapitems.end(), here.begin() ); } if (min == -1) { if( g->check_zone( "NO_AUTO_PICKUP", pos ) ) { here.clear(); } // Recursively pick up adjacent items if that option is on. if( OPTIONS["AUTO_PICKUP_ADJACENT"] && g->u.pos() == pos ) { //Autopickup adjacent direction adjacentDir[8] = {NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST}; for( auto &elem : adjacentDir ) { tripoint apos = tripoint( direction_XY( elem ), 0 ); apos += pos; if( g->m.has_flag( "SEALED", apos ) ) { continue; } if( g->check_zone( "NO_AUTO_PICKUP", apos ) ) { continue; } pick_up( apos, min ); } } } // Not many items, just grab them if ((int)here.size() <= min && min != -1) { g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); // Only one item means index is 0. g->u.activity.values.push_back( 0 ); // auto-pickup means pick up all. g->u.activity.values.push_back( 0 ); return; } if(min != -1) { // don't bother if we're just autopickup-ing g->temp_exit_fullscreen(); } bool sideStyle = use_narrow_sidebar(); // Otherwise, we have Autopickup, 2 or more items and should list them, etc. int maxmaxitems = sideStyle ? TERMY : getmaxy(g->w_messages) - 3; int itemsH = std::min(25, TERMY / 2); int pickupBorderRows = 3; // The pickup list may consume the entire terminal, minus space needed for its // header/footer and the item info window. int minleftover = itemsH + pickupBorderRows; if(maxmaxitems > TERMY - minleftover) { maxmaxitems = TERMY - minleftover; } const int minmaxitems = sideStyle ? 6 : 9; std::vector<bool> getitem; getitem.resize(here.size(), false); int maxitems = here.size(); maxitems = (maxitems < minmaxitems ? minmaxitems : (maxitems > maxmaxitems ? maxmaxitems : maxitems )); int itemcount = 0; std::map<int, unsigned int> pickup_count; // Count of how many we'll pick up from each stack if (min == -1) { //Auto Pickup, select matching items if( !select_autopickup_items( here, getitem) ) { // If we didn't find anything, bail out now. return; } } else { int pickupH = maxitems + pickupBorderRows; int pickupW = getmaxx(g->w_messages); int pickupY = VIEW_OFFSET_Y; int pickupX = getbegx(g->w_messages); int itemsW = pickupW; int itemsY = sideStyle ? pickupY + pickupH : TERMY - itemsH; int itemsX = pickupX; WINDOW *w_pickup = newwin(pickupH, pickupW, pickupY, pickupX); WINDOW *w_item_info = newwin(itemsH, itemsW, itemsY, itemsX); WINDOW_PTR w_pickupptr( w_pickup ); WINDOW_PTR w_item_infoptr( w_item_info ); int ch = ' '; int start = 0, cur_it; int new_weight = g->u.weight_carried(), new_volume = g->u.volume_carried(); bool update = true; mvwprintw(w_pickup, 0, 0, _("PICK UP")); int selected = 0; int iScrollPos = 0; if(g->was_fullscreen) { g->draw_ter(); } // Now print the two lists; those on the ground and about to be added to inv // Continue until we hit return or space do { static const std::string pickup_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;"; int idx = -1; for (int i = 1; i < pickupH; i++) { mvwprintw(w_pickup, i, 0, " "); } if (ch >= '0' && ch <= '9') { ch = (char)ch - '0'; itemcount *= 10; itemcount += ch; if( itemcount < 0 ) { itemcount = 0; } } else if ( ch == KEY_PPAGE) { iScrollPos--; } else if ( ch == KEY_NPAGE) { iScrollPos++; } else if ( ch == '<' ) { if ( start > 0 ) { start -= maxitems; } else { start = (int)( (here.size()-1) / maxitems ) * maxitems; } selected = start; mvwprintw(w_pickup, maxitems + 2, 0, " "); } else if ( ch == '>' ) { if ( start + maxitems < (int)here.size() ) { start += maxitems; } else { start = 0; } iScrollPos = 0; selected = start; mvwprintw(w_pickup, maxitems + 2, pickupH, " "); } else if ( ch == KEY_UP ) { selected--; iScrollPos = 0; if ( selected < 0 ) { selected = here.size() - 1; start = (int)( here.size() / maxitems ) * maxitems; if (start >= (int)here.size()) { start -= maxitems; } } else if ( selected < start ) { start -= maxitems; } } else if ( ch == KEY_DOWN ) { selected++; iScrollPos = 0; if ( selected >= (int)here.size() ) { selected = 0; start = 0; } else if ( selected >= start + maxitems ) { start += maxitems; } } else if ( selected >= 0 && ( ( ch == KEY_RIGHT && !getitem[selected]) || ( ch == KEY_LEFT && getitem[selected] ) ) ) { idx = selected; } else if ( ch == '`' ) { std::string ext = string_input_popup( _("Enter 2 letters (case sensitive):"), 3, "", "", "", 2); if(ext.size() == 2) { int p1 = pickup_chars.find(ext.at(0)); int p2 = pickup_chars.find(ext.at(1)); if ( p1 != -1 && p2 != -1 ) { idx = pickup_chars.size() + ( p1 * pickup_chars.size() ) + p2; } } } else { idx = ( ch <= 127 ) ? pickup_chars.find(ch) : -1; iScrollPos = 0; } if( idx >= 0 && idx < (int)here.size()) { if( getitem[idx] ) { if( pickup_count[idx] != 0 && (int)pickup_count[idx] < here[idx].charges ) { item temp = here[idx]; temp.charges = pickup_count[idx]; new_weight -= temp.weight(); new_volume -= temp.volume(); } else { new_weight -= here[idx].weight(); new_volume -= here[idx].volume(); } } if (itemcount != 0 || pickup_count[idx] == 0) { if (itemcount >= here[idx].charges || !here[idx].count_by_charges()) { // Ignore the count if we pickup the whole stack anyway // or something that is not counted by charges (tools) itemcount = 0; } pickup_count[idx] = itemcount; itemcount = 0; } // Note: this might not change the value of getitem[idx] at all! getitem[idx] = ( ch == KEY_RIGHT ? true : ( ch == KEY_LEFT ? false : !getitem[idx] ) ); if ( ch != KEY_RIGHT && ch != KEY_LEFT) { selected = idx; start = (int)( idx / maxitems ) * maxitems; } if (getitem[idx]) { if (pickup_count[idx] != 0 && (int)pickup_count[idx] < here[idx].charges) { item temp = here[idx]; temp.charges = pickup_count[idx]; new_weight += temp.weight(); new_volume += temp.volume(); } else { new_weight += here[idx].weight(); new_volume += here[idx].volume(); } } else { pickup_count[idx] = 0; } update = true; } werase(w_item_info); if ( selected >= 0 && selected <= (int)here.size() - 1 ) { std::vector<iteminfo> vThisItem, vDummy; here[selected].info(true, vThisItem); draw_item_info(w_item_info, "", "", vThisItem, vDummy, iScrollPos, true, true); } draw_custom_border(w_item_info, false); mvwprintw(w_item_info, 0, 2, "< "); trim_and_print(w_item_info, 0, 4, itemsW - 8, c_white, "%s >", here[selected].display_name().c_str()); wrefresh(w_item_info); if (ch == ',') { int count = 0; for (size_t i = 0; i < here.size(); i++) { if (getitem[i]) { count++; } else { new_weight += here[i].weight(); new_volume += here[i].volume(); } getitem[i] = true; } if (count == (int)here.size()) { for (size_t i = 0; i < here.size(); i++) { getitem[i] = false; } new_weight = g->u.weight_carried(); new_volume = g->u.volume_carried(); } update = true; } for (cur_it = start; cur_it < start + maxitems; cur_it++) { mvwprintw(w_pickup, 1 + (cur_it % maxitems), 0, " "); if (cur_it < (int)here.size()) { nc_color icolor = here[cur_it].color_in_inventory(); if (cur_it == selected) { icolor = hilite(icolor); } if (cur_it < (int)pickup_chars.size() ) { mvwputch(w_pickup, 1 + (cur_it % maxitems), 0, icolor, char(pickup_chars[cur_it])); } else { int p = cur_it - pickup_chars.size(); int p1 = p / pickup_chars.size(); int p2 = p % pickup_chars.size(); mvwprintz(w_pickup, 1 + (cur_it % maxitems), 0, icolor, "`%c%c", char(pickup_chars[p1]), char(pickup_chars[p2])); } if (getitem[cur_it]) { if (pickup_count[cur_it] == 0) { wprintz(w_pickup, c_ltblue, " + "); } else { wprintz(w_pickup, c_ltblue, " # "); } } else { wprintw(w_pickup, " - "); } std::string item_name = here[cur_it].display_name(); if (OPTIONS["ITEM_SYMBOLS"]) { item_name = string_format("%c %s", here[cur_it].symbol(), item_name.c_str()); } trim_and_print(w_pickup, 1 + (cur_it % maxitems), 6, pickupW - 4, icolor, "%s", item_name.c_str()); } } int pw = pickupW; const char *unmark = _("[left] Unmark"); const char *scroll = _("[up/dn] Scroll"); const char *mark = _("[right] Mark"); mvwprintw(w_pickup, maxitems + 1, 0, unmark); mvwprintw(w_pickup, maxitems + 1, (pw - std::strlen(scroll)) / 2, scroll); mvwprintw(w_pickup, maxitems + 1, pw - std::strlen(mark), mark); const char *prev = _("[<] Prev"); const char *all = _("[,] All"); const char *next = _("[>] Next"); mvwprintw(w_pickup, maxitems + 2, 0, prev); mvwprintw(w_pickup, maxitems + 2, (pw - std::strlen(all)) / 2, all); mvwprintw(w_pickup, maxitems + 2, pw - std::strlen(next), next); if (update) { // Update weight & volume information update = false; for (int i = 9; i < pickupW; ++i) { mvwaddch(w_pickup, 0, i, ' '); } mvwprintz(w_pickup, 0, 9, (new_weight > g->u.weight_capacity() ? c_red : c_white), _("Wgt %.1f"), g->u.convert_weight(new_weight) + 0.05); // +0.05 to round up wprintz(w_pickup, c_white, "/%.1f", g->u.convert_weight(g->u.weight_capacity())); mvwprintz(w_pickup, 0, 24, (new_volume > g->u.volume_capacity() ? c_red : c_white), _("Vol %d"), new_volume); wprintz(w_pickup, c_white, "/%d", g->u.volume_capacity()); } wrefresh(w_pickup); ch = (int)getch(); } while (ch != ' ' && ch != '\n' && ch != KEY_ENTER && ch != KEY_ESCAPE); bool item_selected = false; // Check if we have selected an item. for( auto selection : getitem ) { if( selection ) { item_selected = true; } } if( (ch != '\n' && ch != KEY_ENTER) || !item_selected ) { w_pickupptr.reset(); w_item_infoptr.reset(); add_msg(_("Never mind.")); g->reenter_fullscreen(); g->refresh_all(); return; } } // At this point we've selected our items, register an activity to pick them up. g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); if( min == -1 ) { // Auto pickup will need to auto resume since there can be several of them on the stack. g->u.activity.auto_resume = true; } for (size_t i = 0; i < here.size(); i++) { if( getitem[i] ) { g->u.activity.values.push_back( i ); g->u.activity.values.push_back( pickup_count[i] ); } } g->reenter_fullscreen(); }
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 show_auto_pickup() { save_reset_changes(false); const int iHeaderHeight = 4; const int iContentHeight = FULL_SCREEN_HEIGHT - 2 - iHeaderHeight; const int iOffsetX = (TERMX > FULL_SCREEN_WIDTH) ? (TERMX - FULL_SCREEN_WIDTH) / 2 : 0; const int iOffsetY = (TERMY > FULL_SCREEN_HEIGHT) ? (TERMY - FULL_SCREEN_HEIGHT) / 2 : 0; std::map<int, bool> mapLines; mapLines[4] = true; mapLines[50] = true; mapLines[54] = true; const int iTotalCols = mapLines.size() - 1; WINDOW *w_auto_pickup_help = newwin((FULL_SCREEN_HEIGHT / 2) - 2, FULL_SCREEN_WIDTH * 3 / 4, 7 + iOffsetY + (FULL_SCREEN_HEIGHT / 2) / 2, iOffsetX + 19 / 2); WINDOW_PTR w_auto_pickup_helpptr( w_auto_pickup_help ); WINDOW *w_auto_pickup_border = newwin(FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, iOffsetY, iOffsetX); WINDOW_PTR w_auto_pickup_borderptr( w_auto_pickup_border ); WINDOW *w_auto_pickup_header = newwin(iHeaderHeight, FULL_SCREEN_WIDTH - 2, 1 + iOffsetY, 1 + iOffsetX); WINDOW_PTR w_auto_pickup_headerptr( w_auto_pickup_header ); WINDOW *w_auto_pickup = newwin(iContentHeight, FULL_SCREEN_WIDTH - 2, iHeaderHeight + 1 + iOffsetY, 1 + iOffsetX); WINDOW_PTR w_auto_pickupptr( w_auto_pickup ); draw_border(w_auto_pickup_border); mvwputch(w_auto_pickup_border, 3, 0, c_ltgray, LINE_XXXO); // |- mvwputch(w_auto_pickup_border, 3, 79, c_ltgray, LINE_XOXX); // -| for( auto &mapLine : mapLines ) { mvwputch( w_auto_pickup_border, FULL_SCREEN_HEIGHT - 1, mapLine.first + 1, c_ltgray, LINE_XXOX ); // _|_ } mvwprintz(w_auto_pickup_border, 0, 29, c_ltred, _(" AUTO PICKUP MANAGER ")); wrefresh(w_auto_pickup_border); int tmpx = 0; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<A>dd")) + 2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<R>emove")) + 2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<C>opy")) + 2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<M>ove")) + 2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<E>nable")) + 2; tmpx += shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<D>isable")) + 2; shortcut_print(w_auto_pickup_header, 0, tmpx, c_white, c_ltgreen, _("<T>est")); tmpx = 0; tmpx += shortcut_print(w_auto_pickup_header, 1, tmpx, c_white, c_ltgreen, _("<+-> Move up/down")) + 2; tmpx += shortcut_print(w_auto_pickup_header, 1, tmpx, c_white, c_ltgreen, _("<Enter>-Edit")) + 2; shortcut_print(w_auto_pickup_header, 1, tmpx, c_white, c_ltgreen, _("<Tab>-Switch Page")); for (int i = 0; i < 78; i++) { if (mapLines[i]) { mvwputch(w_auto_pickup_header, 2, i, c_ltgray, LINE_OXXX); mvwputch(w_auto_pickup_header, 3, i, c_ltgray, LINE_XOXO); } else { mvwputch(w_auto_pickup_header, 2, i, c_ltgray, LINE_OXOX); // Draw line under header } } mvwprintz(w_auto_pickup_header, 3, 1, c_white, "#"); mvwprintz(w_auto_pickup_header, 3, 8, c_white, _("Rules")); mvwprintz(w_auto_pickup_header, 3, 51, c_white, _("I/E")); wrefresh(w_auto_pickup_header); int iCurrentPage = 1; int iCurrentLine = 0; int iCurrentCol = 1; int iStartPos = 0; bool bStuffChanged = false; input_context ctxt("AUTO_PICKUP"); ctxt.register_cardinal(); ctxt.register_action("CONFIRM"); ctxt.register_action("QUIT"); ctxt.register_action("NEXT_TAB"); ctxt.register_action("PREV_TAB"); ctxt.register_action("ADD_RULE"); ctxt.register_action("REMOVE_RULE"); ctxt.register_action("COPY_RULE"); ctxt.register_action("SWAP_RULE_GLOBAL_CHAR"); ctxt.register_action("ENABLE_RULE"); ctxt.register_action("DISABLE_RULE"); ctxt.register_action("MOVE_RULE_UP"); ctxt.register_action("MOVE_RULE_DOWN"); ctxt.register_action("TEST_RULE"); ctxt.register_action("SWITCH_AUTO_PICKUP_OPTION"); ctxt.register_action("HELP_KEYBINDINGS"); std::stringstream sTemp; while(true) { int locx = 17; locx += shortcut_print(w_auto_pickup_header, 2, locx, c_white, (iCurrentPage == 1) ? hilite(c_white) : c_white, _("[<Global>]")) + 1; shortcut_print(w_auto_pickup_header, 2, locx, c_white, (iCurrentPage == 2) ? hilite(c_white) : c_white, _("[<Character>]")); locx = 55; mvwprintz(w_auto_pickup_header, 0, locx, c_white, _("Auto pickup enabled:")); locx += shortcut_print(w_auto_pickup_header, 1, locx, ((OPTIONS["AUTO_PICKUP"]) ? c_ltgreen : c_ltred), c_white, ((OPTIONS["AUTO_PICKUP"]) ? _("True") : _("False"))); locx += shortcut_print(w_auto_pickup_header, 1, locx, c_white, c_ltgreen, " "); locx += shortcut_print(w_auto_pickup_header, 1, locx, c_white, c_ltgreen, _("<S>witch")); shortcut_print(w_auto_pickup_header, 1, locx, c_white, c_ltgreen, " "); wrefresh(w_auto_pickup_header); // Clear the lines for (int i = 0; i < iContentHeight; i++) { for (int j = 0; j < 79; j++) { if (mapLines[j]) { mvwputch(w_auto_pickup, i, j, c_ltgray, LINE_XOXO); } else { mvwputch(w_auto_pickup, i, j, c_black, ' '); } } } const bool currentPageNonEmpty = !vAutoPickupRules[iCurrentPage].empty(); if (iCurrentPage == 2 && g->u.name == "") { vAutoPickupRules[2].clear(); mvwprintz(w_auto_pickup, 8, 15, c_white, _("Please load a character first to use this page!")); } //Draw Scrollbar draw_scrollbar(w_auto_pickup_border, iCurrentLine, iContentHeight, vAutoPickupRules[iCurrentPage].size(), 5); calcStartPos(iStartPos, iCurrentLine, iContentHeight, vAutoPickupRules[iCurrentPage].size()); // display auto pickup for (int i = iStartPos; i < (int)vAutoPickupRules[iCurrentPage].size(); i++) { if (i >= iStartPos && i < iStartPos + ((iContentHeight > (int)vAutoPickupRules[iCurrentPage].size()) ? (int)vAutoPickupRules[iCurrentPage].size() : iContentHeight)) { nc_color cLineColor = (vAutoPickupRules[iCurrentPage][i].bActive) ? c_white : c_ltgray; sTemp.str(""); sTemp << i + 1; mvwprintz(w_auto_pickup, i - iStartPos, 1, cLineColor, "%s", sTemp.str().c_str()); mvwprintz(w_auto_pickup, i - iStartPos, 5, cLineColor, ""); if (iCurrentLine == i) { wprintz(w_auto_pickup, c_yellow, ">> "); } else { wprintz(w_auto_pickup, c_yellow, " "); } wprintz(w_auto_pickup, (iCurrentLine == i && iCurrentCol == 1) ? hilite(cLineColor) : cLineColor, "%s", ((vAutoPickupRules[iCurrentPage][i].sRule == "") ? _("<empty rule>") : vAutoPickupRules[iCurrentPage][i].sRule).c_str()); mvwprintz(w_auto_pickup, i - iStartPos, 52, (iCurrentLine == i && iCurrentCol == 2) ? hilite(cLineColor) : cLineColor, "%s", ((vAutoPickupRules[iCurrentPage][i].bExclude) ? rm_prefix(_("<Exclude>E")).c_str() : rm_prefix( _("<Include>I")).c_str())); } } wrefresh(w_auto_pickup); const std::string action = ctxt.handle_input(); if (action == "NEXT_TAB") { iCurrentPage++; if (iCurrentPage > 2) { iCurrentPage = 1; iCurrentLine = 0; } } else if (action == "PREV_TAB") { iCurrentPage--; if (iCurrentPage < 1) { iCurrentPage = 2; iCurrentLine = 0; } } else if (action == "QUIT") { break; } else if (iCurrentPage == 2 && g->u.name.empty()) { //Only allow loaded games to use the char sheet } else if (action == "DOWN") { iCurrentLine++; iCurrentCol = 1; if (iCurrentLine >= (int)vAutoPickupRules[iCurrentPage].size()) { iCurrentLine = 0; } } else if (action == "UP") { iCurrentLine--; iCurrentCol = 1; if (iCurrentLine < 0) { iCurrentLine = vAutoPickupRules[iCurrentPage].size() - 1; } } else if (action == "ADD_RULE") { bStuffChanged = true; vAutoPickupRules[iCurrentPage].push_back(cPickupRules("", true, false)); iCurrentLine = vAutoPickupRules[iCurrentPage].size() - 1; } else if (action == "REMOVE_RULE" && currentPageNonEmpty) { bStuffChanged = true; vAutoPickupRules[iCurrentPage].erase(vAutoPickupRules[iCurrentPage].begin() + iCurrentLine); if (iCurrentLine > (int)vAutoPickupRules[iCurrentPage].size() - 1) { iCurrentLine--; } if(iCurrentLine < 0){ iCurrentLine = 0; } } else if (action == "COPY_RULE" && currentPageNonEmpty) { bStuffChanged = true; vAutoPickupRules[iCurrentPage].push_back(cPickupRules( vAutoPickupRules[iCurrentPage][iCurrentLine].sRule, vAutoPickupRules[iCurrentPage][iCurrentLine].bActive, vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude)); iCurrentLine = vAutoPickupRules[iCurrentPage].size() - 1; } else if (action == "SWAP_RULE_GLOBAL_CHAR" && currentPageNonEmpty) { if ((iCurrentPage == 1 && g->u.name != "") || iCurrentPage == 2) { bStuffChanged = true; //copy over vAutoPickupRules[(iCurrentPage == 1) ? 2 : 1].push_back(cPickupRules( vAutoPickupRules[iCurrentPage][iCurrentLine].sRule, vAutoPickupRules[iCurrentPage][iCurrentLine].bActive, vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude)); //remove old vAutoPickupRules[iCurrentPage].erase(vAutoPickupRules[iCurrentPage].begin() + iCurrentLine); iCurrentLine = vAutoPickupRules[(iCurrentPage == 1) ? 2 : 1].size() - 1; iCurrentPage = (iCurrentPage == 1) ? 2 : 1; } } else if (action == "CONFIRM" && currentPageNonEmpty) { bStuffChanged = true; if (iCurrentCol == 1) { fold_and_print(w_auto_pickup_help, 1, 1, 999, c_white, _( "* is used as a Wildcard. A few Examples:\n" "\n" "wooden arrow matches the itemname exactly\n" "wooden ar* matches items beginning with wood ar\n" "*rrow matches items ending with rrow\n" "*avy fle*fi*arrow multiple * are allowed\n" "heAVY*woOD*arrOW case insensitive search\n" "") ); draw_border(w_auto_pickup_help); wrefresh(w_auto_pickup_help); vAutoPickupRules[iCurrentPage][iCurrentLine].sRule = trim_rule(string_input_popup(_("Pickup Rule:"), 30, vAutoPickupRules[iCurrentPage][iCurrentLine].sRule)); } else if (iCurrentCol == 2) { vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude = !vAutoPickupRules[iCurrentPage][iCurrentLine].bExclude; } } else if (action == "ENABLE_RULE" && currentPageNonEmpty) { bStuffChanged = true; vAutoPickupRules[iCurrentPage][iCurrentLine].bActive = true; } else if (action == "DISABLE_RULE" && currentPageNonEmpty) { bStuffChanged = true; vAutoPickupRules[iCurrentPage][iCurrentLine].bActive = false; } else if (action == "LEFT") { iCurrentCol--; if (iCurrentCol < 1) { iCurrentCol = iTotalCols; } } else if (action == "RIGHT") { iCurrentCol++; if (iCurrentCol > iTotalCols) { iCurrentCol = 1; } } else if (action == "MOVE_RULE_UP" && currentPageNonEmpty) { bStuffChanged = true; if (iCurrentLine < (int)vAutoPickupRules[iCurrentPage].size() - 1) { std::swap(vAutoPickupRules[iCurrentPage][iCurrentLine], vAutoPickupRules[iCurrentPage][iCurrentLine + 1]); iCurrentLine++; iCurrentCol = 1; } } else if (action == "MOVE_RULE_DOWN" && currentPageNonEmpty) { bStuffChanged = true; if (iCurrentLine > 0) { std::swap(vAutoPickupRules[iCurrentPage][iCurrentLine], vAutoPickupRules[iCurrentPage][iCurrentLine - 1]); iCurrentLine--; iCurrentCol = 1; } } else if (action == "TEST_RULE" && currentPageNonEmpty) { test_pattern(iCurrentPage, iCurrentLine); } else if (action == "SWITCH_AUTO_PICKUP_OPTION") { OPTIONS["AUTO_PICKUP"].setNext(); save_options((g->u.name != "")); } } if (bStuffChanged) { if(query_yn(_("Save changes?"))) { save_auto_pickup(false); if (g->u.name != "") { save_auto_pickup(true); } } else { save_reset_changes(true); } } }
const recipe *select_crafting_recipe( int &batch_size ) { if( normalized_names.empty() ) { translate_all(); } const int headHeight = 3; const int subHeadHeight = 2; const int freeWidth = TERMX - FULL_SCREEN_WIDTH; bool isWide = ( TERMX > FULL_SCREEN_WIDTH && freeWidth > 15 ); const int width = isWide ? ( freeWidth > FULL_SCREEN_WIDTH ? FULL_SCREEN_WIDTH * 2 : TERMX ) : FULL_SCREEN_WIDTH; const int wStart = ( TERMX - width ) / 2; const int tailHeight = isWide ? 3 : 4; const int dataLines = TERMY - ( headHeight + subHeadHeight ) - tailHeight; const int dataHalfLines = dataLines / 2; const int dataHeight = TERMY - ( headHeight + subHeadHeight ); const int infoWidth = width - FULL_SCREEN_WIDTH - 1; const recipe *last_recipe = nullptr; catacurses::window w_head = catacurses::newwin( headHeight, width, 0, wStart ); catacurses::window w_subhead = catacurses::newwin( subHeadHeight, width, 3, wStart ); catacurses::window w_data = catacurses::newwin( dataHeight, width, headHeight + subHeadHeight, wStart ); int item_info_x = infoWidth; int item_info_y = dataHeight - 3; int item_info_width = wStart + width - infoWidth; int item_info_height = headHeight + subHeadHeight; if( !isWide ) { item_info_x = 1; item_info_y = 1; item_info_width = 1; item_info_height = 1; } catacurses::window w_iteminfo = catacurses::newwin( item_info_y, item_info_x, item_info_height, item_info_width ); list_circularizer<std::string> tab( craft_cat_list ); list_circularizer<std::string> subtab( craft_subcat_list[tab.cur()] ); std::vector<const recipe *> current; std::vector<bool> available; const int componentPrintHeight = dataHeight - tailHeight - 1; //preserves component color printout between mode rotations nc_color rotated_color = c_white; int previous_item_line = -1; std::string previous_tab = ""; std::string previous_subtab = ""; item tmp; int line = 0, ypos, scroll_pos = 0; bool redraw = true; bool keepline = false; bool done = false; bool batch = false; int batch_line = 0; int display_mode = 0; const recipe *chosen = NULL; std::vector<iteminfo> thisItem, dummy; input_context ctxt( "CRAFTING" ); ctxt.register_cardinal(); ctxt.register_action( "QUIT" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "CYCLE_MODE" ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "PREV_TAB" ); ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "FILTER" ); ctxt.register_action( "RESET_FILTER" ); ctxt.register_action( "HELP_RECIPE" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "CYCLE_BATCH" ); const inventory &crafting_inv = g->u.crafting_inventory(); const std::vector<npc *> helpers = g->u.get_crafting_helpers(); std::string filterstring = ""; const auto &available_recipes = g->u.get_available_recipes( crafting_inv, &helpers ); std::map<const recipe *, bool> availability_cache; do { if( redraw ) { // When we switch tabs, redraw the header redraw = false; if( ! keepline ) { line = 0; } else { keepline = false; } if( display_mode > 2 ) { display_mode = 2; } TAB_MODE m = ( batch ) ? BATCH : ( filterstring.empty() ) ? NORMAL : FILTERED; draw_recipe_tabs( w_head, tab.cur(), m ); draw_recipe_subtabs( w_subhead, tab.cur(), subtab.cur(), available_recipes, m ); available.clear(); if( batch ) { current.clear(); for( int i = 1; i <= 20; i++ ) { current.push_back( chosen ); available.push_back( chosen->requirements().can_make_with_inventory( crafting_inv, i ) ); } } else { if( filterstring.empty() ) { current = available_recipes.in_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ); } else { auto qry = trim( filterstring ); if( qry.size() > 2 && qry[1] == ':' ) { switch( qry[0] ) { case 't': current = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::tool ); break; case 'c': current = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::component ); break; case 's': current = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::skill ); break; case 'q': current = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality ); break; case 'Q': current = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality_result ); break; case 'm': { auto &learned = g->u.get_learned_recipes(); current.clear(); if( ( qry.substr( 2 ) == "yes" ) || ( qry.substr( 2 ) == "y" ) || ( qry.substr( 2 ) == "1" ) || ( qry.substr( 2 ) == "true" ) || ( qry.substr( 2 ) == "t" ) || ( qry.substr( 2 ) == "on" ) ) { std::set_intersection( available_recipes.begin(), available_recipes.end(), learned.begin(), learned.end(), std::back_inserter( current ) ); } else { std::set_difference( available_recipes.begin(), available_recipes.end(), learned.begin(), learned.end(), std::back_inserter( current ) ); } } break; default: current.clear(); } } else { current = available_recipes.search( qry ); } } available.reserve( current.size() ); // cache recipe availability on first display for( const auto e : current ) { if( !availability_cache.count( e ) ) { availability_cache.emplace( e, e->requirements().can_make_with_inventory( crafting_inv ) ); } } std::stable_sort( current.begin(), current.end(), []( const recipe * a, const recipe * b ) { return b->difficulty < a->difficulty; } ); std::stable_sort( current.begin(), current.end(), [&]( const recipe * a, const recipe * b ) { return availability_cache[a] && !availability_cache[b]; } ); std::transform( current.begin(), current.end(), std::back_inserter( available ), [&]( const recipe * e ) { return availability_cache[e]; } ); } // current/available have been rebuilt, make sure our cursor is still in range if( current.empty() ) { line = 0; } else { line = std::min( line, ( int )current.size() - 1 ); } } // Clear the screen of recipe data, and draw it anew werase( w_data ); if( isWide ) { werase( w_iteminfo ); } if( isWide ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); wprintz( w_data, c_white, " " ); if( !filterstring.empty() ) { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } else { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [m]ode, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } } else { if( !filterstring.empty() ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, [b]atch [?] keybindings" ) ); } else { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [m]ode, [b]atch [?] keybindings" ) ); } mvwprintz( w_data, dataLines + 2, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); } // Draw borders for( int i = 1; i < width - 1; ++i ) { // _ mvwputch( w_data, dataHeight - 1, i, BORDER_COLOR, LINE_OXOX ); } for( int i = 0; i < dataHeight - 1; ++i ) { // | mvwputch( w_data, i, 0, BORDER_COLOR, LINE_XOXO ); mvwputch( w_data, i, width - 1, BORDER_COLOR, LINE_XOXO ); } mvwputch( w_data, dataHeight - 1, 0, BORDER_COLOR, LINE_XXOO ); // _| mvwputch( w_data, dataHeight - 1, width - 1, BORDER_COLOR, LINE_XOOX ); // |_ int recmin = 0, recmax = current.size(); if( recmax > dataLines ) { if( line <= recmin + dataHalfLines ) { for( int i = recmin; i < recmin + dataLines; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, i - recmin, 2, c_dark_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, i - recmin, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i - recmin, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else if( line >= recmax - dataHalfLines ) { for( int i = recmax - dataLines; i < recmax; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataLines + i - recmax, 2, c_light_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else { for( int i = line - dataHalfLines; i < line - dataHalfLines + dataLines; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataHalfLines + i - line, 2, c_light_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } } else { for( size_t i = 0; i < current.size() && i < ( size_t )dataHeight + 1; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), ( int )i + 1, tmp_name.c_str() ); } if( ( int )i == line ) { mvwprintz( w_data, i, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } if( !current.empty() ) { int pane = FULL_SCREEN_WIDTH - 30 - 1; int count = batch ? line + 1 : 1; // batch size nc_color col = available[ line ] ? c_white : c_light_gray; const auto &req = current[ line ]->requirements(); draw_can_craft_indicator( w_head, 0, *current[line] ); wrefresh( w_head ); ypos = 0; auto qry = trim( filterstring ); std::string qry_comps; if( qry.compare( 0, 2, "c:" ) == 0 ) { qry_comps = qry.substr( 2 ); } std::vector<std::string> component_print_buffer; auto tools = req.get_folded_tools_list( pane, col, crafting_inv, count ); auto comps = req.get_folded_components_list( pane, col, crafting_inv, count, qry_comps ); component_print_buffer.insert( component_print_buffer.end(), tools.begin(), tools.end() ); component_print_buffer.insert( component_print_buffer.end(), comps.begin(), comps.end() ); if( !g->u.knows_recipe( current[line] ) ) { component_print_buffer.push_back( _( "Recipe not memorized yet" ) ); auto books_with_recipe = g->u.get_books_for_recipe( crafting_inv, current[line] ); std::string enumerated_books = enumerate_as_string( books_with_recipe.begin(), books_with_recipe.end(), []( itype_id type_id ) { return item::find_type( type_id )->nname( 1 ); } ); const std::string text = string_format( _( "Written in: %s" ), enumerated_books.c_str() ); std::vector<std::string> folded_lines = foldstring( text, pane ); component_print_buffer.insert( component_print_buffer.end(), folded_lines.begin(), folded_lines.end() ); } //handle positioning of component list if it needed to be scrolled int componentPrintOffset = 0; if( display_mode > 2 ) { componentPrintOffset = ( display_mode - 2 ) * componentPrintHeight; } if( component_print_buffer.size() < static_cast<size_t>( componentPrintOffset ) ) { componentPrintOffset = 0; if( previous_tab != tab.cur() || previous_subtab != subtab.cur() || previous_item_line != line ) { display_mode = 2; } else { display_mode = 0; } } //only used to preserve mode position on components when //moving to another item and the view is already scrolled previous_tab = tab.cur(); previous_subtab = subtab.cur(); previous_item_line = line; const int xpos = 30; if( display_mode == 0 ) { const int width = getmaxx( w_data ) - xpos - item_info_x; mvwprintz( w_data, ypos++, xpos, col, _( "Skills used: %s" ), ( !current[line]->skill_used ? _( "N/A" ) : current[line]->skill_used.obj().name().c_str() ) ); ypos += fold_and_print( w_data, ypos, xpos, width, col, _( "Required skills: %s" ), current[line]->required_skills_string().c_str() ); mvwprintz( w_data, ypos++, xpos, col, _( "Difficulty: %d" ), current[ line ]->difficulty ); if( !current[line]->skill_used ) { mvwprintz( w_data, ypos++, xpos, col, _( "Your skill level: N/A" ) ); } else { mvwprintz( w_data, ypos++, xpos, col, _( "Your skill level: %d" ), g->u.get_skill_level( current[line]->skill_used ) ); } const int expected_turns = g->u.expected_time_to_craft( *current[line], count ) / MOVES( 1 ); ypos += fold_and_print( w_data, ypos, xpos, pane, col, _( "Time to complete: %s" ), to_string( time_duration::from_turns( expected_turns ) ) ); mvwprintz( w_data, ypos++, xpos, col, _( "Dark craftable? %s" ), current[line]->has_flag( "BLIND_EASY" ) ? _( "Easy" ) : current[line]->has_flag( "BLIND_HARD" ) ? _( "Hard" ) : _( "Impossible" ) ); ypos += print_items( *current[line], w_data, ypos, xpos, col, batch ? line + 1 : 1 ); } //color needs to be preserved in case part of the previous page was cut off nc_color stored_color = col; if( display_mode > 2 ) { stored_color = rotated_color; } else { rotated_color = col; } int components_printed = 0; for( size_t i = static_cast<size_t>( componentPrintOffset ); i < component_print_buffer.size(); i++ ) { if( ypos >= componentPrintHeight ) { break; } components_printed++; print_colored_text( w_data, ypos++, xpos, stored_color, col, component_print_buffer[i] ); } if( ypos >= componentPrintHeight && component_print_buffer.size() > static_cast<size_t>( components_printed ) ) { mvwprintz( w_data, ypos++, xpos, col, _( "v (more)" ) ); rotated_color = stored_color; } if( isWide ) { if( last_recipe != current[line] ) { last_recipe = current[line]; tmp = current[line]->create_result(); } tmp.info( true, thisItem, count ); draw_item_info( w_iteminfo, tmp.tname(), tmp.type_name(), thisItem, dummy, scroll_pos, true, true, true, false, true ); } } draw_scrollbar( w_data, line, dataLines, recmax, 0 ); wrefresh( w_data ); if( isWide ) { wrefresh( w_iteminfo ); } const std::string action = ctxt.handle_input(); if( action == "CYCLE_MODE" ) { display_mode = display_mode + 1; if( display_mode <= 0 ) { display_mode = 0; } } else if( action == "LEFT" ) { std::string start = subtab.cur(); do { subtab.prev(); } while( subtab.cur() != start && available_recipes.empty_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ) ); redraw = true; } else if( action == "SCROLL_UP" ) { scroll_pos--; } else if( action == "SCROLL_DOWN" ) { scroll_pos++; } else if( action == "PREV_TAB" ) { tab.prev(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "RIGHT" ) { std::string start = subtab.cur(); do { subtab.next(); } while( subtab.cur() != start && available_recipes.empty_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ) ); redraw = true; } else if( action == "NEXT_TAB" ) { tab.next(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "DOWN" ) { line++; } else if( action == "UP" ) { line--; } else if( action == "CONFIRM" ) { if( available.empty() || !available[line] ) { popup( _( "You can't do that!" ) ); } else if( !g->u.check_eligible_containers_for_crafting( *current[line], ( batch ) ? line + 1 : 1 ) ) { ; // popup is already inside check } else { chosen = current[line]; batch_size = ( batch ) ? line + 1 : 1; done = true; } } else if( action == "HELP_RECIPE" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } tmp = current[line]->create_result(); full_screen_popup( "%s\n%s", tmp.type_name( 1 ).c_str(), tmp.info( true ).c_str() ); redraw = true; keepline = true; } else if( action == "FILTER" ) { string_input_popup() .title( _( "Search:" ) ) .width( 85 ) .description( _( "Special prefixes for requirements:\n" " [t] search tools\n" " [c] search components\n" " [q] search qualities\n" " [s] search skills\n" "Special prefixes for results:\n" " [Q] search qualities\n" "Other:\n" " [m] search for memorized or not\n" "Examples:\n" " t:soldering iron\n" " c:two by four\n" " q:metal sawing\n" " s:cooking\n" " Q:fine bolt turning\n" " m:no" ) ) .edit( filterstring ); redraw = true; } else if( action == "QUIT" ) { chosen = nullptr; done = true; } else if( action == "RESET_FILTER" ) { filterstring.clear(); redraw = true; } else if( action == "CYCLE_BATCH" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } batch = !batch; if( batch ) { batch_line = line; chosen = current[batch_line]; } else { line = batch_line; keepline = true; } redraw = true; } if( line < 0 ) { line = current.size() - 1; } else if( line >= ( int )current.size() ) { line = 0; } } while( !done ); return chosen; }
const recipe *select_crafting_recipe( int &batch_size ) { if( normalized_names.empty() ) { translate_all(); } const int headHeight = 3; const int subHeadHeight = 2; const int freeWidth = TERMX - FULL_SCREEN_WIDTH; bool isWide = ( TERMX > FULL_SCREEN_WIDTH && freeWidth > 15 ); const int width = isWide ? ( freeWidth > FULL_SCREEN_WIDTH ? FULL_SCREEN_WIDTH * 2 : TERMX ) : FULL_SCREEN_WIDTH; const int wStart = ( TERMX - width ) / 2; const int tailHeight = isWide ? 3 : 4; const int dataLines = TERMY - ( headHeight + subHeadHeight ) - tailHeight; const int dataHalfLines = dataLines / 2; const int dataHeight = TERMY - ( headHeight + subHeadHeight ); const int infoWidth = width - FULL_SCREEN_WIDTH - 1; const recipe *last_recipe = nullptr; catacurses::window w_head = catacurses::newwin( headHeight, width, 0, wStart ); catacurses::window w_subhead = catacurses::newwin( subHeadHeight, width, 3, wStart ); catacurses::window w_data = catacurses::newwin( dataHeight, width, headHeight + subHeadHeight, wStart ); int item_info_x = infoWidth; int item_info_y = dataHeight - 3; int item_info_width = wStart + width - infoWidth; int item_info_height = headHeight + subHeadHeight; if( !isWide ) { item_info_x = 1; item_info_y = 1; item_info_width = 1; item_info_height = 1; } catacurses::window w_iteminfo = catacurses::newwin( item_info_y, item_info_x, item_info_height, item_info_width ); list_circularizer<std::string> tab( craft_cat_list ); list_circularizer<std::string> subtab( craft_subcat_list[tab.cur()] ); std::vector<const recipe *> current; std::vector<bool> available; const int componentPrintHeight = dataHeight - tailHeight - 1; //preserves component color printout between mode rotations nc_color rotated_color = c_white; int previous_item_line = -1; std::string previous_tab; std::string previous_subtab; item tmp; int line = 0; int ypos = 0; int scroll_pos = 0; bool redraw = true; bool keepline = false; bool done = false; bool batch = false; bool show_hidden = false; int batch_line = 0; int display_mode = 0; const recipe *chosen = nullptr; std::vector<iteminfo> thisItem; std::vector<iteminfo> dummy; input_context ctxt( "CRAFTING" ); ctxt.register_cardinal(); ctxt.register_action( "QUIT" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "CYCLE_MODE" ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "PREV_TAB" ); ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "FILTER" ); ctxt.register_action( "RESET_FILTER" ); ctxt.register_action( "TOGGLE_FAVORITE" ); ctxt.register_action( "HELP_RECIPE" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "CYCLE_BATCH" ); ctxt.register_action( "RELATED_RECIPES" ); ctxt.register_action( "HIDE_SHOW_RECIPE" ); const inventory &crafting_inv = g->u.crafting_inventory(); const std::vector<npc *> helpers = g->u.get_crafting_helpers(); std::string filterstring; const auto &available_recipes = g->u.get_available_recipes( crafting_inv, &helpers ); std::map<const recipe *, bool> availability_cache; do { if( redraw ) { // When we switch tabs, redraw the header redraw = false; if( ! keepline ) { line = 0; } else { keepline = false; } if( display_mode > 2 ) { display_mode = 2; } TAB_MODE m = ( batch ) ? BATCH : ( filterstring.empty() ) ? NORMAL : FILTERED; draw_recipe_tabs( w_head, tab.cur(), m ); draw_recipe_subtabs( w_subhead, tab.cur(), subtab.cur(), available_recipes, m ); show_hidden = false; available.clear(); if( batch ) { current.clear(); for( int i = 1; i <= 20; i++ ) { current.push_back( chosen ); available.push_back( chosen->requirements().can_make_with_inventory( crafting_inv, i ) ); } } else { std::vector<const recipe *> picking; if( !filterstring.empty() ) { auto qry = trim( filterstring ); if( qry.size() > 2 && qry[1] == ':' ) { switch( qry[0] ) { case 't': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::tool ); break; case 'c': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::component ); break; case 's': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::skill ); break; case 'p': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::primary_skill ); break; case 'Q': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality ); break; case 'q': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality_result ); break; case 'd': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::description_result ); break; case 'm': { auto &learned = g->u.get_learned_recipes(); if( query_is_yes( qry ) ) { std::set_intersection( available_recipes.begin(), available_recipes.end(), learned.begin(), learned.end(), std::back_inserter( picking ) ); } else { std::set_difference( available_recipes.begin(), available_recipes.end(), learned.begin(), learned.end(), std::back_inserter( picking ) ); } break; } case 'h': { std::copy( available_recipes.begin(), available_recipes.end(), std::back_inserter( picking ) ); if( query_is_yes( qry ) ) { show_hidden = true; } break; } default: current.clear(); } } else { picking = available_recipes.search( qry ); } } else if( subtab.cur() == "CSC_*_FAVORITE" ) { picking = available_recipes.favorite(); } else if( subtab.cur() == "CSC_*_RECENT" ) { picking = available_recipes.recent(); } else { picking = available_recipes.in_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ); } current.clear(); for( auto i : picking ) { if( ( uistate.hidden_recipes.find( i->ident() ) != uistate.hidden_recipes.end() ) == show_hidden ) { current.push_back( i ); } } if( !show_hidden ) { draw_hidden_amount( w_head, 0, picking.size() - current.size() ); } available.reserve( current.size() ); // cache recipe availability on first display for( const auto e : current ) { if( !availability_cache.count( e ) ) { availability_cache.emplace( e, e->requirements().can_make_with_inventory( crafting_inv ) ); } } if( subtab.cur() != "CSC_*_RECENT" ) { std::stable_sort( current.begin(), current.end(), []( const recipe * a, const recipe * b ) { return b->difficulty < a->difficulty; } ); std::stable_sort( current.begin(), current.end(), [&]( const recipe * a, const recipe * b ) { return availability_cache[a] && !availability_cache[b]; } ); } std::transform( current.begin(), current.end(), std::back_inserter( available ), [&]( const recipe * e ) { return availability_cache[e]; } ); } // current/available have been rebuilt, make sure our cursor is still in range if( current.empty() ) { line = 0; } else { line = std::min( line, static_cast<int>( current.size() ) - 1 ); } } // Clear the screen of recipe data, and draw it anew werase( w_data ); if( isWide ) { werase( w_iteminfo ); } if( isWide ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); wprintz( w_data, c_white, " " ); if( !filterstring.empty() ) { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } else { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } } else { if( !filterstring.empty() ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, [b]atch [?] keybindings" ) ); } else { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, [b]atch [?] keybindings" ) ); } mvwprintz( w_data, dataLines + 2, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); } // Draw borders for( int i = 1; i < width - 1; ++i ) { // _ mvwputch( w_data, dataHeight - 1, i, BORDER_COLOR, LINE_OXOX ); } for( int i = 0; i < dataHeight - 1; ++i ) { // | mvwputch( w_data, i, 0, BORDER_COLOR, LINE_XOXO ); mvwputch( w_data, i, width - 1, BORDER_COLOR, LINE_XOXO ); } mvwputch( w_data, dataHeight - 1, 0, BORDER_COLOR, LINE_XXOO ); // _| mvwputch( w_data, dataHeight - 1, width - 1, BORDER_COLOR, LINE_XOOX ); // |_ int recmin = 0, recmax = current.size(); if( recmax > dataLines ) { if( line <= recmin + dataHalfLines ) { for( int i = recmin; i < recmin + dataLines; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, i - recmin, 2, c_dark_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, i - recmin, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i - recmin, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else if( line >= recmax - dataHalfLines ) { for( int i = recmax - dataLines; i < recmax; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataLines + i - recmax, 2, c_light_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else { for( int i = line - dataHalfLines; i < line - dataHalfLines + dataLines; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataHalfLines + i - line, 2, c_light_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } } else { for( size_t i = 0; i < current.size() && i < static_cast<size_t>( dataHeight ) + 1; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), static_cast<int>( i ) + 1, tmp_name.c_str() ); } if( static_cast<int>( i ) == line ) { mvwprintz( w_data, i, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } if( !current.empty() ) { int pane = FULL_SCREEN_WIDTH - 30 - 1; int count = batch ? line + 1 : 1; // batch size nc_color col = available[ line ] ? c_white : c_light_gray; const auto &req = current[ line ]->requirements(); draw_can_craft_indicator( w_head, 0, *current[line] ); wrefresh( w_head ); ypos = 0; auto qry = trim( filterstring ); std::string qry_comps; if( qry.compare( 0, 2, "c:" ) == 0 ) { qry_comps = qry.substr( 2 ); } std::vector<std::string> component_print_buffer; auto tools = req.get_folded_tools_list( pane, col, crafting_inv, count ); auto comps = req.get_folded_components_list( pane, col, crafting_inv, count, qry_comps ); component_print_buffer.insert( component_print_buffer.end(), tools.begin(), tools.end() ); component_print_buffer.insert( component_print_buffer.end(), comps.begin(), comps.end() ); if( !g->u.knows_recipe( current[line] ) ) { component_print_buffer.push_back( _( "Recipe not memorized yet" ) ); auto books_with_recipe = g->u.get_books_for_recipe( crafting_inv, current[line] ); std::string enumerated_books = enumerate_as_string( books_with_recipe.begin(), books_with_recipe.end(), []( itype_id type_id ) { return item::find_type( type_id )->nname( 1 ); } ); const std::string text = string_format( _( "Written in: %s" ), enumerated_books.c_str() ); std::vector<std::string> folded_lines = foldstring( text, pane ); component_print_buffer.insert( component_print_buffer.end(), folded_lines.begin(), folded_lines.end() ); } //handle positioning of component list if it needed to be scrolled int componentPrintOffset = 0; if( display_mode > 2 ) { componentPrintOffset = ( display_mode - 2 ) * componentPrintHeight; } if( component_print_buffer.size() < static_cast<size_t>( componentPrintOffset ) ) { componentPrintOffset = 0; if( previous_tab != tab.cur() || previous_subtab != subtab.cur() || previous_item_line != line ) { display_mode = 2; } else { display_mode = 0; } } //only used to preserve mode position on components when //moving to another item and the view is already scrolled previous_tab = tab.cur(); previous_subtab = subtab.cur(); previous_item_line = line; const int xpos = 30; if( display_mode == 0 ) { const int width = getmaxx( w_data ) - xpos - item_info_x; print_colored_text( w_data, ypos++, xpos, col, col, string_format( _( "Primary skill used: <color_cyan>%s</color>" ), ( !current[line]->skill_used ? _( "N/A" ) : current[line]->skill_used.obj().name() ) ) ); auto player_skill = g->u.get_skill_level( current[line]->skill_used ); std::string difficulty_color = current[ line ]->difficulty > player_skill ? "yellow" : "green"; print_colored_text( w_data, ypos++, xpos, col, col, string_format( _( "Difficulty: <color_%s>%d</color>" ), difficulty_color, current[ line ]->difficulty ) ); std::string skill_level_string = current[line]->skill_used ? string_format( _( "Your skill level: <color_%s>%d</color>" ), difficulty_color, player_skill ) : _( "Your skill level: <color_yellow>N/A</color>" ); print_colored_text( w_data, ypos++, xpos, col, col, skill_level_string ); ypos += fold_and_print( w_data, ypos, xpos, width, col, _( "Other skills used: %s" ), current[line]->required_skills_string( &g->u ) ); const int expected_turns = g->u.expected_time_to_craft( *current[line], count ) / to_moves<int>( 1_turns ); ypos += fold_and_print( w_data, ypos, xpos, pane, col, _( "Time to complete: <color_cyan>%s</color>" ), to_string( time_duration::from_turns( expected_turns ) ) ); print_colored_text( w_data, ypos++, xpos, col, col, string_format( _( "Dark craftable? <color_cyan>%s</color>" ), current[line]->has_flag( "BLIND_EASY" ) ? _( "Easy" ) : current[line]->has_flag( "BLIND_HARD" ) ? _( "Hard" ) : _( "Impossible" ) ) ); ypos += print_items( *current[line], w_data, ypos, xpos, col, batch ? line + 1 : 1 ); } //color needs to be preserved in case part of the previous page was cut off nc_color stored_color = col; if( display_mode > 2 ) { stored_color = rotated_color; } else { rotated_color = col; } int components_printed = 0; for( size_t i = static_cast<size_t>( componentPrintOffset ); i < component_print_buffer.size(); i++ ) { if( ypos >= componentPrintHeight ) { break; } components_printed++; print_colored_text( w_data, ypos++, xpos, stored_color, col, component_print_buffer[i] ); } if( ypos >= componentPrintHeight && component_print_buffer.size() > static_cast<size_t>( components_printed ) ) { mvwprintz( w_data, ypos++, xpos, col, _( "v (more)" ) ); rotated_color = stored_color; } if( isWide ) { if( last_recipe != current[line] ) { last_recipe = current[line]; tmp = current[line]->create_result(); } tmp.info( true, thisItem, count ); draw_item_info( w_iteminfo, tmp.tname(), tmp.type_name(), thisItem, dummy, scroll_pos, true, true, true, false, true ); } } draw_scrollbar( w_data, line, dataLines, recmax, 0 ); wrefresh( w_data ); if( isWide ) { wrefresh( w_iteminfo ); } const std::string action = ctxt.handle_input(); if( action == "CYCLE_MODE" ) { display_mode = display_mode + 1; if( display_mode <= 0 ) { display_mode = 0; } } else if( action == "LEFT" ) { std::string start = subtab.cur(); do { subtab.prev(); } while( subtab.cur() != start && available_recipes.empty_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ) ); redraw = true; } else if( action == "SCROLL_UP" ) { scroll_pos--; } else if( action == "SCROLL_DOWN" ) { scroll_pos++; } else if( action == "PREV_TAB" ) { tab.prev(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "RIGHT" ) { std::string start = subtab.cur(); do { subtab.next(); } while( subtab.cur() != start && available_recipes.empty_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ) ); redraw = true; } else if( action == "NEXT_TAB" ) { tab.next(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "DOWN" ) { line++; } else if( action == "UP" ) { line--; } else if( action == "CONFIRM" ) { if( available.empty() || !available[line] ) { popup( _( "You can't do that!" ) ); } else if( !g->u.check_eligible_containers_for_crafting( *current[line], ( batch ) ? line + 1 : 1 ) ) { // popup is already inside check } else { chosen = current[line]; batch_size = ( batch ) ? line + 1 : 1; done = true; } } else if( action == "HELP_RECIPE" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } tmp = current[line]->create_result(); full_screen_popup( "%s\n%s", tmp.type_name( 1 ).c_str(), tmp.info( true ).c_str() ); redraw = true; keepline = true; } else if( action == "FILTER" ) { struct SearchPrefix { char key; std::string example; std::string description; }; std::vector<SearchPrefix> prefixes = { { 'q', _( "metal sawing" ), _( "<color_cyan>quality</color> of resulting item" ) }, //~ Example result description search term { 'd', _( "reach attack" ), _( "<color_cyan>full description</color> of resulting item (slow)" ) }, { 'c', _( "two by four" ), _( "<color_cyan>component</color> required to craft" ) }, { 'p', _( "tailoring" ), _( "<color_cyan>primary skill</color> used to craft" ) }, { 's', _( "cooking" ), _( "<color_cyan>any skill</color> used to craft" ) }, { 'Q', _( "fine bolt turning" ), _( "<color_cyan>quality</color> required to craft" ) }, { 't', _( "soldering iron" ), _( "<color_cyan>tool</color> required to craft" ) }, { 'h', _( "yes" ), _( "recipes which are <color_cyan>hidden</color> or not" ) }, { 'm', _( "no" ), _( "recipes which are <color_cyan>memorized</color> or not" ) }, }; int max_example_length = 0; for( const auto &prefix : prefixes ) { max_example_length = std::max( max_example_length, utf8_width( prefix.example ) ); } std::string spaces( max_example_length, ' ' ); std::string description = _( "The default is to search result names. Some single-character prefixes " "can be used with a colon (:) to search in other ways.\n" "\n" "<color_white>Examples:</color>\n" ); { std::string example_name = _( "shirt" ); auto padding = max_example_length - utf8_width( example_name ); description += string_format( _( " <color_white>%s</color>%.*s %s\n" ), example_name, padding, spaces, _( "<color_cyan>name</color> of resulting item" ) ); } for( const auto &prefix : prefixes ) { auto padding = max_example_length - utf8_width( prefix.example ); description += string_format( _( " <color_yellow>%c</color><color_white>:%s</color>%.*s %s\n" ), prefix.key, prefix.example, padding, spaces, prefix.description ); } string_input_popup() .title( _( "Search:" ) ) .width( 85 ) .description( description ) .desc_color( c_light_gray ) .edit( filterstring ); redraw = true; } else if( action == "QUIT" ) { chosen = nullptr; done = true; } else if( action == "RESET_FILTER" ) { filterstring.clear(); redraw = true; } else if( action == "CYCLE_BATCH" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } batch = !batch; if( batch ) { batch_line = line; chosen = current[batch_line]; } else { line = batch_line; keepline = true; } redraw = true; } else if( action == "TOGGLE_FAVORITE" ) { keepline = true; redraw = true; if( current.empty() ) { popup( _( "Nothing selected!" ) ); continue; } if( uistate.favorite_recipes.find( current[line]->ident() ) != uistate.favorite_recipes.end() ) { uistate.favorite_recipes.erase( current[line]->ident() ); } else { uistate.favorite_recipes.insert( current[line]->ident() ); } } else if( action == "HIDE_SHOW_RECIPE" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } if( show_hidden ) { uistate.hidden_recipes.erase( current[line]->ident() ); } else { uistate.hidden_recipes.insert( current[line]->ident() ); } redraw = true; } else if( action == "RELATED_RECIPES" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } std::string recipe_name = peek_related_recipe( current[ line ], available_recipes ); if( recipe_name.empty() ) { keepline = true; } else { filterstring = recipe_name; } redraw = true; } if( line < 0 ) { line = current.size() - 1; } else if( line >= static_cast<int>( current.size() ) ) { line = 0; } } while( !done ); return chosen; }
int editmap::edit_itm(point coords) { int ret = 0; uimenu ilmenu; ilmenu.w_x = TERRAIN_WINDOW_WIDTH + VIEW_OFFSET_X; ilmenu.w_y = 0; ilmenu.w_width = width; ilmenu.w_height = TERRAIN_WINDOW_HEIGHT - infoHeight - 1; ilmenu.return_invalid = true; std::vector<item>& items = g->m.i_at(target.x , target.y ); for(int i = 0; i < items.size(); i++) { ilmenu.addentry(i, true, 0, "%s%s", items[i].tname(g).c_str(), items[i].light.luminance > 0 ? " L" : "" ); } // todo; ilmenu.addentry(ilmenu.entries.size(), true, 'a', "Add item"); ilmenu.addentry(-10, true, 'q', "Cancel"); do { ilmenu.query(); if ( ilmenu.ret >= 0 && ilmenu.ret < items.size() ) { item *it = &items[ilmenu.ret]; uimenu imenu; imenu.w_x = ilmenu.w_x; imenu.w_y = ilmenu.w_height; imenu.w_height = TERMX - ilmenu.w_height; imenu.w_width = ilmenu.w_width; imenu.addentry(imenu_bday, true, -1, "bday: %d", (int)it->bday); imenu.addentry(imenu_damage, true, -1, "damage: %d", (int)it->damage); imenu.addentry(imenu_burnt, true, -1, "burnt: %d", (int)it->burnt); imenu.addentry(imenu_sep, false, 0, "-[ light emission ]-"); imenu.addentry(imenu_luminance, true, -1, "lum: %f", (float)it->light.luminance); imenu.addentry(imenu_direction, true, -1, "dir: %d", (int)it->light.direction); imenu.addentry(imenu_width, true, -1, "width: %d", (int)it->light.width); imenu.addentry(imenu_exit, true, -1, "exit"); do { imenu.query(); if ( imenu.ret >= 0 && imenu.ret < imenu_exit ) { int intval = -1; switch(imenu.ret) { case imenu_bday: intval = (int)it->bday; break; case imenu_damage: intval = (int)it->damage; break; case imenu_burnt: intval = (int)it->burnt; break; case imenu_luminance: intval = (int)it->light.luminance; break; case imenu_direction: intval = (int)it->light.direction; break; case imenu_width: intval = (int)it->light.width; break; } int retval = helper::to_int ( string_input_popup( "set: ", 20, helper::to_string( intval ) ) ); if ( intval != retval ) { if (imenu.ret == imenu_bday ) { it->bday = retval; imenu.entries[imenu_bday].txt = string_format("bday: %d", it->bday); } else if (imenu.ret == imenu_damage ) { it->damage = retval; imenu.entries[imenu_damage].txt = string_format("damage: %d", it->damage); } else if (imenu.ret == imenu_burnt ) { it->burnt = retval; imenu.entries[imenu_burnt].txt = string_format("burnt: %d", it->burnt); } else if (imenu.ret == imenu_luminance ) { it->light.luminance = (unsigned short)retval; imenu.entries[imenu_luminance].txt = string_format("lum: %f", (float)it->light.luminance); } else if (imenu.ret == imenu_direction ) { it->light.direction = (short)retval; imenu.entries[imenu_direction].txt = string_format("dir: %d", (int)it->light.direction); } else if (imenu.ret == imenu_width ) { it->light.width = (short)retval; imenu.entries[imenu_width].txt = string_format("width: %d", (int)it->light.width); } werase(g->w_terrain); g->draw_ter(target.x, target.y); } wrefresh(ilmenu.window); wrefresh(imenu.window); wrefresh(g->w_terrain); } } while(imenu.ret != imenu_exit); wrefresh(w_info); } } while (ilmenu.ret >= 0 || ilmenu.ret == UIMENU_INVALID); return ret; }
const recipe *select_crafting_recipe( int &batch_size ) { if( normalized_names.empty() ) { translate_all(); } const int headHeight = 3; const int subHeadHeight = 2; const int freeWidth = TERMX - FULL_SCREEN_WIDTH; bool isWide = ( TERMX > FULL_SCREEN_WIDTH && freeWidth > 15 ); const int width = isWide ? ( freeWidth > FULL_SCREEN_WIDTH ? FULL_SCREEN_WIDTH * 2 : TERMX ) : FULL_SCREEN_WIDTH; const int wStart = ( TERMX - width ) / 2; const int tailHeight = isWide ? 3 : 4; const int dataLines = TERMY - ( headHeight + subHeadHeight ) - tailHeight; const int dataHalfLines = dataLines / 2; const int dataHeight = TERMY - ( headHeight + subHeadHeight ); const int infoWidth = width - FULL_SCREEN_WIDTH - 1; const recipe *last_recipe = nullptr; WINDOW *w_head = newwin( headHeight, width, 0, wStart ); WINDOW_PTR w_head_ptr( w_head ); WINDOW *w_subhead = newwin( subHeadHeight, width, 3, wStart ); WINDOW_PTR w_subhead_ptr( w_subhead ); WINDOW *w_data = newwin( dataHeight, width, headHeight + subHeadHeight, wStart ); WINDOW_PTR w_data_ptr( w_data ); int item_info_x = infoWidth; int item_info_y = dataHeight - 3; int item_info_width = wStart + width - infoWidth; int item_info_height = headHeight + subHeadHeight; if( !isWide ) { item_info_x = 1; item_info_y = 1; item_info_width = 1; item_info_height = 1; } WINDOW *w_iteminfo = newwin( item_info_y, item_info_x, item_info_height, item_info_width ); WINDOW_PTR w_iteminfo_ptr( w_iteminfo ); list_circularizer<std::string> tab( craft_cat_list ); list_circularizer<std::string> subtab( craft_subcat_list[tab.cur()] ); std::vector<const recipe *> current; std::vector<bool> available; const int componentPrintHeight = dataHeight - tailHeight - 1; //preserves component color printout between mode rotations nc_color rotated_color = c_white; int previous_item_line = -1; std::string previous_tab = ""; std::string previous_subtab = ""; item tmp; int line = 0, ypos, scroll_pos = 0; bool redraw = true; bool keepline = false; bool done = false; bool batch = false; int batch_line = 0; int display_mode = 0; const recipe *chosen = NULL; std::vector<iteminfo> thisItem, dummy; input_context ctxt( "CRAFTING" ); ctxt.register_cardinal(); ctxt.register_action( "QUIT" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "CYCLE_MODE" ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "PREV_TAB" ); ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "FILTER" ); ctxt.register_action( "RESET_FILTER" ); ctxt.register_action( "HELP_RECIPE" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "CYCLE_BATCH" ); const inventory &crafting_inv = g->u.crafting_inventory(); const std::vector<npc *> helpers = g->u.get_crafting_helpers(); std::string filterstring = ""; do { if( redraw ) { // When we switch tabs, redraw the header redraw = false; if( ! keepline ) { line = 0; } else { keepline = false; } if( display_mode > 2 ) { display_mode = 2; } TAB_MODE m = ( batch ) ? BATCH : ( filterstring == "" ) ? NORMAL : FILTERED; draw_recipe_tabs( w_head, tab.cur(), m ); draw_recipe_subtabs( w_subhead, tab.cur(), subtab.cur(), m ); current.clear(); available.clear(); if( batch ) { batch_recipes( crafting_inv, helpers, current, available, chosen ); } else { // Set current to all recipes in the current tab; available are possible to make pick_recipes( crafting_inv, helpers, current, available, tab.cur(), subtab.cur(), filterstring ); } } // Clear the screen of recipe data, and draw it anew werase( w_data ); if( isWide ) { werase( w_iteminfo ); } if( isWide ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); wprintz( w_data, c_white, " " ); if( filterstring != "" ) { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } else { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [m]ode, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } } else { if( filterstring != "" ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, [b]atch [?] keybindings" ) ); } else { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [m]ode, [b]atch [?] keybindings" ) ); } mvwprintz( w_data, dataLines + 2, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); } // Draw borders for( int i = 1; i < width - 1; ++i ) { // _ mvwputch( w_data, dataHeight - 1, i, BORDER_COLOR, LINE_OXOX ); } for( int i = 0; i < dataHeight - 1; ++i ) { // | mvwputch( w_data, i, 0, BORDER_COLOR, LINE_XOXO ); mvwputch( w_data, i, width - 1, BORDER_COLOR, LINE_XOXO ); } mvwputch( w_data, dataHeight - 1, 0, BORDER_COLOR, LINE_XXOO ); // _| mvwputch( w_data, dataHeight - 1, width - 1, BORDER_COLOR, LINE_XOOX ); // |_ int recmin = 0, recmax = current.size(); if( recmax > dataLines ) { if( line <= recmin + dataHalfLines ) { for( int i = recmin; i < recmin + dataLines; ++i ) { std::string tmp_name = item::nname( current[i]->result ); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, i - recmin, 2, c_dkgray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, i - recmin, 2, ( available[i] ? h_white : h_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i - recmin, 2, ( available[i] ? c_white : c_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else if( line >= recmax - dataHalfLines ) { for( int i = recmax - dataLines; i < recmax; ++i ) { std::string tmp_name = item::nname( current[i]->result ); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataLines + i - recmax, 2, c_ltgray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? h_white : h_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? c_white : c_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else { for( int i = line - dataHalfLines; i < line - dataHalfLines + dataLines; ++i ) { std::string tmp_name = item::nname( current[i]->result ); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataHalfLines + i - line, 2, c_ltgray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? h_white : h_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? c_white : c_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } } else { for( size_t i = 0; i < current.size() && i < ( size_t )dataHeight + 1; ++i ) { std::string tmp_name = item::nname( current[i]->result ); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), ( int )i + 1, tmp_name.c_str() ); } if( ( int )i == line ) { mvwprintz( w_data, i, 2, ( available[i] ? h_white : h_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i, 2, ( available[i] ? c_white : c_dkgray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } if( !current.empty() ) { int pane = FULL_SCREEN_WIDTH - 30 - 1; int count = batch ? line + 1 : 1; // batch size nc_color col = available[ line ] ? c_white : c_ltgray; const auto &req = current[ line ]->requirements(); draw_can_craft_indicator( w_head, 0, *current[line] ); wrefresh( w_head ); ypos = 0; std::vector<std::string> component_print_buffer; auto tools = req.get_folded_tools_list( pane, col, crafting_inv, count ); auto comps = req.get_folded_components_list( pane, col, crafting_inv, count ); component_print_buffer.insert( component_print_buffer.end(), tools.begin(), tools.end() ); component_print_buffer.insert( component_print_buffer.end(), comps.begin(), comps.end() ); if( !g->u.knows_recipe( current[line] ) ) { component_print_buffer.push_back( _( "Recipe not memorized yet" ) ); } //handle positioning of component list if it needed to be scrolled int componentPrintOffset = 0; if( display_mode > 2 ) { componentPrintOffset = ( display_mode - 2 ) * componentPrintHeight; } if( component_print_buffer.size() < static_cast<size_t>( componentPrintOffset ) ) { componentPrintOffset = 0; if( previous_tab != tab.cur() || previous_subtab != subtab.cur() || previous_item_line != line ) { display_mode = 2; } else { display_mode = 0; } } //only used to preserve mode position on components when //moving to another item and the view is already scrolled previous_tab = tab.cur(); previous_subtab = subtab.cur(); previous_item_line = line; if( display_mode == 0 ) { mvwprintz( w_data, ypos++, 30, col, _( "Skills used: %s" ), ( !current[line]->skill_used ? _( "N/A" ) : current[line]->skill_used.obj().name().c_str() ) ); mvwprintz( w_data, ypos++, 30, col, _( "Required skills: %s" ), ( current[line]->required_skills_string().c_str() ) ); mvwprintz( w_data, ypos++, 30, col, _( "Difficulty: %d" ), current[line]->difficulty ); if( !current[line]->skill_used ) { mvwprintz( w_data, ypos++, 30, col, _( "Your skill level: N/A" ) ); } else { mvwprintz( w_data, ypos++, 30, col, _( "Your skill level: %d" ), // Macs don't seem to like passing this as a class, so force it to int ( int )g->u.get_skill_level( current[line]->skill_used ) ); } ypos += current[line]->print_time( w_data, ypos, 30, pane, col, count ); mvwprintz( w_data, ypos++, 30, col, _( "Dark craftable? %s" ), current[line]->has_flag( "BLIND_EASY" ) ? _( "Easy" ) : current[line]->has_flag( "BLIND_HARD" ) ? _( "Hard" ) : _( "Impossible" ) ); ypos += current[line]->print_items( w_data, ypos, 30, col, ( batch ) ? line + 1 : 1 ); } //color needs to be preserved in case part of the previous page was cut off nc_color stored_color = col; if( display_mode > 2 ) { stored_color = rotated_color; } else { rotated_color = col; } int components_printed = 0; for( size_t i = static_cast<size_t>( componentPrintOffset ); i < component_print_buffer.size(); i++ ) { if( ypos >= componentPrintHeight ) { break; } components_printed++; print_colored_text( w_data, ypos++, 30, stored_color, col, component_print_buffer[i] ); } if( ypos >= componentPrintHeight && component_print_buffer.size() > static_cast<size_t>( components_printed ) ) { mvwprintz( w_data, ypos++, 30, col, _( "v (more)" ) ); rotated_color = stored_color; } if( isWide ) { if( last_recipe != current[line] ) { last_recipe = current[line]; tmp = current[line]->create_result(); tmp.info( true, thisItem ); } draw_item_info( w_iteminfo, tmp.tname(), tmp.type_name(), thisItem, dummy, scroll_pos, true, true, true, false, true ); } } draw_scrollbar( w_data, line, dataLines, recmax, 0 ); wrefresh( w_data ); if( isWide ) { wrefresh( w_iteminfo ); } const std::string action = ctxt.handle_input(); if( action == "CYCLE_MODE" ) { display_mode = display_mode + 1; if( display_mode <= 0 ) { display_mode = 0; } } else if( action == "LEFT" ) { subtab.prev(); redraw = true; } else if( action == "SCROLL_UP" ) { scroll_pos--; } else if( action == "SCROLL_DOWN" ) { scroll_pos++; } else if( action == "PREV_TAB" ) { tab.prev(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "RIGHT" ) { subtab.next(); redraw = true; } else if( action == "NEXT_TAB" ) { tab.next(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "DOWN" ) { line++; } else if( action == "UP" ) { line--; } else if( action == "CONFIRM" ) { if( available.empty() || !available[line] ) { popup( _( "You can't do that!" ) ); } else if( !current[line]->check_eligible_containers_for_crafting( ( batch ) ? line + 1 : 1 ) ) { ; // popup is already inside check } else { chosen = current[line]; batch_size = ( batch ) ? line + 1 : 1; done = true; } } else if( action == "HELP_RECIPE" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } tmp = current[line]->create_result(); full_screen_popup( "%s\n%s", tmp.type_name( 1 ).c_str(), tmp.info( true ).c_str() ); redraw = true; keepline = true; } else if( action == "FILTER" ) { filterstring = string_input_popup( _( "Search:" ), 85, filterstring, _( "Special prefixes for requirements:\n" " [t] search tools\n" " [c] search components\n" " [q] search qualities\n" " [s] search skills\n" " [S] search skill used only\n" "Special prefixes for results:\n" " [Q] search qualities\n" "Examples:\n" " t:soldering iron\n" " c:two by four\n" " q:metal sawing\n" " s:cooking\n" " Q:fine bolt turning" ) ); redraw = true; } else if( action == "QUIT" ) { chosen = nullptr; done = true; } else if( action == "RESET_FILTER" ) { filterstring = ""; redraw = true; } else if( action == "CYCLE_BATCH" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } batch = !batch; if( batch ) { batch_line = line; chosen = current[batch_line]; } else { line = batch_line; keepline = true; } redraw = true; } if( line < 0 ) { line = current.size() - 1; } else if( line >= ( int )current.size() ) { line = 0; } } while( !done ); return chosen; }
// Pick up items at (pos). void Pickup::pick_up( const tripoint &pos, int min ) { int cargo_part = -1; const optional_vpart_position vp = g->m.veh_at( pos ); vehicle *const veh = veh_pointer_or_null( vp ); bool from_vehicle = false; if( min != -1 ) { switch( interact_with_vehicle( veh, pos, vp ? vp->part_index() : -1 ) ) { case DONE: return; case ITEMS_FROM_CARGO: { const cata::optional<vpart_reference> carg = vp.part_with_feature( "CARGO", false ); cargo_part = carg ? carg->part_index() : -1; } from_vehicle = cargo_part >= 0; break; case ITEMS_FROM_GROUND: // Nothing to change, default is to pick from ground anyway. if( g->m.has_flag( "SEALED", pos ) ) { return; } break; } } if( !from_vehicle ) { bool isEmpty = ( g->m.i_at( pos ).empty() ); // Hide the pickup window if this is a toilet and there's nothing here // but water. if( ( !isEmpty ) && g->m.furn( pos ) == f_toilet ) { isEmpty = true; for( auto maybe_water : g->m.i_at( pos ) ) { if( maybe_water.typeId() != "water" ) { isEmpty = false; break; } } } if( isEmpty && ( min != -1 || !get_option<bool>( "AUTO_PICKUP_ADJACENT" ) ) ) { return; } } // which items are we grabbing? std::vector<item> here; if( from_vehicle ) { auto vehitems = veh->get_items( cargo_part ); here.resize( vehitems.size() ); std::copy( vehitems.begin(), vehitems.end(), here.begin() ); } else { auto mapitems = g->m.i_at( pos ); here.resize( mapitems.size() ); std::copy( mapitems.begin(), mapitems.end(), here.begin() ); } if( min == -1 ) { // Recursively pick up adjacent items if that option is on. if( get_option<bool>( "AUTO_PICKUP_ADJACENT" ) && g->u.pos() == pos ) { //Autopickup adjacent direction adjacentDir[8] = {NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST}; for( auto &elem : adjacentDir ) { tripoint apos = tripoint( direction_XY( elem ), 0 ); apos += pos; pick_up( apos, min ); } } // Bail out if this square cannot be auto-picked-up if( g->check_zone( zone_type_id( "NO_AUTO_PICKUP" ), pos ) ) { return; } else if( g->m.has_flag( "SEALED", pos ) ) { return; } } // Not many items, just grab them if( ( int )here.size() <= min && min != -1 ) { g->u.assign_activity( activity_id( "ACT_PICKUP" ) ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); // Only one item means index is 0. g->u.activity.values.push_back( 0 ); // auto-pickup means pick up all. g->u.activity.values.push_back( 0 ); return; } std::vector<std::list<item_idx>> stacked_here; for( size_t i = 0; i < here.size(); i++ ) { item &it = here[i]; bool found_stack = false; for( auto &stack : stacked_here ) { if( stack.begin()->_item.stacks_with( it ) ) { item_idx el = { it, i }; stack.push_back( el ); found_stack = true; break; } } if( !found_stack ) { std::list<item_idx> newstack; newstack.push_back( { it, i } ); stacked_here.push_back( newstack ); } } std::reverse( stacked_here.begin(), stacked_here.end() ); if( min != -1 ) { // don't bother if we're just autopickuping g->temp_exit_fullscreen(); } bool sideStyle = use_narrow_sidebar(); // Otherwise, we have Autopickup, 2 or more items and should list them, etc. int maxmaxitems = sideStyle ? TERMY : getmaxy( g->w_messages ) - 3; int itemsH = std::min( 25, TERMY / 2 ); int pickupBorderRows = 3; // The pickup list may consume the entire terminal, minus space needed for its // header/footer and the item info window. int minleftover = itemsH + pickupBorderRows; if( maxmaxitems > TERMY - minleftover ) { maxmaxitems = TERMY - minleftover; } const int minmaxitems = sideStyle ? 6 : 9; std::vector<pickup_count> getitem( stacked_here.size() ); int maxitems = stacked_here.size(); maxitems = ( maxitems < minmaxitems ? minmaxitems : ( maxitems > maxmaxitems ? maxmaxitems : maxitems ) ); int itemcount = 0; if( min == -1 ) { //Auto Pickup, select matching items if( !select_autopickup_items( stacked_here, getitem ) ) { // If we didn't find anything, bail out now. return; } } else { int pickupH = maxitems + pickupBorderRows; int pickupW = getmaxx( g->w_messages ); int pickupY = VIEW_OFFSET_Y; int pickupX = getbegx( g->w_messages ); int itemsW = pickupW; int itemsY = sideStyle ? pickupY + pickupH : TERMY - itemsH; int itemsX = pickupX; catacurses::window w_pickup = catacurses::newwin( pickupH, pickupW, pickupY, pickupX ); catacurses::window w_item_info = catacurses::newwin( itemsH, itemsW, itemsY, itemsX ); std::string action; long raw_input_char = ' '; input_context ctxt( "PICKUP" ); ctxt.register_action( "UP" ); ctxt.register_action( "DOWN" ); ctxt.register_action( "RIGHT" ); ctxt.register_action( "LEFT" ); ctxt.register_action( "NEXT_TAB", _( "Next page" ) ); ctxt.register_action( "PREV_TAB", _( "Previous page" ) ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "SELECT_ALL" ); ctxt.register_action( "QUIT", _( "Cancel" ) ); ctxt.register_action( "ANY_INPUT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "FILTER" ); int start = 0; int cur_it = 0; bool update = true; mvwprintw( w_pickup, 0, 0, _( "PICK UP" ) ); int selected = 0; int iScrollPos = 0; std::string filter; std::string new_filter; std::vector<int> matches;//Indexes of items that match the filter bool filter_changed = true; if( g->was_fullscreen ) { g->draw_ter(); } // Now print the two lists; those on the ground and about to be added to inv // Continue until we hit return or space do { const std::string pickup_chars = ctxt.get_available_single_char_hotkeys( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;" ); int idx = -1; for( int i = 1; i < pickupH; i++ ) { mvwprintw( w_pickup, i, 0, " " ); } if( action == "ANY_INPUT" && raw_input_char >= '0' && raw_input_char <= '9' ) { int raw_input_char_value = ( char )raw_input_char - '0'; itemcount *= 10; itemcount += raw_input_char_value; if( itemcount < 0 ) { itemcount = 0; } } else if( action == "SCROLL_UP" ) { iScrollPos--; } else if( action == "SCROLL_DOWN" ) { iScrollPos++; } else if( action == "PREV_TAB" ) { if( start > 0 ) { start -= maxitems; } else { start = ( int )( ( matches.size() - 1 ) / maxitems ) * maxitems; } selected = start; mvwprintw( w_pickup, maxitems + 2, 0, " " ); } else if( action == "NEXT_TAB" ) { if( start + maxitems < ( int )matches.size() ) { start += maxitems; } else { start = 0; } iScrollPos = 0; selected = start; mvwprintw( w_pickup, maxitems + 2, pickupH, " " ); } else if( action == "UP" ) { selected--; iScrollPos = 0; if( selected < 0 ) { selected = matches.size() - 1; start = ( int )( matches.size() / maxitems ) * maxitems; if( start >= ( int )matches.size() ) { start -= maxitems; } } else if( selected < start ) { start -= maxitems; } } else if( action == "DOWN" ) { selected++; iScrollPos = 0; if( selected >= ( int )matches.size() ) { selected = 0; start = 0; } else if( selected >= start + maxitems ) { start += maxitems; } } else if( selected >= 0 && selected < int( matches.size() ) && ( ( action == "RIGHT" && !getitem[matches[selected]].pick ) || ( action == "LEFT" && getitem[matches[selected]].pick ) ) ) { idx = selected; } else if( action == "FILTER" ) { new_filter = filter; string_input_popup popup; popup .title( _( "Set filter" ) ) .width( 30 ) .edit( new_filter ); if( !popup.canceled() ) { filter_changed = true; } else { wrefresh( g->w_terrain ); } } else if( action == "ANY_INPUT" && raw_input_char == '`' ) { std::string ext = string_input_popup() .title( _( "Enter 2 letters (case sensitive):" ) ) .width( 3 ) .max_length( 2 ) .query_string(); if( ext.size() == 2 ) { int p1 = pickup_chars.find( ext.at( 0 ) ); int p2 = pickup_chars.find( ext.at( 1 ) ); if( p1 != -1 && p2 != -1 ) { idx = pickup_chars.size() + ( p1 * pickup_chars.size() ) + p2; } } } else if( action == "ANY_INPUT" ) { idx = ( raw_input_char <= 127 ) ? pickup_chars.find( raw_input_char ) : -1; iScrollPos = 0; } if( idx >= 0 && idx < ( int )matches.size() ) { size_t true_idx = matches[idx]; if( itemcount != 0 || getitem[true_idx].count == 0 ) { item &temp = stacked_here[true_idx].begin()->_item; int amount_available = temp.count_by_charges() ? temp.charges : stacked_here[true_idx].size(); if( itemcount >= amount_available ) { itemcount = 0; } getitem[true_idx].count = itemcount; itemcount = 0; } // Note: this might not change the value of getitem[idx] at all! getitem[true_idx].pick = ( action == "RIGHT" ? true : ( action == "LEFT" ? false : !getitem[true_idx].pick ) ); if( action != "RIGHT" && action != "LEFT" ) { selected = idx; start = ( int )( idx / maxitems ) * maxitems; } if( !getitem[true_idx].pick ) { getitem[true_idx].count = 0; } update = true; } if( filter_changed ) { matches.clear(); while( matches.empty() ) { auto filter_func = item_filter_from_string( new_filter ); for( size_t index = 0; index < stacked_here.size(); index++ ) { if( filter_func( stacked_here[index].begin()->_item ) ) { matches.push_back( index ); } } if( matches.empty() ) { popup( _( "Your filter returned no results" ) ); wrefresh( g->w_terrain ); // The filter must have results, or simply be emptied or canceled, // as this screen can't be reached without there being // items available string_input_popup popup; popup .title( _( "Set filter" ) ) .width( 30 ) .edit( new_filter ); if( popup.canceled() ) { new_filter = filter; filter_changed = false; } } } if( filter_changed ) { filter = new_filter; filter_changed = false; selected = 0; start = 0; iScrollPos = 0; } wrefresh( g->w_terrain ); } item &selected_item = stacked_here[matches[selected]].begin()->_item; werase( w_item_info ); if( selected >= 0 && selected <= ( int )stacked_here.size() - 1 ) { std::vector<iteminfo> vThisItem; std::vector<iteminfo> vDummy; selected_item.info( true, vThisItem ); draw_item_info( w_item_info, "", "", vThisItem, vDummy, iScrollPos, true, true ); } draw_custom_border( w_item_info, false ); mvwprintw( w_item_info, 0, 2, "< " ); trim_and_print( w_item_info, 0, 4, itemsW - 8, c_white, "%s >", selected_item.display_name().c_str() ); wrefresh( w_item_info ); if( action == "SELECT_ALL" ) { int count = 0; for( auto i : matches ) { if( getitem[i].pick ) { count++; } getitem[i].pick = true; } if( count == ( int )stacked_here.size() ) { for( size_t i = 0; i < stacked_here.size(); i++ ) { getitem[i].pick = false; } } update = true; } for( cur_it = start; cur_it < start + maxitems; cur_it++ ) { mvwprintw( w_pickup, 1 + ( cur_it % maxitems ), 0, " " ); if( cur_it < ( int )matches.size() ) { int true_it = matches[cur_it]; item &this_item = stacked_here[ true_it ].begin()->_item; nc_color icolor = this_item.color_in_inventory(); if( cur_it == selected ) { icolor = hilite( icolor ); } if( cur_it < ( int )pickup_chars.size() ) { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, char( pickup_chars[cur_it] ) ); } else if( cur_it < ( int )pickup_chars.size() + ( int )pickup_chars.size() * ( int )pickup_chars.size() ) { int p = cur_it - pickup_chars.size(); int p1 = p / pickup_chars.size(); int p2 = p % pickup_chars.size(); mvwprintz( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, "`%c%c", char( pickup_chars[p1] ), char( pickup_chars[p2] ) ); } else { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, ' ' ); } if( getitem[true_it].pick ) { if( getitem[true_it].count == 0 ) { wprintz( w_pickup, c_light_blue, " + " ); } else { wprintz( w_pickup, c_light_blue, " # " ); } } else { wprintw( w_pickup, " - " ); } std::string item_name; if( stacked_here[true_it].begin()->_item.ammo_type() == "money" ) { //Count charges //TODO: transition to the item_location system used for the inventory unsigned long charges_total = 0; for( const auto item : stacked_here[true_it] ) { charges_total += item._item.charges; } //Picking up none or all the cards in a stack if( !getitem[true_it].pick || getitem[true_it].count == 0 ) { item_name = stacked_here[true_it].begin()->_item.display_money( stacked_here[true_it].size(), charges_total ); } else { unsigned long charges = 0; int c = getitem[true_it].count; for( auto it = stacked_here[true_it].begin(); it != stacked_here[true_it].end() && c > 0; ++it, --c ) { charges += it->_item.charges; } item_name = string_format( _( "%s of %s" ), stacked_here[true_it].begin()->_item.display_money( getitem[true_it].count, charges ), format_money( charges_total ) ); } } else { item_name = this_item.display_name( stacked_here[true_it].size() ); } if( stacked_here[true_it].size() > 1 ) { item_name = string_format( "%d %s", stacked_here[true_it].size(), item_name.c_str() ); } if( get_option<bool>( "ITEM_SYMBOLS" ) ) { item_name = string_format( "%s %s", this_item.symbol().c_str(), item_name.c_str() ); } trim_and_print( w_pickup, 1 + ( cur_it % maxitems ), 6, pickupW - 4, icolor, item_name ); } } mvwprintw( w_pickup, maxitems + 1, 0, _( "[%s] Unmark" ), ctxt.get_desc( "LEFT", 1 ).c_str() ); center_print( w_pickup, maxitems + 1, c_light_gray, string_format( _( "[%s] Help" ), ctxt.get_desc( "HELP_KEYBINDINGS", 1 ).c_str() ) ); right_print( w_pickup, maxitems + 1, 0, c_light_gray, string_format( _( "[%s] Mark" ), ctxt.get_desc( "RIGHT", 1 ).c_str() ) ); mvwprintw( w_pickup, maxitems + 2, 0, _( "[%s] Prev" ), ctxt.get_desc( "PREV_TAB", 1 ).c_str() ); center_print( w_pickup, maxitems + 2, c_light_gray, string_format( _( "[%s] All" ), ctxt.get_desc( "SELECT_ALL", 1 ).c_str() ) ); right_print( w_pickup, maxitems + 2, 0, c_light_gray, string_format( _( "[%s] Next" ), ctxt.get_desc( "NEXT_TAB", 1 ).c_str() ) ); if( update ) { // Update weight & volume information update = false; for( int i = 9; i < pickupW; ++i ) { mvwaddch( w_pickup, 0, i, ' ' ); } units::mass weight_picked_up = 0; units::volume volume_picked_up = 0; for( size_t i = 0; i < getitem.size(); i++ ) { if( getitem[i].pick ) { item temp = stacked_here[i].begin()->_item; if( temp.count_by_charges() && getitem[i].count < temp.charges && getitem[i].count != 0 ) { temp.charges = getitem[i].count; } int num_picked = std::min( stacked_here[i].size(), getitem[i].count == 0 ? stacked_here[i].size() : getitem[i].count ); weight_picked_up += temp.weight() * num_picked; volume_picked_up += temp.volume() * num_picked; } } auto weight_predict = g->u.weight_carried() + weight_picked_up; auto volume_predict = g->u.volume_carried() + volume_picked_up; mvwprintz( w_pickup, 0, 9, weight_predict > g->u.weight_capacity() ? c_red : c_white, _( "Wgt %.1f" ), round_up( convert_weight( weight_predict ), 1 ) ); wprintz( w_pickup, c_white, "/%.1f", round_up( convert_weight( g->u.weight_capacity() ), 1 ) ); std::string fmted_volume_predict = format_volume( volume_predict ); mvwprintz( w_pickup, 0, 24, volume_predict > g->u.volume_capacity() ? c_red : c_white, _( "Vol %s" ), fmted_volume_predict.c_str() ); std::string fmted_volume_capacity = format_volume( g->u.volume_capacity() ); wprintz( w_pickup, c_white, "/%s", fmted_volume_capacity.c_str() ); }; wrefresh( w_pickup ); action = ctxt.handle_input(); raw_input_char = ctxt.get_raw_input().get_first_input(); } while( action != "QUIT" && action != "CONFIRM" ); bool item_selected = false; // Check if we have selected an item. for( auto selection : getitem ) { if( selection.pick ) { item_selected = true; } } if( action != "CONFIRM" || !item_selected ) { w_pickup = catacurses::window(); w_item_info = catacurses::window(); add_msg( _( "Never mind." ) ); g->reenter_fullscreen(); g->refresh_all(); return; } } // At this point we've selected our items, register an activity to pick them up. g->u.assign_activity( activity_id( "ACT_PICKUP" ) ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); if( min == -1 ) { // Auto pickup will need to auto resume since there can be several of them on the stack. g->u.activity.auto_resume = true; } std::vector<std::pair<int, int>> pick_values; for( size_t i = 0; i < stacked_here.size(); i++ ) { const auto &selection = getitem[i]; if( !selection.pick ) { continue; } const auto &stack = stacked_here[i]; // Note: items can be both charged and stacked // For robustness, let's assume they can be both in the same stack bool pick_all = selection.count == 0; size_t count = selection.count; for( const item_idx &it : stack ) { if( !pick_all && count == 0 ) { break; } if( it._item.count_by_charges() ) { size_t num_picked = std::min( ( size_t )it._item.charges, count ); pick_values.push_back( { static_cast<int>( it.idx ), static_cast<int>( num_picked ) } ); count -= num_picked; } else { size_t num_picked = 1; pick_values.push_back( { static_cast<int>( it.idx ), 0 } ); count -= num_picked; } } } // The pickup activity picks up items last-to-first from its values list, so make sure the // higher indices are at the end. std::sort( pick_values.begin(), pick_values.end() ); for( auto &it : pick_values ) { g->u.activity.values.push_back( it.first ); g->u.activity.values.push_back( it.second ); } g->reenter_fullscreen(); }
void minesweeper_game::new_level(WINDOW *w_minesweeper) { iMaxY = getmaxy(w_minesweeper) - 2; iMaxX = getmaxx(w_minesweeper) - 2; werase(w_minesweeper); mLevel.clear(); mLevelReveal.clear(); auto set_num = [&](const std::string sType, int &iVal, const int iMin, const int iMax) { std::ostringstream ssTemp; ssTemp << _("Min:") << iMin << " " << _("Max:") << " " << iMax; do { if ( iVal < iMin || iVal > iMax ) { iVal = iMin; } iVal = std::atoi(string_input_popup(sType.c_str(), 5, to_string(iVal), ssTemp.str().c_str(), "", -1, true).c_str()); } while( iVal < iMin || iVal > iMax); }; uimenu difficulty; difficulty.text = _("Game Difficulty"); difficulty.entries.push_back(uimenu_entry(0, true, 'b', _("Beginner"))); difficulty.entries.push_back(uimenu_entry(1, true, 'i', _("Intermediate"))); difficulty.entries.push_back(uimenu_entry(2, true, 'e', _("Expert"))); difficulty.entries.push_back(uimenu_entry(3, true, 'c', _("Custom"))); difficulty.query(); switch (difficulty.ret) { case 0: iLevelY = 8; iLevelX = 8; iBombs = 10; break; case 1: iLevelY = 16; iLevelX = 16; iBombs = 40; break; case 2: iLevelY = 16; iLevelX = 30; iBombs = 99; break; case 3: default: iLevelY = iMinY; iLevelX = iMinX; set_num(_("Level width:"), iLevelX, iMinX, iMaxX); set_num(_("Level height:"), iLevelY, iMinY, iMaxY); iBombs = iLevelX * iLevelY * 0.1; set_num(_("Number of bombs:"), iBombs, iBombs, iLevelX * iLevelY * 0.6); break; } iOffsetX = ((iMaxX - iLevelX) / 2) + 1; iOffsetY = ((iMaxY - iLevelY) / 2) + 1; int iRandX; int iRandY; for ( int i = 0; i < iBombs; i++ ) { do { iRandX = rng(0, iLevelX - 1); iRandY = rng(0, iLevelY - 1); } while ( mLevel[iRandY][iRandX] == (int)bomb ); mLevel[iRandY][iRandX] = (int)bomb; } for ( int y = 0; y < iLevelY; y++ ) { for ( int x = 0; x < iLevelX; x++ ) { if (mLevel[y][x] == (int)bomb) { const auto circle = closest_tripoints_first( 1, {x, y, 0} ); for( const auto &p : circle ) { if ( p.x >= 0 && p.x < iLevelX && p.y >= 0 && p.y < iLevelY ) { if ( mLevel[p.y][p.x] != (int)bomb ) { mLevel[p.y][p.x]++; } } } } } } for (int y = 0; y < iLevelY; y++) { mvwputch(w_minesweeper, iOffsetY + y, iOffsetX, c_white, std::string(iLevelX, '#')); } mvwputch(w_minesweeper, iOffsetY, iOffsetX, hilite(c_white), "#"); draw_custom_border(w_minesweeper, true, true, true, true, true, true, true, true, BORDER_COLOR, iOffsetY - 1, iLevelY + 2, iOffsetX - 1, iLevelX + 2); }
void defense_game::setup() { WINDOW* w = newwin(25, 80, 0, 0); int selection = 1; refresh_setup(w, selection); while (true) { char ch = input(); if (ch == 'S') { if (!zombies && !specials && !spiders && !triffids && !robots && !subspace) { popup("You must choose at least one monster group!"); refresh_setup(w, selection); } else return; } else if (ch == '+' || ch == '>' || ch == 'j') { if (selection == 19) selection = 1; else selection++; refresh_setup(w, selection); } else if (ch == '-' || ch == '<' || ch == 'k') { if (selection == 1) selection = 19; else selection--; refresh_setup(w, selection); } else if (ch == '!') { std::string name = string_input_popup("Template Name:", 20); refresh_setup(w, selection); } else if (ch == 'S') return; else { switch (selection) { case 1: // Scenario selection if (ch == 'l') { if (style == defense_style(NUM_DEFENSE_STYLES - 1)) style = defense_style(1); else style = defense_style(style + 1); } if (ch == 'h') { if (style == defense_style(1)) style = defense_style(NUM_DEFENSE_STYLES - 1); else style = defense_style(style - 1); } init_to_style(style); break; case 2: // Location selection if (ch == 'l') { if (location == defense_location(NUM_DEFENSE_LOCATIONS - 1)) location = defense_location(1); else location = defense_location(location + 1); } if (ch == 'h') { if (location == defense_location(1)) location = defense_location(NUM_DEFENSE_LOCATIONS - 1); else location = defense_location(location - 1); } mvwprintz(w, 5, 2, c_black, "\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); mvwprintz(w, 5, 2, c_yellow, defense_location_name(location).c_str()); mvwprintz(w, 5, 28, c_ltgray, defense_location_description(location).c_str()); break; case 3: // Difficulty of the first wave if (ch == 'h' && initial_difficulty > 10) initial_difficulty -= 5; if (ch == 'l' && initial_difficulty < 995) initial_difficulty += 5; mvwprintz(w, 7, 22, c_black, "xxx"); mvwprintz(w, 7, NUMALIGN(initial_difficulty), c_yellow, "%d", initial_difficulty); break; case 4: // Wave Difficulty if (ch == 'h' && wave_difficulty > 10) wave_difficulty -= 5; if (ch == 'l' && wave_difficulty < 995) wave_difficulty += 5; mvwprintz(w, 8, 22, c_black, "xxx"); mvwprintz(w, 8, NUMALIGN(wave_difficulty), c_yellow, "%d", wave_difficulty); break; case 5: if (ch == 'h' && time_between_waves > 5) time_between_waves -= 5; if (ch == 'l' && time_between_waves < 995) time_between_waves += 5; mvwprintz(w, 10, 22, c_black, "xxx"); mvwprintz(w, 10, NUMALIGN(time_between_waves), c_yellow, "%d", time_between_waves); break; case 6: if (ch == 'h' && waves_between_caravans > 1) waves_between_caravans -= 1; if (ch == 'l' && waves_between_caravans < 50) waves_between_caravans += 1; mvwprintz(w, 11, 22, c_black, "xxx"); mvwprintz(w, 11, NUMALIGN(waves_between_caravans), c_yellow, "%d", waves_between_caravans); break; case 7: if (ch == 'h' && initial_cash > 0) initial_cash -= 100; if (ch == 'l' && initial_cash < 99900) initial_cash += 100; mvwprintz(w, 13, 20, c_black, "xxxxx"); mvwprintz(w, 13, NUMALIGN(initial_cash), c_yellow, "%d", initial_cash); break; case 8: if (ch == 'h' && cash_per_wave > 0) cash_per_wave -= 100; if (ch == 'l' && cash_per_wave < 9900) cash_per_wave += 100; mvwprintz(w, 14, 21, c_black, "xxxx"); mvwprintz(w, 14, NUMALIGN(cash_per_wave), c_yellow, "%d", cash_per_wave); break; case 9: if (ch == 'h' && cash_increase > 0) cash_increase -= 50; if (ch == 'l' && cash_increase < 9950) cash_increase += 50; mvwprintz(w, 15, 21, c_black, "xxxx"); mvwprintz(w, 15, NUMALIGN(cash_increase), c_yellow, "%d", cash_increase); break; case 10: if (ch == ' ' || ch == '\n') { zombies = !zombies; specials = false; } mvwprintz(w, 18, 2, (zombies ? c_ltgreen : c_yellow), "Zombies"); mvwprintz(w, 18, 14, c_yellow, "Special Zombies"); break; case 11: if (ch == ' ' || ch == '\n') { specials = !specials; zombies = false; } mvwprintz(w, 18, 2, c_yellow, "Zombies"); mvwprintz(w, 18, 14, (specials ? c_ltgreen : c_yellow), "Special Zombies"); break; case 12: if (ch == ' ' || ch == '\n') spiders = !spiders; mvwprintz(w, 18, 34, (spiders ? c_ltgreen : c_yellow), "Spiders"); break; case 13: if (ch == ' ' || ch == '\n') triffids = !triffids; mvwprintz(w, 18, 46, (triffids ? c_ltgreen : c_yellow), "Triffids"); break; case 14: if (ch == ' ' || ch == '\n') robots = !robots; mvwprintz(w, 18, 59, (robots ? c_ltgreen : c_yellow), "Robots"); break; case 15: if (ch == ' ' || ch == '\n') subspace = !subspace; mvwprintz(w, 18, 70, (subspace ? c_ltgreen : c_yellow), "Subspace"); break; case 16: if (ch == ' ' || ch == '\n') hunger = !hunger; mvwprintz(w, 21, 2, (hunger ? c_ltgreen : c_yellow), "Food"); break; case 17: if (ch == ' ' || ch == '\n') thirst = !thirst; mvwprintz(w, 21, 16, (thirst ? c_ltgreen : c_yellow), "Water"); break; case 18: if (ch == ' ' || ch == '\n') sleep = !sleep; mvwprintz(w, 21, 31, (sleep ? c_ltgreen : c_yellow), "Sleep"); break; case 19: if (ch == ' ' || ch == '\n') mercenaries = !mercenaries; mvwprintz(w, 21, 46, (mercenaries ? c_ltgreen : c_yellow), "Mercenaries"); break; } } if (ch == 'h' || ch == 'l' || ch == ' ' || ch == '\n') refresh_setup(w, selection); } }
void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) { auto global_rules_old = global_rules; auto character_rules_old = character_rules; const int header_height = 4; const int content_height = FULL_SCREEN_HEIGHT - 2 - header_height; const int offset_x = ( TERMX > FULL_SCREEN_WIDTH ) ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0; const int offset_y = ( TERMY > FULL_SCREEN_HEIGHT ) ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0; enum Columns : int { COLUMN_RULE, COLUMN_ATTITUDE, COLUMN_PROXIMITY, COLUMN_WHITE_BLACKLIST, }; std::map<int, int> column_pos; column_pos[COLUMN_RULE] = 4; column_pos[COLUMN_ATTITUDE] = 48; column_pos[COLUMN_PROXIMITY] = 59; column_pos[COLUMN_WHITE_BLACKLIST] = 66; const int num_columns = column_pos.size(); WINDOW *w_help = newwin( ( FULL_SCREEN_HEIGHT / 2 ) - 2, FULL_SCREEN_WIDTH * 3 / 4, 7 + offset_y + ( FULL_SCREEN_HEIGHT / 2 ) / 2, offset_x + 19 / 2 ); WINDOW_PTR w_helpptr( w_help ); WINDOW *w_border = newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, offset_y, offset_x ); WINDOW_PTR w_borderptr( w_border ); WINDOW *w_header = newwin( header_height, FULL_SCREEN_WIDTH - 2, 1 + offset_y, 1 + offset_x ); WINDOW_PTR w_headerptr( w_header ); WINDOW *w = newwin( content_height, FULL_SCREEN_WIDTH - 2, header_height + 1 + offset_y, 1 + offset_x ); WINDOW_PTR wptr( w ); draw_border( w_border, BORDER_COLOR, custom_name_in ); mvwputch( w_border, 3, 0, c_ltgray, LINE_XXXO ); // |- mvwputch( w_border, 3, 79, c_ltgray, LINE_XOXX ); // -| for( auto &column : column_pos ) { mvwputch( w_border, FULL_SCREEN_HEIGHT - 1, column.second + 1, c_ltgray, LINE_XXOX ); // _|_ } wrefresh( w_border ); static const std::vector<std::string> hotkeys = {{ _( "<A>dd" ), _( "<R>emove" ), _( "<C>opy" ), _( "<M>ove" ), _( "<E>nable" ), _( "<D>isable" ), _( "<T>est" ) } }; int tmpx = 0; for( auto &hotkey : hotkeys ) { tmpx += shortcut_print( w_header, 0, tmpx, c_white, c_ltgreen, hotkey ) + 2; } tmpx = 0; tmpx += shortcut_print( w_header, 1, tmpx, c_white, c_ltgreen, _( "<+-> Move up/down" ) ) + 2; tmpx += shortcut_print( w_header, 1, tmpx, c_white, c_ltgreen, _( "<Enter>-Edit" ) ) + 2; shortcut_print( w_header, 1, tmpx, c_white, c_ltgreen, _( "<Tab>-Switch Page" ) ); for( int i = 0; i < 78; i++ ) { mvwputch( w_header, 2, i, c_ltgray, LINE_OXOX ); // Draw line under header } for( auto &pos : column_pos ) { mvwputch( w_header, 2, pos.second, c_ltgray, LINE_OXXX ); mvwputch( w_header, 3, pos.second, c_ltgray, LINE_XOXO ); } mvwprintz( w_header, 3, 1, c_white, "#" ); mvwprintz( w_header, 3, column_pos[COLUMN_RULE] + 4, c_white, _( "Rules" ) ); mvwprintz( w_header, 3, column_pos[COLUMN_ATTITUDE] + 2, c_white, _( "Attitude" ) ); mvwprintz( w_header, 3, column_pos[COLUMN_PROXIMITY] + 2, c_white, _( "Dist" ) ); mvwprintz( w_header, 3, column_pos[COLUMN_WHITE_BLACKLIST] + 2, c_white, _( "B/W" ) ); wrefresh( w_header ); int tab = GLOBAL_TAB; int line = 0; int column = 0; int start_pos = 0; bool changes_made = false; input_context ctxt( "SAFEMODE" ); ctxt.register_cardinal(); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "QUIT" ); ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "PREV_TAB" ); ctxt.register_action( "ADD_DEFAULT_RULESET" ); ctxt.register_action( "ADD_RULE" ); ctxt.register_action( "REMOVE_RULE" ); ctxt.register_action( "COPY_RULE" ); ctxt.register_action( "ENABLE_RULE" ); ctxt.register_action( "DISABLE_RULE" ); ctxt.register_action( "MOVE_RULE_UP" ); ctxt.register_action( "MOVE_RULE_DOWN" ); ctxt.register_action( "TEST_RULE" ); ctxt.register_action( "HELP_KEYBINDINGS" ); if( is_safemode_in ) { ctxt.register_action( "SWITCH_SAFEMODE_OPTION" ); ctxt.register_action( "SWAP_RULE_GLOBAL_CHAR" ); } while( true ) { int locx = 17; locx += shortcut_print( w_header, 2, locx, c_white, ( tab == GLOBAL_TAB ) ? hilite( c_white ) : c_white, _( "[<Global>]" ) ) + 1; shortcut_print( w_header, 2, locx, c_white, ( tab == CHARACTER_TAB ) ? hilite( c_white ) : c_white, _( "[<Character>]" ) ); locx = 55; mvwprintz( w_header, 0, locx, c_white, _( "Safe Mode enabled:" ) ); locx += shortcut_print( w_header, 1, locx, ( ( get_option<bool>( "SAFEMODE" ) ) ? c_ltgreen : c_ltred ), c_white, ( ( get_option<bool>( "SAFEMODE" ) ) ? _( "True" ) : _( "False" ) ) ); locx += shortcut_print( w_header, 1, locx, c_white, c_ltgreen, " " ); locx += shortcut_print( w_header, 1, locx, c_white, c_ltgreen, _( "<S>witch" ) ); shortcut_print( w_header, 1, locx, c_white, c_ltgreen, " " ); wrefresh( w_header ); // Clear the lines for( int i = 0; i < content_height; i++ ) { for( int j = 0; j < 79; j++ ) { mvwputch( w, i, j, c_black, ' ' ); } for( auto &pos : column_pos ) { mvwputch( w, i, pos.second, c_ltgray, LINE_XOXO ); } } auto ¤t_tab = ( tab == GLOBAL_TAB ) ? global_rules : character_rules; if( tab == CHARACTER_TAB && g->u.name.empty() ) { character_rules.clear(); mvwprintz( w, 8, 15, c_white, _( "Please load a character first to use this page!" ) ); } else if( empty() ) { mvwprintz( w, 8, 15, c_white, _( "Safe Mode manager currently inactive." ) ); mvwprintz( w, 9, 15, c_white, _( "Default rules are used. Add a rule to activate." ) ); mvwprintz( w, 10, 15, c_white, _( "Press ~ to add a default ruleset to get started." ) ); } draw_scrollbar( w_border, line, content_height, current_tab.size(), 5 ); wrefresh( w_border ); calcStartPos( start_pos, line, content_height, current_tab.size() ); // display safe mode for( int i = start_pos; i < ( int )current_tab.size(); i++ ) { if( i >= start_pos && i < start_pos + std::min( content_height, static_cast<int>( current_tab.size() ) ) ) { auto rule = current_tab[i]; nc_color line_color = ( rule.active ) ? c_white : c_ltgray; mvwprintz( w, i - start_pos, 1, line_color, "%d", i + 1 ); mvwprintz( w, i - start_pos, 5, c_yellow, ( line == i ) ? ">> " : " " ); auto draw_column = [&]( Columns column_in, std::string text_in ) { mvwprintz( w, i - start_pos, column_pos[column_in] + 2, ( line == i && column == column_in ) ? hilite( line_color ) : line_color, "%s", text_in.c_str() ); }; draw_column( COLUMN_RULE, ( rule.rule.empty() ) ? _( "<empty rule>" ) : rule.rule ); draw_column( COLUMN_ATTITUDE, Creature::get_attitude_ui_data( rule.attitude ).first ); draw_column( COLUMN_PROXIMITY, ( !rule.whitelist ) ? to_string( rule.proximity ).c_str() : "---" ); draw_column( COLUMN_WHITE_BLACKLIST, ( rule.whitelist ) ? _( "Whitelist" ) : _( "Blacklist" ) ); } } wrefresh( w ); const std::string action = ctxt.handle_input(); if( action == "NEXT_TAB" ) { tab++; if( tab >= MAX_TAB ) { tab = 0; line = 0; } } else if( action == "PREV_TAB" ) { tab--; if( tab < 0 ) { tab = MAX_TAB - 1; line = 0; } } else if( action == "QUIT" ) { break; } else if( tab == CHARACTER_TAB && g->u.name.empty() ) { //Only allow loaded games to use the char sheet } else if( action == "DOWN" ) { line++; if( line >= ( int )current_tab.size() ) { line = 0; } } else if( action == "UP" ) { line--; if( line < 0 ) { line = current_tab.size() - 1; } } else if( action == "ADD_DEFAULT_RULESET" ) { changes_made = true; current_tab.push_back( rules_class( "*", true, false, Creature::A_HOSTILE, 0 ) ); line = current_tab.size() - 1; } else if( action == "ADD_RULE" ) { changes_made = true; current_tab.push_back( rules_class( "", true, false, Creature::A_HOSTILE, get_option<int>( "SAFEMODEPROXIMITY" ) ) ); line = current_tab.size() - 1; } else if( action == "REMOVE_RULE" && !current_tab.empty() ) { changes_made = true; current_tab.erase( current_tab.begin() + line ); if( line > ( int )current_tab.size() - 1 ) { line--; } if( line < 0 ) { line = 0; } } else if( action == "COPY_RULE" && !current_tab.empty() ) { changes_made = true; current_tab.push_back( current_tab[line] ); line = current_tab.size() - 1; } else if( action == "SWAP_RULE_GLOBAL_CHAR" && !current_tab.empty() ) { if( ( tab == GLOBAL_TAB && !g->u.name.empty() ) || tab == CHARACTER_TAB ) { changes_made = true; //copy over auto &temp_rules_from = ( tab == GLOBAL_TAB ) ? global_rules : character_rules; auto &temp_rules_to = ( tab == GLOBAL_TAB ) ? character_rules : global_rules; temp_rules_to.push_back( temp_rules_from[line] ); //remove old temp_rules_from.erase( temp_rules_from.begin() + line ); line = temp_rules_from.size() - 1; tab = ( tab == GLOBAL_TAB ) ? CHARACTER_TAB : GLOBAL_TAB; } } else if( action == "CONFIRM" && !current_tab.empty() ) { changes_made = true; if( column == COLUMN_RULE ) { fold_and_print( w_help, 1, 1, 999, c_white, _( "* is used as a Wildcard. A few Examples:\n" "\n" "human matches every NPC\n" "zombie matches the monster name exactly\n" "acidic zo* matches monsters beginning with 'acidic zo'\n" "*mbie matches monsters ending with 'mbie'\n" "*cid*zo*ie multiple * are allowed\n" "AcI*zO*iE case insensitive search" ) ); draw_border( w_help ); wrefresh( w_help ); current_tab[line].rule = wildcard_trim_rule( string_input_popup() .title( _( "Safe Mode Rule:" ) ) .width( 30 ) .text( current_tab[line].rule ) .query_string() ); } else if( column == COLUMN_WHITE_BLACKLIST ) { current_tab[line].whitelist = !current_tab[line].whitelist; } else if( column == COLUMN_ATTITUDE ) { auto &attitude = current_tab[line].attitude; switch( attitude ) { case Creature::A_HOSTILE: attitude = Creature::A_NEUTRAL; break; case Creature::A_NEUTRAL: attitude = Creature::A_FRIENDLY; break; case Creature::A_FRIENDLY: attitude = Creature::A_ANY; break; case Creature::A_ANY: attitude = Creature::A_HOSTILE; } } else if( column == COLUMN_PROXIMITY && !current_tab[line].whitelist ) { const auto text = string_input_popup() .title( _( "Proximity Distance (0=max viewdistance)" ) ) .width( 4 ) .text( to_string( current_tab[line].proximity ) ) .description( _( "Option: " ) + to_string( get_option<int>( "SAFEMODEPROXIMITY" ) ) + " " + get_options().get_option( "SAFEMODEPROXIMITY" ).getDefaultText() ) .max_length( 3 ) .only_digits( true ) .query_string(); if( text.empty() ) { current_tab[line].proximity = get_option<int>( "SAFEMODEPROXIMITY" ); } else { //Let the options class handle the validity of the new value auto temp_option = get_options().get_option( "SAFEMODEPROXIMITY" ); temp_option.setValue( text ); current_tab[line].proximity = atoi( temp_option.getValue().c_str() ); } } } else if( action == "ENABLE_RULE" && !current_tab.empty() ) { changes_made = true; current_tab[line].active = true; } else if( action == "DISABLE_RULE" && !current_tab.empty() ) { changes_made = true; current_tab[line].active = false; } else if( action == "LEFT" ) { column--; if( column < 0 ) { column = num_columns - 1; } } else if( action == "RIGHT" ) { column++; if( column >= num_columns ) { column = 0; } } else if( action == "MOVE_RULE_UP" && !current_tab.empty() ) { changes_made = true; if( line < ( int )current_tab.size() - 1 ) { std::swap( current_tab[line], current_tab[line + 1] ); line++; column = 0; } } else if( action == "MOVE_RULE_DOWN" && !current_tab.empty() ) { changes_made = true; if( line > 0 ) { std::swap( current_tab[line], current_tab[line - 1] ); line--; column = 0; } } else if( action == "TEST_RULE" && !current_tab.empty() ) { test_pattern( tab, line ); } else if( action == "SWITCH_SAFEMODE_OPTION" ) { get_options().get_option( "SAFEMODE" ).setNext(); get_options().save(); } } if( !changes_made ) { return; } if( query_yn( _( "Save changes?" ) ) ) { if( is_safemode_in ) { save_global(); if( !g->u.name.empty() ) { save_character(); } } else { create_rules(); } } else { global_rules = global_rules_old; character_rules = character_rules_old; } }
void game::wishitem( player *p, int x, int y, int z) { if ( p == NULL && x <= 0 ) { debugmsg("game::wishitem(): invalid parameters"); return; } const std::vector<std::string> standard_itype_ids = item_controller->get_all_itype_ids(); int prev_amount, amount = 1; uimenu wmenu; wmenu.w_x = 0; wmenu.w_width = TERMX; wmenu.pad_right = ( TERMX / 2 > 40 ? TERMX - 40 : TERMX / 2 ); wmenu.return_invalid = true; wmenu.selected = uistate.wishitem_selected; wish_item_callback *cb = new wish_item_callback( standard_itype_ids ); wmenu.callback = cb; for (size_t i = 0; i < standard_itype_ids.size(); i++) { item ity( standard_itype_ids[i], 0 ); wmenu.addentry( i, true, 0, string_format( _( "%.*s" ), wmenu.pad_right - 5, ity.tname( 1, false ).c_str() ) ); wmenu.entries[i].extratxt.txt = string_format("%c", ity.symbol()); wmenu.entries[i].extratxt.color = ity.color(); wmenu.entries[i].extratxt.left = 1; } do { wmenu.query(); if ( wmenu.ret >= 0 ) { item granted(standard_itype_ids[wmenu.ret], calendar::turn); prev_amount = amount; if (p != NULL) { amount = std::atoi( string_input_popup(_("How many?"), 20, to_string( amount ), granted.tname()).c_str()); } if (dynamic_cast<wish_item_callback *>(wmenu.callback)->incontainer) { granted = granted.in_its_container(); } if ( p != NULL ) { for (int i = 0; i < amount; i++) { p->i_add(granted); } p->invalidate_crafting_inventory(); } else if ( x >= 0 && y >= 0 ) { m.add_item_or_charges( tripoint( x, y, z ), granted); wmenu.keypress = 'q'; } if ( amount > 0 ) { dynamic_cast<wish_item_callback *>(wmenu.callback)->msg = _("Wish granted. Wish for more or hit 'q' to quit."); } uistate.wishitem_selected = wmenu.ret; if ( !amount ) { amount = prev_amount; } } } while ( wmenu.keypress != 'q' && wmenu.keypress != KEY_ESCAPE && wmenu.keypress != ' ' ); delete wmenu.callback; wmenu.callback = NULL; return; }
int main() { init_environment(); init_data(); init_display(); cuss::interface i_editor; Window w_editor(0, 0, 80, 24); if (!i_editor.load_from_file("cuss/i_element_ed.cuss")) { debugmsg("Can't load cuss/i_element_ed.cuss"); end_display(); return 1; } std::vector<std::string> element_names; std::vector<std::string> transform_names; std::vector<std::string> damage_names; i_editor.ref_data("list_elements", &element_names); i_editor.ref_data("list_transformations", &transform_names); i_editor.ref_data("list_damagetypes", &damage_names); i_editor.select ("list_elements"); bool quit = false; while (!quit) { element_names = get_names(); cuss::element* selected = i_editor.selected(); int ele_num = i_editor.get_int("list_elements"); element* cur_element = NULL; if (ele_num < ELEMENTS_POOL.size()) { cur_element = &(ELEMENTS_POOL[ele_num]); std::stringstream color_data; color_data << "<c=" << color_tag_name(cur_element->color) << ">************<c=/>"; i_editor.set_data("text_color", color_data.str()); i_editor.ref_data("entry_name", &(cur_element->name)); } transform_names = get_tra_names(cur_element); damage_names = get_dmg_names(cur_element); i_editor.draw(&w_editor); long ch = getch(); if (selected->name == "entry_name" && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == ' ')) { cur_element->name += ch; } else if (selected->name == "entry_name" && is_backspace(ch) && !cur_element->name.empty()) { cur_element->name = cur_element->name.substr( 0, cur_element->name.length() - 1); } else if (ch == 's' || ch == 'S') { quit = true; } else if (ch == 'c' || ch == 'C') { change_color(cur_element); } else if (ch == 'a' || ch == 'A') { // Where are we adding an element? if (selected->name == "list_elements") { element tmp; tmp.name = string_input_popup("Name:"); ELEMENTS_POOL.push_back(tmp); i_editor.set_data("list_elements", 999); } else if (selected->name == "list_transformations") { add_transformation(cur_element); i_editor.set_data("list_transformations", 0); } else if (selected->name == "list_damagetypes") { add_damagetype(cur_element); i_editor.set_data("list_damagetypes", 0); } } else if (ch == 'd' || ch == 'D') { // Where are we deleting an element? if (selected->name == "list_elements" && cur_element) { delete_element(ele_num); i_editor.set_data("list_elements", 0); } else if (selected->name == "list_transformations") { int index = i_editor.get_int("list_transformations"); if (cur_element && index >= 0 && index < cur_element->transformations.size()) { delete_transformation(cur_element, index); i_editor.set_data("list_transformations", 0); } } else if (selected->name == "list_damagetypes") { int index = i_editor.get_int("list_damagetypes"); if (cur_element && index >= 0 && index < cur_element->damages.size()) { delete_damagetype(cur_element, index); i_editor.set_data("list_damagetypes", 0); } } } else { i_editor.handle_action(ch); } } // while (!quit) save_data(); end_display(); return 0; }