Index: src/settings.cpp =================================================================== --- src/settings.cpp (revision 12795) +++ src/settings.cpp (working copy) @@ -1356,7 +1356,8 @@ * 'SLE_FILE_I16 | SLE_VAR_U16' in "diff_custom" is needed to get around SlArray() hack * for savegames version 0 - though it is an array, it has to go through the byteswap process */ SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_FILE_I16 | SLE_VAR_U16, 0, 0, GameOptions, diff, 17, 0, 0, 0, 0, NULL, STR_NULL, NULL, NULL, 0, 3), - SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_UINT16, 0, 0, GameOptions, diff, 18, 0, 0, 0, 0, NULL, STR_NULL, NULL, NULL, 4, SL_MAX_VERSION), + SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_UINT16, 0, 0, GameOptions, diff, 18, 0, 0, 0, 0, NULL, STR_NULL, NULL, NULL, 4, 93), + SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_UINT16, 0, 0, GameOptions, diff, 20, 0, 0, 0, 0, NULL, STR_NULL, NULL, NULL,94, SL_MAX_VERSION), SDT_VAR(GameOptions, diff_level, SLE_UINT8, 0, 0, 0, 0, 3, 0, STR_NULL, NULL), SDT_OMANY(GameOptions, currency, SLE_UINT8, N, 0, 0, CUSTOM_CURRENCY_ID, "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK|ESP|FIM|FRF|GRD|HUF|ISK|ITL|NLG|NOK|PLN|ROL|RUR|SIT|SEK|YTL|SKK|BRR|custom", STR_NULL, NULL, NULL), SDT_OMANY(GameOptions, units, SLE_UINT8, N, 0, 1, 2, "imperial|metric|si", STR_NULL, NULL, NULL), Index: src/lang/english.txt =================================================================== --- src/lang/english.txt (revision 12795) +++ src/lang/english.txt (working copy) @@ -2157,7 +2157,7 @@ STR_6807_NO_OF_TOWNS :{LTBLUE}No. of towns: {ORANGE}{STRING} STR_6808_NO_OF_INDUSTRIES :{LTBLUE}No. of industries: {ORANGE}{STRING} STR_6809_MAXIMUM_INITIAL_LOAN_000 :{LTBLUE}Maximum initial loan: {ORANGE}{CURRENCY} -STR_680A_INITIAL_INTEREST_RATE :{LTBLUE}Initial interest rate: {ORANGE}{COMMA}% +STR_680A_INITIAL_INTEREST_RATE :{LTBLUE}Minimum interest rate: {ORANGE}{COMMA}% STR_680B_VEHICLE_RUNNING_COSTS :{LTBLUE}Vehicle running costs: {ORANGE}{STRING} STR_680C_CONSTRUCTION_SPEED_OF_COMPETITOR :{LTBLUE}Construction speed of competitors: {ORANGE}{STRING} STR_680D_INTELLIGENCE_OF_COMPETITORS :{LTBLUE}Intelligence of competitors: {ORANGE}{STRING} @@ -2170,6 +2170,8 @@ STR_6814_TRAIN_REVERSING :{LTBLUE}Train reversing: {ORANGE}{STRING} STR_6815_DISASTERS :{LTBLUE}Disasters: {ORANGE}{STRING} STR_CITY_APPROVAL :{LTBLUE}City council's attitude towards area restructuring: {ORANGE}{STRING} +STR_MAX_INITIAL_INTEREST_RATE :{LTBLUE}Maximum interest rate: {ORANGE}{COMMA}% +STR_MAX_LOAN_PERCENTAGE :{LTBLUE}Maximum loan: {ORANGE}{COMMA}%{LTBLUE} of company value ############ range for difficulty settings ends STR_NONE :None @@ -2254,10 +2256,15 @@ STR_7025_OPERATING_PROFIT_GRAPH :{WHITE}Operating Profit Graph STR_7026_BANK_BALANCE :{WHITE}Bank Balance STR_7027_LOAN :{WHITE}Loan +STR_CURRENT_INTEREST :{WHITE}Current Interest: {BLACK}{COMMA}% STR_MAX_LOAN :{WHITE}Max Loan: {BLACK}{CURRENCY} STR_7028 :{BLACK}{CURRENCY} -STR_7029_BORROW :{BLACK}Borrow {SKIP}{SKIP}{CURRENCY} -STR_702A_REPAY :{BLACK}Repay {SKIP}{SKIP}{CURRENCY} +STR_7029_BORROW :{BLACK}Borrow {SKIP}{SKIP}{STRING2} +STR_702A_REPAY :{BLACK}Repay {SKIP}{SKIP}{SKIP}{SKIP}{STRING2} +STR_LOAN_AMOUNT :{CURRENCY} +STR_LOAN_MAX :{SKIP}Max. +STR_LOAN_INTERVAL_MINUS :{BLACK}< +STR_LOAN_INTERVAL_PLUS :{BLACK}> STR_702B_MAXIMUM_PERMITTED_LOAN :{WHITE}...maximum permitted loan size is {CURRENCY} STR_702C_CAN_T_BORROW_ANY_MORE_MONEY :{WHITE}Can't borrow any more money... STR_702D_LOAN_ALREADY_REPAYED :{WHITE}...no loan to repay Index: src/settings_gui.cpp =================================================================== --- src/settings_gui.cpp (revision 12795) +++ src/settings_gui.cpp (working copy) @@ -405,7 +405,7 @@ { 0, 3, 1, STR_NUM_VERY_LOW}, { 0, 4, 1, STR_NONE}, {100, 500, 50, STR_NULL}, - { 2, 4, 1, STR_NULL}, + { 2, 5, 1, STR_NULL}, { 0, 2, 1, STR_6820_LOW}, { 0, 4, 1, STR_681B_VERY_SLOW}, { 0, 2, 1, STR_6820_LOW}, @@ -418,6 +418,8 @@ { 0, 1, 1, STR_6834_AT_END_OF_LINE_AND_AT_STATIONS}, { 0, 1, 1, STR_6836_OFF}, { 0, 2, 1, STR_PERMISSIVE}, + { 2, 15, 1, STR_NULL}, + { 0, 90, 10, STR_NULL}, }; /* @@ -426,7 +428,7 @@ * C: town count (2 = high, 0 = very low) * D: industry count (4 = high, 0 = none) * E: inital loan / 1000 (in GBP) - * F: interest rate + * F: minimum interest rate * G: running costs (0 = low, 2 = high) * H: construction speed of competitors (0 = very slow, 4 = very fast) * I: intelligence (0-2) @@ -439,12 +441,14 @@ * P: Train reversing (0 = end of line + stations, 1 = end of line) * Q: disasters * R: area restructuring (0 = permissive, 2 = hostile) + * S: maximum interest rate + * T: maximum percentage of company value for loans */ static const GDType _default_game_diff[3][GAME_DIFFICULTY_NUM] = { /* - A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R*/ - {2, 2, 1, 4, 300, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0}, ///< easy - {4, 1, 1, 3, 150, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1}, ///< medium - {7, 0, 0, 2, 100, 4, 1, 3, 2, 2, 0, 2, 3, 2, 1, 1, 1, 2}, ///< hard + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T*/ + {2, 2, 1, 4, 300, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0, 2, 0}, ///< easy + {4, 1, 1, 3, 150, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 3, 0}, ///< medium + {7, 0, 0, 2, 100, 4, 1, 3, 2, 2, 0, 2, 3, 2, 1, 1, 1, 2, 4, 0}, ///< hard }; void SetDifficultyLevel(int mode, GameOptions *gm_opt) @@ -485,8 +489,8 @@ /* Temporary holding place of values in the difficulty window until 'Save' is clicked */ static GameOptions _opt_mod_temp; -// 0x383E = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) -#define DIFF_INGAME_DISABLED_BUTTONS 0x383E +// 0xC383E = (1 << 19) | (1 << 18) | (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) +#define DIFF_INGAME_DISABLED_BUTTONS 0xC383E #define NO_SETTINGS_BUTTON 0xFF @@ -555,7 +559,11 @@ int y = GAMEDIFF_WND_TOP_OFFSET; for (uint i = 0; i != GAME_DIFFICULTY_NUM; i++) { const GameSettingData *gsd = &_game_setting_info[i]; + value = ((GDType*)&_opt_mod_temp.diff)[i]; + //if (i == 6) value = ((GDType*)&_opt_mod_temp.diff)[18]; + //else if (i > 6) value = ((GDType*)&_opt_mod_temp.diff)[i-1]; + //else value = ((GDType*)&_opt_mod_temp.diff)[i]; DrawArrowButtons(5, y, 3, (diffic_d->clicked_button == i) ? 1 + !!diffic_d->clicked_increase : 0, @@ -608,6 +616,15 @@ val = max(val, info->min); diffic_d->clicked_increase = false; } + if (btn == 5 && ((GDType*)&_opt_mod_temp.diff)[18] < val) { + /* Make sure maximum interest is not smaller than minimum */ + ((GDType*)&_opt_mod_temp.diff)[18] = val; + } + else if (btn == 18 && val < ((GDType*)&_opt_mod_temp.diff)[5]) { + /* Make sure minimum interest is not greater than maximum */ + ((GDType*)&_opt_mod_temp.diff)[5] = val; + } + diffic_d->clicked_button = btn; /* save value in temporary variable */ @@ -676,16 +693,16 @@ { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 184, 270, 16, 27, STR_6803_HARD, STR_NULL}, // GDW_LVL_HARD { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 271, 357, 16, 27, STR_6804_CUSTOM, STR_NULL}, // GDW_LVL_CUSTOM { WWT_TEXTBTN, RESIZE_NONE, 6, 10, 357, 28, 39, STR_6838_SHOW_HI_SCORE_CHART, STR_NULL}, // GDW_HIGHSCORE -{ WWT_PANEL, RESIZE_NONE, 10, 0, 369, 42, 262, 0x0, STR_NULL}, // GDW_SETTING_BG -{ WWT_PANEL, RESIZE_NONE, 10, 0, 369, 263, 278, 0x0, STR_NULL}, // GDW_LOWER_BG -{ WWT_PUSHTXTBTN, RESIZE_NONE, 3, 105, 185, 265, 276, STR_OPTIONS_SAVE_CHANGES, STR_NULL}, // GDW_ACCEPT -{ WWT_PUSHTXTBTN, RESIZE_NONE, 3, 186, 266, 265, 276, STR_012E_CANCEL, STR_NULL}, // GDW_CANCEL +{ WWT_PANEL, RESIZE_NONE, 10, 0, 369, 42, 267, 0x0, STR_NULL}, // GDW_SETTING_BG +{ WWT_PANEL, RESIZE_NONE, 10, 0, 369, 268, 283, 0x0, STR_NULL}, // GDW_LOWER_BG +{ WWT_PUSHTXTBTN, RESIZE_NONE, 3, 105, 185, 270, 281, STR_OPTIONS_SAVE_CHANGES, STR_NULL}, // GDW_ACCEPT +{ WWT_PUSHTXTBTN, RESIZE_NONE, 3, 186, 266, 270, 281, STR_012E_CANCEL, STR_NULL}, // GDW_CANCEL { WIDGETS_END}, }; /* Window definition for the game difficulty settings window */ static const WindowDesc _game_difficulty_desc = { - WDP_CENTER, WDP_CENTER, 370, 279, 370, 279, + WDP_CENTER, WDP_CENTER, 370, 284, 370, 284, WC_GAME_OPTIONS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _game_difficulty_widgets, Index: src/saveload.cpp =================================================================== --- src/saveload.cpp (revision 12795) +++ src/saveload.cpp (working copy) @@ -34,7 +34,7 @@ #include "table/strings.h" -extern const uint16 SAVEGAME_VERSION = 93; +extern const uint16 SAVEGAME_VERSION = 94; uint16 _sl_version; ///< the major savegame version identifier byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! Index: src/player_base.h =================================================================== --- src/player_base.h (revision 12795) +++ src/player_base.h (working copy) @@ -35,6 +35,8 @@ Money player_money; Money current_loan; + Money max_loan; + byte interest_rate; byte player_color; Livery livery[LS_END]; @@ -95,5 +97,7 @@ } Money CalculateCompanyValue(const Player *p); +Money CalculateMaxLoan(const Player *p); +byte CalculateCurrentInterest(const Player *p); #endif /* PLAYER_BASE_H */ Index: src/settings_type.h =================================================================== --- src/settings_type.h (revision 12795) +++ src/settings_type.h (working copy) @@ -9,7 +9,7 @@ #include "date_type.h" #include "town_type.h" -#define GAME_DIFFICULTY_NUM 18 +#define GAME_DIFFICULTY_NUM 20 /** Specific type for Game Difficulty to ease changing the type */ typedef uint16 GDType; @@ -19,7 +19,7 @@ GDType number_towns; GDType number_industries; GDType max_loan; - GDType initial_interest; + GDType initial_min_interest; GDType vehicle_costs; GDType competitor_speed; GDType competitor_intelligence; ///< no longer in use @@ -32,6 +32,8 @@ GDType line_reverse_mode; GDType disasters; GDType town_council_tolerance; ///< minimum required town ratings to be allowed to demolish stuff + GDType initial_max_interest; + GDType max_loan_percentage; }; struct GameOptions { Index: src/ai/default/default.cpp =================================================================== --- src/ai/default/default.cpp (revision 12795) +++ src/ai/default/default.cpp (working copy) @@ -3923,7 +3923,7 @@ } } else if (p->player_money < base * 500) { // Increase loan - if (p->current_loan < _economy.max_loan && + if (p->current_loan < p->max_loan && p->num_valid_stat_ent >= 2 && -(p->old_economy[0].expenses + p->old_economy[1].expenses) < base * 60) { DoCommand(0, 0, 0, DC_EXEC, CMD_INCREASE_LOAN); Index: src/economy.cpp =================================================================== --- src/economy.cpp (revision 12795) +++ src/economy.cpp (working copy) @@ -135,6 +135,21 @@ return max(value, (Money)1); } +Money CalculateMaxLoan(const Player *p) +{ + /* Maximum loan for player is at least the global value and at least his + * half company value */ + return max(_economy.max_loan, p->old_economy[0].company_value*_opt.diff.max_loan_percentage/100 - p->old_economy[0].company_value*_opt.diff.max_loan_percentage/100 % 50000); + +} + +byte CalculateCurrentInterest(const Player *p) +{ + /* Player-based interest rate is a linear mapping of p->current_loan in + * [0..p->max_loan] to [min_interest_rate..max_interest_rate] */ + return _economy.min_interest_rate + ((int64)(_economy.max_interest_rate-_economy.min_interest_rate)*p->current_loan+p->max_loan/2)/p->max_loan; +} + /** if update is set to true, the economy is updated with this score * (also the house is updated, should only be true in the on-tick event) * @param update the economy with calculated score @@ -761,16 +776,27 @@ InvalidateWindow(WC_PAYMENT_RATES, 0); } +static void PlayersCalculateLoanAndInterest() +{ + Player* p; + + FOR_ALL_PLAYERS(p) { + if (!p->is_active) continue; + + p->max_loan = CalculateMaxLoan(p); + p->interest_rate = CalculateCurrentInterest(p); + } +} + static void PlayersPayInterest() { const Player* p; - int interest = _economy.interest_rate * 54; FOR_ALL_PLAYERS(p) { if (!p->is_active) continue; _current_player = p->index; - + int interest = p->interest_rate * 54; SubtractMoneyFromPlayer(CommandCost(EXPENSES_LOAN_INT, (Money)BigMulSU(p->current_loan, interest, 16))); SubtractMoneyFromPlayer(CommandCost(EXPENSES_OTHER, _price.station_value >> 2)); @@ -904,9 +930,10 @@ _price_frac[i] = 0; } - _economy.interest_rate = _opt.diff.initial_interest; - _economy.infl_amount = _opt.diff.initial_interest; - _economy.infl_amount_pr = max(0, _opt.diff.initial_interest - 1); + _economy.min_interest_rate = _opt.diff.initial_min_interest; + _economy.max_interest_rate = _opt.diff.initial_max_interest; + _economy.infl_amount = _opt.diff.initial_min_interest; + _economy.infl_amount_pr = max(0, _opt.diff.initial_min_interest - 1); _economy.max_loan_unround = _economy.max_loan = _opt.diff.max_loan * 1000; _economy.fluct = GB(Random(), 0, 8) + 168; } @@ -1811,6 +1838,7 @@ PlayersGenStatistics(); if (_patches.inflation && _cur_year < MAX_YEAR) AddInflation(); + PlayersCalculateLoanAndInterest(); PlayersPayInterest(); /* Reset the _current_player flag */ _current_player = OWNER_NONE; @@ -1995,7 +2023,8 @@ SLE_CONDVAR(Economy, max_loan_unround, SLE_INT64, 65, SL_MAX_VERSION), SLE_CONDVAR(Economy, max_loan_unround_fract, SLE_UINT16, 70, SL_MAX_VERSION), SLE_VAR(Economy, fluct, SLE_FILE_I16 | SLE_VAR_I32), - SLE_VAR(Economy, interest_rate, SLE_UINT8), + SLE_VAR(Economy, min_interest_rate,SLE_UINT8), + SLE_CONDVAR(Economy, max_interest_rate,SLE_UINT8, 94, SL_MAX_VERSION), SLE_VAR(Economy, infl_amount, SLE_UINT8), SLE_VAR(Economy, infl_amount_pr, SLE_UINT8), SLE_END() Index: src/players.cpp =================================================================== --- src/players.cpp (revision 12795) +++ src/players.cpp (working copy) @@ -494,6 +494,8 @@ p->is_active = true; p->player_money = p->current_loan = 100000; + p->max_loan = _economy.max_loan; + p->interest_rate = CalculateCurrentInterest(p); p->is_ai = is_ai; _players_ai[p->index].state = 5; // AIS_WANT_NEW_ROUTE @@ -1101,6 +1103,8 @@ SLE_CONDVAR(Player, current_loan, SLE_VAR_I64 | SLE_FILE_I32, 0, 64), SLE_CONDVAR(Player, current_loan, SLE_INT64, 65, SL_MAX_VERSION), + SLE_CONDVAR(Player, max_loan, SLE_INT64, 94, SL_MAX_VERSION), + SLE_CONDVAR(Player, interest_rate, SLE_UINT8, 94, SL_MAX_VERSION), SLE_VAR(Player, player_color, SLE_UINT8), SLE_VAR(Player, player_money_fraction, SLE_UINT8), Index: src/economy_type.h =================================================================== --- src/economy_type.h (revision 12795) +++ src/economy_type.h (working copy) @@ -16,7 +16,8 @@ Money max_loan_unround; ///< Economy fluctuation status uint16 max_loan_unround_fract; ///< Fraction of the unrounded max loan int fluct; - byte interest_rate; ///< Interest + byte min_interest_rate; ///< Minimum interest (for small loans) + byte max_interest_rate; ///< Maximum interest (near maximum loan) byte infl_amount; ///< inflation amount byte infl_amount_pr; ///< "floating" portion of inflation }; Index: src/oldloader.cpp =================================================================== --- src/oldloader.cpp (revision 12795) +++ src/oldloader.cpp (working copy) @@ -1365,7 +1365,7 @@ OCL_SVAR( OC_UINT16, GameDifficulty, number_towns ), OCL_SVAR( OC_UINT16, GameDifficulty, number_industries ), OCL_SVAR( OC_UINT16, GameDifficulty, max_loan ), - OCL_SVAR( OC_UINT16, GameDifficulty, initial_interest ), + OCL_SVAR( OC_UINT16, GameDifficulty, initial_min_interest ), OCL_SVAR( OC_UINT16, GameDifficulty, vehicle_costs ), OCL_SVAR( OC_UINT16, GameDifficulty, competitor_speed ), OCL_SVAR( OC_UINT16, GameDifficulty, competitor_intelligence ), @@ -1568,7 +1568,7 @@ OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount ), OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount_pr ), - OCL_VAR ( OC_UINT8, 1, &_economy.interest_rate ), + OCL_VAR ( OC_UINT8, 1, &_economy.min_interest_rate ), OCL_NULL( 1 ), // available airports OCL_VAR ( OC_UINT8, 1, &_opt.road_side ), OCL_VAR ( OC_UINT8, 1, &_opt.town_name ), Index: src/misc_cmd.cpp =================================================================== --- src/misc_cmd.cpp (revision 12795) +++ src/misc_cmd.cpp (working copy) @@ -128,36 +128,38 @@ /** Increase the loan of your company. * @param tile unused * @param flags operation to perform - * @param p1 unused - * @param p2 when 0: loans LOAN_INTERVAL + * @param p1 scale + * @param p2 when 0: loans LOAN_INTERVAL * 10^scale * when 1: loans the maximum loan permitting money (press CTRL), */ CommandCost CmdIncreaseLoan(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { Player *p = GetPlayer(_current_player); - if (p->current_loan >= _economy.max_loan) { - SetDParam(0, _economy.max_loan); - return_cmd_error(STR_702B_MAXIMUM_PERMITTED_LOAN); - } - Money loan; switch (p2) { default: return CMD_ERROR; // Invalid method case 0: // Take some extra loan loan = (IsHumanPlayer(_current_player) || _patches.ainew_active) ? LOAN_INTERVAL : LOAN_INTERVAL_OLD_AI; + while (p1-- > 0) loan *= 10; break; case 1: // Take a loan as big as possible - loan = _economy.max_loan - p->current_loan; + loan = p->max_loan - p->current_loan; break; } + if (p->current_loan + loan > p->max_loan) { + SetDParam(0, p->max_loan); + return_cmd_error(STR_702B_MAXIMUM_PERMITTED_LOAN); + } + /* Overflow protection */ if (p->player_money + p->current_loan + loan < p->player_money) return CMD_ERROR; if (flags & DC_EXEC) { p->player_money += loan; p->current_loan += loan; + p->interest_rate = CalculateCurrentInterest(p); InvalidatePlayerWindows(p); } @@ -167,8 +169,8 @@ /** Decrease the loan of your company. * @param tile unused * @param flags operation to perform - * @param p1 unused - * @param p2 when 0: pays back LOAN_INTERVAL + * @param p1 scale + * @param p2 when 0: loans LOAN_INTERVAL * 10^scale * when 1: pays back the maximum loan permitting money (press CTRL), */ CommandCost CmdDecreaseLoan(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) @@ -181,7 +183,9 @@ switch (p2) { default: return CMD_ERROR; // Invalid method case 0: // Pay back one step - loan = min(p->current_loan, (Money)(IsHumanPlayer(_current_player) || _patches.ainew_active) ? LOAN_INTERVAL : LOAN_INTERVAL_OLD_AI); + loan = (Money)(IsHumanPlayer(_current_player) || _patches.ainew_active) ? LOAN_INTERVAL : LOAN_INTERVAL_OLD_AI; + while (p1-- > 0) loan *= 10; + loan = min(p->current_loan, loan); break; case 1: // Pay back as much as possible loan = max(min(p->current_loan, p->player_money), (Money)LOAN_INTERVAL); @@ -197,6 +201,7 @@ if (flags & DC_EXEC) { p->player_money -= loan; p->current_loan -= loan; + p->interest_rate = CalculateCurrentInterest(p); InvalidatePlayerWindows(p); } return CommandCost(); Index: src/player_gui.cpp =================================================================== --- src/player_gui.cpp (revision 12795) +++ src/player_gui.cpp (working copy) @@ -41,6 +41,18 @@ }; assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(facesel_d)); +/* player finance window */ +struct finance_d { + bool small_win; + byte borrow_interval_scale; + byte repay_interval_scale; + Money borrow_interval; + Money repay_interval; +}; +assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(finance_d)); + +enum { MAX_LOAN_INTERVAL_SCALE = 255 }; + enum { FIRST_GUI_CALL = INT_MAX, ///< default value to specify thuis is the first call of the resizable gui }; @@ -48,14 +60,14 @@ static void DoShowPlayerFinances(PlayerID player, bool show_small, bool show_stickied, int top = FIRST_GUI_CALL, int left = FIRST_GUI_CALL); static void DoSelectPlayerFace(PlayerID player, bool show_big, int top = FIRST_GUI_CALL, int left = FIRST_GUI_CALL); -static void DrawPlayerEconomyStats(const Player *p, byte mode) +static void DrawPlayerEconomyStats(const Player *p, bool small_win) { int x, y, i, j, year; const Money (*tbl)[EXPENSES_END]; Money sum, cost; StringID str; - if (!(mode & 1)) { // normal sized economics window (mode&1) is minimized status + if (!small_win) { // normal sized economics window (mode&1) is minimized status /* draw categories */ DrawStringCenterUnderline(61, 15, STR_700F_EXPENDITURE_INCOME, TC_FROMSTRING); for (i = 0; i != EXPENSES_END; i++) @@ -99,8 +111,11 @@ y = 27 + 10 * EXPENSES_END + 14; + SetDParam(0, p->interest_rate); + DrawString(202, y, STR_CURRENT_INTEREST, TC_FROMSTRING); + /* draw max loan aligned to loan below (y += 10) */ - SetDParam(0, _economy.max_loan); + SetDParam(0, p->max_loan); DrawString(202, y + 10, STR_MAX_LOAN, TC_FROMSTRING); } else { y = 15; @@ -125,9 +140,13 @@ } enum PlayerFinancesWindowWidgets { - PFW_WIDGET_TOGGLE_SIZE = 2, - PFW_WIDGET_INCREASE_LOAN = 6, - PFW_WIDGET_REPAY_LOAN = 7, + PFW_WIDGET_TOGGLE_SIZE = 2, + PFW_WIDGET_INCREASE_LOAN_MINUS = 6, + PFW_WIDGET_INCREASE_LOAN, + PFW_WIDGET_INCREASE_LOAN_PLUS, + PFW_WIDGET_REPAY_LOAN_MINUS, + PFW_WIDGET_REPAY_LOAN, + PFW_WIDGET_REPAY_LOAN_PLUS, }; static const Widget _player_finances_widgets[] = { @@ -137,8 +156,12 @@ { WWT_STICKYBOX, RESIZE_NONE, 14, 395, 406, 0, 13, 0x0, STR_STICKY_BUTTON}, { WWT_PANEL, RESIZE_NONE, 14, 0, 406, 14, 39 + 10 * EXPENSES_END, 0x0, STR_NULL}, { WWT_PANEL, RESIZE_NONE, 14, 0, 406, 40 + 10 * EXPENSES_END, 73 + 10 * EXPENSES_END, 0x0, STR_NULL}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 202, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_7029_BORROW, STR_7035_INCREASE_SIZE_OF_LOAN}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 203, 406, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_702A_REPAY, STR_7036_REPAY_PART_OF_LOAN}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 10, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_LOAN_INTERVAL_MINUS, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 11, 191, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_7029_BORROW, STR_7035_INCREASE_SIZE_OF_LOAN}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 192, 202, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_LOAN_INTERVAL_PLUS, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 203, 213, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_LOAN_INTERVAL_MINUS, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 214, 395, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_702A_REPAY, STR_7036_REPAY_PART_OF_LOAN}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 396, 406, 74 + 10 * EXPENSES_END, 85 + 10 * EXPENSES_END, STR_LOAN_INTERVAL_PLUS, STR_NULL}, { WIDGETS_END}, }; @@ -149,8 +172,12 @@ { WWT_STICKYBOX, RESIZE_NONE, 14, 268, 279, 0, 13, 0x0, STR_STICKY_BUTTON}, { WWT_EMPTY, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL}, { WWT_PANEL, RESIZE_NONE, 14, 0, 279, 14, 47, STR_NULL, STR_NULL}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 139, 48, 59, STR_7029_BORROW, STR_7035_INCREASE_SIZE_OF_LOAN}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 140, 279, 48, 59, STR_702A_REPAY, STR_7036_REPAY_PART_OF_LOAN}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 7, 48, 59, STR_LOAN_INTERVAL_MINUS, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 8, 131, 48, 59, STR_7029_BORROW, STR_7035_INCREASE_SIZE_OF_LOAN}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 132, 139, 48, 59, STR_LOAN_INTERVAL_PLUS, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 140, 147, 48, 59, STR_LOAN_INTERVAL_MINUS, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 148, 271, 48, 59, STR_702A_REPAY, STR_7036_REPAY_PART_OF_LOAN}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 272, 279, 48, 59, STR_LOAN_INTERVAL_PLUS, STR_NULL}, { WIDGETS_END}, }; @@ -163,58 +190,166 @@ const Player *p = GetPlayer(player); /* Recheck the size of the window as it might need to be resized due to the local player changing */ - int new_height = ((player != _local_player) ? 0 : 12) + ((WP(w, def_d).data_1 != 0) ? 48 : 74 + 10 * EXPENSES_END); + int new_height = ((player != _local_player) ? 0 : 12) + ((WP(w, finance_d).small_win) ? 48 : 204); if (w->height != new_height) { /* Make window dirty before and after resizing */ SetWindowDirty(w); w->height = new_height; SetWindowDirty(w); + w->SetWidgetHiddenState(PFW_WIDGET_INCREASE_LOAN_MINUS, player != _local_player); w->SetWidgetHiddenState(PFW_WIDGET_INCREASE_LOAN, player != _local_player); + w->SetWidgetHiddenState(PFW_WIDGET_INCREASE_LOAN_PLUS, player != _local_player); + w->SetWidgetHiddenState(PFW_WIDGET_REPAY_LOAN_MINUS, player != _local_player); w->SetWidgetHiddenState(PFW_WIDGET_REPAY_LOAN, player != _local_player); + w->SetWidgetHiddenState(PFW_WIDGET_REPAY_LOAN_PLUS, player != _local_player); } /* Borrow button only shows when there is any more money to loan */ - w->SetWidgetDisabledState(PFW_WIDGET_INCREASE_LOAN, p->current_loan == _economy.max_loan); + w->SetWidgetDisabledState(PFW_WIDGET_INCREASE_LOAN, p->current_loan >= p->max_loan); + if (WP(w, finance_d).borrow_interval > p->max_loan - p->current_loan) { + WP(w, finance_d).borrow_interval_scale = MAX_LOAN_INTERVAL_SCALE; + } + w->SetWidgetDisabledState(PFW_WIDGET_INCREASE_LOAN_MINUS, p->current_loan >= p->max_loan || WP(w, finance_d).borrow_interval_scale == 0); + w->SetWidgetDisabledState(PFW_WIDGET_INCREASE_LOAN_PLUS, p->current_loan >= p->max_loan || WP(w, finance_d).borrow_interval_scale == MAX_LOAN_INTERVAL_SCALE); /* Repay button only shows when there is any more money to repay */ w->SetWidgetDisabledState(PFW_WIDGET_REPAY_LOAN, player != _local_player || p->current_loan == 0); + if (WP(w, finance_d).repay_interval > p->current_loan) { + WP(w, finance_d).repay_interval_scale = MAX_LOAN_INTERVAL_SCALE; + } + w->SetWidgetDisabledState(PFW_WIDGET_REPAY_LOAN_MINUS, player != _local_player || p->current_loan == 0 || WP(w, finance_d).repay_interval_scale == 0); + w->SetWidgetDisabledState(PFW_WIDGET_REPAY_LOAN_PLUS, player != _local_player || p->current_loan == 0 || WP(w, finance_d).repay_interval_scale == MAX_LOAN_INTERVAL_SCALE); SetDParam(0, p->index); SetDParam(1, p->index); - SetDParam(2, LOAN_INTERVAL); + SetDParam(2, WP(w, finance_d).borrow_interval_scale == MAX_LOAN_INTERVAL_SCALE ? STR_LOAN_MAX : STR_LOAN_AMOUNT); + SetDParam(3, WP(w, finance_d).borrow_interval); + SetDParam(4, WP(w, finance_d).repay_interval_scale == MAX_LOAN_INTERVAL_SCALE ? STR_LOAN_MAX : STR_LOAN_AMOUNT); + SetDParam(5, WP(w, finance_d).repay_interval); DrawWindowWidgets(w); - DrawPlayerEconomyStats(p, (byte)WP(w, def_d).data_1); + DrawPlayerEconomyStats(p, WP(w, finance_d).small_win); } break; case WE_CLICK: switch (e->we.click.widget) { case PFW_WIDGET_TOGGLE_SIZE: {/* toggle size */ - byte mode = (byte)WP(w, def_d).data_1; + bool small_win = WP(w, finance_d).small_win; bool stickied = !!(w->flags4 & WF_STICKY); int oldtop = w->top; ///< current top position of the window before closing it int oldleft = w->left; ///< current left position of the window before closing it PlayerID player = (PlayerID)w->window_number; - DeleteWindow(w); /* Open up the (toggled size) Finance window at the same position as the previous */ - DoShowPlayerFinances(player, !HasBit(mode, 0), stickied, oldtop, oldleft); + DoShowPlayerFinances(player, !small_win, stickied, oldtop, oldleft); } break; - case PFW_WIDGET_INCREASE_LOAN: /* increase loan */ - DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_INCREASE_LOAN | CMD_MSG(STR_702C_CAN_T_BORROW_ANY_MORE_MONEY)); + + case PFW_WIDGET_INCREASE_LOAN_MINUS: { /* Decrease loan interval */ + PlayerID player = (PlayerID)w->window_number; + const Player *p = GetPlayer(player); + + /* If this was set to maximum we must try for the highest value */ + if (WP(w, finance_d).borrow_interval_scale == MAX_LOAN_INTERVAL_SCALE) { + WP(w, finance_d).borrow_interval = LOAN_INTERVAL; + WP(w, finance_d).borrow_interval_scale = 0; + while (WP(w, finance_d).borrow_interval <= p->max_loan - p->current_loan) { + WP(w, finance_d).borrow_interval *= 10; + WP(w, finance_d).borrow_interval_scale++; + } + } + + assert(WP(w, finance_d).borrow_interval_scale > 0); + WP(w, finance_d).borrow_interval /= 10; + WP(w, finance_d).borrow_interval_scale--; + + w->InvalidateWidget(PFW_WIDGET_INCREASE_LOAN); + w->InvalidateWidget(PFW_WIDGET_INCREASE_LOAN_MINUS); + w->InvalidateWidget(PFW_WIDGET_INCREASE_LOAN_PLUS); break; + } + case PFW_WIDGET_INCREASE_LOAN_PLUS: { /* Increase loan interval */ + PlayerID player = (PlayerID)w->window_number; + const Player *p = GetPlayer(player); - case PFW_WIDGET_REPAY_LOAN: /* repay loan */ - DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_DECREASE_LOAN | CMD_MSG(STR_702F_CAN_T_REPAY_LOAN)); + WP(w, finance_d).borrow_interval *= 10; + WP(w, finance_d).borrow_interval_scale++; + if (WP(w, finance_d).borrow_interval > p->max_loan - p->current_loan) { + WP(w, finance_d).borrow_interval_scale = MAX_LOAN_INTERVAL_SCALE; + } + w->InvalidateWidget(PFW_WIDGET_INCREASE_LOAN); + w->InvalidateWidget(PFW_WIDGET_INCREASE_LOAN_MINUS); + w->InvalidateWidget(PFW_WIDGET_INCREASE_LOAN_PLUS); break; - } - break; + } + + case PFW_WIDGET_INCREASE_LOAN: { /* increase loan */ + PlayerID player = (PlayerID)w->window_number; + const Player *p = GetPlayer(player); + + bool do_max_borrow = _ctrl_pressed || + WP(w, finance_d).borrow_interval_scale == MAX_LOAN_INTERVAL_SCALE || + ((p->max_loan - p->current_loan < WP(w, finance_d).borrow_interval) && (p->max_loan - p->current_loan >= LOAN_INTERVAL)); + + DoCommandP(0, WP(w, finance_d).borrow_interval_scale, do_max_borrow, NULL, CMD_INCREASE_LOAN | CMD_MSG(STR_702C_CAN_T_BORROW_ANY_MORE_MONEY)); + break; + } + + case PFW_WIDGET_REPAY_LOAN_MINUS: { /* Decrease loan interval */ + PlayerID player = (PlayerID)w->window_number; + const Player *p = GetPlayer(player); + + /* If this was set to maximum we must try for the highest value */ + if (WP(w, finance_d).repay_interval_scale == MAX_LOAN_INTERVAL_SCALE) { + WP(w, finance_d).repay_interval = LOAN_INTERVAL; + WP(w, finance_d).repay_interval_scale = 0; + while (WP(w, finance_d).repay_interval <= p->current_loan) { + WP(w, finance_d).repay_interval *= 10; + WP(w, finance_d).repay_interval_scale++; + } + } + + assert(WP(w, finance_d).repay_interval_scale > 0); + WP(w, finance_d).repay_interval /= 10; + WP(w, finance_d).repay_interval_scale--; + w->InvalidateWidget(PFW_WIDGET_REPAY_LOAN); + w->InvalidateWidget(PFW_WIDGET_REPAY_LOAN_MINUS); + w->InvalidateWidget(PFW_WIDGET_REPAY_LOAN_PLUS); + break; + } + case PFW_WIDGET_REPAY_LOAN_PLUS: { /* Increase loan interval */ + PlayerID player = (PlayerID)w->window_number; + const Player *p = GetPlayer(player); + + WP(w, finance_d).repay_interval *= 10; + WP(w, finance_d).repay_interval_scale++; + if (WP(w, finance_d).repay_interval > p->current_loan) { + WP(w, finance_d).repay_interval_scale = MAX_LOAN_INTERVAL_SCALE; + } + w->InvalidateWidget(PFW_WIDGET_REPAY_LOAN); + w->InvalidateWidget(PFW_WIDGET_REPAY_LOAN_MINUS); + w->InvalidateWidget(PFW_WIDGET_REPAY_LOAN_PLUS); + break; + } + + case PFW_WIDGET_REPAY_LOAN: { /* repay loan */ + PlayerID player = (PlayerID)w->window_number; + const Player *p = GetPlayer(player); + + bool do_max_repay = _ctrl_pressed || + WP(w, finance_d).repay_interval_scale == MAX_LOAN_INTERVAL_SCALE || + (p->player_money < WP(w, finance_d).repay_interval) && (p->player_money >= LOAN_INTERVAL); + + DoCommandP(0, WP(w, finance_d).repay_interval_scale, do_max_repay, NULL, CMD_DECREASE_LOAN | CMD_MSG(STR_702F_CAN_T_REPAY_LOAN)); + } + break; } + } } + static const WindowDesc _player_finances_desc = { WDP_AUTO, WDP_AUTO, 407, 86 + 10 * EXPENSES_END, 407, 86 + 10 * EXPENSES_END, WC_FINANCES, WC_NONE, @@ -249,7 +384,11 @@ Window *w = AllocateWindowDescFront(show_small ? &_player_finances_small_desc : &_player_finances_desc, player); if (w != NULL) { w->caption_color = w->window_number; - WP(w, def_d).data_1 = show_small; + WP(w, finance_d).small_win = show_small; + WP(w, finance_d).borrow_interval_scale = 0; + WP(w, finance_d).repay_interval_scale = 0; + WP(w, finance_d).borrow_interval = LOAN_INTERVAL; + WP(w, finance_d).repay_interval = LOAN_INTERVAL; if (show_stickied) w->flags4 |= WF_STICKY; Index: src/openttd.cpp =================================================================== --- src/openttd.cpp (revision 12795) +++ src/openttd.cpp (working copy) @@ -2451,6 +2451,18 @@ } } + if (CheckSavegameVersion(94)) { + _economy.max_interest_rate = _economy.min_interest_rate; + _opt.diff.initial_max_interest = _opt.diff.initial_min_interest; + _opt.diff.max_loan_percentage = 0; + + Player *p; + FOR_ALL_PLAYERS(p) { + p->max_loan = CalculateMaxLoan(p); + p->interest_rate = CalculateCurrentInterest(p); + } + } + return InitializeWindowsAndCaches(); }