//--------------------------------------------------------------------------------------
// Copyright (C) 2008 http://shift.delblanco.es
//--------------------------------------------------------------------------------------
// SHIFT Videogame 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 3 of the License, or 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.
//--------------------------------------------------------------------------------------
// Player.cpp
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#include "Player.h"
#include "3DObjWeapon.h"
#include "3DObjElement.h"

//--------------------------------------------------------------------------------------
// Variables Globales
//--------------------------------------------------------------------------------------
extern float				   g_TimeGame;    // Tiempo de juego (Game.cpp)

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CPlayer::CPlayer()
{
    m_bInit = false;
    m_awName = NULL;
    m_pVehicle = NULL;

    m_iStateUp        = PLAYER_ACTION_END_RELEASE;
    m_iStateDown      = PLAYER_ACTION_END_RELEASE;
    m_iStateRight     = PLAYER_ACTION_END_RELEASE;
    m_iStateLeft      = PLAYER_ACTION_END_RELEASE;
    m_iStateHandBrake = PLAYER_ACTION_END_RELEASE;
    m_iStateMine      = PLAYER_ACTION_END_RELEASE;
    m_iStateRocket    = PLAYER_ACTION_END_RELEASE;

    m_bUp        = false;
    m_bDown      = false;
    m_bRight     = false;
    m_bLeft      = false;
    m_bHandBrake = false;
    m_bMine      = false;
    m_bRocket    = false;
}

//--------------------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------------------
CPlayer::~CPlayer()
{
    Release();
}

//--------------------------------------------------------------------------------------
// Inicializa el jugador
//--------------------------------------------------------------------------------------
bool CPlayer::initialize(LPCWSTR dName, LPCWSTR dPathVehicle, C3DObject *dParent)
{
    // Salimos si ya se ha inicializado
    if(m_bInit) return false;
    m_bInit = true;

    // Tomamos el nombre
    m_awName = _wcsdup(dName);

	// Tomamos el puntero de la arena
	m_pArena = static_cast<C3DObjArena*>(dParent);

    // Tomamos el puntero del vehículo
    m_pVehicle = new C3DObjVehicle;
    if(!m_pVehicle) return false;
    if(!m_pVehicle->initialize(dParent, dPathVehicle, this)) return false;

    // Tomamos la fricción original
	m_pOriFriction = m_pVehicle->getPhysics()->getLFriction();

	// Tomamos varios parámetros del jugador
	m_pHealth = m_pVehicle->getMaxHealth();
	m_pRockets = (float)((int)(m_pVehicle->getMaxCapacity() / 2.0));
	m_pMines = (float)((int)(m_pVehicle->getMaxCapacity() / 3.0));
	m_pObjectives = 0.0f;
	m_pLastHealth = -1.0f;
	m_pLastRockets = -1.0f;
	m_pLastMines = -1.0f;
	m_pLastObjectives = -1.0f;
	m_pHealthSecond = 0.0f;
    return true;
}

//--------------------------------------------------------------------------------------
// Renderiza el jugador
//--------------------------------------------------------------------------------------
void CPlayer::render()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

    // Renderizamos el vehículo
    m_pVehicle->render();
}

//--------------------------------------------------------------------------------------
// Renderiza el jugador
//--------------------------------------------------------------------------------------
void CPlayer::update()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

	// Almacenamos anteriores parámetros
	if (g_TimeGame > 0.0f)
	{
		m_pLastHealth = m_pHealth;
		m_pLastRockets = m_pRockets;
		m_pLastMines = m_pMines;
		m_pLastObjectives = m_pObjectives;
	}

    // Realizamos las órdenes del jugador, si no están ya terminadas
    if(m_bUp)                                               pressUp();
    else if(m_iStateUp != PLAYER_ACTION_END_RELEASE)        releaseUp();
    if(m_bDown)                                             pressDown();
    else if(m_iStateDown != PLAYER_ACTION_END_RELEASE)      releaseDown();
    if(m_bRight)                                            pressRight();
    else if(m_iStateRight != PLAYER_ACTION_END_RELEASE)     releaseRight();
    if(m_bLeft)                                             pressLeft();
    else if(m_iStateLeft != PLAYER_ACTION_END_RELEASE)      releaseLeft();
    if(m_bHandBrake)                                        pressHandBrake();
    else if(m_iStateHandBrake != PLAYER_ACTION_END_RELEASE) releaseHandBrake();
    if(m_bMine)                                             pressMine();
    else if(m_iStateMine != PLAYER_ACTION_END_RELEASE)      releaseMine();
    if(m_bRocket)                                           pressRocket();
    else if(m_iStateRocket != PLAYER_ACTION_END_RELEASE)    releaseRocket();

    // Actualizamos el vehículo
    m_pVehicle->update();

	// Comprobamos si hemos perdido vida
	if (m_pLastHealth > m_pHealth)
	{
		// Acumulamos la pérdida en el último segundo
		m_pHealthSecond += m_pLastHealth - m_pHealth;
		// Comprobamos que no se pierda más vida del máximo permitido por segundo
		if (m_pHealthSecond > PLAYER_MAX_SEC_HEALTH) m_pHealth = m_pLastHealth;
	}

	// Comprobamos si hemos capturado un objetivo
	if(m_pArena->getObjetive()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getObjetive()->setState(OBJELEMENT_COLLISION);
		// Aumentamos el contador de objetivos
		m_pObjectives += 1.0f;
	}

	// Comprobamos si hemos capturado un elemento vida
	if(m_pArena->getHealth()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getHealth()->setState(OBJELEMENT_COLLISION);
		// Aumentamos el contador de vida
		m_pHealth = min(m_pHealth+5.0f, m_pVehicle->getMaxHealth());
	}

	// Comprobamos si hemos capturado un elemento cohete
	if(m_pArena->getRocket()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getRocket()->setState(OBJELEMENT_COLLISION);
		// Aumentamos el contador de cohetes
		m_pRockets = min(m_pRockets+5.0f, m_pVehicle->getMaxCapacity());
	}

	// Comprobamos si hemos capturado un elemento mina
	if(m_pArena->getMine()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getMine()->setState(OBJELEMENT_COLLISION);
		// Aumentamos el contador de minas
		m_pMines = min(m_pMines+5.0f, m_pVehicle->getMaxCapacity());
	}

	// Comprobamos si hemos capturado un elemento interrogación
	if(m_pArena->getRand()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getRand()->setState(OBJELEMENT_COLLISION);
		// Seleccionamos una acción aleatoria
		int seleccionado = rand() % 6 + 1;
		switch(seleccionado)
		{
		case 1: // Vida ++ 5
			m_pHealth = min(m_pHealth+5.0f, m_pVehicle->getMaxHealth());
			break;
		case 2: // Cohete ++ 5
			m_pRockets = min(m_pRockets+5.0f, m_pVehicle->getMaxCapacity());
			break;
		case 3: // Mina ++ 5
			m_pMines = min(m_pMines+5.0f, m_pVehicle->getMaxCapacity());
			break;
		case 4: // Vida -- 2
			m_pHealth = max(m_pHealth-5.0f, 0.0f);
			break;
		case 5: // Cohete -- 2
			m_pRockets = max(m_pRockets-5.0f, 0.0f);
			break;
		case 6: // Mina -- 2
			m_pMines = max(m_pMines-5.0f, 0.0f);
			break;
		}
	}

}

//--------------------------------------------------------------------------------------
// Libera los datos del jugador
//--------------------------------------------------------------------------------------
void CPlayer::Release()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;
    m_bInit = false;

    // Liberamos estructuras internas
    SAFE_DELETE_ARRAY(m_awName);
    SAFE_DELETE(m_pVehicle);
}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la pulsación de la orden UP
//--------------------------------------------------------------------------------------
void CPlayer::pressUp()
{
    // UP se anula frente a DOWN
    if(m_bDown)
    {
        // La próxima vez será como la primera vez para UP
        releaseUp();
        m_iStateUp = PLAYER_ACTION_END_RELEASE;
        return;
    }

    // Si es la primera vez
    if(m_iStateUp != PLAYER_ACTION_PRESS)
    {
        // Indicamos la nueva aceleración
        m_pVehicle->getPhysics()->setLAcceleration(m_pVehicle->getMaxAcceleration());

        // Activamos el acelerador en el vehículo
        m_pVehicle->setAccelerate(true);

        // Ya no es la primera vez
        m_iStateUp = PLAYER_ACTION_PRESS;
    }

    // Para el resto de las veces
    // UP se reduce frente a HANDBRAKE
    if(m_bHandBrake)
        m_pVehicle->getPhysics()->setLAcceleration(m_pVehicle->getMaxAcceleration()/2.0f);

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la liberación de la orden UP
//--------------------------------------------------------------------------------------
void CPlayer::releaseUp()
{
    // Si es la primera vez
    if(m_iStateUp != PLAYER_ACTION_RELEASE)
    {
        // Reseteamos la aceleración si tampoco está la órden DOWN
        if(!m_bDown) m_pVehicle->getPhysics()->setLAcceleration(0.0f);

        // Desactivamos el acelerador en el vehículo
        m_pVehicle->setAccelerate(false);

        // La orden finalizó
        m_iStateUp = PLAYER_ACTION_END_RELEASE;
    }

    // Para el resto de las veces no se hace nada
}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la pulsación de la orden DOWN
//--------------------------------------------------------------------------------------
void CPlayer::pressDown()
{
    // Si es la primera vez
    if(m_iStateDown != PLAYER_ACTION_PRESS)
    {
        // Calculamos la nueva aceleración
        m_pVehicle->getPhysics()->setLAcceleration(-m_pVehicle->getMaxAcceleration());

        // Ya no es la primera vez
        m_iStateDown = PLAYER_ACTION_PRESS;
    }

    // Para el resto de las veces
    // DOWN se reduce frente a HANDBRAKE
    if(m_bHandBrake)
        m_pVehicle->getPhysics()->setLAcceleration(-m_pVehicle->getMaxAcceleration()/2.0f);

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la liberación de la orden DOWN
//--------------------------------------------------------------------------------------
void CPlayer::releaseDown()
{
    // Si es la primera vez
    if(m_iStateDown != PLAYER_ACTION_RELEASE)
    {
        // Reseteamos la aceleración si tampoco está la órden UP
        if(!m_bUp) m_pVehicle->getPhysics()->setLAcceleration(0.0f);

        // La orden finalizó
        m_iStateDown = PLAYER_ACTION_END_RELEASE;
    }

    // Para el resto de las veces no se hace nada
}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la pulsación de la orden RIGHT
//--------------------------------------------------------------------------------------
void CPlayer::pressRight()
{
    // RIGHT se anula frente a LEFT si ya está realizado
    if(m_iStateLeft == PLAYER_ACTION_PRESS)
    {
        // La próxima vez será como la primera vez para RIGHT
        m_iStateRight = PLAYER_ACTION_END_RELEASE;
        return;
    }

    // Si es la primera vez
    if(m_iStateRight != PLAYER_ACTION_PRESS)
    {
        // Giramos las ruedas delanteras en Y
        m_pVehicle->getFRWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 4.0f, 0.0f));
        m_pVehicle->getFLWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 4.0f, 0.0f));
        m_iStateRight = PLAYER_ACTION_PRESS;
    }

    // Para el resto de las veces
    // Comprobamos que no giren más de cierto ángulo
    D3DXVECTOR3 Vec;
    m_pVehicle->getFLWheel()->getRotation(Vec);
    if(Vec.y > m_pVehicle->getMaxTurnWheelY())
    {
        Vec.y = m_pVehicle->getMaxTurnWheelY();
        m_pVehicle->getFLWheel()->setRotation(Vec);
        m_pVehicle->getFRWheel()->getRotation(Vec);
        Vec.y = D3DX_PI + m_pVehicle->getMaxTurnWheelY();
        m_pVehicle->getFRWheel()->setRotation(Vec);
    }

    // Giramos en Y el vehículo en proporción a la velocidad
    float ElapsedTime = DXUTGetElapsedTime();
    Vec = D3DXVECTOR3(0.0f, m_pVehicle->getPhysics()->getLSpeed() * m_pVehicle->getTurn() * ElapsedTime, 0.0f);
    m_pVehicle->addRotation(Vec);

    // Activamos el giro adicional del freno de mano si está pulsado
    if(m_bHandBrake)
    {
        Vec = D3DXVECTOR3(0.0f, m_pVehicle->getPhysics()->getLSpeed() * m_pVehicle->getTurn(), 0.0f);
        m_pVehicle->getPhysics()->setASpeed(Vec);
    }

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la liberación de la orden RIGHT
//--------------------------------------------------------------------------------------
void CPlayer::releaseRight()
{
    // La liberación de RIGHT termina si está la orden LEFT
    if(m_bLeft)
    {
        m_iStateRight = PLAYER_ACTION_END_RELEASE;
        return;
    }

    // Si es la primera vez
    if(m_iStateRight != PLAYER_ACTION_RELEASE)
    {
        // Giramos las ruedas delanteras en Y para posicionarlas en el centro
        m_pVehicle->getFRWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, -2.0f, 0.0f));
        m_pVehicle->getFLWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, -2.0f, 0.0f));

        // Ya no será la primera vez
        m_iStateRight = PLAYER_ACTION_RELEASE;
    }

    // Para el resto de las veces
    // Cuando llegen a cero terminamos la órden
    D3DXVECTOR3 Vec;
    m_pVehicle->getFLWheel()->getRotation(Vec);
    if(Vec.y < 0)
    {
        Vec.y = 0;
        m_pVehicle->getFLWheel()->setRotation(Vec);
        m_pVehicle->getFLWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
        m_pVehicle->getFRWheel()->getRotation(Vec);
        Vec.y = D3DX_PI;
        m_pVehicle->getFRWheel()->setRotation(Vec);
        m_pVehicle->getFRWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 0.0f, 0.0f));

        // Terminamos la órden
        m_iStateRight = PLAYER_ACTION_END_RELEASE;
    }

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la pulsación de la orden LEFT
//--------------------------------------------------------------------------------------
void CPlayer::pressLeft()
{
    // LEFT se anula frente a RIGHT si ya está realizado
    if(m_iStateRight == PLAYER_ACTION_PRESS)
    {
        // La próxima vez será como la primera vez para LEFT
        m_iStateLeft = PLAYER_ACTION_END_RELEASE;
        return;
    }

    // Si es la primera vez
    if(m_iStateLeft != PLAYER_ACTION_PRESS)
    {
        // Giramos las ruedas delanteras en Y
        m_pVehicle->getFRWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, -4.0f, 0.0f));
        m_pVehicle->getFLWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, -4.0f, 0.0f));
        m_iStateLeft = PLAYER_ACTION_PRESS;
    }

    // Para el resto de las veces
    // Comprobamos que no giren más de cierto ángulo
    D3DXVECTOR3 Vec;
    m_pVehicle->getFLWheel()->getRotation(Vec);
    if(Vec.y < -m_pVehicle->getMaxTurnWheelY())
    {
        Vec.y = -m_pVehicle->getMaxTurnWheelY();
        m_pVehicle->getFLWheel()->setRotation(Vec);
        m_pVehicle->getFRWheel()->getRotation(Vec);
        Vec.y = D3DX_PI - m_pVehicle->getMaxTurnWheelY();
        m_pVehicle->getFRWheel()->setRotation(Vec);
    }

    // Giramos en Y el vehículo en proporción a la velocidad
    float ElapsedTime = DXUTGetElapsedTime();
    Vec = D3DXVECTOR3(0.0f, - m_pVehicle->getPhysics()->getLSpeed() * m_pVehicle->getTurn() * ElapsedTime, 0.0f);
    m_pVehicle->addRotation(Vec);

    // Activamos el giro adicional del freno de mano si está pulsado
    if(m_bHandBrake)
    {
        Vec = D3DXVECTOR3(0.0f, - m_pVehicle->getPhysics()->getLSpeed() * m_pVehicle->getTurn(), 0.0f);
        m_pVehicle->getPhysics()->setASpeed(Vec);
    }

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la liberación de la orden LEFT
//--------------------------------------------------------------------------------------
void CPlayer::releaseLeft()
{
    // La liberación de RIGHT termina si está la orden LEFT
    if(m_bRight)
    {
        m_iStateLeft = PLAYER_ACTION_END_RELEASE;
        return;
    }

    // Si es la primera vez
    if(m_iStateLeft != PLAYER_ACTION_RELEASE)
    {
        // Giramos las ruedas delanteras en Y para posicionarlas en el centro
        m_pVehicle->getFRWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 2.0f, 0.0f));
        m_pVehicle->getFLWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 2.0f, 0.0f));

        // Ya no será la primera vez
        m_iStateLeft = PLAYER_ACTION_RELEASE;
    }

    // Para el resto de las veces
    // Cuando llegen a cero terminamos la órden
    D3DXVECTOR3 Vec;
    m_pVehicle->getFLWheel()->getRotation(Vec);
    if(Vec.y > 0)
    {
        Vec.y = 0;
        m_pVehicle->getFLWheel()->setRotation(Vec);
        m_pVehicle->getFLWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
        m_pVehicle->getFRWheel()->getRotation(Vec);
        Vec.y = D3DX_PI;
        m_pVehicle->getFRWheel()->setRotation(Vec);
        m_pVehicle->getFRWheel()->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 0.0f, 0.0f));

        // Terminamos la órden
        m_iStateLeft = PLAYER_ACTION_END_RELEASE;
    }

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la pulsación de la orden HANDBRAKE
//--------------------------------------------------------------------------------------
void CPlayer::pressHandBrake()
{
    // Si es la primera vez
    if(m_iStateHandBrake != PLAYER_ACTION_PRESS)
    {
        // Activamos el freno de mano en el vehículo
        m_pVehicle->setHandBrake(true);

        // Ya no será la primera vez
        m_iStateHandBrake = PLAYER_ACTION_PRESS;
    }

    // Para el resto de las veces

	// Aumentamos la fricción proporcional a la velocidad
    m_pVehicle->getPhysics()->setLFriction(m_pOriFriction + 2.5f * fabs(m_pVehicle->getPhysics()->getLSpeed()));
}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la liberación de la orden HANDBRAKE
//--------------------------------------------------------------------------------------
void CPlayer::releaseHandBrake()
{
    // Si es la primera vez
    if(m_iStateHandBrake != PLAYER_ACTION_RELEASE)
    {
        // Desactivamos el freno de mano en el vehículo
        m_pVehicle->setHandBrake(false);

        // Restauramos la fricion
        m_pVehicle->getPhysics()->setLFriction(m_pOriFriction);

        // Ya no será la primera vez
        m_iStateHandBrake = PLAYER_ACTION_RELEASE;
    }

    // Para el resto de las veces no se hace nada
}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la pulsación de la orden MINE
//--------------------------------------------------------------------------------------
void CPlayer::pressMine()
{
    // Si es la primera vez
    if(m_iStateMine != PLAYER_ACTION_PRESS)
    {
        // Ya no será la primera vez
        m_iStateMine = PLAYER_ACTION_PRESS;

		// Si tenemos minas
		if (m_pMines > 0.0f)
		{
			// Si el arma no está bloqueada
			if (m_pVehicle->getMine()->getState() == OBJWEAPON_FREE)
			{
				// Activamos la mina
				m_pVehicle->getMine()->setEnabled(true);
				// Actualizamos el número de minas
				m_pMines--;
			}
		}
		// Si no tenemos minas
		else
		{
			// Activamos el sonido de vacío de la mina
			m_pVehicle->getMine()->stopEmpty();
			m_pVehicle->getMine()->playEmpty(false);
		}
	}

    // Para el resto de las veces no se hace nada

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la liberación de la orden MINE
//--------------------------------------------------------------------------------------
void CPlayer::releaseMine()
{
    // Si es la primera vez
    if(m_iStateMine != PLAYER_ACTION_RELEASE)
    {
        // Ya no será la primera vez
        m_iStateMine = PLAYER_ACTION_RELEASE;
    }

    // Para el resto de las veces no se hace nada

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la pulsación de la orden ROCKET
//--------------------------------------------------------------------------------------
void CPlayer::pressRocket()
{
    // Si es la primera vez
    if(m_iStateRocket != PLAYER_ACTION_PRESS)
    {
        // Ya no será la primera vez
        m_iStateRocket = PLAYER_ACTION_PRESS;

		// Si tenemos cohetes
		if (m_pRockets > 0.0f)
		{
			// Si el arma no está bloqueada
			if (m_pVehicle->getRocket()->getState() == OBJWEAPON_FREE)
			{
				// Activamos el cohete
				m_pVehicle->getRocket()->setEnabled(true);
				// Actualizamos el número de cohetes
				m_pRockets--;
			}
		}
		// Si no tenemos cohetes
		else
		{
			// Activamos el sonido de vacío del cohete
			m_pVehicle->getRocket()->stopEmpty();
			m_pVehicle->getRocket()->playEmpty(false);
		}
    }

    // Para el resto de las veces no se hace nada

}

//--------------------------------------------------------------------------------------
// Actualiza el jugador tras la liberación de la orden ROCKET
//--------------------------------------------------------------------------------------
void CPlayer::releaseRocket()
{
    // Si es la primera vez
    if(m_iStateRocket != PLAYER_ACTION_RELEASE)
    {
        // Ya no será la primera vez
        m_iStateRocket = PLAYER_ACTION_RELEASE;
    }

    // Para el resto de las veces no se hace nada

}
