diff --git a/data/raw/keybindings.json b/data/raw/keybindings.json index 317c92cbd6453..f75dde5d547f3 100644 --- a/data/raw/keybindings.json +++ b/data/raw/keybindings.json @@ -186,6 +186,18 @@ "name": "Page down", "bindings": [ { "input_method": "keyboard", "key": "NPAGE" } ] }, + { + "type": "keybinding", + "id": "SCROLL_UP", + "name": "Scroll up", + "bindings": [ { "input_method": "mouse", "key": "SCROLL_UP" } ] + }, + { + "type": "keybinding", + "id": "SCROLL_DOWN", + "name": "Scroll down", + "bindings": [ { "input_method": "mouse", "key": "SCROLL_DOWN" } ] + }, { "type": "keybinding", "id": "SELECT_ALL", @@ -1221,16 +1233,13 @@ { "type": "keybinding", "id": "CONFIRM_QUIT", - "category": "EDITMAP_TERRAIN", + "category": "EDITMAP_FEATURE", "name": "Confirm & quit", - "bindings": [ { "input_method": "keyboard", "key": "g" } ] - }, - { - "type": "keybinding", - "id": "CONFIRM_QUIT", - "category": "EDITMAP_TRAPS", - "name": "Confirm & quit", - "bindings": [ { "input_method": "keyboard", "key": "t" } ] + "bindings": [ + { "input_method": "keyboard", "key": "e" }, + { "input_method": "keyboard", "key": "u" }, + { "input_method": "keyboard", "key": "t" } + ] }, { "type": "keybinding", @@ -1243,7 +1252,7 @@ "type": "keybinding", "id": "START", "category": "EDITMAP_SHAPE", - "name": "Select shape", + "name": "To start", "bindings": [ { "input_method": "keyboard", "key": "z" } ] }, { @@ -1283,8 +1292,15 @@ "type": "keybinding", "id": "EDIT_TERRAIN", "category": "EDITMAP", - "name": "Edit terrain / furniture", - "bindings": [ { "input_method": "keyboard", "key": "g" } ] + "name": "Edit terrain", + "bindings": [ { "input_method": "keyboard", "key": "e" } ] + }, + { + "type": "keybinding", + "id": "EDIT_FURNITURE", + "category": "EDITMAP", + "name": "Edit furniture", + "bindings": [ { "input_method": "keyboard", "key": "u" } ] }, { "type": "keybinding", diff --git a/src/catacharset.cpp b/src/catacharset.cpp index 1f12aa72a35bb..8d21e1bc06794 100644 --- a/src/catacharset.cpp +++ b/src/catacharset.cpp @@ -5,6 +5,7 @@ #include #include "options.h" +#include "output.h" #include "wcwidth.h" #if defined(_WIN32) @@ -140,26 +141,17 @@ std::string utf32_to_utf8( uint32_t ch ) //CJK characters have a width of 2, etc int utf8_width( const char *s, const bool ignore_tags ) { + if( ignore_tags ) { + return utf8_width( remove_color_tags( s ) ); + } int len = strlen( s ); const char *ptr = s; int w = 0; - bool inside_tag = false; while( len > 0 ) { uint32_t ch = UTF8_getch( &ptr, &len ); if( ch == UNKNOWN_UNICODE ) { continue; } - if( ignore_tags ) { - if( ch == '<' ) { - inside_tag = true; - } else if( ch == '>' ) { - inside_tag = false; - continue; - } - if( inside_tag ) { - continue; - } - } w += mk_wcwidth( ch ); } return w; diff --git a/src/editmap.cpp b/src/editmap.cpp index 05c4770d25692..a47ce427d7201 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "avatar.h" #include "calendar.h" @@ -48,16 +49,12 @@ #include "string_id.h" #include "colony.h" -#define dbg(x) DebugLog((DebugLevel)(x),D_GAME) << __FILE__ << ":" << __LINE__ << ": " - static constexpr tripoint editmap_boundary_min( 0, 0, -OVERMAP_DEPTH ); static constexpr tripoint editmap_boundary_max( MAPSIZE_X, MAPSIZE_Y, OVERMAP_HEIGHT + 1 ); static constexpr box editmap_boundaries( editmap_boundary_min, editmap_boundary_max ); static const ter_id undefined_ter_id( -1 ); -static const furn_id undefined_furn_id( -1 ); -static const trap_id undefined_trap_id( -1 ); static std::vector fld_string( const std::string &str, int width ) { @@ -177,24 +174,10 @@ editmap::editmap() height = TERMY; offsetX = VIEW_OFFSET_X; infoHeight = 0; - sel_ter = undefined_ter_id; - target_ter = undefined_ter_id; - sel_frn = undefined_furn_id; - target_frn = undefined_furn_id; - ter_frn_mode = 0; - cur_field = nullptr; - cur_trap = tr_null; sel_field = -1; sel_field_intensity = -1; - sel_trap = undefined_trap_id; - fsel = undefined_furn_id; - fset = undefined_furn_id; - trsel = undefined_trap_id; - trset = undefined_trap_id; w_info = catacurses::window(); - w_help = catacurses::window(); - padding = std::string( std::max( 0, width - 2 ), ' ' ); blink = false; altblink = false; moveall = false; @@ -202,11 +185,6 @@ editmap::editmap() refresh_mplans = true; tmax = point( getmaxx( g->w_terrain ), getmaxy( g->w_terrain ) ); - fids[fd_null] = "-clear-"; - fids[fd_fire_vent] = "fire_vent"; - fids[fd_push_items] = "push_items"; - fids[fd_shock_vent] = "shock_vent"; - fids[fd_acid_vent] = "acid_vent"; target_list.clear(); hilights.clear(); hilights["mplan"].blink_interval.push_back( true ); @@ -273,14 +251,6 @@ tripoint editmap::pos2screen( const tripoint &p ) return p + tmax / 2 - target.xy(); } -/* - * screen position to map position - */ -tripoint editmap::screen2pos( const tripoint &p ) -{ - return p + target.xy() + point( -POSX, -POSY ); -} - /* * get_direction with extended moving via HJKL keys */ @@ -313,31 +283,6 @@ bool editmap::eget_direction( tripoint &p, const std::string &action ) const return true; } -/* - * update the help text, which hijacks w_info's bottom border - */ -void editmap::uphelp( const std::string &txt1, const std::string &txt2, const std::string &title ) -{ - - if( !txt1.empty() ) { - mvwprintw( w_help, point_zero, padding ); - mvwprintw( w_help, point_south, padding ); - mvwprintw( w_help, point( 0, !txt2.empty() ? 0 : 1 ), txt1 ); - if( !txt2.empty() ) { - mvwprintw( w_help, point_south, txt2 ); - } - } - if( !title.empty() ) { - int hwidth = getmaxx( w_help ); - mvwhline( w_help, point( 0, 2 ), LINE_OXOX, hwidth ); - int starttxt = static_cast( ( hwidth - title.size() - 4 ) / 2 ); - mvwprintw( w_help, point( starttxt, 2 ), "< " ); - wprintz( w_help, c_cyan, title ); - wprintw( w_help, " >" ); - } - wrefresh( w_help ); -} - cata::optional editmap::edit() { target = g->u.pos() + g->u.view_offset; @@ -353,6 +298,7 @@ cata::optional editmap::edit() ctxt.register_action( "EDIT_TRAPS" ); ctxt.register_action( "EDIT_FIELDS" ); ctxt.register_action( "EDIT_TERRAIN" ); + ctxt.register_action( "EDIT_FURNITURE" ); ctxt.register_action( "EDIT_OVERMAP" ); ctxt.register_action( "EDIT_ITEMS" ); ctxt.register_action( "EDIT_MONSTER" ); @@ -367,10 +313,6 @@ cata::optional editmap::edit() infoHeight = 20; w_info = catacurses::newwin( infoHeight, width, point( offsetX, TERMY - infoHeight ) ); - w_help = catacurses::newwin( 3, width, point( offsetX + 1, TERMY - 3 ) ); - for( int i = 0; i < getmaxx( w_help ); i++ ) { - mvwaddch( w_help, point( i, 2 ), LINE_OXOX ); - } do { if( target_list.empty() ) { target_list.push_back( target ); // 'editmap.target_list' always has point 'editmap.target' at least @@ -378,20 +320,32 @@ cata::optional editmap::edit() if( target_list.size() == 1 ) { origin = target; // 'editmap.origin' only makes sense if we have a list of target points. } - update_view( true ); - uphelp( pgettext( "map editor", "[t]rap, [f]ield, [HJKL] move++, [v] showall" ), - pgettext( "map editor", "[g] terrain/furn, [o] mapgen, [i]tems, [q]uit" ), - pgettext( "map editor state", "Looking around" ) ); + // \u00A0 is the non-breaking space + update_view_with_help( string_format( pgettext( "keybinding descriptions", + "%s, %s, [%s,%s,%s,%s]\u00A0fast scroll, %s, %s, %s, %s, %s, %s" ), + ctxt.describe_key_and_name( "EDIT_TRAPS" ), + ctxt.describe_key_and_name( "EDIT_FIELDS" ), + ctxt.get_desc( "LEFT_WIDE", 1 ), ctxt.get_desc( "RIGHT_WIDE", 1 ), + ctxt.get_desc( "UP_WIDE", 1 ), ctxt.get_desc( "DOWN_WIDE", 1 ), + ctxt.describe_key_and_name( "EDITMAP_SHOW_ALL" ), + ctxt.describe_key_and_name( "EDIT_TERRAIN" ), + ctxt.describe_key_and_name( "EDIT_FURNITURE" ), + ctxt.describe_key_and_name( "EDIT_OVERMAP" ), + ctxt.describe_key_and_name( "EDIT_ITEMS" ), + ctxt.describe_key_and_name( "QUIT" ) ), pgettext( "map editor state", "Looking around" ) ); + action = ctxt.handle_input( BLINK_SPEED ); if( action == "EDIT_TERRAIN" ) { - edit_ter(); + edit_feature(); + } else if( action == "EDIT_FURNITURE" ) { + edit_feature(); } else if( action == "EDIT_FIELDS" ) { edit_fld(); } else if( action == "EDIT_ITEMS" ) { edit_itm(); } else if( action == "EDIT_TRAPS" ) { - edit_trp(); + edit_feature(); } else if( action == "EDITMAP_SHOW_ALL" ) { uberdraw = !uberdraw; } else if( action == "EDIT_MONSTER" ) { @@ -423,11 +377,6 @@ cata::optional editmap::edit() return cata::nullopt; } -// pending radiation / misc edit -enum edit_drawmode { - drawmode_default, drawmode_radiation, -}; - /* * This is like game::draw_ter except it completely ignores line of sight, lighting, boomered, etc. * This is a map editor after all. @@ -484,28 +433,12 @@ void editmap::uber_draw_ter( const catacurses::window &w, map *m ) refresh_mplans = false; } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///// redraw map and info (or not) -void editmap::update_view( bool update_info ) -{ - // Debug helper 2, child of debug helper - // Gather useful data - int veh_in = -1; - const optional_vpart_position vp = g->m.veh_at( target ); - if( vp ) { - veh_in = vp->is_inside(); - } - target_ter = g->m.ter( target ); - const ter_t &terrain_type = target_ter.obj(); - target_frn = g->m.furn( target ); - const furn_t &furniture_type = target_frn.obj(); - - cur_field = &g->m.get_field( target ); - cur_trap = g->m.tr_at( target ).loadid; +void editmap::update_view_with_help( const std::string &txt, const std::string &title ) +{ + // updating map const Creature *critter = g->critter_at( target ); - // update map always werase( g->w_terrain ); if( uberdraw ) { @@ -573,119 +506,140 @@ void editmap::update_view( bool update_info ) wrefresh( g->w_terrain ); g->draw_panels(); - if( update_info ) { // only if requested; this messes up windows layered on top - int off = 1; - draw_border( w_info ); + // updating info + werase( w_info ); - mvwprintz( w_info, point( 2, 0 ), c_light_gray, "< %d,%d >", target.x, target.y ); - for( int i = 1; i < infoHeight - 2; i++ ) { // clear window - mvwprintz( w_info, point( 1, i ), c_white, padding ); - } + const optional_vpart_position vp = g->m.veh_at( target ); + std::string veh_msg; + if( !vp ) { + veh_msg = pgettext( "vehicle", "no" ); + } else if( vp->is_inside() ) { + veh_msg = pgettext( "vehicle", "in" ); + } else { + veh_msg = pgettext( "vehicle", "out" ); + } - mvwputch( w_info, point( 2, off ), terrain_type.color(), terrain_type.symbol() ); - mvwprintw( w_info, point( 4, off ), _( "%d: %s; movecost %d" ), g->m.ter( target ).to_i(), - terrain_type.name(), - terrain_type.movecost + const ter_t &terrain_type = g->m.ter( target ).obj(); + const furn_t &furniture_type = g->m.furn( target ).obj(); + + int off = 1; + draw_border( w_info ); + + mvwprintz( w_info, point( 2, 0 ), c_light_gray, "< %d,%d >", target.x, target.y ); + + mvwputch( w_info, point( 2, off ), terrain_type.color(), terrain_type.symbol() ); + mvwprintw( w_info, point( 4, off ), _( "%d: %s; movecost %d" ), g->m.ter( target ).to_i(), + terrain_type.name(), + terrain_type.movecost + ); + off++; // 2 + if( g->m.furn( target ) > 0 ) { + mvwputch( w_info, point( 2, off ), furniture_type.color(), furniture_type.symbol() ); + mvwprintw( w_info, point( 4, off ), _( "%d: %s; movecost %d movestr %d" ), + g->m.furn( target ).to_i(), + furniture_type.name(), + furniture_type.movecost, + furniture_type.move_str_req ); - off++; // 2 - if( g->m.furn( target ) > 0 ) { - mvwputch( w_info, point( 2, off ), furniture_type.color(), furniture_type.symbol() ); - mvwprintw( w_info, point( 4, off ), _( "%d: %s; movecost %d movestr %d" ), - g->m.furn( target ).to_i(), - furniture_type.name(), - furniture_type.movecost, - furniture_type.move_str_req - ); - off++; // 3 - } - const auto &map_cache = g->m.get_cache( target.z ); - - mvwprintw( w_info, point( 1, off++ ), _( "dist: %d u_see: %d v_in: %d scent: %d" ), - rl_dist( g->u.pos(), target ), static_cast( g->u.sees( target ) ), - veh_in, g->scent.get( target ) ); - mvwprintw( w_info, point( 1, off++ ), _( "sight_range: %d, daylight_sight_range: %d," ), - g->u.sight_range( g->light_level( g->u.posz() ) ), - g->u.sight_range( current_daylight_level( calendar::turn ) ) ); - mvwprintw( w_info, point( 1, off++ ), _( "transparency: %.5f, visibility: %.5f," ), - map_cache.transparency_cache[target.x][target.y], - map_cache.seen_cache[target.x][target.y] ); - map::apparent_light_info al = map::apparent_light_helper( map_cache, target ); - int apparent_light = static_cast( - g->m.apparent_light_at( target, g->m.get_visibility_variables_cache() ) ); - mvwprintw( w_info, point( 1, off++ ), _( "outside: %d obstructed: %d" ), - static_cast( g->m.is_outside( target ) ), - static_cast( al.obstructed ) ); - mvwprintw( w_info, point( 1, off++ ), _( "light_at: %s" ), - map_cache.lm[target.x][target.y].to_string() ); - mvwprintw( w_info, point( 1, off++ ), _( "apparent light: %.5f (%d)" ), - al.apparent_light, apparent_light ); - std::string extras; - if( veh_in >= 0 ) { - extras += _( " [vehicle]" ); - } - if( g->m.has_flag( TFLAG_INDOORS, target ) ) { - extras += _( " [indoors]" ); - } - if( g->m.has_flag( TFLAG_SUPPORTS_ROOF, target ) ) { - extras += _( " [roof]" ); - } + off++; // 3 + } + const auto &map_cache = g->m.get_cache( target.z ); + + const std::string u_see_msg = g->u.sees( target ) ? _( "yes" ) : _( "no" ); + mvwprintw( w_info, point( 1, off++ ), _( "dist: %d u_see: %s veh: %s scent: %d" ), + rl_dist( g->u.pos(), target ), u_see_msg, veh_msg, g->scent.get( target ) ); + mvwprintw( w_info, point( 1, off++ ), _( "sight_range: %d, daylight_sight_range: %d," ), + g->u.sight_range( g->light_level( g->u.posz() ) ), + g->u.sight_range( current_daylight_level( calendar::turn ) ) ); + mvwprintw( w_info, point( 1, off++ ), _( "transparency: %.5f, visibility: %.5f," ), + map_cache.transparency_cache[target.x][target.y], + map_cache.seen_cache[target.x][target.y] ); + map::apparent_light_info al = map::apparent_light_helper( map_cache, target ); + int apparent_light = static_cast( + g->m.apparent_light_at( target, g->m.get_visibility_variables_cache() ) ); + mvwprintw( w_info, point( 1, off++ ), _( "outside: %d obstructed: %d" ), + static_cast( g->m.is_outside( target ) ), + static_cast( al.obstructed ) ); + mvwprintw( w_info, point( 1, off++ ), _( "light_at: %s" ), + map_cache.lm[target.x][target.y].to_string() ); + mvwprintw( w_info, point( 1, off++ ), _( "apparent light: %.5f (%d)" ), + al.apparent_light, apparent_light ); + std::string extras; + if( vp ) { + extras += _( " [vehicle]" ); + } + if( g->m.has_flag( TFLAG_INDOORS, target ) ) { + extras += _( " [indoors]" ); + } + if( g->m.has_flag( TFLAG_SUPPORTS_ROOF, target ) ) { + extras += _( " [roof]" ); + } - mvwprintw( w_info, point( 1, off ), "%s %s", g->m.features( target ).c_str(), extras ); - // 9 - off++; - for( auto &fld : *cur_field ) { - const field_entry &cur = fld.second; - mvwprintz( w_info, point( 1, off ), cur.color(), - _( "field: %s L:%d[%s] A:%d" ), - cur.get_field_type().id().c_str(), - cur.get_field_intensity(), - cur.name(), - to_turns( cur.get_field_age() ) - ); - off++; // 10ish - } + mvwprintw( w_info, point( 1, off ), "%s %s", g->m.features( target ), extras ); + // 9 + off++; + for( auto &fld : g->m.get_field( target ) ) { + const field_entry &cur = fld.second; + mvwprintz( w_info, point( 1, off ), cur.color(), + _( "field: %s L:%d[%s] A:%d" ), + cur.get_field_type().id().str(), + cur.get_field_intensity(), + cur.name(), + to_turns( cur.get_field_age() ) + ); + off++; // 10ish + } - if( cur_trap != tr_null ) { - auto &t = cur_trap.obj(); - mvwprintz( w_info, point( 1, off ), t.color, _( "trap: %s (%d)" ), t.name(), cur_trap.to_i() ); - off++; // 11 - } + const trap &cur_trap = g->m.tr_at( target ); + if( cur_trap.loadid != tr_null ) { + mvwprintz( w_info, point( 1, off ), cur_trap.color, _( "trap: %s (%d)" ), cur_trap.name(), + cur_trap.loadid.to_i() ); + off++; // 11 + } - if( critter != nullptr ) { - off = critter->print_info( w_info, off, 5, 1 ); - } else if( vp ) { - mvwprintw( w_info, point( 1, off ), _( "There is a %s there. Parts:" ), vp->vehicle().name ); - off++; - vp->vehicle().print_part_list( w_info, off, getmaxy( w_info ) - 1, width, vp->part_index() ); - off += 6; - } - map_stack target_stack = g->m.i_at( target ); - const int target_stack_size = target_stack.size(); - if( !g->m.has_flag( "CONTAINER", target ) && target_stack_size > 0 ) { - trim_and_print( w_info, point( 1, off ), getmaxx( w_info ), c_light_gray, - _( "There is a %s there." ), - target_stack.begin()->tname() ); + if( critter != nullptr ) { + off = critter->print_info( w_info, off, 5, 1 ); + } else if( vp ) { + mvwprintw( w_info, point( 1, off ), _( "There is a %s there. Parts:" ), vp->vehicle().name ); + off++; + vp->vehicle().print_part_list( w_info, off, getmaxy( w_info ) - 1, width, vp->part_index() ); + off += 6; + } + map_stack target_stack = g->m.i_at( target ); + const int target_stack_size = target_stack.size(); + if( !g->m.has_flag( "CONTAINER", target ) && target_stack_size > 0 ) { + trim_and_print( w_info, point( 1, off ), getmaxx( w_info ), c_light_gray, + _( "There is a %s there." ), + target_stack.begin()->tname() ); + off++; + if( target_stack_size > 1 ) { + mvwprintw( w_info, point( 1, off ), ngettext( "There is %d other item there as well.", + "There are %d other items there as well.", + target_stack_size - 1 ), + target_stack_size - 1 ); off++; - if( target_stack_size > 1 ) { - mvwprintw( w_info, point( 1, off ), ngettext( "There is %d other item there as well.", - "There are %d other items there as well.", - target_stack_size - 1 ), - target_stack_size - 1 ); - off++; - } } + } - if( g->m.has_graffiti_at( target ) ) { - mvwprintw( w_info, point( 1, off ), - g->m.ter( target ) == t_grave_new ? _( "Graffiti: %s" ) : _( "Inscription: %s" ), - g->m.graffiti_at( target ) ); - } - - wrefresh( w_info ); - - uphelp(); + if( g->m.has_graffiti_at( target ) ) { + mvwprintw( w_info, point( 1, off ), + g->m.ter( target ) == t_grave_new ? _( "Graffiti: %s" ) : _( "Inscription: %s" ), + g->m.graffiti_at( target ) ); } + // updating help + int line = getmaxy( w_info ) - 1; + if( !title.empty() ) { + const std::string str = string_format( "< %s >", title ); + center_print( w_info, line, BORDER_COLOR, str ); + } + --line; + std::vector folded = foldstring( txt, width - 2 ); + for( auto it = folded.rbegin(); it != folded.rend(); ++it, --line ) { + nc_color dummy = c_light_gray; + print_colored_text( w_info, point( 1, line ), dummy, c_light_gray, *it ); + } + wrefresh( w_info ); } static ter_id get_alt_ter( bool isvert, ter_id sel_ter ) @@ -713,338 +667,269 @@ static ter_id get_alt_ter( bool isvert, ter_id sel_ter ) return undefined_ter_id; } -/** - * Adds the delta value to the given id and adjusts for overflow out of the valid - * range [0...count-1]. - * @return Whether an overflow happened. - */ -template -bool increment( int_id &id, const int delta, const int count ) +template +static std::string info_title(); + +template<> +std::string info_title() { - const int new_id = id.to_i() + delta; - if( new_id < 0 ) { - id = int_id( new_id + count ); - return true; - } else if( new_id >= count ) { - id = int_id( new_id - count ); - return true; - } else { - id = int_id( new_id ); - return false; - } + return pgettext( "map editor state", "Terrain" ); } -template -bool would_overflow( const int_id &id, const int delta, const int count ) + +template<> +std::string info_title() { - const int new_id = id.to_i() + delta; - return new_id < 0 || new_id >= count; + return pgettext( "map editor state", "Furniture" ); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///// edit terrain type / furniture -int editmap::edit_ter() +template<> +std::string info_title() { - int ret = 0; - int pwh = TERMY - 4; + return pgettext( "map editor: traps editing", "Traps" ); +} - catacurses::window w_pickter = catacurses::newwin( pwh, width, point( offsetX, VIEW_OFFSET_Y ) ); - draw_border( w_pickter ); - wrefresh( w_pickter ); +template +static T_id feature( const tripoint &p ); - int pickh = pwh - 2; - int pickw = width - 4; +template<> +ter_id feature( const tripoint &p ) +{ + return g->m.ter( p ); +} - if( sel_ter == undefined_ter_id ) { - sel_ter = target_ter; - } +template<> +furn_id feature( const tripoint &p ) +{ + return g->m.furn( p ); +} - if( sel_frn == undefined_furn_id ) { - sel_frn = target_frn; - } +template<> +trap_id feature( const tripoint &p ) +{ + return g->m.tr_at( p ).loadid; +} - ter_id lastsel_ter = sel_ter; - furn_id lastsel_frn = sel_frn; +template +static int symbol( const T_t &t ); - const int xmin = 3; // left margin - int xmax = pickw - xmin; - int tymax = static_cast( ter_t::count() / xmax ); - if( ter_t::count() % xmax != 0 ) { - tymax++; - } - int fymax = static_cast( furn_t::count() / xmax ); - if( furn_t::count() % xmax != 0 ) { - fymax++; - } +template<> +int symbol( const ter_t &t ) +{ + return t.symbol(); +} - tripoint sel_terp = tripoint_min; // screen coordinates of current selection - tripoint lastsel_terp = tripoint_min; // and last selection - tripoint target_terp = tripoint_min; // and current tile - tripoint sel_frnp = tripoint_min; // for furniture "" - tripoint lastsel_frnp = tripoint_min; - tripoint target_frnp = tripoint_min; +template<> +int symbol( const furn_t &t ) +{ + return t.symbol(); +} - input_context ctxt( "EDITMAP_TERRAIN" ); - ctxt.register_directions(); - ctxt.register_action( "EDITMAP_SHOW_ALL" ); - ctxt.register_action( "CONFIRM" ); - ctxt.register_action( "CONFIRM_QUIT" ); - ctxt.register_action( "EDITMAP_TAB" ); - ctxt.register_action( "EDITMAP_MOVE" ); - ctxt.register_action( "QUIT" ); - ctxt.register_action( "HELP_KEYBINDINGS" ); - std::string action; +template<> +int symbol( const trap &t ) +{ + return t.sym; +} - int mode = ter_frn_mode; - do { - if( mode != ter_frn_mode ) { - mode = ter_frn_mode; - wrefresh( w_pickter ); - } +template +static nc_color color( const T_t &t ); - // cursor is green for terrain or furniture, depending on selection - nc_color c_tercurs = ter_frn_mode == 0 ? c_light_green : c_dark_gray; - nc_color c_frncurs = ter_frn_mode == 1 ? c_light_green : c_dark_gray; - - int cur_t = 0; - int tstart = 2; - // draw icon grid - for( int y = tstart; y < pickh && cur_t < static_cast( ter_t::count() ); y += 2 ) { - for( int x = xmin; x < pickw && cur_t < static_cast( ter_t::count() ); x++, cur_t++ ) { - const ter_id tid( cur_t ); - const ter_t &ttype = tid.obj(); - mvwputch( w_pickter, point( x, y ), ter_frn_mode == 0 ? ttype.color() : c_dark_gray, - ttype.symbol() ); - if( tid == sel_ter ) { - sel_terp = tripoint( x, y, target.z ); - } else if( tid == lastsel_ter ) { - lastsel_terp = tripoint( x, y, target.z ); - } else if( tid == target_ter ) { - target_terp = tripoint( x, y, target.z ); - } - } - } - // clear last cursor area - mvwputch( w_pickter, lastsel_terp.xy() + point_south_west, c_tercurs, ' ' ); - mvwputch( w_pickter, lastsel_terp.xy() + point_north_east, c_tercurs, ' ' ); - mvwputch( w_pickter, lastsel_terp.xy() + point_south_east, c_tercurs, ' ' ); - mvwputch( w_pickter, lastsel_terp.xy() + point_north_west, c_tercurs, ' ' ); - // indicate current tile - mvwputch( w_pickter, target_terp.xy() + point_south, c_light_gray, '^' ); - mvwputch( w_pickter, target_terp.xy() + point_north, c_light_gray, 'v' ); - // draw cursor around selected terrain icon - mvwputch( w_pickter, sel_terp.xy() + point_south_west, c_tercurs, LINE_XXOO ); - mvwputch( w_pickter, sel_terp.xy() + point_north_east, c_tercurs, LINE_OOXX ); - mvwputch( w_pickter, sel_terp.xy() + point_south_east, c_tercurs, LINE_XOOX ); - mvwputch( w_pickter, sel_terp.xy() + point_north_west, c_tercurs, LINE_OXXO ); - - draw_border( w_pickter ); - // calculate offset, print terrain selection info - int tlen = tymax * 2; - int off = tstart + tlen; - mvwprintw( w_pickter, point( 1, off ), padding ); - if( ter_frn_mode == 0 ) { // unless furniture is selected - const ter_t &pttype = sel_ter.obj(); - - for( int i = 1; i < width - 2; i++ ) { - mvwaddch( w_pickter, point( i, 0 ), LINE_OXOX ); - } +template<> +nc_color color( const ter_t &t ) +{ + return t.color(); +} - mvwprintw( w_pickter, point( 2, 0 ), "< %s[%d]: %s >", pttype.id.c_str(), pttype.id.id().to_i(), - pttype.name() ); - mvwprintz( w_pickter, point( 2, off ), c_white, _( "movecost %d" ), pttype.movecost ); - std::string extras; - if( pttype.has_flag( TFLAG_INDOORS ) ) { - extras += _( "[indoors] " ); - } - if( pttype.has_flag( TFLAG_SUPPORTS_ROOF ) ) { - extras += _( "[roof] " ); - } - wprintw( w_pickter, " %s", extras ); - } +template<> +nc_color color( const furn_t &t ) +{ + return t.color(); +} - off += 2; - int cur_f = 0; - int fstart = off; // calculate vertical offset, draw furniture icons - for( int y = fstart; y < pickh && cur_f < static_cast( furn_t::count() ); y += 2 ) { - for( int x = xmin; x < pickw && cur_f < static_cast( furn_t::count() ); x++, cur_f++ ) { - const furn_id fid( cur_f ); - const furn_t &ftype = fid.obj(); - mvwputch( w_pickter, point( x, y ), ter_frn_mode == 1 ? ftype.color() : c_dark_gray, - ftype.symbol() ); - - if( fid == sel_frn ) { - sel_frnp = tripoint( x, y, target.z ); - } else if( fid == lastsel_frn ) { - lastsel_frnp = tripoint( x, y, target.z ); - } else if( fid == target_frn ) { - target_frnp = tripoint( x, y, target.z ); - } - } - } +template<> +nc_color color( const trap &t ) +{ + return t.color; +} - mvwputch( w_pickter, lastsel_frnp.xy() + point_south_west, c_frncurs, ' ' ); - mvwputch( w_pickter, lastsel_frnp.xy() + point_north_east, c_frncurs, ' ' ); - mvwputch( w_pickter, lastsel_frnp.xy() + point_south_east, c_frncurs, ' ' ); - mvwputch( w_pickter, lastsel_frnp.xy() + point_north_west, c_frncurs, ' ' ); +template +static std::string describe( const T_t &t ); - mvwputch( w_pickter, target_frnp.xy() + point_south, c_light_gray, '^' ); - mvwputch( w_pickter, target_frnp.xy() + point_north, c_light_gray, 'v' ); +template<> +std::string describe( const ter_t &type ) +{ + return string_format( _( "Move cost: %d\nIndoors: %s\nRoof: %s" ), type.movecost, + type.has_flag( TFLAG_INDOORS ) ? _( "Yes" ) : _( "No" ), + type.has_flag( TFLAG_SUPPORTS_ROOF ) ? _( "Yes" ) : _( "No" ) ); +} - mvwputch( w_pickter, sel_frnp.xy() + point_south_west, c_frncurs, LINE_XXOO ); - mvwputch( w_pickter, sel_frnp.xy() + point_north_east, c_frncurs, LINE_OOXX ); - mvwputch( w_pickter, sel_frnp.xy() + point_south_east, c_frncurs, LINE_XOOX ); - mvwputch( w_pickter, sel_frnp.xy() + point_north_west, c_frncurs, LINE_OXXO ); +template<> +std::string describe( const furn_t &type ) +{ + return string_format( _( "Move cost: %d\nIndoors: %s\nRoof: %s" ), type.movecost, + type.has_flag( TFLAG_INDOORS ) ? _( "Yes" ) : _( "No" ), + type.has_flag( TFLAG_SUPPORTS_ROOF ) ? _( "Yes" ) : _( "No" ) ); +} - int flen = fymax * 2; - off += flen; - mvwprintw( w_pickter, point( 1, off ), padding ); - if( ter_frn_mode == 1 ) { - const furn_t &pftype = sel_frn.obj(); +template<> +std::string describe( const trap &type ) +{ + return string_format( _( "Visible: %d\nAvoidance: %d\nDifficulty: %d\nBenign: %s" ), + type.get_visibility(), type.get_avoidance(), type.get_difficulty(), + type.is_benign() ? _( "Yes" ) : _( "No" ) ); +} - for( int i = 1; i < width - 2; i++ ) { - mvwaddch( w_pickter, point( i, 0 ), LINE_OXOX ); - } +template +static void apply( const T_t &t, const shapetype editshape, const tripoint &target, + const tripoint &origin, const std::vector &target_list ); - mvwprintw( w_pickter, point( 2, 0 ), "< %s[%d]: %s >", pftype.id.c_str(), pftype.id.id().to_i(), - pftype.name() ); - mvwprintz( w_pickter, point( 2, off ), c_white, _( "movecost %d" ), pftype.movecost ); - std::string fextras; - if( pftype.has_flag( TFLAG_INDOORS ) ) { - fextras += _( "[indoors] " ); - } - if( pftype.has_flag( TFLAG_SUPPORTS_ROOF ) ) { - fextras += _( "[roof] " ); +template<> +void apply( const ter_t &t, const shapetype editshape, const tripoint &target, + const tripoint &origin, const std::vector &target_list ) +{ + bool isvert = false; + bool ishori = false; + bool doalt = false; + ter_id teralt = undefined_ter_id; + int alta = -1; + int altb = -1; + const ter_id sel_ter = t.id.id(); + if( editshape == editmap_rect ) { + if( t.symbol() == LINE_XOXO || t.symbol() == '|' ) { + isvert = true; + teralt = get_alt_ter( isvert, sel_ter ); + } else if( t.symbol() == LINE_OXOX || t.symbol() == '-' ) { + ishori = true; + teralt = get_alt_ter( isvert, sel_ter ); + } + if( teralt != undefined_ter_id ) { + if( isvert ) { + alta = target.y; + altb = origin.y; + } else { + alta = target.x; + altb = origin.x; } - wprintw( w_pickter, " %s", fextras ); + doalt = true; } + } - // draw green |'s around terrain or furniture tilesets depending on selection - for( int y = tstart - 1; y < tstart + tlen + 1; y++ ) { - mvwputch( w_pickter, point( 1, y ), c_light_green, ter_frn_mode == 0 ? '|' : ' ' ); - mvwputch( w_pickter, point( width - 2, y ), c_light_green, ter_frn_mode == 0 ? '|' : ' ' ); - } - for( int y = fstart - 1; y < fstart + flen + 1; y++ ) { - mvwputch( w_pickter, point( 1, y ), c_light_green, ter_frn_mode == 1 ? '|' : ' ' ); - mvwputch( w_pickter, point( width - 2, y ), c_light_green, ter_frn_mode == 1 ? '|' : ' ' ); + for( auto &elem : target_list ) { + ter_id wter = sel_ter; + if( doalt ) { + if( isvert && ( elem.y == alta || elem.y == altb ) ) { + wter = teralt; + } else if( ishori && ( elem.x == alta || elem.x == altb ) ) { + wter = teralt; + } } + g->m.ter_set( elem, wter ); + } +} - uphelp( pgettext( "Map editor: terrain/furniture shortkeys", - "[s/tab] shape select, [m]ove, [<>^v] select" ), - pgettext( "Map editor: terrain/furniture shortkeys", - "[enter] change, [g] change/quit, [q]uit, [v] showall" ), - pgettext( "Map editor: terrain/furniture editing menu", "Terrain / Furniture" ) ); - - wrefresh( w_pickter ); - - action = ctxt.handle_input(); - lastsel_ter = sel_ter; - lastsel_frn = sel_frn; - if( ter_frn_mode == 0 ) { - if( action == "LEFT" ) { - increment( sel_ter, -1, ter_t::count() ); - } else if( action == "RIGHT" ) { - increment( sel_ter, +1, ter_t::count() ); - } else if( action == "UP" ) { - if( would_overflow( sel_ter, -xmax, ter_t::count() ) ) { - ter_frn_mode = ter_frn_mode == 0 ? 1 : 0; - } else { - increment( sel_ter, -xmax, ter_t::count() ); - } - } else if( action == "DOWN" ) { - if( would_overflow( sel_ter, +xmax, ter_t::count() ) ) { - ter_frn_mode = ter_frn_mode == 0 ? 1 : 0; - } else { - increment( sel_ter, +xmax, ter_t::count() ); - } - } else if( action == "CONFIRM" || action == "CONFIRM_QUIT" ) { - bool isvert = false; - bool ishori = false; - bool doalt = false; - ter_id teralt = undefined_ter_id; - int alta = -1; - int altb = -1; - if( editshape == editmap_rect ) { - const ter_t &t = sel_ter.obj(); - if( t.symbol() == LINE_XOXO || t.symbol() == '|' ) { - isvert = true; - teralt = get_alt_ter( isvert, sel_ter ); - } else if( t.symbol() == LINE_OXOX || t.symbol() == '-' ) { - ishori = true; - teralt = get_alt_ter( isvert, sel_ter ); - } - if( teralt != undefined_ter_id ) { - if( isvert ) { - alta = target.y; - altb = origin.y; - } else { - alta = target.x; - altb = origin.x; - } - doalt = true; - } - } +template<> +void apply( const furn_t &t, const shapetype, const tripoint &, + const tripoint &, const std::vector &target_list ) +{ + const furn_id sel_frn = t.id.id(); + for( auto &elem : target_list ) { + g->m.furn_set( elem, sel_frn ); + } +} - for( auto &elem : target_list ) { - ter_id wter = sel_ter; - if( doalt ) { - if( isvert && ( elem.y == alta || elem.y == altb ) ) { - wter = teralt; - } else if( ishori && ( elem.x == alta || elem.x == altb ) ) { - wter = teralt; - } - } - g->m.ter_set( elem, wter ); - } - if( action == "CONFIRM_QUIT" ) { - break; - } - update_view( false ); - } else if( action == "EDITMAP_TAB" || action == "EDITMAP_MOVE" ) { - ter_id sel_tmp = sel_ter; - select_shape( editshape, action == "EDITMAP_MOVE" ? 1 : 0 ); - sel_ter = sel_tmp; - } else if( action == "EDITMAP_SHOW_ALL" ) { - uberdraw = !uberdraw; - update_view( false ); - } - } else { // TODO: cleanup - if( action == "LEFT" ) { - increment( sel_frn, -1, furn_t::count() ); - } else if( action == "RIGHT" ) { - increment( sel_frn, +1, furn_t::count() ); - } else if( action == "UP" ) { - if( would_overflow( sel_frn, -xmax, furn_t::count() ) ) { - ter_frn_mode = ter_frn_mode == 0 ? 1 : 0; - } else { - increment( sel_frn, -xmax, furn_t::count() ); - } - } else if( action == "DOWN" ) { - if( would_overflow( sel_frn, +xmax, furn_t::count() ) ) { - ter_frn_mode = ter_frn_mode == 0 ? 1 : 0; - } else { - increment( sel_frn, +xmax, furn_t::count() ); - } - } else if( action == "CONFIRM" || action == "CONFIRM_QUIT" ) { - for( auto &elem : target_list ) { - g->m.furn_set( elem, sel_frn ); - } - if( action == "CONFIRM_QUIT" ) { - break; - } - update_view( false ); - } else if( action == "EDITMAP_TAB" || action == "EDITMAP_MOVE" ) { - furn_id sel_frn_tmp = sel_frn; - ter_id sel_ter_tmp = sel_ter; - select_shape( editshape, action == "EDITMAP_MOVE" ? 1 : 0 ); - sel_frn = sel_frn_tmp; - sel_ter = sel_ter_tmp; - } else if( action == "EDITMAP_SHOW_ALL" ) { +template<> +void apply( const trap &t, const shapetype, const tripoint &, + const tripoint &, const std::vector &target_list ) +{ + for( auto &elem : target_list ) { + g->m.trap_set( elem, t.loadid ); + } +} + +// edit terrain, furnitrue, or traps +template +void editmap::edit_feature() +{ + if( T_t::count() == 0 ) { + debugmsg( "Empty %s list", typeid( T_t ).name() ); + return; + } + + typedef decltype( T_t().id.id() ) T_id; + + uilist emenu; + emenu.w_width = width; + emenu.w_height = TERMY - infoHeight; + emenu.w_y = 0; + emenu.w_x = offsetX; + emenu.desc_enabled = true; + emenu.input_category = "EDITMAP_FEATURE"; + emenu.additional_actions = { + { "CONFIRM_QUIT", "" }, + { "EDITMAP_SHOW_ALL", "" }, + { "EDITMAP_TAB", "" }, + { "EDITMAP_MOVE", "" }, + { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings + }; + emenu.allow_additional = true; + + for( int i = 0; i < static_cast( T_t::count() ); ++i ) { + const T_t &type = T_id( i ).obj(); + std::string name; + if( type.name().empty() ) { + name = string_format( pgettext( "map feature id", "(%s)" ), type.id.str() ); + } else { + name = string_format( pgettext( "map feature name and id", "%s (%s)" ), type.name(), + type.id.str() ); + } + uilist_entry ent( name ); + ent.retval = i; + ent.enabled = true; + ent.hotkey = 0; + ent.extratxt.sym = symbol( type ); + ent.extratxt.color = color( type ); + ent.desc = describe( type ); + + emenu.entries.emplace_back( ent ); + } + int current_feature = emenu.selected = feature( target ).to_i(); + emenu.entries[current_feature].text_color = c_green; + + bool quit = false; + do { + input_context ctxt( emenu.input_category ); + update_view_with_help( string_format( pgettext( "keybinding descriptions", "%s, %s, %s, %s, %s" ), + ctxt.describe_key_and_name( "CONFIRM" ), + ctxt.describe_key_and_name( "CONFIRM_QUIT" ), + ctxt.describe_key_and_name( "EDITMAP_SHOW_ALL" ), + ctxt.describe_key_and_name( "EDITMAP_TAB" ), + ctxt.describe_key_and_name( "EDITMAP_MOVE" ) ), + info_title() ); + emenu.query(); + if( emenu.ret == UILIST_CANCEL ) { + quit = true; + } else if( ( emenu.ret >= 0 && static_cast( emenu.ret ) < T_t::count() ) || + ( emenu.ret == UILIST_ADDITIONAL && emenu.ret_act == "CONFIRM_QUIT" ) ) { + apply( T_id( emenu.selected ).obj(), editshape, target, origin, target_list ); + emenu.entries[current_feature].text_color = emenu.text_color; + current_feature = emenu.selected; + emenu.entries[current_feature].text_color = c_green; + quit = emenu.ret == UILIST_ADDITIONAL; + } else if( emenu.ret == UILIST_ADDITIONAL ) { + if( emenu.ret_act == "EDITMAP_TAB" ) { + select_shape( editshape, 0 ); + emenu.entries[current_feature].text_color = emenu.text_color; + current_feature = feature( target ).to_i(); + emenu.entries[current_feature].text_color = c_green; + } else if( emenu.ret_act == "EDITMAP_MOVE" ) { + select_shape( editshape, 1 ); + emenu.entries[current_feature].text_color = emenu.text_color; + current_feature = feature( target ).to_i(); + emenu.entries[current_feature].text_color = c_green; + } else if( emenu.ret_act == "EDITMAP_SHOW_ALL" ) { uberdraw = !uberdraw; - update_view( false ); } } - } while( action != "QUIT" ); - return ret; + } while( !quit ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1074,37 +959,51 @@ void editmap::setup_fmenu( uilist &fmenu ) const field_type_id fid = static_cast( i ); fmenu.addentry( fid, true, -2, "" ); fmenu.entries[fid].extratxt.left = 1; - update_fmenu_entry( fmenu, *cur_field, fid ); + update_fmenu_entry( fmenu, g->m.get_field( target ), fid ); } if( sel_field >= 0 ) { fmenu.selected = sel_field; } } -int editmap::edit_fld() +void editmap::edit_fld() { - int ret = 0; uilist fmenu; fmenu.w_width = width; fmenu.w_height = TERMY - infoHeight; fmenu.w_y = 0; fmenu.w_x = offsetX; - fmenu.allow_anykey = true; setup_fmenu( fmenu ); + fmenu.input_category = "EDIT_FIELDS"; + fmenu.additional_actions = { + { "EDITMAP_TAB", "" }, + { "EDITMAP_MOVE", "" }, + { "LEFT", "" }, + { "RIGHT", "" }, + { "EDITMAP_SHOW_ALL", "" }, + { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings + }; + fmenu.allow_additional = true; do { - uphelp( pgettext( "Map editor: Field effects shortkeys", - "[s/tab] shape select, [m]ove, [<,>] intensity" ), - pgettext( "Map editor: Field effects shortkeys", "[enter] edit, [q]uit, [v] showall" ), - pgettext( "Map editor: Editing field effects", "Field effects" ) ); - - fmenu.query( false ); - if( fmenu.selected > 0 && fmenu.selected < static_cast( field_type::count() ) && - ( fmenu.ret > 0 || fmenu.keypress == KEY_LEFT || fmenu.keypress == KEY_RIGHT ) - ) { + input_context ctxt( fmenu.input_category ); + // \u00A0 is the non-breaking space + update_view_with_help( string_format( pgettext( "keybinding descriptions", + "%s, %s, [%s,%s]\u00A0intensity, %s, %s, %s" ), + ctxt.describe_key_and_name( "EDITMAP_TAB" ), + ctxt.describe_key_and_name( "EDITMAP_MOVE" ), + ctxt.get_desc( "LEFT", 1 ), ctxt.get_desc( "RIGHT", 1 ), + ctxt.describe_key_and_name( "CONFIRM" ), + ctxt.describe_key_and_name( "QUIT" ), + ctxt.describe_key_and_name( "EDITMAP_SHOW_ALL" ) ), + pgettext( "Map editor: Editing field effects", "Field effects" ) ); + fmenu.query(); + if( ( fmenu.ret > 0 && static_cast( fmenu.ret ) < field_type::count() ) || + ( fmenu.ret == UILIST_ADDITIONAL && ( fmenu.ret_act == "LEFT" || fmenu.ret_act == "RIGHT" ) ) ) { + int field_intensity = 0; const field_type_id idx = static_cast( fmenu.selected ); - field_entry *fld = cur_field->find_field( idx ); + field_entry *fld = g->m.get_field( target ).find_field( idx ); if( fld != nullptr ) { field_intensity = fld->get_field_intensity(); } @@ -1133,10 +1032,10 @@ int editmap::edit_fld() if( femenu.ret >= 0 ) { fsel_intensity = femenu.ret; } - } else if( fmenu.keypress == KEY_RIGHT && + } else if( fmenu.ret_act == "RIGHT" && field_intensity < static_cast( ftype.get_max_intensity() ) ) { fsel_intensity++; - } else if( fmenu.keypress == KEY_LEFT && field_intensity > 0 ) { + } else if( fmenu.ret_act == "LEFT" && field_intensity > 0 ) { fsel_intensity--; } if( field_intensity != fsel_intensity || target_list.size() > 1 ) { @@ -1160,11 +1059,10 @@ int editmap::edit_fld() } } } - update_fmenu_entry( fmenu, *cur_field, idx ); + update_fmenu_entry( fmenu, g->m.get_field( target ), idx ); sel_field = fmenu.selected; sel_field_intensity = fsel_intensity; } - update_view( true ); } else if( fmenu.ret == 0 ) { for( auto &elem : target_list ) { field &t_field = g->m.get_field( elem ); @@ -1176,107 +1074,28 @@ int editmap::edit_fld() } } } - update_view( true ); sel_field = fmenu.selected; sel_field_intensity = 0; - } else if( fmenu.keypress == 's' || fmenu.keypress == '\t' || fmenu.keypress == 'm' ) { - int sel_tmp = fmenu.selected; - int ret = select_shape( editshape, fmenu.keypress == 'm' ? 1 : 0 ); - if( ret > 0 ) { - setup_fmenu( fmenu ); - } - fmenu.selected = sel_tmp; - } else if( fmenu.keypress == 'v' ) { - uberdraw = !uberdraw; - update_view( false ); - } - } while( fmenu.ret != UILIST_CANCEL ); - return ret; -} -///// edit traps -int editmap::edit_trp() -{ - int ret = 0; - int pwh = TERMY - infoHeight; - - catacurses::window w_picktrap = catacurses::newwin( pwh, width, point( offsetX, VIEW_OFFSET_Y ) ); - draw_border( w_picktrap ); - int tmax = pwh - 3; - int tshift = 0; - input_context ctxt( "EDITMAP_TRAPS" ); - ctxt.register_updown(); - ctxt.register_action( "EDITMAP_SHOW_ALL" ); - ctxt.register_action( "CONFIRM" ); - ctxt.register_action( "CONFIRM_QUIT" ); - ctxt.register_action( "EDITMAP_TAB" ); - ctxt.register_action( "EDITMAP_MOVE" ); - ctxt.register_action( "QUIT" ); - ctxt.register_action( "HELP_KEYBINDINGS" ); - std::string action; - if( trsel == undefined_trap_id ) { - trsel = cur_trap; - } - int num_trap_types = trap::count(); - do { - uphelp( pgettext( "map editor: traps shortkeys", "[s/tab] shape select, [m]ove, [v] showall" ), - pgettext( "map editor: traps shortkeys", "[enter] change, [t] change/quit, [q]uit" ), - pgettext( "map editor: traps editing", "Traps" ) ); - - if( trsel.to_i() < tshift ) { - tshift = trsel.to_i(); - } else if( trsel.to_i() > tshift + tmax ) { - tshift = trsel.to_i() - tmax; - } - std::string tnam; - for( int t = tshift; t <= tshift + tmax; t++ ) { - mvwprintz( w_picktrap, point( 1, t + 1 - tshift ), c_white, padding ); - if( t < num_trap_types ) { - auto &tr = trap_id( t ).obj(); - if( tr.is_null() ) { - tnam = _( "-clear-" ); - } else { - if( tr.name().length() > 0 ) { - //~ trap editor list entry. 1st string is display name, 2nd string is internal name of trap - tnam = string_format( _( "%s (%s)" ), tr.name(), tr.id.c_str() ); - } else { - tnam = tr.id.str(); - } + } else if( fmenu.ret == UILIST_ADDITIONAL ) { + if( fmenu.ret_act == "EDITMAP_TAB" ) { + int sel_tmp = fmenu.selected; + int ret = select_shape( editshape, 0 ); + if( ret > 0 ) { + setup_fmenu( fmenu ); } - mvwputch( w_picktrap, point( 2, t + 1 - tshift ), tr.color, tr.sym ); - mvwprintz( w_picktrap, point( 4, t + 1 - tshift ), - trsel == tr.loadid ? h_white : - cur_trap == tr.loadid ? c_green : c_light_gray, - "%d %s", t, tnam.c_str() ); + fmenu.selected = sel_tmp; + } else if( fmenu.ret_act == "EDITMAP_MOVE" ) { + int sel_tmp = fmenu.selected; + int ret = select_shape( editshape, 1 ); + if( ret > 0 ) { + setup_fmenu( fmenu ); + } + fmenu.selected = sel_tmp; + } else if( fmenu.ret_act == "EDITMAP_SHOW_ALL" ) { + uberdraw = !uberdraw; } } - wrefresh( w_picktrap ); - - action = ctxt.handle_input(); - if( action == "UP" ) { - increment( trsel, -1, num_trap_types ); - } else if( action == "DOWN" ) { - increment( trsel, +1, num_trap_types ); - } else if( action == "CONFIRM" || action == "CONFIRM_QUIT" ) { - trset = trsel; - for( auto &elem : target_list ) { - g->m.trap_set( elem, trset ); - } - if( action == "CONFIRM_QUIT" ) { - break; - } - update_view( false ); - } else if( action == "EDITMAP_TAB" || action == "EDITMAP_MOVE" ) { - trap_id sel_tmp = trsel; - select_shape( editshape, action == "EDITMAP_MOVE" ? 1 : 0 ); - trsel = sel_tmp; - } else if( action == "EDITMAP_SHOW_ALL" ) { - uberdraw = !uberdraw; - update_view( false ); - } - } while( action != "QUIT" ); - - wrefresh( w_info ); - return ret; + } while( fmenu.ret != UILIST_CANCEL ); } /* @@ -1289,9 +1108,8 @@ enum editmap_imenu_ent { imenu_exit, }; -int editmap::edit_itm() +void editmap::edit_itm() { - int ret = 0; uilist ilmenu; ilmenu.w_x = offsetX; ilmenu.w_y = 0; @@ -1304,8 +1122,14 @@ int editmap::edit_itm() an_item.is_emissive() ? " L" : "" ); } ilmenu.addentry( items.size(), true, 'a', _( "Add item" ) ); + ilmenu.input_category = "EDIT_ITEMS"; + ilmenu.additional_actions = { + { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings + }; + ilmenu.allow_additional = true; do { + update_view_with_help( "", "" ); ilmenu.query(); if( ilmenu.ret >= 0 && ilmenu.ret < static_cast( items.size() ) ) { item &it = *items.get_iterator_from_index( ilmenu.ret ); @@ -1324,8 +1148,14 @@ int editmap::edit_itm() "-[ light emission ]-" ) ); imenu.addentry( imenu_savetest, true, -1, pgettext( "item manipulation debug menu entry", "savetest" ) ); + imenu.input_category = "EDIT_ITEMS"; + imenu.additional_actions = { + { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings + }; + imenu.allow_additional = true; do { + ilmenu.show(); imenu.query(); if( imenu.ret >= 0 && imenu.ret < imenu_savetest ) { int intval = -1; @@ -1369,7 +1199,6 @@ int editmap::edit_itm() wrefresh( g->w_terrain ); g->draw_panels(); } while( imenu.ret != UILIST_CANCEL ); - update_view( true ); } else if( ilmenu.ret == static_cast( items.size() ) ) { debug_menu::wishitem( nullptr, target.x, target.y, target.z ); ilmenu.entries.clear(); @@ -1380,42 +1209,36 @@ int editmap::edit_itm() } ilmenu.addentry( items.size(), true, 'a', pgettext( "item manipulation debug menu entry for adding an item on a tile", "Add item" ) ); - update_view( true ); ilmenu.setup(); ilmenu.filterlist(); ilmenu.refresh(); } } while( ilmenu.ret != UILIST_CANCEL ); - return ret; } /* * Todo */ -int editmap::edit_critter( Creature &critter ) +void editmap::edit_critter( Creature &critter ) { if( monster *const mon_ptr = dynamic_cast( &critter ) ) { edit_json( *mon_ptr ); } else if( npc *const npc_ptr = dynamic_cast( &critter ) ) { edit_json( *npc_ptr ); } - return 0; } -int editmap::edit_veh() +void editmap::edit_veh() { - int ret = 0; edit_json( g->m.veh_at( target )->vehicle() ); - return ret; } /* * Calculate target_list based on origin and target class variables, and shapetype. */ -tripoint editmap::recalc_target( shapetype shape ) +void editmap::recalc_target( shapetype shape ) { const int z = target.z; - tripoint ret = target; target_list.clear(); switch( shape ) { case editmap_circle: { @@ -1467,8 +1290,6 @@ tripoint editmap::recalc_target( shapetype shape ) target_list = line_to( origin, target, 0, 0 ); break; } - - return ret; } /* @@ -1541,15 +1362,31 @@ int editmap::select_shape( shapetype shape, int mode ) moveall = mode != 0; } altblink = moveall; - update_view( false ); + do { - uphelp( moveall ? _( "[s] resize, [y] swap" ) : - _( "[m]move, [s]hape, [y] swap, [z] to start" ), - _( "[enter] accept, [q] abort, [v] showall" ), - moveall ? _( "Moving selection" ) : _( "Resizing selection" ) ); + if( moveall ) { + update_view_with_help( string_format( pgettext( "keybinding descriptions", "%s, %s, %s, %s, %s" ), + ctxt.describe_key_and_name( "RESIZE" ), + ctxt.describe_key_and_name( "SWAP" ), + ctxt.describe_key_and_name( "CONFIRM" ), + ctxt.describe_key_and_name( "QUIT" ), + ctxt.describe_key_and_name( "EDITMAP_SHOW_ALL" ) ), + _( "Moving selection" ) ); + } else { + update_view_with_help( string_format( pgettext( "keybinding descriptions", + "%s, %s, %s, %s, %s, %s, %s" ), + ctxt.describe_key_and_name( "EDITMAP_MOVE" ), + ctxt.describe_key_and_name( "RESIZE" ), + ctxt.describe_key_and_name( "SWAP" ), + ctxt.describe_key_and_name( "START" ), + ctxt.describe_key_and_name( "CONFIRM" ), + ctxt.describe_key_and_name( "QUIT" ), + ctxt.describe_key_and_name( "EDITMAP_SHOW_ALL" ) ), + _( "Resizing selection" ) ); + } action = ctxt.handle_input( BLINK_SPEED ); if( action == "RESIZE" ) { - if( ! moveall ) { + if( !moveall ) { const int offset = 16; uilist smenu; smenu.text = _( "Selection type" ); @@ -1560,18 +1397,25 @@ int editmap::select_shape( shapetype shape, int mode ) smenu.addentry( editmap_circle, true, 'c', pgettext( "shape", "Filled Circle" ) ); smenu.addentry( -2, true, 'p', pgettext( "shape", "Point" ) ); smenu.selected = static_cast( shape ); - smenu.query(); - if( smenu.ret == UILIST_CANCEL ) { - // canceled - } else if( smenu.ret != -2 ) { - shape = static_cast( smenu.ret ); - update = true; - } else { - target_list.clear(); - origin = target; - target_list.push_back( target ); - moveall = true; - } + smenu.additional_actions = { + { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings + }; + smenu.allow_additional = true; + do { + update_view_with_help( "", pgettext( "map editor state", "Select a shape" ) ); + smenu.query(); + if( smenu.ret == UILIST_CANCEL ) { + // canceled + } else if( smenu.ret != -2 ) { + shape = static_cast( smenu.ret ); + update = true; + } else if( smenu.ret != UILIST_ADDITIONAL ) { + target_list.clear(); + origin = target; + target_list.push_back( target ); + moveall = true; + } + } while( smenu.ret == UILIST_ADDITIONAL ); } else { moveall = false; } @@ -1606,13 +1450,11 @@ int editmap::select_shape( shapetype shape, int mode ) recalc_target( shape ); } altblink = moveall; - update_view( false ); } while( action != "CONFIRM" && action != "QUIT" ); blink = true; altblink = false; if( action == "CONFIRM" ) { editshape = shape; - update_view( false ); return target_list.size(); } else { target_list.clear(); @@ -1620,7 +1462,6 @@ int editmap::select_shape( shapetype shape, int mode ) origin = origor; target_list.push_back( target ); blink = false; - update_view( false ); return -1; } } @@ -1629,18 +1470,14 @@ int editmap::select_shape( shapetype shape, int mode ) /* * Display mapgen results over selected target position, and optionally regenerate / apply / abort */ -int editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) +void editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) { - int ret = 0; - hilights["mapgentgt"].points.clear(); hilights["mapgentgt"].points[target + point( -SEEX, -SEEY )] = 1; hilights["mapgentgt"].points[target + point( 1 + SEEX, 1 + SEEY )] = 1; hilights["mapgentgt"].points[target + point( -SEEX, 1 + SEEY )] = 1; hilights["mapgentgt"].points[target + point( 1 + SEEX, -SEEY )] = 1; - update_view( true ); - // Coordinates of the overmap terrain that should be generated. const point omt_pos = ms_to_omt_copy( tc.abs_pos ); oter_id &omt_ref = overmap_buffer.ter( tripoint( omt_pos, target.z ) ); @@ -1665,17 +1502,19 @@ int editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) gpmenu.w_height = infoHeight - 4; gpmenu.w_y = gmenu.w_height; gpmenu.w_x = offsetX; - gpmenu.allow_anykey = true; gpmenu.addentry( pgettext( "map generator", "Regenerate" ) ); gpmenu.addentry( pgettext( "map generator", "Rotate" ) ); gpmenu.addentry( pgettext( "map generator", "Apply" ) ); gpmenu.addentry( pgettext( "map generator", "Change Overmap (Doesn't Apply)" ) ); - gpmenu.show(); - uphelp( _( "[pgup/pgdn]: prev/next oter type" ), - _( "[up/dn] select, [enter] accept, [q] abort" ), - string_format( "Mapgen: %s", oter_id( gmenu.ret ).id().str().substr( 0, 40 ).c_str() ) - ); + gpmenu.input_category = "MAPGEN_PREVIEW"; + gpmenu.additional_actions = { + { "LEFT", "" }, + { "RIGHT", "" }, + { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings + }; + gpmenu.allow_additional = true; + int lastsel = gmenu.selected; bool showpreview = true; do { @@ -1686,9 +1525,18 @@ int editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) tmpmap.generate( tripoint( omt_pos.x * 2, omt_pos.y * 2, target.z ), calendar::turn ); showpreview = true; } + input_context ctxt( gpmenu.input_category ); + // \u00A0 is the non-breaking space + update_view_with_help( string_format( pgettext( "keybinding descriptions", + "[%s,%s]\u00A0prev/next oter type, [%s,%s]\u00A0select, %s, %s" ), + ctxt.get_desc( "LEFT", 1 ), ctxt.get_desc( "RIGHT", 1 ), + ctxt.get_desc( "UP", 1 ), ctxt.get_desc( "DOWN", 1 ), + ctxt.describe_key_and_name( "CONFIRM" ), + ctxt.describe_key_and_name( "QUIT" ) ), + string_format( pgettext( "map editor state", "Mapgen: %s" ), + oter_id( gmenu.selected ).id().str() ) ); if( showpreview ) { hilights["mapgentgt"].draw( *this, true ); - wrefresh( g->w_terrain ); g->draw_panels(); tmpmap.reset_vehicle_cache( target.z ); for( int x = 0; x < SEEX * 2; x++ ) { @@ -1698,17 +1546,14 @@ int editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) } } wrefresh( w_preview ); - } else { - update_view( false ); //wrefresh(g->w_terrain); } - int gpmenupos = gpmenu.selected; - gpmenu.query( false, BLINK_SPEED * 3 ); + gmenu.show(); + gpmenu.query( true, BLINK_SPEED * 3 ); if( gpmenu.ret == UILIST_TIMEOUT ) { showpreview = !showpreview; - } else if( gpmenu.ret != UILIST_UNBOUND ) { + } else if( gpmenu.ret != UILIST_ADDITIONAL ) { if( gpmenu.ret == 0 ) { - cleartmpmap( tmpmap ); tmpmap.generate( tripoint( omt_pos.x * 2, omt_pos.y * 2, target.z ), calendar::turn ); showpreview = true; @@ -1754,23 +1599,22 @@ int editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) } else if( gpmenu.ret == 3 ) { popup( _( "Changed oter_id from '%s' (%s) to '%s' (%s)" ), - orig_oters->get_name(), orig_oters.id().c_str(), - omt_ref->get_name(), omt_ref.id().c_str() ); + orig_oters->get_name(), orig_oters.id().str(), + omt_ref->get_name(), omt_ref.id().str() ); + } + } else if( gpmenu.ret == UILIST_ADDITIONAL ) { + if( gpmenu.ret_act == "LEFT" ) { + gmenu.scrollby( -1 ); + gmenu.show(); + gmenu.refresh(); + } else if( gpmenu.ret_act == "RIGHT" ) { + gmenu.scrollby( 1 ); + gmenu.show(); + gmenu.refresh(); } - } else if( gpmenu.keypress == 'm' ) { - // TODO: keep preview as is and move target - } else if( gpmenu.keypress == KEY_NPAGE || gpmenu.keypress == KEY_PPAGE || - gpmenu.keypress == KEY_LEFT || gpmenu.keypress == KEY_RIGHT ) { - - int dir = gpmenu.keypress == KEY_NPAGE || gpmenu.keypress == KEY_RIGHT ? 1 : -1; - gmenu.scrollby( dir ); - gpmenu.selected = gpmenupos; - gmenu.show(); - gmenu.refresh(); } } while( gpmenu.ret != 2 && gpmenu.ret != 3 && gpmenu.ret != UILIST_CANCEL ); - update_view( true ); if( gpmenu.ret != 2 && // we didn't apply, so restore the original om_ter gpmenu.ret != 3 ) { // chose to change oter_id but not apply mapgen omt_ref = orig_oters; @@ -1780,7 +1624,6 @@ int editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) gmenu.redraw(); hilights["mapgentgt"].points.clear(); cleartmpmap( tmpmap ); - return ret; } vehicle *editmap::mapgen_veh_query( const tripoint &omt_tgt ) @@ -1842,9 +1685,8 @@ bool editmap::mapgen_veh_destroy( const tripoint &omt_tgt, vehicle *car_target ) /* * Move mapgen's target, which is different enough from the standard tile edit to warrant it's own function. */ -int editmap::mapgen_retarget() +void editmap::mapgen_retarget() { - int ret = 0; input_context ctxt( "EDITMAP_RETARGET" ); ctxt.set_iso( true ); ctxt.register_directions(); @@ -1855,11 +1697,12 @@ int editmap::mapgen_retarget() ctxt.register_action( "ANY_INPUT" ); std::string action; tripoint origm = target; - uphelp( "", - pgettext( "map generator", "[enter] accept, [q] abort" ), pgettext( "map generator", - "Mapgen: Moving target" ) ); do { + update_view_with_help( string_format( pgettext( "keybinding descriptions", "%s, %s" ), + ctxt.describe_key_and_name( "CONFIRM" ), + ctxt.describe_key_and_name( "QUIT" ) ), + pgettext( "map generator", "Mapgen: Moving target" ) ); action = ctxt.handle_input( BLINK_SPEED ); blink = !blink; if( const cata::optional vec = ctxt.get_direction( action ) ) { @@ -1880,59 +1723,41 @@ int editmap::mapgen_retarget() } else { blink = !blink; } - update_view( false ); } while( action != "QUIT" && action != "CONFIRM" ); if( action != "CONFIRM" ) { target = origm; } blink = true; - return ret; } -class edit_mapgen_callback : public uilist_callback -{ - private: - editmap *_e; - public: - edit_mapgen_callback( editmap *e ) { - _e = e; - } - bool key( const input_context &, const input_event &event, int /*entnum*/, uilist * ) override { - if( event.get_first_input() == 'm' ) { - _e->mapgen_retarget(); - return true; - } - return false; - } -}; - /* * apply mapgen to a temporary map and overlay over terrain window, optionally regenerating, rotating, and applying to the real in-game map */ -int editmap::edit_mapgen() +void editmap::edit_mapgen() { - int ret = 0; uilist gmenu; gmenu.w_width = width; gmenu.w_height = TERMY - infoHeight; gmenu.w_y = 0; gmenu.w_x = offsetX; - edit_mapgen_callback cb( this ); - gmenu.callback = &cb; + gmenu.input_category = "EDIT_MAPGEN"; + gmenu.additional_actions = { + { "EDITMAP_MOVE", "" }, + { "HELP_KEYBINDINGS", "" } // to refresh the view after exiting from keybindings + }; + gmenu.allow_additional = true; for( size_t i = 0; i < overmap_terrains::get_all().size(); i++ ) { const oter_id id( i ); - gmenu.addentry( -1, !id.id().is_null(), 0, "[%3d] %s", static_cast( id ), id.id().c_str() ); + gmenu.addentry( -1, !id.id().is_null(), 0, "[%3d] %s", static_cast( id ), id.id().str() ); gmenu.entries[i].extratxt.left = 1; gmenu.entries[i].extratxt.color = id->get_color(); gmenu.entries[i].extratxt.txt = id->get_symbol(); } real_coords tc; + do { - uphelp( pgettext( "map generator", "[m]ove" ), - pgettext( "map generator", "[enter] change, [q]uit" ), pgettext( "map generator", - "Mapgen stamp" ) ); tc.fromabs( g->m.getabs( target.xy() ) ); point omt_lpos = g->m.getlocal( tc.begin_om_pos() ); tripoint om_ltarget = omt_lpos + tripoint( -1 + SEEX, -1 + SEEY, target.z ); @@ -1952,14 +1777,22 @@ int editmap::edit_mapgen() } blink = true; - update_view( false ); + input_context ctxt( gmenu.input_category ); + update_view_with_help( string_format( pgettext( "keybinding descriptions", "%s, %s, %s" ), + ctxt.describe_key_and_name( "EDITMAP_MOVE" ), + ctxt.describe_key_and_name( "CONFIRM" ), + ctxt.describe_key_and_name( "QUIT" ) ), + pgettext( "map generator", "Mapgen stamp" ) ); gmenu.query(); if( gmenu.ret > 0 ) { mapgen_preview( tc, gmenu ); + } else if( gmenu.ret == UILIST_ADDITIONAL ) { + if( gmenu.ret_act == "EDITMAP_MOVE" ) { + mapgen_retarget(); + } } } while( gmenu.ret != UILIST_CANCEL ); - return ret; } /* diff --git a/src/editmap.h b/src/editmap.h index 521819ba15224..34441801a97c3 100644 --- a/src/editmap.h +++ b/src/editmap.h @@ -44,70 +44,47 @@ struct editmap_hilight { class editmap { public: - void uphelp( const std::string &txt1 = "", const std::string &txt2 = "", - const std::string &title = "" ); tripoint pos2screen( const tripoint &p ); - tripoint screen2pos( const tripoint &p ); bool eget_direction( tripoint &p, const std::string &action ) const; cata::optional edit(); void uber_draw_ter( const catacurses::window &w, map *m ); - void update_view( bool update_info = false ); - int edit_ter(); - - int edit_fld(); - int edit_trp(); - int edit_itm(); - int edit_critter( Creature &critter ); - int edit_veh(); - int edit_mapgen(); + void update_view_with_help( const std::string &txt, const std::string &title ); + + // T_t can be ter_t, furn_t, and trap + template + void edit_feature(); + void edit_fld(); + void edit_itm(); + void edit_critter( Creature &critter ); + void edit_veh(); + void edit_mapgen(); void cleartmpmap( tinymap &tmpmap ); - int mapgen_preview( const real_coords &tc, uilist &gmenu ); + void mapgen_preview( const real_coords &tc, uilist &gmenu ); vehicle *mapgen_veh_query( const tripoint &omt_tgt ); bool mapgen_veh_destroy( const tripoint &omt_tgt, vehicle *car_target ); - int mapgen_retarget(); + void mapgen_retarget(); int select_shape( shapetype shape, int mode = -1 ); void update_fmenu_entry( uilist &fmenu, field &field, field_type_id idx ); void setup_fmenu( uilist &fmenu ); catacurses::window w_info; - catacurses::window w_help; int width; int height; int offsetX; int infoHeight; - int ter_frn_mode; - ter_id sel_ter; - ter_id target_ter; - furn_id sel_frn; - furn_id target_frn; - - tripoint recalc_target( shapetype shape ); + void recalc_target( shapetype shape ); bool move_target( const std::string &action, int moveorigin = -1 ); - field *cur_field; - - trap_id cur_trap; int sel_field; int sel_field_intensity; - trap_id sel_trap; - - furn_id fsel; - furn_id fset; - trap_id trsel; - trap_id trset; - tripoint target; tripoint origin; bool moveall; bool refresh_mplans; shapetype editshape; - std::string padding; - - std::map fids; - std::vector target_list; std::map hilights; bool blink; diff --git a/src/input.cpp b/src/input.cpp index b9e98a330066b..6732dad657351 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -769,8 +769,9 @@ std::string input_context::get_desc( const std::string &action_descriptor, const std::function evt_filter ) const { if( action_descriptor == "ANY_INPUT" ) { + // \u00A0 is the non-breaking space //~ keybinding description for anykey - return string_format( pgettext( "keybinding", "[any] %s" ), text ); + return string_format( pgettext( "keybinding", "[any]\u00A0%s" ), text ); } const auto &events = inp_mngr.get_input_for_action( action_descriptor, category ); @@ -795,14 +796,20 @@ std::string input_context::get_desc( const std::string &action_descriptor, if( na ) { //~ keybinding description for unbound or disabled keys - return string_format( pgettext( "keybinding", "[n/a] %s" ), text ); + return string_format( pgettext( "keybinding", "[n/a]\u00A0%s" ), text ); } else { //~ keybinding description for bound keys - return string_format( pgettext( "keybinding", "[%s] %s" ), + return string_format( pgettext( "keybinding", "[%s]\u00A0%s" ), get_desc( action_descriptor, 1, evt_filter ), text ); } } +std::string input_context::describe_key_and_name( const std::string &action_descriptor, + const std::function evt_filter ) const +{ + return get_desc( action_descriptor, get_action_name( action_descriptor ), evt_filter ); +} + const std::string &input_context::handle_input() { return handle_input( timeout ); diff --git a/src/input.h b/src/input.h index 91d871b9a12b3..a99aaf03bf5e8 100644 --- a/src/input.h +++ b/src/input.h @@ -562,6 +562,15 @@ class input_context return true; } ) const; + /** + * Equivalent to get_desc( act, get_action_name( act ), filter ) + **/ + std::string describe_key_and_name( const std::string &action_descriptor, + std::function evt_filter = + []( const input_event & ) { + return true; + } ) const; + /** * Handles input and returns the next action in the queue. * diff --git a/src/output.cpp b/src/output.cpp index 044ab91a38781..67719a6e4d873 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -394,14 +394,15 @@ void center_print( const catacurses::window &w, const int y, const nc_color &FG, const std::string &text ) { int window_width = getmaxx( w ); - int string_width = utf8_width( text ); + int string_width = utf8_width( text, true ); int x; if( string_width >= window_width ) { x = 0; } else { x = ( window_width - string_width ) / 2; } - mvwprintz( w, point( x, y ), FG, text ); + const int available_width = std::max( 1, window_width - x ); + trim_and_print( w, point( x, y ), available_width, FG, text ); } int right_print( const catacurses::window &w, const int line, const int right_indent, diff --git a/src/ui.cpp b/src/ui.cpp index ed2db2044d15d..52f22b94e8681 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -144,6 +144,7 @@ void uilist::init() allow_disabled = false; // disallow selecting disabled options allow_anykey = false; // do not return on unbound keys allow_cancel = true; // allow cancelling with "QUIT" action + allow_additional = false; // do not return on unhandled additional actions hilight_full = true; // render hilight_color background over the entire line (minus padding) hilight_disabled = false; // if false, hitting 'down' onto a disabled entry will advance downward to the first enabled entry @@ -331,18 +332,13 @@ void uilist::setup() w_width = title.size() + 5; } } + const int max_desc_width = w_auto ? TERMX - 4 : w_width - 4; bool h_auto = ( w_height == -1 ); if( h_auto ) { w_height = 4; } - if( desc_enabled && !( w_auto && h_auto ) ) { - desc_enabled = false; // give up - debugmsg( "desc_enabled without w_auto and h_auto (h: %d, w: %d)", static_cast( h_auto ), - static_cast( w_auto ) ); - } - max_entry_len = 0; max_column_len = 0; std::vector autoassign; @@ -376,10 +372,9 @@ void uilist::setup() } } if( desc_enabled ) { - const int min_width = std::min( TERMX, std::max( w_width, descwidth_final ) ) - 4; - const int max_width = TERMX - 4; + const int min_desc_width = std::min( max_desc_width, std::max( w_width, descwidth_final ) - 4 ); int descwidth = find_minimum_fold_width( footer_text.empty() ? entries[i].desc : footer_text, - desc_lines, min_width, max_width ); + desc_lines, min_desc_width, max_desc_width ); descwidth += 4; // 2x border + 2x ' ' pad if( descwidth_final < descwidth ) { descwidth_final = descwidth; @@ -847,16 +842,14 @@ void uilist::query( bool loop, int timeout ) #endif do { - const auto action = ctxt.handle_input( timeout ); + ret_act = ctxt.handle_input( timeout ); const auto event = ctxt.get_raw_input(); keypress = event.get_first_input(); const auto iter = keymap.find( keypress ); - if( scrollby( scroll_amount_from_action( action ) ) ) { + if( scrollby( scroll_amount_from_action( ret_act ) ) ) { /* nothing */ - } else if( action == "HELP_KEYBINDINGS" ) { - /* nothing, handled by input_context */ - } else if( filtering && action == "FILTER" ) { + } else if( filtering && ret_act == "FILTER" ) { inputfilter(); } else if( iter != keymap.end() ) { selected = iter->second; @@ -865,20 +858,27 @@ void uilist::query( bool loop, int timeout ) } else if( allow_disabled ) { ret = entries[selected].retval; // disabled } - } else if( !fentries.empty() && action == "CONFIRM" ) { + } else if( !fentries.empty() && ret_act == "CONFIRM" ) { if( entries[ selected ].enabled ) { ret = entries[ selected ].retval; // valid } else if( allow_disabled ) { ret = entries[selected].retval; // disabled } - } else if( allow_cancel && action == "QUIT" ) { + } else if( allow_cancel && ret_act == "QUIT" ) { ret = UILIST_CANCEL; - } else if( action == "TIMEOUT" ) { + } else if( ret_act == "TIMEOUT" ) { ret = UILIST_TIMEOUT; - } else { + } else { // including HELP_KEYBINDINGS, in case the caller wants to refresh their contents bool unhandled = callback == nullptr || !callback->key( ctxt, event, selected, this ); if( unhandled && allow_anykey ) { ret = UILIST_UNBOUND; + } else if( unhandled && allow_additional ) { + for( const auto &it : additional_actions ) { + if( it.first == ret_act ) { + ret = UILIST_ADDITIONAL; + break; + } + } } } diff --git a/src/ui.h b/src/ui.h index 65d3df638f3fd..b7d3beaff8b11 100644 --- a/src/ui.h +++ b/src/ui.h @@ -22,6 +22,7 @@ const int UILIST_WAIT_INPUT = -1025; const int UILIST_UNBOUND = -1026; const int UILIST_CANCEL = -1027; const int UILIST_TIMEOUT = -1028; +const int UILIST_ADDITIONAL = -1029; const int MENU_ALIGN_LEFT = -1; const int MENU_ALIGN_CENTER = 0; const int MENU_ALIGN_RIGHT = 1; @@ -161,6 +162,7 @@ class uilist: public ui_container { public: int ret; + std::string ret_act; int selected; int keypress; std::string text; @@ -191,6 +193,9 @@ class uilist: public ui_container bool allow_disabled; // return on selecting disabled entry, default false bool allow_anykey; // return UILIST_UNBOUND on keys unbound & unhandled by callback, default false bool allow_cancel; // return UILIST_CANCEL on "QUIT" action, default true + // return UILIST_ADDITIONAL if the input action is inside `additional_actions` + // and unhandled by callback, default false. + bool allow_additional; bool hilight_disabled; bool hilight_full; int vshift;