//-----------------------------------------------------------------------------
// Torque Game Engine 
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

// Game duration in secs, no limit if the duration is set to 0
$Game::Duration = 60 * 60;

// When a client score reaches this value, the game is ended.
$Game::EndGameScore = 30;

// Pause while looking over the end game screen (in secs)
$Game::EndGamePause = 10;


//-----------------------------------------------------------------------------
//  Functions that implement game-play
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function onServerCreated()
{
   // Server::GameType is sent to the master server.
   // This variable should uniquely identify your game and/or mod.
   $Server::GameType = "FPS Starter Kit";

   // Server::MissionType sent to the master server.  Clients can
   // filter servers based on mission type.
   $Server::MissionType = "Deathmatch";

   // GameStartTime is the sim time the game started. Used to calculated
   // game elapsed time.
   $Game::StartTime = 0;

   // Load up all datablocks, objects etc.  This function is called when
   // a server is constructed.
   exec("./audioProfiles.cs");
   exec("./camera.cs");
   exec("./markers.cs"); 
   exec("./triggers.cs"); 
   exec("./inventory.cs");
   exec("./shapeBase.cs");
   exec("./item.cs");
   exec("./itemTemplateMaker.cs");
   exec("./keys.cs");
   exec("./staticShape.cs");
   exec("./weapon.cs");
   exec("./radiusDamage.cs");
   exec("./moltov.cs");
   exec("./player.cs");
   exec("./chimneyfire.cs");
   exec("./aiPlayer.cs");
   exec("./fxlights.cs");
   
   
   // Keep track of when the game started
   $Game::StartTime = $Sim::Time;
}

function onServerDestroyed()
{
   // This function is called as part of a server shutdown.
}


//-----------------------------------------------------------------------------

function onMissionLoaded()
{
   // Called by loadMission() once the mission is finished loading.
   // Nothing special for now, just start up the game play.
//   if($loadingDone == 1)
//   {
//   	error("Loading1111111111111111111111");
   	startGame();
//   }
//   else
//   {
//   	error("Waiting");
//   	schedule(25, 0, onMissionLoaded);
//   }
}

function onMissionEnded()
{
   // Called by endMission(), right before the mission is destroyed

   // Normally the game should be ended first before the next
   // mission is loaded, this is here in case loadMission has been
   // called directly.  The mission will be ended if the server
   // is destroyed, so we only need to cleanup here.
   save();
   cancel($Game::Schedule);
   $Game::Running = false;
   $Game::Cycling = false;
}


//-----------------------------------------------------------------------------

function startGame()
{
	echo("startGame");
   if ($Game::Running) {
      error("startGame: End the game first!");
      return;
   }

   // Inform the client we're starting up
   for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) {
      %cl = ClientGroup.getObject( %clientIndex );
      commandToClient(%cl, 'GameStart');

      // Other client specific setup..
      %cl.score = 0;
   }

   // Start the game timer
   if ($Game::Duration)
      $Game::Schedule = schedule($Game::Duration * 1000, 0, "onGameDurationEnd" );
   $Game::Running = true;
   
   // Start the AIManager
   new ScriptObject(AIManager) {};
   MissionCleanup.add(AIManager);
   AIManager.think();
   
}

function endGame()
{
   if (!$Game::Running)  {
      error("endGame: No game running!");
      return;
   }
   
   // Stop the AIManager
   AIManager.delete();

   // Stop any game timers
   cancel($Game::Schedule);

   // Inform the client the game is over
   for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) {
      %cl = ClientGroup.getObject( %clientIndex );
      commandToClient(%cl, 'GameEnd');
   }

   // Delete all the temporary mission objects
   resetMission();
   $Game::Running = false;
}

function onGameDurationEnd()
{
   // This "redirect" is here so that we can abort the game cycle if
   // the $Game::Duration variable has been cleared, without having
   // to have a function to cancel the schedule.
   if ($Game::Duration && !isObject(EditorGui))
      cycleGame();
}


//-----------------------------------------------------------------------------

function cycleGame()
{
	echo("cycleGame");
   // This is setup as a schedule so that this function can be called
   // directly from object callbacks.  Object callbacks have to be
   // carefull about invoking server functions that could cause
   // their object to be deleted.
   if (!$Game::Cycling) {
      $Game::Cycling = true;
      $Game::Schedule = schedule(0, 0, "onCycleExec");
   }
}

function onCycleExec()
{
	echo("cycleExec");
   // End the current game and start another one, we'll pause for a little
   // so the end game victory screen can be examined by the clients.
   endGame();
   $Game::Schedule = schedule($Game::EndGamePause * 1000, 0, "onCyclePauseEnd");
}

function onCyclePauseEnd()
{
   $Game::Cycling = false;

   // Just cycle through the missions for now.
   %search = $Server::MissionFileSpec;
   for (%file = findFirstFile(%search); %file !$= ""; %file = findNextFile(%search)) {
      if (%file $= $Server::MissionFile) {
         // Get the next one, back to the first if there
         // is no next.
         %file = findNextFile(%search);
         if (%file $= "")
           %file = findFirstFile(%search);
         break;
      }
   }
   loadMission(%file);
}


//-----------------------------------------------------------------------------
// GameConnection Methods
// These methods are extensions to the GameConnection class. Extending
// GameConnection make is easier to deal with some of this functionality,
// but these could also be implemented as stand-alone functions.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function GameConnection::onClientEnterGame(%this)
{
   commandToClient(%this, 'SyncClock', $Sim::Time - $Game::StartTime);

   // Create a new camera object.
   %this.camera = new Camera() {
      dataBlock = Observer;
   };
   MissionCleanup.add( %this.camera );
   %this.camera.scopeToClient(%this);

   // Setup game parameters, the onConnect method currently starts
   // everyone with a 0 score.
   %this.score = 0;

   // Create a player object.
   %this.spawnPlayer();
   %this.setAmmoAmountHud("45");
   %this.setRegionDistanceHud();

   // Distance calculations (and beacon display)
   calculateDistances(%this);
}

//Function to calculate all the distances in the local region list (Ctrl-R)
function calculateDistances(%this)
{

	%camera = %this.camera;
	%player = %this.player;
	
	//Subtract 32 for the size of the beacon.png
	%width = $screenResX - 32;
	%height = $screenResY - 32;
	
	//Player location X and Y
	%playerLocX = getWord(%player.getPosition(), 0);
	%playerLocY = getWord(%player.getPosition(), 1);

	//Finds the real location of the player because the previous playerLocX/Y is only the location of the map
	$PlayerRealLat = $PlayerLat - (($latMoved / 2) * (%playerLocY / 335));
	$lonMoved = mCeil((1000 / (69 * mAbs(mCos($RADIANS * $PlayerLat)) ) ) * (798/670));
	$PlayerRealLon = $PlayerLon + (($lonMoved / 2) * (%playerLocX / 399));
	
//	echo("-------------player x y " @ %playerLocX @ ", " @ %playerLocY);
//	error("-----------------------lat lon:  " @ $playerLat @ ", " @ $playerLon);
//	error("---------Real player Lat/Lon: " @ $PlayerRealLat @ ", " @ $PlayerRealLon @ "------------");
//	echo("-------------------------------------------------------------------------------------------");
	   
	   
	//Goes through the list of local regions
	for(%i = 0; %i < $localRegionList.count; %i++)
	{
		//error("RegionX: " @ $localRegionList.contents[%i, 1] @ "         PlayerX: " @ $PlayerLon);
		
		//Calculates distance between the player and the region center
		%x = ($localRegionList.contents[%i, 2] - $PlayerRealLon) / 1000;
		%y = ($localRegionList.contents[%i, 1] - $PlayerRealLat) / 1000;
		%distance = (msqrt(%x * %x + %y * %y) - $localRegionList.contents[%i, 3]/10000)* 69.2;
		
		//error(%x @ ", " @ %y @ "     " @ $localRegionList.contents[%i, 0] @ "'s distance is " @ %distance);
		
		//Rounds up the distances so it can be converted to a 6 digit # so it can be handled by the 
		//  torque engine (if it is more than 6 digits, it will be converted to scientific notation)
		%distance = %distance * 100;
		%distance = mCeil(%distance);
		%distance = %distance / 100;
		%dotPos = strpos(%distance, ".", 0);
		if(%dotPos >= 0)
		{
			if(%dotPos == strlen(%distance) - 2)
				%distance = %distance @ 0;
		}
		else
		{
			%distance = %distance @ ".00";
		}

		//Basically a "case" expansion
		//  The reason why this was used because individual values has to be changed, and it can't be accessed
		//  through the loop. For example, Reg1Value.setText("") vs Reg2Value.setText("")
		if(%i == 0)
		{
			//Sets text on the region screen
			Reg1Desc.setText($localRegionList.contents[%i, 0]);
			
			//If player is already in that region
			if($RegionName $= $localRegionList.contents[%i, 0])
			{
				//Set away the becaon, and note in the region list that the player is there
				if($RegionSelected == 1)
				{
					beaconPic.visible = 0;
					RegionDistance.setText("");
				}
				Reg1Value.setText("Here");
			}
			else
			{
				//Sets the becaon if that region is selected and calculate the distance
				if($RegionSelected == 1)
				{
					RegionDistance.setText("mi: " @ %distance);
					beaconPic.visible = 1;
					beaconPic.position = calculateScreenLocation(%x, %y, %player.rotation, %width, %height);
				}
				//Sets the distance in the region list
				Reg1Value.setText(%distance);
			}
		}
		else if(%i == 1)
		{
			Reg2Desc.setText($localRegionList.contents[%i, 0]);
			if($RegionName $= $localRegionList.contents[%i, 0])
			{
				if($RegionSelected == 2)
				{
					beaconPic.visible = 0;
					RegionDistance.setText("");
				}
				Reg2Value.setText("Here");
			}
			else
			{
				if($RegionSelected == 2)
				{
					RegionDistance.setText("mi: " @ %distance);					
					beaconPic.visible = 1;
					beaconPic.position = calculateScreenLocation(%x, %y, %player.rotation, %width, %height);
				}
				Reg2Value.setText(%distance);
			}
		}
		else if(%i == 2)
		{
			Reg3Desc.setText($localRegionList.contents[%i, 0]);
			if($RegionName $= $localRegionList.contents[%i, 0])
			{
				if($RegionSelected == 3)
				{
					beaconPic.visible = 0;				
					RegionDistance.setText("");
				}
				Reg3Value.setText("Here");
			}
			else
			{
				if($RegionSelected == 3)
				{
					RegionDistance.setText("mi: " @ %distance);
					
					beaconPic.visible = 1;
					beaconPic.position = calculateScreenLocation(%x, %y, %player.rotation, %width, %height);
				}
				Reg3Value.setText(%distance);
			}
		}
		else if(%i == 3)
		{
			Reg4Desc.setText($localRegionList.contents[%i, 0]);
			if($RegionName $= $localRegionList.contents[%i, 0])
			{
				if($RegionSelected == 4)
				{
					beaconPic.visible = 0;				
					RegionDistance.setText("");
				}
				Reg4Value.setText("Here");
			}
			else
			{
				if($RegionSelected == 4)
				{
					RegionDistance.setText("mi: " @ %distance);

					beaconPic.visible = 1;
					beaconPic.position = calculateScreenLocation(%x, %y, %player.rotation, %width, %height);
				}
				Reg4Value.setText(%distance);
			}
		}
		else if(%i == 4)
		{
			Reg5Desc.setText($localRegionList.contents[%i, 0]);
			if($RegionName $= $localRegionList.contents[%i, 0])
			{
				if($RegionSelected == 5)
				{
					beaconPic.visible = 0;				
					RegionDistance.setText("");
				}
				Reg5Value.setText("Here");
			}
			else
			{
				if($RegionSelected == 5)
				{
					RegionDistance.setText("mi: " @ %distance);

					beaconPic.visible = 1;
					beaconPic.position = calculateScreenLocation(%x, %y, %player.rotation, %width, %height);
				}
				Reg5Value.setText(%distance);
			}
		}
	}
	
	//If region selected = 0, don't show any beacon
	if($RegionSelected == 0)
	{
		beaconPic.visible = 0;
		RegionDistance.setText("");
	}

	//echo("///////////// You're In Region " @ $RegionName @ "    ///////////////////");

	//Shows all the region slots in the region list NOT in the local region list to nothing
	for(%j = $localRegionList.count; %j < 5; %j++)
	{
		if(%j == 0)
		{
			Reg1Desc.setText("");
			Reg1Value.setText("");
		}
		else if(%j == 1)
		{
			Reg2Desc.setText("");
			Reg2Value.setText("");
		}
		else if(%j == 2)
		{
			Reg3Desc.setText("");
			Reg3Value.setText("");
		}
		else if(%j == 3)
		{
			Reg4Desc.setText("");
			Reg4Value.setText("");
		}
		else if(%j == 4)
		{
			Reg5Desc.setText("");
			Reg5Value.setText("");
		}
	}

	//echo(%player.getTransform());
	
	//This is scheduled so that the calculate distance can be run every 8ms
	// This can be chagned so that it doesn't have to run as often
	//  However, 8ms = 125fps, so it might be possible for computers that run that fast.
	schedule(8, 0, calculateDistances, %this);
}


//Calculates the screen location of the beacon based on the following information
//
//	x = distance away from the player in x direction (Longitude)
//	y = distance away from the player in y direction (Latitude)
//	playerRotation = direction where the player is facing
//	width = width of the screen
//	Height = height of the screen
//
function calculateScreenLocation(%x, %y, %playerRotation, %width, %height)
{
	//Find the angle of the region to the player
	%angle = mRadToDeg(mATan(%y, %x));	//opposite, adjacent
	if(%angle < 0)
		%angle = %angle + 360;		//Make all the angle to positive.


	//Find where the player is facing and calulate the angle in that perspective
	
	%playerAngle = getWord(%playerRotation, 3);
	if(getWord(%playerRotation, 2) < 0)
		%playerAngle = 360 - %playerAngle;
	%playerAngle = %playerAngle - 90;
	if(%playerAngle < 0)
		%playerAngle += 360;


	//Finds the difference between player angle and region angle
	
	%angle -= %playerAngle;
	%playerAngle = 0;

	if(%angle < -180)
		%angle += 360;

	%result = 0 - %angle;
	if(%result < -180)
		%result += 360;
	
	
	//Converts the angle to standard where it if the angle is the positive, the beacon should be on the right side of the screen
	//	if then angle is negative, it is on the left side of the screen


	//Adds the width and the height to become the distance of half a screen.
	%halfScreen = %width + %height;
	%location = mAbs(mCeil(%halfScreen * %result/180));	//finds the location on half a screen based on angle


	if(%result >= 0)	//if it is a positive angle, it should be on the right side of the screen
	{
		if(%location <= %width / 2)
		{
			%screenY = 0;
			%screenX = %width / 2 - %location;
		}
		else if(%location <= %width / 2 + %height)
		{
			%screenX = 0;
			%screenY = %location - %width / 2;
		}
		else
		{
			%screenY = %height;
			%screenX = %location - %width / 2 - %height;
		}

	}
	else		//if it is a negative angle, it appears on the left side of the screen
	{
		if(%location <= %width / 2)
		{
			%screenY = 0;
			%screenX = %width / 2 + %location;
		}
		else if(%location <= %width / 2 + %height)
		{
			%screenX = %width;
			%screenY = %location - %width / 2;
		}
		else
		{
			%screenY = %height;
			%screenX = %width - (%location - %width / 2 - %height);
		}
	}

	return %screenX @ " " @ %screenY;
}

function GameConnection::onClientLeaveGame(%this)
{
	echo("LEAVING GAME ON SERVER");
	//save();
   if (isObject(%this.camera))
      %this.camera.delete();
   if (isObject(%this.player))
      %this.player.delete();
}


//-----------------------------------------------------------------------------

function GameConnection::onLeaveMissionArea(%this)
{
   // The control objects invoked this method when they
   // move out of the mission area.
}

function GameConnection::onEnterMissionArea(%this)
{
   // The control objects invoked this method when they
   // move back into the mission area.
}


//-----------------------------------------------------------------------------

function GameConnection::onDeath(%this, %sourceObject, %sourceClient, %damageType, %damLoc)
{
   // Clear out the name on the corpse
   %this.player.setShapeName("");

   // Switch the client over to the death cam and unhook the player object.
   if (isObject(%this.camera) && isObject(%this.player)) {
      %this.camera.setMode("Corpse",%this.player);
      %this.setControlObject(%this.camera);
   }
   %this.player = 0;

   // Doll out points and display an appropriate message
   if (%damageType $= "Suicide" || %sourceClient == %this) {
      %this.incScore(-1);
      messageAll('MsgClientKilled','%1 takes his own life!',%this.name);
   }
   else {
      %sourceClient.incScore(1);
      messageAll('MsgClientKilled','%1 gets nailed by %2!',%this.name,%sourceClient.name);
      if (%sourceClient.score >= $Game::EndGameScore)
         cycleGame();
   }
}


//-----------------------------------------------------------------------------

function GameConnection::spawnPlayer(%this)
{
   // Combination create player and drop him somewhere
    %spawnPoint = pickSpawnPoint();
    %this.createPlayer(%spawnPoint);
   //test();
  	
} 

//-----------------------------------------------------------------------------

function GameConnection::createPlayer(%this, %spawnPoint)
{
   if (%this.player > 0)  {
      // The client should not have a player currently
      // assigned.  Assigning a new one could result in 
      // a player ghost.
      error( "Attempting to create an angus ghost!" );
   }

   // Create the player object
   %player = new Player() {
      dataBlock = PlayerBody;
      client = %this;
   };
   MissionCleanup.add(%player);

   // Player setup...
   %player.setTransform(%spawnPoint);
   %player.setShapeName(%this.name);
   

// Start the update schedule recursion
startSchedule();

%player.mountImage(MoltovImage, 0);






//***********************************************************
//* Client script files use $inventory to track the inventory.
//* The inventory GUI uses this array as well.
//***********************************************************/
   
   %inventory = getInventory();

   //Syncs server inventory to client inventory
   for(%i = 0; %i < %inventory.count; %i++)
   {
   	%player.getDatablock().maxInv[%inventory.contents[%i, 1]] = %inventory.contents[%i, 5];
   	%player.setInventory(%inventory.contents[%i, 1], %inventory.contents[%i, 3]);
   	$playerItems[%i, 3] = 0;	//initialize all amounts in playerItems to 0
   }
   
   //Extra items contained in the server inventory which are not tracked in the inventory gui
      	%player.getDatablock().maxInv[MoltovAmmo] = 999;
	%player.setInventory(MoltovAmmo,45);
   

	
   // Update the camera to start with the player
   %this.camera.setTransform(%player.getEyeTransform());

   // Give the client control of the player
   %this.player = %player;
   %this.setControlObject(%player);
   serverCmdDisplayInventory(%player);
   
}


//-----------------------------------------------------------------------------
// Support functions
//-----------------------------------------------------------------------------

function pickSpawnPoint() 
{
   %groupName = "MissionGroup/PlayerDropPoints";
   %group = nameToID(%groupName);

   if (%group != -1) {
      %count = %group.getCount();
      if (%count != 0) {
         %index = getRandom(%count-1);
         %spawn = %group.getObject(%index);
         return %spawn.getTransform();
      }
      else
         error("No spawn points found in " @ %groupName);
   }
   else
      error("Missing spawn points group " @ %groupName);

   // Could be no spawn points, in which case we'll stick the
   // player at the center of the world.
   return "0 150 300 1 0 0 0";
}

