//=============================================================================
// TeamGamePlus.
//=============================================================================
class TeamGamePlus expands DeathMatchPlus
	localized
	config;

#exec MESH IMPORT MESH=Flag1M ANIVFILE=MODELS\flag_a.3D DATAFILE=MODELS\flag_d.3D X=0 Y=0 Z=0 ZeroTex=1
#exec MESH ORIGIN MESH=Flag1M X=0 Y=100 Z=0 YAW=128 PITCH=0 ROLL=-64
#exec MESH SEQUENCE MESH=flag1M SEQ=All    STARTFRAME=0  NUMFRAMES=14
#exec MESH SEQUENCE MESH=flag1M SEQ=Wave  STARTFRAME=1  NUMFRAMES=13
#exec TEXTURE IMPORT NAME=JFlag11 FILE=MODELS\flag_red.PCX GROUP=Skins
#exec TEXTURE IMPORT NAME=JFlag12 FILE=MODELS\flag_blue.PCX GROUP=Skins
#exec TEXTURE IMPORT NAME=JFlag13 FILE=MODELS\flag_green.PCX GROUP=Skins
#exec TEXTURE IMPORT NAME=JFlag14 FILE=MODELS\flag_yellow.PCX GROUP=Skins
#exec TEXTURE IMPORT NAME=JFlag15 FILE=MODELS\flag3.PCX GROUP=Skins
#exec MESHMAP SCALE MESHMAP=flag1M X=0.1 Y=0.1 Z=0.2
#exec MESHMAP SETTEXTURE MESHMAP=flag1M NUM=0 TEXTURE=Jflag11
	
var()		 bool   bSpawnInTeamArea;
var()		 bool	bScoreTeamKills;
var() config bool	bNoTeamChanges;
var			 bool	bTeamAlternate; 
var() config float  FriendlyFireScale; //scale friendly fire damage by this value
var() config int	MaxTeams; //Maximum number of teams allowed in (up to 4)
var	TeamInfo Teams[4]; // Red, Blue, Green, Gold
var() config float  GoalTeamScore; //like fraglimit
var() config int	MaxTeamSize;
var  localized string[128] NewTeamMessage;
var  localized string[64] StartUpTeamMessage;
var localized string[32] TeamColor[4];

var		int			NextBotTeam;
var byte TEAM_Red, TEAM_Blue, TEAM_Green, TEAM_Gold;
var name CurrentOrders[4];
var int FriendlyBots;
var int PlayerTeamNum;

function PostBeginPlay()
{
	local int i;
	for (i=0;i<4;i++)
	{
		Teams[i] = Spawn(class'TeamInfo');
		Teams[i].Size = 0;
		Teams[i].Score = 0;
		Teams[i].TeamName = TeamColor[i];
		Teams[i].TeamIndex = i;
	}
	
	Super.PostBeginPlay();

	if ( bRatedGame )
	{
		FriendlyFireScale = 0;
		MaxTeams = 2;
	}
}

function SetRatedBots()
{
	RemainingBots = Level.RecommendedEnemies;
	if ( RemainingBots == 0 )
	{
		RemainingBots = int(Level.IdealPlayerCount);
		if ( RemainingBots == 0 )
			RemainingBots = 4;
	}
	FriendlyBots = Level.RecommendedTeammates;
	if ( FriendlyBots == 0 )
		FriendlyBots = RemainingBots - 1;
	RemainingBots += FriendlyBots;
}

//------------------------------------------------------------------------------
// Player start functions

function PlayStartUpMessage(PlayerPawn NewPlayer)
{
	if ( bRequireReady )
		NewPlayer.SetProgressMessage(GameName$TourneyMessage$StartupTeamMessage$Teams[NewPlayer.PlayerReplicationInfo.Team].TeamName$".");
	else
		NewPlayer.SetProgressMessage(StartUpMessage$StartupTeamMessage$Teams[NewPlayer.PlayerReplicationInfo.Team].TeamName$".");
}

function playerpawn Login
(
	string[32] Portal,
	string[120] Options,
	out string[80 ] Error,
	class<playerpawn> SpawnClass
)
{
	local PlayerPawn newPlayer;
	local int      InTeam;
	local NavigationPoint StartSpot;

	newPlayer = Super.Login(Portal, Options, Error, SpawnClass);
	if ( newPlayer == None)
		return None;

	InTeam     = GetIntOption( Options, "Team", 0 );

	if ( !ChangeTeam(newPlayer, InTeam) )
	{
		Error = "Could not find team for player";
		return None;
	}
	if ( bSpawnInTeamArea && (NewPlayer.PlayerReplicationInfo.Team != 0) )
	{
		StartSpot = FindPlayerStart(NewPlayer.PlayerReplicationInfo.Team, Portal);
		if ( StartSpot != None )
		{
			NewPlayer.SetLocation(StartSpot.Location);
			NewPlayer.SetRotation(StartSpot.Rotation);
			NewPlayer.ViewRotation = StartSpot.Rotation;
			NewPlayer.ClientSetRotation(NewPlayer.Rotation);
			StartSpot.PlayTeleportEffect( NewPlayer, true );
		}
	}
	PlayerTeamNum = NewPlayer.PlayerReplicationInfo.Team;
	PlayStartUpMessage(NewPlayer);
		
	return newPlayer;
}

function Logout(pawn Exiting)
{
	Super.Logout(Exiting);
	if ( Exiting.IsA('Spectator') )
		return;
    Teams[Exiting.PlayerReplicationInfo.Team].Size--;
}
	
function NavigationPoint FindPlayerStart(byte Team, optional string[32] incomingName)
{
	local PlayerStart Dest, Candidate[4], Best;
	local float Score[4], BestScore, NextDist;
	local pawn OtherPlayer;
	local int i, num;
	local Teleporter Tel;
	local NavigationPoint N;

	if( incomingName!="" )
		foreach AllActors( class 'Teleporter', Tel )
			if( string(Tel.Tag)~=incomingName )
				return Tel;
		
	num = 0;
	//choose candidates	
	for ( N=Level.NavigationPointList; N!=None; N=N.nextNavigationPoint )
		if ( N.IsA('PlayerStart') 
			&& (!bSpawnInTeamArea || (Team == PlayerStart(N).TeamNumber)) )
		{
			if (num<4)
				Candidate[num] = PlayerStart(N);
			else if (Rand(num) < 4)
				Candidate[Rand(4)] = PlayerStart(N);
			num++;
		}

	if (num == 0 )
		foreach AllActors( class'PlayerStart', Dest )
		{
			if (num<4)
				Candidate[num] = Dest;
			else if (Rand(num) < 4)
				Candidate[Rand(4)] = Dest;
			num++;
		}

	if (num>4) num = 4;
	else if (num == 0)
		return None;
		
	//assess candidates
	for (i=0;i<num;i++)
		Score[i] = 4000 * FRand(); //randomize
	
	for ( OtherPlayer=Level.PawnList; OtherPlayer!=None; OtherPlayer=OtherPlayer.NextPawn)	
		if ( OtherPlayer.bIsPlayer && (OtherPlayer.Health > 0) )
			for (i=0;i<num;i++)
			{
				NextDist = VSize(OtherPlayer.Location - Candidate[i].Location);
				Score[i] += NextDist;
				if ( OtherPlayer.Region.Zone == Candidate[i].Region.Zone )
				{
					if (NextDist < CollisionRadius + CollisionHeight)
						Score[i] -= 1000000.0;
					else if ( (NextDist < 1400) && (Team != OtherPlayer.PlayerReplicationInfo.Team) && OtherPlayer.LineOfSightTo(Candidate[i]) )
						Score[i] -= 10000.0;
				}
			}
	
	BestScore = Score[0];
	Best = Candidate[0];
	for (i=1;i<num;i++)
		if (Score[i] > BestScore)
		{
			BestScore = Score[i];
			Best = Candidate[i];
		}
				
	return Best;
}

//-------------------------------------------------------------------------------------
// Level gameplay modification

//Use reduce damage for teamplay modifications, etc.
function int ReduceDamage(int Damage, name DamageType, pawn injured, pawn instigatedBy)
{
	Damage = Super.ReduceDamage(Damage, DamageType, injured, instigatedBy);
	
	if ( instigatedBy == None )
		return Damage;

	if ( (instigatedBy != injured) 
		&& (injured.PlayerReplicationInfo.Team ~= instigatedBy.PlayerReplicationInfo.Team) )
	{
		if ( injured.IsA('Bot') )
			Bot(Injured).YellAt(instigatedBy);
		return (Damage * FriendlyFireScale);
	}
	else
		return Damage;
}

function Killed(pawn killer, pawn Other, name damageType)
{
	Super.Killed(killer, Other, damageType);
	if ( bScoreTeamKills && (killer != Other) && (killer != None) )
		Teams[Killer.PlayerReplicationInfo.Team].Score += 1.0;

	if ( (GoalTeamScore > 0) && (Teams[killer.PlayerReplicationInfo.Team].Score >= GoalTeamScore) )
		EndGame();
}

function bool ChangeTeam(Pawn Other, int NewTeam)
{
	local int i, s;
	local pawn APlayer;
	local teaminfo SmallestTeam;

	if ( Other.IsA('Spectator') )
	{
		Other.PlayerReplicationInfo.Team = NewTeam;
		Other.PlayerReplicationInfo.TeamName = Teams[NewTeam].TeamName;
		return true;
	}
	if ( Other.PlayerReplicationInfo.TeamName == Teams[Other.PlayerReplicationInfo.Team].TeamName )
	{
		if ( bNoTeamChanges )
			return false;
	}

	SmallestTeam = None;

	for( i=0; i<MaxTeams; i++ )
		if ( (Teams[i].Size < MaxTeamSize) 
				&& ((SmallestTeam == None) || (SmallestTeam.Size > Teams[i].Size)) )
		{
			s = i;
			SmallestTeam = Teams[i];
		}

	for( i=0; i<MaxTeams; i++ )
	{
		if ( i == NewTeam )
		{
			if (Teams[i].Size < MaxTeamSize)
			{
				AddToTeam(i, Other);
				return true;
			}
			else 
				break;
		}
	}

	if ( (SmallestTeam != None) && (SmallestTeam.Size < MaxTeamSize) )
	{
		AddToTeam(s, Other);
		return true;
	}

	return false;
}

function AddToTeam( int num, Pawn Other )
{
	local texture NewSkin;
	local teaminfo aTeam;
	local Pawn P;
	local bool bSuccess;

	aTeam = Teams[num];

	aTeam.Size++;
	Other.PlayerReplicationInfo.Team = num;
	Other.PlayerReplicationInfo.TeamName = aTeam.TeamName;
	bSuccess = false;
	if ( Other.IsA('PlayerPawn') )
		Other.PlayerReplicationInfo.TeamID = 0;
	else
		Other.PlayerReplicationInfo.TeamID = 1;

	while ( !bSuccess )
	{
		bSuccess = true;
		for ( P=Level.PawnList; P!=None; P=P.nextPawn )
                        if ( P.bIsPlayer && (P != Other) 
							&& (P.PlayerReplicationInfo.Team == Other.PlayerReplicationInfo.Team) 
							&& (P.PlayerReplicationInfo.TeamId == Other.PlayerReplicationInfo.TeamId) )
				bSuccess = false;
		if ( !bSuccess )
			Other.PlayerReplicationInfo.TeamID++;
	}

	BroadcastMessage(Other.PlayerReplicationInfo.PlayerName$NewTeamMessage$aTeam.TeamName, false);

	NewSkin = texture(DynamicLoadObject(GetItemName(string(Other.Mesh))$"Skins.T_"$aTeam.TeamName, class'Texture'));

	if ( NewSkin != None )
		Other.Skin = NewSkin;
}

function bool CanSpectate( pawn Viewer, actor ViewTarget )
{
	return ( (Pawn(ViewTarget) != None) && Pawn(ViewTarget).bIsPlayer 
		&& (Pawn(ViewTarget).PlayerReplicationInfo.Team == Viewer.PlayerReplicationInfo.Team) );
}

function TeamInfo GetTeam(int TeamNum )
{
	if ( TeamNum < ArrayCount(Teams) )
		return Teams[TeamNum];
	else return None;
}

function bool IsOnTeam(Pawn Other, int TeamNum)
{
	if ( Other.PlayerReplicationInfo.Team == TeamNum )
		return true;

	return false;
}

function bool AddBot()
{
	local bot NewBot;
	local NavigationPoint StartSpot, OldStartSpot;
	local int DesiredTeam;

	NewBot = SpawnBot(StartSpot);
	if ( NewBot == None )
	{
		log("Failed to spawn bot");
		return false;
	}

	if ( bRatedGame )
	{
		if (bTeamAlternate && (FriendlyBots > 0) )
		{
			FriendlyBots--;
			DesiredTeam = PlayerTeamNum;
		}
		else
		{
			NextBotTeam++;
			if ( NextBotTeam >= MaxTeams )
				NextBotTeam = 0;
			if ( NextBotTeam == PlayerTeamNum )
			{
				NextBotTeam++;
				if ( NextBotTeam >= MaxTeams )
					NextBotTeam = 0;
			}
			DesiredTeam = NextBotTeam;			
		}
		bTeamAlternate = !bTeamAlternate;
		NewBot.PlayerReplicationInfo.TeamName = "";
		ChangeTeam(NewBot, DesiredTeam);
	}
	else
	{
		DesiredTeam = NewBot.PlayerReplicationInfo.Team;
		NewBot.PlayerReplicationInfo.TeamName = "";
		if ( (DesiredTeam == 255) || !ChangeTeam(NewBot, DesiredTeam) )
		{
			ChangeTeam(NewBot, NextBotTeam);
			NextBotTeam++;
			if ( NextBotTeam >= MaxTeams )
				NextBotTeam = 0;
		}
	}

	if ( bSpawnInTeamArea && (NewBot.PlayerReplicationInfo.Team != 0) )
	{
		OldStartSpot = StartSpot;
		StartSpot = FindPlayerStart(NewBot.PlayerReplicationInfo.Team);
		if ( StartSpot != None )
		{
			NewBot.SetLocation(StartSpot.Location);
			NewBot.SetRotation(StartSpot.Rotation);
			NewBot.ViewRotation = StartSpot.Rotation;
			NewBot.SetRotation(NewBot.Rotation);
			StartSpot.PlayTeleportEffect( NewBot, true );
		}
		else
			StartSpot = OldStartSpot;
	}

	StartSpot.PlayTeleportEffect(NewBot, true);

	SetBotOrders(NewBot);
	return true;
}

function SetBotOrders(Bot NewBot)
{
	local Pawn P, L;
	local int num;

	For ( P=Level.PawnList; P!=None; P= P.NextPawn )
		if ( P.bIsPlayer && (P.PlayerReplicationInfo.Team == NewBot.PlayerReplicationInfo.Team)
			&& !P.IsA('Spectator') && (P != NewBot)
			&& (!P.IsA('Bot') || (Bot(P).Orders == 'FreeLance')) )
		{
			num++;
			if ( (L == None) || (FRand() < 1.0/float(num)) )
				L = P;
		}
		
	if ( (L != None) && (FRand() > 1.0/(1.0 + float(num))) )
		NewBot.SetOrders('Follow',L);
}				 

	
function byte AssessBotAttitude(Bot aBot, Pawn Other)
{
	if ( (aBot.PlayerReplicationInfo.Team == Other.PlayerReplicationInfo.Team)
		|| (Other.IsA('TeamCannon') 
			&& (TeamCannon(Other).MyTeam == aBot.PlayerReplicationInfo.Team)) ) 
		return 3;
	else 
		return Super.AssessBotAttitude(aBot, Other);
}

function Actor SetDefenseFor(Bot aBot)
{
	return None;
}

function bool FindSpecialAttractionFor(Bot aBot)
{
	return false;
}

function SetAttractionStateFor(Bot aBot)
{
	if ( aBot.Enemy != None )
	{
		if ( !aBot.IsInState('FallBack') )
		{
			aBot.bNoClearSpecial = true;
			aBot.GotoState('FallBack','SpecialNavig');
		}
	}
	else if ( !aBot.IsInState('Roaming') )
	{
		aBot.bNoClearSpecial = true;
		aBot.GotoState('Roaming', 'SpecialNavig');
	}
}

function PickAmbushSpotFor(Bot aBot)
{
	local NavigationPoint N;
	local actor HitActor;
	local vector HitLocation, HitNormal;

	for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
		if ( N.IsA('Ambushpoint') && !N.taken )
		{
			if ( aBot.Orders == 'Defend' )
			{
				if ( N.IsA('DefensePoint') && (DefensePoint(N).team == aBot.PlayerReplicationInfo.team) )
				{
					if ( (DefensePoint(aBot.Ambushspot) == None)
						|| (DefensePoint(N).priority > DefensePoint(aBot.Ambushspot).priority) )
						aBot.Ambushspot = Ambushpoint(N);
					else if ( (DefensePoint(N).priority == DefensePoint(aBot.Ambushspot).priority)
						&& (FRand() < 0.4) ) 
						aBot.Ambushspot = Ambushpoint(N);
				}		
				else
				{
					if ( VSize(N.Location - aBot.OrderObject.Location) > 1500 )
						HitActor = aBot;
					else
						HitActor = Trace(HitLocation, HitNormal, aBot.OrderObject.Location , N.Location, false);
					if ( (HitActor == None)
						&& ((aBot.Ambushspot == None) || (FRand() < 0.5)) )
							aBot.Ambushspot = Ambushpoint(N);
				}
			}
			else if ( (aBot.AmbushSpot == None)
				|| (VSize(aBot.Location - aBot.Ambushspot.Location)
					 > VSize(aBot.Location - N.Location)) )
				aBot.Ambushspot = Ambushpoint(N);
		}
}

function bool PriorityObjective(Bot aBot)
{
	return false;
}

function bool SuccessfulGame()
{
	return ( bFulfilledSpecial && (Teams[RatedPlayer.PlayerReplicationInfo.Team].Score == GoalTeamScore) );
}

defaultproperties
{
     bScoreTeamKills=True
     MaxTeams=2
     MaxTeamSize=16
     NewTeamMessage=" is now on "
     StartUpTeamMessage=" You are on "
     TeamColor(0)="Red"
     TeamColor(1)="Blue"
     TeamColor(2)="Green"
     TeamColor(3)="Gold"
     TEAM_Blue=1
     TEAM_Green=2
     TEAM_Gold=3
     CurrentOrders(0)=Defend
     CurrentOrders(1)=Defend
     CurrentOrders(2)=Defend
     CurrentOrders(3)=Defend
     StartUpMessage="Challenge TeamGame. Work with your teammates against the other teams. "
     bCanChangeSkin=False
     bTeamGame=True
     ScoreBoardType=Class'UnrealShare.UnrealTeamScoreBoard'
     GameMenuType=Class'BotPack.ChallengeTeamGameMenu'
     HUDType=Class'BotPack.ChallengeTeamHUD'
     BeaconName="CTeam"
     GameName="Challenge Team Game"
}
