bygfoot/src/game.c

1263 lines
40 KiB
C

/*
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;j<GAME_TEAM_VALUE_END;j++)
team_values[i][j] = 0;
style_factor = (gfloat)tm[i]->style * 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;j<GAME_TEAM_VALUE_DEFEND + 3;j++)
team_values[i][j] *=
((1 + home_advantage * (i == 0)));
team_values[i][GAME_TEAM_VALUE_DEFEND] *= (1 - style_factor);
team_values[i][GAME_TEAM_VALUE_ATTACK] *= (1 + style_factor);
}
}
/** Return the contribution of a player to the attack, midfield or defend.
@param pl The player.
@param type Whether we have defend, midfield or attack value.
@param special Whether to count special influence like boost and
streak.
@return The player's contribution depending on position and
fitness. */
gfloat
game_get_player_contribution(const Player *pl, gint type, gboolean special)
{
/** How the cskill of field players get weighted for the team values in
a match. Rows are player position, columns value type.
@see game_get_player_contribution() */
gfloat player_weights[3][3] =
{{const_float("float_player_team_weight_defender_defense"),
const_float("float_player_team_weight_defender_midfield"),
const_float("float_player_team_weight_defender_attack")},
{const_float("float_player_team_weight_midfielder_defense"),
const_float("float_player_team_weight_midfielder_midfield"),
const_float("float_player_team_weight_midfielder_attack")},
{const_float("float_player_team_weight_forward_defense"),
const_float("float_player_team_weight_forward_midfield"),
const_float("float_player_team_weight_forward_attack")}};
return player_get_game_skill(pl, FALSE, special) *
player_weights[pl->cpos - 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;i<tm->players->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("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("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;i<players->len - 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;j<fix->teams[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;i<tm->players->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;i<tm->players->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;i<tm->players->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;i<stats->players[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("float_player_max_skill");
pl.fitness = 1;
pl.streak = 0;
max_values[0] = const_float("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;
}