/**
 * \file PlayerSkills.cpp
 *
 * Implementation of the PlayerSkills class
 */

#include "Player/PlayerSkills.h"
#include "Common/Main.h"
#include "Common/Tasks.h"
#include "Common/Support/Debug.h"
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <limits.h>

#define BALL_POS_TOL 5.0f
#define DRIBBLE_STOP_DIST 10.0f

#define max(x,y) ( ((x) > (y)) ? (x) : (y) )
#define min(x,y) ( ((x) > (y)) ? (y) : (x) )

static Angle CalcDir(const Vector& vector, Angle rotation)
{
	Vector tmp = vector - rotation;
	return tmp.GetAngle();
}

/**
 * Round a float to int
 * \param fX float to round
 * \returns rounded input (0.49 -> 0, 0.5 -> 1)
 * \relates PlayerSkills
 */
inline static int f2int(float fX) { return int(fX + 0.5f); }

/**
 * Solve quadratic equation
 * \param a, b, c equation coefficients
 * \param x1, x2 results
 * \returns true if x1 and x2 are valid; false if determinant < 0
 * \relates PlayerSkills
 */
inline static bool Quadrat(float a, float b, float c, float* x1, float* x2)
{
	const float d = b*b - 4*a*c;
	if(d < 0) return false; //diskriminant zaporny

	const float sq_d = sqrtf(d);

	*x1 = (-b + sq_d) / (2*a);
	*x2 = (-b - sq_d) / (2*a);
	return true;
}

///////////////////////////////////////////////////////////////////////////////
// PlayerSkills interface

int PlayerSkills::Init(int argc, char** argv, int iTask)
{
	m_pCommandGen = (CommandsInterface*) Main::GetInstance()->GetInterface(COMMANDS_TASK);
	if (m_pCommandGen == NULL) return INIT_ERR;

	return INIT_OK;
}

///////////////////////////////////////////////////////////////////////////////
// PlayerSkills construction

PlayerSkills::PlayerSkills(
	const char* name /*= "PlayerSkills"*/,
	const char* desc /*= "PlayerKernel(PlayerSkills) module"*/)
	: PlayerKernel(name, desc)
	, m_pFCDribbleNet(NULL)
{
	m_pCommandGen = NULL;
	m_bKickable = false;
	m_PlayerRole = PR_Player;
	m_iGoalieCatchBan = 0;
	FCWriteTime = 0;
	FCDiffTime = 0;

//------------------------------------------ FC Farmari -------------------------------------
/*	
	// vytvorenie siete aj s nastavenim poctu jej neuronov...	
	m_pFCDribbleNet = new FCNetwork(DRIBBLE_NET_INPUT, 30, 0.5, 0.85);
	m_pFCDribbleNet->Inicialize();
	m_pFCDribbleNet->LoadFrom("vahydrib.txt");
*/
 }

//------------------------------------------ FC Farmari -------------------------------------
//vytvorenie destruktora.....
PlayerSkills::~PlayerSkills()
{
	if (m_pFCDribbleNet)
	{
		delete m_pFCDribbleNet;
		m_pFCDribbleNet = NULL;
	}
	
}

//------------------------------------------ FC Farmari -------------------------------------


///////////////////////////////////////////////////////////////////////////////
// PlayerSkills operations

void PlayerSkills::Act()
{
	m_BallDiff = world->ball->pos - world->me->pos;
	m_fBallDistance = m_BallDiff.GetLength();
	if(m_fBallDistance < (world->me->type->player_size + world->params->server->ball_size) )
	{
		m_fBallDistance = world->me->type->player_size + world->params->server->ball_size;
	}

	if(world->state->IsTimeStoped()) 
	{
		m_bKickable = false;
		RunPlayOff();
	}
	else
	{
		m_bKickable = m_fBallDistance < world->params->server->ball_size + world->me->type->player_size + world->me->type->kickable_margin;
		RunPlayOn();
	}
 	if(m_iGoalieCatchBan > 0) m_iGoalieCatchBan--;
}

void PlayerSkills::OnInit(RealSide realSide, int uniform, PlayMode mode)
{
	PlayerKernel::OnInit(realSide, uniform, mode);
	
	m_pCommandGen->ChangeView(NARROW_VIEW);
	SetNextSynchMode(Synch_VIOnly);
}

void PlayerSkills::RunPlayOn()
{

	//	if(GetPlayerRole() == PR_Goalie)
//		DBGN("%d: BallAge: %d, valid: %s", world->state->GetSimTime(), world->ball->GetAge(), world->ball->IsValid() ? "true" : "false");
	if(world->ball->IsValid() ||
		(GetPlayerRole() == PR_Goalie && world->ball->GetAge() < 11))
	{

		switch(GetPlayerRole())
		{
		case PR_Goalie: //BRANKAR
			{
				if(CanCatchBall() || IsBallKickAble() || CanTackleBall() ||
				   world->state->GetPlayMode() == PM_GoalKick_Our)
				{
					BehaveWithBallGoalie();
				}
				else
				{
					BehaveWithoutBallGoalie();
				}
			}
			break;

		default:
			{
				if(IsBallKickAble())
				{//kopeme
					BehaveWithBall();
				}
				else //bezime
				{
					BehaveWithoutBall();
				}
			}
			break;
		}


		//poslem spravu svojim spoluhracom
		SendInfo();

		if((!world->ball->IsValid() || world->ball->GetAge() > 3) && 
			(world->ball->pos - world->me->pos).GetLength() > 4)
		{

			m_pCommandGen->ChangeView(WIDE_VIEW);
			SetNextSynchMode(Synch_Normal);

			m_pCommandGen->SenseBody();
		}
		else
		{
			ViewWidth odporucana_sirka = WidthFromDistance(m_fBallDistance);
			if(m_PlayerRole == PR_Goalie && GetNextSynchMode() == Synch_WaitForVI)
			{
				TXT(" goalie,  wait for VI");
				m_pCommandGen->ChangeView(WIDE_VIEW);
	
				//umelo volam SB
				m_pCommandGen->SenseBody();
			}
			else if(odporucana_sirka != world->me->GetViewWidth())
			{
				switch(odporucana_sirka)
				{
				case WIDE_VIEW:
					m_pCommandGen->ChangeView(WIDE_VIEW);
					SetNextSynchMode(Synch_Normal);
					break;
				case NORMAL_VIEW:
					m_pCommandGen->ChangeView(NORMAL_VIEW);
					SetNextSynchMode(Synch_Normal);
					break;
				case NARROW_VIEW:
					m_pCommandGen->ChangeView(NARROW_VIEW);
					SetNextSynchMode(Synch_VIOnly);
					break;
				}
	
				//umelo volam SB
				m_pCommandGen->SenseBody();
			}
		}
	}	
	else //hlada loptu
	{
#ifdef GOALIE
		BehaveWithoutBallKnowledgeGoalie();
#else // GOALIE
		// Scan for ball
		Scan();
#endif //GOALIE
	}
}

void PlayerSkills::RunPlayOff()
{
	PlayMode pmode = world->state->GetPlayMode();
	if(pmode == PM_BeforeKickOff || pmode == PM_AfterGoal_Our || pmode == PM_AfterGoal_Their)
	{
		Point home = GetHomePos();

		if((home - world->me->pos).GetLength() > POS_TOL)
		{
			MoveTo(home);
		}
		else
		{
			TurnTo(Point(0, 0));
		}

		AlignNeck();
    }
	else
	{
#ifndef GOALIE
		// Run from offside
		// FIXME: do not change me->pos!!!
		const float off_l = GetOffsideLine();
		
		if(world->me->pos.GetX() > off_l - 0.1f)
		{
			world->me->pos.SetX(off_l - 0.1f);
		}

		RunToPos(world->me->pos);	// pobehni von z offside
		AlignNeck();
#endif
	}
}

// GP
#ifndef GOALIE 
void PlayerSkills::RunToPos(const Point& target)
{

//  assert(m_CurrPT.player_decay > 0 && m_CurrPT.player_decay < 1);

//    Point mepos = Point(world->me->pos.GetX(),world->me->pos.GetY());
//    Point mevel = Point(world->me->vel.GetX(), world->me->vel.GetY());
         
	Point stop_pos = world->me->pos + world->me->vel / (1 - world->me->type->player_decay); //kde by hrac zastavil
	Vector diff = target - stop_pos;

	Ball fut_ball(*world->ball);
	fut_ball.Step();

	if(diff.GetLength() > POS_TOL) //sme daleko
	{
		Vector diff2 = target - world->me->pos;
		Angle uhol = CalcDir(diff2, world->me->GetAbsDir());
		
		if(uhol.GetAbsDeg() > 10) //musime sa otocit
		{
			Point future_step = world->me->pos + world->me->vel;
			Vector diff3 = target - future_step;

			float fTurnMoment = CompMoment(CalcDir(diff3, world->me->GetAbsDir()).GetDeg());
			m_pCommandGen->Turn(fTurnMoment);

            Point fut_ballpos = Point(fut_ball.pos.GetX(), fut_ball.pos.GetY());
			TurnNeckTo(fut_ballpos, world->me->pos + world->me->vel, CompTurn(fTurnMoment));
		}
		else //netreba sa tocit
		{
//			const float fPower = RecommDashPower(true);
	//Roman Kazimir zmena
			const float fPower = RecommDashPower(false);
			m_pCommandGen->Dash(fPower);
            
            Point fut_ballpos = Point(fut_ball.pos.GetX(), fut_ball.pos.GetY());
            //Vector pnextvel = PredictNextVel(fPower);
//            Point nextvel = Point(pnextvel.GetX() , pnextvel.GetY());
			TurnNeckTo(fut_ballpos, world->me->pos + PredictNextVel(fPower),0);
		}
	}
}

// GP
float PlayerSkills::GetOffsideLine() const
{
	float offside_line = 0;
	float fast = 0;
	float second = 0;
	float tmp;
	int i;

	for (i = 0; i < world->enemies.GetSize(); i++) {	//druhy obranca
		if (world->enemies[i].pos.GetX() > second) {
			second = world->enemies[i].pos.GetX();
			if (second > fast) {
				tmp = fast;
				fast = second;
				second = tmp;
			}
		}
	}

	if (second > world->ball->pos.GetX()) {
		offside_line = second;
	}
	else {
		offside_line = world->ball->pos.GetX();
	}

	return offside_line;
}

#endif

/*
	--- Schopnosti ---
*/

void PlayerSkills::TurnTo(const Point& pos)
{
	Angle dir = CalcDir(pos - world->me->pos, world->me->GetAbsDir());
	if(dir.GetAbsDeg() >= 1) m_pCommandGen->Turn(dir.GetDeg());
}

float PlayerSkills::Scan()
{

//	TXTN("RDBG: PlayerSkills::Scan()");
	float uhol = 0;
	const float TURNFACTOR = 0.875f;

	switch(world->me->GetViewWidth())
	{
	case NARROW_VIEW:
		m_pCommandGen->ChangeView(NORMAL_VIEW);
		m_pCommandGen->SenseBody();
		// fall through

	case NORMAL_VIEW:
		uhol = world->params->server->visible_angle * TURNFACTOR;
		break;

	case WIDE_VIEW:
		uhol = world->params->server->visible_angle * TURNFACTOR * 2.0f;
		break;
	}

	m_pCommandGen->Turn(CompMoment(uhol));
	SetNextSynchMode(Synch_WaitForVI);

	return uhol;
}

#define sqr(x) (x)*(x)
int GetLineCircleIntersect(Point A, Point B, Point S, float r, Point &T1)
{
	static float Ax, Ay, Bx, By, Sx, Sy;
	static float a, b, c, t;
	Ax = A.GetX(); Bx = B.GetX(); Sx = S.GetX();
	Ay = A.GetY(); By = B.GetY(); Sy = S.GetY();
	a = 2 * (sqr(Ax-Bx) + sqr(Ay-By));
	if(fabsf(a) < .0001f) return 0;

	b = -4*(sqr(Ax-Bx) + sqr(Ay-By))*(sqr(Ax)-sqr(r)-2*Ax*Sx+sqr(Sx)+sqr(Ay-Sy));
	b += 4*sqr((sqr(Ax)+sqr(Ay)+Bx*Sx-Ax*(Bx+Sx)+By*Sy-Ay*(By+Sy)));
	if(b < 0) return 0;
	b = sqrtf(b);

	c = 2*(Ax-Bx)*(Ax-Sx)+2*(Ay-By)*(Ay-Sy);

	t = (c - b) / a;
	if(t > 0)
	{
		T1.SetX(Ax + (Bx-Ax)*t);
		T1.SetY(Ay + (By-Ay)*t);
		return 1;
	}
	t = (c + b) / a;
	if(t > 0)
	{
		T1.SetX(Ax + (Bx-Ax)*t);
		T1.SetY(Ay + (By-Ay)*t);
		return 1;
	}
	return 0;
}

void PlayerSkills::DumbKick(const Point& target)
{
	SKILLS_DBGN("%i: ->dumb kicking to position", world->state->GetSimTime());
	SKILLS_DBGN("%i: ->pos (%3.2f, %3.2f)", world->state->GetSimTime(), target.GetX(), target.GetY());
	static float ekp, dir_diff, dist_ball;
	ekp = world->params->server->maxpower * world->params->server->kick_power_rate;
	dir_diff = ((world->ball->pos - world->me->pos) - world->me->GetAbsDir()).GetAngle().GetAbsDeg();
	dist_ball = (world->ball->pos - world->me->pos).GetLength() - world->params->server->ball_size - world->params->server->player_size;
	ekp *= 1.f - 0.25f * dir_diff / 180.f - 0.25f * dist_ball / world->me->type->kickable_margin;

	Point T1;
	if(GetLineCircleIntersect(world->ball->pos, target, world->ball->pos+world->ball->vel, ekp, T1) == 0)
	{
		m_pCommandGen->Kick(world->params->server->maxpower, CalcDir(target - world->ball->pos, world->me->GetAbsDir()).GetDeg());
		TurnTo(target);
	}
	else
	{
		TXTN("%i: ->dumb kicking from (%.2f, %.2f) to position (%.2f, %.2f)", world->state->GetSimTime(), world->ball->pos.GetX(), world->ball->pos.GetY(), target.GetX(), target.GetY());
		TXTN("  (%.2f, %.2f) => newpos (%.2f, %.2f)", world->ball->vel.GetX(), world->ball->vel.GetY(), 
			T1.GetX(), T1.GetY());
		m_pCommandGen->Kick(world->params->server->maxpower, CalcDir((T1 - (world->ball->pos+world->ball->vel)), world->me->GetAbsDir()).GetDeg());
		TurnTo(T1);
	}
}

void PlayerSkills::KickToGoal2()
{
	SKILLS_DBGN("%i: ->kicking to goal (2)", world->state->GetSimTime());

	Point target(52.5f, 0);
	PassBallTo(target, world->params->server->ball_speed_max);
}

void PlayerSkills::KickToGoal3()
{
	TXTN("%i: ->kicking to goal (3)", world->state->GetSimTime());

#define MAX_OBRANCOV 32
#define SEARCH_STEP 0.2f
	// Relative angles to all defenders
	Angle obrancovia[MAX_OBRANCOV];
	int obrancov = 0;
	const float halfgoal = world->params->server->goal_width / 2;
	const float sirka = halfgoal * 0.9f;

	// Default place for kicking
	float posX = 52.5f;
	float posY = 0;

	float pomY;
	int i;
	Point enemy_pos;

	// Find all defenders in my way
	for(i = 0; i < world->enemies.GetSize(); i++)
	{
		enemy_pos = world->enemies[i].pos;
		if((enemy_pos - world->me->pos).GetLength() > 3 &&
			enemy_pos.GetX() > world->me->pos.GetX() &&
			(
			(enemy_pos.GetY() <= world->me->pos.GetY() && enemy_pos.GetY() >= -halfgoal) ||
			(enemy_pos.GetY() >= world->me->pos.GetY() && enemy_pos.GetY() <= halfgoal)
			))
		{
			assert(obrancov < MAX_OBRANCOV);
			DBG("%d [%.2f,%2f] ", world->enemies[i].GetUniform(), enemy_pos.GetX(), enemy_pos.GetY());
			obrancovia[obrancov] = (enemy_pos - world->ball->pos).GetAngle();
			obrancov++;
		}
	}
	DBGN(" are defenders (%d) [%.2f,%.2f]", obrancov, world->me->pos.GetX(), world->me->pos.GetY());

	// If any defenders were found, search for an optimal kicking place
	if(obrancov > 0)
	{
		float max_vaha = -1;

		for(pomY = -sirka; pomY <= sirka; pomY += SEARCH_STEP)
		{
			float vaha = 360; //velke cislo
			Angle moj_uhol = (Point(posX,pomY) - world->ball->pos).GetAngle();

			for(i = 0; i < obrancov; i++)
			{
				const float uhol = (moj_uhol - obrancovia[i]).GetAbsDeg();
				if(uhol < vaha) vaha = uhol;
			}

			if(vaha > max_vaha)
			{
				max_vaha = vaha;
				posY = pomY;
			}
		}
	}

//	PassBallTo(Point(posX, posY), world->params->server->ball_speed_max);
	// ak je brankar mimo, nestrielat do stredu brany, ale do stvrtiny
	if(posY == 0)
	{
		posY = halfgoal / 2.f;
		if(world->me->pos.GetY() < 0) posY = -posY;
	}
	DumbKick(Point(posX, posY));
#undef SEARCH_STEP
#undef MAX_OBRANCOV
}


/// How many cycles after catching the ball do we continue simulation?
/// \author Matus Horvath
#define AFTER_CATCH_CYCLES 5

/// Compensate for the error in percepted ball position
#define POS_ERROR_DELTA 3

/// Simulate a player for one cycle. Input player speed and distance
/// run until now and dash power in next cycle. Output new speed
/// and cumulated distance. Used for catching ball.
/// \author Matus Horvath
void PlayerSkills::CatchSimRunningPlayer(float dashPower, float* speed, float* distance)
{
	float acceleration = dashPower * world->params->server->dash_power_rate;
	if(acceleration > world->params->server->player_accel_max)
		acceleration = world->params->server->player_accel_max;

	*speed += acceleration;
	if(*speed > world->params->server->player_speed_max)
		*speed = world->params->server->player_speed_max;

	*distance += *speed;
	*speed *= world->params->server->player_decay;
}

static inline bool DoLinesIntersect(Point A, Vector U, Point B, Vector V)
{
	// Do lines (A + t1 * U) and (B + t2 * V) intersect?
	float t1 = (U.GetX() * B.GetY() - U.GetX() * A.GetY() - U.GetY() * B.GetX() + U.GetY() * A.GetX()) / (U.GetY() * V.GetX() - U.GetX() * V.GetY());
	float t2 = (V.GetX() * A.GetY() - V.GetX() * B.GetY() - V.GetY() * A.GetX() + V.GetY() * B.GetX()) / (V.GetY() * U.GetX() - V.GetX() * U.GetY());

	return t1 >= 0.f && t1 <= 1.f && t2 >= 0.f && t2 <= 1.f;
}

/// \author Matus Horvath
void PlayerSkills::CatchSimBallPath(List<CatchSimData>* output)
{
	int i;
	Ball tmpBall(*world->ball);

	bool ourKickIn = false;
	if(world->state->IsOurKickIn()) ourKickIn = true;

	// Supposed player speeds
	const float myDashPower = RecommDashPower(true) * world->me->GetEffort();
	const float friendDashPower = 
		world->variables->GetFriendDashAdjust() * (world->params->server->stamina_inc_max + world->params->server->maxpower) / 2;
	const float enemyDashPower = 
		world->variables->GetEnemyDashAdjust() * world->params->server->maxpower;

	// Number of known players
	int friendCnt = world->friends.GetSize();
	int enemyCnt = world->enemies.GetSize();

	// Current distance run by all players.
	float myDist = 0;
	float* friendDists = (float*) malloc(sizeof(float) * friendCnt);
	for(i = 0; i < friendCnt; i++) friendDists[i] = 0;
	float* enemyDists = (float*) malloc(sizeof(float) * enemyCnt);
	for(i = 0; i < enemyCnt; i++) enemyDists[i] = 0;
	
	// Current speeds of all players
	float mySpd = world->me->vel.GetLength();
	float* friendSpds = (float*) malloc(sizeof(float) * friendCnt);
	for(i = 0; i < friendCnt; i++) friendSpds[i] = world->friends[i].vel.GetLength();
	float* enemySpds = (float*) malloc(sizeof(float) * enemyCnt);
	for(i = 0; i < enemyCnt; i++) enemySpds[i] = world->enemies[i].vel.GetLength();

	// For each cycle and each player, store his distance from ball.
	// For each cycle, store ball's position.
	// We do not know number of cycles ahead.
	
	// Simulate ball path. Finish after we catch the ball or when it leaves
	// the playground. When we catch ball, simulate some more cycles to see
	// if someone else catches it at the same time.
	int caught = -1;
	int cycle = 0;

	float hack_firstCycleDistNoTurn = 0.f;
	float hack_firstCycleDistWithTurn = 0.f;

	while(true)
	{
		cycle++;
		tmpBall.Step();

		// Save ball position in this cycle
		CatchSimData* data = new CatchSimData(friendCnt, enemyCnt);
		data->ballPos = tmpBall.pos;


		// For each player, compute the distance he could run in this cycle.
		// Compensate turning in first cycle, if appropriate.
		// Then compute player's distance from ball.

		// Me
		if(cycle == 0)
		{
			// Compute first cycle with and without turning
			CatchSimRunningPlayer(myDashPower, &mySpd, &hack_firstCycleDistNoTurn);
			CatchSimRunningPlayer(0, &mySpd, &hack_firstCycleDistWithTurn);
		}
		else
		{
			CatchSimRunningPlayer(myDashPower, &mySpd, &myDist);
		}

		// Where will I be, if I do not turn in the first cycle?
		Point noTurnPos = world->me->pos + PolarVector(hack_firstCycleDistNoTurn + myDist, world->me->GetAbsDir());

		float ballDiffNoTurn = (tmpBall.pos - noTurnPos).GetLength();
		if(DoLinesIntersect(world->me->pos, noTurnPos - world->me->pos, tmpBall.pos, tmpBall.vel)) ballDiffNoTurn = -ballDiffNoTurn;
		float ballDiffWithTurn = (tmpBall.pos - world->me->pos).GetLength() - myDist - hack_firstCycleDistWithTurn;

#define SOME_DELTA world->params->server->player_size + world->params->server->ball_size + world->params->server->kickable_margin
		bool dbgWhichOne;
		if(ballDiffNoTurn < SOME_DELTA)
		{
			// We do not have to turn
			dbgWhichOne = true;
			data->ballPos = noTurnPos;
			data->SetMyDif(ballDiffNoTurn);
		}
		else
		{
			// We have to turn
			dbgWhichOne = false;
			data->SetMyDif(ballDiffWithTurn);
		}

		// Check if we have caught the ball
		if(data->GetMyDif() <= 0 && caught == -1)
		{
			CATCH_DBGN("  SIMULATION: I caught the ball (%3.2f, %3.2f) after %i", data->ballPos.GetX(), data->ballPos.GetY(), cycle);
			CATCH_DBGN("  without turn: I am at (%3.2f, %3.2f), diff is %3.2f", noTurnPos.GetX(), noTurnPos.GetY(), ballDiffNoTurn);
			CATCH_DBGN("  with turn: diff is %3.2f", ballDiffWithTurn);
			CATCH_DBGN("  using: %s, cond %s: %3.2f < %3.2f", dbgWhichOne ? "no turn" : "with turn", dbgWhichOne ? "TRUE" : "FALSE", ballDiffNoTurn, SOME_DELTA);
			caught = 0;
		}


		// Teammates
		//if(friendCnt > 10) DBGN("friends %i", friendCnt);
		for(i = 0; i < friendCnt; i++)
		{
			CatchSimRunningPlayer(friendDashPower, &friendSpds[i], &friendDists[i]);
			data->SetFriendDif(
				i, (tmpBall.pos - world->friends[i].pos).GetLength() - friendDists[i]);
		}


		// Enemies
		//if(count > 11) DBGN("enemies %i", count);
		for(i = 0; i < enemyCnt; i++)
		{
			CatchSimRunningPlayer(enemyDashPower, &enemySpds[i], &enemyDists[i]);
			data->SetEnemyDif(
				i, (tmpBall.pos - world->enemies[i].pos).GetLength() - enemyDists[i]);
		}

		output->Add(data);

	
		// If the ball is out, ignore it, unless it's one of the OurKickIn modes.
		if(!ourKickIn &&
			((tmpBall.pos.GetX() < world->params->flags[LT].GetX() - POS_ERROR_DELTA) ||
			(tmpBall.pos.GetY() < world->params->flags[LT].GetY() - POS_ERROR_DELTA) ||
			(tmpBall.pos.GetX() > world->params->flags[RB].GetX() + POS_ERROR_DELTA) ||
			(tmpBall.pos.GetY() > world->params->flags[RB].GetY() + POS_ERROR_DELTA)))
		{
//			CATCH_DBGN("  ignoring ball - out");
			break;
		}
		
		// Did we already catch the ball and simulate AFTER_CATCH_CYCLES cycles more?
		if(caught >= AFTER_CATCH_CYCLES) break;
		// If we already have the ball, increase the counter. If we do not have
		// the ball, caught is -1. When we catch it, we set caught to 0.
		if(caught >= 0) caught++;
	}

	free(friendDists); free(enemyDists);
	free(friendSpds); free(enemySpds);
}

/// \author Matus Horvath
bool PlayerSkills::CatchEvaluateData(Point* point, List<CatchSimData>* input)
{
	// TODO: different kickable areas for different heteroplayers
	const float karea = 0;//world->params->server->player_size + world->params->server->ball_size + world->params->server->kickable_margin;

	// Should our side kick the ball to game?
	// If yes, ignore enemies - they cannot kick the ball
	bool ourKickIn = false;
	if(world->state->IsOurKickIn()) ourKickIn = true;
//	CATCH_DBGN("  ourKickIn = %s", ourKickIn ? "true" : "false");
	
	int friendCaught = 0, enemyCaught = 0, meCaught = 0;
	float friendBalance = 0.f, enemyBalance = 0.f, meBalance = 0.f;

	int catchTime = INT_MAX;
	int currTime = 0;


	int i;

	// Which players are already near the ball
	bool* friendGotBall = (bool*) malloc(input->GetFirst()->data->GetFriendCnt() * sizeof(bool));
	for(i = 0; i < input->GetFirst()->data->GetFriendCnt(); i++) friendGotBall[i] = false;
	bool* enemyGotBall = (bool*) malloc(input->GetFirst()->data->GetEnemyCnt() * sizeof(bool));
	for(i = 0; i < input->GetFirst()->data->GetEnemyCnt(); i++) enemyGotBall[i] = false;

	// Process simulation data
	LIST_FOR_EACH(CatchSimData, cycle, *input)
	{
		for(i = 0; i < cycle->data->GetFriendCnt(); i++)
		{
			if(!friendGotBall[i] && cycle->data->GetFriendDif(i) <= karea)
			{
				friendCaught++; friendBalance++;
				friendGotBall[i] = true;
				CATCH_DBGN("  friend %i caught at %i: caught %i balance %3.2f", world->friends[i].GetUniform(), currTime, friendCaught, friendBalance);
				
				if(catchTime == INT_MAX)
				{
					catchTime = currTime;
					CATCH_DBGN("  catchTime = %i", catchTime);
				}
			}
		}

		if(!ourKickIn) for(i = 0; i < cycle->data->GetEnemyCnt(); i++)
		{
			if(!enemyGotBall[i] && cycle->data->GetEnemyDif(i) <= karea)
			{
				enemyCaught++; enemyBalance++;
				enemyGotBall[i] = true;
				CATCH_DBGN("  enemy %i caught at %i: caught %i balance %3.2f", world->enemies[i].GetUniform(), currTime, enemyCaught, enemyBalance);

				if(catchTime == INT_MAX)
				{
					catchTime = currTime;
					CATCH_DBGN("  catchTime = %i", catchTime);
				}
			}
		}

		if(cycle->data->GetMyDif() <= karea)
		{
			*point = cycle->data->ballPos;
			meCaught++; meBalance++;
			CATCH_DBGN("  me caught at %i, BREAK", currTime);
			
			if(catchTime == INT_MAX)
			{
				catchTime = currTime;
				CATCH_DBGN("  catchTime = %i", catchTime);
			}

			break;
		}

		// Too much time passed since first player caught the ball
		if(currTime - catchTime >= AFTER_CATCH_CYCLES)
		{
			//if(meCaught == 0) *point = cycle->data->ballPos;
			CATCH_DBGN("  currTime = %i, catchTime = %i, BREAK", currTime, catchTime);
			break;
		}
		// Too many players have caught the ball
//		if(friendCaught > GetCoachAdjParams()->GetFriendMaxCatchers()) break;
//		if(enemyCaught > GetCoachAdjParams()->GetEnemyMaxCatchers()) break;

		friendBalance *= world->variables->GetFriendBallCtrlBonus();
		enemyBalance *= world->variables->GetEnemyBallCtrlBonus();
		meBalance *= world->variables->GetFriendBallCtrlBonus();
		
		currTime++;
	}

	CATCH_DBGN("  SUMMARY:");
	CATCH_DBGN("  meCaught: %i, friendCaught: %i, enemyCaught: %i", meCaught, friendCaught, enemyCaught);
	CATCH_DBGN("  meBalance: %3.2f, friendBalance: %3.2f, enemyBalance: %3.2f", meBalance, friendBalance, enemyBalance);

	int dbgt = 0;
	LIST_FOR_EACH(CatchSimData, cycle2, *input)
	{
		dbgt++;
		if(cycle2->data->GetMyDif() <= karea)
		{
			CATCH_DBGN("  just DBG: I would catch at %i", currTime);
			break;
		}
	}

	free(friendGotBall); free(enemyGotBall);

	if(meCaught == 0) return false;

	// I can quite surely catch the ball, should I?
	// Who would control the ball if I ignore it?
	float balance = friendBalance - enemyBalance;
	CATCH_DBGN("  balance: %3.2f", balance);

	if(!ourKickIn)
	{
		if(balance + meBalance < -world->variables->GetTooMuchEnemyCnt())
		{
			// Too many enemies. 
			CATCH_DBGN("  RESULT: Too many enemies");
			CATCH_DBGN("  IGNORE");
			return false;
		}
		else if(balance < world->variables->GetTooMuchFriendCnt())
		{
			// It's almost fifty-fifty.
			CATCH_DBGN("  RESULT: Fifty-fifty");
		}
		else
		{
			// Too many friends. No need to add more.
			CATCH_DBGN("  RESULT: Too many friends");
			CATCH_DBGN("  IGNORE");
			return false;
		}
	}

	// If we get there, we are catching the ball.
	CATCH_DBGN("  CATCHING at (%3.2f, %3.2f)", point->GetX(), point->GetY());
	return true;
}

/// \author Peter Trebaticky
bool PlayerSkills::ShouldKickIn()
{
	// Overloaded in PlayerTactics!
	PlayMode pm = world->state->GetPlayMode();
#ifdef GOALIE
	if(pm == PM_GoalKick_Our)
#else // GOALIE
	if(pm == PM_FreeKick_Our ||
	   pm == PM_IndFreeKick_Our ||
	   pm == PM_KickOff_Our ||
	   pm == PM_KickIn_Our ||
	   pm == PM_CornerKick_Our ||
	   pm == PM_PenaltyKick_Our
	  )
#endif // GOALIE
		return true;
	return false;
}

bool PlayerSkills::SomeoneShouldKickIn()
{
	// Is this one of our 'kick in' modes?
	PlayMode pm = world->state->GetPlayMode();
	
	if(pm == PM_GoalKick_Our ||
	   pm == PM_FreeKick_Our ||
	   pm == PM_IndFreeKick_Our ||
	   pm == PM_KickOff_Our ||
	   pm == PM_KickIn_Our ||
	   pm == PM_CornerKick_Our ||
	   pm == PM_PenaltyKick_Our)
	{
		return true;
	}
	else
	{
		return false;
	}
}

/// Find out if we should intercept the ball, if yes,
/// find out where should we run
/// \author Matus Horvath
bool PlayerSkills::CanInterceptBall(Point* point)
{
	CATCH_DBGN("---> enter CanInterceptBall at %i", world->state->GetSimTime());

	if(world->state->GetPlayMode() != PM_PlayOn)
	{
		if(ShouldKickIn()) return true;
		return false;
	}

	// Simulate ball path
	List<CatchSimData> data;
	CatchSimBallPath(&data);

	// Evaluate the results of simulation
	bool retval = CatchEvaluateData(point, &data);

	CATCH_DBGN("<--- exit CanInterceptBall");
	
	return retval;
}


/// Intercept the ball if possible. Return false in impossible.
bool PlayerSkills::InterceptBall()
{
	if(!DependentForBall()) return false;
	const float myKickArea = world->params->server->ball_size + world->me->type->player_size + world->me->type->kickable_margin;

	// Optimal point where we will catch the ball
	Point optimum;

	// Find out if we can kick ball in this cycle
	Ball tmpBall(*world->ball);
	tmpBall.Step();

	if((tmpBall.pos - world->me->pos).GetLength() > (world->me->type->player_speed_max + myKickArea))
	{
		RunToBall2();
		return true;
	}


	const float dashPower = RecommDashPower(true);
	const float maxAccel = dashPower * world->me->type->dash_power_rate * world->me->GetEffort();
	
	const Point aPos = world->me->pos + world->me->vel;
	const Point bPos = aPos + PolarVector(maxAccel, world->me->GetAbsDir());

	// Distance between a point and a line
	float dist = fabsf(
		(bPos.GetX() - aPos.GetX()) * (tmpBall.pos.GetY() - aPos.GetY()) - 
		(bPos.GetY() - aPos.GetY()) * (tmpBall.pos.GetX() - aPos.GetX())) / maxAccel;

	if(dist > myKickArea)
	{
		RunToBall2();
		return true;
	}


	// ??? ask team Roztoce...
	float T = (
		(bPos.GetX() - aPos.GetX()) * (tmpBall.pos.GetX() - aPos.GetX()) + 
		(bPos.GetY() - aPos.GetY()) * (tmpBall.pos.GetY() - aPos.GetY())
					) / (maxAccel * maxAccel);

	if(T > 1)
	{
		if((tmpBall.pos - bPos).GetLength() > myKickArea)
		{
			RunToBall2();
			return true;
		}
	}
	else if(T < 0)
	{
		if ((tmpBall.pos - aPos).GetLength() > myKickArea)
		{
			RunToBall2();
			return true;
		}
	}
	const float minDist = world->params->server->ball_size + world->me->type->player_size + 0.1f;
	if(dist <= minDist)
	{
		// Ball is too near
		float dt = sqrtf(minDist * minDist - dist * dist) / maxAccel;
		T -= dt;
	}

	if(T > 1) T = 1;
	else if(T < 0) T = 0;

	float power = T * dashPower;
	if(power > 0)
	{
		m_pCommandGen->Dash(power);
		TurnNeckTo(tmpBall.pos, world->me->pos + PredictNextVel(power), 0);
	}
	else
	{
		TurnNeckTo(tmpBall.pos, world->me->pos + world->me->vel, 0);
	}

	return true;
}

#ifdef USE_OLD_INTERCEPT

void PlayerSkills::RunToBall2()
{

	int nsteps = 0;
	int psteps;
	Angle uhol;
	Angle uholT = DegAngle(0);
	Ball tmpBall(*world->ball);
	Vector diff;

	const float dashpower = RecommDashPower(true);

	do
	{
		nsteps++;
		tmpBall.Step();

		diff = tmpBall.pos - world->me->pos;
		uhol = CalcDir(diff, world->me->GetAbsDir());
		
		if(uhol.GetAbsDeg() > 10)
		{
			diff = tmpBall.pos - (world->me->pos + world->me->vel);
			uholT = CalcDir(diff, world->me->GetAbsDir());
			psteps = 1 + PredictSteps(diff.GetLength(), dashpower);
		}
		else
		{
			psteps = PredictSteps(diff.GetLength(), dashpower, world->me->vel.GetLength());
		}

	}
	while(nsteps < psteps);

	Ball fut_ball(*world->ball);
	fut_ball.Step();

	if(uhol.GetAbsDeg() > 10)
	{
		float TurnMoment = CompMoment(uholT.GetDeg());
		m_pCommandGen->Turn(TurnMoment);
		TurnNeckTo(fut_ball.pos, world->me->pos + world->me->vel, CompTurn(TurnMoment));
	}
	else
	{
		m_pCommandGen->Dash(dashpower);
		TurnNeckTo(fut_ball.pos, world->me->pos + world->me->vel, 0);
	}
}

bool PlayerSkills::DependentForBall() const
{

	if(world->state->GetPlayMode() == PM_CornerKick_Our)
	{
		if( world->me->GetUniform() == 8 && world->ball->pos.GetY() > 0 ) return true;
		if( world->me->GetUniform() == 7 && world->ball->pos.GetY() < 0 ) return true;

		return false;
	}
	
	int ourc;
	int theirc;
	int closestFriend = NearFriend(&ourc);
	int enemy = NearEnemy(&theirc);

	if(closestFriend == -1) return true;
	else
	{
		//nas tam bude skor
		if(enemy == -1 || theirc > ourc) return false;
		else
		{
			//medzi nasim a loptou je cudzi
			if(InTheMiddle(world->enemies[enemy].pos, world->friends[closestFriend].pos, world->ball->pos))
			{
				//najblizsi cudzi nie je medzi mnou a loptou
				bool dependent = !InTheMiddle(world->enemies[enemy].pos, world->me->pos, world->ball->pos);
				
				//nikto od nas nie je medzi mnou a loptou
				for (int i = 0; i < world->friends.GetSize() && dependent; i++)
				{
					if(InTheMiddle(world->friends[i].pos, world->me->pos, world->ball->pos))
						dependent = false;
				}

				return dependent;
			}
			else return false;
		}
	}
}

int PlayerSkills::NearFriend(int* pCycles) const
{
	float psteps;
	Ball tmpBall(*world->ball);
	Vector diff;

	const float mykarea = world->me->type->player_size + world->params->server->ball_size + world->me->type->kickable_margin;
	
	float kareas[12];
	float speeds[12];
	for(unsigned k = 0; k < 12; k++)
	{
		kareas[k] = world->params->server->ball_size + world->friendTypes[k]->player_size + world->friendTypes[k]->kickable_margin;
		speeds[k] = world->friendTypes[k]->player_speed_max;
	}

	for(int ncycles = 1; ; ncycles++)
	{
		tmpBall.Step();
		diff = tmpBall.pos - world->me->pos;

		psteps = (diff.GetLength() - mykarea) / world->me->type->player_speed_max;
		if(psteps < ncycles)
		{
			*pCycles = ncycles;
			return -1;
		}

		for(int i=0;i < world->friends.GetSize();i++)
		{
			diff = tmpBall.pos - world->friends[i].pos;

			psteps = (diff.GetLength() - kareas[world->friends[i].GetUniform()])/ speeds[world->friends[i].GetUniform()];
			if(psteps < ncycles)
			{
				*pCycles = ncycles;
				return i;
			}
		}
	}
}

int PlayerSkills::NearEnemy(int* pCycles) const
{
	if(world->enemies.GetSize() == 0) return -1;

	float psteps;
	Ball tmpBall(*world->ball);
	Vector diff;
	const float karea = world->params->server->player_size + world->params->server->ball_size + world->params->server->kickable_margin;

	for(int ncycles = 1; ; ncycles++)
	{
		tmpBall.Step();

		for(int i = 0; i < world->enemies.GetSize(); i++)
		{
			diff = tmpBall.pos - world->enemies[i].pos;

			psteps = (diff.GetLength() - karea) / world->params->server->player_speed_max;
			if(psteps < ncycles)
			{
				*pCycles = ncycles;
				return i;
			}
		}
	}
}

#endif // USE_OLD_INTERCEPT

bool PlayerSkills::InTheMiddle(const Point& tested, const Point& p1, const Point& p2) const
{
	const float distP1P2 = (p1 - p2).GetLength();

	if ((tested - p1).GetLength() > distP1P2) return false;
	if ((tested - p2).GetLength() > distP1P2) return false;

	return true;
}

/**
 * Run to point backwards (useful for goalie)
 * \author Trebaticky Peter
 */
void PlayerSkills::RunToPointBackwards(const Point& target, bool hurry, bool forLife, float posTol, int angTol)
{
	assert(world->me->type->player_decay > 0 && world->me->type->player_decay < 1);

	// Where would we stop
	Point stopPos = world->me->pos + world->me->vel / (1 - world->me->type->player_decay);
	Vector diff = target - stopPos;

	Ball tmpBall(*world->ball);
	tmpBall.Step();

	if(diff.GetLength() > posTol)
	{
		// We are too far
		float angle = CalcDir(target - world->me->pos, world->me->GetAbsDir()).GetDeg();

		if(fabsf(angle) < 180 - angTol)
		{
			// Have to turn
			Point futurePos = world->me->pos + world->me->vel;

			float turnMoment = CompMoment(CalcDir(RectVector(0, 0) - (target - futurePos), world->me->GetAbsDir()).GetDeg());
			m_pCommandGen->Turn(turnMoment);

			TurnNeckTo(tmpBall.pos, world->me->pos + world->me->vel, CompTurn(turnMoment));
		}
		else
		{
			// Don't have to turn
			float power;
			if(forLife) power = 200;
			else power = RecommDashPower(hurry);
			m_pCommandGen->Dash(-power * .5f);
			TurnNeckTo(tmpBall.pos, world->me->pos + PredictNextVel(power), 0);
		}
	}
}

void PlayerSkills::RunToPoint(const Point& target, bool hurry, bool forLife, float posTol, int angTol)
{
	assert(world->me->type->player_decay > 0 && world->me->type->player_decay < 1);

	// Where would we stop
	Point stopPos = world->me->pos + world->me->vel / (1 - world->me->type->player_decay);
	Vector diff = target - stopPos;

	Ball tmpBall(*world->ball);
	tmpBall.Step();

	if(diff.GetLength() > posTol)
	{
		// We are too far
//		float angle = CalcDir(target - world->me->pos, world->me->GetAbsDir());

		Point futurePos = world->me->pos + world->me->vel;
		float turnMoment = CompMoment(CalcDir(target - futurePos, world->me->GetAbsDir()).GetDeg());

		if(fabsf(turnMoment) > angTol)
		{
			// Have to turn
			m_pCommandGen->Turn(turnMoment);

			TurnNeckTo(tmpBall.pos, world->me->pos + world->me->vel, CompTurn(turnMoment));
		}
		else
		{
			// Don't have to turn
			float power;
			if(forLife) power = 100;
			else power = RecommDashPower(hurry);

			m_pCommandGen->Dash(power);
			TurnNeckTo(tmpBall.pos, world->me->pos + PredictNextVel(power), 0);
		}
	}
}

void PlayerSkills::TurnBall()
{
	SKILLS_DBGN("%i: ->turn ball", world->state->GetSimTime());

	//ak je dostatocne blizko
	if(IsBallKickAble())
	{
		Angle target_ang = m_BallDiff.GetAngle() + DegAngle(0.4f * 180.f);
		const float controll_dist = world->me->type->player_size + world->params->server->ball_size + 0.45f * world->me->type->kickable_margin;
		Vector rel_target = PolarVector(controll_dist, target_ang);

		//miesto pozadovaneho kopnutia
		Point ciel = (world->me->pos + world->me->vel + rel_target);
		NearKick(ciel);
	}
	else //nemozeme kopnut
	{
		return;
	}
}

void PlayerSkills::PassBallTo(const Point& pos, float spd /* = 0.7f */)
{
	SKILLS_DBGN("%i: ->passing to position", world->state->GetSimTime());
	SKILLS_DBGN("%i: ->pos (%3.2f, %3.2f)", world->state->GetSimTime(), pos.GetX(), pos.GetY());

	Vector path = pos - world->ball->pos;

	float distance = path.GetLength();
	float realSpd = distance * (1 - world->params->server->ball_decay) + spd;
	
	if(realSpd > world->params->server->ball_speed_max)
	{
		realSpd = world->params->server->ball_speed_max;
	}

	Point target(world->ball->pos);
	if(distance > 0) target = world->ball->pos + path * (realSpd / distance);

	NearKick(target);
	TurnNeckTo(target, world->me->pos + world->me->vel, 0);
}

void PlayerSkills::PassBallToPlayer(const Player& player, float spd /* = 0.7f */)
{
	SKILLS_DBGN("%i: ->passing to %s player %i", world->state->GetSimTime(), player.GetSide() == OUR ? "our" : (player.GetSide() == THEIR ? ">>their<<" : ">>unknown<<"), player.GetUniform());
	SKILLS_DBGN("%i: ->pos (%3.2f, %3.2f)", world->state->GetSimTime(), player.pos.GetX(), player.pos.GetY());
	
	PassBallTo(Point(player.pos.GetX() + (world->me->pos - player.pos).GetLength() * .2f, player.pos.GetY()), spd);
}

void PlayerSkills::DribbleTo(const Point& pos)
{
	SKILLS_DBGN("%i: ->dribbling to position", world->state->GetSimTime());
	SKILLS_DBGN("%i: ->pos (%3.2f, %3.2f)", world->state->GetSimTime(), pos.GetX(), pos.GetY());

	Vector path = pos - world->ball->pos;
	float vzdial = path.GetLength();

	if(vzdial > DRIBBLE_STOP_DIST)
	{
		Point ciel = world->ball->pos + path * (DRIBBLE_STOP_DIST / vzdial);
		PassBallTo(ciel, 0);
	}
	else if(vzdial > BALL_POS_TOL)
	{
		PassBallTo(pos, 0);
	}
}

bool PlayerSkills::DribbleRun(float angle)
{
	SKILLS_DBGN("%i: ->dribble run", world->state->GetSimTime());

	const float karea = world->params->server->ball_size + world->me->type->player_size + world->me->type->kickable_margin;
	const Point ball_nextpos = world->ball->pos + world->ball->vel;

	if((ball_nextpos - world->me->pos).GetLength() >
		(world->me->type->player_speed_max + karea))
	{
		//loptu nema sancu dostihnut za ziadnu cenu
		return false;
	}

	const Point A = world->me->pos + world->me->vel;
	const float dashpower = RecommDashPower(true,true);
	const float maxacc = dashpower * world->me->type->dash_power_rate * world->me->GetEffort();
	float u = world->me->GetAbsDir().GetRad();

	const Point B = A + RectVector(maxacc * cosf(u), maxacc * sinf(u));

	//vzdialenost bodu od priamky
	float sDS =  ((B.GetX() - A.GetX())*(ball_nextpos.GetY() - A.GetY()) - (B.GetY() - A.GetY())*(ball_nextpos.GetX() - A.GetX())) / maxacc;
	float DS = fabsf( sDS );

	if(DS > (0.9f * karea))
	{
		//na loptu nedociahne, resp. riziko, ze utecie
		return false;
	}

	const float controll_dist = world->params->server->ball_size + world->me->type->player_size + 0.45f * world->me->type->kickable_margin;
	if(fabsf(sDS - controll_dist*sinf(angle*M_PI/180)) > 0.4f)
	{
		return false;
	}

	float T = ( (B.GetX() - A.GetX())*(ball_nextpos.GetX() - A.GetX()) + (B.GetY() - A.GetY())*(ball_nextpos.GetY() - A.GetY()) ) / (maxacc*maxacc);

	if(T > 1)
	{
		if( (ball_nextpos - B).GetLength() > karea)
		{
			//lopta je prilis daleko
			return false;
		}
	}
	else if(T <= 0)
	{
		//lopta je prilis daleko alebo sa absolutne neoplati pohnut
		return false;
	}

	if(DS < controll_dist ) //ak je prilis blizko
	{
		float dt = sqrtf( controll_dist*controll_dist - DS*DS )/ maxacc;
		T -= dt;
	}

	if(T > 1)
	{
		T = 1;
	}
	else if(T <= 0)
	{
		return false;
	}

	if(T > 0.5f)
	{
		float power = T * dashpower;
		m_pCommandGen->Dash(power);
		TurnNeckTo(ball_nextpos,world->me->pos + PredictNextVel(power),0);
		return true;
	}
	else //bezalo by sa prilis pomaly, neoplati sa bezat
	{
		return false;
	}
}

void PlayerSkills::DribbleTo2(const Point& pos, float angle)
{
	SKILLS_DBGN("%i: ->dribbling (2) to position", world->state->GetSimTime());
	SKILLS_DBGN("%i: ->pos (%3.2f, %3.2f) angle %3.2f", world->state->GetSimTime(), pos.GetX(), pos.GetY(), angle);

	if(angle > 90)
	{
		angle = 90;
	}
	else if(angle < -90)
	{
		angle = -90;
	}

	Point my_nextpos = world->me->pos + world->me->vel;
	Point ball_nextpos = world->ball->pos + world->ball->vel;

	float uhol = CalcDir(pos - my_nextpos, world->me->GetAbsDir()).GetDeg();

	const float dashpower = RecommDashPower(true);
	const float acc = min(world->params->server->player_accel_max, dashpower * world->me->type->dash_power_rate * world->me->GetEffort());

	const float karea = world->params->server->ball_size + world->me->type->player_size + world->me->type->kickable_margin;
	const float controll_dist = world->params->server->ball_size + world->me->type->player_size + 0.45f * world->me->type->kickable_margin;

	if(fabsf(uhol) > 10) //musim zmenit smer
	{
		if((ball_nextpos - my_nextpos).GetLength() < karea) //mozem sa otocit, lopta daleko neujde
		{
			float moment = CompMoment(uhol);
			m_pCommandGen->Turn(moment);
			TurnNeckTo(ball_nextpos,my_nextpos,CompTurn(moment));
		}
		else //musim loptu spracovat, aby neutiekla
		{
			Point my_targetpos = world->me->pos + world->me->vel * (1 + world->me->type->player_decay);
			float dir = (world->me->GetAbsDir() + DegAngle(uhol + angle)).GetRad();
			Point ciel = my_targetpos + RectVector(controll_dist*cosf(dir), controll_dist*sinf(dir));
			ball_nextpos = world->ball->pos + (ciel - world->ball->pos) / (1 + world->params->server->ball_decay);
			NearKick(ball_nextpos);
			TurnNeckTo(ball_nextpos,my_nextpos,0);
		}
	}
	else //idem s loptou rovno
	{
		if(!DribbleRun(angle)) //ak sa nemozno pohybovat
		{
			float dir = (world->me->GetAbsDir() + DegAngle(angle)).GetRad();
			uhol = world->me->GetAbsDir().GetRad();
			Point my_targetpos = world->me->pos + world->me->vel * (1 + world->me->type->player_decay) + RectVector(acc * cosf(uhol), acc * sinf(uhol));
			Point ciel = my_targetpos + RectVector(controll_dist*cosf(dir), controll_dist*sinf(dir));
			ball_nextpos = world->ball->pos + (ciel - world->ball->pos) / (1 + world->params->server->ball_decay);
			NearKick(ball_nextpos);
			TurnNeckTo(ball_nextpos,my_nextpos,0);
		}
	}
}

void PlayerSkills::TurnNeckTo(const Point& future_pos,const Point& future_mypos,float nextturn)
{
	
	//	if(GetPlayerRole() != PR_Goalie)
//	{
//	//ak nebola nova VI, krkom sa netoci
//		if(GetCurrentSynchMode() != Synch_VIOnly && m_iTimeOfLVI <= m_iTimeOfLTurnNeck) return;
//	}
	if(m_iTimeOfLTurnNeck == world->state->GetSimTime()) return;

	//kam treba krk natocit
	int rel = f2int(CalcDir(future_pos - future_mypos, world->me->GetAbsDir() + DegAngle(nextturn)).GetDeg());
	if(rel < world->params->server->minneckang)
	{
		rel = f2int(world->params->server->minneckang);
	}
	else if(rel > world->params->server->maxneckang)
	{
		rel = int(world->params->server->maxneckang);
	}

	int otoc = f2int(rel - world->me->GetNeckDir().GetDeg());
	if(otoc == 0) return; //netreba tocit

	if(otoc > world->params->server->maxneckmoment)
	{
		otoc = int(world->params->server->maxneckmoment);
	}
	else if(otoc < world->params->server->minneckmoment)
	{
		otoc = f2int(world->params->server->minneckmoment);
	}

	m_pCommandGen->TurnNeck(otoc);
	m_iTimeOfLTurnNeck = world->state->GetSimTime();
}

void PlayerSkills::AlignNeck()
{
	if(world->me->GetNeckDir().GetDeg() != 0)
	{
		m_pCommandGen->TurnNeck((int) (-world->me->GetNeckDir().GetDeg()));
		m_iTimeOfLTurnNeck = world->state->GetSimTime();
	}
}

/**
 * Process aureal information
 * \author Matus Horvath
 */
void PlayerSkills::OnHear(int time, float direction, bool ourTeam, int uniform, const char* msg)
{
	// Poznamka> Ak tu chcete nieco zmenit, mozno je jednoduchsie povedat mi to
	// a ja to nakodim... Matus

	// Ignore messages from the other team
	if(!ourTeam) return;

	// Ignore too short messages
	if(strlen(msg) != 10) return;

	// Find out information about sender
	Player player(world);
	player.SetUniform(uniform);

	// Parse his message
	// Each character is converted to number from <0, 71> (we ignore '_' and ' ')
	// The number is the split to five parts: two 3-state and three 2-state (3*3*2*2*2=72)
	// We use the three parts and the <0, 71> numbers to store data in an effective way
	// See also PlayerSkills::SendInfo()
	unsigned char parts[10][6];
	for(int i = 0; i < 10; i++)
	{
		// Convert to <0, 71>
		unsigned char num;
		if((msg[i] >= 'a') && (msg[i] <= 'z')) num = ((char) msg[i]) - 'a';
		else if((msg[i] >= 'A') && (msg[i] <= 'Z')) num = ((char) msg[i]) - 'A' + 26;
		else if((msg[i] >= '0') && (msg[i] <= '9')) num = ((char) msg[i]) - '0' + 52;
		else if(msg[i] == '+') num = 62; else if(msg[i] == '-') num = 63;
		else if(msg[i] == '*') num = 64; else if(msg[i] == '/') num = 65;
		else if(msg[i] == '(') num = 66; else if(msg[i] == ')') num = 67;
		else if(msg[i] == '.') num = 68; else if(msg[i] == '?') num = 69;
		else if(msg[i] == '<') num = 70; else if(msg[i] == '>') num = 71;
		else { WARNN("failed decoding for hear (%i->%c)", i, msg[i]); return; }

		// Split to parts
		parts[i][0] = num;
		parts[i][1] = num % 3; num /= 3;
		parts[i][2] = num % 3; num /= 3;
		parts[i][3] = num % 2; num /= 2;
		parts[i][4] = num % 2; num /= 2;
		parts[i][5] = num % 2; num /= 2;
	}

	Point* flags = world->params->flags;

/*	DBGN("---");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][0]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][1]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][2]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][3]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][4]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][5]); DBGN("");
*/

	// Parts allocation:
	// - = free, X = player x coord, Y = player y coord
	// D = player direction, S = player speed
	// 1(3) XYDS----YY
	// 2(3) XYDS----DX
	// 3(2) XYDS----DX
	// 4(2) XYDS----SX
	// 5(2) XYDS----YX
	// This has to be the same as in SendInfo().

	// X coordinate of player (uses 72,3,2,2,2 see above)
	int tmp = (((parts[0][0] * 3 + parts[9][2]) * 2 + parts[9][3]) * 2 + parts[9][4]) * 2 + parts[9][5];
//	DBGN("tmp_x %i", tmp);
	player.pos.SetX(
		float(tmp) * (flags[R0].GetX() - flags[L0].GetX()) / 
		float(72 * 3 * 2 * 2 * 2) + flags[L0].GetX());

	// Y coordinate of player (uses 72,3,3,2 see above)
	tmp = ((parts[1][0] * 3 + parts[9][1]) * 3 + parts[8][1]) * 2 + parts[8][5];
//	DBGN("tmp_y %i", tmp);
	player.pos.SetY( 
		float(tmp) * (flags[B0].GetY() - flags[T0].GetY()) / 
		float(72 * 3 * 3 * 2) + flags[T0].GetY());

	// Direction of player (uses 72,3,2 see above)
	tmp = (parts[2][0] * 3 + parts[8][2]) * 2 + parts[8][3];
	float dir = float(tmp) * 2.0f * M_PI / float(72 * 3 * 2);
//	DBGN("tmp_dir %i ang %0.3f", tmp, dir);

	// Speed of player (uses 72,2 see above)
	tmp = parts[3][0] * 2 + parts[8][4];
//	DBGN("tmp_spd %i", tmp);
	float spd = 
		float(tmp) * (world->params->server->player_speed_max + world->params->player->player_speed_max_delta_max) / 
		float(72 * 2);

	player.vel.SetX(float(spd * cos(dir)));
	player.vel.SetY(float(spd * sin(dir)));
	player.SetAge(MAX_PLAYER_AGE - 10);

	// Store information about sender
	int index = world->FindFriendIndex(player.GetUniform());
	if(index == -1) world->friends.Add(player);
	else world->friends[index] = player;

	SHOUT_DBGN("SHT %i: RECV INFO: %i: %0.2f, %0.2f, %0.2f, %0.2f", world->state->GetSimTime(), player.GetUniform(), player.pos.GetX(), player.pos.GetY(), player.vel.GetX(), player.vel.GetY());
	if(index == -1) SHOUT_DBGN("SHT %i: Hmmm, new player", world->state->GetSimTime());

//	&other_ball.pos.GetX(), &other_ball.pos.GetY(), &other_ball.vel.GetX(), &other_ball.vel.GetY(), &other_ball.age,

/*	if(ball.valid) //viem o lopte, takze mozem upravit poziciu
	{
		float mydist = (world->ball->pos - world->me->pos).GetLength();
		float hisdist = (other_ball.pos - other_player.pos).GetLength();

		if(hisdist < mydist && other_ball.age < ball.age) //podmienka aktualizacie
		{
			ball = other_ball;
			ball.valid = true;
		}
	}*/
}

/**
 * Should I send aureal information?
 * \author Matus Horvath
 */
bool PlayerSkills::ShouldSendInfo()
{

	if(world->ball->pos.GetX() < 0.f)
	{
		// Ball is on our half. Players 2-11 speak every 20 cycles
		int time = world->state->GetSimTime() % 20;
		
		if(time == (world->me->GetUniform() - 2) * 2) return true;
		else return false;
	}
	else
	{
		// Ball is on their half. Players on their half and on the
		// opposite side to the ball shout every two cycles. Look at those passes! :)
		if(!world->ball->IsValid())
			{ SHOUT_DBGN("SHT %i: invalid ball", world->state->GetSimTime()); return false; }

		if(world->me->pos.GetX() < 0.f)
			{ SHOUT_DBGN("SHT %i: in defense", world->state->GetSimTime()); return false; }

		if(world->me->pos.GetY() * world->ball->pos.GetY() > 0.f)
			{ SHOUT_DBGN("SHT %i: same side", world->state->GetSimTime()); return false; }

		static int lastTime = -1000;
		if(world->state->GetSimTime() - lastTime < 2)
			{ SHOUT_DBGN("SHT %i: too often", world->state->GetSimTime()); return false; }

		SHOUT_DBGN("SHT %i: shouting", world->state->GetSimTime());
		return true;
	}
}

/**
 * Send aureal information
 * \author Matus Horvath
 */
void PlayerSkills::SendInfo()
{
	// Poznamka> Ak tu chcete nieco zmenit, mozno je jednoduchsie povedat mi to
	// a ja to nakodim... Matus

	// Should I speak?
	if(!ShouldSendInfo()) return;
	// Create the message
	// Store data to 3-state and 2-state variables
	// Combine the variables (2 3-state and 3 2-state) to a number from <0, 71>
	// Convert the number to valid characters (not using '_' and ' ')
	// See also PlayerSkills::OnHear()
	
	unsigned char parts[10][6];
	int i;
	for(i = 0; i < 10; i++)
	{
		parts[i][0] = 0xff;
		parts[i][1] = parts[i][2] = parts[i][3] = parts[i][4] = parts[i][5] = 0;
	}

	// Parts allocation:
	// - = free, X = player x coord, Y = player y coord
	// D = player direction, S = player speed
	// 1(3) XYDS----YY
	// 2(3) XYDS----DX
	// 3(2) XYDS----DX
	// 4(2) XYDS----SX
	// 5(2) XYDS----YX
	// This has to be the same as in OnHear().

//	DBGN("---");
//	DBGN("SEND INFO: %0.3f, %0.3f, %0.3f, %0.3f", world->me->pos.GetX(), world->me->pos.GetY(), world->me->vel.GetX(), world->me->vel.GetY());

	// X coordinate of player (uses 72,3,2,2,2 see above)
	int tmp = 
		int((world->me->pos.GetX() - world->params->flags[L0].GetX()) * float(72 * 3 * 2 * 2 * 2) / 
		(world->params->flags[R0].GetX() - world->params->flags[L0].GetX()) + 0.5);
//	DBGN("tmp_x %i", tmp);
	parts[9][5] = tmp % 2; tmp /= 2;
	parts[9][4] = tmp % 2; tmp /= 2;
	parts[9][3] = tmp % 2; tmp /= 2;
	parts[9][2] = tmp % 3; tmp /= 3;
	parts[0][0] = tmp % 72; tmp /= 72;

	// Y coordinate of player (uses 72,3,3,2 see above)
	tmp = 
		int((world->me->pos.GetY() - world->params->flags[T0].GetY()) * float(72 * 3 * 3 * 2) / 
		(world->params->flags[B0].GetY() - world->params->flags[T0].GetY()) + 0.5);
//	DBGN("tmp_y %i", tmp);
	parts[8][5] = tmp % 2; tmp /= 2;
	parts[8][1] = tmp % 3; tmp /= 3;
	parts[9][1] = tmp % 3; tmp /= 3;
	parts[1][0] = tmp % 72; tmp /= 72;

	// Direction of player (uses 72,3,2 see above)
	float angle = world->me->vel.GetAngle().GetRad();
	while(angle < 0) angle += 2.0f * M_PI; while(angle > 2.0f * M_PI) angle -= 2.0f * M_PI;
	
	tmp = int(angle * float(72 * 3 * 2) / (2.f * M_PI) + 0.5);
//	DBGN("tmp_dir %i ang %0.3f", tmp, angle);
	parts[8][3] = tmp % 2; tmp /= 2;
	parts[8][2] = tmp % 3; tmp /= 3;
	parts[2][0] = tmp % 72; tmp /= 72;

	// Speed of player (uses 72,2 see above)
	tmp = 
		int(world->me->vel.GetLength() * float(72 * 2) /
		(world->params->server->player_speed_max + world->params->player->player_speed_max_delta_max) + 0.5);
//	DBGN("tmp_spd %i", tmp);
	parts[8][4] = tmp % 2; tmp /= 2;
	parts[3][0] = tmp % 72; tmp /= 72;

	//world->ball->pos.GetX(), world->ball->pos.GetY(), world->ball->vel.GetX(), world->ball->vel.GetY(), ball.age);

	// Convert parts to numbers and encode them to characters
	char msg[11];
	memset(msg, 0, 11);
	for(i = 0; i < 10; i++)
	{
		if(parts[i][0] == 0xff)
			parts[i][0] = (((parts[i][5] * 2 + parts[i][4]) * 2 + parts[i][3]) * 3 + parts[i][2]) * 3 + parts[i][1];

		if((parts[i][0] >= 0) && (parts[i][0] <= 25)) msg[i] = 'a' + char(parts[i][0]);
		else if((parts[i][0] >= 26) && (parts[i][0] <= 51)) msg[i] = 'A' + char(parts[i][0] - 26);
		else if((parts[i][0] >= 52) && (parts[i][0] <= 61)) msg[i] = '0' + char(parts[i][0] - 52);
		else if(parts[i][0] == 62) msg[i] = '+'; else if(parts[i][0] == 63) msg[i] = '-';
		else if(parts[i][0] == 64) msg[i] = '*'; else if(parts[i][0] == 65) msg[i] = '/';
		else if(parts[i][0] == 66) msg[i] = '('; else if(parts[i][0] == 67) msg[i] = ')';
		else if(parts[i][0] == 68) msg[i] = '.'; else if(parts[i][0] == 69) msg[i] = '?';
		else if(parts[i][0] == 70) msg[i] = '<'; else if(parts[i][0] == 71) msg[i] = '>';
		else { WARNN("failed encoding for say (%i->%i)", i, (int) parts[i][0]); return; }
	}

/*	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][0]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][1]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][2]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][3]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][4]); DBGN("");
	for(i = 0; i < 10; i++) DBG("%i ", (int) parts[i][5]); DBGN("");
*/
	msg[10] = '\0';
	m_pCommandGen->Say(msg);
}

void PlayerSkills::MoveTo(const Point& pos)
{
	m_pCommandGen->Move(pos.GetX(), pos.GetY());
}

void PlayerSkills::SetView(const ViewWidth vw)
{
	m_pCommandGen->ChangeView(vw);
}

bool PlayerSkills::CanCatchBall() const
{
	//iba brankar moze chytat loptu
	if(m_PlayerRole != PR_Goalie) return false;

	//v tomto mode sa nemoze chytat
	if(world->state->GetPlayMode() != PM_PlayOn) return false;
	//lopta je daleko
	if((world->ball->pos - world->me->pos).GetLength() >= 2.0f) return false;

	//pred chvilou chytal
	if(m_iGoalieCatchBan) return false;

	//je mimo penaltoveho uzemia
	if(fabsf(world->me->pos.GetY()) >= 20.16f) return false;
	if(world->ball->pos.GetX() >= -36.f) return false;

	return true;
}

bool PlayerSkills::CanTackleBall() const
{
	// v tomto mode sa nemoze hadzat o zem
	if(world->state->GetPlayMode() != PM_PlayOn) return false;

	// lopta musi byt v obdlzniku sirky 2*tackle_width a vysky tackle_dist + tackle_back_dist
	// obdlznik je natoceny v smere natocenia hraca
	Vector player2ball = (world->ball->pos - world->me->pos);
	player2ball.SetAngle(player2ball.GetAngle() - world->me->GetAbsDir());

	// hrac teda moze urobit smykacku ak:
	// lopta je vedla neho tackle_width a (za nim tackle_back_dist alebo pred nim tackle_dist)
	if(fabsf(player2ball.GetY()) >= world->params->server->tackle_width ||
	   player2ball.GetX() >= world->params->server->tackle_dist ||
	   player2ball.GetX() <= world->params->server->tackle_back_dist)
	return false;

	/*
	DBGN("Tackle can be taken v(%.2f,%.2f) p(%.2f,%.2f;%.2f) b(%.2f,%.2f)",
		player2ball.GetX(), player2ball.GetY(),
		world->me->pos.GetX(), world->me->pos.GetY(), world->me->GetAbsDir().GetAbsDeg(),
		world->ball->pos.GetX(), world->ball->pos.GetY());
	*/
	return true;
}

void PlayerSkills::CatchBall()
{
	float uhol = CalcDir(world->ball->pos - world->me->pos, world->me->GetAbsDir()).GetDeg();
	m_pCommandGen->Catch(uhol);
	m_iGoalieCatchBan = world->params->server->catch_ban_cycle; 
}

/*
	--- Pomocne ---
*/

void PlayerSkills::NearKick(const Point& ciel)
{
	SKILLS_DBGN("%i: ->near kicking to position", world->state->GetSimTime());
	SKILLS_DBGN("%i: ->pos (%3.2f, %3.2f)", world->state->GetSimTime(), ciel.GetX(), ciel.GetY());

	//ak je dostatocne blizko
	if(m_fBallDistance < (world->me->type->player_size + world->me->type->kickable_margin + world->params->server->ball_size))
	{
		if(m_fBallDistance < (world->me->type->player_size + world->params->server->ball_size))
		{
			//nemoze byt tak blizko
			m_fBallDistance = world->me->type->player_size + world->params->server->ball_size;
		}

		float dir_diff = (m_BallDiff.GetAngle() - world->me->GetAbsDir()).GetAbsDeg();
		const float ball_power_rate = world->params->server->kick_power_rate *
			(1 - 0.25f * (dir_diff/180) - 0.25f * (m_fBallDistance - world->me->type->player_size - world->params->server->ball_size) / world->me->type->kickable_margin);

		float maxacc = world->params->server->maxpower * ball_power_rate;
		if(maxacc > world->params->server->ball_accel_max)
		{
			maxacc = world->params->server->ball_accel_max;
		}


		Point ball_zac = (world->ball->pos + world->ball->vel);
		Vector k = ciel - ball_zac;

		float req_acc = 1.14f * k.GetLength();
		float kick_dir = CalcDir(k, world->me->GetAbsDir()).GetDeg(); //povodny vypocet smeru

		if(req_acc > maxacc) //tak silno nedokaze kopnut
		{

			Vector smer = ciel - world->ball->pos;
			const float vzdial = smer.GetLength();

			float a = vzdial * vzdial;
			float b = -2 * world->ball->vel.GetX() * smer.GetX()
				-2 * world->ball->vel.GetY() * smer.GetY();
			float c = world->ball->vel.GetX() * world->ball->vel.GetX() + world->ball->vel.GetY() * world->ball->vel.GetY() - maxacc*maxacc;

			float n1,n2;
			bool success = Quadrat(a,b,c,&n1,&n2);
			if(success) //kvadraticka rovnica ma riesenie
			{
				//riesenie je pouzitelne
				if(n1 >= 0 && n1 <= 1)
				{
					Vector nkdir = smer * n1 - world->ball->vel;
					kick_dir = CalcDir(nkdir, world->me->GetAbsDir()).GetDeg();
				}
				//inak sa pouzije prednastavena hodnota kick_dir
			}
			else //pokus o spomalenie lopty
			{
				kick_dir = CalcDir(world->ball->vel * -1.0f, world->me->GetAbsDir()).GetDeg();
			}


			req_acc = maxacc;
		}

		float req_power = req_acc / ball_power_rate;
		m_pCommandGen->Kick(req_power,kick_dir);
	}
}

float PlayerSkills::CompMoment(float Moment) const
{
	float retval = Moment * (1 + world->me->vel.GetLength() * world->me->type->inertia_moment);
	if(retval > world->params->server->maxmoment)
	{
		return world->params->server->maxmoment;
	}
	else if(retval < world->params->server->minmoment)
	{
		return world->params->server->minmoment;
	}
	return retval;
}

float PlayerSkills::CompTurn(float TurnMoment) const
{
	return TurnMoment / (1 + world->me->vel.GetLength() * world->me->type->inertia_moment);
}

/// Compute how many steps would it take to get to given distance using
/// given dash power and current speed
int PlayerSkills::PredictSteps(float dist, float Power, float aktSpeed) const
{
	int Steps = 0;
	const float acc = Power * world->me->GetEffort() * world->me->type->dash_power_rate;
	while(dist > 0)
	{
		aktSpeed = min(world->me->type->player_speed_max,aktSpeed + acc);
		dist -= aktSpeed;
		aktSpeed *= world->me->type->player_decay;
		Steps++;
	}

	return Steps;
}

ViewWidth PlayerSkills::WidthFromDistance(float Distance) const
{
	if(GetPlayerRole() == PR_Goalie)
	{
		if(Distance < 20)
		{
			return NARROW_VIEW;
		}
		else if(Distance > 40.0f)
		{
			return WIDE_VIEW;
		}
		else
		{
			return NORMAL_VIEW;
		}
	}
	else 
	{
		if(Distance < 5.f /*(0.75f * world->params->server->visible_distance)*/)
		{
			return NARROW_VIEW;
		}
		else if(Distance > 20.0f)
		{
			return WIDE_VIEW;
		}
		else
		{
			return NORMAL_VIEW;
		}
	}
}

/// Compute recomended maximum dash power
float PlayerSkills::RecommDashPower(bool fast, bool reallyFast) const
{
	//Roman Kazimir

	if( fast && reallyFast) 
	{
		return 
			(world->params->server->maxpower);
	}

	
	if( !fast && !reallyFast) 
	{
		return 
			(world->me->type->stamina_inc_max);
	}
	const float power_low = min((world->me->type->stamina_inc_max) * world->params->server->recover_min, world->params->server->maxpower);

	const float stamina_low = world->params->server->stamina_max * world->params->server->effort_dec_thr;
	//Roman Kazimir .. TODO .. situacia kde sa da skorovat
	if(world->me->GetStamina() <= stamina_low) return power_low; //pod 30%
//	if(world->me->GetEffort() < world->me->type->effort_max) return power_low; //je unaveny
	const float stamina_mid = world->params->server->stamina_max * world->params->server->effort_inc_thr;
	const float slow_powermax = min(world->me->type->stamina_inc_max, world->params->server->maxpower);
	
    float result;
    
	if(fast && world->state->GetPlayMode() == PM_PlayOn) //bezime rychlo
	{
		if(world->me->GetStamina() < (stamina_mid)) //pod 60%
		{

			result = slow_powermax + (world->me->GetStamina() - stamina_low) * (world->params->server->maxpower - slow_powermax)/(stamina_mid - stamina_low);
		}
		else //nad 60%
		{
			result = world->params->server->maxpower;
		}
	}
	else //bezime pomaly
	{
		if(world->me->GetStamina() < stamina_mid) //pod 60%
		{
			result = power_low;
		}
		else //nad 60%
		{
			
			if(world->me->GetStamina() < world->params->server->stamina_max) //pod 100%
			{
				result = power_low + (world->me->GetStamina() - stamina_mid) * (slow_powermax - power_low)/(world->params->server->stamina_max - stamina_mid);
			}
			else //stamina je naplno
			{
				result = slow_powermax;
			}
		}
	}

	if (reallyFast) {
        result *= 1.5f;
		if (result > 100.0f)
			result = 100.0f;
    }

	float MaxNeededDash = RecommendDashToMaxSpeed(false);
/*	DBGN("\n%d", world->state->GetSimTime());
	DBGN("\n\n\n\nRealDash %3.2f", result);
	DBGN("\n\tToMaxSpeed: %3.2f", MaxNeededDash);
*/
	if (result>MaxNeededDash)
		result = MaxNeededDash;

    return result;
}

float PlayerSkills::RecommendDashToMaxSpeed(bool wasteStamina)const
{
	//Roman Kazimir
	if(wasteStamina) return world->params->server->maxpower;

	float RealSpeedMax = CalcRealSpeedMax();
	float neededAcceleration = RealSpeedMax - world->me->vel.GetLength();
//	DBGN("Needed accel: %f", neededAcceleration);
	float power = neededAcceleration / (world->me->effort * world->params->server->dash_power_rate);
	if (power > world->params->server->maxpower)
		power = world->params->server->maxpower;
	return power;
}

float PlayerSkills::CalcRealSpeedMax()const
{
	PLAYER_TYPE* player_type = world->me->type;
	float max_accel = 100.0f * player_type->dash_power_rate * player_type->effort_max;
    float real_speed_max = max_accel / (1.0f - player_type->player_decay);
    if ( real_speed_max > player_type->player_speed_max )
    {
        real_speed_max = player_type->player_speed_max;
    }
	return real_speed_max;
}

Vector PlayerSkills::PredictNextVel(float DashPower) const
{
	if(DashPower > world->params->server->maxpower)
	{
		DashPower = world->params->server->maxpower;
	}
	else if(DashPower < world->params->server->minpower)
	{
		DashPower = world->params->server->minpower;
	}

	float Ep = DashPower * world->me->type->dash_power_rate * world->me->GetEffort();
	if(Ep > world->params->server->player_accel_max)
	{
		Ep = world->params->server->player_accel_max;
	}
	else if(Ep < -world->params->server->player_accel_max)
	{
		Ep = -world->params->server->player_accel_max;
	}

	float uhol = world->me->GetAbsDir().GetRad();
	RectVector nextVel(world->me->vel.GetX() + Ep * cosf(uhol),world->me->vel.GetY() + Ep * sinf(uhol));

	if(nextVel.GetLength() > world->me->type->player_speed_max)
	{
		nextVel *= (world->me->type->player_speed_max / nextVel.GetLength());
	}

	return nextVel;
}

void PlayerSkills::Tackle(const float power)
{
	m_pCommandGen->Tackle(power);
}





/*void PlayerSkills::RunToBall2()
{
	int nsteps = 0;
	int psteps;
	float uhol;
	float uholT = 0;
	Ball tmpBall(ball);
	Vector diff;

	const float dashpower = RecommDashPower(true);

	do
	{
		nsteps++;
		tmpBall = StepBall(tmpBall);

		diff = tmpBall.pos - world->me->pos;
		uhol = CalcDir(diff, world->me->GetAbsDir());
		
		if(fabsf(uhol) > 10)
		{
			diff = tmpBall.pos - (world->me->pos + world->me->vel);
			uholT = CalcDir(diff, world->me->GetAbsDir());
			psteps = 1 + PredictSteps(diff.GetLength(), dashpower);
		}
		else
		{
			psteps = PredictSteps(diff.GetLength(), dashpower, world->me->vel.GetLength());
		}

	} while(nsteps < psteps);

	Ball fut_ball = StepBall(ball);

	if(fabsf(uhol) > 10)
	{
		float TurnMoment = CompMoment(uholT);
		m_pCommandGen->Turn(TurnMoment);
		TurnNeckTo(fut_ball.pos,world->me->pos + world->me->vel,CompTurn(TurnMoment));
	}
	else
	{
		m_pCommandGen->Dash(dashpower);
		TurnNeckTo(fut_ball.pos,world->me->pos + world->me->vel,0);
	}
}

void PlayerSkills::RunToBall3()
{
	const Ball fut_ball = StepBall(ball);
	const float my_karea = world->params->server->ball_size + world->me->type->player_size + world->me->type->kickable_margin;

	if( (fut_ball.pos - world->me->pos).GetLength() >
		(world->me->type->player_speed_max + my_karea) )
	{
		//obycajny beh za loptou, za jeden krok loptu nedobehne
		RunToBall2();
		return;
	}

	const Point A = world->me->pos + world->me->vel;
	const float dashpower = RecommDashPower(true);

	const float maxacc = dashpower * world->me->type->dash_power_rate * world->me->GetEffort();
	float u = world->me->GetAbsDir() * M_PI/180;

	const Point B = A + RectVector(maxacc * cosf(u), maxacc * sinf(u));

	//vzdialenost bodu od priamky
	float DS = fabsf( (B.GetX() - A.GetX())*(fut_ball.pos.GetY() - A.GetY()) - (B.GetY() - A.GetY())*(fut_ball.pos.GetX() - A.GetX())) / maxacc;

	if(DS > my_karea)
	{
		//na loptu nedociahne
		RunToBall2();
		return;
	}

	float T = ( (B.GetX() - A.GetX())*(fut_ball.pos.GetX() - A.GetX()) + (B.GetY() - A.GetY())*(fut_ball.pos.GetY() - A.GetY()) ) / (maxacc*maxacc);

	if(T > 1)
	{
		if( (fut_ball.pos - B).GetLength() > my_karea)
		{
			//lopta je prilis daleko
			RunToBall2();
			return;
		}
	}
	else if(T < 0)
	{
		if ((fut_ball.pos - A).GetLength() > my_karea)
		{
			//lopta je prilis daleko
			RunToBall2();
			return;
		}
	}

	const float Dmin = world->params->server->ball_size + world->me->type->player_size + 0.1f;
	if(DS <= Dmin ) //ak je prilis blizko
	{
		float dt = sqrtf( Dmin*Dmin - DS*DS )/ maxacc;
		T -= dt;
	}

	if(T > 1)
	{
		T = 1;
	}
	else if(T < 0)
	{
		T = 0;
	}

	float power = T * dashpower;
	if (power > 0)
	{
		m_pCommandGen->Dash(power);
		TurnNeckTo(fut_ball.pos,world->me->pos + PredictNextVel(power),0);
	}
	else
	{
		TurnNeckTo(fut_ball.pos,world->me->pos + world->me->vel,0);
	}

}
*/

//*********************************************************** FC Mirio a Ajvn		

	bool	PlayerSkills::FCBelongsToArea(Point A, Point B, Point E)
	{
	
		double dist = 2;

		double AB = sqrt(pow((A.GetX()-B.GetX()), 2) + pow((A.GetY()-B.GetY()), 2));	//vzdialenost medzi A a B
		double AE = sqrt(pow((A.GetX()-E.GetX()), 2) + pow((A.GetY()-E.GetY()), 2));	//vzdialenost medzi A a E
		double BE = sqrt(pow((B.GetX()-E.GetX()), 2) + pow((B.GetY()-E.GetY()), 2));	//vzdialenost medzi A a E
		double AS = sqrt(pow(((A.GetX()-B.GetX())/2), 2) + pow(((A.GetY()-B.GetY())/2), 2));	//vzdialenost medzi A a B
		double SE = sqrt(pow(((A.GetX()-B.GetX())/2-E.GetX()), 2) + pow(((A.GetY()-B.GetY())/2-E.GetY()), 2));	//vzdialenost medzi A a E
		double AEp = ((E.GetX()-A.GetX())*(B.GetX()-A.GetX()) + (E.GetY()-A.GetY())*(B.GetY()-A.GetY()))/AB;		//vzdialenost medzi A a priemetom E do AB
		double pE = sqrt(AE*AE - AEp*AEp);
			
		return 	(SE <= AS) && (pE < dist) || (AE < dist) || (BE < dist);
	}

	void	PlayerSkills::FCGetDistances(Point A, Point B, Point E, double* result)
	{
		double AB = sqrt(pow((A.GetX()-B.GetX()), 2) + pow((A.GetY()-B.GetY()), 2));	//vzdialenost medzi A a B
		double AE = sqrt(pow((A.GetX()-E.GetX()), 2) + pow((A.GetY()-E.GetY()), 2));	//vzdialenost medzi A a E
		double AEp = ((E.GetX()-A.GetX())*(B.GetX()-A.GetX()) + (E.GetY()-A.GetY())*(B.GetY()-A.GetY()))/AB;		//vzdialenost medzi A a priemetom E do AB
		double pE = sqrt(AE*AE - AEp*AEp);

		result[0] = fabs(AB);
		result[1] = fabs(AEp);
		result[2] = fabs(pE);
	}
	
//*********************************************************** FC Mirio a Ajvn	

	// -------------------------------------------------------  FC_Farmari Ivan -------------------------------

#ifdef TRAINING

	void PlayerSkills::OnChangeMode(int time, PlayMode mode)
	{
		PlayerKernel::OnChangeMode(time, mode);



	#ifdef DRIBBLING_TRAINING
		
		if (mode == PM_BeforeKickOff) 
		{
			world->me->SetAskedForSubst(false);
			world->me->ResetRecovery();
		}

		if (mode == PM_BeforeKickOff) TXTN("%d time - mode beforekickoff",time);
		
		if (mode == PM_PlayOn)
		{
			//zaciatok trenovania - a nastavenie hraca do stredu
			FCDiffTime = 0;
			
			FCPSource.SetX(world->me->pos.GetX());
			FCPSource.SetY(world->me->pos.GetY());

			FCBSource.SetX(world->ball->pos.GetX());
			FCBSource.SetY(world->ball->pos.GetY());
		
			FCTarget.SetX(0.0);
			FCTarget.SetY(0.0);
			

			FCTime	=	time + 1;
			FCDurTime = -1;
			FCWriteTime++;
		} else if (mode == PM_TimeOver)
		{
			FCFixDribbleWeights(FCTarget);
			if (FCWriteTime == 30)
			{
				printf("Ukladam!\n");
				m_pFCDribbleNet->SaveAs("vahy.txt");
				FCWriteTime = 0;
				printf("Ulozenuo!\n");
			}
			FCDiffTime = 0;

		}
		
		printf("Uhol je %0.2f\n", world->me->GetAbsDir().GetAbsDeg());
	#endif
	
	}

#endif

void PlayerSkills::FCDribbleTo(const Point& Target)
{

	int N = 0, Index, i;
	double	**Input = NULL;
	
	// vygenerovanie vstupov
	FCGetDribbleInputs(N, Input);

	// zvolenie toho najlepsieho
	Index = FCEvaluateInputs(m_pFCDribbleNet, N, Input);
		
	// a vykonanie vybratej akcie
	FCDribblePerformActions(Input[Index]);

	for (i = 0; i < N; i++)
		delete [] Input[i];

	delete [] Input;
	
}

void PlayerSkills::FCGetDribbleInputs(int &N, double **&Input)
{

	int i, j, Ang = 15;
	N = 100;
	double	AngleStep = 3.0, PowerStep = 10.0, MinAngle, MaxAngle;

	double	MinPower = 10.0, //world->params->server->minpower, 
			MaxPower = world->params->server->maxpower/2;

	//uhol ktory zoviera hrac(s jeho aktualnym natocenim) s miesotm kde ma dodribovat
	Angle Pdir = CalcDir(FCTarget - world->me->pos, world->me->GetAbsDir());
	Angle Bdir = CalcDir(world->ball->pos - world->me->pos, world->me->GetAbsDir());

	if (m_bKickable)
	{
		MinAngle = (Pdir.GetDeg() > 0)?(Pdir.GetDeg() - Ang):(Ang - Pdir.GetDeg())*(-1);
		MaxAngle = MinAngle + 2*Ang;
	} else
	{
		MinAngle = (Bdir.GetDeg() > 0)?(Bdir.GetDeg() - Ang):(Ang - Bdir.GetDeg())*(-1);
		MaxAngle = MinAngle + 2*Ang;
	}

	int		Angles = (int)((MaxAngle - MinAngle) / AngleStep),
			Powers = (int)((MaxPower - MinPower) / PowerStep);

	//AngleStep = (MaxAngle < MinAngle)?AngleStep*(-1):AngleStep;
	
	//Angles = (int)((Pdir.GetDeg() - 45) + (Pdir.GetDeg() - 45)) / AngleStep;
	//MinAngle = Pdir.GetDeg() - 45;

	printf("Uhol <%f, %f> = %f \n", MaxAngle, MinAngle, MaxAngle - MinAngle);

	if (m_bKickable)
		N = Powers + Powers + Angles;
	else
		N = Powers + Angles;

	if (Input == NULL)
		Input = new double*[N];
	for (i = 0; i < N; i++)
	{
		Input[i] = new double[DRIBBLE_NET_INPUT];
		memset((void*)Input[i], 0, DRIBBLE_NET_INPUT*sizeof(double));
	}


	i = 0;
	//naplnanie kick-mi
	if (m_bKickable)
	{
		for (j = 0; j < Powers; j++)
			Input[i][DRIBBLE_KICK] = 1.0;
			Input[i][DRIBBLE_KICK_ANGLE] = 0;
			//Input[i][DRIBBLE_KICK_POWER] = MinPower + j*PowerStep;
			Input[i][DRIBBLE_KICK_POWER] = MinPower + ((double)rand()/RAND_MAX)*(MaxPower-MinPower);
			i++;
	}

	//a naplnennie Dash-mi
	for (j = 0; j < Powers; j++)
	{
		Input[i][DRIBBLE_DASH] = 1.0;
		Input[i][DRIBBLE_DASH_POWER] = MinPower + ((double)rand()/RAND_MAX)*(MaxPower-MinPower);
		i++;
	}

	//naplnenie turn-mi
	for (j = 0; j < Angles; j++)
	{
		Input[i][DRIBBLE_TURN] = 1.0;
		Input[i][DRIBBLE_TURN_ANGLE] = MinAngle + ((double)rand()/RAND_MAX)*(MaxAngle - MinAngle);
		i++;
	}

	Vector	BallToPlayer = world->ball->pos - world->me->pos,
			BallToTarget = world->ball->pos - FCTarget,
			TargetToPlayer = FCTarget - world->me->pos;
	
	double	BallX = BallToPlayer.GetX(), BallY = BallToPlayer.GetY(),
			TargetX = TargetToPlayer.GetX(), TargetY = TargetToPlayer.GetY(),
			dB = BallToTarget.GetLength(),
			dP = TargetToPlayer.GetLength(),
			time = TRAINING_DURATION - (world->state->GetSimTime() - FCTime);

	for (i = 0; i < N; i++)
	{
		Input[i][0] = BallToPlayer.GetLength();//BallX;
		Input[i][1] = Bdir.GetDeg();//BallY;
		Input[i][2] = TargetToPlayer.GetLength();//TargetX
		Input[i][3] = Bdir.GetDeg();//TargetY;
		Input[i][DRIBBLE_NET_INPUT - 3] = dB;
		Input[i][DRIBBLE_NET_INPUT - 2] = dP;
		Input[i][DRIBBLE_NET_INPUT - 1] = time;
	}

}


void PlayerSkills::FCFixDribbleWeights(const Point& Target)
{
	
	//upravi vahy siete v zavislosti od aktualnej polohy hraca vzhladom na ciel a cas za ktory to zvladol..
	
	//pociatocne vektory objektov vzhladom k cielu
	Vector	SourceBallPos = FCBSource - FCTarget, 
			SourceMePos = FCPSource - FCTarget;

	//koncove vektory objektov vzhladom k cielu
	Vector	TargetBallPos = world->ball->pos - FCTarget, 
			TargetMePos = world->me->pos - FCTarget;

	
	double dP, dB, dT;

	//dP = (double)SourceMePos.GetLength() / (double)TargetMePos.GetLength();
	//dP = (dP > 1.0)?1.0:dP;

	dP = 1-(((double)SourceMePos.GetLength()-TargetMePos.GetLength()) / (double)SourceMePos.GetLength());
	dP = (dP > 1.0)?1.0:dP;
	
	//dB = (double)SourceBallPos.GetLength() / (double)TargetBallPos.GetLength();
	//dB = (dB > 1.0)?1.0:dB;

	dB = 1-(((double)SourceBallPos.GetLength()-TargetBallPos.GetLength()) / (double)SourceBallPos.GetLength());
	dB = (dB > 1.0)?1.0:dB;


	int currTime = world->state->GetSimTime();
	if (FCDurTime != -1)
	{
		dT = (double)currTime / (double) FCDiffTime;
	} else
	{
		dT = (double)FCDurTime / (double) FCDiffTime;
	}


	dT = (dT > 1.0)?1.0:dT;

	double Demand = (1 - dP)*(1 - dB);//*(1 - dT);
	
	//ulozim nove vahy
	
	//m_pFCDribbleNet->BackProp(Demand, currTime - FCTime);
	m_pFCDribbleNet->BackProp(Demand, FCDiffTime);
	printf("Ohodnotenie %lf\n", Demand);

	//m_pFCDribbleNet->SaveNet(file....);
}

void PlayerSkills::FCDribblePerformActions(double *Input)
{
	//dekoduje Input a vykona elementarne akcie..
	
	printf("Time: %d, Uhol: %f ", world->state->GetSimTime(), world->me->GetAbsDir().GetDeg()); 
	// Kopnem do lopty ak {0, 0, 1}
	if (Input[DRIBBLE_KICK] > 0.5)
	{
		// kopeme do lopty
		m_pCommandGen->Kick((float)Input[DRIBBLE_KICK_POWER], (float)Input[DRIBBLE_KICK_ANGLE]);
		printf("Kopem s uhol %lf a silou %lf\n", Input[DRIBBLE_KICK_ANGLE], Input[DRIBBLE_KICK_POWER]);
	} else if (Input[DRIBBLE_TURN] > 0.5)
	{
		// otacame sa
		m_pCommandGen->Turn((float)Input[DRIBBLE_TURN_ANGLE]);
		printf("Otacam sa o uhol %lf\n", Input[DRIBBLE_TURN_ANGLE]);
	} else if (Input[DRIBBLE_DASH] > 0.5)
	{
		//bezime
		m_pCommandGen->Dash((float)Input[DRIBBLE_DASH_POWER]);
		printf("Bezim silou %lf\n", Input[DRIBBLE_DASH_POWER]);
	}
}

int PlayerSkills::FCEvaluateInputs(FCNetwork *net, int N, double **Inputs)
{
	int i, Index = 0, diffTime = world->state->GetSimTime() - FCTime;
	double Res, Max = 0.0;

	for (i = 0; i < N; i++)
	{
#ifdef DRIBBLING_TESTING
		net->SetInput(Inputs[i], 0);
		Res = net->GetOutput(0);
#else
		net->SetInput(Inputs[i], (FCDiffTime));
		Res = net->GetOutput(FCDiffTime);
#endif
		//printf("%lf\n", Res);
		if (Res > Max)
		{
			Max = Res;
			Index = i;
		}
	
	}

	net->SetInput(Inputs[Index], FCDiffTime);
	net->GetOutput(FCDiffTime);
	printf("\nPull je: %d\n", FCDiffTime);

	return Index;
}

// -------------------------------------------------------  FC_Farmari Ivan -------------------------------

void PlayerSkills::FCSetViewWidth(ViewWidth iWidth)
{
	m_pCommandGen->ChangeView(iWidth);
}

//Roman Kazimir
// posle coachovi spravu o tom, ze hrac potreboje striedat


void PlayerSkills::AskForSubst(int role)
{
	char msg[128];
	sprintf(msg, "Subst");
	m_pCommandGen->Say(msg);
}
