/*****************************************************************************
 * $Id: game_pony.cc,v 1.87 2006/08/26 21:47:51 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Top-level of game module.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define GAME_PONY_CC 1
#include "game_pony_common.hh"
#include "game_base_config.hh"
#include "game_pony_config.hh"
#include "game_pony_listeners.hh"
#include "game_pony_gui.hh"
#include "game_base_craft.hh"
#include "game_base_combat.hh"
#include "game_pony_view.hh"
#include "game_pony_craft.hh"
#include "game_pony_buildings.hh"
#ifndef NO_PYTHON
#include "game_pony_python_ifc.hh"
#endif
#include "gfx_random.hh"

extern Runway* gRunway;

// F-14 Tomcat has multiple 3D models for its variable wing sweep.
vector<string> gModelNameF14Vec;
static const int sOtherF14Cnt = 50;		// besides player #1/#2

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  GamePony  ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Start new game.
 *****************************************************************************/
void
GamePony::Start( void )
{
	Parent::Start();

	SelfChecks();

	gGui.Init();
	gEng.SetBackgroundRGBA( RGBA(RGB_SKYBLUE, 0.0) );
	gEng.SetFogRGBA( RGBA(0.60, 0.62, 0.67, 1.0) );
	gEng.SetFogDensity( mDefaultFogDensity ); // mDefaultFogDensity set by config
	gEng.EnableFog();
	gGui.UpdateFogDensity();
	gGui.SplashOff();
	gEng.PostRedisplay();
	gEng.SetCurrent( &gEng.mEye );
	gEng.GetCurrent()->ClaimInput();

	// Add primary view.
	gEng.mView.AddViewPrimary( new ViewGamePony( gEng.mView ) );

	// Add radar views.
	gEng.mView.AddView( new ViewRadar( gEng.mView, ViewRadar::eRadar_HORIZON ) );
	gEng.mView.AddView( new ViewRadar( gEng.mView, ViewRadar::eRadar_OVERHEAD ) );

	// Place a pair of craft in center quadrant.
	Position pos1 = gRunway->GetPosition();
	Position pos2 = pos1;
	pos1.z += gRunway->Length() * 0.5 - WORLD(300.0);
	pos2.z -= gRunway->Length() * 0.5 - WORLD(300.0);
	pos1.y  = GROUND_LEVEL + WORLD(5);
	pos2.y  = GROUND_LEVEL + WORLD(5);
	gModelNameF14Vec.push_back( mModelDir + string("f-14tomcat.pal")  );
	mPlayer1Craft = new CraftF14Tomcat( gWorld.GetCenterQuadrant(), pos1, gModelNameF14Vec );
	mPlayer2Craft = new CraftF14Tomcat( gWorld.GetCenterQuadrant(), pos2, gModelNameF14Vec );
	mPlayer2Craft->Rotate( AXIS_Y, 180.0 );	// he's at other end of runway, rotate him
	mCraftDlist.Append( GET_PTR(mPlayer1Craft) );
	mCraftDlist.Append( GET_PTR(mPlayer2Craft) );
	mPlayer1Craft->Save();	// for when Dyna::Reset() is called
	mPlayer2Craft->Save();

	mPlayer1 = mPlayer1Craft;
	mPlayer2 = mPlayer2Craft;
	mPlayer	 = mPlayer1;

	// Load Dyna objects from configuration state.
	// If not loaded, LoadDyna() is a NOP.
	mEyeConfigState.LoadDyna(     gEng.mEye );
	mPlayer1ConfigState.LoadDyna( *mPlayer1Craft );
	mPlayer2ConfigState.LoadDyna( *mPlayer2Craft );

#if PUBLIC_RELEASE
	//mCmdarg.mGame = true;
#endif

#if PUBLIC_RELEASE
	*gStreamOut << "Pass -game to run a demo of the experimental dogfight AI. \n";
  //*gStreamOut << "Pass -no-fsaa if aircraft look jagged. \n";
#endif

	if ( mCmdarg.mGame || mCmdarg.mDemo )
	{
		StartTurkeyShoot( sOtherF14Cnt );
	}
	else
	{
#ifndef NO_PYTHON
		// Start Python but not the game module code.
		StartPython( false );
#endif
	}
}
void
GamePony::End( void )
{
	Parent::End();
	StartStopListeners( false );
//	DELETE_NULL( mPlayer1 );		// ~Quadrant() will delete them
//	DELETE_NULL( mPlayer2 );
	mPlayer = NULL;
	CloseConfigFile();
	gGui.Delete();
}

/*****************************************************************************
 * Self-checks.  Causes program exit if any fail.
 *****************************************************************************/
void
GamePony::SelfChecks( void )
{
	// Self-checks.
	ASSERT( TYPESIG_END < TYPESIG_MAX ); // catch if last typesig value is too big
}

/*****************************************************************************
 * Start the Python components of the game module.
 *****************************************************************************/
#ifndef NO_PYTHON
bool
GamePony::StartPython( bool startGame )
{
	// Create the Python game modules which are implemented in C++.
	// The Engine/World Python modules were created prior.
	if ( ! PythonModuleGame() )
	{
		*gStreamError << "ERROR[game]: initializing Python.\n";
		return false;
	}

	// Run the Python module game_pony.py.
	// In order for "import game_pony" to succeed, append its path to
	// Python's module search path (sys.path).
	string pycode;
	pycode += string("import sys\n");
	pycode += string("sys.path.append( '") + string(GetPythonDir()) + string("' )\n");
	pycode += string("import game_pony\n");
	if ( startGame )
	{
		// Start game module (optional).
		pycode += string("game = game_pony.GamePony()\n");
		pycode += string("game.StartTurkeyShoot()\n");
	}
	if ( ! gPython.Run( pycode ) )
	{
		*gStreamError << "ERROR[game]: failed to import/run game_pony Python module. \n";
		return false;
	}

	// Ok.
	return true;
}
#endif // NO_PYTHON

/*****************************************************************************
 * Start rudimentary turkey-shoot game.
 * Implemented in C++ and Python (see game_pony.py).
 *****************************************************************************/
void
GamePony::StartTurkeyShoot( int craftCnt )
{
CHECK_TYPESIG( &mCraftDlist, TYPESIG_LIB_DLIST );

	// Make the "killer" Tomcat indestructible as he flies too close to his victims.
	mPlayer1Craft->EnableDetector( false );

	SetPlayer( 1 );
	gEng.SetCurrent( mPlayer1Craft );
	gEng.GetCurrent()->ClaimInput();
	gEng.mEye.ToggleAnimation( eAnimate_CHASE_PLANE, eAnimate_NONE );

#ifndef NO_PYTHON
	// Start Python and the Python game module.
	StartPython( true );
#endif
}

/*****************************************************************************
 * Spawn a turket (has a Python counterpart, this C++ code is still used).
 *****************************************************************************/
void
GamePony::SpawnTurkey( CraftPtrc pursuer, int i, int craftCnt, bool moreRandom )
{
	// Spawn a turkey near the pursuer.
	Position posCraft = pursuer->GetPosition();
	const fp y = WORLD(2000.0);
	if ( moreRandom )
	{
		posCraft.x += WORLD(700.0) + WORLD(random_f(3000.0)+200.0);
		posCraft.y  = y + WORLD(random_f(300.0));
		posCraft.z += WORLD(700.0) + WORLD(random_f(3000.0)+200.0);
	}
	else
	{
		posCraft.x += (i - (craftCnt/2)) * WORLD(random_f(300.0)+200.0);
		posCraft.y  = y + WORLD(random_f(200.0));
		posCraft.z += WORLD(700.0) + WORLD(random_f(300.0)+200.0);
	}

	Craft* craft = new CraftF14Tomcat( gWorld.GetCenterQuadrant(), posCraft, gModelNameF14Vec );
	craft->Rotate( AXIS_Y, -10.0 );
	mCraftDlist.Append( craft );
	craft->SetAnimation( eAnimate_AUTO_TRANSLATE );
	craft->SetThrottle( 10 );

	static int turkey;
	if ( (++turkey & 7) == 7 )
	{
		craft->SetAnimation( eAnimate_PILOT_PURSUE );
		craft->mPilot->SelectTarget();
	}
}

/*****************************************************************************
 * Game module animation routine.
 * Called periodically by a timer (by the engine's Animate()).
 * @return True if redisplay is necessary.
 *****************************************************************************/
int
GamePony::Animate( void )
{
	return Parent::Animate();
}

/*****************************************************************************
 * Every timer tick.
 * Called periodically by a timer (by the engine's Tick()).
 *****************************************************************************/
void
GamePony::Tick( void )
{
	Parent::Tick();

	// Call GuiTimerOneSecond() every second.  See gfx_gui.hh.
	static int milsecGuiCountdown = 1000;
	const int milsec = gEng.GetMilsecFrame(); // time between current/previous timer-ticks
	milsecGuiCountdown -= milsec;
	if ( milsecGuiCountdown <= 0 )
	{
		RESET_COUNTDOWN( milsecGuiCountdown, 1000 );
		gGui.TimerOneSecond();
	}

	// Spawn new targets before the "killer" Tomcat kills them all.
	// (Inefficient but currently no alt)
	if ( mCmdarg.mGame )
	{
		int aliveCnt = 0;
		for ( const Dlink<Craft>* dlink = gGame.mCraftDlist.ForStart();
	          gGame.mCraftDlist.ForTest(dlink);
	          gGame.mCraftDlist.ForNext(dlink) )
		{
			Craft* craft = dlink->mObj;
			if ( ! craft->IfDestroyed() ) ++aliveCnt;
		}
		if ( aliveCnt < 10 )
		{
			for ( int i = 0; i < 20; ++i )
			{
				SpawnTurkey( mPlayer1Craft, i, 20, true );
			}
		}
	}
}

/*****************************************************************************
 * Reset scene.
 *****************************************************************************/
void
GamePony::ResetScene( void )
{
	if ( gGame.IfDemoMode() ) gEng.mEye.SetOrigin( Oy, gGame.DemoModeAltitude() );
	UpdateAltitudeLandWater();
}

/*****************************************************************************
 * @return Current Craft object or NULL if Eye is current!
 * This is a dangerous method!  Use Engine::GetCurrent() instead.
 * But this is necessary in a few cases to access Craft-specific methods.
 *****************************************************************************/
CraftPtrc
GamePony::GetCurrentCraftOrNULL( void )
{
    if ( gEng.GetCurrent() == &gEng.mEye )
    {
        return NULL;
    }
    else
    {
        // Convert DynaPtr to CraftPtr.
        CraftPtr craft = static_cast<Craft*>(GET_PTR(gEng.GetCurrent()));
        return craft;
    }
}
/// @return Current F-14 Tomcat or NULL.
CraftF14Tomcat*
GamePony::GetCraftF14TomcatOrNULL( void )
{
	CraftPtrc craft = GetCurrentCraftOrNULL();
    if ( craft != NULL && craft->GetName() == "CraftF14Tomcat" )
		return static_cast<CraftF14Tomcat*>( GET_PTR(craft) );
	else
		return NULL;
}

/*****************************************************************************
 * Kludge for OpenGL Mesa to prevent shredding at base of hills by
 * pushing land/water farther down at high altitudes where
 * the shredding would otherwise be seen.
 * Though, a second reason is avoid flying under land/water when
 * collisions are turned off.
 *****************************************************************************/
void
GamePony::UpdateAltitudeLandWater( void )
{
	// TODO eliminate GROUND_LEVEL
	mAltitudeLand  = gEng.mEye.IfAboveAltitude(WORLD(250.0)) ? Max(gEng.mEye.mCm[Oy]*0.08,WORLD(-9000.0)) : GROUND_LEVEL - WORLD(25.0);
	mAltitudeWater = gEng.mEye.IfAboveAltitude(WORLD(250.0)) ? Max(gEng.mEye.mCm[Oy]*0.03,WORLD(-8000.0)) : GROUND_LEVEL - WORLD(5.0);
}
