void ribi::imcw::company::buy_click_card(
  person& customer,
  balance& account_euros,
  bank& the_bank,
  calendar& the_calendar
)
{
  assert(customer.has_account(account_euros));
  #ifndef NDEBUG
  const auto before = account_euros.get_value();
  #endif
  the_bank.transfer(
    account_euros,
    money(click_card::cost_inc_vat_euros),
    m_balance_undistributed,
    the_calendar.get_today()
  );

  #ifndef NDEBUG
  const auto after = account_euros.get_value();
  assert(after < before);
  #endif

  //ClickCard will be valid the first day of the next month
  click_card c(
    the_calendar.get_today() //Purchase date
  );
  customer.add_click_card(c);
}
Ejemplo n.º 2
0
w_point weather_generator::get_weather(const point &location, const calendar &t) const
{
    const double x(location.x / 2000.0);// Integer x position / widening factor of the Perlin function.
    const double y(location.y / 2000.0);// Integer y position / widening factor of the Perlin function.
    // Leaving these in just in case something ELSE goes wrong--KA101
//    int initial_season(0);
//    if(ACTIVE_WORLD_OPTIONS["INITIAL_SEASON"].getValue() == "spring") {
//        initial_season = 1;
//    } else if(ACTIVE_WORLD_OPTIONS["INITIAL_SEASON"].getValue() == "summer") {
//        initial_season = 2;
//    } else if(ACTIVE_WORLD_OPTIONS["INITIAL_SEASON"].getValue() == "autumn") {
//        initial_season = 3;
//    }
    const double z( double( t.get_turn() + DAYS(t.season_length()) ) / 2000.0); // Integer turn / widening factor of the Perlin function.

    const double dayFraction((double)t.minutes_past_midnight() / 1440);

    // Noise factors
    double T(raw_noise_4d(x, y, z, SEED) * 4.0);
    double H(raw_noise_4d(x, y, z / 5, SEED + 101));
    double H2(raw_noise_4d(x, y, z, SEED + 151) / 4);
    double P(raw_noise_4d(x, y, z / 3, SEED + 211) * 70);
    double W;

    const double now( double( t.turn_of_year() + DAYS(t.season_length()) / 2 ) / double(t.year_turns()) ); // [0,1)
    const double ctn(cos(tau * now));

    // Temperature variation
    const double mod_t(0); // TODO: make this depend on latitude and altitude?
    const double current_t(base_t + mod_t); // Current baseline temperature. Degrees Celsius.
    const double seasonal_variation(ctn * -1); // Start and end at -1 going up to 1 in summer.
    const double season_atenuation(ctn / 2 + 1); // Harsh winter nights, hot summers.
    const double season_dispersion(pow(2,
                                       ctn + 1) - 2.3); // Make summers peak faster and winters not perma-frozen.
    const double daily_variation(cos( tau * dayFraction - tau / 8 ) * -1 * season_atenuation / 2 +
                                 season_dispersion * -1); // Day-night temperature variation.

    T += current_t; // Add baseline to the noise.
    T += seasonal_variation * 8 * exp(-pow(current_t * 2.7 / 10 - 0.5,
                                            2)); // Add season curve offset to account for the winter-summer difference in day-night difference.
    T += daily_variation * 8 * exp(-pow(current_t / 30,
                                         2)); // Add daily variation scaled to the inverse of the current baseline. A very specific and finicky adjustment curve.
    T = T * 9 / 5 + 32; // Convert to imperial. =|

    // Humidity variation
    const double mod_h(0);
    const double current_h(base_h + mod_h);
    H = std::max(std::min((ctn / 10.0 + (-pow(H, 2) * 3 + H2)) * current_h / 2.0 + current_h, 100.0),
                 0.0); // Humidity stays mostly at the mean level, but has low peaks rarely. It's a percentage.

    // Pressure variation
    P += seasonal_variation * 20 +
         base_p; // Pressure is mostly random, but a bit higher on summer and lower on winter. In millibars.

    // Wind power
    W = std::max(0, 1020 - (int)P);

    return w_point {T, H, P, W, false};
}
void ribi::imcw::company::distribute_net_profit(
  bank& the_bank,
  calendar& the_calendar
  ) noexcept
{
  const money profit = m_balance_undistributed.get_value();
  const money to_compensation_plan = profit * proportion_of_profit_to_compensation_plan;
  const money to_holding = profit * proportion_of_profit_to_holding;
  const money to_reserves = profit * proportion_of_profit_to_reserves;
  const money to_winners = profit * proportion_of_profit_to_winners;

  the_bank.transfer(
    m_balance_undistributed, //Sender
    to_compensation_plan,
    m_balance_compensation_plan, //Target
    the_calendar.get_today()
  );

  the_bank.transfer(
    m_balance_undistributed, //Sender
    to_holding,
    m_balance_holding, //Target
    the_calendar.get_today()
  );

  the_bank.transfer(
    m_balance_undistributed, //Sender
    to_reserves,
    m_balance_reserves, //Target
    the_calendar.get_today()
  );

  //Shortly transfer it to the Winners balance...
  the_bank.transfer(
    m_balance_undistributed, //Sender
    to_winners,
    m_balance_winners, //Target
    the_calendar.get_today()
  );
  //then distribute all over the customers
  distribute_net_profit_winners(
    m_balance_winners, //Sender
    to_winners,
    the_bank,
    the_calendar
  );


  distribute_net_profit_compensation_plan(
    m_balance_compensation_plan, //Sender
    to_compensation_plan,
    the_bank,
    the_calendar
  );

}
Ejemplo n.º 4
0
inline void proc_weather_sum( const weather_type wtype, weather_sum &data,
                              const calendar &turn, const int tick_size )
{
    switch( wtype ) {
    case WEATHER_DRIZZLE:
        data.rain_amount += 4 * tick_size;
        break;
    case WEATHER_RAINY:
    case WEATHER_THUNDER:
    case WEATHER_LIGHTNING:
        data.rain_amount += 8 * tick_size;
        break;
    case WEATHER_ACID_DRIZZLE:
        data.acid_amount += 4 * tick_size;
        break;
    case WEATHER_ACID_RAIN:
        data.acid_amount += 8 * tick_size;
        break;
    default:
        break;
    }

    // TODO: Change this calendar::sunlight "sampling" here into a proper interpolation
    const float tick_sunlight = turn.sunlight() - weather_data( wtype ).light_modifier;
    data.sunlight += std::max<float>( 0.0f, tick_size * tick_sunlight );
}
Ejemplo n.º 5
0
weather_type weather_generator::get_weather_conditions(const point &location, const calendar &t) const
{
    w_point w(get_weather(location, t));
    weather_type wt = get_weather_conditions(w);
    // Make sure we don't say it's sunny at night! =P
    if (wt == WEATHER_SUNNY && t.is_night()) { return WEATHER_CLEAR; }
    return wt;
}
void ribi::imcw::company::transfer(
  balance& source,
  bank& the_bank,
  calendar& the_calendar
) noexcept
{
  const money the_money = source.get_value();
  the_bank.transfer(
    source,
    the_money,
    m_balance_undistributed,
    the_calendar.get_today()
  );
}
Ejemplo n.º 7
0
/**
 * Determine what a funnel has filled out of game, using funnelcontainer.bday as a starting point.
 */
void retroactively_fill_from_funnel( item *it, const trap_id t, const calendar &endturn,
                                     const point &location )
{
    const calendar startturn = calendar( it->bday > 0 ? it->bday - 1 : 0 );
    if ( startturn > endturn || traplist[t]->funnel_radius_mm < 1 ) {
        return;
    }
    it->bday = int(endturn.get_turn()); // bday == last fill check
    int rain_amount = 0;
    int acid_amount = 0;
    int rain_turns = 0;
    int acid_turns = 0;
    for( calendar turn(startturn); turn >= endturn; turn += 10) {
        switch(g->weatherGen.get_weather_conditions(location, turn)) {
        case WEATHER_DRIZZLE:
            rain_amount += 4;
            rain_turns++;
            break;
        case WEATHER_RAINY:
        case WEATHER_THUNDER:
        case WEATHER_LIGHTNING:
            rain_amount += 8;
            rain_turns++;
            break;
        case WEATHER_ACID_DRIZZLE:
            acid_amount += 4;
            acid_turns++;
            break;
        case WEATHER_ACID_RAIN:
            acid_amount += 8;
            acid_turns++;
            break;
        default:
            break;
        }
    }
    int rain = rain_turns / traplist[t]->funnel_turns_per_charge( rain_amount );
    int acid = acid_turns / traplist[t]->funnel_turns_per_charge( acid_amount );
    it->add_rain_to_container( false, rain );
    it->add_rain_to_container( true, acid );
}
Ejemplo n.º 8
0
void Messages::display_messages()
{
    WINDOW_PTR w_ptr {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)};

    WINDOW *const w = w_ptr.get();

    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");

    int offset = 0;
    const int maxlength = FULL_SCREEN_WIDTH - 2 - 1;
    const int bottom = FULL_SCREEN_HEIGHT - 2;
    const int msg_count = size();

    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);

        int line = 1;
        int lasttime = -1;
        for( int i = offset; i < msg_count; ++i ) {
            if (line > bottom) {
                break;
            }

            const game_message &m     = player_messages.impl_->history(i);
            const nc_color col        = m.get_color(player_messages.impl_->curmes);
            const calendar timepassed = calendar::turn - m.time;

            if (timepassed.get_turn() > lasttime) {
                mvwprintz(w, line++, 3, c_ltblue, _("%s ago:"),
                    timepassed.textify_period().c_str());
                lasttime = timepassed.get_turn();
            }

            for( const std::string &folded : foldstring(m.get_with_count(), maxlength) ) {
                if (line > bottom) {
                    break;
                }
                mvwprintz(w, line++, 1, col, "%s", folded.c_str());
            }
        }

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

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

    player_messages.impl_->curmes = calendar::turn.get_turn();
}
void ribi::imcw::company::distribute_net_profit_winners(
  balance& source,
  const money& total_money,
  bank& the_bank,
  calendar& the_calendar
) noexcept
{
  //Collect the Winners from all customers
  std::vector<std::reference_wrapper<winner>> winners = collect_winners();

  //Distribute the money over the winners
  //const int n_winners{count_winners()};
  const int n_winners{static_cast<int>(winners.size())};
  if (n_winners == 0) {
    //Transfer money to reserves
    the_bank.transfer(
      source,
      total_money,
      m_balance_reserves,
      the_calendar.get_today()
    );
    return;
  }

  assert(n_winners > 0);

  const money income_per_winners_euros
    = total_money
    / static_cast<double>(n_winners)
  ;
  for (std::reference_wrapper<winner>& w: winners)
  {
    #ifndef NDEBUG
    const auto winner_value_before = w.get().get_value();
    #endif

    the_bank.transfer(
      source,
      income_per_winners_euros,
      w.get().get_balance(),
      the_calendar.get_today()
    );

    #ifndef NDEBUG
    const auto winner_value_after = w.get().get_value();
    assert(winner_value_after >= winner_value_before);
    #endif
  }

  if (m_verbose) { std::clog << "Process the Winners" << std::endl; }
  for (person& customer: m_customers)
  {
    customer.process_winners(
      the_bank,
      the_calendar,
      *this
    );
  }

  if (m_verbose)
  {
    std::clog
      << "Distributing " << total_money << " over the winners\n"
      << "n_winners: " << n_winners << '\n'
      << "income_per_winners_euros: " << income_per_winners_euros << " \n"
    ;
  }
}
void ribi::imcw::company::distribute_net_profit_compensation_plan(
  balance& source,
  const money& total_money,
  bank& the_bank,
  calendar& the_calendar
) noexcept
{
  if (count_active_customers(the_calendar.get_today()) == 0) {
    //Transfer money to reserves
    the_bank.transfer(
      source,
      total_money,
      m_balance_undistributed,
      the_calendar.get_today()
    );
    return;
  }

  assert(count_active_customers(the_calendar.get_today()) > 0);
  #ifdef FIX_ISSUE_5
  assert(!"Not implemented yet");
  #else
  //STUB: Transfer money to reserves
  the_bank.transfer(
    source,
    total_money,
    m_balance_undistributed,
    the_calendar.get_today()
  );
  return;
  #endif
  /*
  const money income_per_winners_euros
    = total_money
    / static_cast<double>(n_winners)
  ;
  for (std::reference_wrapper<winner>& w: winners)
  {
    #ifndef NDEBUG
    const auto winner_value_before = w.get().get_value();
    #endif

    the_bank.transfer(
      source,
      income_per_winners_euros,
      w.get().get_balance(),
      the_calendar.get_today()
    );

    #ifndef NDEBUG
    const auto winner_value_after = w.get().get_value();
    assert(winner_value_after >= winner_value_before);
    #endif
  }

  if (m_verbose) { std::clog << "Process the Winners" << std::endl; }
  for (person& customer: m_customers)
  {
    customer.process_winners(
      the_bank,
      the_calendar,
      *this
    );
  }

  if (m_verbose)
  {
    std::clog
      << "Distributing " << total_money << " over the winners\n"
      << "n_winners: " << n_winners << '\n'
      << "income_per_winners_euros: " << income_per_winners_euros << " \n"
    ;
  }
  */
}
Ejemplo n.º 11
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();
}