Esempio n. 1
0
std::string to_string( const time_duration &d )
{
    if( d >= time_duration::from_turns( calendar::INDEFINITELY_LONG ) ) {
        return _( "forever" );
    }

    if( d <= 1_minutes ) {
        return to_string_clipped( d );
    }

    time_duration divider = 0_turns;
    if( d < 1_hours ) {
        divider = 1_minutes;
    } else if( d < 1_days ) {
        divider = 1_hours;
    } else {
        divider = 24_hours;
    }

    if( d % divider != 0_turns ) {
        //~ %1$s - greater units of time (e.g. 3 hours), %2$s - lesser units of time (e.g. 11 minutes).
        return string_format( _( "%1$s and %2$s" ),
                              to_string_clipped( d ),
                              to_string_clipped( d % divider ) );
    }
    return to_string_clipped( d );
}
Esempio n. 2
0
std::string to_string( const time_duration &d )
{
    const int turns = to_turns<int>( d );
    int divider = 0;

    if( turns > MINUTES( 1 ) && turns < calendar::INDEFINITELY_LONG ) {
        if( turns < HOURS( 1 ) ) {
            divider = MINUTES( 1 );
        } else if( turns < DAYS( 1 ) ) {
            divider = HOURS( 1 );
        } else {
            divider = DAYS( 1 );
        }
    }

    const int remainder = divider ? turns % divider : 0;
    if( remainder != 0 ) {
        //~ %1$s - greater units of time (e.g. 3 hours), %2$s - lesser units of time (e.g. 11 minutes).
        return string_format( _( "%1$s and %2$s" ),
                              to_string_clipped( time_duration::from_turns( turns ) ),
                              to_string_clipped( time_duration::from_turns( remainder ) ) );
    }

    return to_string_clipped( d );
}
        comestible_inventory_preset( const player &p ) : inventory_selector_preset(), p( p ) {

            append_cell( [ p, this ]( const item_location & loc ) {
                return good_bad_none( p.nutrition_for( get_comestible_item( loc ) ) );
            }, _( "NUTRITION" ) );

            append_cell( [ this ]( const item_location & loc ) {
                return good_bad_none( get_edible_comestible( loc ).quench );
            }, _( "QUENCH" ) );

            append_cell( [ p, this ]( const item_location & loc ) {
                return good_bad_none( p.fun_for( get_comestible_item( loc ) ).first );
            }, _( "JOY" ) );

            append_cell( [ this ]( const item_location & loc ) {
                const int spoils = get_edible_comestible( loc ).spoils;
                if( spoils > 0 ) {
                    return to_string_clipped( time_duration::from_turns( spoils ) );
                }
                return std::string();
            }, _( "SPOILS IN" ) );

            append_cell( [ this, &p ]( const item_location & loc ) {
                std::string cbm_name;

                switch( p.get_cbm_rechargeable_with( get_comestible_item( loc ) ) ) {
                    case rechargeable_cbm::none:
                        break;
                    case rechargeable_cbm::battery:
                        cbm_name = _( "Battery" );
                        break;
                    case rechargeable_cbm::reactor:
                        cbm_name = _( "Reactor" );
                        break;
                    case rechargeable_cbm::furnace:
                        cbm_name = _( "Furnace" );
                        break;
                }

                if( !cbm_name.empty() ) {
                    return string_format( "<color_cyan>%s</color>", cbm_name.c_str() );
                }

                return std::string();
            }, _( "CBM" ) );

            append_cell( [ this, &p ]( const item_location & loc ) {
                return good_bad_none( p.get_acquirable_energy( get_comestible_item( loc ) ) );
            }, _( "ENERGY" ) );
        }
        disassemble_inventory_preset( const player &p, const inventory &inv ) :
            pickup_inventory_preset( p ), p( p ), inv( inv ) {

            append_cell( [ this ]( const item_location & loc ) {
                const auto &req = get_recipe( loc ).disassembly_requirements();
                if( req.is_empty() ) {
                    return std::string();
                }
                const auto components = req.get_components();
                return enumerate_as_string( components.begin(), components.end(),
                []( const decltype( components )::value_type & comps ) {
                    return comps.front().to_string();
                } );
            }, _( "YIELD" ) );

            append_cell( [ this ]( const item_location & loc ) {
                return to_string_clipped( time_duration::from_turns( get_recipe( loc ).time / 100 ) );
            }, _( "TIME" ) );
        }
Esempio n. 5
0
std::string to_string_approx( const time_duration &dur, const bool verbose )
{
    time_duration d = dur;
    const auto make_result = [verbose]( const time_duration & d, const char *verbose_str,
    const char *short_str ) {
        return string_format( verbose ? verbose_str : short_str, to_string_clipped( d ) );
    };

    time_duration divider = 0_turns;
    time_duration vicinity = 0_turns;

    if( d > 1_days ) {
        divider = 1_days;
        vicinity = 2_hours;
    } else if( d > 1_hours ) {
        divider = 1_hours;
        vicinity = 5_minutes;
    } // Minutes and seconds can be estimated precisely.

    if( divider != 0_turns ) {
        const time_duration remainder = d % divider;

        if( remainder >= divider - vicinity ) {
            d += divider;
        } else if( remainder > vicinity ) {
            if( remainder < divider / 2 ) {
                //~ %s - time (e.g. 2 hours).
                return make_result( d, _( "more than %s" ), ">%s" );
            } else {
                //~ %s - time (e.g. 2 hours).
                return make_result( d + divider, _( "less than %s" ), "<%s" );
            }
        }
    }
    //~ %s - time (e.g. 2 hours).
    return make_result( d, _( "about %s" ), "%s" );
}
Esempio n. 6
0
std::string to_string_approx( const time_duration &d, const bool verbose )
{
    int turns = to_turns<int>( d );
    const auto make_result = [verbose]( int turns, const char *verbose_str, const char *short_str ) {
        return string_format( verbose ? verbose_str : short_str, to_string_clipped( time_duration::from_turns( turns ) ) );
    };

    int divider = 0;
    int vicinity = 0;

    if( turns > DAYS( 1 ) ) {
        divider = DAYS( 1 );
        vicinity = HOURS( 2 );
    } else if( turns > HOURS( 1 ) ) {
        divider = HOURS( 1 );
        vicinity = MINUTES( 5 );
    } // Minutes and seconds can be estimated precisely.

    if( divider != 0 ) {
        const int remainder = turns % divider;

        if( remainder >= divider - vicinity ) {
            turns += divider;
        } else if( remainder > vicinity ) {
            if( remainder < divider / 2 ) {
                //~ %s - time (e.g. 2 hours).
                return make_result( turns, _( "more than %s" ), ">%s" );
            } else {
                //~ %s - time (e.g. 2 hours).
                return make_result( turns + divider, _( "less than %s" ), "<%s" );
            }
        }
    }
    //~ %s - time (e.g. 2 hours).
    return make_result( turns, _( "about %s" ), "%s" );
}
Esempio n. 7
0
std::string to_string_clipped( const time_duration &d,
                               const clipped_align align )
{
    const std::pair<int, clipped_unit> time = clipped_time( d );
    return to_string_clipped( time.first, time.second, align );
}
Esempio n. 8
0
void Messages::dialog::show()
{
    werase( w );
    draw_border( w, border_color );

    scrollbar()
    .offset_x( 0 )
    .offset_y( border_width )
    .content_size( folded_filtered.size() )
    .viewport_pos( offset )
    .viewport_size( max_lines )
    .apply( w );

    // Range of window lines to print
    size_t line_from = 0, line_to;
    if( offset < folded_filtered.size() ) {
        line_to = std::min( max_lines, folded_filtered.size() - offset );
    } else {
        line_to = 0;
    }

    if( !log_from_top ) {
        // Always print from new to old
        std::swap( line_from, line_to );
    }
    std::string prev_time_str;
    bool printing_range = false;
    for( size_t line = line_from; line != line_to; ) {
        // Decrement here if printing from bottom to get the correct line number
        if( !log_from_top ) {
            --line;
        }

        const size_t folded_ind = offset + line;
        const size_t msg_ind = folded_all[folded_filtered[folded_ind]].first;
        const game_message &msg = player_messages.history( msg_ind );

        nc_color col = msgtype_to_color( msg.type, false );

        // Print current line
        print_colored_text( w, border_width + line, border_width + time_width + padding_width,
                            col, col, folded_all[folded_filtered[folded_ind]].second );

        // Generate aligned time string
        const time_point msg_time = msg.timestamp_in_turns;
        const std::string time_str = to_string_clipped( calendar::turn - msg_time, clipped_align::right );

        if( time_str != prev_time_str ) {
            // Time changed, print time string
            prev_time_str = time_str;
            right_print( w, border_width + line, border_width + msg_width + padding_width,
                         time_color, time_str );
            printing_range = false;
        } else {
            // Print line brackets to mark ranges of time
            if( printing_range ) {
                const size_t last_line = log_from_top ? line - 1 : line + 1;
                wattron( w, bracket_color );
                mvwaddch( w, border_width + last_line, border_width + time_width - 1, LINE_XOXO );
                wattroff( w, bracket_color );
            }
            wattron( w, bracket_color );
            mvwaddch( w, border_width + line, border_width + time_width - 1,
                      log_from_top ? LINE_XXOO : LINE_OXXO );
            wattroff( w, bracket_color );
            printing_range = true;
        }

        // Decrement for !log_from_top is done at the beginning
        if( log_from_top ) {
            ++line;
        }
    }

    if( filtering ) {
        wrefresh( w );
        // Print the help text
        werase( w_filter_help );
        draw_border( w_filter_help, border_color );
        for( size_t line = 0; line < help_text.size(); ++line ) {
            nc_color col = filter_help_color;
            print_colored_text( w_filter_help, border_width + line, border_width, col, col,
                                help_text[line] );
        }
        mvwprintz( w_filter_help, w_fh_height - 1, border_width, border_color, "< " );
        mvwprintz( w_filter_help, w_fh_height - 1, w_fh_width - border_width - 2, border_color, " >" );
        wrefresh( w_filter_help );

        // This line is preventing this method from being const
        filter.query( false, true ); // Draw only
    } else {
        if( filter_str.empty() ) {
            mvwprintz( w, w_height - 1, border_width, border_color, _( "< Press %s to filter, %s to reset >" ),
                       ctxt.get_desc( "FILTER" ), ctxt.get_desc( "RESET_FILTER" ) );
        } else {
            mvwprintz( w, w_height - 1, border_width, border_color, "< %s >", filter_str );
            mvwprintz( w, w_height - 1, border_width + 2, filter_color, "%s", filter_str );
        }
        wrefresh( w );
    }
}
Esempio n. 9
0
void Messages::dialog::init()
{
    w_width = std::min( TERMX, FULL_SCREEN_WIDTH );
    w_height = std::min( TERMY, FULL_SCREEN_HEIGHT );
    w_x = ( TERMX - w_width ) / 2;
    w_y = ( TERMY - w_height ) / 2;

    w = catacurses::newwin( w_height, w_width, w_y, w_x );

    ctxt = input_context( "MESSAGE_LOG" );
    ctxt.register_action( "UP", _( "Scroll up" ) );
    ctxt.register_action( "DOWN", _( "Scroll down" ) );
    ctxt.register_action( "PAGE_UP" );
    ctxt.register_action( "PAGE_DOWN" );
    ctxt.register_action( "FILTER" );
    ctxt.register_action( "RESET_FILTER" );
    ctxt.register_action( "QUIT" );
    ctxt.register_action( "HELP_KEYBINDINGS" );

    // Calculate time string display width. The translated strings are expected to
    // be aligned, so we choose an arbitrary duration here to calculate the width.
    time_width = utf8_width( to_string_clipped( 1_turns, clipped_align::right ) );

    if( border_width * 2 + time_width + padding_width >= w_width ||
        border_width * 2 >= w_height ) {

        debugmsg( "No enough space for the message window" );
        errored = true;
        return;
    }
    msg_width = w_width - border_width * 2 - time_width - padding_width;
    max_lines = static_cast<size_t>( w_height - border_width * 2 );

    // Initialize filter help text and window
    w_fh_width = w_width;
    w_fh_x = w_x;
    help_text = filter_help_text( w_fh_width - border_width * 2 );
    w_fh_height = help_text.size() + border_width * 2;
    w_fh_y = w_y + w_height - w_fh_height;
    w_filter_help = catacurses::newwin( w_fh_height, w_fh_width, w_fh_y, w_fh_x );

    // Initialize filter input
    filter.window( w_filter_help, border_width + 2, w_fh_height - 1, w_fh_width - border_width - 2 );
    filtering = false;

    // Initialize folded messages
    folded_all.clear();
    folded_filtered.clear();
    const size_t msg_count = size();
    for( size_t ind = 0; ind < msg_count; ++ind ) {
        const size_t msg_ind = log_from_top ? ind : msg_count - 1 - ind;
        const game_message &msg = player_messages.history( msg_ind );
        const auto &folded = foldstring( msg.get_with_count(), msg_width );
        for( const auto &it : folded ) {
            folded_filtered.emplace_back( folded_all.size() );
            folded_all.emplace_back( msg_ind, it );
        }
    }

    // Initialize scrolling offset
    if( log_from_top || max_lines > folded_filtered.size() ) {
        offset = 0;
    } else {
        offset = folded_filtered.size() - max_lines;
    }

    canceled = false;
    errored = false;
}
Esempio n. 10
0
void Messages::display_messages()
{
    catacurses::window w = catacurses::newwin(
                               FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH,
                               ( TERMY > FULL_SCREEN_HEIGHT ) ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0,
                               ( TERMX > FULL_SCREEN_WIDTH ) ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0 );

    input_context ctxt( "MESSAGE_LOG" );
    ctxt.register_action( "UP", _( "Scroll up" ) );
    ctxt.register_action( "DOWN", _( "Scroll down" ) );
    ctxt.register_action( "QUIT" );
    ctxt.register_action( "HELP_KEYBINDINGS" );

    /* Right-Aligning Time Epochs For Readability
     * ==========================================
     * Given display_messages(); right-aligns epochs, we must declare one quick variable first:
     * max_padlength refers to the length of the LONGEST possible unit of time returned by to_string_clipped() any language has to offer.
     * This variable is, for now, set to '10', which seems most reasonable.
     *
     * The reason right-aligned epochs don't use a "shortened version" (e.g. only showing the first letter) boils down to:
     * 1. The first letter of every time unit being unique is a property that might not carry across to other languages.
     * 2. Languages where displayed characters change depending on the characters preceding/following it will become unclear.
     * 3. Some polymorphic languages might not be able to appropriately convey meaning with one character (FRS is not a linguist- correct her on this if needed.)
     *
     * This right padlength is then incorporated into a so-called 'epoch_format' which, in turn, will be used to format the correct epoch.
     * If an external language introduces time units longer than 10 characters in size, consider altering these variables.
     * The game (likely) shan't segfault, though the text may appear a bit messed up if these variables aren't set properly.
     */
    const int max_padlength = 10;

    /* Dealing With Screen Extremities
     * ===============================
     * 'maxlength' corresponds to the most extreme length a log message may be before foldstring() wraps it around to two or more lines.
     * The numbers subtracted from FULL_SCREEN_WIDTH are - in order:
     * '-2' the characters reserved for the borders of the box, both on the left and right side.
     * '-1' the leftmost guide character that's drawn on screen.
     * '-4' the padded three-digit number each epoch starts with.
     * '-max_padlength' the characters of space that are allocated to time units (e.g. "years", "minutes", etc.)
     *
     * 'bottom' works much like 'maxlength', but instead it refers to the amount of lines that the message box may hold.
     */
    const int maxlength = FULL_SCREEN_WIDTH - 2 - 1 - 4 - max_padlength;
    const int bottom = FULL_SCREEN_HEIGHT - 2;
    const int msg_count = size();

    /* Dealing With Scroll Direction
     * =============================
     * Much like how the sidebar can have variable scroll direction, so will the message box.
     * To properly differentiate the two methods of displaying text, we will label them NEWEST-TOP, and OLDEST-TOP. This labeling should be self explanatory.
     *
     * Note that 'offset' tracks only our current position in the list; it shan't at all affect the manner in which the messages are drawn.
     * Messages are always drawn top-to-bottom. If NEWEST-TOP is used, then the top line (line=1) corresponds to the newest message. The one further down the second-newest, etc.
     * If the OLDEST-TOP method is used, then the top line (line=1) corresponds to the oldest message, and the bottom one to the newest.
     * The 'for (;;)' block below is nearly completely method-agnostic, save for the `player_messages.impl_->history(i)` call.
     *
     * In case of NEWEST-TOP, the 'i' variable easily enough corresponds to the newest message.
     * In case of OLDEST-TOP, the 'i' variable must be flipped- meaning the highest value of 'i' returns the result for the lowest value of 'i', etc.
     * To achieve this, the 'flip_message' variable is set to either the value of 'msg_count', or '0'. This variable is then subtracted from 'i' in each call to player_messages.impl_->history();
     *
     * 'offset' refers to the corresponding message that will be displayed at the very TOP of the message box window.
     *  NEWEST-TOP: 'offset' starts simply at '0' - the very top of the window.
     *  OLDEST-TOP: 'offset' is set to the maximum value it could possibly be. That is- 'msg_count-bottom'. This way, the screen starts with the scrollbar all the way down.
     * 'retrieve_history' refers to the line that should be displayed- this is either 'i' if it's NEWEST-TOP, or a flipped version of 'i' if it's OLDEST-TOP.
     */
    int offset = log_from_top ? 0 : ( msg_count - bottom );
    const int flip = log_from_top ? 0 : msg_count - 1;

    for( ;; ) {
        werase( w );
        draw_border( w );
        mvwprintz( w, bottom + 1, 32, c_red, _( "Press %s to return" ), ctxt.get_desc( "QUIT" ).c_str() );
        draw_scrollbar( w, offset, bottom, msg_count, 1, 0, c_white, true );

        int line = 1;
        int lasttime = -1;
        for( int i = offset; i < msg_count; ++i ) {
            const int retrieve_history = abs( i - flip );
            if( line > bottom ) {
                break;
                // This statement makes it so that no non-existent messages are printed (which usually results in a segfault)
            } else if( retrieve_history >= msg_count ) {
                continue;
            }

            const game_message &m     = player_messages.impl_->history( retrieve_history );
            const calendar timepassed = calendar::turn - m.timestamp_in_turns;
            std::string long_ago      = to_string_clipped( time_duration::from_turns( timepassed ) );
            nc_color col              = msgtype_to_color( m.type, false );

            // Here we separate the unit and amount from one another so that they can be properly padded when they're drawn on the screen.
            // Note that the very first character of 'unit' is often a space (except for languages where the time unit directly follows the number.)
            const auto amount_len = long_ago.find_first_not_of( "0123456789" );
            std::string amount = long_ago.substr( 0, amount_len );
            std::string unit = long_ago.substr( amount_len );
            if( timepassed.get_turn() != lasttime ) {
                right_print( w, line, 2, c_light_blue, string_format( _( "%-3s%-10s" ), amount.c_str(),
                             unit.c_str() ) );
                lasttime = timepassed.get_turn();
            }

            nc_color col_out = col;
            for( const std::string &folded : foldstring( m.get_with_count(), maxlength ) ) {
                if( line > bottom ) {
                    break;
                }
                print_colored_text( w, line, 2, col_out, col, folded );


                // So-called special "markers"- alternating '=' and '-'s at the edges of te message window so players can properly make sense of which message belongs to which time interval.
                // The '+offset%4' in the calculation makes it so that the markings scroll along with the messages.
                // On lines divisible by 4, draw a dark gray '-' at both horizontal extremes of the window.
                if( ( line + offset % 4 ) % 4 == 0 ) {
                    mvwprintz( w, line, 1, c_dark_gray, "-" );
                    mvwprintz( w, line, FULL_SCREEN_WIDTH - 2, c_dark_gray, "-" );
                    // On lines divisible by 2 (but not 4), draw a light gray '=' at the horizontal extremes of the window.
                } else if( ( line + offset % 4 ) % 2 == 0 ) {
                    mvwprintz( w, line, 1, c_light_gray, "=" );
                    mvwprintz( w, line, FULL_SCREEN_WIDTH - 2, c_light_gray, "=" );
                }

                // Only now are we done with this line:
                line++;
            }
        }

        if( offset < msg_count - bottom ) {
            mvwprintz( w, bottom + 1, 5, c_magenta, "vvv" );
        }
        if( offset > 0 ) {
            mvwprintz( w, bottom + 1, FULL_SCREEN_WIDTH - 8, c_magenta, "^^^" );
        }
        wrefresh( w );

        const std::string &action = ctxt.handle_input();
        if( action == "DOWN" && offset < msg_count - bottom ) {
            offset++;
        } else if( action == "UP" && offset > 0 ) {
            offset--;
        } else if( action == "QUIT" ) {
            break;
        }
    }

    player_messages.impl_->curmes = calendar::turn.get_turn();
}