/*
   live_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 "callbacks.h"
#include "fixture.h"
#include "free.h"
#include "game.h"
#include "game_gui.h"
#include "league.h"
#include "lg_commentary.h"
#include "live_game.h"
#include "main.h"
#include "maths.h"
#include "misc.h"
#include "misc_callback_func.h"
#include "option.h"
#include "player.h"
#include "strategy.h"
#include "support.h"
#include "team.h"
#include "treeview.h"
#include "user.h"
#include "variables.h"
#include "window.h"

/** The live game we calculate. */
#define match ((LiveGame*)statp)

/** Whether the events are actually shown or not. */
gboolean show;

/** Convenience abbrevs. */
#define unis match->units
#define uni(i) g_array_index(unis, LiveGameUnit, i)
#define last_unit uni(unis->len - 1)
#define tms match->fix->teams
#define tm0 match->fix->teams[0]
#define tm1 match->fix->teams[1]

/** Calculate the result of a fixture using
    the live game variable.
    @param fix The fixture we calculate.
*/
void
live_game_calculate_fixture(Fixture *fix)
{
    if(stat0 != STATUS_LIVE_GAME_PAUSE && 
       stat0 != STATUS_LIVE_GAME_CHANGE)
	live_game_initialize(fix);
    else
	stat0 = STATUS_SHOW_LIVE_GAME;

    game_get_values(match->fix, match->team_values,
		    match->home_advantage);

    if((debug > 80 && stat2 != -1) ||
       debug > 130)
	g_print("\n\nlive_game_calculate_fixture\n%04d %s %s %04d\n\n",
	       tm0->id, tm0->name, tm1->name, tm1->id);

    do
    {
	live_game_create_unit();
	live_game_evaluate_unit(&last_unit);
    }
    while(last_unit.event.type != LIVE_GAME_EVENT_END_MATCH &&
	  stat0 != STATUS_LIVE_GAME_PAUSE &&
	  stat0 != STATUS_LIVE_GAME_CHANGE);

    if(last_unit.event.type == LIVE_GAME_EVENT_END_MATCH)
    {
	if(stat2 != -1 || stat5 < -1000)
	    lg_commentary_free_tokens();
	game_post_match(fix);
    }
    else if(stat0 == STATUS_LIVE_GAME_CHANGE)
	live_game_resume();
}

/** Initialize a few things at the beginning of a live game. */
void
live_game_initialize(Fixture *fix)
{
    stat2 = fixture_user_team_involved(fix);

    statp= (stat2 != -1) ? 
	&usr(stat2).live_game : &live_game_temp;
    show = (stat2 != -1 && 
	    option_int("int_opt_user_show_live_game", 
		       &usr(stat2).options));

    live_game_reset(match, fix, TRUE);

    if(show)
    {
	cur_user = stat2;
	on_button_back_to_main_clicked(NULL, NULL);

	if(window.live == NULL)
	    window.live = window_create(WINDOW_LIVE);
	else
	    gtk_window_set_title(
		GTK_WINDOW(window.live),
		league_cup_get_name_string(((LiveGame*)statp)->fix->clid));
	window_live_set_up();
	game_gui_live_game_show_opponent();
    }

    game_initialize(fix);
    match->attendance = fix->attendance;

    if(stat2 != -1 || stat5 < -1000)
	lg_commentary_initialize(fix);
}

/** Create a game unit for the live game.
    @see #LiveGameUnit, #LiveGame, live_game_fill_new_unit() */
void
live_game_create_unit(void)
{
    LiveGameUnit new;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_create_unit\n");

    if(unis->len == 0)
    {
	live_game_create_start_unit();
	return;
    }

    if(uni(unis->len - 1).event.type == LIVE_GAME_EVENT_END_MATCH)
    {
	g_warning("live_game_create_unit: called after end of match.\n");
	return;
    }

    new.minute = live_game_get_minute();
    new.time = live_game_get_time(&last_unit);
    new.event.commentary = NULL;
    new.event.team =
	new.event.player =
	new.event.player2 = -1;
    new.area = last_unit.area;
    new.result[0] = last_unit.result[0];
    new.result[1] = last_unit.result[1];

    if(last_unit.event.type == LIVE_GAME_EVENT_HALF_TIME ||
       last_unit.event.type == LIVE_GAME_EVENT_EXTRA_TIME)
    {
	live_game_event_general(TRUE);
	return;
    }
    else if(query_live_game_event_is_break(new.minute, new.time))
    {
	new.event.type = live_game_get_break();
	new.possession = last_unit.possession;
	g_array_append_val(unis, new);
	return;
    }
    else if(new.time == LIVE_GAME_UNIT_TIME_PENALTIES)
	new.event.type = LIVE_GAME_EVENT_PENALTY;
    else
	live_game_fill_new_unit(&new);

    g_array_append_val(unis, new);
}

/** Fill in a new unit depending on the team values and the constants from above.
    @param new The unit to fill in. */
void
live_game_fill_new_unit(LiveGameUnit *new)
{
    LiveGameUnit *old = &last_unit;
    gfloat rndom = math_rnd(0, 1);
    gfloat stadium_event = 
	1 - powf(tm0->stadium.safety,
		 const_float("float_live_game_stadium_event_exponent"));
    gfloat possession_change, scoring_chance = 0, 
	injury_event_prob, foul_event_prob;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_fill_new_unit\n");

    possession_change = const_float("float_live_game_event_general") *
	const_float("float_live_game_possession_changes") /
	live_game_pit_teams(old, const_float("float_live_game_possession_team_exponent"));	

    injury_event_prob = const_float("float_live_game_injury") * 
	(1 + (const_float("float_player_boost_injury_effect") *
	      (tm0->boost != 0 || tm1->boost != 0)));

    foul_event_prob = const_float("float_live_game_foul") *
	(1 + (tm0->boost + tm1->boost) * const_float("float_team_boost_foul_factor"));

    new->possession = old->possession;

    if(old->event.type == LIVE_GAME_EVENT_GENERAL)
	new->area = live_game_get_area(new);

    if(new->area == LIVE_GAME_UNIT_AREA_ATTACK)
	scoring_chance = const_float("float_live_game_scoring_chance") *
	    live_game_pit_teams(new, const_float("float_live_game_scoring_chance_team_exponent"));

    if(rndom < foul_event_prob)
	new->event.type = LIVE_GAME_EVENT_FOUL;
    else if(rndom < foul_event_prob +
	    injury_event_prob)
	new->event.type = LIVE_GAME_EVENT_INJURY;
    else if(rndom < foul_event_prob +
	    injury_event_prob +
	    stadium_event && match->stadium_event == -1)
	new->event.type = LIVE_GAME_EVENT_STADIUM;
    else if(rndom < foul_event_prob +
	    injury_event_prob +
	    stadium_event + possession_change)
    {
	new->event.type = LIVE_GAME_EVENT_LOST_POSSESSION;
	new->possession = !old->possession;
	if(new->area == LIVE_GAME_UNIT_AREA_ATTACK)
	    new->area = LIVE_GAME_UNIT_AREA_DEFEND;
	else if(new->area == LIVE_GAME_UNIT_AREA_DEFEND)
	    new->area = LIVE_GAME_UNIT_AREA_ATTACK;

    }
    else if(rndom < foul_event_prob +
	    injury_event_prob +
	    stadium_event + possession_change +
	    scoring_chance)
	new->event.type = LIVE_GAME_EVENT_SCORING_CHANCE;
    else
	new->event.type = LIVE_GAME_EVENT_GENERAL;
}

/** Create the first unit of a match. */
void
live_game_create_start_unit(void)
{
    LiveGameUnit new;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_create_start_unit\n");
    new.event.player =
	new.event.player2 = -1;

    new.minute = 0;
    new.event.commentary = NULL;
    new.time = LIVE_GAME_UNIT_TIME_FIRST_HALF;
    new.possession = math_rndi(0, 1);
    new.area = LIVE_GAME_UNIT_AREA_MIDFIELD;
    match->started_game = new.possession;
    new.result[0] = new.result[1] = 0;

    new.event.type = LIVE_GAME_EVENT_START_MATCH;
    new.event.team = new.possession;
	    
    g_array_append_val(unis, new);
}

/** Evaluate a live game unit. This means we find out what happens
    after the unit, depending on its type.
    @param unit The unit we evaluate.
    @see The live_game_event* functions. */
void
live_game_evaluate_unit(LiveGameUnit *unit)
{
    gint type = unit->event.type;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_evaluate_unit\n");
    if(type == LIVE_GAME_EVENT_FOUL)
	live_game_event_foul();
    else if(type == LIVE_GAME_EVENT_LOST_POSSESSION)
	live_game_event_lost_possession();
    else if(type == LIVE_GAME_EVENT_INJURY)
	live_game_event_injury(-1, -1, FALSE);
    else if(type == LIVE_GAME_EVENT_STADIUM)
	live_game_event_stadium();
    else if(type == LIVE_GAME_EVENT_SCORING_CHANCE)
	live_game_event_scoring_chance();
    else if(type == LIVE_GAME_EVENT_PENALTY)
	live_game_event_penalty();
    else if(type == LIVE_GAME_EVENT_GENERAL)
	live_game_event_general(FALSE);
    else if(type == LIVE_GAME_EVENT_START_MATCH)
	live_game_finish_unit();
    else if(type == LIVE_GAME_EVENT_HALF_TIME ||
	    type == LIVE_GAME_EVENT_EXTRA_TIME ||
	    type == LIVE_GAME_EVENT_PENALTIES ||
	    type == LIVE_GAME_EVENT_END_MATCH)
    {
	live_game_finish_unit();
	if(type != LIVE_GAME_EVENT_END_MATCH && show && 
	   option_int("int_opt_user_pause_break", &usr(stat2).options))
	    misc_callback_pause_live_game();
    }
    else if(type != LIVE_GAME_EVENT_END_MATCH)
	g_warning("live_game_evaluate_unit: unknown event type %d\n",
		  type);
}

/** Calculate a foul event. */
void
live_game_event_foul(void)
{
    gfloat rndom = math_rnd(0, 1);
    gint type, fouled_player, foul_player, foul_team;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_foul\n");
    if(math_rnd(0, 1) > const_float("float_live_game_foul_by_possession") *
       game_get_foul_possession_factor(
	   tms[last_unit.possession]->boost, tms[!last_unit.possession]->boost))
    {	
	foul_team = last_unit.event.team = !last_unit.possession;
	if(uni(unis->len - 2).event.type == LIVE_GAME_EVENT_GENERAL)
	    fouled_player = last_unit.event.player =
		uni(unis->len - 2).event.player;
	else
	    fouled_player = last_unit.event.player =
		game_get_player(tms[last_unit.possession],
				last_unit.area, 0, -1, FALSE);

	foul_player = last_unit.event.player2 =
	    game_get_player(tms[!last_unit.possession],
			    last_unit.area, 0, -1, FALSE);
    }
    else
    {
	foul_team = last_unit.event.team = last_unit.possession;
	fouled_player = last_unit.event.player = 
	    game_get_player(tms[!last_unit.possession],
			    last_unit.area, 0, -1, FALSE);
	foul_player = last_unit.event.player2 =
	    game_get_player(tms[last_unit.possession],
			    last_unit.area, 0, -1, FALSE);
    }

    if(rndom < const_float("float_live_game_foul_red_injury"))
	type = LIVE_GAME_EVENT_FOUL_RED_INJURY;
    else if(rndom < const_float("float_live_game_foul_red"))
	type = LIVE_GAME_EVENT_FOUL_RED;
    else if(rndom < const_float("float_live_game_foul_yellow"))
    {
	type = LIVE_GAME_EVENT_FOUL_YELLOW;
	player_card_set(player_of_id_team(tms[foul_team], foul_player),
			match->fix->clid, PLAYER_VALUE_CARD_YELLOW, 1, TRUE);
	player_of_id_team(tms[foul_team], foul_player)->career[PLAYER_VALUE_CARD_YELLOW]++;
    }
    else
	type = LIVE_GAME_EVENT_FOUL;

    last_unit.event.type = type;

    live_game_finish_unit();

    if(type == LIVE_GAME_EVENT_FOUL_RED ||
       type == LIVE_GAME_EVENT_FOUL_RED_INJURY ||
       (type == LIVE_GAME_EVENT_FOUL_YELLOW &&
	query_live_game_second_yellow(foul_team, foul_player)))
    {
	live_game_event_send_off(foul_team, foul_player, 
				 query_live_game_second_yellow(foul_team, foul_player));
	if(type == LIVE_GAME_EVENT_FOUL_RED_INJURY)
	    live_game_event_injury(!foul_team, fouled_player, TRUE);
    }

    if(last_unit.area == LIVE_GAME_UNIT_AREA_ATTACK && foul_team !=
       last_unit.possession)
    {
	rndom = math_rnd(0, 1);
	if(rndom < const_float("float_live_game_penalty_prob"))
	    live_game_event_penalty();
	else if(rndom < const_float("float_live_game_free_kick_prob"))
	    live_game_event_free_kick();
	else
	    last_unit.possession = !foul_team;
    }
    else
	last_unit.possession = !foul_team;
}

/** Calculate a lost possession event. */
void
live_game_event_lost_possession(void)
{
    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_lost_possession\n");
    last_unit.event.player =
	game_get_player(tms[last_unit.possession], 
			last_unit.area, 0, -1, TRUE);
	
    if(uni(unis->len - 2).event.type == LIVE_GAME_EVENT_GENERAL)
	last_unit.event.player2 =
	    uni(unis->len - 2).event.player;
    else
	last_unit.event.player2 =
	    game_get_player(tms[!last_unit.possession], 
			    uni(unis->len - 2).area, 0, -1, FALSE);

    live_game_finish_unit();

    live_game_event_general(TRUE);
}

/** Calculate an injury event. 
    @param team The team the player is from.
    @param player The player that's injured, or -1 if we have to 
    choose a random one.
    @param create_new Whether to put the event into a new unit instead of
    the last one. */
void
live_game_event_injury(gint team, gint player, gboolean create_new)
{
    LiveGameUnit new;
    gint old_structure = -1, sub_in = -1;
    gint usr_idx = -1;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_injury\n");

    if(create_new)
    {
	new = last_unit;
	new.event.commentary = NULL;
	g_array_append_val(unis, new);
	last_unit.event.player =
	    player;
	last_unit.event.team =
	    team;
    }
    else
	live_game_injury_get_player();

    usr_idx = team_is_user(tms[last_unit.event.team]);
    
    last_unit.minute = -1;
    last_unit.event.type = LIVE_GAME_EVENT_INJURY;

    if(math_rnd(0, 1) < const_float("float_live_game_injury_is_temp"))
    {
	last_unit.event.type = LIVE_GAME_EVENT_TEMP_INJURY;

	if(debug < 50 ||
	   usr_idx == -1)
	    player_of_id_team(tms[last_unit.event.team],
			      last_unit.event.player)->fitness =
		MAX(0, player_of_id_team(tms[last_unit.event.team],
					 last_unit.event.player)->fitness -
		    math_rnd(
			const_float("float_live_game_temp_injury_fitness_decrease_lower"),
			const_float("float_live_game_temp_injury_fitness_decrease_upper")));
    }
    
    live_game_finish_unit();

    if(debug >= 50 &&
       usr_idx != -1)
	return;

    if(last_unit.event.type == LIVE_GAME_EVENT_INJURY)
    {
	game_player_injury(player_of_id_team(tms[last_unit.event.team],
					     last_unit.event.player));

	if(match->subs_left[last_unit.event.team] > 0)
	{
	    if(show && 
	       usr_idx != -1 && 
	       ((option_int("int_opt_user_pause_injury",
			    &usr(usr_idx).options) &&
		 !option_int("int_opt_user_auto_sub",
			     &usr(usr_idx).options)) ||
		tms[last_unit.event.team]->players->len == 11))
		misc_callback_pause_live_game();
	    else if(tms[last_unit.event.team]->players->len > 11)
	    {
		sub_in = game_substitute_player(tms[last_unit.event.team],
						player_id_index(tms[last_unit.event.team],
								last_unit.event.player));
		if(sub_in != -1)
		{
		    old_structure = tms[last_unit.event.team]->structure;
		    live_game_event_substitution(
			last_unit.event.team, sub_in,
			last_unit.event.player);
		    
		if(old_structure != tms[last_unit.event.team]->structure)
		    live_game_event_team_change(last_unit.event.team,
						LIVE_GAME_EVENT_STRUCTURE_CHANGE);
		}
	    }
	}
    }

    game_get_values(match->fix, match->team_values,
		    match->home_advantage);
}

/** Calculate a stadium event. */
void
live_game_event_stadium(void)
{
    gint i;
    gfloat rndom = math_rnd(0, 1);
    gfloat probs[3] =
	{const_float("float_live_game_stadium_event_breakdown"),
	 const_float("float_live_game_stadium_event_riots"),
	 const_float("float_live_game_stadium_event_fire")};

    for(i=1;i<3;i++)
	probs[i] += probs[i - 1];

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_stadium\n");
    if(rndom <= probs[0])
	last_unit.event.type = LIVE_GAME_EVENT_STADIUM_BREAKDOWN;
    else if(rndom <= probs[1])
	last_unit.event.type = LIVE_GAME_EVENT_STADIUM_RIOTS;
    else if(rndom <= probs[2])
	last_unit.event.type = LIVE_GAME_EVENT_STADIUM_FIRE;

    live_game_finish_unit();

    if(team_is_user(tm0) != -1 && debug < 50)
	game_stadium_event(&tm0->stadium, last_unit.event.type);

    match->stadium_event = last_unit.event.type;

    live_game_event_general(TRUE);
}

/** Calculate a scoring chance event. */
void
live_game_event_scoring_chance(void)
{
    gint res_idx = -1;

    if(last_unit.time == LIVE_GAME_UNIT_TIME_EXTRA_TIME)
	res_idx = 1;
    else
	res_idx = 0;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_scoring_chance\n");

    if(math_rnd(0, 1) < const_float("float_live_game_scoring_chance_is_own_goal"))
    {
	last_unit.event.type = LIVE_GAME_EVENT_OWN_GOAL;
	last_unit.event.player =
	    game_get_player(tms[!last_unit.possession], GAME_PLAYER_TYPE_DEFEND, 0, -1, FALSE);
	last_unit.event.team = !last_unit.possession;
	match->fix->result[last_unit.possession][res_idx]++;
	last_unit.result[last_unit.possession]++;

	player_streak_add_to_prob(player_of_id_team(tms[last_unit.event.team],
						    last_unit.event.player),
				  const_float("float_player_streak_add_own_goal"));
    }
    else
    {
	last_unit.event.team = last_unit.possession;

	if(uni(unis->len - 2).event.player != -1 &&
	   math_rnd(0, 1) < const_float("float_live_game_player_in_poss_shoots") &&
	   query_player_id_in_team(uni(unis->len - 2).event.player,
				   tms[last_unit.possession]))
	    last_unit.event.player =
		uni(unis->len - 2).event.player;
	else
	{
	    if(uni(unis->len - 2).event.player != -1 &&
	       query_player_id_in_team(uni(unis->len - 2).event.player,
				       tms[last_unit.possession]))
	    {
		last_unit.event.player =
		    game_get_player(tms[last_unit.possession], last_unit.area, 0, 
				    uni(unis->len - 2).event.player,
				    TRUE);

		last_unit.event.player2 =
		    uni(unis->len - 2).event.player;
	    }
	    else
	    {
		last_unit.event.player =
		    game_get_player(
			tms[last_unit.possession], last_unit.area, 0, -1, TRUE);

		last_unit.event.player2 =
		    game_get_player(tms[last_unit.possession], last_unit.area, 0, 
				    last_unit.event.player, TRUE);
	    }
	}

	if(math_rnd(0, 1) < const_float("float_live_game_scoring_chance_is_header"))
	    last_unit.event.type = LIVE_GAME_EVENT_HEADER;
    }

    live_game_finish_unit();

    if(last_unit.event.type != LIVE_GAME_EVENT_OWN_GOAL)
	live_game_event_duel();
    else
	live_game_event_general(TRUE);
}

/** Calculate a penalty event. */
void
live_game_event_penalty(void)
{
    LiveGameUnit new;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_penalty\n");

    if(last_unit.time != LIVE_GAME_UNIT_TIME_PENALTIES)
    {
	new = last_unit;
	new.event.commentary = NULL;
	new.minute = -1;
	new.event.type = LIVE_GAME_EVENT_PENALTY;

	g_array_append_val(unis, new);
    }

    last_unit.event.player2 =
	last_unit.event.team = -1;

    if(last_unit.time == LIVE_GAME_UNIT_TIME_PENALTIES)
    {
	if(live_game_penalties_taken() == 1)
	{
	    last_unit.possession = math_rndi(0, 1);
	    last_unit.event.player =
		game_get_player(tms[last_unit.possession],
				GAME_PLAYER_TYPE_PENALTY, -1, -1, FALSE);
	}
	else if(live_game_penalties_taken() == 2)
	{
	    last_unit.possession = !uni(unis->len - 3).possession;
	    last_unit.event.player =
		game_get_player(tms[last_unit.possession],
				GAME_PLAYER_TYPE_PENALTY, -1, -1, FALSE);
	}
	else
	{
	    last_unit.possession = !uni(unis->len - 3).possession;
	    last_unit.event.player =
		game_get_player(tms[last_unit.possession],
				GAME_PLAYER_TYPE_PENALTY,
				uni(unis->len - 4).event.player, -1, FALSE);
	}
    }
    else
    {
	last_unit.event.team =
	    last_unit.possession;

	last_unit.event.player = 
	    game_get_default_penalty_shooter(tms[last_unit.possession]);
	if(last_unit.event.player == -1)
	    last_unit.event.player =
		game_get_player(tms[last_unit.possession], GAME_PLAYER_TYPE_PENALTY, -1, -1, FALSE);
    }

    live_game_finish_unit();

    live_game_event_duel();
}

/** Calculate a general event. 
    @param create_new Whether we create a new unit for the event. */
void
live_game_event_general(gboolean create_new)
{
    LiveGameUnit new;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_general\n");

    if(create_new && stat0 == STATUS_LIVE_GAME_PAUSE)
	return;

    if(create_new)
    {
	new.minute = live_game_get_minute();
	new.time = last_unit.time;
	new.event.commentary = NULL;
	new.event.type = LIVE_GAME_EVENT_GENERAL;
	new.result[0] = last_unit.result[0];
	new.result[1] = last_unit.result[1];
	new.event.team = -1;

	if(last_unit.event.type == LIVE_GAME_EVENT_GENERAL ||
	   last_unit.event.type == LIVE_GAME_EVENT_START_MATCH ||
	   last_unit.event.type == LIVE_GAME_EVENT_LOST_POSSESSION ||
	   last_unit.event.type == LIVE_GAME_EVENT_FOUL ||
	   last_unit.event.type == LIVE_GAME_EVENT_FOUL_YELLOW ||
	   last_unit.event.type == LIVE_GAME_EVENT_SEND_OFF ||
	   last_unit.event.type == LIVE_GAME_EVENT_INJURY ||
	   last_unit.event.type == LIVE_GAME_EVENT_TEMP_INJURY ||
	   last_unit.event.type == LIVE_GAME_EVENT_STADIUM ||
	   last_unit.event.type == LIVE_GAME_EVENT_STADIUM_BREAKDOWN ||
	   last_unit.event.type == LIVE_GAME_EVENT_STADIUM_FIRE ||
	   last_unit.event.type == LIVE_GAME_EVENT_STADIUM_RIOTS ||
	   (last_unit.event.type >= LIVE_GAME_EVENT_STRUCTURE_CHANGE &&
	    last_unit.event.type <= LIVE_GAME_EVENT_BOOST_CHANGE_ON) ||
	   ((last_unit.event.type == LIVE_GAME_EVENT_POST ||
	     last_unit.event.type == LIVE_GAME_EVENT_CROSS_BAR) &&
	    math_rnd(0, 1) < const_float("float_live_game_possession_after_post")))
	{
	    new.possession = last_unit.possession;
	    new.area = (last_unit.event.type == LIVE_GAME_EVENT_GENERAL) ?
		live_game_get_area(&last_unit) : last_unit.area;
	}
	else if(last_unit.event.type == LIVE_GAME_EVENT_GOAL ||
		last_unit.event.type == LIVE_GAME_EVENT_OWN_GOAL ||
		last_unit.event.type == LIVE_GAME_EVENT_MISS ||
		last_unit.event.type == LIVE_GAME_EVENT_SAVE ||
		last_unit.event.type == LIVE_GAME_EVENT_POST ||
		last_unit.event.type == LIVE_GAME_EVENT_CROSS_BAR)
	{
	    new.possession = !last_unit.possession;
	    if(last_unit.event.type == LIVE_GAME_EVENT_GOAL ||
	       last_unit.event.type == LIVE_GAME_EVENT_OWN_GOAL)
		new.area = LIVE_GAME_UNIT_AREA_MIDFIELD;
	    else
		new.area = LIVE_GAME_UNIT_AREA_DEFEND;
	}
	else if(last_unit.event.type == LIVE_GAME_EVENT_HALF_TIME)
	{
	    new.possession = !match->started_game;
	    new.time = LIVE_GAME_UNIT_TIME_SECOND_HALF;
	    new.area = LIVE_GAME_UNIT_AREA_MIDFIELD;
	}
	else if(last_unit.event.type == LIVE_GAME_EVENT_EXTRA_TIME)
	{
	    new.possession = math_rndi(0, 1);
	    new.time = LIVE_GAME_UNIT_TIME_EXTRA_TIME;
	    new.area = LIVE_GAME_UNIT_AREA_MIDFIELD;
	}
	else
	    main_exit_program(EXIT_INT_NOT_FOUND, 
			      "live_game_event_general: unknown event type: %d\n",
			      last_unit.event.type);
	
	g_array_append_val(unis, new);
    }

    live_game_event_general_get_players();

    live_game_finish_unit();

    /** First, check whether CPU strategy changes are made. */
    if(team_is_user(tm0) == -1)
	strategy_live_game_check(match, 0);

    if(team_is_user(tm1) == -1)
	strategy_live_game_check(match, 1);

    if(last_unit.event.type >= LIVE_GAME_EVENT_STRUCTURE_CHANGE &&
       last_unit.event.type <= LIVE_GAME_EVENT_BOOST_CHANGE_ON)
	live_game_event_general(TRUE);
}

/** Fill in the players values in a general unit. */
void
live_game_event_general_get_players(void)
{
    gint *pl1 = &last_unit.event.player;
    gint *pl2 = &last_unit.event.player2;
    gint old_pl1 = 
	uni(unis->len - 2).event.player;
    gint type = uni(unis->len - 2).event.type;
	
    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_general_get_players\n");
    *pl1 = *pl2 = -1;

    if(type == LIVE_GAME_EVENT_LOST_POSSESSION)
    {
	*pl2 = old_pl1;
	*pl1 = game_get_player(tms[last_unit.possession], 
			       last_unit.area, 0, *pl2,
			       TRUE);
    }
    else if(type != LIVE_GAME_EVENT_GENERAL)
    {
	*pl1 = game_get_player(tms[last_unit.possession],
			       last_unit.area, 0, -1, TRUE);
	if(math_rnd(0, 1) < const_float("float_live_game_general_event_second_player"))
	    *pl2 = game_get_player(tms[last_unit.possession],
				   last_unit.area, 0, *pl1, TRUE);
    }
    else
    {
	*pl2 = query_player_id_in_team(old_pl1, tms[last_unit.possession]) ? old_pl1 : -1;
	*pl1 = game_get_player(tms[last_unit.possession],
			       last_unit.area, 0, *pl2, TRUE);
    }
}

/** Calculate a free kick event. */
void
live_game_event_free_kick(void)
{
    LiveGameUnit new = last_unit;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_free_kick\n");
    new.event.player =
	new.event.player2 = -1;

    new.minute = -1;

    new.event.type = LIVE_GAME_EVENT_FREE_KICK;
    new.event.team =
	new.possession;
    new.event.commentary = NULL;

    new.event.player =
	game_get_default_penalty_shooter(tms[new.possession]);

    if(new.event.player == -1)
	new.event.player =
	    game_get_player(tms[new.possession], new.area, 0, -1, TRUE);

    g_array_append_val(unis, new);

    live_game_finish_unit();

    live_game_event_duel();
}

/** Calculate a send-off event. */
void
live_game_event_send_off(gint team, gint player, gboolean second_yellow)
{
    LiveGameUnit new = last_unit;
    gint substitute = -1, to_substitute = -1;
    gint usr_idx = team_is_user(tms[team]);

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_send_off\n");
    new.event.player =
	new.event.player2 = -1;

    new.minute = -1;

    new.event.type = LIVE_GAME_EVENT_SEND_OFF;
    new.event.player = player;
    new.event.team = team;
    new.event.commentary = NULL;

    g_array_append_val(unis, new);

    live_game_finish_unit();

    if(debug >= 50 && usr_idx != -1)
	return;

    player_streak_add_to_prob(
	player_of_id_team(tms[team], player),
	const_float("float_player_streak_add_sendoff"));
    if(player_of_id_team(tms[team], player)->streak == PLAYER_STREAK_HOT)
    {
	player_of_id_team(tms[team], player)->streak = PLAYER_STREAK_NONE;
	player_streak_reset_count(player_of_id_team(tms[team], player));
    }

    player_of_id_team(tms[team], player)->cskill = 0;

    if(second_yellow)
	player_card_set(player_of_id_team(tms[team], player), match->fix->clid, PLAYER_VALUE_CARD_RED, 2, FALSE);
    else
	player_card_set(player_of_id_team(tms[team], player), match->fix->clid, PLAYER_VALUE_CARD_RED, 
			game_player_get_ban_duration(), FALSE);

    player_of_id_team(tms[team], player)->career[PLAYER_VALUE_CARD_RED]++;
    
    if(usr_idx != -1)
    {
	tms[team]->structure = team_find_appropriate_structure(tms[team]);
	team_rearrange(tms[team]);
	live_game_event_team_change(team, LIVE_GAME_EVENT_STRUCTURE_CHANGE);
    }

    if(match->subs_left[team] > 0)
    {
	if(show && usr_idx != -1 &&
	   ((option_int("int_opt_user_pause_red",
			&usr(usr_idx).options) &&
	     !option_int("int_opt_user_auto_sub",
			 &usr(usr_idx).options)) ||
	    tms[team]->players->len == 1))
	    misc_callback_pause_live_game();
	else if(tms[team]->players->len > 11)
	{
	    game_substitute_player_send_off(match->fix->clid,
					    tms[team], player_id_index(tms[team], player),
					    &to_substitute, &substitute);

	    if(to_substitute != -1)
		live_game_event_substitution(team, substitute, to_substitute);
	    else
	    {
		tms[team]->structure = team_find_appropriate_structure(tms[team]);
		team_rearrange(tms[team]);
	    }
	    live_game_event_team_change(team, LIVE_GAME_EVENT_STRUCTURE_CHANGE);
	}
    }
    else if(usr_idx == -1)
    {
	tms[team]->structure = team_find_appropriate_structure(tms[team]);
	team_rearrange(tms[team]);
	live_game_event_team_change(team, LIVE_GAME_EVENT_STRUCTURE_CHANGE);
    }

    game_get_values(match->fix, match->team_values,
		    match->home_advantage);
}

/** Show a substitution event.
    @param team_number The team that substitutes.
    @param sub_in The id of the player who moves into the team.
    @param sub_out The id of the player who gets replaced. */
void
live_game_event_substitution(gint team_number, gint sub_in, gint sub_out)
{
    LiveGameUnit new = last_unit;
    
    new.minute = -1;
    new.time = live_game_get_time(&last_unit);

    new.event.type = LIVE_GAME_EVENT_SUBSTITUTION;
    new.event.team = team_number;
    new.event.player = sub_in;
    new.event.player2 = sub_out;
    new.event.commentary = NULL;

    if(player_of_id_team(tms[team_number], sub_in)->cskill > 0)
    {
	match->subs_left[team_number]--;

	player_streak_add_to_prob(
	    player_of_id_team(tms[team_number], sub_in),
	    const_float("float_player_streak_add_sub_out"));
	
	player_streak_add_to_prob(
	    player_of_id_team(tms[team_number], sub_in),
	    const_float("float_player_streak_add_sub_in"));	

	player_games_goals_set(player_of_id_team(tms[team_number], sub_in),
			       match->fix->clid, PLAYER_VALUE_GAMES, 1);
	player_of_id_team(tms[team_number], sub_in)->career[PLAYER_VALUE_GAMES]++;
	player_of_id_team(tms[team_number], sub_in)->participation = TRUE;

	if(show)
	    game_gui_live_game_show_opponent();
    }

    g_array_append_val(unis, new);

    live_game_finish_unit();
}

/** Show a team change event, e.g. structure change. 
    @param team_number The index of the team.
    @param event_type The event type. */
void
live_game_event_team_change(gint team_number, gint event_type)
{
    LiveGameUnit new = last_unit;
    
    new.minute = -1;
    new.time = live_game_get_time(&last_unit);

    new.event.team = team_number;
    new.event.player =
	new.event.player2 = -1;    
    new.event.type = event_type;
    new.event.commentary = NULL;

    g_array_append_val(unis, new);
    
    live_game_finish_unit();

    if(show)
	game_gui_live_game_show_opponent();
}

/** Calculate whether a player who tries to score succeeds. */
void
live_game_event_duel(void)
{
    gfloat rndom = math_rnd(0, 1);
    gfloat scoring_prob;
    gfloat duel_factor;
    LiveGameUnit new = last_unit;
    Player *attacker, *goalie, *assistant;
    gint res_idx1, res_idx2;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_event_duel\n");

    new.minute = -1;
    new.event.team = new.possession;
    new.event.commentary = NULL;
	
    attacker = player_of_id_team(tms[new.possession],
				 new.event.player);
    goalie = player_of_idx_team(tms[!new.possession], 0);

    assistant = (new.event.player2 != -1) ? 
	player_of_id_team(tms[new.possession], new.event.player2) : NULL;

    new.event.player2 = goalie->id;

    duel_factor = player_get_game_skill(attacker, FALSE, TRUE) /
	player_get_game_skill(goalie, FALSE, TRUE);

    res_idx1 = new.possession;
    if(new.time == LIVE_GAME_UNIT_TIME_PENALTIES)
	res_idx2 = 2;
    else if(new.time == LIVE_GAME_UNIT_TIME_EXTRA_TIME)
	res_idx2 = 1;
    else
	res_idx2 = 0;

    if(last_unit.event.type == LIVE_GAME_EVENT_PENALTY)
	scoring_prob = const_float("float_live_game_score_penalty") * duel_factor * attacker->team->luck;
    else if(last_unit.event.type == LIVE_GAME_EVENT_FREE_KICK)
	scoring_prob = const_float("float_live_game_score_free_kick") * duel_factor * attacker->team->luck;
    else
	scoring_prob = const_float("float_live_game_score_base_prob") * 
	    powf(duel_factor, const_float("float_live_game_score_duel_exponent")) *
	    powf(match->team_values[new.possession][GAME_TEAM_VALUE_ATTACK] /
		 match->team_values[!new.possession][GAME_TEAM_VALUE_DEFEND],
		 const_float("float_live_game_score_team_exponent")) * attacker->team->luck;

    if(new.time != LIVE_GAME_UNIT_TIME_PENALTIES)
    {
	player_games_goals_set(attacker, match->fix->clid, PLAYER_VALUE_SHOTS, 1);
	attacker->career[PLAYER_VALUE_SHOTS]++;
    }

    if(rndom < scoring_prob)
    {
	new.event.type = LIVE_GAME_EVENT_GOAL;
	match->fix->result[res_idx1][res_idx2]++;
	new.result[res_idx1]++;

	if(new.time != LIVE_GAME_UNIT_TIME_PENALTIES)
	{
	    player_games_goals_set(attacker, match->fix->clid, PLAYER_VALUE_GOALS, 1);
	    player_games_goals_set(goalie, match->fix->clid, PLAYER_VALUE_GOALS, 1);
	    attacker->career[PLAYER_VALUE_GOALS]++;
	    goalie->career[PLAYER_VALUE_GOALS]++;

	    player_streak_add_to_prob(attacker,
				      const_float("float_player_streak_add_goal"));
	    player_streak_add_to_prob(goalie,
				      const_float("float_player_streak_add_goalie_goal"));
	    if(assistant != NULL)
		player_streak_add_to_prob(
		    assistant, const_float("float_player_streak_add_assist"));
	}
    }
    else
	new.event.type = math_gauss_disti(LIVE_GAME_EVENT_POST, LIVE_GAME_EVENT_CROSS_BAR);

    if(new.time != LIVE_GAME_UNIT_TIME_PENALTIES &&
       (new.event.type == LIVE_GAME_EVENT_SAVE ||
	new.event.type == LIVE_GAME_EVENT_GOAL))
    {
	player_games_goals_set(goalie, match->fix->clid, PLAYER_VALUE_SHOTS, 1);
	goalie->career[PLAYER_VALUE_SHOTS]++;

	if(new.event.type == LIVE_GAME_EVENT_SAVE)
	    player_streak_add_to_prob(goalie,
				      const_float("float_player_streak_add_goalie_save"));
    }
    
    g_array_append_val(unis, new);

    live_game_finish_unit();

    if(last_unit.time != LIVE_GAME_UNIT_TIME_PENALTIES)
	live_game_event_general(TRUE);
}

/** Find out whether the specified player already has a yellow card
    in this game.
    @param team The team index, 0 or 1.
    @param player The player index.
    @return TRUE or FALSE. */
gboolean
query_live_game_second_yellow(gint team, gint player)
{
    gint i;

    for(i=0;i<unis->len - 1;i++)
	if(uni(i).event.type == LIVE_GAME_EVENT_FOUL_YELLOW &&
	   uni(i).possession != team &&
	   uni(i).event.player2 == player)
	    return TRUE;

    return FALSE;
}
/** Find out whether there should be a half-time break 
    or extra time break or so. 
    @param minute The minute of the #LiveGameUnit we want to find
    the break event for.
    @param time The #LiveGameUnitTime of the #LiveGameUnit we want to find
    the break event for.
    @return TRUE if we have a break, FALSE otherwise. */
gboolean
query_live_game_event_is_break(gint minute, gint time)
{
    gfloat rndom;

    if(time == LIVE_GAME_UNIT_TIME_EXTRA_TIME)
	return (minute >= 120);

    if(time == LIVE_GAME_UNIT_TIME_PENALTIES)
	return query_live_game_penalties_over();

    rndom = math_rnd(0, 1);

    if(time == LIVE_GAME_UNIT_TIME_FIRST_HALF)
	return (minute >= 45 && rndom > 
		powf(const_float("float_live_game_break_base"),
		     (gfloat)(minute - 44) * 
		     const_float("float_live_game_45_break_exponent_factor")));
    else
	return (minute >= 90 && rndom > 
		powf(const_float("float_live_game_break_base"),
		     (gfloat)(minute - 89) * 
		     const_float("float_live_game_90_break_exponent_factor")));
}

/** Find out whether the final result of the penalties is
    already reached. 
    @return TRUE if the penalties are over,
    FALSE otherwise. */
gboolean
query_live_game_penalties_over(void)
{
    gint i;
    gint pen_attempted[2] = {0, 0};

    for(i=unis->len - 1; i > 0; i--)
	if(uni(i).time == LIVE_GAME_UNIT_TIME_PENALTIES)
	{
	    if(uni(i).event.type == LIVE_GAME_EVENT_PENALTY)
		pen_attempted[uni(i).possession]++;
	}
	else
	    break;

    if(pen_attempted[0] + pen_attempted[1] >= 10)
	return (match->fix->result[0][2] != match->fix->result[1][2] &&
		pen_attempted[0] == pen_attempted[1]);

    return
	(match->fix->result[0][2] - match->fix->result[1][2] > 5 - pen_attempted[1] ||
	 match->fix->result[1][2] - match->fix->result[0][2] > 5 - pen_attempted[0]);
}

/** Return a #LiveGameUnitTime depending on the time
    of the last unit. 
    @return A new #LiveGameUnitTime. */
gint
live_game_get_break(void)
{
    gint type;
    
    if(last_unit.time == LIVE_GAME_UNIT_TIME_FIRST_HALF)
	type = LIVE_GAME_EVENT_HALF_TIME;
    else if(last_unit.time == LIVE_GAME_UNIT_TIME_SECOND_HALF)
    {
	if(query_fixture_is_draw(match->fix))
	    type = LIVE_GAME_EVENT_EXTRA_TIME;
	else
	    type = LIVE_GAME_EVENT_END_MATCH;
    }
    else if(last_unit.time == LIVE_GAME_UNIT_TIME_EXTRA_TIME)
    {
	if(query_fixture_is_draw(match->fix))
	    type = LIVE_GAME_EVENT_PENALTIES;
	else
	    type = LIVE_GAME_EVENT_END_MATCH;
    }
    else
	type = LIVE_GAME_EVENT_END_MATCH;

    return type;
}

/** Get the time for the unit depending of time and
    event of the last one. 
    @param unit The unit before the one we create. 
    @return A #LiveGameUnitTime */
gint
live_game_get_time(const LiveGameUnit *unit)
{
    gint time;

    if(unit->event.type == LIVE_GAME_EVENT_HALF_TIME)
	time = LIVE_GAME_UNIT_TIME_SECOND_HALF;
    else if(unit->event.type == LIVE_GAME_EVENT_EXTRA_TIME)
	time = LIVE_GAME_UNIT_TIME_EXTRA_TIME;
    else if(unit->event.type == LIVE_GAME_EVENT_PENALTIES)
	time = LIVE_GAME_UNIT_TIME_PENALTIES;
    else
	time = unit->time;

    return time;
}

/** Return the minute for the next game unit.
    @return A new minute for a LiveGameUnit. */
gint
live_game_get_minute(void)
{
    gint i;
    
    if(last_unit.event.type == LIVE_GAME_EVENT_HALF_TIME)
	return 46;
    else if(last_unit.event.type == LIVE_GAME_EVENT_EXTRA_TIME)
	return 91;
    else if(last_unit.event.type == LIVE_GAME_EVENT_PENALTIES ||
	    last_unit.time == LIVE_GAME_UNIT_TIME_PENALTIES)
	return 120;

    for(i=unis->len - 1; i>=0; i--)
	if(uni(i).minute != -1)
	    return uni(i).minute + 1;

    return -1;
}

/** Return the minutes remaining in the match. */
gint
live_game_get_minutes_remaining(const LiveGameUnit *unit)
{
    gint current_min = live_game_unit_get_minute(unit);
    gint return_value = -1;
    
    if(unit->time == LIVE_GAME_UNIT_TIME_EXTRA_TIME)
	return_value = 120 - current_min;
    else if(unit->time == LIVE_GAME_UNIT_TIME_SECOND_HALF)
	return_value = 90 - current_min;
    else if(unit->time == LIVE_GAME_UNIT_TIME_FIRST_HALF)
	return_value = 45 - current_min;	    

    return return_value;
}

/** Return the minute of the unit (ie. look up the last unit
    with a 'normal' minute value if minute = -1).
    @param unit The unit we examine.
    @return A minute between 1 and 120. */
gint
live_game_unit_get_minute(const LiveGameUnit *unit)
{
    gint i, j;

    for(i=unis->len - 1; i >= 0; i--)
	if(&uni(i) == unit)
	    break;

    if(i == -1)
	main_exit_program(EXIT_INT_NOT_FOUND,
			  "live_game_unit_get_minute: reached end of units array.");
    else
	for(j=i;j>=0;j--)
	    if(uni(j).minute != -1)
		return uni(j).minute;

    return -1;
}

/** Return the unit before or after the specified one.
    @param unit The unit specified.
    @param gap How many units to skip. */
LiveGameUnit*
live_game_unit_before(const LiveGameUnit* unit, gint gap)
{
    gint i;

    if(gap > 0)
    {
	for(i=unis->len - 1;i>=0;i--)
	    if(&uni(i) == unit)
	    {
		if(i - gap > 0)
		    return &uni(i - gap);
		else
		    g_warning("live_game_unit_before: no unit found for gap %d\n", gap);
	    }
    }
    else
    {
	for(i=unis->len - 1;i>=0;i--)
	    if(&uni(i) == unit)
	    {
		if(i + gap < unis->len - 1)
		    return &uni(i + gap);
		else
		    g_warning("live_game_unit_before: no unit found for gap %d\n", gap);
	    }
    }

    main_exit_program(EXIT_POINTER_NOT_FOUND, NULL);

    return NULL;
}

/** Calculate which area the ball is going to be in in
    the next unit.
    @param unit The previous unit.
    @return An area, defend, midfield or attack. */
gint
live_game_get_area(const LiveGameUnit *unit)
{
    gint new_area = unit->area;
    gfloat rndom = math_rnd(0, 1);
    gfloat probs[4] =
	{const_float("float_live_game_area_def_mid") *
	 live_game_pit_teams(unit, const_float("float_live_game_area_def_mid_team_exponent")),
	 const_float("float_live_game_area_mid_att") *
	 live_game_pit_teams(unit, const_float("float_live_game_area_mid_team_exponent")),
	 const_float("float_live_game_area_mid_def") /
	 live_game_pit_teams(unit, const_float("float_live_game_area_mid_team_exponent")),
	 const_float("float_live_game_area_att_mid") /
	 live_game_pit_teams(unit, const_float("float_live_game_area_att_mid_team_exponent"))};

    if(unit->area == LIVE_GAME_UNIT_AREA_DEFEND && rndom < probs[0])
	new_area = LIVE_GAME_UNIT_AREA_MIDFIELD;
    else if(unit->area == LIVE_GAME_UNIT_AREA_MIDFIELD)
    {
	if(rndom < probs[1])
	    new_area = LIVE_GAME_UNIT_AREA_ATTACK;
	else if(rndom < probs[1] + probs[2])
	    new_area = LIVE_GAME_UNIT_AREA_DEFEND;
    }
    else
	if(rndom < probs[3])
	    new_area = LIVE_GAME_UNIT_AREA_MIDFIELD;

    return new_area;
}

/** Return the team values factor weighted with the given exponent
    and depending on the pitch area.
    @param unit The unit we calculate the value for.
    @param exponent The weighting exponent. */
gfloat
live_game_pit_teams(const LiveGameUnit *unit, gfloat exponent)
{
    gfloat factor;

    if(unit->area == LIVE_GAME_UNIT_AREA_DEFEND)
	factor = powf((match->team_values[unit->possession][GAME_TEAM_VALUE_DEFEND] * tms[unit->possession]->luck) /
		      (match->team_values[!unit->possession][GAME_TEAM_VALUE_ATTACK] * tms[!unit->possession]->luck),
		      exponent);
    else if(unit->area == LIVE_GAME_UNIT_AREA_MIDFIELD)
	factor = 
	    powf((match->team_values[unit->possession][GAME_TEAM_VALUE_MIDFIELD] * tms[unit->possession]->luck) /
		 (match->team_values[!unit->possession][GAME_TEAM_VALUE_MIDFIELD] * tms[!unit->possession]->luck),
		  exponent);
    else
	factor = powf((match->team_values[unit->possession][GAME_TEAM_VALUE_ATTACK] * tms[unit->possession]->luck) /
		      (match->team_values[!unit->possession][GAME_TEAM_VALUE_DEFEND] * tms[!unit->possession]->luck),
		      exponent);

    return factor;
}

/** Some polishing of the latest unit. Write commentary etc. */
void
live_game_finish_unit(void)
{
    LiveGameUnit *unit = &last_unit;

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("\t\tlive_game_finish_unit\n");

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("OOOO1 idx %d min %d type %d poss %d team %d pl %d %d\n", unis->len - 1,
	       unit->minute,
	       unit->event.type, unit->possession, unit->event.team,
	       unit->event.player,
	       unit->event.player2);

    if(unit->minute != -1 && unit->time != LIVE_GAME_UNIT_TIME_PENALTIES)
    {
	if(stat2 != -1 && usr(stat2).tm->boost == 1)
	    game_boost_cost();

	game_decrease_fitness(match->fix);
	game_get_values(match->fix, match->team_values,
			match->home_advantage);
	if(stat2 != -1 &&
	   stat2 == cur_user && show &&
	   unit->minute % opt_int("int_opt_live_game_player_list_refresh") == 0)
	    treeview_show_user_player_list();
    }

    if(stat2 != -1 || stat5 < -1000)
    {
	if(unit->time != LIVE_GAME_UNIT_TIME_PENALTIES)
	{
	    game_update_stats(match, unit);
	    if(show)
		treeview_show_game_stats(GTK_TREE_VIEW(lookup_widget(window.live, "treeview_stats")),
					 match);
	}

	lg_commentary_generate(match, unit, NULL, -1);

	if(-stat5 - 1000 == unit->event.type)
	{
	    g_print("type %d com **%s**", unit->event.type, unit->event.commentary);
	    if(g_strrstr(unit->event.commentary, "[") ||
	       g_strrstr(unit->event.commentary, "]") ||
	       g_strrstr(unit->event.commentary, "<") ||
	       g_strrstr(unit->event.commentary, ">") ||
	       g_strrstr(unit->event.commentary, "=") ||
	       g_strrstr(unit->event.commentary, " G ") ||
	       g_strrstr(unit->event.commentary, " L ") ||
	       g_strrstr(unit->event.commentary, " GE ") ||
	       g_strrstr(unit->event.commentary, " LE "))
		g_print(" ERROR?\n");
	    else
		g_print("\n");
	}

	unit->event.verbosity = live_game_event_get_verbosity(unit->event.type);
    }

    if(show)
	game_gui_live_game_show_unit(unit);

    if((debug > 100 && stat2 != -1) ||
       debug > 130)
	g_print("OOOO idx %d type %d poss %d team %d pl %d %d\n", unis->len - 1,
	       unit->event.type, unit->possession, unit->event.team,
	       unit->event.player,
	       unit->event.player2);
}

/** Find a random player (influenced by fitness) who gets
    injured. */
void
live_game_injury_get_player(void)
{
    gint i, j;
    gfloat probs[22];
    gfloat rndom, fitness_factor;
    gfloat goalie_factor = 
	const_float("float_live_game_injury_goalie_factor");
    gfloat boost_factor = 
	const_float("float_player_boost_injury_effect");
    
    for(j=0;j<2;j++)
    {
	fitness_factor = (player_of_idx_team(tms[j], 0)->fitness < 0.025) ?
	    40 : 1 / player_of_idx_team(tms[j], 0)->fitness;
	probs[j * 11] = goalie_factor * fitness_factor * 
	    (player_of_idx_team(tms[j], 0)->cskill != 0) * (1 + tms[j]->boost * boost_factor);
	if(j == 1)
	    probs[11] += probs[10];
	
	for(i=1;i<11;i++)
	{
	    fitness_factor = (player_of_idx_team(tms[j], i)->fitness < 0.025) ?
		40 : 1 / ((gfloat)player_of_idx_team(tms[j], i)->fitness);
	    probs[i + j * 11] = probs[i + j * 11 - 1] + (1 - goalie_factor) * fitness_factor *
		(player_of_idx_team(tms[j], i)->cskill != 0) * (1 + tms[j]->boost * boost_factor);
	}
    }
    
    rndom = math_rnd(0, probs[21]);

    if(rndom < probs[0])
    {
	last_unit.event.player = 
	    player_of_idx_team(tm0, 0)->id;
	last_unit.event.team = 0;
    }
    else
	for(i=1;i<22;i++)
	    if(probs[i - 1] <= rndom && rndom < probs[i])
	    {
		last_unit.event.player = 
		    player_of_idx_team(tms[(i > 10)], i % 11)->id;
		last_unit.event.team = (i > 10);
	    }
}

/** Resume a live game. Show team changes. */
void
live_game_resume(void)
{
    gint i, j;
    gint subs_in[3],
	subs_out[3];

    statp = &usr(stat2).live_game;

    for(i=0;i<2;i++)
    {
	game_get_subs(i, subs_in, subs_out);
	for(j=0;j<3;j++)
	{
	    if(subs_in[j] != -1)
		live_game_event_substitution(i, subs_in[j], subs_out[j]);
	}

	if(tms[i]->structure != usr(stat2).live_game.team_state[i].structure)
	    live_game_event_team_change(i, LIVE_GAME_EVENT_STRUCTURE_CHANGE);

	if(tms[i]->style != usr(stat2).live_game.team_state[i].style)
	    live_game_event_team_change(i, LIVE_GAME_EVENT_STYLE_CHANGE_ALL_OUT_DEFEND +
					tms[i]->style + 2);

	if(tms[i]->boost != usr(stat2).live_game.team_state[i].boost)
	    live_game_event_team_change(i, LIVE_GAME_EVENT_BOOST_CHANGE_ANTI +
					tms[i]->boost + 1);
    }

    live_game_calculate_fixture(usr(stat2).live_game.fix);
}


/** Reset the live game variable before we begin a new live game.
    @param live_game The pointer to the live game.
    @param fix The fixture we'll show.
    @param free Whether or not to free the variable before resetting. */
void
live_game_reset(LiveGame *live_game, Fixture *fix, gboolean free_variable)
{
    gint i;

    if(free_variable)
	free_live_game(live_game);

    live_game->units = g_array_new(FALSE, FALSE, sizeof(LiveGameUnit));
    live_game->action_ids[0] = g_array_new(FALSE, FALSE, sizeof(gint));
    live_game->action_ids[1] = g_array_new(FALSE, FALSE, sizeof(gint));
    
    for(i=0;i<LIVE_GAME_STAT_ARRAY_END;i++)
    {
	live_game->stats.players[0][i] = g_ptr_array_new();
	live_game->stats.players[1][i] = g_ptr_array_new();
    }

    for(i=0;i<LIVE_GAME_STAT_VALUE_END;i++)
	live_game->stats.values[0][i] =
	    live_game->stats.values[1][i] = 0;

    live_game->fix = fix;
    if(fix != NULL)
    {
	live_game->fix_id = fix->id;
	live_game->team_names[0] = g_strdup(fix->teams[0]->name);
	live_game->team_names[1] = g_strdup(fix->teams[1]->name);
    }
    else
    {
	live_game->fix_id = -1;
	live_game->attendance = -1;
	live_game->team_names[0] = 
	    live_game->team_names[1] = NULL;
    }

    live_game->subs_left[0] = live_game->subs_left[1] = 3;
    live_game->stadium_event = -1;
    
    if(fix != NULL && fix->home_advantage)
	live_game->home_advantage = 
	    math_rnd(const_float("float_game_home_advantage_lower"),
		     const_float("float_game_home_advantage_upper"));
    else
	live_game->home_advantage = 0;
}

/** Determine verbosity level depending on the event type. */
gint
live_game_event_get_verbosity(gint event_type)
{
    gint return_value = -1;

    if(event_type == LIVE_GAME_EVENT_START_MATCH ||
       event_type == LIVE_GAME_EVENT_HALF_TIME ||
       event_type == LIVE_GAME_EVENT_EXTRA_TIME ||
       event_type == LIVE_GAME_EVENT_END_MATCH ||
       event_type == LIVE_GAME_EVENT_PENALTIES ||
       event_type == LIVE_GAME_EVENT_GOAL ||
       event_type == LIVE_GAME_EVENT_OWN_GOAL)
	return_value = 0;
    else if(event_type == LIVE_GAME_EVENT_PENALTY ||
	    event_type == LIVE_GAME_EVENT_SCORING_CHANCE ||
	    event_type == LIVE_GAME_EVENT_HEADER ||
	    event_type == LIVE_GAME_EVENT_FREE_KICK ||
	    event_type == LIVE_GAME_EVENT_POST ||
	    event_type == LIVE_GAME_EVENT_MISS ||
	    event_type == LIVE_GAME_EVENT_CROSS_BAR ||
	    event_type == LIVE_GAME_EVENT_SAVE)
	return_value = 1;
    else if(event_type == LIVE_GAME_EVENT_SEND_OFF ||
	    event_type == LIVE_GAME_EVENT_INJURY)
	return_value = 2;
    else if(event_type == LIVE_GAME_EVENT_FOUL_RED ||
	    event_type == LIVE_GAME_EVENT_FOUL_YELLOW ||
	    event_type == LIVE_GAME_EVENT_FOUL_RED_INJURY)
	return_value = 3;
    else if(event_type == LIVE_GAME_EVENT_FOUL ||
	    event_type == LIVE_GAME_EVENT_TEMP_INJURY ||
	    event_type == LIVE_GAME_EVENT_STADIUM_RIOTS ||
	    event_type == LIVE_GAME_EVENT_STADIUM_BREAKDOWN ||
	    event_type == LIVE_GAME_EVENT_STADIUM_FIRE ||
	    event_type == LIVE_GAME_EVENT_LOST_POSSESSION)
	return_value = 4;
    else if(event_type == LIVE_GAME_EVENT_SUBSTITUTION ||
	    event_type == LIVE_GAME_EVENT_STRUCTURE_CHANGE ||
	    event_type == LIVE_GAME_EVENT_STYLE_CHANGE_ALL_OUT_DEFEND ||
	    event_type == LIVE_GAME_EVENT_STYLE_CHANGE_DEFEND ||
	    event_type == LIVE_GAME_EVENT_STYLE_CHANGE_BALANCED ||
	    event_type == LIVE_GAME_EVENT_STYLE_CHANGE_ATTACK ||
	    event_type == LIVE_GAME_EVENT_STYLE_CHANGE_ALL_OUT_ATTACK ||
	    event_type == LIVE_GAME_EVENT_BOOST_CHANGE_ANTI ||
	    event_type == LIVE_GAME_EVENT_BOOST_CHANGE_OFF ||
	    event_type == LIVE_GAME_EVENT_BOOST_CHANGE_ON)
	return_value = 5;
    else if(event_type == LIVE_GAME_EVENT_GENERAL)
	return_value = 6;
    else
	main_exit_program(EXIT_INT_NOT_FOUND, 
			  "live_game_event_get_verbosity: unknown event type %d \n",
			  event_type);

    return return_value;
}

/** Write a result like '2:3 e.t.' into the buffer.
    @param swap Whether to swap the scores. */
void
live_game_unit_result_to_buf(const LiveGameUnit *unit, gchar *buf, gboolean swap)
{
    gint idx = (swap) ? 1 : 0;

    if(unit->time == LIVE_GAME_UNIT_TIME_PENALTIES)
	/* A result after penalties. */
	sprintf(buf, _("%d : %d p."), unit->result[idx],
		unit->result[!idx]);
    else if(unit->time == LIVE_GAME_UNIT_TIME_EXTRA_TIME)
	/* A result after extra time. */
	sprintf(buf, _("%d : %d e.t."), unit->result[idx],
		unit->result[!idx]);
    else
	sprintf(buf, "%d : %d", unit->result[idx],
		unit->result[!idx]);
}

/** Count the number of penalties taken during penalty shoot-out. */
gint
live_game_penalties_taken(void)
{
    gint i, penalties = 0;

    for(i=unis->len - 1;i>=0;i--)
    {
	if(uni(i).event.type == LIVE_GAME_EVENT_PENALTIES)
	    break;
	else if(uni(i).time == LIVE_GAME_UNIT_TIME_PENALTIES &&
		uni(i).event.type == LIVE_GAME_EVENT_PENALTY)
	    penalties++;
    }

    return penalties;
}
