void check_constructions() { for( size_t i = 0; i < constructions.size(); i++ ) { const construction *c = &constructions[ i ]; const std::string display_name = std::string("construction ") + c->description; // Note: print the description as the id is just a generated number, // the description can be searched for in the json files. if( !c->skill.is_valid() ) { debugmsg("Unknown skill %s in %s", c->skill.c_str(), display_name.c_str()); } if( !c->requirements.is_valid() ) { debugmsg( "construction %s has missing requirement data %s", display_name.c_str(), c->requirements.c_str() ); } if( !c->pre_terrain.empty() ) { if( c->pre_is_furniture ) { if( !furn_str_id( c->pre_terrain ).is_valid() ) { debugmsg("Unknown pre_terrain (furniture) %s in %s", c->pre_terrain.c_str(), display_name.c_str() ); } } else if( !ter_str_id( c->pre_terrain ).is_valid() ) { debugmsg("Unknown pre_terrain (terrain) %s in %s", c->pre_terrain.c_str(), display_name.c_str()); } } if( !c->post_terrain.empty() ) { if( c->post_is_furniture ) { if( !furn_str_id( c->post_terrain ).is_valid() ) { debugmsg("Unknown post_terrain (furniture) %s in %s", c->post_terrain.c_str(), display_name.c_str()); } } else if( !ter_str_id( c->post_terrain ).is_valid() ) { debugmsg("Unknown post_terrain (terrain) %s in %s", c->post_terrain.c_str(), display_name.c_str()); } } if( c->id != i ) { debugmsg( "Construction \"%s\" has id %d, but should have %d", c->description.c_str(), c->id, i ); } } }
bool map_bash_info::load( JsonObject &jsobj, const std::string &member, bool is_furniture ) { if( !jsobj.has_object( member ) ) { return false; } JsonObject j = jsobj.get_object( member ); str_min = j.get_int( "str_min", 0 ); str_max = j.get_int( "str_max", 0 ); str_min_blocked = j.get_int( "str_min_blocked", -1 ); str_max_blocked = j.get_int( "str_max_blocked", -1 ); str_min_supported = j.get_int( "str_min_supported", -1 ); str_max_supported = j.get_int( "str_max_supported", -1 ); explosive = j.get_int( "explosive", -1 ); sound_vol = j.get_int( "sound_vol", -1 ); sound_fail_vol = j.get_int( "sound_fail_vol", -1 ); collapse_radius = j.get_int( "collapse_radius", 1 ); destroy_only = j.get_bool( "destroy_only", false ); bash_below = j.get_bool( "bash_below", false ); sound = j.get_string( "sound", _( "smash!" ) ); sound_fail = j.get_string( "sound_fail", _( "thump!" ) ); if( is_furniture ) { furn_set = furn_str_id( j.get_string( "furn_set", "f_null" ) ); } else { ter_set = ter_str_id( j.get_string( "ter_set" ) ); } if( j.has_member( "items" ) ) { JsonIn &stream = *j.get_raw( "items" ); drop_group = item_group::load_item_group( stream, "collection" ); } else { drop_group = "EMPTY_GROUP"; } if( j.has_array( "tent_centers" ) ) { load_map_bash_tent_centers( j.get_array( "tent_centers" ), tent_centers ); } return true; }
void complete_construction() { player &u = g->u; const construction &built = constructions[u.activity.index]; u.practice( built.skill, ( int )( ( 10 + 15 * built.difficulty ) * ( 1 + built.time / 30000.0 ) ), ( int )( built.difficulty * 1.25 ) ); // Friendly NPCs gain exp from assisting or watching... for( auto &elem : g->u.get_crafting_helpers() ) { //If the NPC can understand what you are doing, they gain more exp if (elem->get_skill_level(built.skill) >= built.difficulty){ elem->practice( built.skill, (int)( (10 + 15*built.difficulty) * (1 + built.time/30000.0) ), (int)(built.difficulty * 1.25) ); add_msg(m_info, _("%s assists you with the work..."), elem->name.c_str()); //NPC near you isn't skilled enough to help } else { elem->practice( built.skill, (int)( (10 + 15*built.difficulty) * (1 + built.time/30000.0) ), (int)(built.difficulty * 1.25) ); add_msg(m_info, _("%s watches you work..."), elem->name.c_str()); } } for( const auto &it : built.requirements->get_components() ) { u.consume_items( it ); } for( const auto &it : built.requirements->get_tools() ) { u.consume_tools( it ); } // Make the terrain change const tripoint terp = u.activity.placement; if( !built.post_terrain.empty() ) { if( built.post_is_furniture ) { g->m.furn_set( terp, furn_str_id( built.post_terrain ) ); } else { g->m.ter_set( terp, ter_str_id( built.post_terrain ) ); } } // clear the activity u.activity.set_to_null(); // This comes after clearing the activity, in case the function interrupts // activities built.post_special( terp ); }
bool map_deconstruct_info::load( JsonObject &jsobj, const std::string &member, bool is_furniture ) { if (!jsobj.has_object(member)) { return false; } JsonObject j = jsobj.get_object(member); furn_set = furn_str_id( j.get_string("furn_set", "f_null" ) ); if (!is_furniture) { ter_set = ter_str_id( j.get_string( "ter_set" ) ); } can_do = true; JsonIn& stream = *j.get_raw( "items" ); drop_group = item_group::load_item_group( stream, "collection" ); return true; }
void sfx::do_obstacle() { int heard_volume = sfx::get_heard_volume( g->u.pos() ); const auto terrain = g->m.ter( g->u.pos() ).id(); static std::set<ter_str_id> const water = { ter_str_id( "t_water_sh" ), ter_str_id( "t_water_dp" ), ter_str_id( "t_swater_sh" ), ter_str_id( "t_swater_dp" ), ter_str_id( "t_water_pool" ), ter_str_id( "t_sewage" ), }; if( water.count( terrain ) > 0 ) { return; } else { play_variant_sound( "plmove", "clear_obstacle", heard_volume, 0, 0.8, 1.2 ); } }
void sfx::do_footstep() { end_sfx_timestamp = std::chrono::high_resolution_clock::now(); sfx_time = end_sfx_timestamp - start_sfx_timestamp; if( std::chrono::duration_cast<std::chrono::milliseconds> ( sfx_time ).count() > 400 ) { int heard_volume = sfx::get_heard_volume( g->u.pos() ); const auto terrain = g->m.ter( g->u.pos() ).id(); static std::set<ter_str_id> const grass = { ter_str_id( "t_grass" ), ter_str_id( "t_shrub" ), ter_str_id( "t_underbrush" ), }; static std::set<ter_str_id> const dirt = { ter_str_id( "t_dirt" ), ter_str_id( "t_sand" ), ter_str_id( "t_dirtfloor" ), ter_str_id( "t_palisade_gate_o" ), ter_str_id( "t_sandbox" ), }; static std::set<ter_str_id> const metal = { ter_str_id( "t_ov_smreb_cage" ), ter_str_id( "t_metal_floor" ), ter_str_id( "t_grate" ), ter_str_id( "t_bridge" ), ter_str_id( "t_elevator" ), ter_str_id( "t_guardrail_bg_dp" ), }; static std::set<ter_str_id> const water = { ter_str_id( "t_water_sh" ), ter_str_id( "t_water_dp" ), ter_str_id( "t_swater_sh" ), ter_str_id( "t_swater_dp" ), ter_str_id( "t_water_pool" ), ter_str_id( "t_sewage" ), }; static std::set<ter_str_id> const chain_fence = { ter_str_id( "t_chainfence_h" ), ter_str_id( "t_chainfence_v" ), }; if( !g->u.wearing_something_on( bp_foot_l ) ) { play_variant_sound( "plmove", "walk_barefoot", heard_volume, 0, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); return; } else if( grass.count( terrain ) > 0 ) { play_variant_sound( "plmove", "walk_grass", heard_volume, 0, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); return; } else if( dirt.count( terrain ) > 0 ) { play_variant_sound( "plmove", "walk_dirt", heard_volume, 0, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); return; } else if( metal.count( terrain ) > 0 ) { play_variant_sound( "plmove", "walk_metal", heard_volume, 0, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); return; } else if( water.count( terrain ) > 0 ) { play_variant_sound( "plmove", "walk_water", heard_volume, 0, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); return; } else if( chain_fence.count( terrain ) > 0 ) { play_variant_sound( "plmove", "clear_obstacle", heard_volume, 0, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); return; } else { play_variant_sound( "plmove", "walk_tarmac", heard_volume, 0, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); return; } } }
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(); }