/* finance.c Bygfoot Football Manager -- a small and simple GTK2-based football management game. http://bygfoot.sourceforge.net Copyright (C) 2005 Gyözö Both (gyboth@bygfoot.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "callbacks.h" #include "finance.h" #include "fixture.h" #include "game_gui.h" #include "league.h" #include "maths.h" #include "misc.h" #include "option.h" #include "player.h" #include "team.h" #include "user.h" /** Weekly update of finances. */ void finance_update_user_weekly(User *user) { #ifdef DEBUG printf("finance_update_user_weekly\n"); #endif gint i; Team *tm = user->tm; Team *new_team = NULL; gfloat physio_factor[4] = {const_float("float_finance_physio_factor1"), const_float("float_finance_physio_factor2"), const_float("float_finance_physio_factor3"), const_float("float_finance_physio_factor4")}; gfloat scout_factor[4] = {const_float("float_finance_scout_factor1"), const_float("float_finance_scout_factor2"), const_float("float_finance_scout_factor3"), const_float("float_finance_scout_factor4")}; gfloat yc_factor[4] = {const_float("float_finance_yc_factor1"), const_float("float_finance_yc_factor2"), const_float("float_finance_yc_factor3"), const_float("float_finance_yc_factor4")}; if(user->money > G_MAXINT - 50000000) { user_event_add(user, EVENT_TYPE_CHARITY, -1, -1, NULL, NULL); user->money = (user->money - user->money % 2) / 2; } if(week % 2 == 1) { for(i=0;imoney_out[0][i] = user->money_out[1][i]; user->money_out[1][i] = 0; } for(i=0;imoney_in[0][i] = user->money_in[1][i]; user->money_in[1][i] = 0; } } user->money += user->sponsor.benefit; user->money_in[1][MON_IN_SPONSOR] += user->sponsor.benefit; user->sponsor.contract = MAX(user->sponsor.contract - 1, 0); user->money -= (gint)rint((gfloat)user->sponsor.benefit * (gfloat)user->youth_academy.percentage / 100); user->money_out[1][MON_OUT_YA] -= (gint)rint((gfloat)user->sponsor.benefit * (gfloat)user->youth_academy.percentage / 100); if(user->counters[COUNT_USER_NEW_SPONSOR] > 1) user->counters[COUNT_USER_NEW_SPONSOR]--; else if(user->sponsor.contract == 4 && math_rnd(0, 1) < ((1 - const_float("float_sponsor_continue_prob")) / (2 * (gfloat)const_int("int_user_success_offer_limit")) * user->counters[COUNT_USER_SUCCESS]) + (1 - (1 - const_float("float_sponsor_continue_prob")) / 2)) user->counters[COUNT_USER_NEW_SPONSOR] = -1; else if(user->sponsor.contract == 0) user->counters[COUNT_USER_NEW_SPONSOR] = 1; if(query_team_plays(tm, week - 1, 1)) for(i=0;iplayers->len;i++) { user->money_out[1][MON_OUT_WAGE] -= g_array_index(tm->players, Player, i).wage; user->money -= g_array_index(tm->players, Player, i).wage; if(player_of_idx_team(tm, i)->health > 0) { user->money -= (gint)(finance_wage_unit(tm) * physio_factor[user->physio % 10]); user->money_out[1][MON_OUT_PHYSIO] -= (gint)(finance_wage_unit(tm) * physio_factor[user->physio % 10]); } } user->money_out[1][MON_OUT_SCOUT] -= (gint)(finance_wage_unit(tm) * scout_factor[user->scout % 10]); user->money -= (gint)(finance_wage_unit(tm) * scout_factor[user->scout % 10]); if(user->youth_academy.players->len > 0) { user->money_out[1][MON_OUT_YC] -= (gint)(finance_wage_unit(tm) * yc_factor[user->youth_academy.coach % 10]); user->money -= (gint)(finance_wage_unit(tm) * yc_factor[user->youth_academy.coach % 10]); } user->debt = (gint)rint((gfloat)user->debt * (1 + user->debt_interest)); if(user->alr_start_week != 0 && week >= user->alr_start_week) { finance_pay_loan(user, user->alr_weekly_installment); if(user->debt == 0) { user->alr_start_week = user->alr_weekly_installment = 0; } } if(user->money < -finance_team_drawing_credit_loan(user->tm, FALSE) && user->counters[COUNT_USER_POSITIVE] == -1 && debug < 50) { user->counters[COUNT_USER_OVERDRAWN]++; if(user->counters[COUNT_USER_OVERDRAWN] <= const_int("int_finance_overdraw_limit")) { user_event_add(user, EVENT_TYPE_OVERDRAW, user->counters[COUNT_USER_OVERDRAWN], -1, NULL, NULL); user->counters[COUNT_USER_POSITIVE] = const_int("int_finance_overdraw_positive"); } } else if(user->counters[COUNT_USER_POSITIVE] > -1) user->counters[COUNT_USER_POSITIVE]--; if(user->counters[COUNT_USER_LOAN] > -1) user->counters[COUNT_USER_LOAN]--; if(user->counters[COUNT_USER_LOAN] == 0 && debug < 50) user_event_add(user, EVENT_TYPE_WARNING, -1, -1, NULL, _("You have to pay back your loan this week.")); if(user->counters[COUNT_USER_POSITIVE] == 0 && debug < 50) user_event_add(user, EVENT_TYPE_WARNING, -1, -1, NULL, _("Your bank account has to be above your drawing credit limit next week.")); if(((user->counters[COUNT_USER_LOAN] == -1 && user->debt != 0) || (user->counters[COUNT_USER_POSITIVE] == -1 && user->money < -finance_team_drawing_credit_loan(user->tm, FALSE))) && debug < 50) { new_team = team_get_new(tm, TRUE); user_event_add(user, EVENT_TYPE_FIRE_FINANCE, -1, -1, new_team, NULL); } } /** Return a base value for team finances. Scout wages etc. depend on this value. */ gfloat finance_wage_unit(const Team *tm) { #ifdef DEBUG printf("finance_wage_unit\n"); #endif gint i; gfloat sum = 0; for(i=0;iplayers->len;i++) sum += player_of_idx_team(tm, i)->wage; return sum / (gfloat)tm->players->len; } /** Calculate the drawing credit or maximum loan of a team based on player values and stadium data. */ gint finance_team_drawing_credit_loan(const Team *tm, gboolean loan) { #ifdef DEBUG printf("finance_team_drawing_credit_loan\n"); #endif gint i; gfloat sum = 0; for(i=0;iplayers->len;i++) sum += player_of_idx_team(tm, i)->value; sum = (sum * const_float("float_finance_credit_player_value_weight")) + ((gfloat)tm->stadium.capacity * powf(tm->stadium.safety, const_float("float_finance_credit_stadium_safety_exponent")) * const_float("float_finance_credit_stadium_weight")); sum *= (loan) ? const_float("float_finance_credit_factor_loan") : const_float("float_finance_credit_factor_drawing"); return (gint)rint(sum); } /** Contract a loan for the current user. @param value The amount of money. */ void finance_get_loan(gint value) { #ifdef DEBUG printf("finance_get_loan\n"); #endif gfloat debt_old = current_user.debt; gfloat debt_new = -value; current_user.money += value; current_user.debt -= value; if(current_user.counters[COUNT_USER_LOAN] == -1) { current_user.counters[COUNT_USER_LOAN] = const_int("int_finance_payback_weeks"); current_user.debt_interest = current_interest; } else { /** Calculate new interest in a way that the user can't take unfair advantage of new market interest. */ if(current_interest != current_user.debt_interest) { current_user.debt_interest = powf((debt_old * powf(1 + current_user.debt_interest, (gfloat)current_user.counters[COUNT_USER_LOAN]) + debt_new * powf(1 + current_interest, (gfloat)current_user.counters[COUNT_USER_LOAN])) / (gfloat)current_user.debt, 1 / (gfloat)current_user.counters[COUNT_USER_LOAN]) - 1; } } game_gui_print_message(_("You have %d weeks to pay back your loan."), current_user.counters[COUNT_USER_LOAN]); } /** Pay back some loan for the specified user. @param value The amount of money paid back. */ void finance_pay_loan(User *user, gint value) { #ifdef DEBUG printf("finance_pay_loan\n"); #endif gint add = (gint)rint((gfloat)value / (gfloat)(-user->debt) * (gfloat)const_int("int_finance_payback_weeks")); if(value > -user->debt) value = -user->debt; user->money -= value; user->debt += value; if(user->debt == 0) { user->counters[COUNT_USER_LOAN] = -1; user->alr_start_week = 0; user->alr_weekly_installment = 0; game_gui_print_message(_("You are free from debt.")); } else { user->counters[COUNT_USER_LOAN] = MIN(user->counters[COUNT_USER_LOAN] + add, const_int("int_finance_payback_weeks")); game_gui_print_message(_("You have %d weeks to pay back the rest of your loan."), user->counters[COUNT_USER_LOAN]); } } /** Return the cost of a stadium improvement. @param value The improvement value, either number of new seats or safety increase. @param capacity Whether seats are built or capacity increased. */ gint finance_get_stadium_improvement_cost(gfloat value, gboolean capacity) { #ifdef DEBUG printf("finance_get_stadium_improvement_cost\n"); #endif gfloat return_value; if(capacity) { return_value = finance_wage_unit(current_user.tm) * (value / (gfloat)const_int("int_stadium_improvement_base_seats"))* const_float("float_stadium_improvement_wage_unit_factor_seats"); if(value - (gfloat)const_int("int_stadium_improvement_base_seats") >= const_int("int_stadium_improvement_max_discount_seats")) return_value *= (1 - const_float("float_stadium_improvement_max_discount")); else return_value *= (1 - const_float("float_stadium_improvement_max_discount") * ((value - (gfloat)const_int("int_stadium_improvement_base_seats")) / (gfloat)const_int("int_stadium_improvement_max_discount_seats"))); } else { return_value = finance_wage_unit(current_user.tm) * (value / const_float("float_stadium_improvement_base_safety"))* const_float("float_stadium_improvement_wage_unit_factor_safety"); if(value - const_float("float_stadium_improvement_base_safety") >= const_float("float_stadium_improvement_max_discount_safety")) return_value *= (1 - const_float("float_stadium_improvement_max_discount")); else return_value *= (1 - const_float("float_stadium_improvement_max_discount") * ((value - const_float("float_stadium_improvement_base_safety")) / const_float("float_stadium_improvement_max_discount_safety"))); } return (gint)rint(return_value); } /** Return the expected duration of a stadium improvement. @param value The improvement value, either number of new seats or safety increase. @param capacity Whether seats are built or capacity increased. */ gint finance_get_stadium_improvement_duration(gfloat value, gboolean capacity) { #ifdef DEBUG printf("finance_get_stadium_improvement_duration\n"); #endif gint return_value; if(capacity) return_value = (gint)((value - 1) / (gfloat)const_int("int_stadium_improvement_base_seats")) + 1; else return_value = (gint)(((gint)rint(value * 100) - 1) / (gint)rint(const_float("float_stadium_improvement_base_safety") * 100)) + 1; return return_value; } /** Update the user's accounts depending on match type and attendance. @fix The fixture being examined. */ void finance_assign_game_money(const Fixture *fix) { #ifdef DEBUG printf("finance_assign_game_money\n"); #endif gint i; gint user_idx[2] = {team_is_user(fix->teams[0]), team_is_user(fix->teams[1])}; gfloat journey_factor = (fix->clid < ID_CUP_START || (fix->clid >= ID_CUP_START && query_league_cup_has_property(fix->clid, "national"))) ? const_float("float_game_finance_journey_factor_national") : const_float("float_game_finance_journey_factor_international"); gint ticket_income[2] = {0, 0}; if (fix->clid >= ID_CUP_START && ! g_array_index(cup_from_clid(fix->clid)->rounds, CupRound, fix->round).home_away) { ticket_income[0] = ticket_income[1] = fix->attendance * fix->teams[0]->stadium.ticket_price / 2; } else ticket_income[0] = fix->attendance * fix->teams[0]->stadium.ticket_price; for(i = 0; i < 2; i++) { if(user_idx[i] != -1) { usr(user_idx[i]).money += ticket_income[i]; usr(user_idx[i]).money_in[1][MON_IN_TICKET] += ticket_income[i]; usr(user_idx[i]).money -= (gint)rint((gfloat)ticket_income[i] * (gfloat)usr(user_idx[i]).youth_academy.percentage / 100); usr(user_idx[i]).money_out[1][MON_OUT_YA] -= (gint)rint((gfloat)ticket_income[i] * (gfloat)usr(user_idx[i]).youth_academy.percentage / 100); if(i == 0 && debug < 50) { fix->teams[0]->stadium.safety -= math_rnd(const_float("float_game_stadium_safety_deterioration_lower"), const_float("float_game_stadium_safety_deterioration_upper")); fix->teams[0]->stadium.safety = CLAMP(fix->teams[0]->stadium.safety, 0, 1); } if(i == 1 || !fix->home_advantage) { usr(user_idx[i]).money_out[1][MON_OUT_JOURNEY] -= (gint)(finance_wage_unit(fix->teams[i]) * journey_factor); usr(user_idx[i]).money -= (gint)(finance_wage_unit(fix->teams[i]) * journey_factor); } } } } /** Change the current interest on the market (random walk with three possibilities). */ void finance_update_current_interest(Bygfoot *bygfoot) { #ifdef DEBUG printf("finance_update_current_interest\n"); #endif current_interest += math_rndi(-1, 1) * const_float("float_finance_interest_step"); if(current_interest < const_float("float_finance_interest_lower")) current_interest = const_float("float_finance_interest_lower"); else if(current_interest > const_float("float_finance_interest_upper")) current_interest = const_float("float_finance_interest_upper"); } /** Calculate the weekly installment for an automatic loan repayment depending on the start week. */ gint finance_calculate_alr_weekly_installment(gint start_week) { #ifdef DEBUG printf("finance_calculate_alr_weekly_installment\n"); #endif gfloat debt_end; gfloat interest_factor; gfloat max_start_week; gfloat installment; gint weekly_installment; max_start_week = MIN(week + current_user.counters[COUNT_USER_LOAN], fixture_get_last_scheduled_week()); debt_end = current_user.debt * powf(1 + current_user.debt_interest, max_start_week - 1); interest_factor = (powf(1 + current_user.debt_interest, (gfloat)(max_start_week - start_week + 1)) - 1) / current_user.debt_interest; installment = -debt_end / interest_factor; weekly_installment = (gint)rint(installment); return (weekly_installment > installment) ? weekly_installment : weekly_installment + 1; } /** Calculate the start week for an automatic loan repayment depending on the weekly installment. */ gint finance_calculate_alr_start_week(gint weekly_installment) { #ifdef DEBUG printf("finance_calculate_alr_start_week\n"); #endif gint upper; gint start_week; gint installment; upper = MIN(week + current_user.counters[COUNT_USER_LOAN], fixture_get_last_scheduled_week()); for(start_week = week + 1; start_week <= upper; start_week++) { installment = finance_calculate_alr_weekly_installment(start_week); if(installment > weekly_installment) return start_week - 1; else if(installment == weekly_installment) return start_week; } return start_week; }