bygfoot/src/player.c

1707 lines
49 KiB
C

/*
player.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 "fixture.h"
#include "free.h"
#include "game_gui.h"
#include "league.h"
#include "main.h"
#include "maths.h"
#include "misc.h"
#include "name.h"
#include "option.h"
#include "player.h"
#include "team.h"
#include "transfer.h"
#include "user.h"
/** Create and return a new player.
@param tm The team the player will belong to.
@param average_talent The average talent of the team.
@return A newly created player. */
Player
player_new(Team *tm, gfloat average_talent, gboolean new_id)
{
#ifdef DEBUG
printf("player_new\n");
#endif
gint i;
gfloat skill_factor =
math_rnd(1 - const_float("float_player_average_talent_variance"),
1 + const_float("float_player_average_talent_variance"));
Player new;
new.name = (new_id) ?
name_get(tm->names_file) : NULL;
new.id = (new_id) ? player_id_new : -1;
new.pos = player_get_position_from_structure(tm->structure, tm->players->len);
new.cpos = new.pos;
new.age = math_gauss_dist(const_float("float_player_age_lower"),
const_float("float_player_age_upper"));//30;
new.peak_age =
math_rnd(const_float("float_player_peak_age_lower") +
(new.pos == PLAYER_POS_GOALIE) *
const_float("float_player_peak_age_goalie_addition"),
const_float("float_player_peak_age_upper") +
(new.pos == PLAYER_POS_GOALIE) *
const_float("float_player_peak_age_goalie_addition"));//30;
new.peak_region =
math_gauss_dist(const_float("float_player_peak_region_lower"),
const_float("float_player_peak_region_upper"));//2;
new.talent =
CLAMP(average_talent * skill_factor, 0,
const_float_fast(float_player_max_skill));;
new.skill = player_skill_from_talent(&new);
new.cskill = new.skill;
player_estimate_talent(&new);
new.fitness = math_rnd(const_float("float_player_fitness_lower"),
const_float("float_player_fitness_upper"));
new.health = new.recovery = 0;
new.value = player_assign_value(&new);
new.wage = player_assign_wage(&new);
new.contract = math_rnd(const_float("float_player_contract_lower"),
const_float("float_player_contract_upper"));
new.lsu = math_rnd(const_float("float_player_lsu_lower"),
const_float("float_player_lsu_upper"));
new.cards = g_array_new(FALSE, FALSE, sizeof(PlayerCard));
new.games_goals = g_array_new(FALSE, FALSE, sizeof(PlayerGamesGoals));
new.card_status = PLAYER_CARD_STATUS_NONE;
for(i=0;i<PLAYER_VALUE_END;i++)
new.career[i] = 0;
new.team = tm;
new.participation = FALSE;
new.offers = 0;
new.streak = PLAYER_STREAK_NONE;
new.streak_count = new.streak_prob = 0;
return new;
}
/** Complete the player structure (some values have been read
from a team def file). */
void
player_complete_def(Player *pl, gfloat average_talent)
{
#ifdef DEBUG
printf("player_complete_def\n");
#endif
gint i;
gfloat skill_factor =
math_rnd(1 - const_float("float_player_average_talent_variance"),
1 + const_float("float_player_average_talent_variance"));
pl->peak_age = math_rnd(const_float("float_player_peak_age_lower") +
(pl->pos == PLAYER_POS_GOALIE) *
const_float("float_player_peak_age_goalie_addition"),
const_float("float_player_peak_age_upper") +
(pl->pos == PLAYER_POS_GOALIE) *
const_float("float_player_peak_age_goalie_addition"));
if(opt_int("int_opt_load_defs") == 2)
{
pl->age = math_gauss_dist(const_float("float_player_age_lower"),
const_float("float_player_age_upper"));
pl->peak_age =
math_rnd(const_float("float_player_peak_age_lower") +
(pl->pos == PLAYER_POS_GOALIE) *
const_float("float_player_peak_age_goalie_addition"),
const_float("float_player_peak_age_upper") +
(pl->pos == PLAYER_POS_GOALIE) *
const_float("float_player_peak_age_goalie_addition"));
pl->peak_region =
math_gauss_dist(const_float("float_player_peak_region_lower"),
const_float("float_player_peak_region_upper"));
pl->talent = CLAMP(average_talent * skill_factor, 0,
const_float_fast(float_player_max_skill));
pl->skill = player_skill_from_talent(pl);
}
pl->cpos = pl->pos;
player_estimate_talent(pl);
pl->cskill = pl->skill;
pl->fitness = math_rnd(const_float("float_player_fitness_lower"),
const_float("float_player_fitness_upper"));
pl->health = pl->recovery = 0;
pl->value = player_assign_value(pl);
pl->wage = player_assign_wage(pl);
pl->contract = math_rnd(const_float("float_player_contract_lower"),
const_float("float_player_contract_upper"));
pl->lsu = math_rnd(const_float("float_player_lsu_lower"),
const_float("float_player_lsu_upper"));
pl->cards = g_array_new(FALSE, FALSE, sizeof(PlayerCard));
pl->games_goals = g_array_new(FALSE, FALSE, sizeof(PlayerGamesGoals));
for(i=0;i<PLAYER_VALUE_END;i++)
pl->career[i] = 0;
pl->participation = FALSE;
pl->offers = 0;
}
/** Return the appropriate position for the player with the given number.
The position depends on the team structure if the player number is < 11
and on some constants otherwise.
@param structure The team structure, something like 442 or 352.
@param player_number The number of the player within the team.
@return A new position for the player. */
gint
player_get_position_from_structure(gint structure, gint player_number)
{
#ifdef DEBUG
printf("player_get_position_from_structure\n");
#endif
gint position = -1;
gint bound[2] =
{math_get_place(structure, 3) + 1,
math_get_place(structure, 3) +
math_get_place(structure, 2) + 1};
if(player_number == 0 ||
player_number == 11)
position = PLAYER_POS_GOALIE;
else if(player_number < bound[0] ||
(player_number > 10 &&
player_number < (11 + (const_int("int_team_cpu_players") - 11) *
const_float("float_player_pos_bound1"))))
position = PLAYER_POS_DEFENDER;
else if(player_number < bound[1] ||
(player_number > 10 &&
player_number < (11 + (const_int("int_team_cpu_players") - 11) *
const_float("float_player_pos_bound2"))))
position = PLAYER_POS_MIDFIELDER;
else
position = PLAYER_POS_FORWARD;
return position;
}
/** Calculate a player's skill depending on his age
and talent. */
gfloat
player_skill_from_talent(const Player *pl)
{
#ifdef DEBUG
printf("player_skill_from_talent\n");
#endif
gfloat skill = pl->talent, cur_age = pl->peak_age;
if(pl->age < pl->peak_age)
{
while(cur_age > pl->age)
{
cur_age -= ((const_float_fast(float_player_lsu_update_limit) + 2) * 0.0192);
if(pl->peak_age - cur_age > pl->peak_region)
skill *= (1 - ((pl->peak_age - cur_age) *
const_float_fast(float_player_skill_update_younger_factor) +
const_float_fast(float_player_skill_update_younger_add)));
}
}
else
{
while(cur_age < pl->age)
{
cur_age += ((const_float_fast(float_player_lsu_update_limit) + 2) * 0.0192);
if(cur_age - pl->peak_age > pl->peak_region)
skill *= (1 - ((cur_age - pl->peak_age) *
const_float("float_player_skill_update_older_factor") +
const_float("float_player_skill_update_older_add")));
}
}
if(skill > pl->talent)
skill = pl->talent;
return skill;
}
/** Estimate a player's talent.
@param pl The player of which we'd like to estimate the talent. */
void
player_estimate_talent(Player *pl)
{
#ifdef DEBUG
printf("player_estimate_talent\n");
#endif
gint i, j;
gfloat scout_deviance[QUALITY_END];
/* the maximal deviance in both directions */
gfloat deviance_bound[2] =
{pl->talent - pl->skill, const_float_fast(float_player_max_skill) - pl->talent};
for(i=0;i<QUALITY_END;i++)
{
scout_deviance[i] = (i + 1) * const_float_fast(float_player_max_skill) *
(const_float_fast(float_player_etal_scout_factor) / 100);
/* adjust deviance_bounds with regard to the scout's
deviance */
for(j=0;j<2;j++)
deviance_bound[j] = MIN(deviance_bound[j], scout_deviance[i]);
pl->etal[i] = math_rnd(pl->talent - deviance_bound[0],
pl->talent + deviance_bound[1]);
}
}
/** Assign a (transfer) value to a player. The value depends on skill,
talent and age.
@param pl The player we examine.
@return The value of the player. */
gint
player_assign_value(const Player *pl)
{
#ifdef DEBUG
printf("player_assign_value\n");
#endif
gfloat value;
value = powf((const_float("float_player_value_skill_weight") * pl->skill +
(1 - const_float("float_player_value_skill_weight")) *
pl->talent * 0.7), const_float("float_player_value_power"));
if(pl->peak_age > pl->age)
value *= (1 + const_float("float_player_value_younger_factor") *
(pl->peak_age - pl->age));
else
value *= (1 - const_float("float_player_value_older_factor") *
(pl->age - pl->peak_age));
return math_round_integer((gint)rint(value), 2);
}
/** Assign a wage to a player. The wage depends mainly on
the value.
@param pl The player we examine.
@return The wage of the player. */
gint
player_assign_wage(const Player *pl)
{
#ifdef DEBUG
printf("player_assign_wage\n");
#endif
gfloat wage;
wage = rint(((gfloat)pl->value * const_float("float_player_wage_value_factor")) *
math_rnd(1 - const_float("float_player_wage_random_dev"),
1 + const_float("float_player_wage_random_dev") ));
return math_round_integer((gint)wage, 1);
}
/** Get a player's index in the players array from
his id.
@param tm The team.
@param player_id The player's id.
@param stopWhenPlayerNotFound Should we stop when we don't find the player
@return The array index or -1. */
gint
player_id_index(const Team *tm, gint player_id, gboolean stopWhenPlayerNotFound)
{
gint i;
for(i=0;i<tm->players->len;i++)
if(g_array_index(tm->players, Player, i).id == player_id)
return i;
if (stopWhenPlayerNotFound) {
main_exit_program(EXIT_INT_NOT_FOUND,
"player_id_index: didn't find player with id %d of team %s\n",
player_id, tm->name);
}
return -1;
}
/** Return a pointer to the number'th player of the team.
@param tm The team.
@param number The player number.
@return A pointer to the player or NULL. */
Player*
player_of_idx_team(const Team *tm, gint number)
{
if(tm->players->len <= number)
main_exit_program(EXIT_POINTER_NOT_FOUND,
"player_of_idx_team: Player list of team %s too short for number %d.\n",
tm->name, number);
return &g_array_index(tm->players, Player, number);
}
/** Return a pointer to the player with specified id of the team.
@param tm The team.
@param id The player's id.
@return A pointer to the player or NULL. */
Player*
player_of_id_team(const Team *tm, gint id)
{
#ifdef DEBUG
printf("player_of_id_team\n");
#endif
gint i;
for(i=0;i<tm->players->len;i++)
if(g_array_index(tm->players, Player, i).id == id)
return &g_array_index(tm->players, Player, i);
main_exit_program(EXIT_POINTER_NOT_FOUND,
"player_of_id_team: didn't find player with id %d of team %s\n",
id, tm->name);
return NULL;
}
/** Find out whether the given player id is in the team. */
gboolean
query_player_id_in_team(gint player_id, const Team *tm)
{
#ifdef DEBUG
printf("query_player_id_in_team\n");
#endif
gint i;
for(i=0;i<tm->players->len;i++)
if(g_array_index(tm->players, Player, i).id == player_id)
return TRUE;
return FALSE;
}
/** Return the number of all games or goals the player's
participated in / scored in all cups and leagues.
@param pl The player we examine.
@param goals Whether we sum up the goals.
@return The number of goals. */
gint
player_all_games_goals(const Player *pl, gint type)
{
#ifdef DEBUG
printf("player_all_games_goals\n");
#endif
gint i, sum = 0;
for(i=0;i<pl->games_goals->len;i++)
if(type == PLAYER_VALUE_GOALS)
sum += g_array_index(pl->games_goals, PlayerGamesGoals, i).goals;
else if(type == PLAYER_VALUE_GAMES)
sum += g_array_index(pl->games_goals, PlayerGamesGoals, i).games;
else if(type == PLAYER_VALUE_SHOTS)
sum += g_array_index(pl->games_goals, PlayerGamesGoals, i).shots;
return sum;
}
/** Return the sum of all the yellow cards in all
leagues and cups for the player.
@param pl The player we examine.
@return The number of all cards.*/
gint
player_all_cards(const Player *pl)
{
#ifdef DEBUG
printf("player_all_cards\n");
#endif
gint i, sum = 0;
for(i=0;i<pl->cards->len;i++)
sum += g_array_index(pl->cards, PlayerCard, i).yellow;
return sum;
}
/** Compare two players in a (pointer) array.
@param pl1 Pointer to the pointer to the first player.
@param pl2 Pointer to the pointer to the second player.
@param data Coded integer that tells us which attribute to compare.
@return 1, 0 or -1 (see the C qsort() function). */
gint
player_compare_func(gconstpointer a, gconstpointer b, gpointer data)
{
#ifdef DEBUG
printf("player_compare_func\n");
#endif
gint data_int = ABS(GPOINTER_TO_INT(data));
gint data_int2 = GPOINTER_TO_INT(data);
gint type = data_int % 100;
const Player *pl1 = (data_int < 100) ?
*(const Player**)a : (const Player*)a;
const Player *pl2 = (data_int < 100) ?
*(const Player**)b : (const Player*)b;
gint return_value = 0;
if(type == PLAYER_COMPARE_ATTRIBUTE_GAME_SKILL)
return_value =
misc_float_compare(player_get_game_skill(pl1, FALSE, TRUE),
player_get_game_skill(pl2, FALSE, TRUE));
else if(type == PLAYER_COMPARE_ATTRIBUTE_POS)
{
if(MIN(player_id_index(pl1->team, pl1->id, TRUE),
player_id_index(pl2->team, pl2->id, TRUE)) < 11 &&
MAX(player_id_index(pl1->team, pl1->id, TRUE),
player_id_index(pl2->team, pl2->id, TRUE)) >= 11)
return_value =
(player_id_index(pl1->team, pl1->id, TRUE) >
player_id_index(pl2->team, pl2->id, TRUE)) ?
1 : -1;
else if(pl1->cskill == 0)
return_value = (pl2->cskill == 0) ? 0 : 1;
else if(pl2->cskill == 0)
return_value = (pl1->cskill == 0) ? 0 : -1;
else if(pl2->pos != pl1->pos)
return_value = misc_int_compare(pl2->pos, pl1->pos);
else
return_value = 0;
}
else if(type == PLAYER_COMPARE_ATTRIBUTE_LEAGUE_GOALS)
{
gint goals1 = player_games_goals_get(pl1, pl1->team->clid, PLAYER_VALUE_GOALS),
games1 = player_games_goals_get(pl1, pl1->team->clid, PLAYER_VALUE_GAMES),
shots1 = player_games_goals_get(pl1, pl1->team->clid, PLAYER_VALUE_SHOTS),
goals2 = player_games_goals_get(pl2, pl2->team->clid, PLAYER_VALUE_GOALS),
games2 = player_games_goals_get(pl2, pl2->team->clid, PLAYER_VALUE_GAMES),
shots2 = player_games_goals_get(pl2, pl2->team->clid, PLAYER_VALUE_SHOTS);
if(goals1 != goals2)
return_value = misc_int_compare(goals1, goals2);
else if(games1 != games2)
return_value = misc_int_compare(games2, games1);
else
return_value = misc_int_compare(shots2, shots1);
}
if(data_int2 != 0)
return_value *= (data_int / data_int2);
return return_value;
}
/** Compare two players in a pointer array.
@param pl1 Pointer to the pointer to the first player.
@param pl2 Pointer to the pointer to the second player.
@param data Coded integer that tells us which position we
would like to substitute.
@return 1, 0 or -1 (see the C qsort() function). */
gint
player_compare_substitute_func(gconstpointer a, gconstpointer b, gpointer data)
{
#ifdef DEBUG
printf("player_compare_substitute_func\n");
#endif
const Player *pl1 = *(const Player**)a;
const Player *pl2 = *(const Player**)b;
gint position = GPOINTER_TO_INT(data);
gfloat skill_for_pos1 = player_get_cskill(pl1, position, FALSE) *
powf(pl1->fitness, const_float_fast(float_player_fitness_exponent)),
skill_for_pos2 = player_get_cskill(pl2, position, FALSE) *
powf(pl2->fitness, const_float_fast(float_player_fitness_exponent));
gfloat game_skill1 = player_get_game_skill(pl1, FALSE, TRUE),
game_skill2 = player_get_game_skill(pl2, FALSE, TRUE);
gboolean good_structure1 =
player_substitution_good_structure(pl1->team->structure, position, pl1->pos),
good_structure2 =
player_substitution_good_structure(pl2->team->structure, position, pl2->pos);
gint return_value = 0;
if(pl1->pos == position && pl2->pos == position)
return_value =
misc_float_compare(game_skill1, game_skill2);
else if(pl1->pos == position)
return_value = -1;
else if(pl2->pos == position)
return_value = 1;
else if(position != PLAYER_POS_GOALIE)
{
if(good_structure1 && good_structure2)
return_value =
misc_float_compare(game_skill1, game_skill2);
else if(good_structure1)
return_value =
misc_float_compare(game_skill1, skill_for_pos2);
else if(good_structure2)
return_value =
misc_float_compare(skill_for_pos1, game_skill2);
else
return_value =
misc_float_compare(skill_for_pos1, skill_for_pos2);
}
else
return_value =
misc_float_compare(skill_for_pos1, skill_for_pos2);
return return_value;
}
/** Find out whether substituting a player into a team
and adapting the team structure results in a normal
structure.
@param old_structure The team's structure before substituting.
@param old_pos The position of the player that gets replaced.
@param player_pos The position of the player that comes into the game.
@return TRUE or FALSE. */
gboolean
player_substitution_good_structure(gint old_structure, gint old_pos, gint player_pos)
{
#ifdef DEBUG
printf("player_substitution_good_structure\n");
#endif
gint accepted_structures[5] = {532, 442, 352, 433, 343};
gint new_structure =
old_structure - (gint)rint(powf(10, PLAYER_POS_FORWARD - old_pos)) +
(gint)rint(powf(10, PLAYER_POS_FORWARD - player_pos));
return query_integer_is_in_array(new_structure, accepted_structures, 5);
}
/** Copy a player into a team.
@param pl The player.
@param tm The dest team.
@param insert_at The new index of the player in the players array. */
void
player_copy(Player *pl, Team *tm, gint insert_at)
{
#ifdef DEBUG
printf("player_copy\n");
#endif
Player new = *pl;
new.team = tm;
g_array_insert_val(tm->players, insert_at, new);
if(insert_at < 11)
player_of_idx_team(tm, insert_at)->cpos =
player_get_position_from_structure(tm->structure, insert_at);
else
player_of_idx_team(tm, insert_at)->cpos = player_of_idx_team(tm, insert_at)->pos;
player_of_idx_team(tm, insert_at)->cskill =
player_get_cskill(player_of_idx_team(tm, insert_at),
player_of_idx_team(tm, insert_at)->cpos, TRUE);
}
/** Move a player from one player array to another one.
@param tm1 The source team.
@param player_number The index of the player in the players array.
@param tm2 The dest team.
@param insert_at The new index of the player in the players array. */
void
player_move(Team *tm1, gint player_number, Team *tm2, gint insert_at)
{
#ifdef DEBUG
printf("player_move\n");
#endif
Player pl = *player_of_idx_team(tm1, player_number);
pl.team = tm2;
g_array_remove_index(tm1->players, player_number);
g_array_insert_val(tm2->players, insert_at, pl);
}
/** Swap two players. */
void
player_swap(Team *tm1, gint player_number1, Team *tm2, gint player_number2)
{
#ifdef DEBUG
printf("player_swap\n");
#endif
gint move = (tm1 == tm2 && player_number1 < player_number2) ?
-1 : 1;
if(stat0 == STATUS_LIVE_GAME_PAUSE)
{
if((player_number1 < 11 && player_is_banned(player_of_idx_team(tm1, player_number1)) > 0 &&
player_of_idx_team(tm1, player_number1)->participation) ||
(player_number2 < 11 && player_is_banned(player_of_idx_team(tm2, player_number2)) > 0 &&
player_of_idx_team(tm2, player_number2)->participation))
{
game_gui_show_warning(_("You can't replace a banned player."));
return;
}
}
player_move(tm1, player_number1, tm2, player_number2);
if(player_number2 < 11)
player_of_idx_team(tm2, player_number2)->cpos =
player_get_position_from_structure(tm2->structure, player_number2);
else
player_of_idx_team(tm2, player_number2)->cpos = player_of_idx_team(tm2, player_number2)->pos;
player_of_idx_team(tm2, player_number2)->cskill =
player_get_cskill(player_of_idx_team(tm2, player_number2),
player_of_idx_team(tm2, player_number2)->cpos, TRUE);
player_move(tm2, player_number2 + move,
tm1, player_number1);
if(player_number1 < 11)
player_of_idx_team(tm1, player_number1)->cpos =
player_get_position_from_structure(tm1->structure, player_number1);
else
player_of_idx_team(tm1, player_number1)->cpos = player_of_idx_team(tm1, player_number1)->pos;
player_of_idx_team(tm1, player_number1)->cskill =
player_get_cskill(player_of_idx_team(tm1, player_number1),
player_of_idx_team(tm1, player_number1)->cpos, TRUE);
}
/** Return the player's cskill depending on
whether he's on his normal position or not.
@param pl The player we examine.
@param position The position we's like to put the player.
@param check_health Whether to check for injury and ban (profiling reasons).
@return A new cskill. */
gfloat
player_get_cskill(const Player *pl, gint position, gboolean check_health)
{
#ifdef DEBUG
printf("player_get_cskill\n");
#endif
gfloat cskill_factor;
if(check_health &&
(pl->health != PLAYER_INJURY_NONE ||
player_is_banned(pl) > 0))
return 0;
if(pl->pos != position)
{
if(position == PLAYER_POS_GOALIE ||
pl->pos == PLAYER_POS_GOALIE)
cskill_factor = 0.5;
else if(abs(position - pl->pos) == 2)
cskill_factor = 0.65;
else
cskill_factor = 0.75;
return MIN(pl->talent * cskill_factor, pl->skill);
}
else
return pl->skill;
}
/** Find out whether the player is banned in
the next match or whether he will be if he
gets a yellow card.
@param pl The player we examine.
@return Number of games banned, or 0 if not banned, or -1
if yellow. */
gint
player_is_banned(const Player *pl)
{
#ifdef DEBUG
printf("player_is_banned\n");
#endif
gint i, result = 0;
GPtrArray *cards = g_ptr_array_new();
/* Calling team_get_fixture() is expensive, so first check if a player is
* banned from any leagues. If not, then we don't need to call
* team_get_fixture() */
for (i = 0; i < pl->cards->len; i++) {
PlayerCard *card = &g_array_index(pl->cards, PlayerCard, i);
gint yellow_red;
if (card->red) {
g_ptr_array_add(cards, card);
continue;
}
if(card->clid < ID_CUP_START)
yellow_red = league_from_clid(card->clid)->yellow_red;
else
yellow_red = cup_from_clid(card->clid)->yellow_red;
if(card->yellow == yellow_red - 1)
g_ptr_array_add(cards, card);
}
/* Player is not banned from any leagues. */
if (cards->len == 0)
goto done;
const Fixture *fix = team_get_fixture(pl->team, FALSE);
if(fix == NULL)
goto done;
/* Check if one of the leagues we are banned from is the next fixture
* for this player. */
for (i = 0; i < cards->len; i++) {
PlayerCard *card = g_ptr_array_index(cards, i);
if (card->clid != fix->clid)
continue;
if (card->red > 0) {
result = card->red;
goto done;
}
/* If it's not a red card then it must be one away from a red. */
result = -1;
goto done;
}
done:
g_ptr_array_unref(cards);
return result;
}
/** Return the player's skill contribution to his team.
@param pl The player.
@param skill Whether to take his skill or current skill into account.
@param special Whether to count special influence like boost and
streak.
@return A float value representing the player's contribution. */
gfloat
player_get_game_skill(const Player *pl, gboolean skill, gboolean count_special)
{
gfloat boost = (count_special) ?
1 + const_float_fast(float_player_boost_skill_effect) * pl->team->boost : 1;
gfloat streak = (count_special) ?
1 + (gfloat)pl->streak * const_float_fast(float_player_streak_influence_skill) : 1;
return (skill) ? pl->skill * boost * streak *
powf(pl->fitness, const_float_fast(float_player_fitness_exponent)) :
pl->cskill * boost * streak *
powf(pl->fitness, const_float_fast(float_player_fitness_exponent));
}
/** Decrease a player's fitness during a match.
@param pl The player. */
void
player_decrease_fitness(Player *pl)
{
#ifdef DEBUG
printf("player_decrease_fitness\n");
#endif
gfloat goalie_factor =
1 - const_float_fast(float_player_fitness_decrease_factor_goalie) *
(pl->cpos == 0);
gfloat boost_factor =
1 + (gfloat)pl->team->boost *
const_float_fast(float_player_boost_fitness_effect);
gfloat streak_factor = 1 + (gfloat)pl->streak *
const_float_fast(float_player_streak_influence_fitness_decrease);
if(pl->age < pl->peak_age - pl->peak_region)
{
pl->fitness -= (((pl->peak_age - pl->peak_region - pl->age) *
const_float_fast(float_player_fitness_decrease_younger_factor) +
const_float_fast(float_player_fitness_decrease_add)) *
goalie_factor * boost_factor * streak_factor);
}
else if(pl->age > pl->peak_age + pl->peak_region)
{
pl->fitness -= (((pl->age - pl->peak_age - pl->peak_region) *
const_float("float_player_fitness_decrease_older_factor") +
const_float_fast(float_player_fitness_decrease_add)) *
goalie_factor * boost_factor * streak_factor);
}
else
{
pl->fitness -= (const_float_fast(float_player_fitness_decrease_add) *
goalie_factor * boost_factor * streak_factor);
}
if(pl->fitness < 0)
pl->fitness = 0;
}
/** Increase of player fitness after a match.
Depends on the decrease values and the
percentage values that determine how much of the
decrease a player gains.
@param pl The player we edit. */
void
player_update_fitness(Player *pl)
{
#ifdef DEBUG
printf("player_update_fitness\n");
#endif
gfloat variance =
math_rnd(1 - const_float_fast(float_player_fitness_increase_variance),
1 + const_float_fast(float_player_fitness_increase_variance));
gfloat streak_factor =
1 + (pl->streak * const_float_fast(float_player_streak_influence_fitness_increase));
if(pl->participation)
{
pl->participation = FALSE;
return;
}
if(pl->age < pl->peak_age - pl->peak_region)
pl->fitness += (((pl->peak_age - pl->peak_region - pl->age) *
const_float_fast(float_player_fitness_increase_younger_factor) +
const_float_fast(float_player_fitness_increase_add)) *
variance * streak_factor);
else if(pl->age > pl->peak_age + pl->peak_region)
pl->fitness += (((pl->age - pl->peak_age - pl->peak_region) *
const_float("float_player_fitness_increase_older_factor") +
const_float_fast(float_player_fitness_increase_add)) *
variance * streak_factor);
else
pl->fitness += (const_float_fast(float_player_fitness_increase_add) *
variance * streak_factor);
pl->fitness = MIN(pl->fitness, 1);
}
/** Return the number of yellow cards of a player
or the number of weeks banned.
@param pl The player.
@param clid The cup or league id.
@param card_type Whether red or yellow cards. */
gint
player_card_get(const Player *pl, gint clid, gint card_type)
{
#ifdef DEBUG
printf("player_card_get\n");
#endif
gint i;
gint return_value = 0;
for(i=0;i<pl->cards->len;i++)
if(g_array_index(pl->cards, PlayerCard, i).clid == clid)
{
if(card_type == PLAYER_VALUE_CARD_YELLOW)
return_value = g_array_index(pl->cards, PlayerCard, i).yellow;
else
return_value = g_array_index(pl->cards, PlayerCard, i).red;
break;
}
return return_value;
}
/** Change a card value for the player.
@param pl The player.
@param clid The cup or league id.
@param card_type Whether red or yellow card.
@param value The new value.
@param diff Whether we add the value to the old one or
replace the old value by the new one. */
void
player_card_set(Player *pl, gint clid, gint card_type, gint value, gboolean diff)
{
#ifdef DEBUG
printf("player_card_set\n");
#endif
gint i, *card_value = NULL;
PlayerCard new;
for(i=0;i<pl->cards->len;i++)
if(g_array_index(pl->cards, PlayerCard, i).clid == clid)
{
if(card_type == PLAYER_VALUE_CARD_YELLOW)
card_value = &g_array_index(pl->cards, PlayerCard, i).yellow;
else if(card_type == PLAYER_VALUE_CARD_RED)
card_value = &g_array_index(pl->cards, PlayerCard, i).red;
if(diff)
*card_value += value;
else
*card_value = value;
if(*card_value < 0)
{
debug_print_message("player_card_set: negative card value; setting to 0\n");
*card_value = 0;
}
return;
}
new.clid = clid;
new.yellow = new.red = 0;
g_array_append_val(pl->cards, new);
player_card_set(pl, clid, card_type, value, diff);
}
/** Return the number of games or goals.
@param pl The player.
@param clid The cup or league id.
@param card_type Whether games or goals cards. */
gint
player_games_goals_get(const Player *pl, gint clid, gint type)
{
#ifdef DEBUG
printf("player_games_goals_get\n");
#endif
gint i, return_value = 0;
for(i=0;i<pl->games_goals->len;i++)
if(g_array_index(pl->games_goals, PlayerGamesGoals, i).clid == clid)
{
if(type == PLAYER_VALUE_GAMES)
return_value = g_array_index(pl->games_goals, PlayerGamesGoals, i).games;
else if(type == PLAYER_VALUE_GOALS)
return_value = g_array_index(pl->games_goals, PlayerGamesGoals, i).goals;
else if(type == PLAYER_VALUE_SHOTS)
return_value = g_array_index(pl->games_goals, PlayerGamesGoals, i).shots;
else
main_exit_program(EXIT_INT_NOT_FOUND,
"player_games_goals_get: unknown type %d.\n", type);
}
return return_value;
}
/** Change a card value for the player.
@param pl The player.
@param clid The cup or league id.
@param type Whether games or goals or shots.
@param value The new value.
@param diff Whether we add the value to the old one or
replace the old value by the new one. */
void
player_games_goals_set(Player *pl, gint clid, gint type, gint value)
{
#ifdef DEBUG
printf("player_games_goals_set\n");
#endif
gint i, *games_goals_value = NULL;
PlayerGamesGoals new;
for(i=0;i<pl->games_goals->len;i++)
if(g_array_index(pl->games_goals, PlayerGamesGoals, i).clid == clid)
{
if(type == PLAYER_VALUE_GAMES)
games_goals_value = &g_array_index(pl->games_goals, PlayerGamesGoals, i).games;
else if(type == PLAYER_VALUE_GOALS)
games_goals_value = &g_array_index(pl->games_goals, PlayerGamesGoals, i).goals;
else if(type == PLAYER_VALUE_SHOTS)
games_goals_value = &g_array_index(pl->games_goals, PlayerGamesGoals, i).shots;
*games_goals_value += value;
if(*games_goals_value < 0)
{
debug_print_message("player_games_goals_set: negative value; setting to 0\n");
*games_goals_value = 0;
}
return;
}
new.clid = clid;
new.games = new.goals = new.shots = 0;
g_array_append_val(pl->games_goals, new);
player_games_goals_set(pl, clid, type, value);
}
/** Update skill and lsu of a user player.
@param pl The player we update. */
void
player_update_skill(Player *pl)
{
#ifdef DEBUG
printf("player_update_skill\n");
#endif
gint i;
if(!query_player_is_youth(pl))
{
if(pl->age > pl->peak_age)
pl->lsu += (pl->health == 0) ? 1 : const_float("float_player_lsu_injured_old");
else if(pl->health == 0 &&
((gfloat)player_games_goals_get(pl, pl->team->clid, PLAYER_VALUE_GAMES) / (gfloat)week >=
const_float("float_player_lsu_games_percentage") ||
math_rnd(0, 1) <= const_float("float_player_lsu_increase_prob")))
pl->lsu++;
}
else
pl->lsu += (pl->health != 0) ? 0 :
const_float("float_youth_academy_lsu_addition_best") -
(user_from_team(pl->team)->youth_academy.av_coach *
const_float("float_youth_academy_lsu_penalty"));
if(pl->lsu < const_float_fast(float_player_lsu_update_limit) ||
math_rnd(0, 1) < powf(const_float("float_player_lsu_update_base_prob"),
pl->lsu - const_float_fast(float_player_lsu_update_limit)))
return;
pl->lsu = 0;
if(pl->age < pl->peak_age - pl->peak_region)
pl->skill *= (1 + ((pl->peak_age - pl->age) *
const_float_fast(float_player_skill_update_younger_factor) +
const_float_fast(float_player_skill_update_younger_add)));
else if(pl->age > pl->peak_age + pl->peak_region)
pl->skill *= (1 - ((pl->age - pl->peak_age) *
const_float("float_player_skill_update_older_factor") +
const_float("float_player_skill_update_older_add")));
else
pl->skill *= math_rnd(1 - const_float("float_player_skill_update_peak"),
1 + const_float("float_player_skill_update_peak"));
pl->skill = CLAMP(pl->skill, 0, pl->talent);
pl->cskill = player_get_cskill(pl, pl->cpos, TRUE);
pl->value = player_assign_value(pl);
for(i=0;i<QUALITY_END;i++)
if(pl->skill > pl->etal[i])
{
player_estimate_talent(pl);
break;
}
}
/** Decrement the number of weeks until
recovery depending on the user's physio.
@param pl An injured player. */
void
player_update_injury(Player *pl)
{
#ifdef DEBUG
printf("player_update_injury\n");
#endif
gint i, j;
gfloat rndom;
gint physio = (query_player_is_cpu(pl)) ?
QUALITY_GOOD : user_from_team(pl->team)->physio;
gfloat injury_decrease_probs[4][3] =
{{const_float("float_player_injury_recovery_best0"),
const_float("float_player_injury_recovery_best1"),
const_float("float_player_injury_recovery_best2")},
{const_float("float_player_injury_recovery_good0"),
const_float("float_player_injury_recovery_good1"),
const_float("float_player_injury_recovery_good2")},
{const_float("float_player_injury_recovery_good2"),
const_float("float_player_injury_recovery_good1"),
const_float("float_player_injury_recovery_good0")},
{const_float("float_player_injury_recovery_best2"),
const_float("float_player_injury_recovery_best1"),
const_float("float_player_injury_recovery_best0")}};
for(j=1;j<3;j++)
injury_decrease_probs[physio][j] += injury_decrease_probs[physio][j - 1];
rndom = math_rnd(0, 1);
for(i=0;i<3;i++)
if(rndom <= injury_decrease_probs[physio][i])
{
pl->recovery = MAX(pl->recovery - i, 0);
break;
}
if(pl->recovery == 0)
{
pl->health = 0;
pl->fitness = math_rnd(const_float("float_player_injury_recovery_fitness_lower"),
const_float("float_player_injury_recovery_fitness_upper"));
}
}
/** Weekly test whether a player goes on a hot/cold
streak; if he's on a streak, decrease the streak counter. */
void
player_update_streak(Player *pl)
{
#ifdef DEBUG
printf("player_update_streak\n");
#endif
gfloat streak_type, streak_prob,
streak_length, decrease_factor = 0;
gfloat streak_prob_factor =
const_float_fast(float_player_streak_prob_max) -
const_float_fast(float_player_streak_prob_zero),
streak_prob_add = const_float_fast(float_player_streak_prob_zero);
/** Player streak is locked. */
if(pl->streak_count < 0)
{
pl->streak_count++;
if(pl->streak_count >= 0)
pl->streak_count = 0;
return;
}
else if(pl->streak_count > 0)
{
if(pl->streak == PLAYER_STREAK_HOT)
decrease_factor = -1;
else if(pl->streak == PLAYER_STREAK_COLD)
decrease_factor = 1;
else
debug_print_message("player_update_streak: streak count is positive (%.1f) but player %s is not on a streak!\n", pl->streak_count, pl->name);
pl->streak_count -=
(pl->streak_prob * decrease_factor *
const_float("float_player_streak_count_decrease_factor") +
const_float("float_player_streak_count_decrease_add"));
/** Streak is over. */
if(pl->streak_count <= 0)
{
pl->streak = PLAYER_STREAK_NONE;
player_streak_reset_count(pl);
}
return;
}
if(query_player_is_in_ya(pl))
return;
/** Now let's find out whether there's a new streak. */
streak_type = math_rnd(-1, 1);
streak_prob = math_rnd(0, 1);
streak_length = math_rnd(const_float_fast(float_player_streak_length_lower),
const_float_fast(float_player_streak_length_upper));
if(streak_type < pl->streak_prob &&
((pl->streak_prob > 0 &&
streak_prob < streak_prob_factor * pl->streak_prob + streak_prob_add) ||
(pl->streak_prob <= 0 &&
streak_prob < streak_prob_add * pl->streak_prob + streak_prob_add)))
{
pl->streak = PLAYER_STREAK_HOT;
pl->streak_count = streak_length;
}
else if(streak_type > pl->streak_prob &&
((pl->streak_prob > 0 &&
streak_prob < -streak_prob_add * pl->streak_prob + streak_prob_add) ||
(pl->streak_prob <= 0 &&
streak_prob < -streak_prob_factor * pl->streak_prob + streak_prob_add)))
{
pl->streak = PLAYER_STREAK_COLD;
pl->streak_count = streak_length;
}
}
/** Update a player in a team (age, skill etc.). */
void
player_update_weekly(Player *pl)
{
#ifdef DEBUG
printf("player_update_weekly\n");
#endif
// workaround, possibly unnecessary
if(pl->fitness < 0)
pl->fitness = const_float("float_player_fitness_lower");
if(pl->health > 0)
player_update_injury(pl);
else
player_update_streak(pl);
if(query_player_is_cpu(pl))
return;
pl->age += 0.0192;
if(debug < 50)
if(!sett_int("int_opt_disable_contracts") &&
pl->age > const_float("float_player_age_lower"))
pl->contract -= 0.0192;
if(!sett_int("int_opt_disable_contracts") && debug < 50 &&
pl->contract * 12 <= opt_user_int("int_opt_user_contract_limit") &&
(pl->contract + 0.0192) * 12 > opt_user_int("int_opt_user_contract_limit"))
user_event_add(user_from_team(pl->team), EVENT_TYPE_WARNING,
-1, -1, NULL, _("%s's contract expires in %.1f years."),
pl->name, pl->contract);
if(pl->contract <= 0)
{
player_remove_contract(pl);
return;
}
player_update_skill(pl);
}
/** Remove a player from a user team after the contract expired.
@param tm The user team.
@param idx The player index. */
void
player_remove_contract(Player *pl)
{
#ifdef DEBUG
printf("player_remove_contract\n");
#endif
if(debug < 50)
user_event_add(user_from_team(pl->team), EVENT_TYPE_PLAYER_LEFT, -1, -1, NULL,
pl->name, NULL);
player_remove_from_team(pl->team, player_id_index(pl->team, pl->id, TRUE));
}
/** Remove a player from a team.
@param idx The player index in the players array. */
void
player_remove_from_team(Team *tm, gint idx)
{
#ifdef DEBUG
printf("player_remove_from_team\n");
#endif
gint usr_idx = team_is_user(tm);
if(usr_idx != -1 &&
player_of_idx_team(tm, idx)->id ==
option_int("int_opt_user_penalty_shooter", &usr(usr_idx).options))
option_set_int("int_opt_user_penalty_shooter", &usr(usr_idx).options, -1);
transfer_remove_player_ptr(player_of_idx_team(tm, idx));
free_player(player_of_idx_team(tm, idx));
g_array_remove_index(tm->players, idx);
}
/** Make some player updates after a match
for players.
@param pl The player we update.
@param clid The fixture clid. */
void
player_update_post_match(Player *pl, const Fixture *fix)
{
#ifdef DEBUG
printf("player_update_post_match\n");
#endif
gint yellow_red = league_cup_get_yellow_red(fix->clid);
gint winner = -1;
if(player_card_get(pl, fix->clid, PLAYER_VALUE_CARD_RED) > 0)
player_card_set(pl, fix->clid, PLAYER_VALUE_CARD_RED, -1, TRUE);
if(player_card_get(pl, fix->clid, PLAYER_VALUE_CARD_YELLOW) >= yellow_red)
{
player_card_set(pl, fix->clid, PLAYER_VALUE_CARD_YELLOW, 0, FALSE);
if(player_card_get(pl, fix->clid, PLAYER_VALUE_CARD_RED) == 0 && debug < 50)
player_card_set(pl, fix->clid, PLAYER_VALUE_CARD_RED, 1, FALSE);
}
pl->card_status = PLAYER_CARD_STATUS_NONE;
if(pl->cpos == PLAYER_POS_GOALIE &&
((fix->result[0][0] == 0 && fix->teams[1] == pl->team) ||
(fix->result[1][0] == 0 && fix->teams[0] == pl->team)))
player_streak_add_to_prob(
pl, const_float("float_player_streak_add_goalie_clean"));
/** No streak change if we had a draw. */
if(!fix->decisive && fix->result[0][0] == fix->result[1][0])
return;
winner = GPOINTER_TO_INT(fixture_winner_of(fix, TRUE));
if(winner == pl->team->id)
player_streak_add_to_prob(
pl, const_float("float_player_streak_add_win"));
else
player_streak_add_to_prob(
pl, const_float("float_player_streak_add_loss"));
}
/** Replace a player by a new one in a cpu team.
@param free_player Whether to free the memory occupied
by the player. */
void
player_replace_by_new(Player *pl, gboolean free_player)
{
#ifdef DEBUG
printf("player_replace_by_new\n");
#endif
Team *tm = pl->team;
gint idx = player_id_index(tm, pl->id, TRUE);
Player new = player_new(tm, team_get_average_talent(tm), FALSE);
new.name = name_get(pl->team->names_file);
new.pos = pl->pos;
new.cpos = pl->cpos;
new.id = pl->id;
if(free_player)
player_remove_from_team(tm, idx);
else
g_array_remove_index(tm->players, idx);
g_array_insert_val(tm->players, idx, new);
}
/** Update players in teams.
@param tm The team of the player.
@param idx The index in the players array. */
void
player_update_week_roundly(Team *tm, gint idx)
{
#ifdef DEBUG
printf("player_update_week_roundly\n");
#endif
Player *pl = player_of_idx_team(tm, idx);
if(pl->health == 0 &&
(week_round == 1 ||
(week_round > 1 && query_team_plays(tm, week, week_round))))
player_update_fitness(pl);
pl->cskill = (pl->health > 0 || player_is_banned(pl) > 0) ?
0 : player_get_cskill(pl, pl->cpos, TRUE);
}
/** Return injury descriptions. */
gchar*
player_injury_to_char(gint injury_type)
{
#ifdef DEBUG
printf("player_injury_to_char\n");
#endif
#ifdef DEBUG
printf("player_injury_to_char\n");
#endif
switch(injury_type)
{
default:
debug_print_message("player_injury_to_char: unknown type %d\n", injury_type);
return "";
break;
case PLAYER_INJURY_NONE:
return _("OK");
break;
case PLAYER_INJURY_CONCUSSION:
return _("Concussion");
break;
case PLAYER_INJURY_PULLED_MUSCLE:
return _("Pulled muscle");
break;
case PLAYER_INJURY_HAMSTRING:
return _("Hamstring");
break;
case PLAYER_INJURY_GROIN:
return _("Groin injury");
break;
case PLAYER_INJURY_FRAC_ANKLE:
return _("Fractured ankle");
break;
case PLAYER_INJURY_RIB:
return _("Broken rib");
break;
case PLAYER_INJURY_LEG:
return _("Broken leg");
break;
case PLAYER_INJURY_BROK_ANKLE:
return _("Broken ankle");
break;
case PLAYER_INJURY_ARM:
return _("Broken arm");
break;
case PLAYER_INJURY_SHOULDER:
return _("Broken shoulder");
break;
case PLAYER_INJURY_LIGAMENT:
return _("Torn crucial ligament");
break;
}
return NULL;
}
/** Nullify some stuff at the beginning of the season and change the skills
and talents of cpu players.
@param skill_change Skill/talent change of the player in percentage. */
void
player_season_start(Player *pl, gfloat skill_change)
{
#ifdef DEBUG
printf("player_season_start\n");
#endif
gint i;
if(pl->games_goals->len > 0)
{
g_array_free(pl->games_goals, TRUE);
pl->games_goals = g_array_new(FALSE, FALSE, sizeof(PlayerGamesGoals));
}
if(pl->cards->len > 0)
{
g_array_free(pl->cards, TRUE);
pl->cards = g_array_new(FALSE, FALSE, sizeof(PlayerCard));
}
if(skill_change != 0)
{
pl->talent *= (1 + skill_change);
pl->talent = CLAMP(pl->talent, 0, const_float_fast(float_player_max_skill));
pl->skill *= (1 + skill_change);
pl->skill = CLAMP(pl->skill, 0, pl->talent);
pl->cskill = player_get_cskill(pl, pl->cpos, TRUE);
pl->value = player_assign_value(pl);
for(i=0;i<QUALITY_END;i++)
if(pl->skill > pl->etal[i])
{
player_estimate_talent(pl);
break;
}
}
}
/** Return the last name of a player. */
gchar*
player_get_last_name(const gchar *name)
{
#ifdef DEBUG
printf("player_get_last_name\n");
#endif
#ifdef DEBUG
printf("player_get_last_name\n");
#endif
gint i;
gchar buf[SMALL];
gchar *rev_name = NULL;
const gchar *temp;
if(!g_utf8_validate(name, -1, NULL))
{
debug_print_message("player_get_last_name: invalid utf8-string: %s \n", name);
return "";
}
temp = name;
for(i=0;i<g_utf8_strlen(name, -1);i++)
{
temp = g_utf8_next_char(temp);
if(g_unichar_isspace(g_utf8_get_char(temp)))
break;
}
rev_name = g_utf8_strreverse(name, -1);
g_utf8_strncpy(buf, rev_name, g_utf8_strlen(name, -1) - i - 2);
g_free(rev_name);
rev_name = g_utf8_strreverse(buf, -1);
return rev_name;
}
/** Create a pointer array with pointers to the players
and return it. */
GPtrArray*
player_get_pointers_from_array(const GArray *players_array)
{
#ifdef DEBUG
printf("player_get_pointers_from_array\n");
#endif
gint i;
GPtrArray *players = g_ptr_array_new();
for(i=0;i<players_array->len;i++)
g_ptr_array_add(players, &g_array_index(players_array, Player, i));
return players;
}
/** Move a player from the team to the youth academy. */
void
player_move_to_ya(gint idx)
{
#ifdef DEBUG
printf("player_move_to_ya\n");
#endif
Player *pl = player_of_idx_team(current_user.tm, idx);
Player player = *pl;
if(query_transfer_player_is_on_list(pl))
transfer_remove_player_ptr(pl);
player.cskill = player.skill;
player.cpos = player.pos;
g_array_remove_index(current_user.tm->players, idx);
g_array_append_val(current_user.youth_academy.players, player);
}
/** Move a player from the youth academy to the team. */
void
player_move_from_ya(gint idx)
{
#ifdef DEBUG
printf("player_move_from_ya\n");
#endif
Player *pl = &g_array_index(current_user.youth_academy.players, Player, idx);
Player player = *pl;
player.contract = const_float("float_player_contract_youth");
g_array_remove_index(current_user.youth_academy.players, idx);
g_array_append_val(current_user.tm->players, player);
}
/** Wrapper for the streak probability addition operation
so that it always stays between -1 and 1. */
void
player_streak_add_to_prob(Player *pl, gfloat add)
{
#ifdef DEBUG
printf("player_streak_add_to_prob\n");
#endif
pl->streak_prob += add;
pl->streak_prob = CLAMP(pl->streak_prob, -1, 1);
}
/** Find out whether a player is in the youth academy. */
gboolean
query_player_is_in_ya(const Player *pl)
{
#ifdef DEBUG
printf("query_player_is_in_ya\n");
#endif
gint i;
for(i=0;i<pl->team->players->len;i++)
if(pl == &g_array_index(pl->team->players, Player, i))
return FALSE;
return TRUE;
}
/** Find out if a star balks when offered a new or a renewed contract
because there are already enough stars on the prospective new team. */
gboolean
query_player_star_balks(const Player *pl, const Team *tm, gboolean transfer)
{
#ifdef DEBUG
printf("query_player_star_balks\n");
#endif
gint i;
gint number_of_stars_field;
gint number_of_stars_goal;
gfloat skill_limit;
/* Weak players never balk. */
if(pl->skill < const_float("float_transfer_star_skill_limit"))
return FALSE;
/* There is some chance that the new star doesn't balk at all. */
if((transfer && math_rnd(0, 1) < const_float("float_transfer_star_no_balk")) ||
(!transfer && math_rnd(0, 1) < const_float("float_contract_star_no_balk")))
return FALSE;
/* Find out if there are any stars at all on the team. */
number_of_stars_field =
number_of_stars_goal = 0;
skill_limit = (transfer ?
const_float("float_transfer_star_skill_limit") :
const_float("float_contract_star_skill_limit"));
for(i = 0; i < tm->players->len; i++)
{
if(g_array_index(tm->players, Player, i).skill > skill_limit)
{
if(g_array_index(tm->players, Player, i).pos == PLAYER_POS_GOALIE)
number_of_stars_goal++;
else
number_of_stars_field++;
}
}
if(pl->pos == PLAYER_POS_GOALIE)
{
if(number_of_stars_goal == 0)
return FALSE;
else
return (math_rnd(0, 1) > const_float("float_transfer_star_goalie_accepts"));
}
else
{
if(number_of_stars_field == 0)
return FALSE;
else
return (math_rnd(0, 1) > 1 - number_of_stars_field *
const_float("float_transfer_star_prob_decrease"));
}
}