/* game.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 "cup.h" #include "finance.h" #include "fixture.h" #include "game.h" #include "game_gui.h" #include "league.h" #include "live_game.h" #include "main.h" #include "maths.h" #include "misc.h" #include "option.h" #include "player.h" #include "table.h" #include "team.h" #include "treeview.h" #include "user.h" #include "variables.h" /** Calculate attacking, defending and goalie values for the two teams of a fixture. @param fix The fixture we calculate. @param team_value The place to store the values. @param home_advantage The home advantage factor. @see #GameTeamValue */ void game_get_values(const Fixture *fix, gfloat team_values[][GAME_TEAM_VALUE_END], gfloat home_advantage) { #ifdef DEBUG printf("game_get_values\n"); #endif gint i, j; Team *tm[2] = {fix->teams[0], fix->teams[1]}; gfloat style_factor; for(i=0;i<2;i++) { for(j=0;jstyle * const_float("float_game_style_factor"); team_values[i][GAME_TEAM_VALUE_GOALIE] = player_get_game_skill(player_of_idx_team(tm[i], 0), FALSE, TRUE) * (1 + home_advantage * (i == 0)); for(j=1;j<11;j++) if(player_of_idx_team(tm[i], j)->cskill > 0) { team_values[i][GAME_TEAM_VALUE_ATTACK] += game_get_player_contribution(player_of_idx_team(tm[i], j), GAME_TEAM_VALUE_ATTACK, TRUE); team_values[i][GAME_TEAM_VALUE_MIDFIELD] += game_get_player_contribution(player_of_idx_team(tm[i], j), GAME_TEAM_VALUE_MIDFIELD, TRUE); team_values[i][GAME_TEAM_VALUE_DEFEND] += game_get_player_contribution(player_of_idx_team(tm[i], j), GAME_TEAM_VALUE_DEFEND, TRUE); } for(j=GAME_TEAM_VALUE_DEFEND;jcpos - 1][type - GAME_TEAM_VALUE_DEFEND]; } /** Return a random attacking or defending player or the player that has to shoot the number_of_penalty'th penalty. @param tm The team we examine. @param player_type Whether we concentrate on attacking or defending players or would like to have a penalty shooting player. @param last_penalty The player that shot the last penalty. @param not_this_one A player to exclude. @param skills Whether to weight with skills, too. @return A player index. */ gint game_get_player(const Team *tm, gint player_type, gint last_penalty, gint not_this_one, gboolean skills) { #ifdef DEBUG printf("game_get_player\n"); #endif gint i, player = not_this_one; gfloat weights[3]; gfloat probs[10]; gfloat rndom; if(player_type == GAME_PLAYER_TYPE_ATTACK) { weights[0] = const_float("float_game_player_weight_attack_def"); weights[1] = const_float("float_game_player_weight_attack_mid"); weights[2] = const_float("float_game_player_weight_attack_fwd"); } else if(player_type == GAME_PLAYER_TYPE_MIDFIELD) { weights[0] = const_float("float_game_player_weight_midfield_def"); weights[1] = const_float("float_game_player_weight_midfield_mid"); weights[2] = const_float("float_game_player_weight_midfield_fwd"); } else if(player_type == GAME_PLAYER_TYPE_DEFEND) { weights[0] = const_float("float_game_player_weight_defend_def"); weights[1] = const_float("float_game_player_weight_defend_mid"); weights[2] = const_float("float_game_player_weight_defend_fwd"); } else if(player_type == GAME_PLAYER_TYPE_PENALTY) return game_get_penalty_taker(tm, last_penalty); else main_exit_program(EXIT_INT_NOT_FOUND, "game_get_player: unknown player type %d\n", player_type); game_get_player_probs(tm->players, probs, weights, skills); if(probs[9] > 0) { while(player == not_this_one) { rndom = math_rnd(0, probs[9]); if(rndom < probs[0]) player = player_of_idx_team(tm, 1)->id; else for(i=1;i<10;i++) if(rndom < probs[i] && rndom > probs[i - 1]) player = player_of_idx_team(tm, i + 1)->id; } } else { debug_print_message("game_get_player: All players injured or banned, apparently.\n"); g_print("%s %s player list:\n", league_cup_get_name_string(tm->clid), tm->name); for(i=0;iplayers->len;i++) { if(i < 10) g_print("prob %.3f ", probs[i]); g_print("%d %20s health %d cskill %.2f\n", i, player_of_idx_team(tm, i)->name, player_of_idx_team(tm, i)->health, player_of_idx_team(tm, i)->cskill); } main_exit_program(EXIT_INT_NOT_FOUND, NULL); return -1; } return player; } /** Write the probabilities for field players being picked (e.g. a random defender, or a player who gets injured) into the float array. Depends on skill mostly. @param players Player array. @param probs Array with probabilities that gets filled. @param weights How to weight the players depending on their position. @param skills Whether to weight with skills, too. */ void game_get_player_probs(GArray *players, gfloat *probs, gfloat *weights, gboolean skills) { #ifdef DEBUG printf("game_get_player_probs\n"); #endif gint i; probs[0] = (skills) ? player_get_game_skill(&g_array_index(players, Player, 1), FALSE, TRUE) * weights[g_array_index(players, Player, 1).cpos - 1] : weights[g_array_index(players, Player, 1).cpos - 1] * (g_array_index(players, Player, 1).cskill != 0); probs[0] *= (1 + (gfloat)g_array_index(players, Player, 1).streak * const_float_fast(float_player_streak_influence_skill)); for(i=1;i<10;i++) { probs[i] = probs[i - 1] + ((skills) ? player_get_game_skill(&g_array_index(players, Player, i + 1), FALSE, TRUE) * weights[g_array_index(players, Player, i + 1).cpos - 1] : weights[g_array_index(players, Player, i + 1).cpos - 1] * (g_array_index(players, Player, i + 1).cskill != 0)); probs[i] *= (1 + (gfloat)g_array_index(players, Player, i + 1).streak * const_float_fast(float_player_streak_influence_skill)); } } /** Return the player who's shooting the following penalty (at penalty shoot-out). @param tm The team that has the penalty. @param last_penalty The player that shot the last penalty. @return A player id. */ gint game_get_penalty_taker(const Team *tm, gint last_penalty) { #ifdef DEBUG printf("game_get_penalty_taker\n"); #endif gint i, return_value = -1; GPtrArray *players = g_ptr_array_new(); for(i=0;i<11;i++) if(player_of_idx_team(tm, i)->cskill != 0) g_ptr_array_add(players, player_of_idx_team(tm, i)); g_ptr_array_sort_with_data(players, (GCompareDataFunc)player_compare_func, GINT_TO_POINTER(PLAYER_COMPARE_ATTRIBUTE_GAME_SKILL)); if(last_penalty == -1 || last_penalty == ((Player*)g_ptr_array_index(players, players->len - 1))->id) return_value = ((Player*)g_ptr_array_index(players, 0))->id; for(i=0;ilen - 1;i++) if(last_penalty == ((Player*)g_ptr_array_index(players, i))->id) { return_value = ((Player*)g_ptr_array_index(players, i + 1))->id; break; } g_ptr_array_free(players, TRUE); return return_value; } /** Do some things that have to be done at the beginning of a game, e.g. calculate attendance etc. @param fix The fixture we examine. */ void game_initialize(Fixture *fix) { #ifdef DEBUG printf("game_initialize\n"); #endif gint i, j; gint user_idx[2] = {team_is_user(fix->teams[0]), team_is_user(fix->teams[1])}; if(fix->home_advantage) game_assign_attendance(fix); else game_assign_attendance_neutral(fix); if(!sett_int("int_opt_disable_finances")) finance_assign_game_money(fix); for(i=0;i<2;i++) { for(j=0;jteams[i]->players->len;j++) { if(j < 11) { if(g_array_index(fix->teams[i]->players, Player, j).cskill > 0) { player_games_goals_set(&g_array_index(fix->teams[i]->players, Player, j), fix->clid, PLAYER_VALUE_GAMES, 1); g_array_index(fix->teams[i]->players, Player, j). career[PLAYER_VALUE_GAMES]++; g_array_index(fix->teams[i]->players, Player, j). participation = TRUE; if(query_player_is_youth((&g_array_index(fix->teams[i]->players, Player, j)))) g_array_index(fix->teams[i]->players, Player, j).lsu += const_float("float_youth_lsu_addition_match"); player_streak_add_to_prob( &g_array_index(fix->teams[i]->players, Player, j), const_float("float_player_streak_add_startup")); } } else player_streak_add_to_prob( &g_array_index(fix->teams[i]->players, Player, j), const_float("float_player_streak_add_no_startup")); } if(user_idx[i] != -1) { usr(user_idx[i]).counters[COUNT_USER_SHOW_RES] = 1; } } } /** Find out how many spectators there were, depending on safety of the stadium etc. @param fix The match we examine. */ void game_assign_attendance(Fixture *fix) { #ifdef DEBUG printf("game_assign_attendance\n"); #endif Team *tm[2] = {fix->teams[0], fix->teams[1]}; gfloat factor = math_rnd(const_float("float_game_stadium_attendance_percentage_lower"), const_float("float_game_stadium_attendance_percentage_upper")) * powf(tm[0]->stadium.safety, const_float("float_game_stadium_attendance_safety_exponent")); gint max_att = MIN((gint)rint((gfloat)league_cup_average_capacity(tm[0]->clid) * const_float("float_game_stadium_attendance_average_exceed_factor") * math_rnd(0.9, 1.1)), tm[0]->stadium.capacity); if(fix->clid < ID_CUP_START && team_get_league_rank(tm[1], fix->clid) < (gint)rint((gfloat)league_from_clid(fix->clid)->teams->len * const_float("float_game_stadium_attendance_rank_percentage"))) factor *= const_float("float_game_stadium_attendance_rank_factor"); if(fix->clid >= ID_CUP_START) { if(cup_from_clid(fix->clid)->rounds->len - fix->round <= const_int("int_game_stadium_attendance_cup_rounds_full_house")) factor = 1; else if(query_league_cup_has_property(fix->clid, "national")) factor *= const_float("float_game_stadium_attendance_cup_national_factor"); else factor *= const_float("float_game_stadium_attendance_cup_international_factor"); } /* calculate the differnce in ticket prices has on attendance */ gfloat delta_price = tm[0]->stadium.ticket_price - const_int("int_team_stadium_ticket_price"); gfloat price_factor = math_gauss_dist(0.92, 1.08) * 1/(1+const_float("float_team_stadium_price_attendance_factor")*delta_price); factor = MIN(factor * price_factor, 1); fix->attendance = MIN(MIN((gint)rint((gfloat)tm[0]->stadium.capacity * factor), max_att * factor), max_att); tm[0]->stadium.average_attendance = (gint)rint((gfloat)(tm[0]->stadium.average_attendance * tm[0]->stadium.games + fix->attendance) / (gfloat)(tm[0]->stadium.games + 1)); tm[0]->stadium.possible_attendance += tm[0]->stadium.capacity; tm[0]->stadium.games++; } /** Assign attendance for a match on neutral ground. */ void game_assign_attendance_neutral(Fixture *fix) { #ifdef DEBUG printf("game_assign_attendance_neutral\n"); #endif const GPtrArray *teamsp = (GPtrArray*)league_cup_get_teams(fix->clid); gfloat av_att = (fix->clid >= ID_CUP_START && query_league_cup_has_property(fix->clid, "international") && teamsp->len > 0) ? (gfloat)league_cup_average_capacity(fix->clid) : (gfloat)league_cup_average_capacity(lig(0).id); fix->attendance = (gint)rint(av_att * math_rnd(const_float("float_game_stadium_attendance_neutral_lower"), const_float("float_game_stadium_attendance_neutral_upper"))); } /** Save the team states in the current live game when a pause occurs. */ void game_save_team_states(void) { #ifdef DEBUG printf("game_save_team_states\n"); #endif gint i, j; const Team *teams[2] = {usr(stat2).live_game.fix->teams[0], usr(stat2).live_game.fix->teams[1]}; for(i=0;i<2;i++) { usr(stat2).live_game.team_state[i].structure = teams[i]->structure; usr(stat2).live_game.team_state[i].style = teams[i]->style; usr(stat2).live_game.team_state[i].boost = teams[i]->boost; for(j=0;j<11;j++) usr(stat2).live_game.team_state[i].player_ids[j] = player_of_idx_team(teams[i], j)->id; } } /** Check whether the number of substitutions during a live game pause isn't too high. @return TRUE if the subs were ok, FALSE else. */ gboolean game_check_live_game_resume_state(void) { #ifdef DEBUG printf("game_check_live_game_resume_state\n"); #endif gint i, j; gint subs[2] = {0, 0}; const Team *teams[2] = {usr(stat2).live_game.fix->teams[0], usr(stat2).live_game.fix->teams[1]}; gboolean return_value = TRUE; for(i=0;i<2;i++) { for(j=0;j<11;j++) if(!query_integer_is_in_array(player_of_idx_team(teams[i], j)->id, usr(stat2).live_game.team_state[i].player_ids, 11)) subs[i]++; if(subs[i] > usr(stat2).live_game.subs_left[i]) { game_reset_players(i); return_value = FALSE; } } return return_value; } /** Undo the player swaps the user made during a live game pause. @param idx The team index in the current live game fixture. */ void game_reset_players(gint idx) { #ifdef DEBUG printf("game_reset_players\n"); #endif gint i; Team *tm = usr(stat2).live_game.fix->teams[idx]; GArray *players = g_array_new(FALSE, FALSE, sizeof(Player)); for(i=0;i<11;i++) g_array_append_val(players, *player_of_id_team(tm, usr(stat2).live_game.team_state[idx].player_ids[i])); for(i=0;iplayers->len;i++) if(!query_integer_is_in_array(player_of_idx_team(tm, i)->id, usr(stat2).live_game.team_state[idx].player_ids, 11)) g_array_append_val(players, *player_of_idx_team(tm, i)); g_array_free(tm->players, TRUE); tm->players = players; team_change_structure(tm, team_find_appropriate_structure(tm)); team_rearrange(tm); treeview_show_user_player_list(); } /** Find pairs of substituted players after a live game pause. */ void game_get_subs(gint team_number, gint *subs_in, gint *subs_out) { #ifdef DEBUG printf("game_get_subs\n"); #endif gint i, cnt = 0; const Team *tm = usr(stat2).live_game.fix->teams[team_number]; gint current_players[11]; for(i=0;i<3;i++) subs_in[i] = subs_out[i] = -1; for(i=0;i<11;i++) { current_players[i] = player_of_idx_team(tm, i)->id; if(!query_integer_is_in_array(player_of_idx_team(tm, i)->id, usr(stat2).live_game.team_state[team_number].player_ids, 11)) { subs_in[cnt] = player_of_idx_team(tm, i)->id; cnt++; } } cnt = 0; for(i=0;i<11;i++) if(!query_integer_is_in_array(usr(stat2).live_game.team_state[team_number].player_ids[i], current_players, 11)) { subs_out[cnt] = usr(stat2).live_game.team_state[team_number].player_ids[i]; cnt++; } } /** Choose an injury for a player and adjust health values. @param pl The player that gets injured. */ void game_player_injury(Player *pl) { #ifdef DEBUG printf("game_player_injury\n"); #endif gint i; gfloat rndom; /* probabilities of different injuries */ gfloat injury_probs[13]={0, const_float("float_player_injury_concussion"), const_float("float_player_injury_pulled_muscle"), const_float("float_player_injury_hamstring"), const_float("float_player_injury_groin"), const_float("float_player_injury_frac_ankle"), const_float("float_player_injury_rib"), const_float("float_player_injury_leg"), const_float("float_player_injury_brok_ankle"), const_float("float_player_injury_arm"), const_float("float_player_injury_shoulder"), const_float("float_player_injury_ligament"), const_float("float_player_injury_career_stop")}; gint duration[12]={ math_gauss_disti(const_int("int_player_injury_duration_concussion") - const_int("int_player_injury_duration_dev_concussion"), const_int("int_player_injury_duration_concussion") + const_int("int_player_injury_duration_dev_concussion")), math_gauss_disti(const_int("int_player_injury_duration_pulled_muscle") - const_int("int_player_injury_duration_dev_pulled_muscle"), const_int("int_player_injury_duration_pulled_muscle") + const_int("int_player_injury_duration_dev_pulled_muscle")), math_gauss_disti(const_int("int_player_injury_duration_hamstring") - const_int("int_player_injury_duration_dev_hamstring"), const_int("int_player_injury_duration_hamstring") + const_int("int_player_injury_duration_dev_hamstring")), math_gauss_disti(const_int("int_player_injury_duration_groin") - const_int("int_player_injury_duration_dev_groin"), const_int("int_player_injury_duration_groin") + const_int("int_player_injury_duration_dev_groin")), math_gauss_disti(const_int("int_player_injury_duration_frac_ankle") - const_int("int_player_injury_duration_dev_frac_ankle"), const_int("int_player_injury_duration_frac_ankle") + const_int("int_player_injury_duration_dev_frac_ankle")), math_gauss_disti(const_int("int_player_injury_duration_rib") - const_int("int_player_injury_duration_dev_rib"), const_int("int_player_injury_duration_rib") + const_int("int_player_injury_duration_dev_rib")), math_gauss_disti(const_int("int_player_injury_duration_leg") - const_int("int_player_injury_duration_dev_leg"), const_int("int_player_injury_duration_leg") + const_int("int_player_injury_duration_dev_leg")), math_gauss_disti(const_int("int_player_injury_duration_brok_ankle") - const_int("int_player_injury_duration_dev_brok_ankle"), const_int("int_player_injury_duration_brok_ankle") + const_int("int_player_injury_duration_dev_brok_ankle")), math_gauss_disti(const_int("int_player_injury_duration_arm") - const_int("int_player_injury_duration_dev_arm"), const_int("int_player_injury_duration_arm") + const_int("int_player_injury_duration_dev_arm")), math_gauss_disti(const_int("int_player_injury_duration_shoulder") - const_int("int_player_injury_duration_dev_shoulder"), const_int("int_player_injury_duration_shoulder") + const_int("int_player_injury_duration_dev_shoulder")), math_gauss_disti(const_int("int_player_injury_duration_ligament") - const_int("int_player_injury_duration_dev_ligament"), const_int("int_player_injury_duration_ligament") + const_int("int_player_injury_duration_dev_ligament")), 50}; for(i=1;i<13;i++) injury_probs[i] += injury_probs[i - 1]; rndom = math_rnd(0, 1); for(i=1;i<13;i++) if(rndom >= injury_probs[i - 1] && rndom < injury_probs[i]) { pl->health = i; pl->recovery = duration[i - 1] + 1; pl->cskill = pl->fitness = 0; } if(pl->health == PLAYER_INJURY_CAREER_STOP && team_is_user(pl->team) != -1) user_event_add(&usr(team_is_user(pl->team)), EVENT_TYPE_PLAYER_CAREER_STOP, pl->id, -1, NULL, NULL); player_streak_add_to_prob( pl, const_float("float_player_streak_add_injury")); if(pl->streak == PLAYER_STREAK_HOT) { pl->streak = PLAYER_STREAK_NONE; player_streak_reset_count(pl); } } /** Calculate the probability of a foul event occurring. */ gfloat game_get_foul_prob(const LiveGame *live_game, const LiveGameUnit *unit) { gfloat prob; gint i; /* Base probability (a linear function of match time). */ prob = const_float("float_live_game_foul_base") + const_float("float_live_game_foul_max_inc") * MIN(1, (gfloat)(unit->minute) / 90); /* Add possible boost influence of the team not in possession. */ prob *= (1 + live_game->fix->teams[!unit->possession]->boost * const_float("float_team_boost_foul_factor")); /* Reduce probability depending on the current cards of the team not in possession, except if their boost is on. */ if(live_game->fix->teams[!unit->possession]->boost != 1) { for(i = 0; i < 11; i++) if(g_array_index(live_game->fix->teams[!unit->possession]->players, Player, i).card_status == PLAYER_CARD_STATUS_YELLOW) prob *= (1 - const_float("float_live_game_foul_prob_reduction_yellow")); else if(g_array_index(live_game->fix->teams[!unit->possession]->players, Player, i).card_status == PLAYER_CARD_STATUS_RED) prob *= (1 - const_float("float_live_game_foul_prob_reduction_red")); } return prob; } /** Return a factor influencing who's fouled whom depending on the states of the team boosts. @param boost1 Boost of the team in possession. @param boost2 Boost of the team not in possession. @return A factor. */ gfloat game_get_foul_possession_factor(gint boost1, gint boost2) { #ifdef DEBUG printf("game_get_foul_possession_factor\n"); #endif if(boost1 == boost2) return 1; if(abs(boost1 - boost2) == 1) return 1 + const_float("float_team_boost_foul_by_possession_factor1") * (1 - 2 * (boost1 < boost2)); return 1 + const_float("float_team_boost_foul_by_possession_factor2") * (1 - 2 * (boost1 < boost2)); } /** Substitute a player during a match. @param tm The team we work on. @param player_number The index of the player. */ gint game_substitute_player(Team *tm, gint player_number) { #ifdef DEBUG printf("game_substitute_player\n"); #endif gint i, substitute = -1; GPtrArray *substitutes = g_ptr_array_new(); gboolean adapt_structure; for(i=11;iplayers->len;i++) if(g_array_index(tm->players, Player, i).cskill > 0) g_ptr_array_add(substitutes, &g_array_index(tm->players, Player, i)); if(substitutes->len == 0) { g_ptr_array_free(substitutes, TRUE); debug_print_message("game_substitute_player: no suitable substitutes found (all injured/banned?)"); return -1; } g_ptr_array_sort_with_data(substitutes, (GCompareDataFunc)player_compare_substitute_func, GINT_TO_POINTER(player_of_idx_team(tm, player_number)->cpos)); adapt_structure = (math_get_place(team_find_appropriate_structure(tm), 1) + math_get_place(team_find_appropriate_structure(tm), 2) + math_get_place(team_find_appropriate_structure(tm), 3) != 10 || (player_of_idx_team(tm, player_number)->cpos != ((Player*)g_ptr_array_index(substitutes, 0))->pos && player_substitution_good_structure(tm->structure, player_of_idx_team(tm, player_number)->cpos, ((Player*)g_ptr_array_index(substitutes, 0))->pos))); substitute = ((Player*)g_ptr_array_index(substitutes, 0))->id; player_swap(tm, player_number, tm, player_id_index(tm, substitute,TRUE)); g_ptr_array_free(substitutes, TRUE); if(adapt_structure) { team_change_structure(tm, team_find_appropriate_structure(tm)); team_rearrange(tm); } if(team_is_user(tm) == cur_user) { game_gui_write_av_skills(NULL); selected_row = -1; treeview_show_user_player_list(); } return substitute; } /** Find out whether we substitute a player to balance a team after a red card. @param clid The cup/league id of the fixture. @param tm The team. @return A player index or -1 if we don't substitute. */ gint game_find_to_substitute(gint clid, const Team *tm) { #ifdef DEBUG printf("game_find_to_substitute\n"); #endif gint i; gint position_to_substitute = -1; GPtrArray *players = g_ptr_array_new(); gint return_value = -1; gint current_structure = team_find_appropriate_structure(tm); gint num_forw = current_structure % 10, num_mid = math_get_place(current_structure, 2), num_def = math_get_place(current_structure, 3); for(i=0;i<11;i++) if(player_is_banned(player_of_idx_team(tm, i)) <= 0) g_ptr_array_add(players, player_of_idx_team(tm, i)); g_ptr_array_sort_with_data(players, (GCompareDataFunc)player_compare_func, GINT_TO_POINTER(PLAYER_COMPARE_ATTRIBUTE_GAME_SKILL)); if(num_forw > 1 || MAX(num_mid, num_def) <= 2) position_to_substitute = PLAYER_POS_FORWARD; else if(ABS(num_def - num_mid) > 1 || (num_forw == 0 && MAX(num_mid, num_def) > 2)) position_to_substitute = (num_def > num_mid) ? PLAYER_POS_DEFENDER : PLAYER_POS_MIDFIELDER; else return -1; for(i=players->len - 1; i >= 0; i--) if(((Player*)g_ptr_array_index(players, i))->pos == position_to_substitute) { return_value = ((Player*)g_ptr_array_index(players, i))->id; g_ptr_array_free(players, TRUE); return return_value; } g_ptr_array_free(players, TRUE); return -1; } /** Find out how long a player is banned. */ gint game_player_get_ban_duration(void) { #ifdef DEBUG printf("game_player_get_ban_duration\n"); #endif gint i; gfloat rndom; gfloat duration_probs[6] = {0, const_float("float_live_game_ban_1"), const_float("float_live_game_ban_2"), const_float("float_live_game_ban_3"), const_float("float_live_game_ban_4"), const_float("float_live_game_ban_5")}; for(i=1;i<6;i++) duration_probs[i] += duration_probs[i - 1]; rndom = math_rnd(0, 1); for(i=1;i<6;i++) if(duration_probs[i - 1] < rndom && rndom < duration_probs[i]) return i + 1; return -1; } /** Find out whether we make a sub after a send-off. @param clid The id of the fixture. @param tm The team. @param player The player index. @param to_substitute The return location for the index of the player to substitute. @param substitute The return location for the player who comes into the game. */ void game_substitute_player_send_off(gint clid, Team *tm, gint player_number, gint *to_substitute, gint *substitute) { #ifdef DEBUG printf("game_substitute_player_send_off\n"); #endif gint i; gint position = -1; GPtrArray *substitutes = NULL; gint current_structure = team_find_appropriate_structure(tm); gint num_forw = current_structure % 10, num_mid = math_get_place(current_structure, 2), num_def = math_get_place(current_structure, 3); *to_substitute = game_find_to_substitute(clid, tm); if(*to_substitute == -1) return; substitutes = g_ptr_array_new(); for(i=11;iplayers->len;i++) if(g_array_index(tm->players, Player, i).cskill > 0) g_ptr_array_add(substitutes, player_of_idx_team(tm, i)); if(substitutes->len == 0) { g_ptr_array_free(substitutes, TRUE); debug_print_message("game_substitute_player_send_off: no suitable substitutes found (all injured/banned?)"); *to_substitute = -1; return; } if(num_forw == 0 && MAX(num_def, num_mid) > 2) position = PLAYER_POS_FORWARD; else position = (num_def > num_mid) ? PLAYER_POS_MIDFIELDER : PLAYER_POS_DEFENDER; g_ptr_array_sort_with_data(substitutes, (GCompareDataFunc)player_compare_substitute_func, GINT_TO_POINTER(position)); *substitute = ((Player*)g_ptr_array_index(substitutes, 0))->id; player_swap(tm, player_id_index(tm, *to_substitute, TRUE), tm, player_id_index(tm, *substitute, TRUE)); g_ptr_array_free(substitutes, TRUE); team_change_structure(tm, team_find_appropriate_structure(tm)); team_rearrange(tm); if(team_is_user(tm) == cur_user) { game_gui_write_av_skills(NULL); selected_row = -1; treeview_show_user_player_list(); } } /** Decrease the players' fitness during a live game. @param fix The match being played. */ void game_decrease_fitness(const Fixture *fix) { #ifdef DEBUG printf("game_decrease_fitness\n"); #endif gint i, j; for(i=0;i<2;i++) { if(debug < 50 || team_is_user(fix->teams[i]) == -1) for(j=0;j<11;j++) if(player_of_idx_team(fix->teams[i], j)->cskill > 0) player_decrease_fitness(player_of_idx_team(fix->teams[i], j)); } } /** Update the live game stats using the live game unit. @param live_game_stats Pointer to the live game. @param live_game_unit The live game unit. */ void game_update_stats(LiveGame *lg, const LiveGameUnit *unit) { #ifdef DEBUG printf("game_update_stats\n"); #endif gint i; LiveGameStats *stats = &lg->stats; if(unit->minute != -1) stats->values[unit->possession][LIVE_GAME_STAT_VALUE_POSSESSION]++; if(unit->event.type == LIVE_GAME_EVENT_SCORING_CHANCE || unit->event.type == LIVE_GAME_EVENT_FREE_KICK || unit->event.type == LIVE_GAME_EVENT_HEADER) stats->values[unit->possession][LIVE_GAME_STAT_VALUE_SHOTS]++; else if(unit->event.type == LIVE_GAME_EVENT_PENALTY) stats->values[unit->possession][LIVE_GAME_STAT_VALUE_PENALTIES]++; else if(unit->event.type == LIVE_GAME_EVENT_INJURY) { stats->values[unit->event.team][LIVE_GAME_STAT_VALUE_INJURIES]++; game_update_stats_player(lg, unit); } else if(unit->event.type == LIVE_GAME_EVENT_FOUL || unit->event.type == LIVE_GAME_EVENT_FOUL_YELLOW || unit->event.type == LIVE_GAME_EVENT_FOUL_RED || unit->event.type == LIVE_GAME_EVENT_FOUL_RED_INJURY) { stats->values[unit->event.team][LIVE_GAME_STAT_VALUE_FOULS]++; if(unit->event.type == LIVE_GAME_EVENT_FOUL_YELLOW) { stats->values[unit->event.team][LIVE_GAME_STAT_VALUE_CARDS]++; game_update_stats_player(lg, unit); } } else if(unit->event.type == LIVE_GAME_EVENT_SEND_OFF) { stats->values[unit->event.team][LIVE_GAME_STAT_VALUE_REDS]++; game_update_stats_player(lg, unit); } else if(unit->event.type == LIVE_GAME_EVENT_GOAL || unit->event.type == LIVE_GAME_EVENT_OWN_GOAL) { if(live_game_unit_before(unit, -1)->event.type != LIVE_GAME_EVENT_PENALTY && unit->event.type != LIVE_GAME_EVENT_OWN_GOAL) stats->values[unit->event.team][LIVE_GAME_STAT_VALUE_GOALS_REGULAR]++; game_update_stats_player(lg, unit); } for(i=0;i<2;i++) stats->values[i][LIVE_GAME_STAT_VALUE_SHOT_PERCENTAGE] = (stats->values[i][LIVE_GAME_STAT_VALUE_SHOTS] > 0) ? (gint)rint(((gfloat)stats->values[i][LIVE_GAME_STAT_VALUE_GOALS_REGULAR] / (gfloat)stats->values[i][LIVE_GAME_STAT_VALUE_SHOTS]) * 100) : 0; } /** Update the player array in the live game stats. @param live_game Pointer to the live game. @param team The team of the player. @param player The player id. @param type The type of the stat. */ void game_update_stats_player(LiveGame *lg, const LiveGameUnit *unit) { #ifdef DEBUG printf("game_update_stats_player\n"); #endif gint i; gchar buf[SMALL], buf2[SMALL]; LiveGameStats *stats = &lg->stats; gint minute = live_game_unit_get_minute(unit), array_index = -1; gboolean own_goal; gint team = unit->event.team, player = unit->event.player, player2 = unit->event.player2; const Team *tm[2] = {lg->fix->teams[0], lg->fix->teams[1]}; GPtrArray *players = NULL; const gchar *player_name; if(unit->event.type == LIVE_GAME_EVENT_GOAL || unit->event.type == LIVE_GAME_EVENT_OWN_GOAL) { own_goal = (unit->event.type == LIVE_GAME_EVENT_OWN_GOAL); array_index = (own_goal) ? !team : team; if(live_game_unit_before(unit, -1)->event.type == LIVE_GAME_EVENT_PENALTY) /* A goal scored with penalty. */ strcpy(buf2, _(" (P)")); else if(live_game_unit_before(unit, -1)->event.type == LIVE_GAME_EVENT_FREE_KICK) /* A goal scored with a free kick. */ strcpy(buf2, _(" (FK)")); else if(unit->event.type == LIVE_GAME_EVENT_OWN_GOAL) /* An own goal */ strcpy(buf2, _(" (OG)")); else strcpy(buf2, ""); player_name = player_of_id_team(tm[team], player)->name; if(!own_goal) g_ptr_array_add(stats->players[array_index][LIVE_GAME_STAT_ARRAY_SCORERS], g_strdup(player_name)); for(i=0;iplayers[array_index][LIVE_GAME_STAT_ARRAY_SCORERS_FOR_DISPLAY]->len;i++) { if(g_str_has_prefix((gchar*)g_ptr_array_index( stats->players[array_index][LIVE_GAME_STAT_ARRAY_SCORERS_FOR_DISPLAY], i), player_name)) { sprintf(buf, "%s %d%s", (gchar*)g_ptr_array_index( stats->players[array_index][LIVE_GAME_STAT_ARRAY_SCORERS_FOR_DISPLAY], i), minute, buf2); misc_string_assign((gchar**)&g_ptr_array_index( stats->players[array_index][LIVE_GAME_STAT_ARRAY_SCORERS_FOR_DISPLAY], i), buf); return; } } sprintf(buf, "%s %d%s", player_name, minute, buf2); g_ptr_array_add(stats->players[array_index][LIVE_GAME_STAT_ARRAY_SCORERS_FOR_DISPLAY], g_strdup(buf)); } else { strcpy(buf, ""); if(unit->event.type == LIVE_GAME_EVENT_INJURY) { sprintf(buf, "%s", player_of_id_team(tm[team], player)->name); players = stats->players[team][LIVE_GAME_STAT_ARRAY_INJURED]; } else if(unit->event.type == LIVE_GAME_EVENT_FOUL_YELLOW) { sprintf(buf, "%s", player_of_id_team(tm[team], player2)->name); players = stats->players[team][LIVE_GAME_STAT_ARRAY_YELLOWS]; } else if(unit->event.type == LIVE_GAME_EVENT_SEND_OFF) { sprintf(buf, "%s", player_of_id_team(tm[team], player)->name); players = stats->players[team][LIVE_GAME_STAT_ARRAY_REDS]; } if(strlen(buf) > 0) g_ptr_array_add(players, strdup(buf)); } } /** Make some team updates after a match. @param fix The match we examine. */ void game_post_match(Fixture *fix) { #ifdef DEBUG printf("game_post_match\n"); #endif gint i; GPtrArray *teams = NULL; Cup *cup = NULL; gchar buf[SMALL], buf2[SMALL]; gint usr_idx; usr_idx = fixture_user_team_involved(fix); if((debug > 100 && usr_idx != -1) || debug > 130) g_print("game_post_match: %s - %s\n", fix->teams[0]->name, fix->teams[1]->name); if(query_fixture_has_tables(fix)) table_update(fix); for(i=0;i<2;i++) team_update_post_match(fix->teams[i], fix); if(fix->clid < ID_CUP_START) return; cup = cup_from_clid(fix->clid); if(fix->round == cup->rounds->len - 1 && fix == &g_array_index(cup->fixtures, Fixture, cup->fixtures->len - 1)) { teams = cup_get_teams_sorted(cup); if(team_is_user((Team*)g_ptr_array_index(teams, 0)) != -1) { user_history_add(&usr(team_is_user((Team*)g_ptr_array_index(teams, 0))), USER_HISTORY_WIN_FINAL, ((Team*)g_ptr_array_index(teams, 0))->name, league_cup_get_name_string(fix->clid), ((Team*)g_ptr_array_index(teams, 1))->name, NULL); user_add_cup_success(&usr(team_is_user((Team*)g_ptr_array_index(teams, 0))), cup, fix->round, USER_HISTORY_WIN_FINAL); } else if(team_is_user((Team*)g_ptr_array_index(teams, 1)) != -1) { user_history_add(&usr(team_is_user((Team*)g_ptr_array_index(teams, 1))), USER_HISTORY_LOSE_FINAL, ((Team*)g_ptr_array_index(teams, 1))->name, league_cup_get_name_string(fix->clid), ((Team*)g_ptr_array_index(teams, 0))->name, NULL); user_add_cup_success(&usr(team_is_user((Team*)g_ptr_array_index(teams, 1))), cup, fix->round, USER_HISTORY_LOSE_FINAL); } g_ptr_array_free(teams, TRUE); } else if(usr_idx != -1) { cup_get_round_name(cup_from_clid(fix->clid), fix->round, buf); sprintf(buf2, "%d", fix->round + 1); user_history_add(&usr(usr_idx), USER_HISTORY_REACH_CUP_ROUND, usr(usr_idx).tm->name, league_cup_get_name_string(fix->clid), buf, buf2); user_add_cup_success(&usr(usr_idx), cup, fix->round, USER_HISTORY_REACH_CUP_ROUND); } } /** Reduce stadium capacity and safety after a stadium event. @param user The user whose stadium we demolish. @param type The event type. */ void game_stadium_event(Stadium *stadium, gint type) { #ifdef DEBUG printf("game_stadium_event\n"); #endif gfloat reduce; gfloat reduce_factor[3][2] = {{const_float("float_game_stadium_safety_reduce_breakdown_lower"), const_float("float_game_stadium_safety_reduce_breakdown_upper")}, {const_float("float_game_stadium_safety_reduce_riots_lower"), const_float("float_game_stadium_safety_reduce_riots_upper")}, {const_float("float_game_stadium_safety_reduce_fire_lower"), const_float("float_game_stadium_safety_reduce_fire_upper")}}; reduce = math_rnd(reduce_factor[type - LIVE_GAME_EVENT_STADIUM_BREAKDOWN][0], reduce_factor[type - LIVE_GAME_EVENT_STADIUM_BREAKDOWN][1]); stadium->safety *= (1 - reduce); stadium->capacity = (gint)rint((gfloat)stadium->capacity * (1 - reduce * const_float("float_game_stadium_capacity_reduce_factor"))); } /** Return the maximum possible values for defence, midfield and attack. Used in the opponent preview. */ void game_get_max_values(gfloat max_values[4]) { #ifdef DEBUG printf("game_get_max_values\n"); #endif gint i, j; Player pl; pl.skill = pl.cskill = const_float_fast(float_player_max_skill); pl.fitness = 1; pl.streak = 0; max_values[0] = const_float_fast(float_player_max_skill); for(i=1;i<4;i++) { max_values[i] = 0; for(j=1;j<11;j++) { pl.cpos = player_get_position_from_structure(442, j); max_values[i] += game_get_player_contribution(&pl, i, FALSE); } } } /** Find out whether there's a default penalty shooter in the team. @return A player id or -1. */ gint game_get_default_penalty_shooter(const Team *tm) { #ifdef DEBUG printf("game_get_default_penalty_shooter\n"); #endif gint return_value = -1; if(team_is_user(tm) != -1 && option_int("int_opt_user_penalty_shooter", &usr(team_is_user(tm)).options) != -1 && player_of_id_team(tm, option_int("int_opt_user_penalty_shooter", &usr(team_is_user(tm)).options)) != NULL && player_id_index(tm, option_int("int_opt_user_penalty_shooter", &usr(team_is_user(tm)).options), TRUE) < 11 && player_of_id_team(tm, option_int("int_opt_user_penalty_shooter", &usr(team_is_user(tm)).options))->cskill != 0) return_value = option_int("int_opt_user_penalty_shooter", &usr(team_is_user(tm)).options); return return_value; } /** Deduce some money for boost during a match. */ void game_boost_cost(void) { #ifdef DEBUG printf("game_boost_cost\n"); #endif gfloat wage_unit = finance_wage_unit(usr(stat2).tm); gint deduce = (gint)rint(wage_unit * const_float("float_boost_cost_factor")); usr(stat2).money -= deduce; usr(stat2).money_out[1][MON_OUT_BOOST] -= deduce; }